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 **
;***************************************************************************

View file

@ -2360,6 +2360,30 @@ static AnimTypeClass const ChemBall(
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, // 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?
0x0000, // 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
);
AnimTypeClass const * const AnimTypeClass::Pointers[ANIM_COUNT] = {
@ -2459,6 +2483,7 @@ AnimTypeClass const * const AnimTypeClass::Pointers[ANIM_COUNT] = {
&StegDie,
&RaptDie,
&ChemBall,
&Flag,
&Fire3Virtual,
&Fire2Virtual,
&Fire1Virtual,
@ -2502,7 +2527,7 @@ AnimTypeClass::AnimTypeClass(AnimType anim, char const *name, int size, int bigg
IsWhiteTrans = iswhitetrans;
LoopEnd = loopend;
LoopStart = loopstart;
Loops = (unsigned char)loops;
Loops = (char)loops;
Size = size;
Sound = sound;
Stages = stages;

View file

@ -607,6 +607,10 @@ int AircraftClass::Mission_Hunt(void)
{
Validate();
if (Class->IsFixedWing) {
if (Class->Primary == WEAPON_NONE && Class->Secondary == WEAPON_NONE) {
Assign_Mission(MISSION_RETREAT);
return(1);
}
enum {
LOOK_FOR_TARGET,
FLY_TO_TARGET,
@ -1600,10 +1604,15 @@ 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());
Delete_This();
{
Kill_Cargo(source);
Death_Announcement();
COORDINATE coord = Target_Coord();
if (!(coord & 0xC000C000L)) {
new AnimClass(ANIM_FBALL1, coord);
}
Delete_This();
}
break;
default:

View file

@ -270,6 +270,12 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window)
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;
break;
}
/*
@ -285,7 +291,11 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window)
*/
if (IsAlternate) {
flags = flags | SHAPE_FADING;
remap = Map.RemapTables[HOUSE_GOOD][0];
if (OwnerHouse != HOUSE_NONE) {
remap = HouseClass::As_Pointer(OwnerHouse)->Remap_Table(false, false);
} else {
remap = Map.RemapTables[HOUSE_GOOD][0];
}
}
if (transtable) flags = flags | SHAPE_GHOST;
@ -380,6 +390,8 @@ short const * AnimClass::Overlap_List(void) const
REFRESH_EOL
};
static short const OverlapFlag[] = { 0, 1, -MAP_CELL_W, -(MAP_CELL_W-1), MAP_CELL_W, MAP_CELL_W+1, REFRESH_EOL };
switch (Class->Type) {
case ANIM_CHEM_N:
case ANIM_FLAME_N:
@ -419,6 +431,9 @@ short const * AnimClass::Overlap_List(void) const
case ANIM_ATOM_BLAST:
return(OverlapAtom);
case ANIM_FLAG:
return(OverlapFlag);
default:
break;
}
@ -550,12 +565,12 @@ 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, bool alt) :
AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay, char loop, bool alt) :
Class(&AnimTypeClass::As_Reference(animnum))
{
Object = 0;
SortTarget = TARGET_NONE;
Owner = HOUSE_NONE;
OwnerHouse = HOUSE_NONE;
if (Class->Stages == -1) {
((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
@ -590,8 +605,12 @@ AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay
*/
Delay = timedelay;
Loops = (unsigned char)(MAX(loop, (unsigned char)1) * Class->Loops);
Loops = (unsigned char)MAX(Loops, (unsigned char)1);
if (Class->Loops >= 0) {
Loops = (char)(MAX(loop, (char)1) * Class->Loops);
Loops = (char)MAX(Loops, (char)1);
} else {
Loops = Class->Loops;
}
IsToDelete = false;
IsBrandNew = true;
@ -798,8 +817,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 {
if (Class->VirtualAnim != ANIM_NONE) {
@ -992,11 +1011,11 @@ void AnimClass::Middle(void)
*/
BuildingClass * building = NULL;
TechnoClass * backup = NULL;
if (Owner != HOUSE_NONE) {
if (OwnerHouse != HOUSE_NONE) {
for (int index = 0; index < Logic.Count(); index++) {
ObjectClass * obj = Logic[index];
if (obj && obj->Is_Techno() && obj->Owner() == Owner) {
if (obj && obj->Is_Techno() && obj->Owner() == OwnerHouse) {
backup = (TechnoClass *)obj;
if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_TEMPLE) {
building = (BuildingClass *)obj;
@ -1108,11 +1127,11 @@ void AnimClass::Middle(void)
case ANIM_ION_CANNON: {
BuildingClass * building = NULL;
TechnoClass * backup = NULL;
if (Owner != HOUSE_NONE) {
if (OwnerHouse != HOUSE_NONE) {
for (int index = 0; index < Logic.Count(); index++) {
ObjectClass * obj = Logic[index];
if (obj && obj->Is_Techno() && obj->Owner() == Owner && !obj->IsInLimbo) {
if (obj && obj->Is_Techno() && obj->Owner() == OwnerHouse && !obj->IsInLimbo) {
backup = (TechnoClass *)obj;
if (obj->What_Am_I() == RTTI_BUILDING && *((BuildingClass *)obj) == STRUCT_EYE) {
building = (BuildingClass *)obj;

View file

@ -47,11 +47,12 @@ class AnimClass : public ObjectClass, private StageClass {
static void * AnimClass::operator new(size_t size);
static void AnimClass::operator delete(void *ptr);
AnimClass(void) : Class(0) {Owner=HOUSE_NONE;Object=0;}; // Default constructor does nothing.
AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, unsigned char loop=1, bool alt=false);
AnimClass(void) : Class(0) {OwnerHouse=HOUSE_NONE;Object=0;}; // Default constructor does nothing.
AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay=0, char loop=1, bool alt=false);
virtual ~AnimClass(void);
operator AnimType(void) const {return Class->Type;};
virtual RTTIType What_Am_I(void) const {return RTTI_ANIM;};
virtual HousesType Owner(void) const {return OwnerHouse;};
/*---------------------------------------------------------------------
** Member function prototypes.
@ -114,13 +115,13 @@ class AnimClass : public ObjectClass, private StageClass {
** is used when damage is caused by this animation during the middle of its
** animation.
*/
HousesType Owner;
HousesType OwnerHouse;
/*
** This counter tells how many more times the animation should loop before it
** terminates.
*/
unsigned char Loops;
char Loops;
protected:
void Middle(void);
@ -146,7 +147,9 @@ class AnimClass : public ObjectClass, private StageClass {
*/
unsigned IsBrandNew:1;
// Use alternate color when drawing?
/*
** Use alternate color when drawing
*/
unsigned IsAlternate:1;
/*

View file

@ -293,7 +293,32 @@ RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageT
Begin_Mode(BSTATE_FULL);
}
if (Transmit_Message(RADIO_NEED_TO_MOVE, from) == RADIO_ROGER) {
/*
** If this building is already in radio contact, then it might
** be able to satisfy the request to load by bumping off any
** preoccupying task.
*/
if (*this == STRUCT_REPAIR) {
if (Contact_With_Whom() != from) {
if (Transmit_Message(RADIO_ON_DEPOT) == RADIO_ROGER) {
if (Transmit_Message(RADIO_NEED_REPAIR) == RADIO_NEGATIVE) {
Transmit_Message(RADIO_RUN_AWAY);
Transmit_Message(RADIO_OVER_OUT);
return(RADIO_ROGER);
}
}
}
}
/*
** Establish contact with the object if this building isn't already in contact
** with another.
*/
if (!In_Radio_Contact()) {
Transmit_Message(RADIO_HELLO, from);
}
if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
if (*this == STRUCT_HELIPAD) {
param = As_Target();
} else {
@ -308,7 +333,7 @@ RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageT
/*
** Tell the harvester to move to the docking pad of the refinery.
*/
if (Transmit_Message(RADIO_MOVE_HERE, param, from) == RADIO_YEA_NOW_WHAT) {
if (Transmit_Message(RADIO_MOVE_HERE, param) == RADIO_YEA_NOW_WHAT) {
/*
** Since the harvester is already there, tell it to begin the backup
@ -1899,7 +1924,7 @@ void BuildingClass::Drop_Debris(TARGET source)
CELL cell;
/*
** Special case for Moebius to run from destroyed technology
** Special case for Chan to run from destroyed technology
** building.
*/
if (GameToPlay == GAME_NORMAL && *this == STRUCT_MISSION && PlayerPtr->ActLike == HOUSE_BAD && Scenario == 10) {
@ -1907,7 +1932,7 @@ void BuildingClass::Drop_Debris(TARGET source)
ScenarioInit++;
if (i->Unlimbo(Center_Coord(), DIR_N)) {
i->Trigger = TriggerClass::As_Pointer("CHAN");
i->Trigger = TriggerClass::As_Pointer("win");
i->Strength = Random_Pick(5, (int)i->Class->MaxStrength);
ScenarioInit--;
i->Scatter(0, true);
@ -1915,6 +1940,7 @@ void BuildingClass::Drop_Debris(TARGET source)
i->Assign_Mission(MISSION_GUARD_AREA);
} else {
delete i;
PlayerPtr->Flag_To_Win();
}
ScenarioInit--;
}
@ -3523,7 +3549,7 @@ bool BuildingClass::Toggle_Primary(void)
bool BuildingClass::Captured(HouseClass * newowner)
{
Validate();
if (Class->IsCaptureable && newowner != House) {
if (Can_Capture() && newowner != House) {
switch (Owner()) {
case HOUSE_GOOD:
Speak(VOX_GDI_CAPTURED);
@ -3803,6 +3829,21 @@ bool BuildingClass::Can_Demolish_Unit(void) const
}
bool BuildingClass::Can_Capture(void) const
{
bool can_capture = Class->IsCaptureable;
if (*this == STRUCT_EYE) {
// Don't allow the Advanced Comm Center to be capturable in skirmish, MP, or beyond scenario 13 in SP
if (GameToPlay == GAME_NORMAL) {
can_capture &= Scenario < 13;
} else {
can_capture = false;
}
}
return(can_capture);
}
/***********************************************************************************************
* BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. *
* *
@ -5020,7 +5061,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);
CellClass * cptr = cellptr->Adjacent_Cell(f);
if (!cptr) continue;
UnitClass * cellunit = cptr->Cell_Unit();
if ((cellunit && cellunit != unit) || cptr->Cell_Infantry()) {
cptr->Incoming(coord, true, true);

View file

@ -185,6 +185,7 @@ class BuildingClass : public TechnoClass
virtual ActionType What_Action(CELL cell) const;
virtual bool Can_Demolish(void) const;
virtual bool Can_Demolish_Unit(void) const;
virtual bool Can_Capture(void) const;
virtual ObjectTypeClass const & Class_Of(void) const {return *Class;};
virtual int Refund_Amount(void) const;
virtual DirType Fire_Direction(void) const;

View file

@ -471,9 +471,9 @@ void BulletClass::AI(void)
// Fix for Nuke or Atom Bomb killing structures and units during animation sequence and not getting kills tracked
// Per https://jaas.ea.com/browse/TDRA-6610
//
if (newanim && Class->Explosion == ANIM_ATOM_BLAST && newanim->Owner == HOUSE_NONE) {
if (newanim && Class->Explosion == ANIM_ATOM_BLAST && newanim->OwnerHouse == HOUSE_NONE) {
if (Payback && Payback->House && Payback->House->Class) {
newanim->Owner = Payback->House->Class->House;
newanim->OwnerHouse = Payback->House->Class->House;
}
}
}

View file

@ -211,7 +211,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; // Coordinate relative to cell corner.
long dist;
@ -248,7 +248,7 @@ ObjectClass * CellClass::Cell_Find_Object(RTTIType rtti) const
Validate();
ObjectClass * object = Cell_Occupier();
while (object) {
while (object && object->IsActive) {
if (object->What_Am_I() == rtti) {
return(object);
}
@ -1132,14 +1132,6 @@ void CellClass::Draw_It(int x, int y, int draw_type) const
}
#endif
}
/*
** Draw the flag if there is one located at this cell.
*/
if (IsFlagged) {
void const * remap = HouseClass::As_Pointer(Owner)->Remap_Table(false, false);
CC_Draw_Shape(MixFileClass::Retrieve("FLAGFLY.SHP"), Frame % 14, x+(ICON_PIXEL_W/2), y+(ICON_PIXEL_H/2), WINDOW_TACTICAL, SHAPE_CENTER|SHAPE_GHOST|SHAPE_FADING, remap, Map.UnitShadow);
}
}
}
}
@ -1202,13 +1194,13 @@ void CellClass::Concrete_Calc(void)
*/
index = 0;
for (int i = 0; i < (sizeof(_even)/sizeof(_even[0])); i++) {
CellClass & cellptr = Adjacent_Cell(*ptr++);
CellClass * cellptr = Adjacent_Cell(*ptr++);
// if ((cellptr->IsConcrete) ||
// cellptr->Concrete == C_UPDOWN_RIGHT ||
// cellptr->Concrete == C_UPDOWN_LEFT) {
if (cellptr.Overlay == OVERLAY_CONCRETE) {
if (cellptr && cellptr->Overlay == OVERLAY_CONCRETE) {
index |= (1<<i);
}
}
@ -1399,9 +1391,9 @@ void CellClass::Wall_Update(void)
}
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;
/*
@ -1409,41 +1401,42 @@ 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 = icon;
newcell->OverlayData = (newcell->OverlayData & 0xFFF0) | icon;
// newcell->OverlayData = 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;
ObjectClass::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;
ObjectClass::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;
ObjectClass::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;
ObjectClass::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;
ObjectClass::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;
ObjectClass::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;
ObjectClass::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;
ObjectClass::Detach_This_From_All(::As_Target(newcell->Cell_Number()), true);
}
newcell.Recalc_Attributes();
newcell.Redraw_Objects();
newcell->Recalc_Attributes();
newcell->Redraw_Objects();
}
}
}
@ -1550,10 +1543,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();
return(true);
}
}
@ -1813,28 +1810,23 @@ 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
{
Validate();
if (face == FACING_NONE) {
return(this);
}
if ((unsigned)face >= FACING_COUNT) {
return(*this);
return(NULL);
}
CELL id = Cell_Number();
//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 (ptr->Cell_Number() & 0xF000) return(*this);
return(*ptr);
// return(*(this + AdjacentCell[face]));
return &Map[newcell];
}
@ -1911,10 +1903,10 @@ long CellClass::Tiberium_Adjust(bool pregame)
CELL cell = Cell_Number() + AdjacentCell[face];
if ((unsigned)cell >= 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++;
}
}
@ -2386,6 +2378,7 @@ bool CellClass::Flag_Place(HousesType house)
if (!IsFlagged && Is_Generally_Clear()) {
IsFlagged = true;
Owner = house;
Flag_Update();
Redraw_Objects();
return(true);
}
@ -2413,6 +2406,7 @@ bool CellClass::Flag_Remove(void)
if (IsFlagged) {
IsFlagged = false;
Owner = HOUSE_NONE;
Flag_Update();
Redraw_Objects();
return(true);
}
@ -2420,6 +2414,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(), 0, 1, true);
if (CTFFlag) {
CTFFlag->OwnerHouse = Owner;
}
}
}
void CellClass::Flag_Destroy(void)
{
delete CTFFlag;
CTFFlag = NULL;
}
/***********************************************************************************************
* CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
* *

View file

@ -194,8 +194,8 @@ class CellClass
bool Is_Generally_Clear(bool ignore_cloaked = false) const;
TARGET As_Target(void) const {return ::As_Target(Cell_Number());};
BuildingClass * Cell_Building(void) const;
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;
CellClass * Adjacent_Cell(FacingType face) {return (CellClass *)((*((CellClass const *)this)).Adjacent_Cell(face));};
COORDINATE Cell_Coord(void) const;
int Cell_Color(bool override=false) const;
CELL Cell_Number(void) const;
@ -221,6 +221,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.
@ -286,10 +289,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

@ -160,7 +160,8 @@ void Explosion_Damage(COORDINATE coord, unsigned strength, TechnoClass * source,
** 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

@ -3140,15 +3140,13 @@ 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()) {
switch (CurrentObject[0]->What_Am_I()) {
case RTTI_UNIT:
case RTTI_INFANTRY:
case RTTI_AIRCRAFT:
if (((FootClass *)CurrentObject[0])->Group != team) {
Unselect_All();
}
for (index = 0; index < CurrentObject.Count(); index++) {
ObjectClass * obj = CurrentObject[index];
if (obj->What_Am_I() == RTTI_UNIT || obj->What_Am_I() == RTTI_INFANTRY || obj->What_Am_I() == RTTI_AIRCRAFT) {
if (((FootClass *)obj)->Group != team) {
Unselect_All();
break;
}
}
}
for (index = 0; index < Units.Count(); index++) {

View file

@ -1509,6 +1509,7 @@ typedef enum AnimType : char {
ANIM_STEG_DIE,
ANIM_RAPT_DIE,
ANIM_CHEM_BALL, // Chemical warrior explosion.
ANIM_FLAG, // CTF flag.
ANIM_FIRE_SMALL_VIRTUAL, // Small flame animation (virtual).
ANIM_FIRE_MED_VIRTUAL, // Medium flame animation (virtual).
@ -1630,6 +1631,9 @@ typedef enum RadioMessageType : unsigned char {
RADIO_PUNCH, // "Take this punch, you.. you.."
RADIO_PREPARE_TO_BOX,// "Fancy a little fisticuffs, eh?"
RADIO_NEED_REPAIR, // "Are you in need of service depot work?"
RADIO_ON_DEPOT, // "Are you sitting on a service depot?"
RADIO_COUNT
} RadioMessageType;

View file

@ -106,7 +106,7 @@ typedef enum {
*/
#define RANDOM_START_POSITION 0x7f
#define KILL_PLAYER_ON_DISCONNECT 1
@ -1695,7 +1695,7 @@ extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const cha
}
result = Load_Game(file_path_and_name);
DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0], true);
Set_Logic_Page(SeenBuff);
VisiblePage.Clear();
@ -1747,9 +1747,6 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uin
if (PlayerWins || PlayerLoses || DLLExportClass::Get_Game_Over()) {
return;
}
HousesType house;
HouseClass *ptr;
GlyphX_Debug_Print("CNC_Handle_Player_Switch_To_AI");
@ -1757,9 +1754,28 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uin
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;
@ -1803,6 +1819,9 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uin
}
}
}
#endif //KILL_PLAYER_ON_DISCONNECT
}
@ -2489,18 +2508,26 @@ 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<MPlayerCount ; i++) {
// Trigger an event for each human player, winner first (even if it's an AI)
for (int i = 0; i < MPlayerCount; i++) {
HouseClass* player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2);
if ( player_ptr != NULL )
{
if ( player_ptr->IsHuman == true )
{
event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr);
event.GameOver.PlayerWins = !player_ptr->IsDefeated;
event.GameOver.RemainingCredits = player_ptr->Available_Money();
EventCallback(event);
}
if (player_ptr != NULL && !player_ptr->IsDefeated) {
event.GlyphXPlayerID = Get_GlyphX_Player_ID(player_ptr);
event.GameOver.IsHuman = player_ptr->IsHuman;
event.GameOver.PlayerWins = true;
event.GameOver.RemainingCredits = player_ptr->Available_Money();
EventCallback(event);
}
}
for (int i = 0; i < MPlayerCount; i++) {
HouseClass* player_ptr = HouseClass::As_Pointer(MPlayerHouses[i]); //HouseClass::As_Pointer(HOUSE_MULTI2);
if (player_ptr != NULL && 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);
}
}
}
@ -3761,6 +3788,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;
@ -6627,8 +6658,12 @@ void DLLExportClass::Selected_Guard_Mode(uint64 player_id)
for (int index = 0; index < CurrentObject.Count(); index++) {
ObjectClass const * tech = CurrentObject[index];
if (tech && tech->Can_Player_Move() && tech->Can_Player_Fire()) {
OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD_AREA));
if (tech && tech->Can_Player_Fire()) {
if (tech->Can_Player_Move()) {
OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD_AREA));
} else {
OutList.Add(EventClass(tech->As_Target(), MISSION_GUARD));
}
}
}
}
@ -7158,7 +7193,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

@ -28,7 +28,7 @@ struct CarryoverObjectStruct;
**
**
*/
#define CNC_DLL_API_VERSION 0x100
#define CNC_DLL_API_VERSION 0x101
@ -623,6 +623,7 @@ struct EventCallbackStruct {
//
// Single-player data
//
bool IsHuman;
bool PlayerWins;
const char* MovieName;
const char* MovieName2;

View file

@ -521,8 +521,8 @@ void DriveClass::Assign_Destination(TARGET target)
** facility is currently not involved with some other unit (radio or unloading).
*/
if (b && *b == STRUCT_REPAIR) {
if (b->In_Radio_Contact()) {
target = 0;
if (b->In_Radio_Contact() && (b->Contact_With_Whom() != this)) {
ArchiveTarget = target;
} else {
/*
@ -1598,12 +1598,18 @@ void DriveClass::Mark_Track(COORDINATE headto, MarkType type)
if (TrackIndex < cellidx && cellidx != -1) {
COORDINATE offset = Smooth_Turn(ptr[cellidx].Offset, &dir);
Map[Coord_Cell(offset)].Flag.Occupy.Vehicle = value;
CELL cell = Coord_Cell(offset);
if ((unsigned)cell < MAP_CELL_TOTAL) {
Map[cell].Flag.Occupy.Vehicle = value;
}
}
}
}
}
Map[Coord_Cell(headto)].Flag.Occupy.Vehicle = value;
CELL cell = Coord_Cell(headto);
if ((unsigned)cell < MAP_CELL_TOTAL) {
Map[cell].Flag.Occupy.Vehicle = value;
}
}
}

View file

@ -546,23 +546,10 @@ bool FactoryClass::Start(void)
time = TICKS_PER_MINUTE * 5;
}
/*
** 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 = (int)((time*Rule.MaxIQ*2.0f) / (House->IQ+Rule.MaxIQ));
}
time /= STEP_COUNT;
time = Bound(time, 1, 255);
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

@ -98,6 +98,8 @@
#define MAX_MLIST_SIZE 300
#define THREAT_THRESHOLD 5
#define MAX_PATH_EDGE_FOLLOW 400
#ifdef NEVER
typedef enum {
FACING_N, // North
@ -734,8 +736,14 @@ top_of_list:
pleft.Overlap = LeftOverlap;
Mem_Copy(path.Command, pleft.Command, path.Length);
Mem_Copy(path.Overlap, pleft.Overlap, sizeof(LeftOverlap));
// 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;
#if 0
left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, sizeof(moves_left), threshhold);
// left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, follow_len, threshhold);
#endif
left = Follow_Edge(startcell, next, &pleft, COUNTERCLOCK, direction, threat, threat_stage, MAX_MLIST_SIZE, threshhold);
if (left) {
follow_len = MIN(maxlen, pleft.Length + (pleft.Length >> 1));
@ -761,8 +769,14 @@ top_of_list:
pright.Overlap = RightOverlap;
Mem_Copy(path.Command, pright.Command, path.Length);
Mem_Copy(path.Overlap, pright.Overlap, sizeof(RightOverlap));
// 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;
#if 0
right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, sizeof(moves_right), threshhold);
// right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, follow_len, threshhold);
#endif
right = Follow_Edge(startcell, next, &pright, CLOCK, direction, threat, threat_stage, MAX_MLIST_SIZE, threshhold);
/*
** If we are in debug mode then let us know how well our right path
@ -1119,7 +1133,7 @@ bool FootClass::Follow_Edge(CELL start, CELL target, PathType *path, FacingType
online = true;
}
cellcount++;
if (cellcount==100) {
if (cellcount==MAX_PATH_EDGE_FOLLOW) {
// DrawPath = true;
// Debug_Find_Path = true;
// Debug_Draw_Map("Loop failure", start, target, false);

View file

@ -1006,6 +1006,7 @@ int FootClass::Mission_Guard_Area(void)
Target_Something_Nearby(THREAT_AREA);
Coord = old;
if (Target_Legal(TarCom)) return(1);
Random_Animate();
} else {
Approach_Target();
}
@ -1258,7 +1259,7 @@ void FootClass::Active_Click_With(ActionType action, ObjectClass * object)
break;
case ACTION_ENTER:
if (Can_Player_Move() && object && object->Is_Techno() && !((RadioClass *)object)->In_Radio_Contact()) {
if (Can_Player_Move() && object && object->Is_Techno() /*&& !((RadioClass *)object)->In_Radio_Contact()*/) {
Player_Assign_Mission(MISSION_ENTER, TARGET_NONE, object->As_Target());
}
break;
@ -1498,6 +1499,18 @@ RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType
{
switch (message) {
/*
** Answers if this object is located on top of a service depot.
*/
case RADIO_ON_DEPOT:
if (Map[Coord_Cell(Center_Coord())].Cell_Building() != NULL) {
BuildingClass const * building = Map[Coord_Cell(Center_Coord())].Cell_Building();
if (*building == STRUCT_REPAIR) {
return(RADIO_ROGER);
}
}
return(RADIO_NEGATIVE);
/*
** Intercept the repair request and if this object is moving, then no repair
** is possible.
@ -1524,7 +1537,7 @@ RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType
Assign_Mission(MISSION_GUARD);
}
if (!IsRotating && !Target_Legal(NavCom)) {
Scatter(0, true);
Scatter(0, true, true);
}
break;
@ -1590,36 +1603,40 @@ RadioMessageType FootClass::Receive_Message(RadioClass * from, RadioMessageType
int FootClass::Mission_Enter(void)
{
/*
** If radio contact has not yet been established with the transport, try to
** establish contact now.
** Find out who to coordinate with. If in radio contact, then this the transporter is
** defined. If not in radio contact, then try the archive target value to see if that
** is suitable.
*/
if (!In_Radio_Contact()) {
TechnoClass * techno = As_Techno(ArchiveTarget);
if (!techno) techno = As_Techno(NavCom);
if (techno) {
TechnoClass * contact = Contact_With_Whom();
if (contact == NULL) {
contact = As_Techno(ArchiveTarget);
}
if (contact == NULL) {
contact = As_Techno(NavCom);
}
/*
** If the transport is already in radio contact, do nothing. Try to
** establish radio contact later.
*/
if (Transmit_Message(RADIO_HELLO, techno) == RADIO_ROGER) {
Assign_Destination(TARGET_NONE);
}
} else {
ArchiveTarget = TARGET_NONE;
/*
** If in contact, then let the transporter handle the movement coordination.
*/
if (contact != NULL) {
/*
** If the transport says to "bug off", then abort the enter mission. The transport may
** likely say all is 'ok' with the "RADIO ROGER", then try again later.
*/
if (Transmit_Message(RADIO_DOCKING, contact) != RADIO_ROGER && !IsTethered) {
Transmit_Message(RADIO_OVER_OUT);
Enter_Idle_Mode();
}
} else {
/*
** Since radio contact exists with the transport, maintain a dialogue so that
** the transport can give proper instructions to the passenger.
** Since there is no potential object to enter, then abort this
** mission with some default standby mission.
*/
if (Transmit_Message(RADIO_DOCKING) != RADIO_ROGER) {
Transmit_Message(RADIO_OVER_OUT);
Enter_Idle_Mode();
}
ArchiveTarget = TARGET_NONE;
Enter_Idle_Mode();
}
return(TICKS_PER_SECOND/2);
}

View file

@ -669,6 +669,7 @@ void Do_Win(void);
void Do_Restart(void);
void Fill_In_Data(void);
bool Restate_Mission(char const * name, int button1, int button2);
void Fixup_Scenario(void);
/*
** SCORE.CPP

View file

@ -1221,6 +1221,7 @@ void HouseClass::AI(void)
unit->Mark(MARK_CHANGE);
} else {
CELL cell = As_Cell(FlagLocation);
Map[cell].Flag_Update();
Map[cell].Redraw_Objects();
}
}
@ -1665,7 +1666,11 @@ void HouseClass::Attacked(BuildingClass* source)
Validate();
if (SpeakAttackDelay.Expired() && PlayerPtr->Class->House == Class->House) {
Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0);
SpeakAttackDelay.Set(Options.Normalize_Delay(SPEAK_DELAY));
// MBL 06.13.2020 - Timing change from 2 minute cooldown, per https://jaas.ea.com/browse/TDRA-6784
// SpeakAttackDelay.Set(Options.Normalize_Delay(SPEAK_DELAY)); // 2 minutes
// SpeakAttackDelay.Set(Options.Normalize_Delay(TICKS_PER_MINUTE/2)); // 30 seconds as requested
SpeakAttackDelay.Set(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
@ -2655,7 +2660,7 @@ bool HouseClass::Place_Special_Blast(SpecialWeaponType id, CELL cell)
case SPC_ION_CANNON:
if (IonCannon.Is_Ready()) {
anim = new AnimClass(ANIM_ION_CANNON, Cell_Coord(cell));
if (anim) anim->Owner = Class->House;
if (anim) anim->OwnerHouse = Class->House;
if (this == PlayerPtr) {
Map.IsTargettingMode = false;
}
@ -4055,7 +4060,6 @@ void HouseClass::MPlayer_Defeated(void)
------------------------------------------------------------------------*/
if (PlayerPtr == this) {
MPlayerObiWan = 1;
Debug_Unshroud = true;
HiddenPage.Clear();
Map.Flag_To_Redraw(true);
@ -4313,11 +4317,6 @@ void HouseClass::MPlayer_Defeated(void)
MPlayerCount = 0;
}
}
/*------------------------------------------------------------------------
Be sure our messages get displayed, even if we're about to exit.
------------------------------------------------------------------------*/
Map.Render();
}
@ -4738,10 +4737,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();
ObjectClass::Detach_This_From_All(::As_Target(cell), true);

View file

@ -572,7 +572,7 @@ static InfantryTypeClass const Commando(
false, // Is this a civlian?
false, // Always use the given name for the infantry?
false, // Is this a "fraidycat" run-away type infantry?
true, // Can this infantry type capture a building?
false, // Can this infantry type capture a building?
false, // Theater specific graphic image?
-1, // Number of shots it has (default).
&CommandoDos[0][0], // ptr to DO table
@ -1648,6 +1648,7 @@ InfantryTypeClass::InfantryTypeClass (
IsCapture = is_capture;
IsFraidyCat = is_fraidycat;
IsCivilian = is_civilian;
IsAvoidingTiberium = false;
Type = type;
FireLaunch = firelaunch;
ProneLaunch = pronelaunch;

View file

@ -1000,7 +1000,7 @@ void InfantryClass::Assign_Target(TARGET target)
*/
if (!Target_Legal(NavCom) && Class->IsCapture && Class->Primary == WEAPON_NONE) {
BuildingClass const * building = As_Building(target);
if (building && building->Class->IsCaptureable && (GameToPlay != GAME_NORMAL || *building != STRUCT_EYE && Scenario < 13)) {
if (building && building->Can_Capture()) {
Assign_Destination(target);
}
}
@ -2086,8 +2086,12 @@ void InfantryClass::Scatter(COORDINATE threat, bool forced, bool nokidding)
newcell = Adjacent_Cell(Coord_Cell(Coord), newface);
if (Map.In_Radar(newcell) && Can_Enter_Cell(newcell) == MOVE_OK) {
Assign_Mission(MISSION_MOVE);
Assign_Destination(::As_Target(newcell));
if (!Class->IsAvoidingTiberium ||
Map[newcell].Overlay == OVERLAY_NONE ||
OverlayTypeClass::As_Reference(Map[newcell].Overlay).Land != LAND_TIBERIUM) {
Assign_Mission(MISSION_MOVE);
Assign_Destination(::As_Target(newcell));
}
}
}
}
@ -2909,7 +2913,7 @@ ActionType InfantryClass::What_Action(ObjectClass * object) const
if (object->Owner() != Owner() &&
((object->What_Am_I() == RTTI_AIRCRAFT && ((AircraftClass *)object)->Pip_Count() == 0 && *((AircraftClass *)object) == AIRCRAFT_TRANSPORT) ||
(object->What_Am_I() == RTTI_BUILDING &&
((BuildingClass *)object)->Class->IsCaptureable && (GameToPlay != GAME_NORMAL || *((BuildingClass *)object) != STRUCT_EYE || Scenario < 13) ))
((BuildingClass *)object)->Can_Capture()))
) {
action = ACTION_CAPTURE;
@ -2978,6 +2982,13 @@ void InfantryClass::Read_INI(char *buffer)
*/
classid = InfantryTypeClass::From_Name(strtok(NULL, ",\n\r"));
/*
** Special case: replace C7 with C5 on scg08eb
*/
if (GameToPlay == GAME_NORMAL && PlayerPtr->ActLike == HOUSE_GOOD && Scenario == 8 && ScenVar == SCEN_VAR_B && classid == INFANTRY_C7) {
classid = INFANTRY_C5;
}
if (classid != INFANTRY_NONE) {
if (HouseClass::As_Pointer(inhouse) != NULL) {
@ -3009,7 +3020,12 @@ void InfantryClass::Read_INI(char *buffer)
infantry->Trigger->AttachCount++;
}
if (infantry->Unlimbo(coord, dir)) {
/*
** Special case: delete pre-placed Chan on scb10ea; he will spawn from the Tech Center.
*/
bool is_scb10ea_chan = GameToPlay == GAME_NORMAL && PlayerPtr->ActLike == HOUSE_BAD && Scenario == 10 && ScenVar == SCEN_VAR_A && *infantry == INFANTRY_CHAN;
if (!is_scb10ea_chan && infantry->Unlimbo(coord, dir)) {
infantry->Strength = Fixed_To_Cardinal(infantry->Class_Of().MaxStrength, strength);
if (GameToPlay == GAME_NORMAL || infantry->House->IsHuman) {
infantry->Assign_Mission(mission);
@ -3181,6 +3197,11 @@ int InfantryClass::Made_A_Kill(void)
void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index)
{
Validate();
if ((unsigned)cell >= MAP_CELL_TOTAL) {
return;
}
/*
** Set the occupy postion for the spot that we passed in
*/
@ -3208,6 +3229,11 @@ void InfantryClass::Set_Occupy_Bit(CELL cell, int spot_index)
void InfantryClass::Clear_Occupy_Bit(CELL cell, int spot_index)
{
Validate();
if ((unsigned)cell >= MAP_CELL_TOTAL) {
return;
}
/*
** Clear the occupy bit for the infantry in that cell
*/

View file

@ -554,6 +554,11 @@ bool Read_Scenario_Ini(char *root, bool fresh)
Map[(CELL)2015].Override_Land_Type(LAND_ROCK);
}
/*
** Scenario fix-up (applied on loaded games as well)
*/
Fixup_Scenario();
/*
** Multi-player last-minute fixups:
** - If computer players are disabled, remove all computer-owned houses

View file

@ -218,6 +218,11 @@ void CellClass::Code_Pointers(void)
if (IsTrigger) {
CellTriggers[Cell_Number()] = (TriggerClass *)CellTriggers[Cell_Number()]->As_Target();
}
/*
------------------------ Convert flag pointer -------------------------
*/
assert(CTFFlag == NULL);
}
@ -299,6 +304,11 @@ void CellClass::Decode_Pointers(void)
CellTriggers[Cell_Number()] = As_Trigger( (TARGET)CellTriggers[Cell_Number()], false );
Check_Ptr((void *)CellTriggers[Cell_Number()],__FILE__,__LINE__);
}
/*
** Convert flag pointer.
*/
CTFFlag = NULL;
}
@ -1012,6 +1022,13 @@ void MapClass::Code_Pointers(void)
{
CELL cell;
/*
------------------------- Destroy all flag animations --------------------
*/
for (cell = 0; cell < MAP_CELL_TOTAL; cell++) {
(*this)[cell].Flag_Destroy();
}
/*
------------------------- Code the cell pointers -------------------------
*/

View file

@ -980,7 +980,7 @@ void MapClass::Logic(void)
if (Map.In_Radar(cell)) {
FacingType offset = Random_Pick(FACING_N, FACING_NW);
for (FacingType index = FACING_N; index < FACING_COUNT; index++) {
CellClass *newcell = &(*this)[cell].Adjacent_Cell(index+offset);
CellClass *newcell = (*this)[cell].Adjacent_Cell(index+offset);
if (newcell && newcell->Cell_Object() == NULL && newcell->Land_Type() == LAND_CLEAR && newcell->Overlay == OVERLAY_NONE) {
bool found = false;

View file

@ -428,7 +428,7 @@ dxisbig:
#if (0)
/*
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/MiscAsm.cpp#33 $
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/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

@ -489,6 +489,12 @@ bool ObjectClass::Can_Demolish_Unit(void) const
}
bool ObjectClass::Can_Capture(void) const
{
return(false);
}
/***********************************************************************************************
* ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? *
* *

View file

@ -159,6 +159,7 @@ class ObjectClass : public AbstractClass
virtual bool Can_Repair(void) const;
virtual bool Can_Demolish(void) const;
virtual bool Can_Demolish_Unit(void) const;
virtual bool Can_Capture(void) const;
virtual bool Can_Player_Fire(void) const;
virtual bool Can_Player_Move(void) const;

View file

@ -309,7 +309,8 @@ bool OverlayClass::Mark(MarkType mark)
static FacingType _face[4] = {FACING_N, FACING_E, FACING_S, FACING_W};
for (int index = 0; index < (sizeof(_face)/sizeof(_face[0])); index++) {
cellptr->Adjacent_Cell(_face[index]).Concrete_Calc();
CellClass * adjcell = cellptr->Adjacent_Cell(_face[index]);
if (adjcell) adjcell->Concrete_Calc();
}
}
}

View file

@ -539,12 +539,14 @@ bool Load_Game(const char *file_name)
Map.Init_IO();
Map.Flag_To_Redraw(true);
Fixup_Scenario();
ScenarioInit = 0;
/*
** Fixup remap tables. ST - 2/28/2020 1:50PM
*/
for (HousesType house = HOUSE_MULTI1; house < HOUSE_COUNT; house++) {
for (HousesType house = HOUSE_FIRST; house < HOUSE_COUNT; house++) {
HouseClass * hptr = HouseClass::As_Pointer(house);
if (hptr && hptr->IsActive) {
hptr->Init_Data(hptr->RemapColor, hptr->ActLike, hptr->Credits);

View file

@ -138,16 +138,6 @@ bool Start_Scenario(char *root, bool briefing)
}
#endif
/*
** Hack for laser-firing Orcas in the PATSUX secret mission
*/
if (GameToPlay == GAME_NORMAL && Scenario == 72) {
((AircraftTypeClass&)AircraftTypeClass::As_Reference(AIRCRAFT_ORCA)).Primary = WEAPON_OBELISK_LASER;
}
else {
((AircraftTypeClass&)AircraftTypeClass::As_Reference(AIRCRAFT_ORCA)).Primary = WEAPON_DRAGON;
}
/*
** Set the options values, since the palette has been initialized by Read_Scenario
*/
@ -786,4 +776,27 @@ bool Restate_Mission(char const * name, int button1, int button2)
#endif
}
return(false);
}
void Fixup_Scenario(void)
{
/*
** "Fraidycat" civilians avoid wandering into Tiberium for SCG08EB, since they're mission-critical.
*/
bool is_scg08ea = GameToPlay == GAME_NORMAL && PlayerPtr->ActLike == HOUSE_GOOD && Scenario == 8 && ScenVar == SCEN_VAR_B;
for (InfantryType index = INFANTRY_FIRST; index < INFANTRY_COUNT; index++) {
InfantryTypeClass& infantry_type = (InfantryTypeClass&)InfantryTypeClass::As_Reference(index);
if (infantry_type.IsFraidyCat) {
infantry_type.IsAvoidingTiberium = is_scg08ea;
}
}
/*
** Laser-firing Orcas in the PATSUX secret mission
*/
if (GameToPlay == GAME_NORMAL && Scenario == 72) {
((AircraftTypeClass&)AircraftTypeClass::As_Reference(AIRCRAFT_ORCA)).Primary = WEAPON_OBELISK_LASER;
} else {
((AircraftTypeClass&)AircraftTypeClass::As_Reference(AIRCRAFT_ORCA)).Primary = WEAPON_DRAGON;
}
}

View file

@ -290,7 +290,15 @@ unsigned short TechnoTypeClass::Get_Ownable(void) const
*=============================================================================================*/
int TechnoTypeClass::Time_To_Build(HousesType house) const
{
int cost = Raw_Cost();
/*
** Base time is the raw cost.
*/
int time = Raw_Cost();
HouseClass* hptr = HouseClass::As_Pointer(house);
if (!hptr || !hptr->IsActive) {
return time;
}
/*
** For computer controlled buildings, slow down production on
@ -299,9 +307,8 @@ int TechnoTypeClass::Time_To_Build(HousesType house) const
if (PlayerPtr->Difficulty != DIFF_HARD &&
GameToPlay == GAME_NORMAL &&
What_Am_I() == RTTI_BUILDINGTYPE &&
PlayerPtr->Class->House != house) {
cost = (cost + (PlayerPtr->Difficulty == DIFF_EASY ? 4000 : 2000)) / 2;
PlayerPtr->Class->House != hptr->Class->House) {
time = (time + (PlayerPtr->Difficulty == DIFF_EASY ? 4000 : 2000)) / 2;
}
/*
@ -309,10 +316,35 @@ int TechnoTypeClass::Time_To_Build(HousesType house) const
** an airfield.
*/
if (What_Am_I() == RTTI_UNITTYPE && !(Ownable & HOUSEF_GOOD)) {
return (cost - (cost/4));
time -= (time / 4);
}
return(cost);
/*
** 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 = (int)((time * Rule.MaxIQ * 2.0f) / (hptr->IQ + Rule.MaxIQ));
}
/*
** Adjust the time to build based on the power output of the owning house.
*/
int power = hptr->Power_Fraction();
int inv_scale(256);
if (power == 0x000) {
inv_scale = 64;
}
else if (power < 0x080) {
inv_scale = 103;
}
else if (power < 0x0100) {
inv_scale = 171;
}
time = (time * 256) / inv_scale;
return(time);
}
@ -988,7 +1020,9 @@ void TechnoClass::Draw_It(int x, int y, WindowNumberType window)
{
Clear_Redraw_Flag();
const bool show_health_bar = (Strength > 0) && (Is_Selected_By_Player() ||
const bool show_health_bar =
(Strength > 0) && ((Cloak != CLOAKED) || Is_Owned_By_Player()) &&
(Is_Selected_By_Player() ||
((Special.HealthBarDisplayMode == SpecialClass::HB_DAMAGED) && (Strength < Techno_Type_Class()->MaxStrength)) ||
(Special.HealthBarDisplayMode == SpecialClass::HB_ALWAYS));
@ -1013,10 +1047,8 @@ void TechnoClass::Draw_It(int x, int y, WindowNumberType window)
for (int i = start_line; i < LineCount; i++) {
CC_Draw_Line(Lines[i][0], Lines[i][1], Lines[i][2], Lines[i][3], (unsigned char)Lines[i][4], LineFrame, window);
}
if ((window == WINDOW_VIRTUAL) && (++LineFrame >= LineMaxFrames)) {
LineCount = 0;
LineFrame = 0;
LineMaxFrames = 0;
if (window == WINDOW_VIRTUAL) {
LineFrame++;
}
}
@ -1968,8 +2000,13 @@ void TechnoClass::AI(void)
/*
** Handle line delay logic.
*/
if ((LineMaxFrames > 0) && (LineCount > 0)) {
if (LineMaxFrames > 0) {
Map.Flag_To_Redraw(true);
if (LineFrame >= LineMaxFrames) {
LineCount = 0;
LineFrame = 0;
LineMaxFrames = 0;
}
}
/*
@ -2249,6 +2286,9 @@ BulletClass * TechnoClass::Fire_At(TARGET target, int which)
COORDINATE fire_coord; // Coordinate of firing position.
TechnoTypeClass const & tclass = *Techno_Type_Class();
ObjectClass *object;
if ((which == 0 && tclass.Primary == WEAPON_NONE) || (which != 0 && tclass.Secondary == WEAPON_NONE)) {
return(NULL);
}
WeaponTypeClass const *weapon = (which == 0) ? &Weapons[tclass.Primary] : &Weapons[tclass.Secondary];
BulletTypeClass const &btype = BulletTypeClass::As_Reference(weapon->Fires);
@ -2558,11 +2598,17 @@ ActionType TechnoClass::What_Action(ObjectClass * object) const
//if (IsOwnedByPlayer && (ctrldown || !House->Is_Ally(object)) && (ctrldown || object->Class_Of().IsLegalTarget || (Special.IsTreeTarget && object->What_Am_I() == RTTI_TERRAIN))) {
if (Can_Player_Move() || In_Range(object, 0)) {
// Check for anti-air capability
if ((object->What_Am_I() != RTTI_AIRCRAFT) ||
((Techno_Type_Class()->Primary != WEAPON_NONE) && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).IsAntiAircraft) ||
((Techno_Type_Class()->Secondary != WEAPON_NONE) && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Secondary].Fires).IsAntiAircraft) ||
(((AircraftClass *)object)->Altitude == 0)) {
if (object->What_Am_I() != RTTI_AIRCRAFT) {
return(ACTION_ATTACK);
} else {
AircraftClass* aircraft = (AircraftClass*)object;
if (*aircraft != AIRCRAFT_CARGO) {
if (((Techno_Type_Class()->Primary != WEAPON_NONE) && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Primary].Fires).IsAntiAircraft) ||
((Techno_Type_Class()->Secondary != WEAPON_NONE) && BulletTypeClass::As_Reference(Weapons[Techno_Type_Class()->Secondary].Fires).IsAntiAircraft) ||
(aircraft->Altitude == 0)) {
return(ACTION_ATTACK);
}
}
}
}
}

View file

@ -1111,6 +1111,12 @@ class InfantryTypeClass : public TechnoTypeClass
*/
unsigned IsCivilian:1;
/*
** For "fraidycat" infantry types that will run away from any damage causing
** events, this controls whether they avoid wandering into Tiberium.
*/
unsigned IsAvoidingTiberium:1;
/*
** This value represents the unit class. It can serve as a unique
** identification number for this unit class.
@ -1642,7 +1648,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 char Loops;
char Loops;
/*
** This is the sound effect to play when this animation starts. Usually, this

View file

@ -583,6 +583,13 @@ RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType
Validate();
switch (message) {
/*
** Checks to see if this object is in need of service depot processing.
*/
case RADIO_NEED_REPAIR:
if (!IsDriving && !Target_Legal(NavCom) && Health_Ratio() >= 0x100) return(RADIO_NEGATIVE);
break;
/*
** Asks if the passenger can load on this transport.
*/
@ -623,6 +630,38 @@ RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType
** to the impatient unit.
*/
case RADIO_DOCKING:
/*
** Check for the case of a docking message arriving from a unit that does not
** have formal radio contact established. This might be a unit that is standing
** by. If this transport is free to proceed with normal docking operation, then
** establish formal contact now. If the transport is completely full, then break
** off contact. In all other cases, just tell the pending unit to stand by.
*/
if (Contact_With_Whom() != from) {
/*
** Can't ever load up so tell the passenger to bug off.
*/
if (How_Many() >= Class->Max_Passengers()) {
return(RADIO_NEGATIVE);
}
/*
** Establish contact and let the loading process proceed normally.
*/
if (!In_Radio_Contact()) {
Transmit_Message(RADIO_HELLO, from);
} else {
/*
** This causes the potential passenger to think that all is ok and to
** hold on for a bit.
*/
return(RADIO_ROGER);
}
}
if (Class->IsTransporter && *this == UNIT_APC && How_Many() < Class->Max_Passengers()) {
TarComClass::Receive_Message(from, message, param);
@ -1215,6 +1254,16 @@ void UnitClass::Active_Click_With(ActionType action, ObjectClass * object)
void UnitClass::Active_Click_With(ActionType action, CELL cell) {TarComClass::Active_Click_With(action, cell);};
void UnitClass::Player_Assign_Mission(MissionType mission, TARGET target, TARGET destination)
{
Validate();
if (mission == MISSION_HARVEST) {
ArchiveTarget = TARGET_NONE;
}
TarComClass::Player_Assign_Mission(mission, target, destination);
}
/***********************************************************************************************
* UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. *
* *
@ -1744,7 +1793,7 @@ void UnitClass::Per_Cell_Process(bool center)
return;
default:
Scatter(true);
Scatter(0, true);
break;
}
}
@ -1755,28 +1804,39 @@ void UnitClass::Per_Cell_Process(bool center)
** When breaking away from a transport object or building, possibly
** scatter or otherwise begin normal unit operations.
*/
if (IsTethered && center && Mission != MISSION_ENTER && (*this != UNIT_APC || IsDriving || !Target_Legal(TarCom))) {
TechnoClass * contact = Contact_With_Whom();
if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) {
if (*this == UNIT_HARVESTER && contact && contact->What_Am_I() == RTTI_BUILDING) {
Assign_Mission(MISSION_HARVEST);
} else if (!Target_Legal(NavCom)) {
Scatter(0, true);
}
if (IsTethered && center &&
(Mission != MISSION_ENTER || (As_Techno(NavCom) != NULL && Contact_With_Whom() != As_Techno(NavCom))) &&
Mission != MISSION_UNLOAD) {
/*
** Special hack check to make sure that even though it has moved one
** cell, if it is still on the building (e.g., service depot), have
** it scatter again.
*/
if (Map[cell].Cell_Building() != NULL && !Target_Legal(NavCom)) {
Scatter(0, true, true);
} else {
if (*this == UNIT_HARVESTER) {
if (Target_Legal(ArchiveTarget)) {
TechnoClass * contact = Contact_With_Whom();
if (Transmit_Message(RADIO_UNLOADED) == RADIO_RUN_AWAY) {
if (*this == UNIT_HARVESTER && contact && contact->What_Am_I() == RTTI_BUILDING) {
Assign_Mission(MISSION_HARVEST);
Assign_Destination(ArchiveTarget);
ArchiveTarget = TARGET_NONE;
} else {
} else if (!Target_Legal(NavCom)) {
Scatter(0, true);
}
} else {
if (*this == UNIT_HARVESTER) {
if (Target_Legal(ArchiveTarget)) {
Assign_Mission(MISSION_HARVEST);
Assign_Destination(ArchiveTarget);
ArchiveTarget = TARGET_NONE;
} else {
/*
** Since there is no place to go, move away to clear
** the pad for another harvester.
*/
if (!Target_Legal(NavCom)) {
Scatter(0, true);
/*
** Since there is no place to go, move away to clear
** the pad for another harvester.
*/
if (!Target_Legal(NavCom)) {
Scatter(0, true);
}
}
}
}
@ -2204,7 +2264,10 @@ void UnitClass::Draw_It(int x, int y, WindowNumberType window)
** If this unit is carrying the flag, then draw that on top of everything else.
*/
if (Flagged != HOUSE_NONE) {
CC_Draw_Shape(this, "FLAGFLY", MixFileClass::Retrieve("FLAGFLY.SHP"), Frame % 14, x, y, window, SHAPE_CENTER|SHAPE_FADING|SHAPE_GHOST, HouseClass::As_Pointer(Flagged)->Remap_Table(false, false), Map.UnitShadow, Flagged);
shapefile = MixFileClass::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, false), Map.UnitShadow, Flagged);
}
TarComClass::Draw_It(x, y, window);
@ -2221,34 +2284,34 @@ void UnitClass::Draw_It(int x, int y, WindowNumberType window)
* *
* INPUT: none *
* *
* 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)
{
Validate();
/*
** 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);
//using function for IsVisible so we have different results for different players - JAS 2019/09/30
if ((GameToPlay != GAME_NORMAL || (!IsOwnedByPlayer || Map[center].Is_Visible(PlayerPtr)))) {
if (!Map[center].Cell_Techno() && Map[center].Land_Type() == LAND_TIBERIUM) {
return(true);
return(Map[center].OverlayData);
}
}
return(false);
return(0);
}
@ -2265,32 +2328,42 @@ bool UnitClass::Goto_Tiberium(void)
** Perform a ring search outward from the center.
*/
for (int radius = 1; radius < 64; 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);
}
}
}
}
@ -2589,6 +2662,7 @@ int UnitClass::Mission_Harvest(void)
Set_Rate(2);
Set_Stage(0);
Status = HARVESTING;
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
return(1);
} else {
@ -2623,7 +2697,6 @@ int UnitClass::Mission_Harvest(void)
IsHarvesting = false;
if (Tiberium_Load() == 0x0100) {
Status = FINDHOME;
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
} else {
if (!Goto_Tiberium() && !Target_Legal(NavCom)) {
ArchiveTarget = TARGET_NONE;
@ -2634,6 +2707,8 @@ int UnitClass::Mission_Harvest(void)
}
}
return(1);
} else if (!Target_Legal(NavCom) && ArchiveTarget == TARGET_NONE) {
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
}
break;
@ -2823,7 +2898,7 @@ short const * UnitClass::Overlap_List(void) const
if (Is_Selected_By_Player() || IsFiring) {
size += 24;
}
if (Is_Selected_By_Player() || Class->IsGigundo || IsAnimAttached) {
if (Is_Selected_By_Player() || Class->IsGigundo || IsAnimAttached || Flagged != HOUSE_NONE) {
size = ICON_PIXEL_W*2;
}
return(Coord_Spillage_List(Coord, size)+1);
@ -3245,7 +3320,11 @@ void UnitClass::Scatter(COORDINATE threat, bool forced, bool nokidding)
{
Validate();
if (*this != UNIT_GUNBOAT && *this != UNIT_HOVER) {
if (!PrimaryFacing.Is_Rotating() && ((!Target_Legal(TarCom) && !Target_Legal(NavCom)) || forced || nokidding || Random_Pick(1, 4) == 1)) {
if (PrimaryFacing.Is_Rotating()) return;
if (Target_Legal(NavCom) && !nokidding) return;
if (threat == 0) {
Assign_Destination(::As_Target(Map.Nearby_Location(Coord_Cell(Coord))));
} else if (((!Target_Legal(TarCom) && !Target_Legal(NavCom)) || forced || nokidding || Random_Pick(1, 4) == 1)) {
FacingType toface;
FacingType newface;
CELL newcell;
@ -3375,7 +3454,13 @@ bool UnitClass::Limbo(void)
if (!IsInLimbo) {
Stop_Driver();
}
return(TarComClass::Limbo());
if (TarComClass::Limbo()) {
if (Flagged != HOUSE_NONE) {
HouseClass::As_Pointer(Flagged)->Flag_Attach(Coord_Cell(Coord));
}
return(true);
}
return(false);
}
@ -3543,16 +3628,14 @@ ActionType UnitClass::What_Action(ObjectClass * object) const
/*
** Special return to friendly refinery action.
*/
if (IsOwnedByPlayer && 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 (IsOwnedByPlayer && 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 && !building->In_Radio_Contact() && !building->Is_Something_Attached()) {
action = ACTION_MOVE;

View file

@ -75,7 +75,7 @@ class UnitClass : public TarComClass
bool Goto_Clear_Spot(void);
bool Try_To_Deploy(void);
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);
void Find_LZ(void);
@ -125,6 +125,7 @@ class UnitClass : public TarComClass
virtual ActionType What_Action(ObjectClass * 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_NONE, TARGET destination=TARGET_NONE);
virtual void Response_Select(void);
virtual void Response_Move(void);
virtual void Response_Attack(void);

View file

@ -4841,7 +4841,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/TIBERIANDAWN/WIN32LIB/DrawMisc.cpp#33 $
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/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 **
;***************************************************************************

View file

@ -321,7 +321,7 @@ int __cdecl Desired_Facing8(long x1, long y1, long x2, long y2);
/*
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/WIN32LIB/FACINGFF.h#33 $
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/WIN32LIB/FACINGFF.h#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 **
;***************************************************************************