September 16th patch update
DLL version incremented Beacon functionality added Support for loading screen match preview display Placeholder handling of new key-bindable mod commands
This commit is contained in:
parent
e37e174be1
commit
fd05be35c1
|
@ -0,0 +1,28 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.1022
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CnCTDRAMapEditor", "CnCTDRAMapEditor\CnCTDRAMapEditor.csproj", "{397CEF00-8930-4EC8-B15F-F7CF7193FB22}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Gold|Any CPU = Gold|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{397CEF00-8930-4EC8-B15F-F7CF7193FB22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{397CEF00-8930-4EC8-B15F-F7CF7193FB22}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{397CEF00-8930-4EC8-B15F-F7CF7193FB22}.Gold|Any CPU.ActiveCfg = Gold|Any CPU
|
||||
{397CEF00-8930-4EC8-B15F-F7CF7193FB22}.Gold|Any CPU.Build.0 = Gold|Any CPU
|
||||
{397CEF00-8930-4EC8-B15F-F7CF7193FB22}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{397CEF00-8930-4EC8-B15F-F7CF7193FB22}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {3A313796-A0F0-4740-98D6-037699DBC902}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -2132,7 +2132,61 @@ static AnimTypeClass const Flag(
|
|||
0, // Loop start frame number.
|
||||
-1, // Ending frame of loop back.
|
||||
-1, // Number of animation stages.
|
||||
-1, // Number of times the animation loops.
|
||||
-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.
|
||||
true, // Theater specific art imagery?
|
||||
false, // Normalized animation rate?
|
||||
false, // Uses white translucent table?
|
||||
false, // Scorches the ground?
|
||||
false, // Forms a crater?
|
||||
false, // Sticks to unit in square?
|
||||
false, // Ground level animation?
|
||||
false, // Translucent colors in this animation?
|
||||
false, // Is this a flame thrower animation?
|
||||
0, // Damage to apply per tick (fixed point).
|
||||
1, // Delay between frames.
|
||||
0, // Starting frame number.
|
||||
0, // Loop start frame number.
|
||||
-1, // Ending frame of loop back.
|
||||
1, // Number of animation stages.
|
||||
-1, // Number of times the animation loops.
|
||||
VOC_NONE, // Sound effect to play.
|
||||
ANIM_NONE,
|
||||
-1, // Virtual stages
|
||||
0x100, // Virtual scale
|
||||
NULL, // Virtual name
|
||||
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, // Theater specific art imagery?
|
||||
false, // Normalized animation rate?
|
||||
false, // Uses white translucent table?
|
||||
false, // Scorches the ground?
|
||||
false, // Forms a crater?
|
||||
false, // Sticks to unit in square?
|
||||
false, // Ground level animation?
|
||||
false, // Translucent colors in this animation?
|
||||
false, // Is this a flame thrower animation?
|
||||
0, // Damage to apply per tick (fixed point).
|
||||
1, // Delay between frames.
|
||||
0, // Starting frame number.
|
||||
0, // Loop start frame number.
|
||||
-1, // Ending frame of loop back.
|
||||
1, // Number of animation stages.
|
||||
-1, // Number of times the animation loops.
|
||||
VOC_NONE, // Sound effect to play.
|
||||
ANIM_NONE
|
||||
);
|
||||
|
@ -2450,6 +2504,7 @@ void AnimTypeClass::Init_Heap(void)
|
|||
new AnimTypeClass(ParaBomb);
|
||||
new AnimTypeClass(MineExp1);
|
||||
new AnimTypeClass(Flag);
|
||||
new AnimTypeClass(Beacon);
|
||||
#ifdef FIXIT_ANTS
|
||||
new AnimTypeClass(Ant1Death);
|
||||
new AnimTypeClass(Ant2Death);
|
||||
|
@ -2459,6 +2514,7 @@ void AnimTypeClass::Init_Heap(void)
|
|||
new AnimTypeClass(Fire2Virtual);
|
||||
new AnimTypeClass(Fire1Virtual);
|
||||
new AnimTypeClass(Fire4Virtual);
|
||||
new AnimTypeClass(BeaconVirtual);
|
||||
}
|
||||
|
||||
/***********************************************************************************************
|
||||
|
@ -2529,6 +2585,9 @@ void AnimTypeClass::Init(TheaterType theater)
|
|||
((void const *&)anim.ImageData) = MFCD::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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -262,6 +262,8 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window) const
|
|||
void const * remap = NULL;
|
||||
ShapeFlags_Type flags = SHAPE_CENTER|SHAPE_WIN_REL;
|
||||
bool alt = false;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
/*
|
||||
** Some animations require special fixups.
|
||||
|
@ -277,6 +279,12 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window) const
|
|||
transtable = Map.UnitShadow;
|
||||
alt = true;
|
||||
break;
|
||||
|
||||
case ANIM_BEACON_VIRTUAL:
|
||||
width = 29;
|
||||
height = 39;
|
||||
flags = flags | SHAPE_BOTTOM | SHAPE_COMPACT;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -305,7 +313,7 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window) const
|
|||
*/
|
||||
if ((window == WINDOW_VIRTUAL) || (Fetch_Stage() < Class->Stages)) {
|
||||
// Add 'this' parameter to call new shape draw intercept. ST - 5/22/2019
|
||||
CC_Draw_Shape(this, shapefile, shapenum, x, y, window, flags, remap, transtable, DIR_N, Class->VirtualScale);
|
||||
CC_Draw_Shape(this, shapefile, shapenum, x, y, window, flags, remap, transtable, DIR_N, Class->VirtualScale, width, height);
|
||||
}
|
||||
}
|
||||
IsTheaterShape = false;
|
||||
|
@ -553,7 +561,8 @@ AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay
|
|||
IsInvisible(false),
|
||||
Delay(timedelay),
|
||||
Accum(0),
|
||||
AttachLayer(LAYER_NONE)
|
||||
AttachLayer(LAYER_NONE),
|
||||
KillTime(0ULL)
|
||||
{
|
||||
#ifdef VIC
|
||||
if (Class->Stages == -1) {
|
||||
|
@ -741,6 +750,19 @@ void AnimClass::AI(void)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** 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.
|
||||
|
@ -1255,4 +1277,20 @@ void AnimClass::Do_Atom_Damage(HousesType ownerhouse, CELL cell)
|
|||
//GamePalette.Set(FADE_PALETTE_SLOW, Call_Back); //TO_FIX. ST 5/8/2019
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void AnimClass::Set_Owner(HousesType owner)
|
||||
{
|
||||
OwnerHouse = owner;
|
||||
if (Target_Legal(VirtualAnimTarget)) {
|
||||
As_Animation(VirtualAnimTarget)->Set_Owner(owner);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimClass::Set_Visible_Flags(unsigned flags)
|
||||
{
|
||||
VisibleFlags = flags;
|
||||
if (Target_Legal(VirtualAnimTarget)) {
|
||||
As_Animation(VirtualAnimTarget)->Set_Visible_Flags(flags);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,15 +70,17 @@ class AnimClass : public ObjectClass, public 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;}
|
||||
static void Do_Atom_Damage(HousesType ownerhouse, CELL cell);
|
||||
|
||||
/*
|
||||
** 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 HousesType Owner(void) const {return OwnerHouse;};
|
||||
virtual bool Can_Place_Here(COORDINATE ) const {return true;}
|
||||
virtual bool Mark(MarkType mark=MARK_CHANGE);
|
||||
|
@ -179,10 +181,15 @@ class AnimClass : public ObjectClass, public StageClass {
|
|||
*/
|
||||
TARGET VirtualAnimTarget;
|
||||
|
||||
/*
|
||||
** 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];
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -281,6 +281,8 @@ struct SoundEffectNameStruct {
|
|||
{"MADEXPLO", 20, IN_NOVAR}, // VOC_MAD_EXPLODE MAD tank explodes
|
||||
{"SHKTROP1", 20, IN_NOVAR}, // VOC_SHOCK_TROOP1 Shock Trooper fires
|
||||
|
||||
{"BEACON", 10, IN_NOVAR}, // VOC_BEACON Beacon sound.
|
||||
|
||||
#endif
|
||||
};
|
||||
|
||||
|
|
|
@ -1850,7 +1850,7 @@ void BuildingClass::Active_Click_With(ActionType action, CELL cell)
|
|||
OutList.Add(EventClass(EventClass::SELL, TargetClass(this)));
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3152,7 +3152,7 @@ bool BuildingClass::Captured(HouseClass * newowner)
|
|||
assert(Buildings.ID(this) == ID);
|
||||
assert(IsActive);
|
||||
|
||||
if (Class->IsCaptureable && newowner != House) {
|
||||
if (Can_Capture() && newowner != House) {
|
||||
#ifdef TOFIX
|
||||
switch (Owner()) {
|
||||
case HOUSE_GOOD:
|
||||
|
@ -3457,6 +3457,19 @@ bool BuildingClass::Can_Demolish_Unit(void) const
|
|||
}
|
||||
|
||||
|
||||
bool BuildingClass::Can_Capture(void) const
|
||||
{
|
||||
bool can_capture = Class->IsCaptureable && Mission != MISSION_DECONSTRUCTION;
|
||||
|
||||
// Only allow capturing of multiplayer-owned structures
|
||||
if (Session.Type != GAME_NORMAL) {
|
||||
can_capture &= House->Class->House >= HOUSE_MULTI1 && House->Class->House <= HOUSE_MULTI8;
|
||||
}
|
||||
|
||||
return(can_capture);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************************
|
||||
* BuildingClass::Mission_Guard -- Handles guard mission for combat buildings. *
|
||||
* *
|
||||
|
@ -3759,6 +3772,7 @@ int BuildingClass::Mission_Deconstruction(void)
|
|||
Status = DURING;
|
||||
Begin_Mode(BSTATE_CONSTRUCTION);
|
||||
IsReadyToCommence = false;
|
||||
IsSurvivorless = true;
|
||||
break;
|
||||
}
|
||||
Transmit_Message(RADIO_RUN_AWAY);
|
||||
|
|
|
@ -246,6 +246,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 DirType Fire_Direction(void) const;
|
||||
virtual short const * Overlap_List(bool redraw=false) const;
|
||||
|
|
|
@ -1075,7 +1075,7 @@ void BulletClass::Bullet_Explodes(bool forced)
|
|||
//
|
||||
else if (aptr && anim == ANIM_ATOM_BLAST && aptr->OwnerHouse == HOUSE_NONE) {
|
||||
if (Payback && Payback->House && Payback->House->Class) {
|
||||
aptr->OwnerHouse = Payback->House->Class->House;
|
||||
aptr->Set_Owner(Payback->House->Class->House);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2818,7 +2818,7 @@ void CellClass::Flag_Create(void)
|
|||
CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord());
|
||||
}
|
||||
assert(CTFFlag != NULL);
|
||||
CTFFlag->OwnerHouse = Owner;
|
||||
CTFFlag->Set_Owner(Owner);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3430,11 +3430,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);
|
||||
void CC_Draw_Shape(void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation);
|
||||
|
||||
void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long virtualscale)
|
||||
void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata, void const * ghostdata, DirType rotation, long virtualscale, 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, rotation, virtualscale, NULL, HOUSE_NONE);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -2324,6 +2324,7 @@ typedef enum AnimType : char {
|
|||
ANIM_PARA_BOMB,
|
||||
ANIM_MINE_EXP1,
|
||||
ANIM_FLAG,
|
||||
ANIM_BEACON,
|
||||
|
||||
#ifdef FIXIT_ANTS
|
||||
ANIM_ANT1_DEATH,
|
||||
|
@ -2335,6 +2336,7 @@ typedef enum AnimType : char {
|
|||
ANIM_FIRE_MED_VIRTUAL, // Medium flame animation.
|
||||
ANIM_FIRE_MED2_VIRTUAL, // Medium flame animation (oranger).
|
||||
ANIM_FIRE_TINY_VIRTUAL, // Very tiny flames.
|
||||
ANIM_BEACON_VIRTUAL, // Beacon (virtual).
|
||||
|
||||
ANIM_COUNT,
|
||||
ANIM_FIRST=0
|
||||
|
@ -3333,6 +3335,8 @@ typedef enum VocType : short {
|
|||
VOC_MAD_EXPLODE,
|
||||
VOC_SHOCK_TROOP1,
|
||||
|
||||
VOC_BEACON,
|
||||
|
||||
#endif
|
||||
VOC_COUNT,
|
||||
VOC_FIRST=0
|
||||
|
|
|
@ -996,7 +996,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
|
||||
|
@ -2916,6 +2916,7 @@ void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2, bool addit
|
|||
HouseClass * hptr = HouseClass::As_Pointer(obj->Owner());
|
||||
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 = (hptr != NULL) && hptr->IsPlayerControl;
|
||||
|
@ -2942,6 +2943,7 @@ void DisplayClass::Select_These(COORDINATE coord1, COORDINATE coord2, bool addit
|
|||
** Only try to select objects that are allowed to be selected, and are within the bounding box.
|
||||
*/
|
||||
if ( aircraft->Class->IsSelectable &&
|
||||
!aircraft->Is_Cloaked(PlayerPtr) &&
|
||||
!aircraft->Is_Selected_By_Player() &&
|
||||
x >= x1 && x <= x2 && y >= y1 && y <= y2) {
|
||||
bool old_allow_voice = AllowVoice;
|
||||
|
|
|
@ -141,6 +141,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);
|
||||
|
@ -150,6 +151,7 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Restore_Carryover_Objects(cons
|
|||
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);
|
||||
|
||||
|
||||
|
||||
|
@ -418,14 +420,13 @@ void Display_Briefing_Text_GlyphX()
|
|||
|
||||
void On_Sound_Effect(int sound_index, int variation, COORDINATE coord, int house)
|
||||
{
|
||||
// MBL 06.17.2019
|
||||
int voc = sound_index;
|
||||
if (voc == VOC_NONE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// MBL 06.17.2019 - Borrowed from RedAlert\AUDIO.CPP Sound_Effect()
|
||||
// Borrowed from RedAlert\AUDIO.CPP Sound_Effect()
|
||||
//
|
||||
#if 1
|
||||
/*
|
||||
|
@ -487,14 +488,13 @@ void On_Sound_Effect(int sound_index, int variation, COORDINATE coord, int house
|
|||
|
||||
|
||||
#if 0
|
||||
// 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()
|
||||
//
|
||||
char const * ext = ""; // ".AUD";
|
||||
#ifdef TIBERIAN_DAWN
|
||||
|
@ -531,10 +531,8 @@ void On_Sound_Effect(int sound_index, int variation, COORDINATE coord, int house
|
|||
}
|
||||
|
||||
|
||||
// void On_Speech(int speech_index) // MBL 02.06.2020
|
||||
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);
|
||||
}
|
||||
|
@ -622,7 +620,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();
|
||||
|
@ -1034,7 +1031,7 @@ void GlyphX_Assign_Houses(void)
|
|||
UseGlyphXStartLocations = true;
|
||||
}
|
||||
}
|
||||
if (!preassigned) {
|
||||
if (!preassigned && i < MAX_PLAYERS) {
|
||||
random_start_locations[num_random_start_locations] = num_start_locations;
|
||||
num_random_start_locations++;
|
||||
}
|
||||
|
@ -1943,13 +1940,13 @@ extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const cha
|
|||
|
||||
result = Load_Game(file_path_and_name);
|
||||
|
||||
// MBL 07.21.2020
|
||||
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);
|
||||
|
@ -2148,6 +2145,40 @@ 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;
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
* Is_Legacy_Render_Enabled -- Is the legacy rendering enabled?
|
||||
*
|
||||
* In:
|
||||
*
|
||||
* Out: True if only one human player
|
||||
*
|
||||
*
|
||||
*
|
||||
* History: 8/25/2020 5:55PM - ST
|
||||
**************************************************************************************************/
|
||||
bool Is_Legacy_Render_Enabled(void)
|
||||
{
|
||||
return DLLExportClass::Legacy_Render_Enabled();
|
||||
}
|
||||
|
||||
|
||||
/**************************************************************************************************
|
||||
* DLLExportClass::Init -- Init the class
|
||||
*
|
||||
|
@ -4096,6 +4127,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;
|
||||
Keyboard->MouseQX = x1;
|
||||
Keyboard->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;
|
||||
}
|
||||
|
@ -4220,7 +4276,7 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(Sidebar
|
|||
|
||||
switch (request_type) {
|
||||
|
||||
// MBL 06.02.2020 - Changing right-click support for first put building on hold, and then subsequenct right-clicks to decrement that queue count for 1x or 5x; Then, 1x or 5x Left click will resume from hold
|
||||
// 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:
|
||||
|
||||
|
@ -4461,7 +4517,7 @@ bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_i
|
|||
sidebar_entry.SuperWeaponType = SW_NONE;
|
||||
|
||||
if (tech) {
|
||||
sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias; // MBL: If this gets modified, also modify below for skirmish and multiplayer
|
||||
sidebar_entry.Cost = tech->Cost * PlayerPtr->CostBias; // If this gets modified, also modify below for skirmish and multiplayer
|
||||
sidebar_entry.PowerProvided = 0;
|
||||
sidebar_entry.BuildTime = tech->Time_To_Build(PlayerPtr->Class->House); // sidebar_entry.BuildTime = tech->Time_To_Build() / 60;
|
||||
strncpy(sidebar_entry.AssetName, tech->IniName, CNC_OBJECT_ASSET_NAME_LENGTH);
|
||||
|
@ -4619,7 +4675,7 @@ bool DLLExportClass::Get_Sidebar_State(uint64 player_id, unsigned char *buffer_i
|
|||
|
||||
if (tech) {
|
||||
|
||||
// MBL 06.22.2020 - Updated to apply and difficulty abd/or faction price modifier; See https://jaas.ea.com/browse/TDRA-6864
|
||||
// Updated to apply and difficulty abd/or faction price modifier; See https://jaas.ea.com/browse/TDRA-6864
|
||||
// If this gets modified, also modify above for non-skirmish / non-multiplayer
|
||||
//
|
||||
// sidebar_entry.Cost = tech->Cost;
|
||||
|
@ -6014,6 +6070,22 @@ bool DLLExportClass::Get_Shroud_State(uint64 player_id, unsigned char *buffer_in
|
|||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** Apply mobile gap generators
|
||||
*/
|
||||
static unsigned int _shroud_bits[UNIT_MAX];
|
||||
|
||||
if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) {
|
||||
for (int index = 0; index < Units.Count(); index++) {
|
||||
UnitClass * obj = Units.Ptr(index);
|
||||
if (obj->Class->IsGapper && obj->IsActive && obj->Strength) {
|
||||
if (!obj->House->Is_Ally(PlayerPtr)) {
|
||||
_shroud_bits[index] = obj->Apply_Temporary_Jamming_Shroud(PlayerPtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CNCShroudStruct *shroud = (CNCShroudStruct*) buffer_in;
|
||||
|
||||
unsigned int memory_needed = sizeof(*shroud) + 256; // Base amount needed. Will need more depending on how many entries there are
|
||||
|
@ -6087,6 +6159,17 @@ bool DLLExportClass::Get_Shroud_State(uint64 player_id, unsigned char *buffer_in
|
|||
|
||||
shroud->Count = entry_index;
|
||||
|
||||
if (GAME_TO_PLAY == GAME_GLYPHX_MULTIPLAYER) {
|
||||
for (int index = 0; index < Units.Count(); index++) {
|
||||
UnitClass * obj = Units.Ptr(index);
|
||||
if (obj->Class->IsGapper && obj->IsActive && obj->Strength) {
|
||||
if (!obj->House->Is_Ally(PlayerPtr)) {
|
||||
obj->Unapply_Temporary_Jamming_Shroud(PlayerPtr, _shroud_bits[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -7551,7 +7634,7 @@ void DLLExportClass::Team_Units_Formation_Toggle_On(uint64 player_id)
|
|||
}
|
||||
|
||||
//
|
||||
// MBL 03.23.2020: Code here copied and modified from Toggle_Formation(), since obj->IsSelected is not supported
|
||||
// Code here copied and modified from Toggle_Formation(), since obj->IsSelected is not supported
|
||||
// Replacing with ObjectClass::Is_Selected_By_Player(HouseClass *player);
|
||||
//
|
||||
|
||||
|
@ -7847,7 +7930,37 @@ 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ struct CarryoverObjectStruct;
|
|||
**
|
||||
**
|
||||
*/
|
||||
#define CNC_DLL_API_VERSION 0x101
|
||||
#include "DLLInterfaceVersion.h"
|
||||
|
||||
|
||||
|
||||
|
@ -425,7 +425,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,
|
||||
};
|
||||
|
||||
|
||||
|
@ -476,6 +480,18 @@ enum GameRequestEnum {
|
|||
};
|
||||
|
||||
|
||||
/**************************************************************************************
|
||||
**
|
||||
** Beacon Requests
|
||||
**
|
||||
**
|
||||
*/
|
||||
enum BeaconRequestEnum {
|
||||
INPUT_BEACON_NONE,
|
||||
INPUT_BEACON_PLACE,
|
||||
};
|
||||
|
||||
|
||||
/**************************************************************************************
|
||||
**
|
||||
** Special Keys
|
||||
|
|
|
@ -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
|
|
@ -282,7 +282,7 @@ EventClass::EventClass(EventType type, TargetClass src, TargetClass 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 = PlayerPtr->ID;
|
||||
Type = ANIMATION;
|
||||
|
@ -290,6 +290,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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -600,14 +601,17 @@ void EventClass::Execute(void)
|
|||
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.Owner == HOUSE_NONE || Data.Anim.What != ANIM_MOVE_FLASH)
|
||||
{
|
||||
anim->Set_Visible_Flags(static_cast<unsigned int>(-1));
|
||||
}
|
||||
else
|
||||
{
|
||||
anim->Set_Visible_Flags(1 << Data.Anim.Owner);
|
||||
anim->Set_Owner(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;
|
||||
|
|
|
@ -136,6 +136,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
|
||||
|
@ -239,7 +240,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);
|
||||
EventClass(void *ptr, unsigned long size);
|
||||
EventClass(EventType type, void *ptr, unsigned long size);
|
||||
|
||||
|
|
|
@ -946,8 +946,12 @@ 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 (What_Am_I() == RTTI_INFANTRY) {
|
||||
Assign_Destination(TarCom);
|
||||
} else if (found) {
|
||||
Assign_Destination(::As_Target(trycell));
|
||||
} else {
|
||||
|
||||
|
@ -1005,6 +1009,20 @@ int FootClass::Mission_Guard_Area(void)
|
|||
ArchiveTarget = ::As_Target(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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** If this is a bomber type infantry and the current target is a building, then go into
|
||||
** sabotage mode if not already.
|
||||
|
@ -1311,7 +1329,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.
|
||||
|
||||
|
|
|
@ -547,7 +547,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, DirType rotation = DIR_N);
|
||||
|
||||
// Added for draw intercept. ST - 1/17/2019 12:31PM
|
||||
void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N, long virtualscale = 0x0100);
|
||||
void CC_Draw_Shape(const ObjectClass *object, void const * shapefile, int shapenum, int x, int y, WindowNumberType window, ShapeFlags_Type flags, void const * fadingdata = 0, void const * ghostdata = 0, DirType rotation = DIR_N, long virtualscale = 0x0100, int width = 0, int height = 0);
|
||||
void CC_Draw_Shape(const 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, DirType rotation = DIR_N, long virtualscale = 0x0100, char override_owner = HOUSE_NONE);
|
||||
|
||||
// Added for pip draw intercept - SKY
|
||||
|
|
|
@ -1821,18 +1821,16 @@ void HouseClass::Attacked(BuildingClass* source)
|
|||
{
|
||||
assert(Houses.ID(this) == ID);
|
||||
|
||||
bool expired = SpeakAttackDelay == 0;
|
||||
bool spoke = false;
|
||||
|
||||
#ifdef FIXIT_BASE_ANNOUNCE
|
||||
// if (SpeakAttackDelay == 0 && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) {
|
||||
if (expired && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) {
|
||||
if (SpeakAttackDelay == 0 && ((Session.Type == GAME_NORMAL && IsPlayerControl) || PlayerPtr->Class->House == Class->House)) {
|
||||
#else
|
||||
// if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) {
|
||||
if (expired && PlayerPtr->Class->House == Class->House) {
|
||||
if (SpeakAttackDelay == 0 && PlayerPtr->Class->House == Class->House) {
|
||||
#endif
|
||||
Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0);
|
||||
spoke = true;
|
||||
if (Session.Type == 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 = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); // 2 minutes
|
||||
|
@ -1847,22 +1845,6 @@ void HouseClass::Attacked(BuildingClass* source)
|
|||
HouseTriggers[Class->House][index]->Spring(TEVENT_ATTACKED);
|
||||
}
|
||||
}
|
||||
|
||||
// MBL 07.07.2020 - CNC Patch 3, fix for not working for all players in MP, per https://jaas.ea.com/browse/TDRA-7249
|
||||
// Separated to here as did not want to change any logic around the HouseTriggers[] Spring events
|
||||
//
|
||||
if (expired == true && spoke == false)
|
||||
{
|
||||
if (Session.Type != GAME_NORMAL) // Multiplayer
|
||||
{
|
||||
Speak(VOX_BASE_UNDER_ATTACK, this);
|
||||
spoke = true;
|
||||
|
||||
// SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE * Rule.SpeakDelay); // 2 minutes
|
||||
// SpeakAttackDelay = Options.Normalize_Delay(TICKS_PER_MINUTE/2); // 30 seconds as requested
|
||||
SpeakAttackDelay = Options.Normalize_Delay( (TICKS_PER_MINUTE/2)+(TICKS_PER_SECOND*5) ); // Tweaked for accuracy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -703,6 +703,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
|
||||
|
@ -782,6 +783,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
|
||||
|
||||
/*
|
||||
** File I/O.
|
||||
*/
|
||||
|
|
|
@ -622,8 +622,14 @@ void InfantryClass::Per_Cell_Process(PCPType why)
|
|||
*/
|
||||
if (Mission == MISSION_CAPTURE) {
|
||||
TechnoClass * tech = cellptr->Cell_Building();
|
||||
|
||||
if (tech == NULL) tech = cellptr->Cell_Techno();
|
||||
if (tech != NULL) {
|
||||
if ((tech->As_Target() == NavCom || tech->As_Target() == TarCom) && !tech->Can_Capture()) {
|
||||
tech = NULL;
|
||||
Assign_Destination(TARGET_NONE);
|
||||
}
|
||||
} else {
|
||||
tech = cellptr->Cell_Techno();
|
||||
}
|
||||
if (tech != NULL && (tech->As_Target() == NavCom || tech->As_Target() == TarCom)) {
|
||||
if (*this == INFANTRY_RENOVATOR) {
|
||||
|
||||
|
@ -638,11 +644,14 @@ void InfantryClass::Per_Cell_Process(PCPType why)
|
|||
#else
|
||||
if (tech->House->Is_Ally(House)) {
|
||||
#endif
|
||||
if (tech->Trigger.Is_Valid()) {
|
||||
tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this);
|
||||
}
|
||||
tech->Renovate();
|
||||
} else {
|
||||
bool iscapturable = false;
|
||||
if (tech->What_Am_I() == RTTI_BUILDING) {
|
||||
iscapturable = ((BuildingClass *)tech)->Class->IsCaptureable;
|
||||
iscapturable = tech->Can_Capture();
|
||||
}
|
||||
#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98
|
||||
if (tech->Health_Ratio() <= EngineerCaptureLevel && iscapturable) {
|
||||
|
@ -1207,7 +1216,7 @@ void InfantryClass::Assign_Target(TARGET target)
|
|||
*/
|
||||
if (!Target_Legal(NavCom) && Class->IsCapture && !Is_Weapon_Equipped()) {
|
||||
BuildingClass const * building = As_Building(target);
|
||||
if (building != NULL && building->Class->IsCaptureable) {
|
||||
if (building != NULL && building->Can_Capture()) {
|
||||
Assign_Destination(target);
|
||||
}
|
||||
}
|
||||
|
@ -2918,7 +2927,7 @@ ActionType InfantryClass::What_Action(ObjectClass const * object) const
|
|||
return(ACTION_GREPAIR);
|
||||
} else {
|
||||
|
||||
if (bldg->Class->IsCaptureable) {
|
||||
if (bldg->Can_Capture()) {
|
||||
#ifdef FIXIT_ENGINEER // checked - ajw 9/28/98
|
||||
if (bldg->Health_Ratio() <= EngineerCaptureLevel) {
|
||||
#else
|
||||
|
@ -2929,7 +2938,7 @@ ActionType InfantryClass::What_Action(ObjectClass const * object) const
|
|||
return(ACTION_DAMAGE);
|
||||
}
|
||||
|
||||
// if (bldg->Health_Ratio() <= Rule.ConditionRed && bldg->Class->IsCaptureable) {
|
||||
// if (bldg->Health_Ratio() <= Rule.ConditionRed && bldg->Can_Capture()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3050,7 +3059,7 @@ ActionType InfantryClass::What_Action(ObjectClass const * object) const
|
|||
if (Class->IsCapture && action == ACTION_ATTACK) {
|
||||
if (!House->Is_Ally(object) && (
|
||||
//Disable capturing of helicopters (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) )
|
||||
(object->What_Am_I() == RTTI_BUILDING && object->Can_Capture()) )
|
||||
) {
|
||||
|
||||
if (*this == INFANTRY_THIEF && (object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->Capacity == 0)) {
|
||||
|
@ -3284,7 +3293,7 @@ int InfantryClass::Mission_Attack(void)
|
|||
return(1);
|
||||
}
|
||||
|
||||
if (Class->IsCapture && As_Building(TarCom) != NULL && As_Building(TarCom)->Class->IsCaptureable) {
|
||||
if (Class->IsCapture && As_Building(TarCom) != NULL && As_Building(TarCom)->Can_Capture()) {
|
||||
Assign_Destination(TarCom);
|
||||
Assign_Mission(MISSION_CAPTURE);
|
||||
return(1);
|
||||
|
@ -3914,7 +3923,7 @@ void InfantryClass::Doing_AI(void)
|
|||
}
|
||||
}
|
||||
if (anim != NULL) {
|
||||
anim->OwnerHouse = House->Class->House;
|
||||
anim->Set_Owner(House->Class->House);
|
||||
}
|
||||
delete this;
|
||||
return;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
Electronic Arts Inc. released only TiberianDawn.dll, RedAlert.dll and
|
||||
the Command & Conquer Map Editor and their corresponding source code
|
||||
under the GPL V3 below, with additional terms at the bottom.
|
||||
Electronic Arts Inc. released only TiberianDawn.dll and RedAlert.dll and their corresponding source code under the GPL V3 below, with additional terms at the bottom.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
@ -711,4 +709,4 @@ PROVIDED BY ELECTRONIC ARTS OR ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A
|
|||
WARRANTY. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON
|
||||
IMPLIED WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
|
||||
CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY NOT APPLY
|
||||
TO YOU.
|
||||
TO YOU.
|
|
@ -428,7 +428,7 @@ dxisbig:
|
|||
#if (0)
|
||||
|
||||
/*
|
||||
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/MiscAsm.cpp#97 $
|
||||
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/MiscAsm.cpp#131 $
|
||||
;***************************************************************************
|
||||
;** 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 **
|
||||
;***************************************************************************
|
||||
|
|
|
@ -432,6 +432,15 @@ bool ObjectClass::Can_Demolish_Unit(void) const
|
|||
}
|
||||
|
||||
|
||||
bool ObjectClass::Can_Capture(void) const
|
||||
{
|
||||
assert(this != 0);
|
||||
assert(IsActive);
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************************
|
||||
* ObjectClass::Can_Player_Fire -- Can the player give this object an attack mission? *
|
||||
* *
|
||||
|
|
|
@ -165,6 +165,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;
|
||||
|
||||
|
|
|
@ -1409,7 +1409,7 @@ bool RadarClass::Jam_Cell(CELL cell, HouseClass * house/*KO, bool shadeit*/)
|
|||
|
||||
for (int i = 0; i < Session.Players.Count(); i++) {
|
||||
HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
|
||||
if (player_house->IsHuman && player_house != house) {
|
||||
if (player_house->IsHuman && player_house != house && !house->Is_Ally(player_house)) {
|
||||
Shroud_Cell(cell, player_house);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -514,6 +514,7 @@
|
|||
<ClInclude Include="DIBUTIL.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" />
|
||||
|
|
|
@ -1766,6 +1766,9 @@
|
|||
<ClInclude Include="Resource\resource.h">
|
||||
<Filter>Source Files\Resource</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="DLLInterfaceVersion.h">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="COORDA.ASM">
|
||||
|
|
|
@ -319,6 +319,14 @@ Theme.Stop();
|
|||
return(false);
|
||||
}
|
||||
|
||||
/* Swap Lt. Blue and Blue color remaps in skirmish/multiplayer */
|
||||
if (Session.Type != GAME_NORMAL) {
|
||||
RemapControlType temp;
|
||||
memcpy(&temp, &ColorRemaps[PCOLOR_LTBLUE], sizeof(RemapControlType));
|
||||
memcpy(&ColorRemaps[PCOLOR_LTBLUE], &ColorRemaps[PCOLOR_BLUE], sizeof(RemapControlType));
|
||||
memcpy(&ColorRemaps[PCOLOR_BLUE], &temp, sizeof(RemapControlType));
|
||||
}
|
||||
|
||||
/*
|
||||
** Play the winning movie and then start the next scenario.
|
||||
*/
|
||||
|
@ -2293,11 +2301,15 @@ bool Read_Scenario_INI(char * fname, bool )
|
|||
Rule.Difficulty(ini);
|
||||
|
||||
/*
|
||||
** Fix a legacy bug with England and France country bonuses
|
||||
** - Fix a legacy bug with England and France country bonuses.
|
||||
** - Use ore growth and spread values from the special settings.
|
||||
*/
|
||||
if (Session.Type != GAME_NORMAL) {
|
||||
HouseTypeClass::As_Reference(HOUSE_ENGLAND).ArmorBias = fixed(9, 10);
|
||||
HouseTypeClass::As_Reference(HOUSE_FRANCE).ROFBias = fixed(9, 10);
|
||||
|
||||
Rule.IsTGrowth = Special.IsTGrowth;
|
||||
Rule.IsTSpread = Special.IsTSpread;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2477,6 +2489,7 @@ bool Read_Scenario_INI(char * fname, bool )
|
|||
** Blue Lakes multiplayer map cell 11937 - LAND_RIVER
|
||||
** USSR mission 13 - fixup trigger action
|
||||
** Allied mission 5B - fail mission if spy re-boards the transport at mission start
|
||||
** Allied mission 8A - Germany is allied with Greece and itself
|
||||
** Allied mission 9A - fail mission if tech center is destroyed before being spied
|
||||
** Aftermath: Brother in Arms - have transports move to separate waypoints
|
||||
** Aftermath: Let's Make a Steal - Make the pillboxes un-capturable
|
||||
|
@ -2525,6 +2538,17 @@ bool Read_Scenario_INI(char * fname, bool )
|
|||
frc1_trigger->Action1.Trigger = los3_trigger;
|
||||
}
|
||||
|
||||
if (_stricmp(Scen.ScenarioName, "scg08ea.ini") == 0) {
|
||||
for (int house = HOUSE_FIRST; house < HOUSE_COUNT; ++house) {
|
||||
HouseClass* ptr = Houses.Ptr(house);
|
||||
if (ptr != NULL && ptr->IsActive) {
|
||||
if (ptr->Class->House == HOUSE_GREECE || ptr->Class->House == HOUSE_GERMANY) {
|
||||
ptr->Make_Ally(HOUSE_GERMANY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_stricmp(Scen.ScenarioName, "scg09ea.ini") == 0) {
|
||||
TriggerTypeClass* spyd_trigger = TriggerTypeClass::From_Name("Spyd");
|
||||
assert(spyd_trigger != NULL);
|
||||
|
@ -3202,6 +3226,7 @@ static void Create_Units(bool official)
|
|||
if (numtaken == 0) {
|
||||
int pick = Random_Pick(0, num_waypts-1);
|
||||
centroid = waypts[pick];
|
||||
hptr->StartLocationOverride = pick;
|
||||
taken[pick] = true;
|
||||
numtaken++;
|
||||
} else {
|
||||
|
@ -3251,6 +3276,7 @@ static void Create_Units(bool official)
|
|||
** Assign this best position to the house.
|
||||
*/
|
||||
centroid = waypts[best];
|
||||
hptr->StartLocationOverride = best;
|
||||
taken[best] = true;
|
||||
numtaken++;
|
||||
}
|
||||
|
|
|
@ -1647,7 +1647,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())) {
|
||||
BEnd(BENCH_EVAL_OBJECT);
|
||||
return(false);
|
||||
}
|
||||
|
@ -3378,6 +3378,13 @@ BulletClass * TechnoClass::Fire_At(TARGET target, int which)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** For electric zaps, immediately perform bullet logic.
|
||||
*/
|
||||
if (weapon->IsElectric) {
|
||||
bullet->AI();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -3560,7 +3567,7 @@ ActionType TechnoClass::What_Action(ObjectClass const * object) const
|
|||
((weapon != NULL) && weapon->Bullet->IsAntiAircraft) ||
|
||||
(object->Is_Techno() && (((TechnoClass *)object)->Height == 0))) {
|
||||
if (Can_Player_Move() || In_Range(object, primary)) {
|
||||
if (In_Range(object, primary) || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCapture && object->What_Am_I() == RTTI_BUILDING && ((BuildingClass *)object)->Class->IsCaptureable)) {
|
||||
if (In_Range(object, primary) || (What_Am_I() == RTTI_INFANTRY && ((InfantryClass *)this)->Class->IsCapture && object->What_Am_I() == RTTI_BUILDING && object->Can_Capture())) {
|
||||
return(ACTION_ATTACK);
|
||||
} else {
|
||||
if (!Can_Player_Move()) {
|
||||
|
|
|
@ -100,6 +100,32 @@
|
|||
#include "function.h"
|
||||
#include "COORDA.h"
|
||||
|
||||
|
||||
extern void Logic_Switch_Player_Context(ObjectClass *object);
|
||||
extern void Logic_Switch_Player_Context(HouseClass *object);
|
||||
extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type);
|
||||
extern bool Is_Legacy_Render_Enabled(void);
|
||||
|
||||
|
||||
static int _GapShroudXTable[]={
|
||||
-1, 0, 1,
|
||||
-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,
|
||||
-1, 0, 1
|
||||
};
|
||||
static int _GapShroudYTable[]={
|
||||
-3,-3,-3,
|
||||
-2,-2,-2,-2,-2,
|
||||
-1,-1,-1,-1,-1,
|
||||
0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2,
|
||||
3, 3, 3
|
||||
};
|
||||
|
||||
/***********************************************************************************************
|
||||
* Recoil_Adjust -- Adjust pixel values in direction specified. *
|
||||
* *
|
||||
|
@ -290,7 +316,8 @@ UnitClass::UnitClass(UnitType classid, HousesType house) :
|
|||
ShroudBits(0xFFFFFFFFUL),
|
||||
ShroudCenter(0),
|
||||
Reload(0),
|
||||
SecondaryFacing(PrimaryFacing)
|
||||
SecondaryFacing(PrimaryFacing),
|
||||
TiberiumUnloadRefinery(TARGET_NONE)
|
||||
{
|
||||
Reload = 0;
|
||||
House->Tracking_Add(this);
|
||||
|
@ -414,6 +441,20 @@ void UnitClass::AI(void)
|
|||
IsHarvesting = false;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the unload refinery if not haresting or entering a refinery.
|
||||
*/
|
||||
if (Class->IsToHarvest) {
|
||||
if (Mission != MISSION_HARVEST) {
|
||||
if (Mission != MISSION_ENTER ||
|
||||
!In_Radio_Contact() ||
|
||||
Contact_With_Whom()->What_Am_I() != RTTI_BUILDING ||
|
||||
*((BuildingClass*)Contact_With_Whom()) != STRUCT_REFINERY) {
|
||||
TiberiumUnloadRefinery = TARGET_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Handle combat logic for this unit. It will determine if it has a target and
|
||||
** if so, if conditions are favorable for firing. When conditions permit, the
|
||||
|
@ -2275,29 +2316,47 @@ bool UnitClass::Goto_Tiberium(int rad)
|
|||
int tiberium = 0;
|
||||
int besttiberium = 0;
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
|
||||
/*
|
||||
** 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;
|
||||
tiberium = Tiberium_Check(cell, x, -radius);
|
||||
tiberium = Tiberium_Check(cell, corners[0][0], corners[0][1]);
|
||||
if (tiberium > besttiberium) {
|
||||
bestcell = cell;
|
||||
besttiberium = tiberium;
|
||||
}
|
||||
|
||||
cell = center;
|
||||
tiberium = Tiberium_Check(cell, x, +radius);
|
||||
tiberium = Tiberium_Check(cell, corners[1][0], corners[1][1]);
|
||||
if (tiberium > besttiberium) {
|
||||
bestcell = cell;
|
||||
besttiberium = tiberium;
|
||||
}
|
||||
|
||||
cell = center;
|
||||
tiberium = Tiberium_Check(cell, -radius, x);
|
||||
tiberium = Tiberium_Check(cell, corners[2][0], corners[2][1]);
|
||||
if (tiberium > besttiberium) {
|
||||
bestcell = cell;
|
||||
besttiberium = tiberium;
|
||||
}
|
||||
|
||||
cell = center;
|
||||
tiberium = Tiberium_Check(cell, +radius, x);
|
||||
tiberium = Tiberium_Check(cell, corners[3][0], corners[3][1]);
|
||||
if (tiberium > besttiberium) {
|
||||
bestcell = cell;
|
||||
besttiberium = tiberium;
|
||||
|
@ -2411,10 +2470,6 @@ bool UnitClass::Harvesting(void)
|
|||
* HISTORY: *
|
||||
* 07/18/1994 JLB : Created. *
|
||||
*=============================================================================================*/
|
||||
extern void Logic_Switch_Player_Context(ObjectClass *object);
|
||||
extern void Logic_Switch_Player_Context(HouseClass *object);
|
||||
extern void On_Special_Weapon_Targetting(const HouseClass* player_ptr, SpecialWeaponType weapon_type);
|
||||
|
||||
int UnitClass::Mission_Unload(void)
|
||||
{
|
||||
assert(Units.ID(this) == ID);
|
||||
|
@ -2871,6 +2926,7 @@ int UnitClass::Mission_Harvest(void)
|
|||
/*
|
||||
** Look for ore where we last found some - mine the same patch
|
||||
*/
|
||||
TiberiumUnloadRefinery = TARGET_NONE;
|
||||
if (Target_Legal(ArchiveTarget)) {
|
||||
Assign_Destination(ArchiveTarget);
|
||||
ArchiveTarget = 0;
|
||||
|
@ -2952,24 +3008,17 @@ int UnitClass::Mission_Harvest(void)
|
|||
if (!Target_Legal(NavCom)) {
|
||||
|
||||
/*
|
||||
** Find nearby refinery and head to it?
|
||||
** Find nearby refinery and head to it.
|
||||
*/
|
||||
BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false);
|
||||
|
||||
/*
|
||||
** Since the refinery said it was ok to load, establish radio
|
||||
** contact with the refinery and then await docking orders.
|
||||
*/
|
||||
if (nearest != NULL && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
|
||||
Status = HEADINGHOME;
|
||||
if (nearest->House == PlayerPtr && (PlayerPtr->Capacity - PlayerPtr->Tiberium) < 300 && PlayerPtr->Capacity > 500 && (PlayerPtr->ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) {
|
||||
Speak(VOX_NEED_MO_CAPACITY);
|
||||
}
|
||||
} else {
|
||||
ScenarioInit++;
|
||||
nearest = Find_Docking_Bay(STRUCT_REFINERY, false);
|
||||
ScenarioInit--;
|
||||
if (nearest != NULL) {
|
||||
BuildingClass * nearest = Find_Best_Refinery();
|
||||
if (nearest != NULL) {
|
||||
TiberiumUnloadRefinery = nearest->As_Target();
|
||||
if (Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
|
||||
Status = HEADINGHOME;
|
||||
if (nearest->House == PlayerPtr && (PlayerPtr->Capacity - PlayerPtr->Tiberium) < 300 && PlayerPtr->Capacity > 500 && (PlayerPtr->ActiveBScan & (STRUCTF_REFINERY | STRUCTF_CONST))) {
|
||||
Speak(VOX_NEED_MO_CAPACITY);
|
||||
}
|
||||
} else {
|
||||
Assign_Destination(::As_Target(Nearby_Location(nearest)));
|
||||
}
|
||||
}
|
||||
|
@ -2990,6 +3039,7 @@ int UnitClass::Mission_Harvest(void)
|
|||
** no where to go.
|
||||
*/
|
||||
case GOINGTOIDLE:
|
||||
TiberiumUnloadRefinery = TARGET_NONE;
|
||||
if (IsUseless) {
|
||||
if (House->ActiveBScan & STRUCTF_REPAIR) {
|
||||
Assign_Mission(MISSION_REPAIR);
|
||||
|
@ -4363,6 +4413,103 @@ fixed UnitClass::Tiberium_Load(void) const
|
|||
}
|
||||
|
||||
|
||||
BuildingClass* UnitClass::Tiberium_Unload_Refinery(void) const
|
||||
{
|
||||
return Target_Legal(TiberiumUnloadRefinery) ? As_Building(TiberiumUnloadRefinery) : NULL;
|
||||
}
|
||||
|
||||
|
||||
struct RefineryData
|
||||
{
|
||||
BuildingClass* Refinery;
|
||||
int Distance;
|
||||
int Harvesters;
|
||||
};
|
||||
|
||||
static bool operator==(const RefineryData& lhs, const RefineryData& rhs)
|
||||
{
|
||||
return lhs.Refinery == rhs.Refinery;
|
||||
}
|
||||
|
||||
static bool operator!=(const RefineryData& lhs, const RefineryData& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
static int _refinery_compare(const void * left, const void * right)
|
||||
{
|
||||
const RefineryData& lhs = *reinterpret_cast<const RefineryData*>(left);
|
||||
const RefineryData& rhs = *reinterpret_cast<const RefineryData*>(right);
|
||||
if (lhs.Distance < rhs.Distance) {
|
||||
return -1;
|
||||
} else if (rhs.Distance < lhs.Distance) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BuildingClass* UnitClass::Find_Best_Refinery(void) const
|
||||
{
|
||||
static DynamicVectorClass<RefineryData> _refineries;
|
||||
|
||||
_refineries.Clear();
|
||||
for (int i = 0; i < Buildings.Count(); ++i) {
|
||||
BuildingClass* refinery = Buildings.Ptr(i);
|
||||
if (refinery != NULL &&
|
||||
refinery->House == House &&
|
||||
!refinery->IsInLimbo &&
|
||||
*refinery == STRUCT_REFINERY &&
|
||||
Map[refinery->Center_Coord()].Zones[Techno_Type_Class()->MZone] == Map[Center_Coord()].Zones[Techno_Type_Class()->MZone]) {
|
||||
_refineries.Add(RefineryData{ refinery, Distance(refinery), 0 });
|
||||
}
|
||||
}
|
||||
|
||||
// Base case for zero or one refineries.
|
||||
if (_refineries.Count() == 0) {
|
||||
return NULL;
|
||||
} else if (_refineries.Count() == 1) {
|
||||
return _refineries[0].Refinery;
|
||||
}
|
||||
|
||||
// Count harvesters going to each refinery as well as the total.
|
||||
int num_harvesters = 0;
|
||||
for (int i = 0; i < Units.Count(); ++i) {
|
||||
UnitClass* unit = Units.Ptr(i);
|
||||
if (unit->IsActive && Class->IsToHarvest && unit->House == House) {
|
||||
BuildingClass* refinery = unit->Tiberium_Unload_Refinery();
|
||||
if (refinery != NULL) {
|
||||
int index = _refineries.ID(RefineryData{ refinery });
|
||||
assert(index >= 0);
|
||||
_refineries[index].Harvesters++;
|
||||
num_harvesters++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by distance (special case for 2 refineries as that's a single swap).
|
||||
if (_refineries.Count() == 2) {
|
||||
if (_refineries[0].Distance > _refineries[1].Distance) {
|
||||
RefineryData temp = _refineries[0];
|
||||
_refineries[0] = _refineries[1];
|
||||
_refineries[1] = temp;
|
||||
}
|
||||
} else {
|
||||
qsort(&_refineries[0], _refineries.Count(), sizeof(RefineryData), _refinery_compare);
|
||||
}
|
||||
|
||||
// Evenly distribute harvesters among refineries.
|
||||
int harvesters_per_refinery = (num_harvesters + _refineries.Count() - 1) / _refineries.Count();
|
||||
for (int i = 0; i < _refineries.Count(); ++i) {
|
||||
if (_refineries[i].Harvesters < harvesters_per_refinery) {
|
||||
return _refineries[i].Refinery;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back on closest refinery
|
||||
return _refineries[0].Refinery;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************************
|
||||
* UnitClass::Offload_Tiberium_Bail -- Offloads one Tiberium quantum from the object. *
|
||||
* *
|
||||
|
@ -4517,7 +4664,7 @@ void UnitClass::Overrun_Square(CELL cell, bool threaten)
|
|||
if (object->Height == 0) {
|
||||
AnimClass* anim = new AnimClass(ANIM_CORPSE1, object->Center_Coord());
|
||||
if (anim != NULL) {
|
||||
anim->OwnerHouse = object->Owner();
|
||||
anim->Set_Owner(object->Owner());
|
||||
}
|
||||
}
|
||||
object->Record_The_Kill(this);
|
||||
|
@ -5022,64 +5169,143 @@ bool UnitClass::Limbo(void)
|
|||
return(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************************************
|
||||
* UnitClass::Apply_Temporary_Jamming_Shroud -- Apply a temporary gap generator shroud effect *
|
||||
* *
|
||||
* This is intended as a temporary effect that is active only during export of the *
|
||||
* shroud data *
|
||||
* *
|
||||
* INPUT: House to apply effect for *
|
||||
* *
|
||||
* OUTPUT: Bitmask of cells that effect was applied to *
|
||||
* *
|
||||
* WARNINGS: none *
|
||||
* *
|
||||
* HISTORY: *
|
||||
* 8/19/2020 12:13PM ST : Created. *
|
||||
*=============================================================================================*/
|
||||
unsigned int UnitClass::Apply_Temporary_Jamming_Shroud(HouseClass *house_to_apply_for)
|
||||
{
|
||||
unsigned int shroud_bits_applied = 0;
|
||||
|
||||
if (!IsActive || !Strength) {
|
||||
return shroud_bits_applied;
|
||||
}
|
||||
|
||||
if (!Class->IsGapper) {
|
||||
return shroud_bits_applied;
|
||||
}
|
||||
|
||||
CELL shroud_center = Coord_Cell(Center_Coord());
|
||||
int centerx = Cell_X(shroud_center);
|
||||
int centery = Cell_Y(shroud_center);
|
||||
CELL trycell;
|
||||
|
||||
for (int index = 0; index < 31; index++) {
|
||||
shroud_bits_applied <<= 1;
|
||||
trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
|
||||
if (Map[trycell].Is_Mapped(house_to_apply_for)) {
|
||||
Map.Jam_Cell(trycell, House);
|
||||
shroud_bits_applied |= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (shroud_bits_applied) {
|
||||
Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, house_to_apply_for);
|
||||
}
|
||||
|
||||
return shroud_bits_applied;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/***********************************************************************************************
|
||||
* UnitClass::Unapply_Temporary_Jamming_Shroud -- Remove temporary gap generator shroud effect *
|
||||
* *
|
||||
* Remove gap effect added by Apply_Temporary_Jamming_Shroud *
|
||||
* *
|
||||
* INPUT: House to unapply effect for *
|
||||
* Bitmask of cells that effect was applied to *
|
||||
* *
|
||||
* OUTPUT: *
|
||||
* *
|
||||
* WARNINGS: none *
|
||||
* *
|
||||
* HISTORY: *
|
||||
* 8/19/2020 12:16PM ST : Created. *
|
||||
*=============================================================================================*/
|
||||
void UnitClass::Unapply_Temporary_Jamming_Shroud(HouseClass *house_to_unapply_for, unsigned int shroud_bits_applied)
|
||||
{
|
||||
if (!IsActive || !Strength) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Class->IsGapper) {
|
||||
return;
|
||||
}
|
||||
|
||||
CELL shroud_center = Coord_Cell(Center_Coord());
|
||||
int centerx = Cell_X(shroud_center);
|
||||
int centery = Cell_Y(shroud_center);
|
||||
CELL trycell;
|
||||
|
||||
for (int index = 30; index >= 0 && shroud_bits_applied; index--) {
|
||||
if (shroud_bits_applied & 1) {
|
||||
trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
|
||||
Map.UnJam_Cell(trycell, House);
|
||||
Map.Map_Cell(trycell, house_to_unapply_for);
|
||||
}
|
||||
shroud_bits_applied >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** Updated for client/server multiplayer - ST 8/12/2019 11:46AM
|
||||
*/
|
||||
void UnitClass::Shroud_Regen(void)
|
||||
{
|
||||
if (Class->IsGapper/*KO && !House->IsPlayerControl*/) {
|
||||
static int _xtab[]={
|
||||
-1, 0, 1,
|
||||
-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,
|
||||
-2,-1, 0, 1, 2,
|
||||
-1, 0, 1
|
||||
};
|
||||
static int _ytab[]={
|
||||
-3,-3,-3,
|
||||
-2,-2,-2,-2,-2,
|
||||
-1,-1,-1,-1,-1,
|
||||
0, 0, 0, 0, 0,
|
||||
1, 1, 1, 1, 1,
|
||||
2, 2, 2, 2, 2,
|
||||
3, 3, 3
|
||||
};
|
||||
|
||||
int index;
|
||||
int centerx, centery;
|
||||
CELL trycell;
|
||||
|
||||
// Only restore under the shroud if it's a valid field.
|
||||
if (ShroudBits != (unsigned)-1L) {
|
||||
centerx = Cell_X(ShroudCenter);
|
||||
centery = Cell_Y(ShroudCenter);
|
||||
for (index = 30; index >= 0 && ShroudBits; index--) {
|
||||
if (ShroudBits & 1) {
|
||||
trycell = XY_Cell(centerx + _xtab[index], centery + _ytab[index]);
|
||||
#if(0)
|
||||
Map.Map_Cell(trycell, PlayerPtr);
|
||||
#else
|
||||
Map.UnJam_Cell(trycell, House);
|
||||
Map.Map_Cell(trycell, House);
|
||||
#endif
|
||||
if (Session.Type != GAME_GLYPHX_MULTIPLAYER || Is_Legacy_Render_Enabled()) {
|
||||
// Only restore under the shroud if it's a valid field.
|
||||
if (ShroudBits != (unsigned)-1L) {
|
||||
centerx = Cell_X(ShroudCenter);
|
||||
centery = Cell_Y(ShroudCenter);
|
||||
for (index = 30; index >= 0 && ShroudBits; index--) {
|
||||
if (ShroudBits & 1) {
|
||||
trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
|
||||
#if(0)
|
||||
Map.Map_Cell(trycell, PlayerPtr);
|
||||
#else
|
||||
Map.UnJam_Cell(trycell, House);
|
||||
Map.Map_Cell(trycell, House);
|
||||
#endif
|
||||
}
|
||||
ShroudBits >>= 1;
|
||||
}
|
||||
ShroudBits >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(IsActive && Strength) {
|
||||
// Now shroud around the new center
|
||||
ShroudBits = 0L;
|
||||
ShroudCenter = Coord_Cell(Center_Coord());
|
||||
centerx = Cell_X(ShroudCenter);
|
||||
centery = Cell_Y(ShroudCenter);
|
||||
for (index = 0; index < 31; index++) {
|
||||
ShroudBits <<= 1;
|
||||
trycell = XY_Cell(centerx + _xtab[index], centery + _ytab[index]);
|
||||
if (Map[trycell].Is_Mapped(House)) {
|
||||
Map.Jam_Cell(trycell, House);
|
||||
ShroudBits |= 1;
|
||||
if(IsActive && Strength) {
|
||||
// Now shroud around the new center
|
||||
ShroudBits = 0L;
|
||||
ShroudCenter = Coord_Cell(Center_Coord());
|
||||
centerx = Cell_X(ShroudCenter);
|
||||
centery = Cell_Y(ShroudCenter);
|
||||
for (index = 0; index < 31; index++) {
|
||||
ShroudBits <<= 1;
|
||||
trycell = XY_Cell(centerx + _GapShroudXTable[index], centery + _GapShroudYTable[index]);
|
||||
if (Map[trycell].Is_Mapped(House)) {
|
||||
Map.Jam_Cell(trycell, House);
|
||||
ShroudBits |= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -5094,14 +5320,15 @@ void UnitClass::Shroud_Regen(void)
|
|||
|
||||
} else {
|
||||
|
||||
for (int i = 0; i < Session.Players.Count(); i++) {
|
||||
HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
|
||||
if (player_house->IsHuman && player_house != House) {
|
||||
Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, player_house);
|
||||
if (Is_Legacy_Render_Enabled()) {
|
||||
for (int i = 0; i < Session.Players.Count(); i++) {
|
||||
HouseClass *player_house = HouseClass::As_Pointer(Session.Players[i]->Player.ID);
|
||||
if (player_house->IsHuman && player_house != House) {
|
||||
Map.Constrained_Look(Coord, 5 * CELL_LEPTON_W, player_house);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,10 +111,15 @@ class UnitClass : public DriveClass
|
|||
*/
|
||||
FacingClass SecondaryFacing;
|
||||
|
||||
/*
|
||||
** This is the refinery a harvester is interested in unloading at.
|
||||
*/
|
||||
TARGET 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];
|
||||
|
||||
/*---------------------------------------------------------------------
|
||||
** Constructors, Destructors, and overloaded operators.
|
||||
|
@ -145,6 +150,9 @@ class UnitClass : public DriveClass
|
|||
void APC_Close_Door(void);
|
||||
void APC_Open_Door(void);
|
||||
|
||||
unsigned int Apply_Temporary_Jamming_Shroud(HouseClass *house_to_apply_for);
|
||||
void Unapply_Temporary_Jamming_Shroud(HouseClass *house_to_unapply_for, unsigned int shroud_bits_applied);
|
||||
|
||||
/*
|
||||
** Query functions.
|
||||
*/
|
||||
|
@ -158,6 +166,8 @@ class UnitClass : public DriveClass
|
|||
virtual bool Ok_To_Move(DirType facing) const;
|
||||
virtual FireErrorType Can_Fire(TARGET target, int which) const;
|
||||
virtual fixed Tiberium_Load(void) const;
|
||||
virtual BuildingClass* Tiberium_Unload_Refinery(void) const;
|
||||
virtual BuildingClass* Find_Best_Refinery(void) const;
|
||||
|
||||
/*
|
||||
** Coordinate inquiry functions. These are used for both display and
|
||||
|
@ -248,4 +258,4 @@ class UnitClass : public DriveClass
|
|||
bool Save(Pipe & file) const;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
|
@ -4842,7 +4842,7 @@ extern "C" int __cdecl Confine_Rect ( int * x , int * y , int w , int h , int wi
|
|||
|
||||
|
||||
/*
|
||||
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/WIN32LIB/DrawMisc.cpp#97 $
|
||||
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/REDALERT/WIN32LIB/DrawMisc.cpp#131 $
|
||||
;***************************************************************************
|
||||
;** 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 **
|
||||
;***************************************************************************
|
||||
|
|
|
@ -2379,7 +2379,58 @@ static AnimTypeClass const Flag(
|
|||
0, // Loop start frame number.
|
||||
-1, // Ending frame of loop back.
|
||||
-1, // Number of animation stages.
|
||||
-1, // Number of times the animation loops.
|
||||
-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
|
||||
);
|
||||
|
@ -2484,10 +2535,12 @@ AnimTypeClass const * const AnimTypeClass::Pointers[ANIM_COUNT] = {
|
|||
&RaptDie,
|
||||
&ChemBall,
|
||||
&Flag,
|
||||
&Beacon,
|
||||
&Fire3Virtual,
|
||||
&Fire2Virtual,
|
||||
&Fire1Virtual,
|
||||
&Fire4Virtual
|
||||
&Fire4Virtual,
|
||||
&BeaconVirtual
|
||||
};
|
||||
|
||||
|
||||
|
@ -2570,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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -2368,6 +2368,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)) {
|
||||
|
|
|
@ -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.
|
||||
|
@ -276,6 +278,12 @@ void AnimClass::Draw_It(int x, int y, WindowNumberType window)
|
|||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -303,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -571,6 +579,7 @@ AnimClass::AnimClass(AnimType animnum, COORDINATE coord, unsigned char timedelay
|
|||
Object = 0;
|
||||
SortTarget = TARGET_NONE;
|
||||
OwnerHouse = HOUSE_NONE;
|
||||
KillTime = 0ULL;
|
||||
|
||||
if (Class->Stages == -1) {
|
||||
((int&)Class->Stages) = Get_Build_Frame_Count(Class->Get_Image_Data());
|
||||
|
@ -732,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.
|
||||
|
@ -1292,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);
|
||||
}
|
||||
}
|
|
@ -63,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);
|
||||
|
@ -194,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];
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -2077,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));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3872,13 +3872,16 @@ bool BuildingClass::Can_Demolish_Unit(void) const
|
|||
|
||||
bool BuildingClass::Can_Capture(void) const
|
||||
{
|
||||
bool can_capture = Class->IsCaptureable;
|
||||
bool can_capture = Class->IsCaptureable && Mission != MISSION_DECONSTRUCTION;
|
||||
|
||||
// Override capturable state if this building has a capture win trigger
|
||||
if (GameToPlay == GAME_NORMAL) {
|
||||
if (Trigger != NULL && Trigger->Action == TriggerClass::ACTION_WINLOSE) {
|
||||
if (!House->IsHuman && Trigger != NULL && Trigger->Action == TriggerClass::ACTION_WINLOSE) {
|
||||
can_capture = true;
|
||||
}
|
||||
} else {
|
||||
// Only allow capturing of multiplayer-owned structures
|
||||
can_capture &= House->Class->House >= HOUSE_MULTI1 && House->Class->House <= HOUSE_MULTI6;
|
||||
}
|
||||
|
||||
return(can_capture);
|
||||
|
@ -4161,6 +4164,7 @@ int BuildingClass::Mission_Deconstruction(void)
|
|||
Status = DURING;
|
||||
Begin_Mode(BSTATE_CONSTRUCTION);
|
||||
IsReadyToCommence = false;
|
||||
IsSurvivorless = true;
|
||||
break;
|
||||
}
|
||||
Transmit_Message(RADIO_RUN_AWAY);
|
||||
|
|
|
@ -473,7 +473,7 @@ void BulletClass::AI(void)
|
|||
//
|
||||
if (newanim && Class->Explosion == ANIM_ATOM_BLAST && newanim->OwnerHouse == HOUSE_NONE) {
|
||||
if (Payback && Payback->House && Payback->House->Class) {
|
||||
newanim->OwnerHouse = Payback->House->Class->House;
|
||||
newanim->Set_Owner(Payback->House->Class->House);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -2448,7 +2450,7 @@ void CellClass::Flag_Create(void)
|
|||
CTFFlag = new AnimClass(ANIM_FLAG, Cell_Coord(), 0, 1, true);
|
||||
}
|
||||
assert(CTFFlag != NULL);
|
||||
CTFFlag->OwnerHouse = Owner;
|
||||
CTFFlag->Set_Owner(Owner);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -1510,11 +1510,13 @@ typedef enum AnimType : char {
|
|||
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
|
||||
|
@ -2309,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
|
||||
|
|
|
@ -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
|
||||
|
@ -2965,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;
|
||||
|
@ -2992,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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
/*
|
||||
|
@ -390,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";
|
||||
|
@ -425,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);
|
||||
}
|
||||
|
@ -504,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();
|
||||
|
@ -879,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++;
|
||||
}
|
||||
|
@ -1334,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);
|
||||
|
@ -1701,13 +1700,13 @@ extern "C" __declspec(dllexport) bool __cdecl CNC_Save_Load(bool save, const cha
|
|||
|
||||
result = Load_Game(file_path_and_name);
|
||||
|
||||
// MBL 07.21.2020
|
||||
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);
|
||||
|
@ -1860,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
|
||||
*
|
||||
|
@ -3682,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;
|
||||
}
|
||||
|
@ -3806,7 +3848,7 @@ extern "C" __declspec(dllexport) void __cdecl CNC_Handle_Sidebar_Request(Sidebar
|
|||
|
||||
switch (request_type) {
|
||||
|
||||
// MBL 06.02.2020 - Changing right-click support for first put building on hold, and then subsequenct right-clicks to decrement that queue count for 1x or 5x; Then, 1x or 5x Left click will resume from hold
|
||||
// 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:
|
||||
|
||||
|
@ -6851,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
|
||||
*
|
||||
|
|
|
@ -28,7 +28,7 @@ struct CarryoverObjectStruct;
|
|||
**
|
||||
**
|
||||
*/
|
||||
#define CNC_DLL_API_VERSION 0x101
|
||||
#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,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**************************************************************************************
|
||||
**
|
||||
|
|
|
@ -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
|
|
@ -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(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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
@ -1320,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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -560,7 +560,7 @@ DynamicVectorClass <int> MPlayerFilenum;
|
|||
/***************************************************************************
|
||||
** This value determines the max allowable # of players.
|
||||
*/
|
||||
int MPlayerMax = 4;
|
||||
int MPlayerMax = 6;
|
||||
|
||||
|
||||
/***************************************************************************
|
||||
|
|
|
@ -1673,14 +1673,13 @@ void HouseClass::Attacked(BuildingClass* source)
|
|||
{
|
||||
Validate();
|
||||
|
||||
bool expired = SpeakAttackDelay.Expired();
|
||||
bool spoke = false;
|
||||
if (SpeakAttackDelay.Expired() && PlayerPtr->Class->House == Class->House) {
|
||||
|
||||
// if (SpeakAttackDelay.Expired() && PlayerPtr->Class->House == Class->House) {
|
||||
if (expired && PlayerPtr->Class->House == Class->House) {
|
||||
|
||||
Speak(VOX_BASE_UNDER_ATTACK, NULL, source ? source->Center_Coord() : 0);
|
||||
spoke = true;
|
||||
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
|
||||
|
@ -1695,23 +1694,6 @@ void HouseClass::Attacked(BuildingClass* source)
|
|||
HouseTriggers[Class->House][index]->Spring(EVENT_ATTACKED, Class->House);
|
||||
}
|
||||
}
|
||||
|
||||
// MBL 07.07.2020 - CNC Patch 3, fix for not working for all players in MP, per https://jaas.ea.com/browse/TDRA-7249
|
||||
// Separated to here as did not want to change any logic around the HouseTriggers[] Spring events
|
||||
//
|
||||
if (expired == true && spoke == false)
|
||||
{
|
||||
if (GameToPlay != GAME_NORMAL) // Multiplayer
|
||||
{
|
||||
Speak(VOX_BASE_UNDER_ATTACK, this);
|
||||
spoke = true;
|
||||
|
||||
// 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
|
||||
}
|
||||
}
|
||||
// END MBL 07.07.2020
|
||||
}
|
||||
|
||||
|
||||
|
@ -2584,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 {
|
||||
|
||||
|
@ -2692,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->OwnerHouse = Class->House;
|
||||
if (anim) anim->Set_Owner(Class->House);
|
||||
if (this == PlayerPtr) {
|
||||
Map.IsTargettingMode = false;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -491,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);
|
||||
|
@ -541,6 +545,7 @@ bool Read_Scenario_Ini(char *root, bool fresh)
|
|||
** 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) {
|
||||
|
@ -572,6 +577,10 @@ bool Read_Scenario_Ini(char *root, bool fresh)
|
|||
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) {
|
||||
|
@ -920,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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
Electronic Arts Inc. released only TiberianDawn.dll, RedAlert.dll and
|
||||
the Command & Conquer Map Editor and their corresponding source code
|
||||
under the GPL V3 below, with additional terms at the bottom.
|
||||
Electronic Arts Inc. released only TiberianDawn.dll and RedAlert.dll and their corresponding source code under the GPL V3 below, with additional terms at the bottom.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
@ -711,4 +709,4 @@ PROVIDED BY ELECTRONIC ARTS OR ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A
|
|||
WARRANTY. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON
|
||||
IMPLIED WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A
|
||||
CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY NOT APPLY
|
||||
TO YOU.
|
||||
TO YOU.
|
|
@ -428,7 +428,7 @@ dxisbig:
|
|||
#if (0)
|
||||
|
||||
/*
|
||||
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/MiscAsm.cpp#97 $
|
||||
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/MiscAsm.cpp#131 $
|
||||
;***************************************************************************
|
||||
;** 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 **
|
||||
;***************************************************************************
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -1402,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);
|
||||
}
|
||||
|
||||
|
|
|
@ -206,6 +206,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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -396,6 +396,20 @@ void UnitClass::AI(void)
|
|||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
** Clear the unload refinery if not haresting or entering a refinery.
|
||||
*/
|
||||
if (Class->IsToHarvest) {
|
||||
if (Mission != MISSION_HARVEST) {
|
||||
if (Mission != MISSION_ENTER ||
|
||||
!In_Radio_Contact() ||
|
||||
Contact_With_Whom()->What_Am_I() != RTTI_BUILDING ||
|
||||
*((BuildingClass*)Contact_With_Whom()) != STRUCT_REFINERY) {
|
||||
TiberiumUnloadRefinery = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
** Rocket launchers will reload every so often.
|
||||
*/
|
||||
|
@ -1198,6 +1212,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));
|
||||
|
||||
/*
|
||||
|
@ -2345,29 +2360,47 @@ bool UnitClass::Goto_Tiberium(void)
|
|||
int tiberium = 0;
|
||||
int besttiberium = 0;
|
||||
for (int x = -radius; x <= radius; x++) {
|
||||
|
||||
/*
|
||||
** 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;
|
||||
tiberium = Tiberium_Check(cell, x, -radius);
|
||||
tiberium = Tiberium_Check(cell, corners[0][0], corners[0][1]);
|
||||
if (tiberium > besttiberium) {
|
||||
bestcell = cell;
|
||||
besttiberium = tiberium;
|
||||
}
|
||||
|
||||
cell = center;
|
||||
tiberium = Tiberium_Check(cell, x, +radius);
|
||||
tiberium = Tiberium_Check(cell, corners[1][0], corners[1][1]);
|
||||
if (tiberium > besttiberium) {
|
||||
bestcell = cell;
|
||||
besttiberium = tiberium;
|
||||
}
|
||||
|
||||
cell = center;
|
||||
tiberium = Tiberium_Check(cell, -radius, x);
|
||||
tiberium = Tiberium_Check(cell, corners[2][0], corners[2][1]);
|
||||
if (tiberium > besttiberium) {
|
||||
bestcell = cell;
|
||||
besttiberium = tiberium;
|
||||
}
|
||||
|
||||
cell = center;
|
||||
tiberium = Tiberium_Check(cell, +radius, x);
|
||||
tiberium = Tiberium_Check(cell, corners[3][0], corners[3][1]);
|
||||
if (tiberium > besttiberium) {
|
||||
bestcell = cell;
|
||||
besttiberium = tiberium;
|
||||
|
@ -2385,6 +2418,96 @@ bool UnitClass::Goto_Tiberium(void)
|
|||
}
|
||||
|
||||
|
||||
struct RefineryData
|
||||
{
|
||||
BuildingClass* Refinery;
|
||||
int Distance;
|
||||
int Harvesters;
|
||||
};
|
||||
|
||||
static bool operator==(const RefineryData& lhs, const RefineryData& rhs)
|
||||
{
|
||||
return lhs.Refinery == rhs.Refinery;
|
||||
}
|
||||
|
||||
static bool operator!=(const RefineryData& lhs, const RefineryData& rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
static int _refinery_compare(const void * left, const void * right)
|
||||
{
|
||||
const RefineryData& lhs = *reinterpret_cast<const RefineryData*>(left);
|
||||
const RefineryData& rhs = *reinterpret_cast<const RefineryData*>(right);
|
||||
if (lhs.Distance < rhs.Distance) {
|
||||
return -1;
|
||||
} else if (rhs.Distance < lhs.Distance) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
BuildingClass* UnitClass::Find_Best_Refinery(void) const
|
||||
{
|
||||
static DynamicVectorClass<RefineryData> _refineries;
|
||||
|
||||
_refineries.Clear();
|
||||
for (int i = 0; i < Buildings.Count(); ++i) {
|
||||
BuildingClass* refinery = Buildings.Ptr(i);
|
||||
if (refinery != NULL &&
|
||||
refinery->House == House &&
|
||||
!refinery->IsInLimbo &&
|
||||
*refinery == STRUCT_REFINERY) {
|
||||
_refineries.Add(RefineryData{ refinery, Distance(refinery), 0 });
|
||||
}
|
||||
}
|
||||
|
||||
// Base case for zero or one refineries.
|
||||
if (_refineries.Count() == 0) {
|
||||
return NULL;
|
||||
} else if (_refineries.Count() == 1) {
|
||||
return _refineries[0].Refinery;
|
||||
}
|
||||
|
||||
// Count harvesters going to each refinery as well as the total.
|
||||
int num_harvesters = 0;
|
||||
for (int i = 0; i < Units.Count(); ++i) {
|
||||
UnitClass* unit = Units.Ptr(i);
|
||||
if (unit->IsActive && unit->Class->IsToHarvest && unit->House == House) {
|
||||
BuildingClass* refinery = unit->Tiberium_Unload_Refinery();
|
||||
if (refinery != NULL) {
|
||||
int index = _refineries.ID(RefineryData{ refinery });
|
||||
assert(index >= 0);
|
||||
_refineries[index].Harvesters++;
|
||||
num_harvesters++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by distance (special case for 2 refineries as that's a single swap).
|
||||
if (_refineries.Count() == 2) {
|
||||
if (_refineries[0].Distance > _refineries[1].Distance) {
|
||||
RefineryData temp = _refineries[0];
|
||||
_refineries[0] = _refineries[1];
|
||||
_refineries[1] = temp;
|
||||
}
|
||||
} else {
|
||||
qsort(&_refineries[0], _refineries.Count(), sizeof(RefineryData), _refinery_compare);
|
||||
}
|
||||
|
||||
// Evenly distribute harvesters among refineries.
|
||||
int harvesters_per_refinery = (num_harvesters + _refineries.Count() - 1) / _refineries.Count();
|
||||
for (int i = 0; i < _refineries.Count(); ++i) {
|
||||
if (_refineries[i].Harvesters < harvesters_per_refinery) {
|
||||
return _refineries[i].Refinery;
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back on closest refinery
|
||||
return _refineries[0].Refinery;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************************************
|
||||
* UnitClass::Harvesting -- Harvests tiberium at the current location. *
|
||||
* *
|
||||
|
@ -2677,7 +2800,10 @@ int UnitClass::Mission_Harvest(void)
|
|||
Assign_Target(TARGET_NONE);
|
||||
Status = FINDHOME;
|
||||
return(1);
|
||||
} else if (Goto_Tiberium()) {
|
||||
}
|
||||
|
||||
TiberiumUnloadRefinery = NULL;
|
||||
if (Goto_Tiberium()) {
|
||||
IsHarvesting = true;
|
||||
Set_Rate(2);
|
||||
Set_Stage(0);
|
||||
|
@ -2739,21 +2865,14 @@ int UnitClass::Mission_Harvest(void)
|
|||
if (!Target_Legal(NavCom)) {
|
||||
|
||||
/*
|
||||
** Find nearby refinery and head to it?
|
||||
** Find nearby refinery and head to it.
|
||||
*/
|
||||
BuildingClass * nearest = Find_Docking_Bay(STRUCT_REFINERY, false);
|
||||
|
||||
/*
|
||||
** Since the refinery said it was ok to load, establish radio
|
||||
** contact with the refinery and then await docking orders.
|
||||
*/
|
||||
if (nearest && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
|
||||
Status = HEADINGHOME;
|
||||
} else {
|
||||
ScenarioInit++;
|
||||
nearest = Find_Docking_Bay(STRUCT_REFINERY, false);
|
||||
ScenarioInit--;
|
||||
if (nearest) {
|
||||
BuildingClass * nearest = Find_Best_Refinery();
|
||||
if (nearest) {
|
||||
TiberiumUnloadRefinery = nearest;
|
||||
if (Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) {
|
||||
Status = HEADINGHOME;
|
||||
} else {
|
||||
Assign_Destination(::As_Target(nearest->Nearby_Location(this)));
|
||||
}
|
||||
}
|
||||
|
@ -2770,6 +2889,7 @@ int UnitClass::Mission_Harvest(void)
|
|||
return(1);
|
||||
|
||||
case GOINGTOIDLE:
|
||||
TiberiumUnloadRefinery = NULL;
|
||||
Assign_Mission(MISSION_GUARD);
|
||||
break;
|
||||
|
||||
|
|
|
@ -84,6 +84,8 @@ class UnitClass : public TarComClass
|
|||
bool Harvesting(void);
|
||||
void APC_Close_Door(void);
|
||||
void APC_Open_Door(void);
|
||||
BuildingClass* Tiberium_Unload_Refinery(void) const {return TiberiumUnloadRefinery;}
|
||||
BuildingClass* Find_Best_Refinery(void) const;
|
||||
|
||||
/*
|
||||
** Query functions.
|
||||
|
@ -199,10 +201,15 @@ class UnitClass : public TarComClass
|
|||
*/
|
||||
TCountDownTimerClass HarvestTimer;
|
||||
|
||||
/*
|
||||
** This is the refinery a harvester is interested in unloading at.
|
||||
*/
|
||||
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
|
||||
|
|
|
@ -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#97 $
|
||||
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/WIN32LIB/DrawMisc.cpp#131 $
|
||||
;***************************************************************************
|
||||
;** 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 **
|
||||
;***************************************************************************
|
||||
|
|
|
@ -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#97 $
|
||||
; $Header: //depot/Projects/Mobius/QA/Project/Run/SOURCECODE/TIBERIANDAWN/WIN32LIB/FACINGFF.h#131 $
|
||||
;***************************************************************************
|
||||
;** 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 **
|
||||
;***************************************************************************
|
||||
|
|
Loading…
Reference in New Issue