Merge pull request #1 from electronicarts/master

merging upstream
This commit is contained in:
David Amador 2022-05-28 08:33:23 +01:00 committed by GitHub
commit 7dd96e38b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
407 changed files with 84520 additions and 927 deletions

View file

@ -337,7 +337,7 @@ static AnimTypeClass const LZSmoke(
72, // Loop start frame number.
91, // Ending frame of loop back.
-1, // Number of animation stages.
255, // Number of times the animation loops.
127, // Number of times the animation loops.
VOC_NONE, // Sound effect to play.
ANIM_NONE
);
@ -1427,7 +1427,7 @@ static AnimTypeClass const OilFieldBurn(
33, // Loop start frame number.
99, // Ending frame of loop back.
66, // Number of animation stages.
65535, // Number of times the animation loops.
127, // Number of times the animation loops.
VOC_NONE, // Sound effect to play.
ANIM_NONE
);
@ -2360,6 +2360,81 @@ 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
);
static AnimTypeClass const Beacon(
ANIM_BEACON, // Animation number.
"MOVEFLSH", // 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,
-1, // Virtual stages
0x100, // Virtual scale
ANIM_BEACON_VIRTUAL // Virtual anim
);
static AnimTypeClass const BeaconVirtual(
ANIM_BEACON_VIRTUAL, // Animation number.
"BEACON", // 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,10 +2534,13 @@ AnimTypeClass const * const AnimTypeClass::Pointers[ANIM_COUNT] = {
&StegDie,
&RaptDie,
&ChemBall,
&Flag,
&Beacon,
&Fire3Virtual,
&Fire2Virtual,
&Fire1Virtual,
&Fire4Virtual
&Fire4Virtual,
&BeaconVirtual
};
@ -2502,7 +2580,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;
@ -2545,6 +2623,9 @@ void AnimTypeClass::One_Time(void)
((void const *&)As_Reference(index).ImageData) = MixFileClass::Retrieve(fullname);
}
}
// Set up beacon image data manually since they're new animations only available in the virtual renderer
((void const *&)As_Reference(ANIM_BEACON_VIRTUAL).ImageData) = As_Reference(ANIM_BEACON).ImageData;
}

View file

@ -245,6 +245,7 @@ AircraftClass::AircraftClass(AircraftType classid, HousesType house) :
NavCom = TARGET_NONE;
SecondaryFacing = PrimaryFacing;
Jitter = 0;
ReinforcementStart = -1;
/*
** Keep count of the number of units created. Dont track cargo planes as they are created
@ -346,6 +347,14 @@ void AircraftClass::Draw_It(int x, int y, WindowNumberType window)
int shapenum = 0;
int facing = Facing_To_32(SecondaryFacing);
/*
** Don't draw Cargo aircraft that are delayed.
*/
if (Special.ModernBalance) {
if (*this == AIRCRAFT_CARGO && !Map.In_Radar(Coord_Cell(Coord)) && ReinforcementStart > Frame) {
return;
}
}
/*
** Verify the legality of the unit class.
@ -607,6 +616,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,
@ -739,7 +752,17 @@ void AircraftClass::AI(void)
Mark();
}
}
if (Physics(Coord, PrimaryFacing) != RESULT_NONE) {
/*
** Handle reinforcement delay.
*/
bool do_physics = true;
if (Special.ModernBalance) {
if (*this == AIRCRAFT_CARGO && !Map.In_Radar(Coord_Cell(Coord)) && ReinforcementStart > Frame) {
do_physics = false;
}
}
if (do_physics && Physics(Coord, PrimaryFacing) != RESULT_NONE) {
Mark();
}
@ -1395,9 +1418,13 @@ int AircraftClass::Mission_Retreat(void)
{
Validate();
if (Class->IsFixedWing) {
if (Class->IsFixedWing && Altitude < FLIGHT_LEVEL) {
if (*this == AIRCRAFT_CARGO) {
PrimaryFacing.Set_Desired(DIR_W);
SecondaryFacing.Set_Desired(PrimaryFacing.Desired());
}
if (Altitude < FLIGHT_LEVEL) {
Altitude++;
return(3);
return(1);
}
return(TICKS_PER_SECOND*10);
}
@ -1600,10 +1627,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:
@ -1867,15 +1899,24 @@ void AircraftClass::Enter_Idle_Mode(bool )
} else {
/*
** Normal aircraft try to find a good landing spot to rest.
** Continue with the current helipad if there is one.
*/
BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false);
Assign_Destination(TARGET_NONE);
if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) {
mission = MISSION_ENTER;
if (!In_Radio_Contact() ||
Contact_With_Whom()->What_Am_I() != RTTI_BUILDING ||
*((BuildingClass*)Contact_With_Whom()) != STRUCT_HELIPAD) {
/*
** Normal aircraft try to find a good landing spot to rest.
*/
BuildingClass * building = Find_Docking_Bay(STRUCT_HELIPAD, false);
Assign_Destination(TARGET_NONE);
if (building && Transmit_Message(RADIO_HELLO, building) == RADIO_ROGER) {
mission = MISSION_ENTER;
} else {
Assign_Destination(Good_LZ());
mission = MISSION_MOVE;
}
} else {
Assign_Destination(Good_LZ());
mission = MISSION_MOVE;
mission = MISSION_ENTER;
}
}
} else {
@ -2166,7 +2207,7 @@ ActionType AircraftClass::What_Action(CELL cell) const
ActionType action = FootClass::What_Action(cell);
//using function for IsVisible so we have different results for different players - JAS 2019/09/30
if (action == ACTION_MOVE && !Map[cell].Is_Visible(PlayerPtr)) {
if ((action == ACTION_MOVE || action == ACTION_ATTACK) && !Map[cell].Is_Visible(PlayerPtr)) {
action = ACTION_NOMOVE;
}
@ -2336,6 +2377,11 @@ int AircraftClass::Mission_Attack(void)
return(1);
}
/*
** Clear second shot flag so fire burst works correctly.
*/
IsSecondShot = false;
PrimaryFacing.Set_Desired(Direction(TarCom));
SecondaryFacing.Set_Desired(Direction(TarCom));
switch (Can_Fire(TarCom, 0)) {
@ -2429,7 +2475,7 @@ int AircraftClass::Mission_Attack(void)
* HISTORY: *
* 06/19/1995 JLB : Created. *
*=============================================================================================*/
TARGET AircraftClass::New_LZ(TARGET oldlz) const
TARGET AircraftClass::New_LZ(TARGET oldlz, bool stable) const
{
Validate();
if (Target_Legal(oldlz) && (!Is_LZ_Clear(oldlz) || !Cell_Seems_Ok(As_Cell(oldlz)))) {
@ -2440,7 +2486,7 @@ TARGET AircraftClass::New_LZ(TARGET oldlz) const
** in cells.
*/
for (int radius = 0; radius < 16; radius++) {
FacingType modifier = Random_Pick(FACING_N, FACING_NW);
FacingType modifier = stable ? FACING_N : Random_Pick(FACING_N, FACING_NW);
CELL lastcell = -1;
/*
@ -2829,8 +2875,9 @@ TARGET AircraftClass::Good_Fire_Location(TARGET target) const
for (int face = 0; face < 255; face += 16) {
COORDINATE newcoord = Coord_Move(tcoord, (DirType)face, r);
CELL newcell = Coord_Cell(newcoord);
CELL actualcell = Coord_Cell(Coord_Sub(newcoord, XYPixel_Coord(0, FLIGHT_LEVEL)));
if (Map.In_Radar(newcell) && (GameToPlay != GAME_NORMAL || Map[newcell].Is_Visible(PlayerPtr)) && Cell_Seems_Ok(newcell, true)) {
if (Map.In_Radar(actualcell) && (GameToPlay != GAME_NORMAL || Map[newcell].Is_Visible(PlayerPtr)) && Cell_Seems_Ok(newcell, true)) {
int dist = Distance(newcoord);
if (bestval == -1 || dist < bestval) {
best2val = bestval;
@ -3536,4 +3583,9 @@ void AircraftClass::Response_Select(void)
if (AllowVoice) {
Sound_Effect(response, 0, -(Aircraft.ID(this)+1));
}
}
void AircraftClass::Set_Reinforcement_Delay(long delay)
{
ReinforcementStart = Frame + delay;
}

View file

@ -98,7 +98,7 @@ class AircraftClass : public FootClass, public FlyClass
** Landing zone support functionality.
*/
bool Is_LZ_Clear(TARGET target) const;
TARGET New_LZ(TARGET oldlz) const;
TARGET New_LZ(TARGET oldlz, bool stable = false) const;
/*
** Coordinate inquiry functions. These are used for both display and
@ -148,6 +148,7 @@ class AircraftClass : public FootClass, public FlyClass
virtual void Enter_Idle_Mode(bool initial = false);
virtual RadioMessageType Receive_Message(RadioClass * from, RadioMessageType message, long & param);
virtual void Scatter(COORDINATE threat, bool forced=false, bool nokidding=false);
void Set_Reinforcement_Delay(long delay);
/*
** Scenario and debug support.
@ -242,10 +243,15 @@ class AircraftClass : public FootClass, public FlyClass
*/
char AttacksRemaining;
/*
** Cargo planes will wait a certain number of ticks before flying in.
*/
long ReinforcementStart;
/*
** 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];
/*
** This contains the value of the Virtual Function Table Pointer

View file

@ -249,6 +249,8 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window)
int shapenum = Class->Start + Fetch_Stage();
void const * remap = NULL;
ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL;
int width = 0;
int height = 0;
/*
** Some animations require special fixups.
@ -270,6 +272,18 @@ 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;
case ANIM_BEACON_VIRTUAL:
width = 29;
height = 39;
flags = flags | SHAPE_BOTTOM | SHAPE_COMPACT;
break;
}
/*
@ -285,7 +299,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;
@ -293,7 +311,7 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window)
** Draw the animation shape, but ignore legacy if beyond normal stage count.
*/
if ((window == WINDOW_VIRTUAL) || (Fetch_Stage() < Class->Stages)) {
CC_Draw_Shape(this, shapefile, shapenum, x, y, window, flags, remap, transtable, Class->VirtualScale);
CC_Draw_Shape(this, shapefile, shapenum, x, y, window, flags, remap, transtable, Class->VirtualScale, width, height);
}
}
}
@ -380,6 +398,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 +439,9 @@ short const * AnimClass::Overlap_List(void) const
case ANIM_ATOM_BLAST:
return(OverlapAtom);
case ANIM_FLAG:
return(OverlapFlag);
default:
break;
}
@ -550,12 +573,13 @@ 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;
KillTime = 0ULL;
if (Class->Stages == -1) {
((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
@ -574,7 +598,7 @@ AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay
coord = Adjust_Coord(coord);
Unlimbo(coord);
VisibleFlags = 0xffff;
VisibleFlags = static_cast<unsigned int>(-1);
/*
** Drop zone smoke always reveals the map around itself.
@ -590,8 +614,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;
@ -609,7 +637,7 @@ AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay
** Check for a virtual animation
*/
if (Class->VirtualAnim != ANIM_NONE) {
VirtualAnim = new AnimClass(Class->VirtualAnim, 0, timedelay, loop, alt);
VirtualAnim = new AnimClass(Class->VirtualAnim, Coord, timedelay, loop, alt);
if (VirtualAnim != NULL) {
VirtualAnim->Make_Invisible();
}
@ -713,6 +741,19 @@ void AnimClass::AI(void)
IsToDelete = true;
}
/*
** Check the kill time.
*/
if (KillTime > 0ULL) {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
unsigned long long now = (unsigned long long)ft.dwLowDateTime + ((unsigned long long)ft.dwHighDateTime << 32ULL);
if (now >= KillTime) {
IsToDelete = true;
}
}
/*
** Delete this animation and bail early if the animation is flagged to be deleted
** immediately.
@ -732,6 +773,16 @@ void AnimClass::AI(void)
return;
}
/*
** Lazy-initialize animation data (for loaded saves).
*/
if (Class->Stages == -1) {
((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
}
if (Class->LoopEnd == -1) {
((int&)Class->LoopEnd) = Class->Stages;
}
if (Delay) {
Delay--;
if (!Delay) {
@ -769,10 +820,10 @@ void AnimClass::AI(void)
int damage = accum >> 8;
if (Object->Take_Damage(damage, 0, WARHEAD_FIRE) == RESULT_DESTROYED) {
//Object = 0;
Delete_This();
if (VirtualAnim != NULL) {
VirtualAnim->Delete_This();
}
Delete_This();
return;
}
}
@ -798,8 +849,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 +1043,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 +1159,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;
@ -1253,6 +1304,9 @@ void AnimClass::Detach(TARGET target, bool all)
if (all) {
if (VirtualAnim && VirtualAnim->As_Target() == target) {
VirtualAnim = NULL;
if (IsInvisible) {
IsToDelete = true;
}
}
if (Object && Object->As_Target() == target) {
Map.Remove(this, In_Which_Layer());
@ -1260,4 +1314,20 @@ void AnimClass::Detach(TARGET target, bool all)
IsToDelete = true;
}
}
}
void AnimClass::Set_Owner(HousesType owner)
{
OwnerHouse = owner;
if (VirtualAnim != NULL) {
VirtualAnim->Set_Owner(owner);
}
}
void AnimClass::Set_Visible_Flags(unsigned flags)
{
VisibleFlags = flags;
if (VirtualAnim != NULL) {
VirtualAnim->Set_Visible_Flags(flags);
}
}

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.
@ -62,14 +63,16 @@ class AnimClass : public ObjectClass, private StageClass {
void Sort_Above(TARGET target);
void Make_Invisible(void) {IsInvisible = true;};
void Make_Visible(void) {IsInvisible = false;};
void Kill_At(unsigned long long kill_time) {KillTime = kill_time;}
/*
** 2019/09/19 JAS
** Added functions for accessing which players can see this anim
*/
void Set_Visible_Flags(unsigned flags) { VisibleFlags = flags; }
void Set_Visible_Flags(unsigned flags);
unsigned Get_Visible_Flags() const { return (Delay == 0) ? VisibleFlags : 0; }
virtual void Set_Owner(HousesType owner);
virtual bool Can_Place_Here(COORDINATE ) const {return true;}
virtual bool Mark(MarkType mark=MARK_CHANGE);
virtual bool Render(bool forced);
@ -114,13 +117,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 +149,9 @@ class AnimClass : public ObjectClass, private StageClass {
*/
unsigned IsBrandNew:1;
// Use alternate color when drawing?
/*
** Use alternate color when drawing
*/
unsigned IsAlternate:1;
/*
@ -191,10 +196,15 @@ class AnimClass : public ObjectClass, private StageClass {
*/
AnimClass * VirtualAnim;
/*
** Real-time point to kill this animation.
*/
unsigned long long KillTime;
/*
** 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[24];
};

View file

@ -254,6 +254,7 @@ struct SoundEffectNameStruct {
{"DINOYES", 10, IN_NOVAR}, // VOC_DINOYES Yes Sir in dino-speak.
{"DINOATK1", 10, IN_NOVAR}, // VOC_DINOATK1 Dino attack sound.
{"DINODIE1", 10, IN_NOVAR}, // VOC_DINODIE1 Dino die sound.
{"BEACON", 10, IN_NOVAR }, // VOC_BEACON Beacon sound.
#ifdef PETROGLYPH_EXAMPLE_MOD
{"NUKE_LOB", 10, IN_NOVAR} // VOC_NUKE_LOB Mod expansion unit firing sound

View file

@ -234,7 +234,7 @@ static BuildingTypeClass const ClassEye(
false, // Always use the given name for the building?
false, // Is this a wall type structure?
false, // Is it a factory type building?
true, // Can this building be captured?
false, // Can this building be captured?
false, // Does it catch fire?
false, // Simple (one frame) damage imagery?
false, // Is it invisible to radar?

View file

@ -213,7 +213,7 @@ RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageT
case RADIO_CAN_LOAD:
TechnoClass::Receive_Message(from, message, param);
if (BState == BSTATE_CONSTRUCTION || (!ScenarioInit && In_Radio_Contact())) return(RADIO_NEGATIVE);
if (BState == BSTATE_CONSTRUCTION || (!ScenarioInit && Class->Type != STRUCT_REFINERY && In_Radio_Contact())) return(RADIO_NEGATIVE);
switch (Class->Type) {
case STRUCT_AIRSTRIP:
if (from->What_Am_I() == RTTI_AIRCRAFT && *((AircraftClass const *)from) == AIRCRAFT_CARGO) {
@ -238,7 +238,7 @@ RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageT
*((UnitClass *)from) == UNIT_HARVESTER &&
(ScenarioInit || !Is_Something_Attached())) {
return(RADIO_ROGER);
return((Contact_With_Whom() != from) ? RADIO_ROGER : RADIO_NEGATIVE);
}
break;
@ -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
@ -1141,7 +1166,9 @@ void BuildingClass::AI(void)
Repair(1);
} else {
if (IsTickedOff && (int)Scenario > 2 && Random_Pick(0, 50) < (int)Scenario && !Trigger) {
Sell_Back(1);
if (GameToPlay != GAME_NORMAL || Scenario != 15 || PlayerPtr->ActLike != HOUSE_GOOD || *this != STRUCT_TEMPLE) {
Sell_Back(1);
}
}
}
}
@ -1899,7 +1926,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 +1934,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 +1942,7 @@ void BuildingClass::Drop_Debris(TARGET source)
i->Assign_Mission(MISSION_GUARD_AREA);
} else {
delete i;
PlayerPtr->Flag_To_Win();
}
ScenarioInit--;
}
@ -2049,7 +2077,7 @@ void BuildingClass::Active_Click_With(ActionType action, CELL cell)
OutList.Add(EventClass(EventClass::SELL, As_Target()));
COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y());
OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord));
OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord, 1 << PlayerPtr->Class->House));
}
}
@ -2708,6 +2736,39 @@ bool BuildingClass::Limbo(void)
* HISTORY: *
* 12/24/1994 JLB : Created. *
*=============================================================================================*/
FireDataType BuildingClass::Fire_Data(int ) const
{
Validate();
COORDINATE coord = Center_Coord();
int dist = 0;
/*
** Make adjustments to the firing coordinate to account for turret
** position. This depends on building type and turret facing.
*/
switch (Class->Type) {
default:
case STRUCT_GTOWER:
case STRUCT_ATOWER:
coord = Coord_Move(coord, DIR_N, 0x0030);
dist = 0x0040;
break;
case STRUCT_OBELISK:
coord = Coord_Move(coord, DIR_N, 0x00A8);
coord = Coord_Move(coord, DIR_W, 0x0018);
break;
case STRUCT_SAM:
case STRUCT_TURRET:
coord = Coord_Move(coord, DIR_N, 0x0030);
dist = 0x0080;
break;
}
return{coord,dist};
}
COORDINATE BuildingClass::Fire_Coord(int ) const
{
Validate();
@ -3493,8 +3554,14 @@ bool BuildingClass::Toggle_Primary(void)
}
}
IsLeader = true;
if (House == PlayerPtr) {
Speak(VOX_PRIMARY_SELECTED);
//
// MBL 07.22.2020 - Update so that each player in multiplayer will properly hear this when it applies to them
//
// if (House == PlayerPtr) {
// Speak(VOX_PRIMARY_SELECTED);
// }
if ((HouseClass *)House->IsHuman) {
Speak(VOX_PRIMARY_SELECTED, House);
}
}
Mark(MARK_CHANGE);
@ -3523,7 +3590,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 +3870,21 @@ bool BuildingClass::Can_Demolish_Unit(void) const
}
bool BuildingClass::Can_Capture(void) const
{
bool can_capture = Class->IsCaptureable && Mission != MISSION_DECONSTRUCTION;
// Override capturable state if this building has a capture win trigger
if (GameToPlay == GAME_NORMAL) {
if (!House->IsHuman && Trigger != NULL && Trigger->Action == TriggerClass::ACTION_WINLOSE) {
can_capture = true;
}
}
return(can_capture);
}
/***********************************************************************************************
* BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. *
* *
@ -4052,11 +4134,18 @@ int BuildingClass::Mission_Deconstruction(void)
}
}
//Changed for multiplayer ST - 3/13/2019 5:31PM
if (Is_Owned_By_Player()) {
//if (IsOwnedByPlayer) {
// MBL 07.10.2020 - In 1v1, sometimes both players will hear this SFX, or neither player will hear it
// Making it so all players hear it positionally in the map; Per thread discussion in https://jaas.ea.com/browse/TDRA-7245
//
#if 0
//Changed for multiplayer ST - 3/13/2019 5:31PM
if (Is_Owned_By_Player()) {
//if (IsOwnedByPlayer) {
Sound_Effect(VOC_CASHTURN, Coord);
}
#else
Sound_Effect(VOC_CASHTURN, Coord);
}
#endif
/*
** Destroy all attached objects. ST - 4/24/2020 9:38PM
@ -4072,6 +4161,7 @@ int BuildingClass::Mission_Deconstruction(void)
Status = DURING;
Begin_Mode(BSTATE_CONSTRUCTION);
IsReadyToCommence = false;
IsSurvivorless = true;
break;
}
Transmit_Message(RADIO_RUN_AWAY);
@ -4516,6 +4606,11 @@ int BuildingClass::Mission_Repair(void)
if (Transmit_Message(RADIO_NEED_TO_MOVE) == RADIO_ROGER) {
if (Contact_With_Whom()->Health_Ratio() < 0x0100 && Transmit_Message(RADIO_REPAIR) == RADIO_ROGER) {
// MBL 07.06.2020 - Patch 3: Change to TD Legacy: Adding "Repairing" VO for units on repair bay
// Per https://jaas.ea.com/browse/TDRA-7271
if (IsOwnedByPlayer && House) Speak(VOX_REPAIRING, House);
Status = DURING;
Begin_Mode(BSTATE_ACTIVE);
IsReadyToCommence = false;
@ -5020,7 +5115,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;
@ -195,6 +196,7 @@ class BuildingClass : public TechnoClass
** combat purposes.
*/
virtual COORDINATE Docking_Coord(void) const;
virtual FireDataType Fire_Data(int which) const;
virtual COORDINATE Fire_Coord(int which) const;
virtual COORDINATE Center_Coord(void) const;
virtual COORDINATE Sort_Y(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->Set_Owner(Payback->House->Class->House);
}
}
}

View file

@ -1824,7 +1824,7 @@ static TemplateTypeClass const Patch15(
"P15",
TXT_PATCH,
LAND_CLEAR,
1,1,
4,2,
LAND_CLEAR,
NULL
);

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);
}
@ -389,7 +389,9 @@ void CellClass::Redraw_Objects(bool forced)
if (Cell_Occupier()) {
ObjectClass * optr = Cell_Occupier();
while (optr) {
optr->Mark(MARK_CHANGE);
if (optr->IsActive) {
optr->Mark(MARK_CHANGE);
}
optr = optr->Next;
}
}
@ -1132,14 +1134,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 +1196,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 +1393,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 +1403,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 +1545,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 +1812,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 +1905,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++;
}
}
@ -1944,7 +1938,7 @@ long CellClass::Tiberium_Adjust(bool pregame)
* 05/22/1995 JLB : Created. *
* 07/08/1995 JLB : Added a bunch of goodies to the crates. *
*=============================================================================================*/
bool CellClass::Goodie_Check(FootClass * object)
bool CellClass::Goodie_Check(FootClass * object, bool check_steel)
{
Validate();
enum {
@ -1998,21 +1992,30 @@ bool CellClass::Goodie_Check(FootClass * object)
bool steel = (Overlay == OVERLAY_STEEL_CRATE);
COORDINATE coord; // Temporary working coordinate value.
/*
** A triggered crate is automatically destroyed regardless of who or how
** it was triggered.
*/
Redraw_Objects();
Overlay = OVERLAY_NONE;
OverlayData = 0;
if (check_steel && steel) {
/*
** A triggered crate is automatically destroyed regardless of who or how
** it was triggered.
*/
Redraw_Objects();
Overlay = OVERLAY_NONE;
OverlayData = 0;
if (steel) {
if (object->Owner() == HOUSE_BAD) {
object->House->Add_Nuke_Piece();
new AnimClass(ANIM_CRATE_EMPULSE, Cell_Coord());
}
} else {
} else if(!check_steel && !steel) {
/*
** A triggered crate is automatically destroyed regardless of who or how
** it was triggered.
*/
Redraw_Objects();
Overlay = OVERLAY_NONE;
OverlayData = 0;
int index;
UnitClass * unit = 0;
@ -2386,6 +2389,7 @@ bool CellClass::Flag_Place(HousesType house)
if (!IsFlagged && Is_Generally_Clear()) {
IsFlagged = true;
Owner = house;
Flag_Update();
Redraw_Objects();
return(true);
}
@ -2413,6 +2417,7 @@ bool CellClass::Flag_Remove(void)
if (IsFlagged) {
IsFlagged = false;
Owner = HOUSE_NONE;
Flag_Update();
Redraw_Objects();
return(true);
}
@ -2420,6 +2425,43 @@ 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 == NULL) {
for (int i = 0; i < Anims.Count(); ++i) {
AnimClass* anim = Anims.Ptr(i);
if (*anim != ANIM_FLAG) {
anim->Delete_This();
break;
}
}
CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord(), 0, 1, true);
}
assert(CTFFlag != NULL);
CTFFlag->Set_Owner(Owner);
}
}
void CellClass::Flag_Destroy(void)
{
delete CTFFlag;
CTFFlag = NULL;
}
/***********************************************************************************************
* CellClass::Shimmer -- Causes all objects in the cell to shimmer. *
* *

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;
@ -208,7 +208,7 @@ class CellClass
InfantryClass * Cell_Infantry(void) const;
TriggerClass * Get_Trigger(void) const;
int Clear_Icon(void) const;
bool Goodie_Check(FootClass * object);
bool Goodie_Check(FootClass * object, bool check_steel = false);
ObjectClass * Fetch_Occupier(void) const;
bool Get_Template_Info(char *template_name, int &icon, void *&image_data);
@ -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

@ -2730,11 +2730,11 @@ extern void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int he
extern void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip);
extern void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame);
void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, int scale)
void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, int scale, int width, int height)
{
if (window == WINDOW_VIRTUAL) {
int width = Get_Build_Frame_Width(shapefile);
int height = Get_Build_Frame_Height(shapefile);
if (width == 0) width = Get_Build_Frame_Width(shapefile);
if (height == 0) height = Get_Build_Frame_Height(shapefile);
DLL_Draw_Intercept(shapenum, x, y, width, height, (int)flags, object, NULL, -1, scale);
return;
}
@ -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,11 +1509,14 @@ typedef enum AnimType : char {
ANIM_STEG_DIE,
ANIM_RAPT_DIE,
ANIM_CHEM_BALL, // Chemical warrior explosion.
ANIM_FLAG, // CTF flag.
ANIM_BEACON, // Beacon.
ANIM_FIRE_SMALL_VIRTUAL, // Small flame animation (virtual).
ANIM_FIRE_MED_VIRTUAL, // Medium flame animation (virtual).
ANIM_FIRE_MED2_VIRTUAL, // Medium flame animation (oranger) (virtual).
ANIM_FIRE_TINY_VIRTUAL, // Very tiny flames (virtual).
ANIM_FIRE_SMALL_VIRTUAL, // Small flame animation (virtual).
ANIM_FIRE_MED_VIRTUAL, // Medium flame animation (virtual).
ANIM_FIRE_MED2_VIRTUAL, // Medium flame animation (oranger) (virtual).
ANIM_FIRE_TINY_VIRTUAL, // Very tiny flames (virtual).
ANIM_BEACON_VIRTUAL, // Beacon (virtual).
ANIM_COUNT,
ANIM_FIRST=0
@ -1630,6 +1633,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;
@ -2305,6 +2311,8 @@ typedef enum VocType : char{
VOC_DINOATK1, // Dino attack sound.
VOC_DINODIE1, // Dino die sound.
VOC_BEACON, // Beacon sound.
#ifdef PETROGLYPH_EXAMPLE_MOD
VOC_NUKE_LOB, // Modded unit firing sound
#endif
@ -2843,6 +2851,15 @@ typedef enum OptionControlType : char {
} OptionControlType;
/****************************************************************************
** Used to store firing data for a unit.
*/
typedef struct {
COORDINATE Center;
int Distance;
} FireDataType;
#define TOTAL_CRATE_TYPES 15
#define size_of(typ,id) sizeof(((typ*)0)->id)

View file

@ -1107,7 +1107,7 @@ void DisplayClass::Cursor_Mark(CELL pos, bool on)
CELL const *ptr;
CellClass *cellptr;
if (pos == -1) return;
if ((unsigned)pos >= MAP_CELL_TOTAL) return;
/*
** For every cell in the CursorSize list, invoke its Redraw_Objects and
@ -1810,8 +1810,9 @@ bool DisplayClass::Map_Cell(CELL cell, HouseClass * house, bool and_for_allies)
/*
** Maybe also recurse to map for allies
*/
if (ShareAllyVisibility && and_for_allies && GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
for (HousesType house_type = HOUSE_MULTI1; house_type < HOUSE_COUNT; house_type++) {
if (ShareAllyVisibility && and_for_allies) {
HousesType first_house = (GameToPlay == GAME_NORMAL) ? HOUSE_FIRST : HOUSE_MULTI1;
for (HousesType house_type = first_house; house_type < HOUSE_COUNT; house_type++) {
HouseClass *hptr = HouseClass::As_Pointer(house_type);
if (hptr && hptr->IsActive) {
if (hptr != house && house->Is_Ally(hptr)) {
@ -2964,6 +2965,7 @@ void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2, bool addit
*/
if ( obj->Class_Of().IsSelectable &&
obj->What_Am_I() != RTTI_BUILDING &&
(!obj->Is_Techno() || !((TechnoClass*)obj)->Is_Cloaked(PlayerPtr)) &&
x >= x1 && x <= x2 && y >= y1 && y <= y2) {
bool old_allow_voice = AllowVoice;
bool is_player_controlled = obj->Owner() == PlayerPtr->Class->House;
@ -2991,6 +2993,7 @@ void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2, bool addit
** selected, and are within the bounding box.
*/
if ( aircraft->Class_Of().IsSelectable &&
!aircraft->Is_Cloaked(PlayerPtr) &&
!aircraft->Is_Selected_By_Player() &&
x >= x1 && x <= x2 && y >= y1 && y <= y2) {
bool old_allow_voice = AllowVoice;
@ -3870,12 +3873,17 @@ void DisplayClass::Mouse_Left_Release(CELL cell, int x, int y, ObjectClass * obj
AllowVoice = true;
for (int index = 0; index < CurrentObject.Count(); index++) {
ObjectClass * tobject = CurrentObject[index];
ActionType action = ACTION_NONE;
if (object) {
tobject->Active_Click_With(tobject->What_Action(object), object);
action = tobject->What_Action(object);
tobject->Active_Click_With(action, object);
} else {
tobject->Active_Click_With(tobject->What_Action(cell), cell);
action = tobject->What_Action(cell);
tobject->Active_Click_With(action, cell);
}
if (action != ACTION_NONE) {
AllowVoice = false;
}
AllowVoice = false;
}
AllowVoice = true;

View file

@ -106,7 +106,7 @@ typedef enum {
*/
#define RANDOM_START_POSITION 0x7f
#define KILL_PLAYER_ON_DISCONNECT 1
@ -140,6 +140,7 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(Sidebar
extern "C" __declspec(dllexport) void __cdecl CNC_Handle_SuperWeapon_Request(SuperWeaponRequestEnum request_type, uint64 player_id, int buildable_type, int buildable_id, int x1, int y1);
extern "C" __declspec(dllexport) void __cdecl CNC_Handle_ControlGroup_Request(ControlGroupRequestEnum request_type, uint64 player_id, unsigned char control_group_index);
extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Debug_Request(DebugRequestEnum debug_request_type, uint64 player_id, const char *object_name, int x, int y, bool unshroud, bool enemy);
extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Beacon_Request(BeaconRequestEnum beacon_request_type, uint64 player_id, int pixel_x, int pixel_y);
extern "C" __declspec(dllexport) bool __cdecl CNC_Set_Multiplayer_Data(int scenario_index, CNCMultiplayerOptionsStruct &game_options, int num_players, CNCPlayerInfoStruct *player_list, int max_players);
extern "C" __declspec(dllexport) bool __cdecl CNC_Clear_Object_Selection(uint64 player_id);
extern "C" __declspec(dllexport) bool __cdecl CNC_Select_Object(uint64 player_id, int object_type_id, int object_to_select_id);
@ -148,7 +149,7 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Set_Difficulty(int difficulty)
extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uint64 player_id);
extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Human_Team_Wins(uint64 player_id);
extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time);
extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Start_Game_Info(uint64 player_id, int &start_location_waypoint_index);
/*
@ -296,6 +297,7 @@ class DLLExportClass {
static int CurrentDrawCount;
static int TotalObjectCount;
static int SortOrder;
static int ExportLayer;
static CNCObjectListStruct *ObjectList;
static CNC_Event_Callback_Type EventCallback;
@ -337,6 +339,7 @@ class DLLExportClass {
int DLLExportClass::CurrentDrawCount = 0;
int DLLExportClass::TotalObjectCount = 0;
int DLLExportClass::SortOrder = 0;
int DLLExportClass::ExportLayer = 0;
CNCObjectListStruct *DLLExportClass::ObjectList = NULL;
SidebarGlyphxClass DLLExportClass::MultiplayerSidebars [MAX_PLAYERS];
uint64 DLLExportClass::GlyphxPlayerIDs[MAX_PLAYERS] = {0xffffffffl};
@ -388,14 +391,13 @@ void Play_Movie_GlyphX(const char * movie_name, ThemeType theme)
void On_Sound_Effect(int sound_index, int variation, COORDINATE coord)
{
// MBL 02.26.2019
int voc = sound_index;
if (voc == VOC_NONE)
{
return;
}
// MBL 02.26.2019 - Borrowed from AUDIO.CPP Sound_Effect()
// Borrowed from AUDIO.CPP Sound_Effect()
//
#if 1
char const * ext = ""; // ".AUD";
@ -423,16 +425,12 @@ void On_Sound_Effect(int sound_index, int variation, COORDINATE coord)
}
}
#endif
// END MBL
DLLExportClass::On_Sound_Effect(PlayerPtr, sound_index, ext, variation, coord);
}
// MBL 02.06.2020
// void On_Speech(int speech_index)
void On_Speech(int speech_index, HouseClass *house)
{
// DLLExportClass::On_Speech(PlayerPtr, speech_index); // MBL 02.06.2020
if (house == NULL) {
DLLExportClass::On_Speech(PlayerPtr, speech_index);
}
@ -502,7 +500,6 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Init(const char *command_line,
DLL_Startup(command_line);
// MBL
DLLExportClass::Set_Event_Callback( event_callback );
DLLExportClass::Init();
@ -672,6 +669,7 @@ extern "C" __declspec(dllexport) bool __cdecl CNC_Set_Multiplayer_Data(int scena
Special.IsVisceroids = game_options.SpawnVisceroids;
Special.IsCaptureTheFlag = game_options.CaptureTheFlag;
Special.IsEarlyWin = game_options.DestroyStructures;
Special.ModernBalance = game_options.ModernBalance;
Rule.AllowSuperWeapons = game_options.EnableSuperweapons; // Are superweapons available
@ -876,7 +874,7 @@ void GlyphX_Assign_Houses(void)
preassigned = true;
}
}
if (!preassigned) {
if (!preassigned && i < MAX_PLAYERS) {
random_start_locations[num_random_start_locations] = num_start_locations;
num_random_start_locations++;
}
@ -1314,7 +1312,9 @@ extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Custom_Instance(const ch
Clear_Scenario();
Read_Scenario_Ini_File(scenario_file_name, bin_file_name, scenario_name, true);
if (!Read_Scenario_Ini_File(scenario_file_name, bin_file_name, scenario_name, true)) {
return false;
}
HiddenPage.Clear();
VisiblePage.Clear();
@ -1329,6 +1329,10 @@ extern "C" __declspec(dllexport) bool __cdecl CNC_Start_Custom_Instance(const ch
GlyphXClientSidebarWidthInLeptons = 0;
Play_Movie(IntroMovie);
Play_Movie(BriefMovie);
Play_Movie(ActionMovie, TransitTheme);
/*
if (!Start_Scenario(ScenarioName)) {
return(false);
@ -1695,8 +1699,14 @@ extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const cha
}
result = Load_Game(file_path_and_name);
if (result == false)
{
return false;
}
DLLExportClass::Set_Player_Context(DLLExportClass::GlyphxPlayerIDs[0], true);
DLLExportClass::Cancel_Placement(DLLExportClass::GlyphxPlayerIDs[0], -1, -1);
Set_Logic_Page(SeenBuff);
VisiblePage.Clear();
Map.Flag_To_Redraw(true);
@ -1747,9 +1757,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 +1764,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 +1829,9 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Player_Switch_To_AI(uin
}
}
}
#endif //KILL_PLAYER_ON_DISCONNECT
}
@ -1830,6 +1859,24 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Start_Mission_Timer(int time)
/**************************************************************************************************
* CNC_Get_Start_Game_Info
*
* History: 8/31/2020 11:37AM - ST
**************************************************************************************************/
extern "C" __declspec(dllexport) bool __cdecl CNC_Get_Start_Game_Info(uint64 player_id, int &start_location_waypoint_index)
{
start_location_waypoint_index = 0;
if (!DLLExportClass::Set_Player_Context(player_id)) {
return false;
}
start_location_waypoint_index = PlayerPtr->StartLocationOverride;
return true;
}
/**************************************************************************************************
* DLLExportClass::Init -- Init the class
*
@ -2489,18 +2536,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);
}
}
}
@ -2911,6 +2966,7 @@ void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color,
void DLLExportClass::DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, ObjectClass *object, const char *shape_file_name, char override_owner, int scale)
{
CNCObjectStruct& new_object = ObjectList->Objects[TotalObjectCount + CurrentDrawCount];
memset(&new_object, 0, sizeof(new_object));
Convert_Type(object, new_object);
if (new_object.Type == UNKNOWN) {
return;
@ -2927,7 +2983,11 @@ void DLLExportClass::DLL_Draw_Intercept(int shape_number, int x, int y, int widt
new_object.CNCInternalObjectPointer = (void*)object;
new_object.OccupyListLength = 0;
new_object.SortOrder = SortOrder++;
if (CurrentDrawCount == 0) {
new_object.SortOrder = (ExportLayer << 29) + (object->Sort_Y() >> 3);
} else {
new_object.SortOrder = ObjectList->Objects[TotalObjectCount].SortOrder + CurrentDrawCount;
}
strncpy(new_object.TypeName, object->Class_Of().IniName, CNC_OBJECT_ASSET_NAME_LENGTH);
@ -3295,6 +3355,8 @@ bool DLLExportClass::Get_Layer_State(uint64 player_id, unsigned char *buffer_in,
*/
for (int layer = 0; layer < DLL_LAYER_COUNT; layer++) {
ExportLayer = layer;
for (int index = 0; index < Map.Layer[layer].Count(); index++) {
ObjectClass *object = Map.Layer[layer][index];
@ -3637,6 +3699,31 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Input(InputRequestEnum
break;
}
// MBL 09.08.2020 - Mod Support
case INPUT_REQUEST_MOD_GAME_COMMAND_1_AT_POSITION:
case INPUT_REQUEST_MOD_GAME_COMMAND_2_AT_POSITION:
case INPUT_REQUEST_MOD_GAME_COMMAND_3_AT_POSITION:
case INPUT_REQUEST_MOD_GAME_COMMAND_4_AT_POSITION:
{
DLLExportClass::Adjust_Internal_View();
DLLForceMouseX = x1;
DLLForceMouseY = y1;
_Kbd->MouseQX = x1;
_Kbd->MouseQY = y1;
COORDINATE coord = Map.Pixel_To_Coord(x1, y1);
CELL cell = Coord_Cell(coord);
if (Map.Pixel_To_Coord(x1, y1))
{
// TBD: For our ever-awesome Community Modders!
//
// PlayerPtr->Handle_Mod_Game_Command(cell, input_event - INPUT_REQUEST_MOD_GAME_COMMAND_1_AT_POSITION);
}
break;
}
default:
break;
}
@ -3761,6 +3848,10 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(Sidebar
switch (request_type) {
// 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;
@ -3981,6 +4072,8 @@ bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_i
if ((entry_index + 1) * sizeof(CNCSidebarEntryStruct) + memory_needed > buffer_size) {
return false;
}
memset(&sidebar_entry, 0, sizeof(sidebar_entry));
sidebar_entry.AssetName[0] = 0;
sidebar_entry.Type = UNKNOWN;
@ -4160,6 +4253,8 @@ bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_i
return false;
}
memset(&sidebar_entry, 0, sizeof(sidebar_entry));
sidebar_entry.AssetName[0] = 0;
sidebar_entry.Type = UNKNOWN;
sidebar_entry.BuildableID = context_sidebar->Column[c].Buildables[b].BuildableID;
@ -4321,6 +4416,8 @@ bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_i
}
static const int _map_width_shift_bits = 6;
void DLLExportClass::Calculate_Placement_Distances(BuildingTypeClass* placement_type, unsigned char* placement_distance)
{
int map_cell_x = Map.MapCellX;
@ -4349,7 +4446,7 @@ void DLLExportClass::Calculate_Placement_Distances(BuildingTypeClass* placement_
memset(placement_distance, 255U, MAP_CELL_TOTAL);
for (int y = 0; y < map_cell_height; y++) {
for (int x = 0; x < map_cell_width; x++) {
CELL cell = (CELL)map_cell_x + x + ((map_cell_y + y) << 6);
CELL cell = (CELL)map_cell_x + x + ((map_cell_y + y) << _map_width_shift_bits);
BuildingClass* base = (BuildingClass*)Map[cell].Cell_Find_Object(RTTI_BUILDING);
if ((base && base->House->Class->House == PlayerPtr->Class->House) || (Map[cell].Owner == PlayerPtr->Class->House)) {
placement_distance[cell] = 0U;
@ -4440,7 +4537,7 @@ bool DLLExportClass::Get_Placement_State(uint64 player_id, unsigned char *buffer
for (int y=0 ; y < map_cell_height ; y++) {
for (int x=0 ; x < map_cell_width ; x++) {
CELL cell = (CELL) map_cell_x + x + ((map_cell_y + y) << 6);
CELL cell = (CELL) map_cell_x + x + ((map_cell_y + y) << _map_width_shift_bits);
bool pass = Passes_Proximity_Check(cell, PlacementType[CurrentLocalPlayerIndex], PlacementDistance[CurrentLocalPlayerIndex]);
@ -5101,7 +5198,7 @@ Map.Passes_Proximity_Check
map_cell_height++;
}
CELL cell = (CELL) (map_cell_x + cell_x) + ( (map_cell_y + cell_y) << 6 );
CELL cell = (CELL) (map_cell_x + cell_x) + ( (map_cell_y + cell_y) << _map_width_shift_bits );
/*
** Call the place directly instead of queueing it, so we can evaluate the return code.
@ -6627,8 +6724,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));
}
}
}
}
@ -6792,6 +6893,39 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Debug_Request(DebugRequ
}
extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Beacon_Request(BeaconRequestEnum beacon_request_type, uint64 player_id, int pixel_x, int pixel_y)
{
if (!DLLExportClass::Set_Player_Context(player_id)) {
return;
}
// Beacons are only available if legacy rendering is disabled
if (DLLExportClass::Legacy_Render_Enabled()) {
return;
}
// Only allow one beacon per player
for (int index = 0; index < Anims.Count(); ++index) {
AnimClass* anim = Anims.Ptr(index);
if (anim != NULL &&
(*anim == ANIM_BEACON || *anim == ANIM_BEACON_VIRTUAL) &&
anim->OwnerHouse == PlayerPtr->Class->House) {
delete anim;
}
}
OutList.Add(EventClass(ANIM_BEACON, PlayerPtr->Class->House, Map.Pixel_To_Coord(pixel_x, pixel_y), PlayerPtr->Get_Allies()));
// Send sound effect to allies
for (int index = 0; index < Houses.Count(); ++index) {
HouseClass* hptr = Houses.Ptr(index);
if (hptr != NULL && hptr->IsActive && hptr->IsHuman && PlayerPtr->Is_Ally(hptr)) {
DLLExportClass::On_Sound_Effect(hptr, VOC_BEACON, "", 0, 0);
}
}
}
/**************************************************************************************************
* DLLExportClass::Debug_Spawn_All -- Debug spawn all buildable units and structures
*
@ -7158,7 +7292,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++) {
@ -7468,10 +7602,18 @@ bool DLLExportClass::Save(FileClass & file)
return false;
}
/*
** Special case for Rule.AllowSuperWeapons - store negated value so it defaults to enabled
*/
bool not_allow_super_weapons = !Rule.AllowSuperWeapons;
if (file.Write(&not_allow_super_weapons, sizeof(not_allow_super_weapons)) != sizeof(not_allow_super_weapons)) {
return false;
}
/*
** Room for save game expansion
*/
unsigned char padding[4096];
unsigned char padding[4095];
memset(padding, 0, sizeof(padding));
if (file.Write(padding, sizeof(padding)) != sizeof(padding)) {
@ -7586,7 +7728,16 @@ bool DLLExportClass::Load(FileClass & file)
return false;
}
unsigned char padding[4096];
/*
** Special case for Rule.AllowSuperWeapons - store negated value so it defaults to enabled
*/
bool not_allow_super_weapons = false;
if (file.Read(&not_allow_super_weapons, sizeof(not_allow_super_weapons)) != sizeof(not_allow_super_weapons)) {
return false;
}
Rule.AllowSuperWeapons = !not_allow_super_weapons;
unsigned char padding[4095];
if (file.Read(padding, sizeof(padding)) != sizeof(padding)) {
return false;

View file

@ -28,7 +28,7 @@ struct CarryoverObjectStruct;
**
**
*/
#define CNC_DLL_API_VERSION 0x100
#include "DLLInterfaceVersion.h"
@ -421,7 +421,11 @@ enum InputRequestEnum {
INPUT_REQUEST_SELL_AT_POSITION,
INPUT_REQUEST_SELECT_AT_POSITION,
INPUT_REQUEST_COMMAND_AT_POSITION,
INPUT_REQUEST_SPECIAL_KEYS
INPUT_REQUEST_SPECIAL_KEYS,
INPUT_REQUEST_MOD_GAME_COMMAND_1_AT_POSITION,
INPUT_REQUEST_MOD_GAME_COMMAND_2_AT_POSITION,
INPUT_REQUEST_MOD_GAME_COMMAND_3_AT_POSITION,
INPUT_REQUEST_MOD_GAME_COMMAND_4_AT_POSITION,
};
@ -472,6 +476,18 @@ enum GameRequestEnum {
};
/**************************************************************************************
**
** Beacon Requests
**
**
*/
enum BeaconRequestEnum {
INPUT_BEACON_NONE,
INPUT_BEACON_PLACE,
};
/**************************************************************************************
**
@ -623,6 +639,7 @@ struct EventCallbackStruct {
//
// Single-player data
//
bool IsHuman;
bool PlayerWins;
const char* MovieName;
const char* MovieName2;
@ -727,7 +744,7 @@ struct CNCMultiplayerOptionsStruct {
int MPlayerCount; // # of human players in this game
int MPlayerBases; // 1 = bases are on for this scenario
int MPlayerCredits; // # credits everyone gets
int MPlayerTiberium; // 1 = tiberium enabled for this scenario
int MPlayerTiberium; // >0 = tiberium enabled for this scenario
int MPlayerGoodies; // 1 = goodies enabled for this scenario
int MPlayerGhosts; // 1 = houses with no players will still play
int MPlayerSolo; // 1 = allows a single-player net game
@ -739,6 +756,7 @@ struct CNCMultiplayerOptionsStruct {
bool MPlayerAftermathUnits;
bool CaptureTheFlag;
bool DestroyStructures; // New early win condition via destroying all a player's structures
bool ModernBalance;
};

View file

@ -0,0 +1,33 @@
//
// Copyright 2020 Electronic Arts Inc.
//
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is free
// software: you can redistribute it and/or modify it under the terms of
// the GNU General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
// TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed
// in the hope that it will be useful, but with permitted additional restrictions
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT
// distributed with this program. You should have received a copy of the
// GNU General Public License along with permitted additional restrictions
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
#pragma once
#ifndef DLL_INTERFACE_VERSION_H
#define DLL_INTERFACE_VERSION_H
/*
** DLL Interface version
**
**
**
*/
#define CNC_DLL_API_VERSION 0x102
#endif //DLL_INTERFACE_VERSION_H

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 {
/*
@ -962,9 +962,17 @@ bool DriveClass::Start_Of_Move(void)
/*
** If a basic path could be found, but the immediate move destination is
** blocked by a friendly temporary blockage, then cause that blockage
** to scatter.
** to scatter. If the destination is also one cell away, then scatter
** regardless of direction.
*/
CELL cell = Adjacent_Cell(Coord_Cell(Center_Coord()), PrimaryFacing.Current());
CELL ourcell = Coord_Cell(Center_Coord());
CELL navcell = As_Cell(NavCom);
CELL cell = -1;
if (::Distance(ourcell, navcell) < 2) {
cell = navcell;
} else {
cell = Adjacent_Cell(ourcell, PrimaryFacing.Current());
}
if (Map.In_Radar(cell)) {
if (Can_Enter_Cell(cell) == MOVE_TEMP) {
CellClass * cellptr = &Map[cell];
@ -1598,12 +1606,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

@ -250,7 +250,7 @@ EventClass::EventClass(EventType type, TARGET src, TARGET dest)
* HISTORY: *
* 05/19/1995 JLB : Created. *
*=============================================================================================*/
EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord)
EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord, int visible)
{
ID = Houses.ID(PlayerPtr);
Type = ANIMATION;
@ -258,6 +258,7 @@ EventClass::EventClass(AnimType anim, HousesType owner, COORDINATE coord)
Data.Anim.What = anim;
Data.Anim.Owner = owner;
Data.Anim.Where = coord;
Data.Anim.Visible = visible;
}
@ -506,14 +507,26 @@ CCDebugString ("C&C95 - Sell packet received\n");
case ANIMATION:
anim = new AnimClass(Data.Anim.What, Data.Anim.Where);
if (anim) {
//2019/09/19 JAS - Visibility needs to be determined per player
if (Data.Anim.What != ANIM_MOVE_FLASH || Data.Anim.Owner == HOUSE_NONE || Special.IsVisibleTarget)
anim->Set_Owner(Data.Anim.Owner);
if (Special.IsVisibleTarget)
{
anim->Set_Visible_Flags(0xffff);
anim->Set_Visible_Flags(static_cast<unsigned int>(-1));
}
else
{
anim->Set_Visible_Flags(1 << Data.Anim.Owner);
anim->Set_Visible_Flags(static_cast<unsigned int>(Data.Anim.Visible));
}
/*
** Beacons have a 30-second kill time.
*/
if (Data.Anim.What == ANIM_BEACON) {
FILETIME ft;
GetSystemTimeAsFileTime(&ft);
unsigned long long kill_time = ((unsigned long long)ft.dwLowDateTime + ((unsigned long long)ft.dwHighDateTime << 32ULL)) + 300000000ULL;
anim->Kill_At(kill_time);
}
}
break;
@ -626,6 +639,12 @@ CCDebugString ("C&C95 - Primary building packet received\n");
techno->ArchiveTarget = Data.MegaMission.Target;
techno->Assign_Target(TARGET_NONE);
techno->Assign_Destination(Data.MegaMission.Target);
} else if (Data.MegaMission.Mission == MISSION_ENTER &&
object != NULL &&
object->What_Am_I() == RTTI_BUILDING &&
*((BuildingClass*)object) == STRUCT_REFINERY) {
techno->Transmit_Message(RADIO_HELLO, (BuildingClass*)object);
techno->Assign_Destination(TARGET_NONE);
} else {
techno->Assign_Target(Data.MegaMission.Target);
techno->Assign_Destination(Data.MegaMission.Destination);

View file

@ -127,6 +127,7 @@ class EventClass
AnimType What; // The animation to create.
HousesType Owner; // The owner of the animation (when it matters).
COORDINATE Where; // The location to place the animation.
int Visible; // Who this animation is visible to.
} Anim;
struct {
int Value; // general-purpose data
@ -208,7 +209,7 @@ class EventClass
EventClass(EventType type, RTTIType object, int id);
EventClass(EventType type, RTTIType object, CELL cell);
EventClass(EventType type, int id, CELL cell);
EventClass(AnimType anim, HousesType owner, COORDINATE coord);
EventClass(AnimType anim, HousesType owner, COORDINATE coord, int visible = -1);
// Process the event.
void Execute(void);

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

@ -305,14 +305,20 @@ bool FootClass::Mark(MarkType mark)
/*
** Inform the map of the refresh, occupation, and overlap
** request.
** Special case is fixed-wing aircraft, which are never placed
** or picked up since they can never land.
*/
switch (mark) {
case MARK_UP:
Map.Pick_Up(cell, this);
if (What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass*)this)->Class->IsFixedWing) {
Map.Pick_Up(cell, this);
}
break;
case MARK_DOWN:
Map.Place_Down(cell, this);
if (What_Am_I() != RTTI_AIRCRAFT || !((AircraftClass*)this)->Class->IsFixedWing) {
Map.Place_Down(cell, this);
}
break;
default:
@ -947,8 +953,10 @@ void FootClass::Approach_Target(void)
/*
** If a suitable intermediate location was found, then head toward it.
** Otherwise, head toward the enemy unit directly.
** Infantry always head towards the target since they can enter a cell
** in range, but still not be able to hit the target if the spot is out of range.
*/
if (found) {
if (found && What_Am_I() != RTTI_INFANTRY) {
Assign_Destination(::As_Target(trycell));
} else {
Assign_Destination(TarCom);
@ -990,6 +998,20 @@ int FootClass::Mission_Guard_Area(void)
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
}
/*
** Ensure units aren't trying to guard cells off the map.
*/
if (Target_Legal(NavCom) && Is_Target_Cell(NavCom)) {
CELL cell = As_Cell(NavCom);
int x = Cell_X(cell);
int y = Cell_Y(cell);
if (x < Map.MapCellX || y < Map.MapCellY || x >= (Map.MapCellX + Map.MapCellWidth) || y >= (Map.MapCellY + Map.MapCellHeight)) {
Assign_Target(TARGET_NONE);
Assign_Destination(TARGET_NONE);
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
}
}
/*
** Make sure that the unit has not strayed too far from the home position.
** If it has, then race back to it.
@ -1006,6 +1028,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 +1281,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;
@ -1319,7 +1342,7 @@ void FootClass::Active_Click_With(ActionType action, CELL cell)
case ACTION_MOVE:
if (AllowVoice) {
COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y());
OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord));
OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord, 1 << PlayerPtr->Class->House));
}
// Fall into next case.
@ -1422,6 +1445,8 @@ void FootClass::Per_Cell_Process(bool center)
}
// }
Map[Coord_Cell(Coord)].Goodie_Check(this, true);
TechnoClass::Per_Cell_Process(center);
}
@ -1498,6 +1523,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 +1561,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,35 +1627,51 @@ 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();
if (MissionQueue == MISSION_NONE) {
/*
** If this is a harvester, then return to harvesting.
** Set a hacky target so we know to skip to the proper state.
*/
if (What_Am_I() == RTTI_UNIT && ((UnitClass*)this)->Class->IsToHarvest) {
Assign_Mission(MISSION_HARVEST);
Assign_Target(As_Target());
Assign_Destination(TARGET_NONE);
} else {
ArchiveTarget = TARGET_NONE;
Enter_Idle_Mode();
}
}
}
return(TICKS_PER_SECOND/2);

View file

@ -360,7 +360,7 @@ void const *Get_Radar_Icon(void const *shapefile, int shapenum, int frames, int
void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0);
// Added for draw intercept. ST - 1/17/2019 12:31PM
void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0, int scale=0x100);
void CC_Draw_Shape(ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata=0, void const * ghostdata=0, int scale=0x100, int width=0, int height=0);
void CC_Draw_Shape(ObjectClass *object, const char *shape_file_name, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, char override_owner = HOUSE_NONE);
// Added for pip draw intercept - SKY
@ -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

@ -560,7 +560,7 @@ DynamicVectorClass <int> MPlayerFilenum;
/***************************************************************************
** This value determines the max allowable # of players.
*/
int MPlayerMax = 4;
int MPlayerMax = 6;
/***************************************************************************

View file

@ -569,16 +569,18 @@ bool HouseClass::Can_Build(TechnoTypeClass const * type, HousesType house) const
*/
long flags = ActiveBScan;
#ifdef USE_RA_AI
// OldBScan Copied from RA for AI. ST - 7/25/2019 3:27PM
/*
** The computer records prerequisite buildings because it can't relay on the
** sidebar to keep track of this information.
** AI players update flags using building quantity tracker.
** Ensures consistent logic when determining building choices.
*/
if (!IsHuman) {
flags = OldBScan;
flags = 0;
for (int i = 0; i < 32; i++) {
if (BQuantity[i] > 0) {
flags |= (1 << i);
}
}
}
#endif
int pre = type->Pre;
if (flags & STRUCTF_ADVANCED_POWER) flags |= STRUCTF_POWER;
@ -972,6 +974,12 @@ void HouseClass::AI(void)
if (IsToDie && BorrowedTime.Expired()) {
IsToDie = false;
Blowup_All();
// MBL 07.15.2020 - Steve made this change for RA, so also applying here to TD
// See Change 737595 by Steve_Tall@STEVET3-VICTORY-H on 2020/07/10 13:40:02
if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
MPlayer_Defeated();
}
}
/*
@ -1221,6 +1229,7 @@ void HouseClass::AI(void)
unit->Mark(MARK_CHANGE);
} else {
CELL cell = As_Cell(FlagLocation);
Map[cell].Flag_Update();
Map[cell].Redraw_Objects();
}
}
@ -1423,7 +1432,7 @@ void HouseClass::AI(void)
}
#endif
if (GameToPlay != GAME_NORMAL) {
if (GameToPlay != GAME_NORMAL && Class->House != HOUSE_JP) {
Check_Pertinent_Structures();
}
@ -1663,9 +1672,19 @@ void HouseClass::AI(void)
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));
if (GameToPlay == GAME_NORMAL) {
Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0);
} else {
Speak(VOX_BASE_UNDER_ATTACK, this);
}
// 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
@ -2547,7 +2566,15 @@ ProdFailType HouseClass::Abandon_Production(RTTIType type)
if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
if (IsHuman) {
Sidebar_Glyphx_Abandon_Production(type, *factory, this);
// Need to clear pending object here?
// Need to clear pending object here if legacy renderer enabled
if (type == RTTI_BUILDINGTYPE || type == RTTI_BUILDING && Map.PendingObjectPtr) {
Map.PendingObjectPtr = 0;
Map.PendingObject = 0;
Map.PendingHouse = HOUSE_NONE;
Map.Set_Cursor_Shape(0);
}
}
} else {
@ -2655,7 +2682,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->Set_Owner(Class->House);
if (this == PlayerPtr) {
Map.IsTargettingMode = false;
}
@ -4046,6 +4073,13 @@ void HouseClass::MPlayer_Defeated(void)
}
}
/*
** Remove any one-time superweapons the player might have.
*/
IonCannon.Remove(true);
AirStrike.Remove(true);
NukeStrike.Remove(true);
/*------------------------------------------------------------------------
If this is me:
- Set MPlayerObiWan, so I can only send messages to all players, and
@ -4055,7 +4089,6 @@ void HouseClass::MPlayer_Defeated(void)
------------------------------------------------------------------------*/
if (PlayerPtr == this) {
MPlayerObiWan = 1;
Debug_Unshroud = true;
HiddenPage.Clear();
Map.Flag_To_Redraw(true);
@ -4313,11 +4346,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 +4766,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);
@ -4781,6 +4813,13 @@ void HouseClass::Check_Pertinent_Structures(void)
return;
}
// MBL 07.15.2020 - Prevention of recent issue with constant "player defeated logic" and message to client spamming
// Per https://jaas.ea.com/browse/TDRA-7433
//
if (IsDefeated) {
return;
}
bool any_good_buildings = false;
for (int index = 0; index < Buildings.Count(); index++) {

View file

@ -444,6 +444,7 @@ class HouseClass {
bool Is_Ally(HousesType house) const;
bool Is_Ally(HouseClass const * house) const;
bool Is_Ally(ObjectClass const * object) const;
unsigned int Get_Allies(void) const {return Allies;}
#ifdef CHEAT_KEYS
void Debug_Dump(MonoClass *mono) const;
#endif
@ -517,6 +518,9 @@ class HouseClass {
void Init_Unit_Trackers(void);
void Free_Unit_Trackers(void);
// MBL 09.08.2020 Mod support stub
void Handle_Mod_Game_Command(CELL cell, int mod_command_index); // mod_command_index = 0-3
#ifdef USE_RA_AI
/*
** AI Functions imported from RA

View file

@ -430,7 +430,7 @@ static InfantryTypeClass const E5(
70, // Strength of infantry (in damage points).
1, // Sight range.
300, // Cost of infantry (in credits).
99, // Scenario when they first appear.
98, // Scenario when they first appear.
80,10, // Risk/Reward of this infantry unit.
HOUSEF_MULTI1|
HOUSEF_MULTI2|
@ -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

@ -677,6 +677,10 @@ void InfantryClass::Per_Cell_Process(bool center)
*/
if (center && Mission == MISSION_CAPTURE) {
TechnoClass * tech = cellptr->Cell_Techno();
if (tech && tech->As_Target() == NavCom && tech->What_Am_I() == RTTI_BUILDING && !tech->Can_Capture()) {
tech = NULL;
Assign_Destination(TARGET_NONE);
}
if (tech && tech->As_Target() == NavCom) {
tech->Captured(House);
Delete_This();
@ -882,7 +886,7 @@ void InfantryClass::Look(bool incremental)
if (!IsInLimbo) {
//if (IsOwnedByPlayer) { // Changed for multiple player mapping. ST - 3/6/2019 1:27PM
if (House->IsHuman) {
if (House->IsHuman || GameToPlay != GAME_NORMAL) {
sight = Class->SightRange;
if (sight) {
@ -1000,7 +1004,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);
}
}
@ -1098,7 +1102,14 @@ void InfantryClass::AI(void)
** run in circles, scream, and shout.
*/
if (Class->IsFraidyCat && Fear > FEAR_ANXIOUS && !IsDriving && !Target_Legal(NavCom)) {
Scatter(true);
Scatter(0);
}
/*
** Scatter infantry off buildings in guard modes.
*/
if (!IsTethered && !IsFiring && !IsDriving && !IsRotating && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) && MissionQueue == MISSION_NONE && Map[Coord_Cell(Coord)].Cell_Building() != NULL) {
Scatter(0, true, true);
}
/*
@ -1659,7 +1670,7 @@ MoveType InfantryClass::Can_Enter_Cell(CELL cell, FacingType ) const
** Cloaked enemy objects are not considered if this is a Find_Path()
** call.
*/
if (!obj->Is_Techno() || ((TechnoClass *)obj)->Cloak != CLOAKED) {
if (!obj->Is_Techno() || !((TechnoClass *)obj)->Is_Cloaked(this)) {
/*
** Any non-allied blockage is considered impassible if the infantry
@ -2086,8 +2097,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));
}
}
}
}
@ -2443,7 +2458,8 @@ bool InfantryClass::Unlimbo(COORDINATE coord, DirType facing)
** If there is no sight range, then this object isn't discovered by the player unless
** it actually appears in a cell mapped by the player.
*/
if (Class->SightRange == 0) {
if (Class->SightRange == 0 && GameToPlay == GAME_NORMAL && !House->IsHuman && !Map[Coord_Cell(coord)].Is_Visible(PlayerPtr)) {
IsDiscoveredByPlayerMask &= ~(1 << (int)PlayerPtr->Class->House);
IsDiscoveredByPlayer = false;
}
@ -2909,7 +2925,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 +2994,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 +3032,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 +3209,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 +3241,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

@ -321,6 +321,9 @@ bool Read_Scenario_Ini(char *root, bool fresh)
#ifdef NEWMENU
if (Scenario <= 15) {
BuildLevel = Scenario;
} else if (_stricmp(ScenarioName, "scg30ea") == 0 || _stricmp(ScenarioName, "scg90ea") == 0 || _stricmp(ScenarioName, "scb22ea") == 0) {
// N64 missions require build level 15
BuildLevel = 15;
} else {
BuildLevel = WWGetPrivateProfileInt("Basic", "BuildLevel", Scenario, buffer);
}
@ -444,6 +447,9 @@ bool Read_Scenario_Ini(char *root, bool fresh)
UnitClass::Read_INI(buffer);
Call_Back();
AircraftClass::Read_INI(buffer);
Call_Back();
/*
** Read in and place the infantry units (all sides).
*/
@ -485,11 +491,15 @@ bool Read_Scenario_Ini(char *root, bool fresh)
** Build the full text of the mission objective.
*/
for (;;) {
char buff[16];
int len = (sizeof(BriefingText)-strlen(BriefingText))-1;
if (len <= 0) {
break;
}
char buff[16];
sprintf(buff, "%d", index++);
*stage = '\0';
WWGetPrivateProfileString("Briefing", buff, "", stage, (sizeof(BriefingText)-strlen(BriefingText))-1, buffer);
WWGetPrivateProfileString("Briefing", buff, "", stage, len, buffer);
if (strlen(stage) == 0) break;
strcat(stage, " ");
stage += strlen(stage);
@ -534,6 +544,9 @@ bool Read_Scenario_Ini(char *root, bool fresh)
** NOD7A cell 2795 - LAND_ROCK
** NOD09A - delete airstrike trigger when radar destroyed
** NOD10B cell 2015 - LAND_ROCK
** NOD13B - trigger AI production when the player reaches the transports
- fix repeating airstrike trigger
** NOD13C - delete airstrike trigger when radar destroyed
*/
if (_stricmp(ScenarioName, "scb07ea") == 0) {
Map[(CELL)2795].Override_Land_Type(LAND_ROCK);
@ -553,6 +566,39 @@ bool Read_Scenario_Ini(char *root, bool fresh)
if (_stricmp(ScenarioName, "scb10eb") == 0) {
Map[(CELL)2015].Override_Land_Type(LAND_ROCK);
}
if (_stricmp(ScenarioName, "scb13eb") == 0) {
TriggerClass* prod = new TriggerClass();
prod->Set_Name("prod");
prod->Event = EVENT_PLAYER_ENTERED;
prod->Action = TriggerClass::ACTION_BEGIN_PRODUCTION;
prod->House = HOUSE_BAD;
CellTriggers[276] = prod; prod->AttachCount++;
CellTriggers[340] = prod; prod->AttachCount++;
CellTriggers[404] = prod; prod->AttachCount++;
CellTriggers[468] = prod; prod->AttachCount++;
TriggerClass* xxxx = TriggerClass::As_Pointer("xxxx");
assert(xxxx != NULL);
xxxx->IsPersistant = TriggerClass::PERSISTANT;
}
if (_stricmp(ScenarioName, "scb13ec") == 0) {
for (int index = 0; index < Buildings.Count(); ++index) {
BuildingClass* building = Buildings.Ptr(index);
if (building != NULL && building->Owner() == HOUSE_GOOD && *building == STRUCT_RADAR && building->Trigger == NULL) {
building->Trigger = TriggerClass::As_Pointer("delx");
if (building->Trigger) {
building->Trigger->AttachCount++;
}
break;
}
}
}
/*
** Scenario fix-up (applied on loaded games as well)
*/
Fixup_Scenario();
/*
** Multi-player last-minute fixups:
@ -839,6 +885,9 @@ bool Read_Scenario_Ini_File(char *scenario_file_name, char* bin_file_name, const
UnitClass::Read_INI(buffer);
Call_Back();
AircraftClass::Read_INI(buffer);
Call_Back();
/*
** Read in and place the infantry units (all sides).
*/
@ -880,11 +929,15 @@ bool Read_Scenario_Ini_File(char *scenario_file_name, char* bin_file_name, const
** Build the full text of the mission objective.
*/
for (;;) {
char buff[16];
int len = (sizeof(BriefingText) - strlen(BriefingText)) - 1;
if (len <= 0) {
break;
}
char buff[16];
sprintf(buff, "%d", index++);
*stage = '\0';
WWGetPrivateProfileString("Briefing", buff, "", stage, (sizeof(BriefingText) - strlen(BriefingText)) - 1, buffer);
WWGetPrivateProfileString("Briefing", buff, "", stage, len, buffer);
if (strlen(stage) == 0) break;
strcat(stage, " ");
stage += strlen(stage);
@ -924,6 +977,11 @@ bool Read_Scenario_Ini_File(char *scenario_file_name, char* bin_file_name, const
Map.Overpass();
Call_Back();
/*
** 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

@ -1560,6 +1560,10 @@ bool UnitClass::Save(FileClass & file)
*=============================================================================================*/
void UnitClass::Code_Pointers(void)
{
if (TiberiumUnloadRefinery) {
TiberiumUnloadRefinery = (BuildingClass *)TiberiumUnloadRefinery->As_Target();
}
TarComClass::Code_Pointers();
}
@ -1584,6 +1588,11 @@ void UnitClass::Code_Pointers(void)
*=============================================================================================*/
void UnitClass::Decode_Pointers(void)
{
if (TiberiumUnloadRefinery) {
TiberiumUnloadRefinery = As_Building((TARGET)TiberiumUnloadRefinery, false);
Check_Ptr((void *)TiberiumUnloadRefinery, __FILE__, __LINE__);
}
TarComClass::Decode_Pointers();
}

View file

@ -190,6 +190,7 @@ void LogicClass::AI(void)
*/
for (index = 0; index < Count(); index++) {
ObjectClass * obj = (*this)[index];
int count = Count();
obj->AI();
@ -197,9 +198,9 @@ void LogicClass::AI(void)
** If the object was destroyed in the process of performing its AI, then
** adjust the index so that no object gets skipped.
*/
if (obj != (*this)[index]) {
// if (!obj->IsActive) {
index--;
int count_diff = Count() - count;
if (count_diff < 0) {
index += count_diff;
}
}

View file

@ -362,11 +362,9 @@ void MapClass::Sight_From(HouseClass *house, CELL cell, int sightrange, bool inc
** adjacent cells as well. For full scans, just update
** the cell itself.
*/
if (!(*this)[newcell].Is_Mapped(house)) {
// Pass the house through, instead of assuming it's the local player. ST - 3/6/2019 10:26AM
//Map.Map_Cell(newcell, PlayerPtr);
Map.Map_Cell(newcell, house, true);
}
// Pass the house through, instead of assuming it's the local player. ST - 3/6/2019 10:26AM
//Map.Map_Cell(newcell, PlayerPtr);
Map.Map_Cell(newcell, house, true);
}
}
@ -950,6 +948,15 @@ void MapClass::Logic(void)
if (TiberiumScan >= MAP_CELL_TOTAL) {
int tries = 1;
if (Special.IsTFast || GameToPlay != GAME_NORMAL) tries = 2;
/*
** Use the Tiberium setting as a multiplier on growth rate. ST - 7/1/2020 3:05PM
*/
if (GameToPlay == GAME_GLYPHX_MULTIPLAYER) {
if (MPlayerTiberium > 1) {
tries += (MPlayerTiberium - 1) << 1;
}
}
TiberiumScan = 0;
IsForwardScan = (IsForwardScan == false);
@ -958,11 +965,17 @@ void MapClass::Logic(void)
*/
if (TiberiumGrowthCount) {
for (int i = 0; i < tries; i++) {
CELL cell = TiberiumGrowth[Random_Pick(0, TiberiumGrowthCount-1)];
int pick = Random_Pick(0, TiberiumGrowthCount-1);
CELL cell = TiberiumGrowth[pick];
CellClass * newcell = &(*this)[cell];
if (newcell->Land_Type() == LAND_TIBERIUM && newcell->OverlayData < 12-1) {
newcell->OverlayData++;
}
TiberiumGrowth[pick] = TiberiumGrowth[TiberiumGrowthCount - 1];
TiberiumGrowthCount--;
if (TiberiumGrowthCount <= 0) {
break;
}
}
}
TiberiumGrowthCount = 0;
@ -972,7 +985,8 @@ void MapClass::Logic(void)
*/
if (TiberiumSpreadCount) {
for (int i = 0; i < tries; i++) {
CELL cell = TiberiumSpread[Random_Pick(0, TiberiumSpreadCount-1)];
int pick = Random_Pick(0, TiberiumSpreadCount-1);
CELL cell = TiberiumSpread[pick];
/*
** Find a pseudo-random adjacent cell that doesn't contain any tiberium.
@ -980,7 +994,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;
@ -1003,6 +1017,11 @@ void MapClass::Logic(void)
}
}
}
TiberiumSpread[pick] = TiberiumSpread[TiberiumSpreadCount - 1];
TiberiumSpreadCount--;
if (TiberiumSpreadCount <= 0) {
break;
}
}
}
TiberiumSpreadCount = 0;
@ -1400,10 +1419,10 @@ ObjectClass * MapClass::Close_Object(COORDINATE coord) const
while (o) {
/*
** Special case check to ignore cloaked object if not owned by the player.
** Special case check to ignore cloaked object if not allied with the player.
*/
// Changed for multiplayer. ST - 3/13/2019 5:38PM
if (!o->Is_Techno() || ((TechnoClass *)o)->Is_Owned_By_Player() || ((TechnoClass *)o)->Cloak != CLOAKED) {
if (!o->Is_Techno() || !((TechnoClass *)o)->Is_Cloaked(PlayerPtr)) {
//if (!o->Is_Techno() || ((TechnoClass *)o)->IsOwnedByPlayer || ((TechnoClass *)o)->Cloak != CLOAKED) {
int d=-1;
if (o->What_Am_I() == RTTI_BUILDING) {
@ -1429,7 +1448,7 @@ ObjectClass * MapClass::Close_Object(COORDINATE coord) const
AircraftClass * aircraft = Aircraft.Ptr(index);
if (aircraft->In_Which_Layer() != LAYER_GROUND) {
if (aircraft->Is_Owned_By_Player() || (aircraft->Cloak != CLOAKED)) {
if (!aircraft->Is_Cloaked(PlayerPtr)) {
int d = Distance(coord, Coord_Add(aircraft->Center_Coord(), XY_Coord(0, -Pixel_To_Lepton(aircraft->Altitude))));
if (d >= 0 && (!object || d < distance)) {
distance = d;

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#138 $
;***************************************************************************
;** 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? *
* *
@ -558,6 +564,7 @@ COORDINATE ObjectClass::Center_Coord(void) const {return Coord;};
COORDINATE ObjectClass::Render_Coord(void) const {return(Center_Coord());}
COORDINATE ObjectClass::Docking_Coord(void) const {return(Center_Coord());}
COORDINATE ObjectClass::Sort_Y(void) const {return Coord;};
FireDataType ObjectClass::Fire_Data(int which) const {return{Fire_Coord(which),0};}
COORDINATE ObjectClass::Fire_Coord(int ) const {return Coord;};
void ObjectClass::Record_The_Kill(TechnoClass * ) {};
void ObjectClass::Do_Shimmer(void) {};

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;
@ -171,6 +172,7 @@ class ObjectClass : public AbstractClass
virtual COORDINATE Center_Coord(void) const;
virtual COORDINATE Render_Coord(void) const;
virtual COORDINATE Sort_Y(void) const;
virtual FireDataType Fire_Data(int) const;
virtual COORDINATE Fire_Coord(int ) 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

@ -235,7 +235,7 @@ bool RadarClass::Radar_Activate(int control)
case 0:
if (Map.IsSidebarActive) {
if (IsRadarActive && !IsRadarDeactivating) {
Sound_Effect(VOC_RADAR_OFF);
// Sound_Effect(VOC_RADAR_OFF); // MBL 07.20.2020: These are never being sent to the client, so handled there; Disabling here for good measure.
IsRadarDeactivating = true;
IsRadarActive = false;
if (IsRadarActivating == true) {
@ -252,7 +252,7 @@ bool RadarClass::Radar_Activate(int control)
case 1:
if (Map.IsSidebarActive) {
if (!IsRadarActivating && !IsRadarActive) {
Sound_Effect(VOC_RADAR_ON);
// Sound_Effect(VOC_RADAR_ON); // MBL 07.20.2020: These are never being sent to the client, so handled there; Disabling here for good measure.
IsRadarActivating = true;
if (IsRadarDeactivating == true) {
IsRadarDeactivating = false;

View file

@ -338,6 +338,7 @@ bool Do_Reinforcements(TeamTypeClass *teamtype)
*/
case SOURCE_AIR: {
AircraftClass * thisone = (AircraftClass *)object;
TARGET target = TARGET_NONE;
while (thisone) {
AircraftClass * next = (AircraftClass *)thisone->Next;
@ -345,25 +346,57 @@ bool Do_Reinforcements(TeamTypeClass *teamtype)
** Find a suitable map entry location. Cargo planes will try to find a cell that
** exactly lines up with the airfield they will unload at.
*/
CELL newcell;
COORDINATE newcoord;
long reinforcement_delay = -1;
ScenarioInit++;
newcell = Map.Calculated_Cell(HouseClass::As_Pointer(teamtype->House)->Edge, teamtype->House);
newcoord = Cell_Coord(Map.Calculated_Cell(HouseClass::As_Pointer(teamtype->House)->Edge, teamtype->House));
ScenarioInit--;
if (*thisone == AIRCRAFT_CARGO) {
BuildingClass const * building = thisone->Find_Docking_Bay(STRUCT_AIRSTRIP, false);
if (building) {
newcell = XY_Cell(Map.MapCellX+Map.MapCellWidth, Coord_YCell(building->Docking_Coord()+2));
COORDINATE docking_coord = building->Docking_Coord();
const int border_x = Cell_To_Lepton(Map.MapCellX + Map.MapCellWidth) | 0x80;
if (Special.ModernBalance) {
/*
** Cargo plane takes 5 seconds to reach the airstrip on Normal (1.5x legacy), or (75 / 10) seconds at speed.
** Assumes a 45ms (1000 / 45 ticks per second) service rate.
*/
const int speed = AircraftTypeClass::As_Reference(AIRCRAFT_CARGO).MaxSpeed;
int spawn_x = Coord_X(docking_coord) + ((speed * 1000 * 75) / (45 * 10));
if (spawn_x > border_x) {
reinforcement_delay = (spawn_x - border_x) / speed;
spawn_x = border_x;
}
newcoord = XY_Coord(spawn_x, Coord_Y(docking_coord));
} else {
newcoord = XY_Coord(border_x, Coord_Y(docking_coord));
}
if (teamtype->MissionCount) {
teamtype->MissionList[0].Argument = building->As_Target();
}
}
}
thisone->Next = 0;
ScenarioInit++;
placed = thisone->Unlimbo(Cell_Coord(newcell), DIR_W);
placed = thisone->Unlimbo(newcoord, DIR_W);
if (Special.ModernBalance && reinforcement_delay >= 0) {
thisone->Set_Reinforcement_Delay(reinforcement_delay);
}
ScenarioInit--;
if (placed) {
if (!team) {
if (thisone->Class->IsFixedWing) {
thisone->Assign_Mission(MISSION_HUNT);
if (*thisone == AIRCRAFT_A10) {
/*
** Groups of A10s always go after the same target initally.
*/
if (target == TARGET_NONE) {
target = thisone->Greatest_Threat(THREAT_NORMAL);
}
thisone->Assign_Target(target);
}
} else {
if (thisone->Class->IsTransporter && thisone->Is_Something_Attached()) {
thisone->Assign_Mission(MISSION_UNLOAD);

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,45 @@ 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;
}
/*
** Modern Balance
*/
if (Special.ModernBalance) {
/*
** GDI Weapons Factory has 30% more health.
*/
((BuildingTypeClass&)BuildingTypeClass::As_Reference(STRUCT_WEAP)).MaxStrength = 520;
/*
** Repair Pad is a pre-requisite for the APC.
*/
((UnitTypeClass&)UnitTypeClass::As_Reference(UNIT_APC)).Pre |= STRUCTF_REPAIR;
} else {
((BuildingTypeClass&)BuildingTypeClass::As_Reference(STRUCT_WEAP)).MaxStrength = 400;
((UnitTypeClass&)UnitTypeClass::As_Reference(UNIT_APC)).Pre &= ~STRUCTF_REPAIR;
}
}

View file

@ -70,6 +70,7 @@ class SpecialClass
IsEarlyWin = false;
HealthBarDisplayMode = HB_SELECTED;
ResourceBarDisplayMode = RB_SELECTED;
ModernBalance = false;
}
/*
@ -254,11 +255,21 @@ class SpecialClass
RB_ALWAYS,
} ResourceBarDisplayMode;
/*
** New modern balance setting.
*/
unsigned ModernBalance:1;
/*
** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load
*/
unsigned char SaveLoadPadding[128];
// MBL 07.21.2020 - https://jaas.ea.com/browse/TDRA-7537
// Loading save files from Live and July Patch 3 Beta versions results in a crash
// Fixes issue from Change 738397 2020/07/17 14:06:03
//
// unsigned char SaveLoadPadding[127];
//
unsigned char SaveLoadPadding[124];
};

View file

@ -317,7 +317,7 @@ int DLL_Startup(const char * command_line_in)
if (Parse_Command_Line(argc, argv)) {
WindowsTimer = new WinTimerClass(60,FALSE);
#if (0)
int time_test = WindowsTimer->Get_System_Tick_Count();
Sleep (1000);
if (WindowsTimer->Get_System_Tick_Count() == time_test){
@ -332,6 +332,7 @@ int DLL_Startup(const char * command_line_in)
#endif //FRENCH
return(EXIT_FAILURE);
}
#endif
RawFileClass cfile("CONQUER.INI");

View file

@ -1266,7 +1266,14 @@ void TeamClass::Coordinate_Move(void)
unit->Assign_Mission(MISSION_MOVE);
}
if (unit->NavCom != Target) {
unit->Assign_Destination(Target);
TARGET target = Target;
if (unit->What_Am_I() == RTTI_AIRCRAFT) {
AircraftClass* aircraft = (AircraftClass *)unit;
if (!aircraft->Class->IsFixedWing && !aircraft->Is_LZ_Clear(target)) {
target = aircraft->New_LZ(target, true);
}
}
unit->Assign_Destination(target);
}
finished = false;
} else {

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) && !Is_Cloaked(PlayerPtr) &&
(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++;
}
}
@ -1159,7 +1191,8 @@ bool TechnoClass::In_Range(TARGET target, int which, bool reciprocal_check) cons
if (building) {
range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4));
}
if (::Distance(Fire_Coord(which), As_Coord(target)) <= range) {
FireDataType data = Fire_Data(which);
if (MAX(0, ::Distance(data.Center, As_Coord(target)) - data.Distance) <= range) {
return(true);
}
@ -1212,8 +1245,8 @@ bool TechnoClass::In_Range(ObjectClass const * target, int which, bool reciproca
BuildingClass const * building = (BuildingClass const *)target;
range += ((building->Class->Width() + building->Class->Height()) * (ICON_LEPTON_W / 4));
}
if (::Distance(Fire_Coord(which), target->Center_Coord()) <= range) {
FireDataType data = Fire_Data(which);
if (MAX(0, ::Distance(data.Center, target->Center_Coord()) - data.Distance) <= range) {
return(true);
}
@ -1335,7 +1368,7 @@ bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, Techno
/*
** If the object is cloaked, then it isn't a legal target.
*/
if (object->Cloak == CLOAKED) return(false);
if (object->Is_Cloaked(this)) return(false);
/*
** Determine if the target is theoretically allowed to be a target. If
@ -1369,7 +1402,7 @@ bool TechnoClass::Evaluate_Object(ThreatType method, int mask, int range, Techno
** If the scan is limited to capturable buildings only, then bail if the examined
** object isn't a capturable building.
*/
if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !((BuildingTypeClass const *)tclass)->IsCaptureable)) {
if ((method & THREAT_CAPTURE) && (otype != RTTI_BUILDING || !object->Can_Capture())) {
return(false);
}
@ -1968,8 +2001,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;
}
}
/*
@ -2066,7 +2104,7 @@ FireErrorType TechnoClass::Can_Fire(TARGET target, int which) const
//Mono_Printf("Buildings[0]=%p.\n", Buildings.Raw_Ptr(0));
//Mono_Printf("Aircraft[0]=%p.\n", Aircraft.Raw_Ptr(0));
//Mono_Printf("object=%p, Strength=%d, IsActive=%d, IsInLimbo=%d.\n", object, (long)object->Strength, object->IsActive, object->IsInLimbo);Get_Key();
if (object && /*(object->IsActive || GameToPlay != GAME_NORMAL) &&*/ object->Is_Techno() && ((TechnoClass *)object)->Cloak == CLOAKED) {
if (object && /*(object->IsActive || GameToPlay != GAME_NORMAL) &&*/ object->Is_Techno() && ((TechnoClass *)object)->Is_Cloaked(this)) {
return(FIRE_CANT);
}
@ -2249,6 +2287,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);
@ -2385,7 +2426,7 @@ BulletClass * TechnoClass::Fire_At(TARGET target, int which)
}
#else
/*
** Now need to reveal for any human player that is the target. ST - 3/13/2019 5:43PM
** Now need to reveal for any player (only humans in normal node) that is the target. ST - 3/13/2019 5:43PM
*/
ObjectClass *obj = As_Object(target);
@ -2393,7 +2434,7 @@ BulletClass * TechnoClass::Fire_At(TARGET target, int which)
HousesType tgt_owner = obj->Owner();
HouseClass *player = HouseClass::As_Pointer(tgt_owner);
if (player != nullptr && player->IsHuman) {
if (player != nullptr && (player->IsHuman || GameToPlay != GAME_NORMAL)) {
if ((!Is_Owned_By_Player(player) && !Is_Discovered_By_Player(player)) || !Map[Coord_Cell(Center_Coord())].Is_Mapped(House)) {
Map.Sight_From(player, Coord_Cell(Center_Coord()), 1, false);
}
@ -2558,11 +2599,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);
}
}
}
}
}
@ -4396,6 +4443,7 @@ BuildingClass * TechnoClass::Find_Docking_Bay(StructType b, bool friendly) const
if (bestval == -1 || Distance(building) < bestval || building->IsLeader) {
best = building;
bestval = Distance(building);
if (building->IsLeader) break;
}
}
}
@ -4570,8 +4618,23 @@ bool TechnoClass::Is_Owned_By_Player(HouseClass *player) const
}
bool TechnoClass::Is_Cloaked(HousesType house) const
{
return !House->Is_Ally(house) && (Cloak == CLOAKED);
}
bool TechnoClass::Is_Cloaked(HouseClass const * house) const
{
return !House->Is_Ally(house) && (Cloak == CLOAKED);
}
bool TechnoClass::Is_Cloaked(ObjectClass const * object) const
{
return !House->Is_Ally(object) && (Cloak == CLOAKED);
}

View file

@ -280,8 +280,11 @@ class TechnoClass : public RadioClass,
virtual int Weapon_Range(int which) const;
virtual bool Captured(HouseClass * newowner);
virtual ResultType Take_Damage(int & damage, int distance, WarheadType warhead, TechnoClass * source);
bool Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const ** object, int & value) const;
bool Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value) const;
bool Evaluate_Cell(ThreatType method, int mask, CELL cell, int range, TechnoClass const ** object, int & value) const;
bool Evaluate_Object(ThreatType method, int mask, int range, TechnoClass const * object, int & value) const;
bool Is_Cloaked(HousesType house) const;
bool Is_Cloaked(HouseClass const * house) const;
bool Is_Cloaked(ObjectClass const * object) const;
/*
** AI.

View file

@ -361,6 +361,64 @@ FireErrorType TurretClass::Can_Fire(TARGET target, int which) const
* HISTORY: *
* 12/28/1994 JLB : Created. *
*=============================================================================================*/
FireDataType TurretClass::Fire_Data(int which) const
{
COORDINATE coord = Center_Coord();
int dist = 0;
switch (Class->Type) {
case UNIT_GUNBOAT:
coord = Coord_Move(coord, PrimaryFacing.Current(), Pixel2Lepton[Class->TurretOffset]);
dist = 0x0060;
break;
case UNIT_ARTY:
coord = Coord_Move(coord, DIR_N, 0x0040);
dist = 0x0060;
break;
case UNIT_FTANK:
dist = 0x30;
break;
case UNIT_HTANK:
coord = Coord_Move(coord, DIR_N, 0x0040);
if (which == 0) {
dist = 0x00C0;
} else {
dist = 0x0008;
}
break;
case UNIT_LTANK:
coord = Coord_Move(coord, DIR_N, 0x0020);
dist = 0x00C0;
break;
case UNIT_MTANK:
coord = Coord_Move(coord, DIR_N, 0x0030);
dist = 0x00C0;
break;
case UNIT_APC:
case UNIT_JEEP:
case UNIT_BUGGY:
coord = Coord_Move(coord, DIR_N, 0x0030);
dist = 0x0030;
break;
#ifdef PETROGLYPH_EXAMPLE_MOD
case UNIT_NUKE_TANK:
coord = Coord_Move(coord, DIR_N, 0x00A0);
dist = 0x00A0;
break;
#endif //PETROGLYPH_EXAMPLE_MOD
}
return {coord,dist};
}
COORDINATE TurretClass::Fire_Coord(int which) const
{
COORDINATE coord = Center_Coord();

View file

@ -75,6 +75,7 @@ class TurretClass : public DriveClass
virtual FireErrorType Can_Fire(TARGET target, int which) const;
virtual bool Ok_To_Move(DirType facing);
virtual void AI(void);
virtual FireDataType Fire_Data(int which) const;
virtual COORDINATE Fire_Coord(int which) const;
};

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

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
@ -210,6 +210,7 @@
<ClInclude Include="DIAL8.H" />
<ClInclude Include="DISPLAY.H" />
<ClInclude Include="DLLInterface.h" />
<ClInclude Include="DLLInterfaceVersion.h" />
<ClInclude Include="DOOR.H" />
<ClInclude Include="DPMI.H" />
<ClInclude Include="DRIVE.H" />

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
@ -627,6 +627,9 @@
<ClInclude Include="RULES.H">
<Filter>Source Files</Filter>
</ClInclude>
<ClInclude Include="DLLInterfaceVersion.h">
<Filter>Source Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="AADATA.CPP">

View file

@ -390,7 +390,7 @@ void UnitClass::AI(void)
** Delete this unit if it finds itself off the edge of the map and it is in
** guard or other static mission mode.
*/
if (!Team && Mission == MISSION_GUARD && !Map.In_Radar(Coord_Cell(Coord))) {
if (!Team && Mission == MISSION_GUARD && MissionQueue == MISSION_NONE && !Map.In_Radar(Coord_Cell(Coord))) {
Stun();
Delete_This();
return;
@ -501,6 +501,13 @@ void UnitClass::AI(void)
}
}
/*
** Scatter units off buildings in guard modes.
*/
if (!IsTethered && !IsFiring && !IsDriving && !IsRotating && (Mission == MISSION_GUARD || Mission == MISSION_GUARD_AREA) && MissionQueue == MISSION_NONE && Map[Coord_Cell(Coord)].Cell_Building() != NULL) {
Scatter(0, true, true);
}
/*
** A cloaked object that is carrying the flag will always shimmer.
*/
@ -583,6 +590,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 +637,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);
@ -703,6 +749,22 @@ RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType
}
break;
/*
** Something bad has happened to the object in contact with. Abort any coordinated
** activity with this object. Basically, ... run away! Run away!
*/
case RADIO_RUN_AWAY:
if (Class->IsToHarvest && In_Radio_Contact() && Mission == MISSION_ENTER) {
TechnoClass * contact = Contact_With_Whom();
if (contact->What_Am_I() == RTTI_BUILDING && *((BuildingClass*)contact) == STRUCT_REFINERY) {
// Slight hack; set a target so the harvest mission knows to skip to finding home state
Assign_Mission(MISSION_HARVEST);
TarCom = As_Target();
return(RADIO_ROGER);
}
}
return(DriveClass::Receive_Message(from, message, param));
/*
** When this message is received, it means that the other object
** has already turned its radio off. Turn this radio off as well.
@ -1152,6 +1214,7 @@ UnitClass::UnitClass(UnitType classid, HousesType house) :
Reload = 0;
Ammo = Class->MaxAmmo;
IsCloakable = Class->IsCloakable;
TiberiumUnloadRefinery = NULL;
if (Class->IsAnimating) Set_Rate(Options.Normalize_Delay(3));
/*
@ -1215,6 +1278,21 @@ 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;
} else if (mission == MISSION_ENTER) {
BuildingClass* building = As_Building(destination);
if (building != NULL && *building == STRUCT_REFINERY && building->In_Radio_Contact()) {
building->Transmit_Message(RADIO_OVER_OUT);
}
}
TarComClass::Player_Assign_Mission(mission, target, destination);
}
/***********************************************************************************************
* UnitClass::Enter_Idle_Mode -- Unit enters idle mode state. *
* *
@ -1744,7 +1822,7 @@ void UnitClass::Per_Cell_Process(bool center)
return;
default:
Scatter(true);
Scatter(0, true);
break;
}
}
@ -1755,28 +1833,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 && *((BuildingClass*)contact) != STRUCT_REPAIR) {
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 +2293,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 +2313,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+1);
}
}
return(false);
return(0);
}
@ -2265,31 +2357,60 @@ 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);
/*
** Randomize the corners.
*/
int corner[2];
int corners[4][2] = {
{x, -radius},
{x, +radius},
{-radius, x},
{+radius, x}
};
for (int i = 0; i < 3; i++) {
int j = i + rand() / (RAND_MAX / (4 - i) + 1);
memcpy(&corner, &corners[j], sizeof(corner));
memcpy(&corners[j], &corners[i], sizeof(corner));
memcpy(&corners[i], corner, sizeof(corner));
}
cell = center;
if (Tiberium_Check(cell, x, +radius)) {
Assign_Destination(::As_Target(cell));
return(false);
tiberium = Tiberium_Check(cell, corners[0][0], corners[0][1]);
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, corners[1][0], corners[1][1]);
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, corners[2][0], corners[2][1]);
if (tiberium > besttiberium) {
bestcell = cell;
besttiberium = tiberium;
}
cell = center;
tiberium = Tiberium_Check(cell, corners[3][0], corners[3][1]);
if (tiberium > besttiberium) {
bestcell = cell;
besttiberium = tiberium;
}
}
if (bestcell) {
Assign_Destination(::As_Target(bestcell));
return(false);
}
}
}
@ -2299,6 +2420,29 @@ bool UnitClass::Goto_Tiberium(void)
}
BuildingClass* UnitClass::Find_Best_Refinery(void) const
{
/*
** Remember our last refinery and prefer that one, if still available and valid.
*/
if (TiberiumUnloadRefinery != NULL) {
if (TiberiumUnloadRefinery->House == House &&
!TiberiumUnloadRefinery->IsInLimbo &&
TiberiumUnloadRefinery->Mission != MISSION_DECONSTRUCTION &&
*TiberiumUnloadRefinery == STRUCT_REFINERY) {
return TiberiumUnloadRefinery;
} else {
TiberiumUnloadRefinery = NULL;
}
}
/*
** Find nearby refinery and head to it?
*/
return Find_Docking_Bay(STRUCT_REFINERY, false);
}
/***********************************************************************************************
* UnitClass::Harvesting -- Harvests tiberium at the current location. *
* *
@ -2584,11 +2728,19 @@ int UnitClass::Mission_Harvest(void)
*/
case LOOKING:
IsHarvesting = false;
if (Goto_Tiberium()) {
/*
** Slightly hacky; if TarCom is set then skip to finding home state.
*/
if (Target_Legal(TarCom)) {
Assign_Target(TARGET_NONE);
Status = FINDHOME;
return(1);
} else if (Goto_Tiberium()) {
IsHarvesting = true;
Set_Rate(2);
Set_Stage(0);
Status = HARVESTING;
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
return(1);
} else {
@ -2623,7 +2775,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 +2785,8 @@ int UnitClass::Mission_Harvest(void)
}
}
return(1);
} else if (!Target_Legal(NavCom) && ArchiveTarget == TARGET_NONE) {
ArchiveTarget = ::As_Target(Coord_Cell(Coord));
}
break;
@ -2644,9 +2797,9 @@ int UnitClass::Mission_Harvest(void)
if (!Target_Legal(NavCom)) {
/*
** Find nearby refinery and head to it?
** Find best refinery.
*/
BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false);
BuildingClass * nearest = Find_Best_Refinery();
/*
** Since the refinery said it was ok to load, establish radio
@ -2656,7 +2809,7 @@ int UnitClass::Mission_Harvest(void)
Status = HEADINGHOME;
} else {
ScenarioInit++;
nearest = Find_Docking_Bay(STRUCT_REFINERY, false);
nearest = Find_Best_Refinery();
ScenarioInit--;
if (nearest) {
Assign_Destination(::As_Target(nearest->Nearby_Location(this)));
@ -2753,6 +2906,28 @@ int UnitClass::Mission_Hunt(void)
}
int UnitClass::Mission_Enter(void)
{
Validate();
if (Class->IsToHarvest) {
TechnoClass * contact = Contact_With_Whom();
if (contact == NULL) {
contact = As_Techno(ArchiveTarget);
}
if (contact == NULL) {
contact = As_Techno(NavCom);
}
if (contact != NULL &&
contact->What_Am_I() == RTTI_BUILDING &&
*((BuildingClass*)contact) == STRUCT_REFINERY) {
TiberiumUnloadRefinery = (BuildingClass*)contact;
}
}
return(TarComClass::Mission_Enter());
}
/***********************************************************************************************
* UnitClass::Look -- Perform map revelation from a unit's position. *
* *
@ -2778,7 +2953,7 @@ void UnitClass::Look(bool incremental)
{
Validate();
//if (!IsInLimbo && IsOwnedByPlayer) { // Changed for mapping of multiple players
if (!IsInLimbo && House && House->IsHuman) {
if (!IsInLimbo && House && (House->IsHuman || GameToPlay != GAME_NORMAL)) {
int sight = Class->SightRange;
if (sight) {
@ -2823,7 +2998,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 +3420,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 +3554,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 +3728,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);
@ -84,6 +84,7 @@ class UnitClass : public TarComClass
bool Harvesting(void);
void APC_Close_Door(void);
void APC_Open_Door(void);
virtual BuildingClass* Find_Best_Refinery(void) const;
/*
** Query functions.
@ -125,6 +126,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);
@ -155,6 +157,7 @@ class UnitClass : public TarComClass
virtual int Mission_Guard(void);
virtual int Mission_Harvest(void);
virtual int Mission_Hunt(void);
virtual int Mission_Enter(void);
virtual int UnitClass::Mission_Move(void);
virtual FireErrorType Can_Fire(TARGET, int which) const;
@ -198,10 +201,15 @@ class UnitClass : public TarComClass
*/
TCountDownTimerClass HarvestTimer;
/*
** This is the refinery a harvester is interested in unloading at.
*/
mutable BuildingClass* TiberiumUnloadRefinery;
/*
** 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];
/*
** This contains the value of the Virtual Function Table Pointer

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#138 $
;***************************************************************************
;** 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#138 $
;***************************************************************************
;** 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 **
;***************************************************************************