From cea2290ae365202a26e703def23891027d4a6a29 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Thu, 11 Jun 2020 04:51:34 +0200 Subject: [PATCH 1/6] Ported CFE Patch from cfehunter to Red Alert. Unsure about AIRCRAFT.CPP Loads config from CFEPATCH_RA.INI Last commit included: 29bb30d1d24d38f802d71f1bcbf84b2d50bbe27a --- REDALERT/AIRCRAFT.CPP | 17 ++- REDALERT/BUILDING.CPP | 125 +++++++++++++++-- REDALERT/BUILDING.H | 15 ++- REDALERT/CONQUER.CPP | 13 ++ REDALERT/DEFINES.H | 1 + REDALERT/DLLInterface.cpp | 24 ++++ REDALERT/EVENT.CPP | 12 ++ REDALERT/EVENT.H | 3 + REDALERT/EXTERNS.H | 11 ++ REDALERT/FINDPATH.CPP | 277 +++++++++++++++++++++++++++++++++++++- REDALERT/FOOT.H | 2 + REDALERT/FUNCTION.H | 3 + REDALERT/GLOBALS.CPP | 2 + REDALERT/INIT.CPP | 25 ++++ REDALERT/LOGIC.CPP | 5 +- REDALERT/License.txt | 4 +- REDALERT/RedAlert.vcxproj | 2 +- REDALERT/UNIT.CPP | 24 +++- TIBERIANDAWN/License.txt | 4 +- 19 files changed, 551 insertions(+), 18 deletions(-) diff --git a/REDALERT/AIRCRAFT.CPP b/REDALERT/AIRCRAFT.CPP index 7722529e2..8169fe6a1 100644 --- a/REDALERT/AIRCRAFT.CPP +++ b/REDALERT/AIRCRAFT.CPP @@ -1430,11 +1430,26 @@ int AircraftClass::Exit_Object(TechnoClass * unit) ** unloading the next passenger or taking off. */ if (unit->Unlimbo(Coord, Facing_Dir(_toface[face]))) { + TARGET target = ::As_Target(cell); + + /* CFE Patch - unsure what the changes in this file are meant to do. + Possible solution might be (Root-Core @ 20200611): + + if (const BuildingClass* const target_building = As_Building(NavCom)) + { + if (target_building->Can_Have_Rally_Point() && Target_Legal(target_building->RallyPoint)) + { + target = target_building->Target_For_Rally_Point(); + } + }*/ + unit->Assign_Mission(MISSION_MOVE); - unit->Assign_Destination(::As_Target(cell)); + unit->Assign_Destination(target); + if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { Transmit_Message(RADIO_UNLOAD); } + unit->Look(false); return(true); } diff --git a/REDALERT/BUILDING.CPP b/REDALERT/BUILDING.CPP index 8a8e2d8c6..6e71ea10e 100644 --- a/REDALERT/BUILDING.CPP +++ b/REDALERT/BUILDING.CPP @@ -134,6 +134,45 @@ COORDINATE const BuildingClass::CenterOffset[BSIZE_COUNT] = { 0x02800280L, }; +/*********************************************************************************************** + * BuildingClass::Can_Have_Rally_Point -- Query if a building can have a rally point. * + * * + * This routine simply checks the buildings type against a hard coded list. * + * of buildings that may set a rally point for produced units. * + * * + * INPUT: None * + * * + * OUTPUT: True if true false if false * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 07/06/2020 cfehunter : Created. * + *=============================================================================================*/ +bool BuildingClass::Can_Have_Rally_Point() const +{ + switch (Class->ToBuild) + { + case RTTI_AIRCRAFTTYPE: + case RTTI_INFANTRYTYPE: + case RTTI_UNITTYPE: + case RTTI_VESSELTYPE: + return ActiveCFEPatchConfig.EnableRallyPoints; + + default: + return (StructType(*this) == STRUCT_REPAIR); + } +} + +void BuildingClass::Set_Unselected_By_Player(HouseClass* player) +{ + TechnoClass::Set_Unselected_By_Player(player); + + if (Can_Have_Rally_Point() && RallyPoint) + { + Map.Flag_To_Redraw(true); + } +} /*********************************************************************************************** * BuildingClass::Receive_Message -- Handle an incoming message to the building. * @@ -166,6 +205,26 @@ RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageT switch (message) { + case RADIO_HELLO: + if (Class->Type == STRUCT_REFINERY && ActiveCFEPatchConfig.EnableHarvyQueueJump && In_Radio_Contact()) { + if (from->What_Am_I() == RTTI_UNIT && *((UnitClass*)from) == UNIT_HARVESTER) { + + UnitClass& currentHarvy = *static_cast(Contact_With_Whom()); + UnitClass& newHarvy = *static_cast(from); + + const int currentHarvyDistance = currentHarvy.Distance(this); + if (currentHarvyDistance > ActiveCFEPatchConfig.HarvyQueueJumpCutoff && newHarvy.Distance(this) < currentHarvyDistance) { + + //Kick the current harvester out + Transmit_Message(RADIO_CANCEL, ¤tHarvy); + + //Accept the new one + return(TechnoClass::Receive_Message(from, message, param)); + } + } + } + break; + /* ** This message is received as a request to attach/load/dock with this building. ** Verify that this is allowed and return the appropriate response. @@ -373,7 +432,16 @@ RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageT } } TechnoClass::Receive_Message(from, message, param); - if (*this == STRUCT_WEAP || *this == STRUCT_AIRSTRIP || *this == STRUCT_REPAIR) return(RADIO_RUN_AWAY); + + //Either tell the unit to move to the rally point, or have it run away + if (Can_Have_Rally_Point() && Target_Legal(RallyPoint)) { + SpeedType speed = from->Is_Techno() ? ((TechnoClass const*)from)->Techno_Type_Class()->Speed : SPEED_TRACK; + + from->Set_Mission(MISSION_MOVE); + static_cast(from)->Assign_Destination(Target_For_Rally_Point(speed)); + return RADIO_ROGER; + } + return(RADIO_ROGER); default: @@ -506,6 +574,14 @@ void BuildingClass::Draw_It(int x, int y, WindowNumberType window) const } } + if (Can_Have_Rally_Point() && Is_Selected_By_Player() && Is_Owned_By_Player() && RallyPoint && Target_Legal(RallyPoint)) + { + int startX, startY, endX, endY; + Map.Coord_To_Pixel(Center_Coord(), startX, startY); + Map.Coord_To_Pixel(As_Coord(RallyPoint), endX, endY); + CC_Draw_Line(startX, startY, endX, endY, LTGREEN, 1, window); + } + TechnoClass::Draw_It(x, y, window); /* @@ -810,6 +886,11 @@ bool BuildingClass::Mark(MarkType mark) return(false); } +void BuildingClass::Player_Set_Rally_Point(TARGET target) +{ + OutList.Add(EventClass(EventClass::SET_RALLY, As_Target(), target)); +} + /*********************************************************************************************** * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * @@ -1518,6 +1599,10 @@ ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType wa return(res); } +TARGET BuildingClass::Target_For_Rally_Point(const SpeedType speed) const +{ + return Is_Target_Cell(RallyPoint) ? ::As_Target(Map.Nearby_Location(As_Cell(RallyPoint), speed)) : RallyPoint; +} /*********************************************************************************************** * BuildingClass::new -- Allocates a building object from building pool. * @@ -1812,6 +1897,10 @@ void BuildingClass::Active_Click_With(ActionType action, ObjectClass * object) if (action == ACTION_TOGGLE_PRIMARY && Class->Is_Factory()) { OutList.Add(EventClass(EventClass::PRIMARY, TargetClass(this))); } + + if (action == ACTION_MOVE && Can_Have_Rally_Point()) { + Player_Set_Rally_Point(object->As_Target()); + } } @@ -1842,12 +1931,18 @@ void BuildingClass::Active_Click_With(ActionType action, CELL cell) Player_Assign_Mission(MISSION_ATTACK, ::As_Target(cell)); } - if (action == ACTION_MOVE && *this == STRUCT_CONST) { - OutList.Add(EventClass(EventClass::ARCHIVE, TargetClass(this), TargetClass(::As_Target(cell)))); - OutList.Add(EventClass(EventClass::SELL, TargetClass(this))); + if (action == ACTION_MOVE) { + + if (*this == STRUCT_CONST) { + OutList.Add(EventClass(EventClass::ARCHIVE, TargetClass(this), TargetClass(::As_Target(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)); + COORDINATE coord = Map.Pixel_To_Coord(Get_Mouse_X(), Get_Mouse_Y()); + OutList.Add(EventClass(ANIM_MOVE_FLASH, PlayerPtr->Class->House, coord)); + } + else if (Can_Have_Rally_Point()) { + Player_Set_Rally_Point(::As_Target(cell)); + } } } @@ -1877,6 +1972,11 @@ void BuildingClass::Assign_Target(TARGET target) target = TARGET_NONE; } + if (Can_Have_Rally_Point() && Target_Legal(target)) { + RallyPoint = target; + return; + } + TechnoClass::Assign_Target(target); } @@ -2846,7 +2946,7 @@ ActionType BuildingClass::What_Action(ObjectClass const * object) const action = ACTION_NONE; } - if (action == ACTION_MOVE) { + if (action == ACTION_MOVE && !Can_Have_Rally_Point()) { action = ACTION_NONE; } @@ -2877,7 +2977,7 @@ ActionType BuildingClass::What_Action(CELL cell) const ActionType action = TechnoClass::What_Action(cell); - if (action == ACTION_MOVE && (*this != STRUCT_CONST || !Is_MCV_Deploy())) { + if (action == ACTION_MOVE && !Can_Have_Rally_Point() && (*this != STRUCT_CONST || !Is_MCV_Deploy())) { action = ACTION_NONE; } @@ -3400,6 +3500,11 @@ MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const return(Map[cell].Is_Clear_To_Build(Class->Speed) ? MOVE_OK : MOVE_NO); } + if (Can_Have_Rally_Point()) { + SpeedType buildSpeed = Fetch_Techno_Type(Class->ToBuild, 0)->Speed; // 0 = Some of this kind + return Map[cell].Is_Clear_To_Move(buildSpeed, true, true) ? MOVE_OK : MOVE_NO; + } + if (!Debug_Map && ScenarioInit == 0 && Session.Type == GAME_NORMAL && House->IsPlayerControl && !Map[cell].IsMapped) { return(MOVE_NO); } @@ -5170,7 +5275,7 @@ bool BuildingClass::Can_Player_Move(void) const assert(Buildings.ID(this) == ID); assert(IsActive); - return(*this == STRUCT_CONST && (Mission == MISSION_GUARD) && Special.IsMCVDeploy); + return Can_Have_Rally_Point() || (*this == STRUCT_CONST && (Mission == MISSION_GUARD) && Special.IsMCVDeploy); } @@ -5771,7 +5876,7 @@ void BuildingClass::Repair_AI(void) Strength = Class->MaxStrength; IsRepairing = false; } - } else { + } else if (!ActiveCFEPatchConfig.EnableOOMRepair) { IsRepairing = false; } } diff --git a/REDALERT/BUILDING.H b/REDALERT/BUILDING.H index f0f991a71..5bf0eefe8 100644 --- a/REDALERT/BUILDING.H +++ b/REDALERT/BUILDING.H @@ -208,6 +208,15 @@ class BuildingClass : public TechnoClass */ CDTimerClass PlacementDelay; + //cfehunter 7th June 2020. Took a lot of digging into the allocation + //but it looks like memory is initialised to zeros when allocated from the object pools. + //So even though this CELL doesn't exist in older saves, it should be zero initialised. + //NOTE: /!\ Stole 2 bytes from the save game padding /!\/ + TARGET RallyPoint; + + TARGET Target_For_Rally_Point(const SpeedType speed = SPEED_TRACK) const; + + /*--------------------------------------------------------------------- ** Constructors, Destructors, and overloaded operators. */ @@ -347,13 +356,17 @@ class BuildingClass : public TechnoClass virtual unsigned Spied_By() const; + bool Can_Have_Rally_Point() const; + virtual void Set_Unselected_By_Player(HouseClass* player = NULL) override; + private: void Drop_Debris(TARGET source = TARGET_NONE); + void Player_Set_Rally_Point(TARGET target); /* ** 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[30]; static COORDINATE const CenterOffset[BSIZE_COUNT]; }; diff --git a/REDALERT/CONQUER.CPP b/REDALERT/CONQUER.CPP index 1262c2f59..ea3d7e8d6 100644 --- a/REDALERT/CONQUER.CPP +++ b/REDALERT/CONQUER.CPP @@ -3428,6 +3428,8 @@ void const * Get_Radar_Icon(void const * shapefile, int shapenum, int frames, in extern void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, const ObjectClass *object, DirType rotation, long scale, const char *shape_file_name, char override_owner); 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(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) @@ -3466,6 +3468,17 @@ void CC_Draw_Pip(const ObjectClass *object, void const * shapefile, int shapenum } +void CC_Draw_Line(int x, int y, int x1, int y1, unsigned char color, int frame, WindowNumberType window) +{ + if (window == WINDOW_VIRTUAL) { + DLL_Draw_Line_Intercept(x, y, x1, y1, color, frame); + return; + } + + LogicPage->Draw_Line(x, y, x1, y1, color); +} + + /*********************************************************************************************** * CC_Draw_Shape -- Custom draw shape handler. * * * diff --git a/REDALERT/DEFINES.H b/REDALERT/DEFINES.H index c541e07ad..bc509b617 100644 --- a/REDALERT/DEFINES.H +++ b/REDALERT/DEFINES.H @@ -2435,6 +2435,7 @@ typedef enum RadioMessageType : unsigned char { RADIO_ALL_DONE, // "I have completed the task." RADIO_NEED_REPAIR, // "Are you in need of service depot work?" RADIO_ON_DEPOT, // "Are you sitting on a service depot?" + RADIO_CANCEL, RADIO_COUNT } RadioMessageType; diff --git a/REDALERT/DLLInterface.cpp b/REDALERT/DLLInterface.cpp index 0842ddb31..ec18571ab 100644 --- a/REDALERT/DLLInterface.cpp +++ b/REDALERT/DLLInterface.cpp @@ -182,6 +182,7 @@ class DLLExportClass { static void Convert_Type(const ObjectClass *object, CNCObjectStruct &object_out); static void DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, const ObjectClass *object, DirType rotation, long scale, const char *shape_file_name = NULL, char override_owner = HOUSE_NONE); static void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip); + static void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame); static bool Place(uint64 player_id, int buildable_type, int buildable_id, short cell_x, short cell_y); static bool Cancel_Placement(uint64 player_id, int buildable_type, int buildable_id); static bool Place_Super_Weapon(uint64 player_id, int buildable_type, int buildable_id, int x, int y); @@ -3217,6 +3218,12 @@ void DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) } +void DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame) +{ + DLLExportClass::DLL_Draw_Line_Intercept(x, y, x1, y1, color, frame); +} + + void DLLExportClass::DLL_Draw_Intercept(int shape_number, int x, int y, int width, int height, int flags, const ObjectClass *object, DirType rotation, long scale, const char *shape_file_name, char override_owner) { CNCObjectStruct& new_object = ObjectList->Objects[TotalObjectCount + CurrentDrawCount]; @@ -3583,6 +3590,23 @@ void DLLExportClass::DLL_Draw_Pip_Intercept(const ObjectClass* object, int pip) +void DLLExportClass::DLL_Draw_Line_Intercept(int x, int y, int x1, int y1, unsigned char color, int frame) +{ + CNCObjectStruct& root_object = ObjectList->Objects[TotalObjectCount]; + if (root_object.NumLines < MAX_OBJECT_LINES) { + root_object.Lines[root_object.NumLines].X = x; + root_object.Lines[root_object.NumLines].Y = y; + root_object.Lines[root_object.NumLines].X1 = x1; + root_object.Lines[root_object.NumLines].Y1 = y1; + root_object.Lines[root_object.NumLines].Frame = frame; + root_object.Lines[root_object.NumLines].Color = color; + + SortOrder++; + root_object.NumLines++; + } +} + + /************************************************************************************************** * DLLExportClass::Get_Layer_State -- Get game objects from the layers diff --git a/REDALERT/EVENT.CPP b/REDALERT/EVENT.CPP index 19eca6f6f..f2b06a91c 100644 --- a/REDALERT/EVENT.CPP +++ b/REDALERT/EVENT.CPP @@ -1050,6 +1050,18 @@ void EventClass::Execute(void) break; #endif + //CFE Patch events + case SET_RALLY: + if (BuildingClass* const building = As_Building(Data.NavCom.Whom)) + { + if (building->RallyPoint != Data.NavCom.Where) + { + building->RallyPoint = Data.NavCom.Where; + Map.Flag_To_Redraw(true); + } + } + break; + /* ** Default: do nothing. */ diff --git a/REDALERT/EVENT.H b/REDALERT/EVENT.H index 647bff6c6..4edd10f70 100644 --- a/REDALERT/EVENT.H +++ b/REDALERT/EVENT.H @@ -97,6 +97,9 @@ class EventClass RETRACT_DRAW, // Player retracts proposed draw offer. #endif + //CFE Patch events + SET_RALLY, + LAST_EVENT, // one past the last event } EventType; diff --git a/REDALERT/EXTERNS.H b/REDALERT/EXTERNS.H index 452644020..e0d9d59d9 100644 --- a/REDALERT/EXTERNS.H +++ b/REDALERT/EXTERNS.H @@ -91,6 +91,17 @@ extern void const *LightningShapes; extern int NewINIFormat; +struct CFEPatchConfig +{ + bool EnableASPathing = false; + bool EnableOOMRepair = false; + bool EnableRallyPoints = false; + bool EnableHarvyQueueJump = false; + int HarvyQueueJumpCutoff = 3; + int OreGrowthScale = 1; +}; + +extern CFEPatchConfig ActiveCFEPatchConfig; #ifdef FIXIT_ANTS extern bool AntsEnabled; diff --git a/REDALERT/FINDPATH.CPP b/REDALERT/FINDPATH.CPP index 2a3107d16..795df0490 100644 --- a/REDALERT/FINDPATH.CPP +++ b/REDALERT/FINDPATH.CPP @@ -48,7 +48,11 @@ * Set_Path_Overlap -- Sets the overlap bit for given cell * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +#include +#include +#include #include "function.h" + //#include /* @@ -123,13 +127,40 @@ typedef enum { /*-------------------------------------------------------------------------*/ -//static bool DrawPath; +static bool DrawPath; inline FacingType Opposite(FacingType face) { return( (FacingType) (face ^ 4)); } +static inline void Draw_Cell_Point(CELL cell, bool passable, int threat_stage, int overide = 0) +{ + if (DrawPath) { + if (!Debug_Find_Path) { + int x, y; + + if (Map.Coord_To_Pixel(Cell_Coord(cell), x, y)) { + if (threat_stage > 2) { + SeenBuff.Put_Pixel(x, y, (passable) ? LTGREEN : RED); + } + else { + SeenBuff.Put_Pixel(x, y, (passable) ? 9 + threat_stage : RED); + } + } + } + else { + int x = cell & 63; + int y = cell / 64; + if (!overide) { + SeenBuff.Put_Pixel(64 + (x * 3) + 1, 8 + (y * 3) + 1, (passable) ? WHITE : BLACK); + } + else { + SeenBuff.Put_Pixel(64 + (x * 3) + 1, 8 + (y * 3) + 1, overide); + } + } + } +} inline static FacingType Next_Direction(FacingType facing, FacingType dir) { @@ -410,6 +441,207 @@ bool FootClass::Register_Cell(PathType * path, CELL cell, FacingType dir, int co return(true); } +int Manhattan_Cell_Distance_Estimate(const CELL source, const CELL dest) +{ + return abs(Cell_X(dest) - Cell_X(source)) + abs(Cell_Y(dest) - Cell_Y(source)); +} + +float Euclidian_Cell_Distance_Estimate_Sqrd(const CELL source, const CELL dest) +{ + const float x = abs(Cell_X(dest) - Cell_X(source)); + const float y = abs(Cell_Y(dest) - Cell_Y(source)); + return abs(x * x + y * y); +} + +float Euclidian_Cell_Distance_Estimate(const CELL source, const CELL dest) +{ + return sqrtf(Euclidian_Cell_Distance_Estimate_Sqrd(source, dest)); +} + +void Add_Cell_To_Overlap(const CELL cell, unsigned long* const Overlap) +{ + Overlap[cell >> 5] |= (1 << ((cell & 31) - 1)); +} + +void Remove_Cell_From_Overlap(const CELL cell, unsigned long* const Overlap) +{ + Overlap[cell >> 5] &= ~(1 << ((cell & 31) - 1)); +} + +bool Is_Cell_In_Overlap(const CELL cell, const unsigned long* const Overlap) +{ + return Overlap[cell >> 5] & (1 << ((cell & 31) - 1)); +} + +CELL FootClass::Find_Passable_Position_Near(const CELL target, const int maxRadius, const MoveType threshhold, const int threat) +{ + if (Passable_Cell(target, FACING_NONE, threat, threshhold)) { + return target; + } + + const int targetX = Cell_X(target); + const int targetY = Cell_Y(target); + + const auto validate_cell = [this, target, threat, threshhold](const CELL cell) + { + return Map.In_Radar(cell) && Passable_Cell(target, FACING_NONE, threat, threshhold); + }; + + for (int curDistance = 1; curDistance <= maxRadius; ++curDistance) + { + const int left = targetX - curDistance; + const int right = targetX + curDistance; + const int top = targetY - curDistance; + const int bottom = targetY + curDistance; + + //Test top row + for (int x = left; x <= right; ++x) + { + const CELL currentCell = XY_Cell(x, top); + if (validate_cell(currentCell)) + return currentCell; + } + + //Test bottom row + for (int x = left; x <= right; ++x) + { + const CELL currentCell = XY_Cell(x, bottom); + if (validate_cell(currentCell)) + return currentCell; + } + + //Test left column + for (int y = top + 1; y < bottom; ++y) + { + const CELL currentCell = XY_Cell(y, left); + if (validate_cell(currentCell)) + return currentCell; + } + + //Test the right column + for (int y = top + 1; y < bottom; ++y) + { + const CELL currentCell = XY_Cell(y, right); + if (validate_cell(currentCell)) + return currentCell; + } + } + + return 0; +} + +bool FootClass::Find_Path_AStar(PathType& resultPath, const CELL source, CELL dest, const int maxLen, const MoveType threshhold, const int threat) +{ + struct AStarCell { + AStarCell* prev = nullptr; + CELL position = 0; + int cell_distance_from_start = 0; + int cost_from_start = 0; + float estimated_cost_to_end = 0; + }; + + const auto inverse_node_sort = [](const AStarCell* const lhs, const AStarCell* const rhs) { + static constexpr float flt_epsilon = std::numeric_limits::epsilon(); + const float lhs_cost = lhs->cost_from_start + lhs->estimated_cost_to_end; + const float rhs_cost = rhs->cost_from_start + rhs->estimated_cost_to_end; + + return lhs_cost > rhs_cost; + }; + + //General error case early exits + if (maxLen <= 0 || source == dest) { + return false; + } + + //Target is impassable, try to account for that by finding a position nearby, or staying still if already close + if (!Passable_Cell(dest, FACING_NONE, threat, threshhold)) { + static const int impassableCloseEnough = 3; + const int distanceToDest = ::Distance(Cell_Coord(source), Cell_Coord(dest)); + if (distanceToDest > impassableCloseEnough) { + const CELL nearbyDest = Find_Passable_Position_Near(dest, impassableCloseEnough, threshhold, threat); + + //Failed to find a passable position at or near the target or nearby position is further away or equidistant to our current position, so just stay put + if (nearbyDest == 0 || ::Distance(Cell_Coord(dest), Cell_Coord(nearbyDest)) >= distanceToDest) { + return false; + } + } + else { + return false; + } + } + + std::unordered_map visited_cells; + std::vector open_list; + + //Add the source cell to the open list + open_list.push_back(&visited_cells.emplace(source, AStarCell()).first->second); + open_list.back()->position = source; + + AStarCell* dest_result = nullptr; + + while (!open_list.empty()) { + AStarCell& prev_cell = *open_list.back(); + open_list.pop_back(); + + if (prev_cell.position == dest) { + dest_result = &prev_cell; + break; + } + + for (FacingType facing = FACING_FIRST; facing < FACING_COUNT; ++facing) { + const CELL adjacent_cell_id = Adjacent_Cell(prev_cell.position, facing); + + //Passable cell returns 0 if the object can't enter the cell from this direction + int cell_cost = Passable_Cell(adjacent_cell_id, facing, threat, threshhold); + if (cell_cost) + { + cell_cost += prev_cell.cost_from_start; + auto emplace_result = visited_cells.try_emplace(adjacent_cell_id); + AStarCell& current_cell = emplace_result.first->second; + if (emplace_result.second || cell_cost < current_cell.cost_from_start) + { + current_cell.position = adjacent_cell_id; + current_cell.prev = &prev_cell; + current_cell.cell_distance_from_start = prev_cell.cell_distance_from_start + 1; + current_cell.cost_from_start = cell_cost; + current_cell.estimated_cost_to_end = Euclidian_Cell_Distance_Estimate(adjacent_cell_id, dest); + + //Insert the new node before the first item that is *NOT* greater than it. Which should leave the list with the lowest cost nodes at the back. + open_list.insert(std::lower_bound(open_list.begin(), open_list.end(), ¤t_cell, inverse_node_sort), ¤t_cell); + } + } + } + + } + + if (dest_result) { + //Cost is used for comparing paths with different threat thesholds, so use the overall cost regardless of truncation + resultPath.Cost = dest_result->cost_from_start; + + //Scrub back to the max path length to store + while (dest_result->prev && dest_result->cell_distance_from_start > maxLen) { + dest_result = dest_result->prev; + } + + resultPath.Command[dest_result->cell_distance_from_start] = END; + resultPath.Length = dest_result->cell_distance_from_start; + + for (; dest_result->prev != nullptr; dest_result = dest_result->prev) { + const AStarCell& prev_node = *dest_result->prev; + resultPath.Command[prev_node.cell_distance_from_start] = CELL_FACING(prev_node.position, dest_result->position); + } + + Optimize_Moves(&resultPath, threshhold); + if (Debug_Flag || Debug_Find_Path) + { + Debug_Draw_Path(&resultPath); + } + + return true; + } + + return false; +} /*********************************************************************************************** * Find_Path -- Find a path from point a to point b. * @@ -488,6 +720,37 @@ PathType * FootClass::Find_Path(CELL dest, FacingType * final_moves, int maxlen, memset(path.Overlap, 0, sizeof(MainOverlap)); + if (ActiveCFEPatchConfig.EnableASPathing) + { + int asThreat = threat; + bool result = false; + do + { + result = Find_Path_AStar(path, source, dest, maxlen, threshhold, threat); + + if (!result && asThreat != -1) { + switch (++threat_stage) { + case 0: + asThreat = unit_threat >> 1; + break; + + case 1: + asThreat += unit_threat; + break; + + case 2: + asThreat = -1; + break; + } + } + } while (!result && asThreat != -1); + + if (result) + { + return &path; + } + } + /* ** Clear the over lap list and then make sure that our starting position is marked ** on the overlap list. (Otherwise the harvesters will drive in circles... ) @@ -1305,3 +1568,15 @@ int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType th return(_value[move]); } +void FootClass::Debug_Draw_Path(PathType* path) +{ + if (!path) return; + + FacingType* list = path->Command; + CELL pos = path->Start; + + for (int idx = 0; idx < path->Length; idx++) { + pos = Adjacent_Cell(pos, *list++); + Draw_Cell_Point(pos, true, -1, 0); + } +} diff --git a/REDALERT/FOOT.H b/REDALERT/FOOT.H index 244833b8f..24e6fd14c 100644 --- a/REDALERT/FOOT.H +++ b/REDALERT/FOOT.H @@ -364,6 +364,8 @@ class FootClass : public TechnoClass private: int Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold); PathType * Find_Path(CELL dest, FacingType *final_moves, int maxlen, MoveType threshhold); + bool Find_Path_AStar(PathType& reuslt_path, const CELL source, CELL dest, const int maxLen, const MoveType threshhold, const int threat); + CELL Find_Passable_Position_Near(const CELL target, const int maxRadius, const MoveType threshhold, const int threat); void Debug_Draw_Map(char const * txt, CELL start, CELL dest, bool pause); void Debug_Draw_Path(PathType *path); bool Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold); diff --git a/REDALERT/FUNCTION.H b/REDALERT/FUNCTION.H index 6d99d8c1d..4eff0073c 100644 --- a/REDALERT/FUNCTION.H +++ b/REDALERT/FUNCTION.H @@ -553,6 +553,9 @@ void CC_Draw_Shape(const ObjectClass *object, const char *shape_file_name, void // Added for pip draw intercept - SKY void CC_Draw_Pip(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); +// Added for line draw intercept (ex. Obelisk laser) - SKY +void CC_Draw_Line(int x, int y, int x1, int y1, unsigned char color, int frame, WindowNumberType window); + void Go_Editor(bool flag); //long MixFileHandler(VQAHandle * vqa, long action, void * buffer, long nbytes); char *CC_Get_Shape_Filename(void const * shapeptr ); diff --git a/REDALERT/GLOBALS.CPP b/REDALERT/GLOBALS.CPP index 9e8bb1440..4cd786351 100644 --- a/REDALERT/GLOBALS.CPP +++ b/REDALERT/GLOBALS.CPP @@ -59,6 +59,8 @@ bool Debug_Modem_Dump = false; // true = print the Modem Stuff bool Debug_Print_Events = false; // true = print event & packet processing bool Debug_Force_Crash = false; +CFEPatchConfig ActiveCFEPatchConfig; + TFixedIHeapClass Aircraft; TFixedIHeapClass Anims; TFixedIHeapClass Buildings; diff --git a/REDALERT/INIT.CPP b/REDALERT/INIT.CPP index b1ed212d6..bb1e2778a 100644 --- a/REDALERT/INIT.CPP +++ b/REDALERT/INIT.CPP @@ -172,6 +172,29 @@ static void Load_Prolog_Page(void) Show_Mouse(); } +void Init_CFEPatch() +{ + char* const fileBuffer = _ShapeBuffer; + CCFileClass file("CFEPATCH_RA.INI"); + memset(fileBuffer, '\0', _ShapeBufferSize); + + if (file.Is_Available()) + { + file.Read(fileBuffer, _ShapeBufferSize - 1); + file.Close(); + + ActiveCFEPatchConfig.EnableASPathing = WWGetPrivateProfileInt("FEATURES", "ASTAR_PATHING", 0, fileBuffer); + ActiveCFEPatchConfig.EnableOOMRepair = WWGetPrivateProfileInt("FEATURES", "OOM_REPAIR", 0, fileBuffer); + ActiveCFEPatchConfig.EnableRallyPoints = WWGetPrivateProfileInt("FEATURES", "RALLY_POINTS", 0, fileBuffer); + ActiveCFEPatchConfig.EnableHarvyQueueJump = WWGetPrivateProfileInt("FEATURES", "HARVY_QUEUE_JUMP", 0, fileBuffer); + + ActiveCFEPatchConfig.HarvyQueueJumpCutoff = WWGetPrivateProfileInt("SETTINGS", "QUEUE_JUMP_CUTOFF", 3, fileBuffer); + ActiveCFEPatchConfig.OreGrowthScale = WWGetPrivateProfileInt("SETTINGS", "ORE_GROWTH_SCALE", 1, fileBuffer); + } + else { + throw; + } +} /*********************************************************************************************** * Init_Game -- Main game initialization routine. * @@ -434,6 +457,8 @@ bool Init_Game(int , char * []) ChronalVortex.Stop(); ChronalVortex.Setup_Remap_Tables(Scen.Theater); + Init_CFEPatch(); + return(true); } diff --git a/REDALERT/LOGIC.CPP b/REDALERT/LOGIC.CPP index ed4b6fa7e..954c79d69 100644 --- a/REDALERT/LOGIC.CPP +++ b/REDALERT/LOGIC.CPP @@ -363,7 +363,10 @@ void LogicClass::AI(void) /* ** Map related logic is performed. */ - Map.Logic(); + for (int i = 0; i < ActiveCFEPatchConfig.OreGrowthScale; ++i) + { + Map.Logic(); + } /* ** Factory processing is performed. diff --git a/REDALERT/License.txt b/REDALERT/License.txt index a4adf6d01..a656b7867 100644 --- a/REDALERT/License.txt +++ b/REDALERT/License.txt @@ -1,4 +1,6 @@ -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. +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 diff --git a/REDALERT/RedAlert.vcxproj b/REDALERT/RedAlert.vcxproj index 7161a6d7f..148f8d77c 100644 --- a/REDALERT/RedAlert.vcxproj +++ b/REDALERT/RedAlert.vcxproj @@ -95,7 +95,7 @@ ./win32lib true false - TRUE_FALSE_DEFINED;ENGLISH;WIN32;NDEBUG;_WINDOWS;_USRDLL;REDALERT_EXPORTS;%(PreprocessorDefinitions) + _HAS_EXCEPTIONS=0;TRUE_FALSE_DEFINED;ENGLISH;WIN32;NDEBUG;_WINDOWS;_USRDLL;REDALERT_EXPORTS;%(PreprocessorDefinitions) 1Byte false MultiThreaded diff --git a/REDALERT/UNIT.CPP b/REDALERT/UNIT.CPP index bdcdb6597..cda4feaef 100644 --- a/REDALERT/UNIT.CPP +++ b/REDALERT/UNIT.CPP @@ -889,6 +889,14 @@ RadioMessageType UnitClass::Receive_Message(RadioClass * from, RadioMessageType DriveClass::Receive_Message(from, message, param); return(RADIO_ROGER); + case RADIO_CANCEL: + if ((Mission == MISSION_RETURN || Mission == MISSION_ENTER) && Class->IsToHarvest) + { + Set_Mission(MISSION_HARVEST); + Transmit_Message(RADIO_OVER_OUT); + } + return RADIO_ROGER; + } return(DriveClass::Receive_Message(from, message, param)); } @@ -2805,6 +2813,14 @@ int UnitClass::Mission_Harvest(void) ** Go and find a Tiberium field to harvest. */ case LOOKING: + //cfehunter 09th June 2020. Honestly this should probably be here anyway, but don't change normal behaviour unless the feature is turned on + if (ActiveCFEPatchConfig.EnableHarvyQueueJump && Tiberium_Load() == 0x0100) { + IsHarvesting = false; + Status = FINDHOME; + Assign_Target(TARGET_NONE); //Clear out the target or it'll assume it already has a refinery to go towards + return 1; + } + /* ** When full of tiberium, just skip to finding a free refinery ** to unload at. @@ -2914,7 +2930,13 @@ int UnitClass::Mission_Harvest(void) nearest = Find_Docking_Bay(STRUCT_REFINERY, false); ScenarioInit--; if (nearest != NULL) { - Assign_Destination(::As_Target(Nearby_Location(nearest))); + + if (ActiveCFEPatchConfig.EnableHarvyQueueJump && Transmit_Message(RADIO_HELLO, nearest) == RADIO_ROGER) { + Status = HEADINGHOME; + } + else { + Assign_Destination(::As_Target(Nearby_Location(nearest))); + } } } } diff --git a/TIBERIANDAWN/License.txt b/TIBERIANDAWN/License.txt index a4adf6d01..352d4414a 100644 --- a/TIBERIANDAWN/License.txt +++ b/TIBERIANDAWN/License.txt @@ -1,4 +1,6 @@ -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. +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 From f023eb0380ded86437efee919e5fd6ddd5389a1d Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Fri, 12 Jun 2020 23:46:03 +0200 Subject: [PATCH 2/6] Reverted License.txt and AIRCRAFT.CPP changes --- REDALERT/AIRCRAFT.CPP | 17 +---------------- REDALERT/License.txt | 4 +--- TIBERIANDAWN/License.txt | 4 +--- 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/REDALERT/AIRCRAFT.CPP b/REDALERT/AIRCRAFT.CPP index 8169fe6a1..7722529e2 100644 --- a/REDALERT/AIRCRAFT.CPP +++ b/REDALERT/AIRCRAFT.CPP @@ -1430,26 +1430,11 @@ int AircraftClass::Exit_Object(TechnoClass * unit) ** unloading the next passenger or taking off. */ if (unit->Unlimbo(Coord, Facing_Dir(_toface[face]))) { - TARGET target = ::As_Target(cell); - - /* CFE Patch - unsure what the changes in this file are meant to do. - Possible solution might be (Root-Core @ 20200611): - - if (const BuildingClass* const target_building = As_Building(NavCom)) - { - if (target_building->Can_Have_Rally_Point() && Target_Legal(target_building->RallyPoint)) - { - target = target_building->Target_For_Rally_Point(); - } - }*/ - unit->Assign_Mission(MISSION_MOVE); - unit->Assign_Destination(target); - + unit->Assign_Destination(::As_Target(cell)); if (Transmit_Message(RADIO_HELLO, unit) == RADIO_ROGER) { Transmit_Message(RADIO_UNLOAD); } - unit->Look(false); return(true); } diff --git a/REDALERT/License.txt b/REDALERT/License.txt index a656b7867..a4adf6d01 100644 --- a/REDALERT/License.txt +++ b/REDALERT/License.txt @@ -1,6 +1,4 @@ -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. +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 diff --git a/TIBERIANDAWN/License.txt b/TIBERIANDAWN/License.txt index 352d4414a..a4adf6d01 100644 --- a/TIBERIANDAWN/License.txt +++ b/TIBERIANDAWN/License.txt @@ -1,6 +1,4 @@ -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. +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 From 533d3fcaf7858c9a5ca35ecd48c61dfd3dc2ab4c Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sat, 13 Jun 2020 00:53:08 +0200 Subject: [PATCH 3/6] Enables instant capturing for RA --- REDALERT/EXTERNS.H | 2 ++ REDALERT/INFANTRY.CPP | 8 ++++---- REDALERT/INIT.CPP | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/REDALERT/EXTERNS.H b/REDALERT/EXTERNS.H index e0d9d59d9..d213daeb8 100644 --- a/REDALERT/EXTERNS.H +++ b/REDALERT/EXTERNS.H @@ -97,6 +97,8 @@ struct CFEPatchConfig bool EnableOOMRepair = false; bool EnableRallyPoints = false; bool EnableHarvyQueueJump = false; + bool EnableInstantCapture = false; + int HarvyQueueJumpCutoff = 3; int OreGrowthScale = 1; }; diff --git a/REDALERT/INFANTRY.CPP b/REDALERT/INFANTRY.CPP index edcbf75ff..14364d722 100644 --- a/REDALERT/INFANTRY.CPP +++ b/REDALERT/INFANTRY.CPP @@ -642,9 +642,9 @@ void InfantryClass::Per_Cell_Process(PCPType why) iscapturable = ((BuildingClass *)tech)->Class->IsCaptureable; } #ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 - if (tech->Health_Ratio() <= EngineerCaptureLevel && iscapturable) { + if ((ActiveCFEPatchConfig.EnableInstantCapture || tech->Health_Ratio() <= EngineerCaptureLevel) && iscapturable) { #else - if (tech->Health_Ratio() <= Rule.ConditionRed && iscapturable) { + if ((ActiveCFEPatchConfig.EnableInstantCapture || tech->Health_Ratio() <= Rule.ConditionRed) && iscapturable) { #endif if (tech->Trigger.Is_Valid()) { tech->Trigger->Spring(TEVENT_PLAYER_ENTERED, this); @@ -2917,9 +2917,9 @@ ActionType InfantryClass::What_Action(ObjectClass const * object) const if (bldg->Class->IsCaptureable) { #ifdef FIXIT_ENGINEER // checked - ajw 9/28/98 - if (bldg->Health_Ratio() <= EngineerCaptureLevel) { + if (ActiveCFEPatchConfig.EnableInstantCapture || bldg->Health_Ratio() <= EngineerCaptureLevel) { #else - if (bldg->Health_Ratio() <= Rule.ConditionRed) { + if (ActiveCFEPatchConfig.EnableInstantCapture || bldg->Health_Ratio() <= Rule.ConditionRed) { #endif return(ACTION_CAPTURE); } diff --git a/REDALERT/INIT.CPP b/REDALERT/INIT.CPP index bb1e2778a..247114854 100644 --- a/REDALERT/INIT.CPP +++ b/REDALERT/INIT.CPP @@ -187,6 +187,7 @@ void Init_CFEPatch() ActiveCFEPatchConfig.EnableOOMRepair = WWGetPrivateProfileInt("FEATURES", "OOM_REPAIR", 0, fileBuffer); ActiveCFEPatchConfig.EnableRallyPoints = WWGetPrivateProfileInt("FEATURES", "RALLY_POINTS", 0, fileBuffer); ActiveCFEPatchConfig.EnableHarvyQueueJump = WWGetPrivateProfileInt("FEATURES", "HARVY_QUEUE_JUMP", 0, fileBuffer); + ActiveCFEPatchConfig.EnableInstantCapture = WWGetPrivateProfileInt("FEATURES", "INSTANT_CAPTURE", 0, fileBuffer); ActiveCFEPatchConfig.HarvyQueueJumpCutoff = WWGetPrivateProfileInt("SETTINGS", "QUEUE_JUMP_CUTOFF", 3, fileBuffer); ActiveCFEPatchConfig.OreGrowthScale = WWGetPrivateProfileInt("SETTINGS", "ORE_GROWTH_SCALE", 1, fileBuffer); From 90cfc5cdfa70a3d340a0e375aee6de4b8736d04f Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sat, 13 Jun 2020 05:21:12 +0200 Subject: [PATCH 4/6] Fixed usage of RA specific TargetClass wrapper --- REDALERT/BUILDING.CPP | 4 ++-- REDALERT/EVENT.CPP | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/REDALERT/BUILDING.CPP b/REDALERT/BUILDING.CPP index 6e71ea10e..40e7be8cc 100644 --- a/REDALERT/BUILDING.CPP +++ b/REDALERT/BUILDING.CPP @@ -160,7 +160,7 @@ bool BuildingClass::Can_Have_Rally_Point() const return ActiveCFEPatchConfig.EnableRallyPoints; default: - return (StructType(*this) == STRUCT_REPAIR); + return false; //(StructType(*this) == STRUCT_REPAIR); } } @@ -888,7 +888,7 @@ bool BuildingClass::Mark(MarkType mark) void BuildingClass::Player_Set_Rally_Point(TARGET target) { - OutList.Add(EventClass(EventClass::SET_RALLY, As_Target(), target)); + OutList.Add(EventClass(EventClass::SET_RALLY, TargetClass(As_Target()), TargetClass(target))); } diff --git a/REDALERT/EVENT.CPP b/REDALERT/EVENT.CPP index f2b06a91c..8af0c751e 100644 --- a/REDALERT/EVENT.CPP +++ b/REDALERT/EVENT.CPP @@ -1052,11 +1052,11 @@ void EventClass::Execute(void) //CFE Patch events case SET_RALLY: - if (BuildingClass* const building = As_Building(Data.NavCom.Whom)) + if (BuildingClass* const building = Data.NavCom.Whom.As_Building()) { - if (building->RallyPoint != Data.NavCom.Where) + if (building->RallyPoint != Data.NavCom.Where.As_Target()) { - building->RallyPoint = Data.NavCom.Where; + building->RallyPoint = Data.NavCom.Where.As_Target(); Map.Flag_To_Redraw(true); } } From dca735d144a72c7e2408da459af0751b66cfa51c Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sat, 13 Jun 2020 12:57:04 +0200 Subject: [PATCH 5/6] Enabled vessels to use rallypoints --- REDALERT/BUILDING.CPP | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/REDALERT/BUILDING.CPP b/REDALERT/BUILDING.CPP index 40e7be8cc..0ff94eb30 100644 --- a/REDALERT/BUILDING.CPP +++ b/REDALERT/BUILDING.CPP @@ -153,7 +153,7 @@ bool BuildingClass::Can_Have_Rally_Point() const { switch (Class->ToBuild) { - case RTTI_AIRCRAFTTYPE: + // case RTTI_AIRCRAFTTYPE: case RTTI_INFANTRYTYPE: case RTTI_UNITTYPE: case RTTI_VESSELTYPE: @@ -2089,7 +2089,13 @@ int BuildingClass::Exit_Object(TechnoClass * base) ScenarioInit++; cell = Find_Exit_Cell(base); if (cell != 0 && base->Unlimbo(Cell_Coord(cell), Direction(Cell_Coord(cell)))) { - base->Assign_Mission(MISSION_GUARD); + if (Can_Have_Rally_Point() && RallyPoint) { + base->Set_Mission(MISSION_MOVE); + base->Assign_Destination(Target_For_Rally_Point(base->Techno_Type_Class()->Speed)); + } + else { + base->Assign_Mission(MISSION_GUARD); + } ScenarioInit--; return(2); } From a616625bfca65851274b2e38c193dad027093682 Mon Sep 17 00:00:00 2001 From: Fabian Arndt Date: Sat, 13 Jun 2020 16:10:32 +0200 Subject: [PATCH 6/6] Adapted to 10cc2e0ef2e794538028b536082d9ee551f314a8 --- Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI | 11 + .../SRGB/COMMON/MISC/PLACEMENT_EXTRA.tga | Bin 0 -> 21339 bytes Mod Data/Data/XML/TILESETS/COMMON_MISC.XML | 214 ++++++++++++++++++ REDALERT/BDATA.CPP | 34 +++ REDALERT/BUILDING.CPP | 126 ++++++----- REDALERT/BUILDING.H | 5 +- REDALERT/CONST.CPP | 2 + REDALERT/DEFINES.H | 2 +- REDALERT/DISPLAY.CPP | 54 ++++- REDALERT/DISPLAY.H | 2 + REDALERT/DLLInterface.cpp | 31 ++- REDALERT/EVENT.CPP | 12 +- REDALERT/EXTERNS.H | 2 + REDALERT/FINDPATH.CPP | 3 +- REDALERT/FUNCTION.H | 5 + REDALERT/HOUSE.CPP | 5 +- REDALERT/INIT.CPP | 27 +-- REDALERT/MAP.CPP | 47 +++- REDALERT/MAP.H | 2 + REDALERT/RADIO.CPP | 3 +- REDALERT/RedAlert.vcxproj | 7 +- REDALERT/RedAlert.vcxproj.filters | 9 + REDALERT/TYPE.H | 1 + REDALERT/VECTOR.H | 10 + REDALERT/WIN32LIB/DSOUND.H | 1 + 25 files changed, 525 insertions(+), 90 deletions(-) create mode 100644 Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI create mode 100644 Mod Data/Data/ART/TEXTURES/SRGB/COMMON/MISC/PLACEMENT_EXTRA.tga create mode 100644 Mod Data/Data/XML/TILESETS/COMMON_MISC.XML diff --git a/Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI b/Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI new file mode 100644 index 000000000..aeb7bb1ee --- /dev/null +++ b/Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI @@ -0,0 +1,11 @@ +[FEATURES] +ASTAR_PATHING=1 +OOM_REPAIR=1 +RALLY_POINTS=1 +HARVY_QUEUE_JUMP=1 +INSTANT_CAPTURE=1 + +[SETTINGS] +QUEUE_JUMP_CUTOFF=3 +ORE_GROWTH_SCALE=1 +WALL_BUILD_LENGTH=5 diff --git a/Mod Data/Data/ART/TEXTURES/SRGB/COMMON/MISC/PLACEMENT_EXTRA.tga b/Mod Data/Data/ART/TEXTURES/SRGB/COMMON/MISC/PLACEMENT_EXTRA.tga new file mode 100644 index 0000000000000000000000000000000000000000..68a305b319ea501a3773542555068684d5b30e89 GIT binary patch literal 21339 zcmeI3TS~(~6o#h=S_Hu*WCI2jw@@J<4b}_3`jCgd6d$^PmXdvCF}}M6GZQlvQj#V! zX8t383Z+eNf6koCH)p0wovZrTsEvxI+bqlGd3jgpM`^BK<+&a<05{mq=#wC_Qi|)2 zTWv%UA{oSPiHw=hy&52wlU-fz;6+-cpoLjSN+CjIUXMAbCDHYf341VM>1z$9mVjUe zaeznK(3c1V6CskENKOo07U2=hAdbER1qVZ4A`nc3NOTTE*9S}wGRhq}(Y+*)6Pxpo zvVt|{B;DuCj6g=j0()^m1bLxhI9c8+{8l@FJ~JP?$BaEkdMqOi)Xr>mw(! zpO}T-bwExW!i0wkDLK)7$uuP=x*HK5!3^R6kF=pL5eOziBszzo>mw(U6Um9{h%n&( zL|-s_2yl_k0n-C|IkEc4iG$Vy_F+Iy^tB^EhjU>all{a&TP=<#kbB>yv74om>(1^a zf&Ij$UzN#?>&|6x1xQfjMD`Pdr*(27IZ*>HNHPkLhW{6L22UvM@Lv7#LW0Oj=`cJR z!=>0y90IOmjEwK!07=d5f$jUEJ%1lceLG}CyHE-*(kcb*g6T-%|DD6kW<3XNhli09 z9a|O>5X>MB@JO?t=nKKPKheVsoSf)xM0f-?g9Hs3{!8MZYE*ba;$pfm3y% zKF^f8RMWn{`0>76t>aa;j_%^y_~yFh^O1gS@^kzBzLUjc_4Il1GJ8#)l5~FccAq@V GV)YFjX+;JA literal 0 HcmV?d00001 diff --git a/Mod Data/Data/XML/TILESETS/COMMON_MISC.XML b/Mod Data/Data/XML/TILESETS/COMMON_MISC.XML new file mode 100644 index 000000000..920b26bb7 --- /dev/null +++ b/Mod Data/Data/XML/TILESETS/COMMON_MISC.XML @@ -0,0 +1,214 @@ + + + + Common\Misc + + + + MISSING + 0 + + + + missing.tga + + + 1.0 + 1.0 + 1.0 + 0.5 + + + + + + FLASH + 0 + + + + flash.tga + + + + + + SHADOW + 0 + + + + shadow.tga + + + + + + PLACEMENT_GOOD + 0 + + + + placement_good.tga + + + + + + PLACEMENT_EXTRA + 0 + + + + placement_extra.tga + + + + + + PLACEMENT_BAD + 0 + + + + placement_bad.tga + + + + + + STEALTH + 0 + + + + 40 + + + stealth-0000-000.tga + stealth-0000-001.tga + stealth-0000-002.tga + stealth-0000-003.tga + stealth-0000-004.tga + stealth-0000-005.tga + stealth-0000-006.tga + stealth-0000-007.tga + stealth-0000-008.tga + stealth-0000-009.tga + stealth-0000-010.tga + stealth-0000-011.tga + stealth-0000-012.tga + stealth-0000-013.tga + stealth-0000-014.tga + stealth-0000-015.tga + stealth-0000-016.tga + stealth-0000-017.tga + stealth-0000-018.tga + stealth-0000-019.tga + stealth-0000-020.tga + stealth-0000-021.tga + stealth-0000-022.tga + stealth-0000-023.tga + stealth-0000-024.tga + stealth-0000-025.tga + stealth-0000-026.tga + stealth-0000-027.tga + stealth-0000-028.tga + stealth-0000-029.tga + stealth-0000-030.tga + stealth-0000-031.tga + stealth-0000-032.tga + stealth-0000-033.tga + stealth-0000-034.tga + stealth-0000-035.tga + stealth-0000-036.tga + stealth-0000-037.tga + stealth-0000-038.tga + stealth-0000-039.tga + stealth-0000-040.tga + stealth-0000-041.tga + stealth-0000-042.tga + stealth-0000-043.tga + stealth-0000-044.tga + stealth-0000-045.tga + stealth-0000-046.tga + stealth-0000-047.tga + stealth-0000-048.tga + stealth-0000-049.tga + stealth-0000-050.tga + stealth-0000-051.tga + stealth-0000-052.tga + stealth-0000-053.tga + stealth-0000-054.tga + stealth-0000-055.tga + stealth-0000-056.tga + stealth-0000-057.tga + stealth-0000-058.tga + stealth-0000-059.tga + stealth-0000-060.tga + stealth-0000-061.tga + stealth-0000-062.tga + stealth-0000-063.tga + stealth-0000-064.tga + stealth-0000-065.tga + stealth-0000-066.tga + stealth-0000-067.tga + stealth-0000-068.tga + stealth-0000-069.tga + stealth-0000-070.tga + stealth-0000-071.tga + stealth-0000-072.tga + stealth-0000-073.tga + stealth-0000-074.tga + stealth-0000-075.tga + stealth-0000-076.tga + stealth-0000-077.tga + stealth-0000-078.tga + stealth-0000-079.tga + stealth-0000-080.tga + stealth-0000-081.tga + stealth-0000-082.tga + stealth-0000-083.tga + stealth-0000-084.tga + stealth-0000-085.tga + stealth-0000-086.tga + stealth-0000-087.tga + stealth-0000-088.tga + stealth-0000-089.tga + stealth-0000-090.tga + stealth-0000-091.tga + stealth-0000-092.tga + stealth-0000-093.tga + stealth-0000-094.tga + stealth-0000-095.tga + stealth-0000-096.tga + stealth-0000-097.tga + stealth-0000-098.tga + stealth-0000-099.tga + stealth-0000-100.tga + stealth-0000-101.tga + stealth-0000-102.tga + stealth-0000-103.tga + stealth-0000-104.tga + stealth-0000-105.tga + stealth-0000-106.tga + stealth-0000-107.tga + stealth-0000-108.tga + stealth-0000-109.tga + stealth-0000-110.tga + stealth-0000-111.tga + stealth-0000-112.tga + stealth-0000-113.tga + stealth-0000-114.tga + stealth-0000-115.tga + stealth-0000-116.tga + stealth-0000-117.tga + stealth-0000-118.tga + stealth-0000-119.tga + + AL_POSTFX_TYPE_ENGINE_HEAT_DISTORTION + + + + + \ No newline at end of file diff --git a/REDALERT/BDATA.CPP b/REDALERT/BDATA.CPP index 8e0045280..29df4f426 100644 --- a/REDALERT/BDATA.CPP +++ b/REDALERT/BDATA.CPP @@ -3627,6 +3627,40 @@ bool BuildingTypeClass::Bib_And_Offset(SmudgeType & bib, CELL & cell) const } +/*********************************************************************************************** + * BuildingTypeClass::Overlay_Type * + * * + * Determines the overlay type required to display this building * + * * + * INPUT: none * + * * + * OUTPUT: overlay type to display * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/2020 cfehunter : Created. * + *=============================================================================================*/ +OverlayType BuildingTypeClass::Overlay_Type() const +{ + switch (StructType(*this)) + { + case STRUCT_SANDBAG_WALL: + return OVERLAY_SANDBAG_WALL; + case STRUCT_CYCLONE_WALL: + return OVERLAY_CYCLONE_WALL; + case STRUCT_BRICK_WALL: + return OVERLAY_BRICK_WALL; + case STRUCT_BARBWIRE_WALL: + return OVERLAY_BARBWIRE_WALL; + case STRUCT_WOOD_WALL: + return OVERLAY_WOOD_WALL; + default: + return OVERLAY_NONE; + } +} + + /*********************************************************************************************** * BuildingTypeClass::Max_Pips -- Determines the maximum pips to display. * * * diff --git a/REDALERT/BUILDING.CPP b/REDALERT/BUILDING.CPP index 0ff94eb30..bca438a62 100644 --- a/REDALERT/BUILDING.CPP +++ b/REDALERT/BUILDING.CPP @@ -891,6 +891,70 @@ void BuildingClass::Player_Set_Rally_Point(TARGET target) OutList.Add(EventClass(EventClass::SET_RALLY, TargetClass(As_Target()), TargetClass(target))); } +bool BuildingClass::Unlimbo_Wall(COORDINATE coord) +{ + const CELL placecell = Coord_Cell(coord); + if (Can_Enter_Cell(placecell, FACING_NONE) == MOVE_OK) { + + const OverlayType otype = Class->Overlay_Type(); + if (otype != OVERLAY_NONE) + { + + //Attempt to place the core pillar + const OverlayTypeClass& oclass = OverlayTypeClass::As_Reference(otype); + if (Create_Overlay_At(oclass, placecell)) + { + if (ActiveCFEPatchConfig.WallBuildLength > 1) + { + /* cfehunter 12/06/2020 + * Walk through every cardinal direction and run the same scanning logic as the build cursor + * Then place new wall overlays and transfer cell ownership up to the scan result distance + */ + for (FacingType dir : FacingCardinals) + { + const int scanDist = Map.Scan_For_Overlay(placecell, dir, otype, ActiveCFEPatchConfig.WallBuildLength); + CELL adjcell = placecell; + for (int i = 0; i < scanDist; ++i) + { + adjcell = Adjacent_Cell(adjcell, dir); + + //If we fail to create the overlay here, abort this direction and move on + if (!Create_Overlay_At(oclass, adjcell)) + break; + } + } + } + Transmit_Message(RADIO_OVER_OUT); + Map.Sight_From(Coord_Cell(coord), Class->SightRange, House); + delete this; + return true; + } + } + } + + return false; +} + +bool BuildingClass::Create_Overlay_At(const OverlayTypeClass& oclass, const CELL cell) +{ + /* cfehunter 12/06/2020 + * The original wall creation code would leak memory if it failed to unlimbo. This shouldn't + */ + if (ObjectClass* const o = oclass.Create_One_Of(House)) + { + if (o->Unlimbo(Cell_Coord(cell))) + { + Map[cell].Owner = House->Class->House; + return true; + } + else + { + delete o; + } + } + return false; +} + /*********************************************************************************************** * BuildingClass::AI -- Handles non-graphic AI processing for buildings. * @@ -1159,50 +1223,7 @@ bool BuildingClass::Unlimbo(COORDINATE coord, DirType dir) ** converted to an overlay type. */ if (Class->IsWall) { - if (Can_Enter_Cell(Coord_Cell(coord), FACING_NONE) == MOVE_OK) { - OverlayType otype = OVERLAY_NONE; - switch (Class->Type) { - case STRUCT_SANDBAG_WALL: - otype = OVERLAY_SANDBAG_WALL; - break; - - case STRUCT_CYCLONE_WALL: - otype = OVERLAY_CYCLONE_WALL; - break; - - case STRUCT_BRICK_WALL: - otype = OVERLAY_BRICK_WALL; - break; - - case STRUCT_BARBWIRE_WALL: - otype = OVERLAY_BARBWIRE_WALL; - break; - - case STRUCT_WOOD_WALL: - otype = OVERLAY_WOOD_WALL; - break; - - case STRUCT_FENCE: - otype = OVERLAY_FENCE; - break; - - default: - otype = OVERLAY_NONE; - break; - - } - if (otype != OVERLAY_NONE) { - ObjectClass * o = OverlayTypeClass::As_Reference(otype).Create_One_Of(House); - if (o && o->Unlimbo(coord)) { - Map[coord].Owner = House->Class->House; - Transmit_Message(RADIO_OVER_OUT); - Map.Sight_From(Coord_Cell(coord), Class->SightRange, House); - delete this; - return(true); - } - } - } - return(false); + return Unlimbo_Wall(coord); } /* @@ -1601,7 +1622,9 @@ ResultType BuildingClass::Take_Damage(int & damage, int distance, WarheadType wa TARGET BuildingClass::Target_For_Rally_Point(const SpeedType speed) const { - return Is_Target_Cell(RallyPoint) ? ::As_Target(Map.Nearby_Location(As_Cell(RallyPoint), speed)) : RallyPoint; + return Target_Legal(RallyPoint) ? + (Is_Target_Cell(RallyPoint) ? ::As_Target(Map.Nearby_Location(As_Cell(RallyPoint), speed)) : RallyPoint) : + ::As_Target(Nearby_Location()); } /*********************************************************************************************** @@ -1972,8 +1995,9 @@ void BuildingClass::Assign_Target(TARGET target) target = TARGET_NONE; } - if (Can_Have_Rally_Point() && Target_Legal(target)) { + if (RallyPoint != target && Can_Have_Rally_Point() && (target == TARGET_NONE || Target_Legal(target))) { RallyPoint = target; + Map.Flag_To_Redraw(true); return; } @@ -3502,15 +3526,15 @@ MoveType BuildingClass::Can_Enter_Cell(CELL cell, FacingType) const assert(Buildings.ID(this) == ID); assert(IsActive); - if (*this == STRUCT_CONST && IsDown) { - return(Map[cell].Is_Clear_To_Build(Class->Speed) ? MOVE_OK : MOVE_NO); - } - - if (Can_Have_Rally_Point()) { + if (Can_Have_Rally_Point() && IsDown && IsLocked) { SpeedType buildSpeed = Fetch_Techno_Type(Class->ToBuild, 0)->Speed; // 0 = Some of this kind return Map[cell].Is_Clear_To_Move(buildSpeed, true, true) ? MOVE_OK : MOVE_NO; } + if (*this == STRUCT_CONST && IsDown && IsLocked) { + return(Map[cell].Is_Clear_To_Build(Class->Speed) ? MOVE_OK : MOVE_NO); + } + if (!Debug_Map && ScenarioInit == 0 && Session.Type == GAME_NORMAL && House->IsPlayerControl && !Map[cell].IsMapped) { return(MOVE_NO); } diff --git a/REDALERT/BUILDING.H b/REDALERT/BUILDING.H index 5bf0eefe8..4ef841b45 100644 --- a/REDALERT/BUILDING.H +++ b/REDALERT/BUILDING.H @@ -357,12 +357,15 @@ class BuildingClass : public TechnoClass virtual unsigned Spied_By() const; bool Can_Have_Rally_Point() const; - virtual void Set_Unselected_By_Player(HouseClass* player = NULL) override; + virtual void Set_Unselected_By_Player(HouseClass* player = nullptr) override; private: void Drop_Debris(TARGET source = TARGET_NONE); void Player_Set_Rally_Point(TARGET target); + bool Unlimbo_Wall(COORDINATE coord); + bool Create_Overlay_At(const OverlayTypeClass& oclass, const CELL cell); + /* ** Some additional padding in case we need to add data to the class and maintain backwards compatibility for save/load */ diff --git a/REDALERT/CONST.CPP b/REDALERT/CONST.CPP index 912b70a51..ef4cc4f08 100644 --- a/REDALERT/CONST.CPP +++ b/REDALERT/CONST.CPP @@ -843,3 +843,5 @@ char const Keys[] = "1=AigKVje8mROcR8QixnxUEF5b29Curkq01DNDWCdOG99XBqH79OaCiTCB\n" #endif "\n"; + +const FacingType FacingCardinals[4]{ FACING_N, FACING_E, FACING_S, FACING_W }; diff --git a/REDALERT/DEFINES.H b/REDALERT/DEFINES.H index bc509b617..6434937a3 100644 --- a/REDALERT/DEFINES.H +++ b/REDALERT/DEFINES.H @@ -2435,7 +2435,7 @@ typedef enum RadioMessageType : unsigned char { RADIO_ALL_DONE, // "I have completed the task." RADIO_NEED_REPAIR, // "Are you in need of service depot work?" RADIO_ON_DEPOT, // "Are you sitting on a service depot?" - RADIO_CANCEL, + RADIO_CANCEL, // "Scratch that" RADIO_COUNT } RadioMessageType; diff --git a/REDALERT/DISPLAY.CPP b/REDALERT/DISPLAY.CPP index c865e5aea..469f69d7a 100644 --- a/REDALERT/DISPLAY.CPP +++ b/REDALERT/DISPLAY.CPP @@ -1067,6 +1067,8 @@ void DisplayClass::AI(KeyNumType & input, int x, int y) Mouse_Left_Release(-1, Get_Mouse_X(), Get_Mouse_Y(), NULL, ACTION_NONE); } + Update_Placement_Cursor(); + MapClass::AI(input, x, y); } @@ -5135,4 +5137,54 @@ ActionType Best_Object_Action(const ObjectClass* object) ActionType Best_Object_Action(CELL cell) { return Best_Object_Action(CurrentObject.Raw(), cell); -} \ No newline at end of file +} + +/*********************************************************************************************** + * DisplayClass::Update_Placement_Cursor -- per-tick logic for the placement cursor * + * * + * This routine will run any updates for the cursor while in placement mode * + * * + * INPUT: none * + * * + * OUTPUT: none * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/2020 cfehunter: created * + *=============================================================================================*/ +void DisplayClass::Update_Placement_Cursor() +{ + if (ActiveCFEPatchConfig.WallBuildLength > 1 && In_Radar(ZoneCell)) + { + const BuildingTypeClass* const placementBuilding = PendingObject && PendingObject->What_Am_I() == RTTI_BUILDINGTYPE ? static_cast(PendingObject) : nullptr; + + if (placementBuilding && placementBuilding->IsWall) + { + const OverlayType wallOverlay = placementBuilding->Overlay_Type(); + + static short offsetlist[50]; + static constexpr int MaxOffsets = sizeof(offsetlist) / sizeof(offsetlist[0]) - 1; + int offsetslength = 1; + offsetlist[0] = 0; + + for (FacingType dir : FacingCardinals) + { + const int scandist = Scan_For_Overlay(ZoneCell, dir, wallOverlay, ActiveCFEPatchConfig.WallBuildLength); + + //Break out if we're going to overflow the buffer + if (offsetslength + scandist > MaxOffsets) + break; + + CELL current = ZoneCell; + for (int i = 0; i < scandist; ++i) + { + current = Adjacent_Cell(current, dir); + offsetlist[offsetslength++] = current - ZoneCell - ZoneOffset; + } + } + offsetlist[offsetslength] = REFRESH_EOL; + Set_Cursor_Shape(offsetlist); + } + } +} diff --git a/REDALERT/DISPLAY.H b/REDALERT/DISPLAY.H index e2fae5b06..0354e3e59 100644 --- a/REDALERT/DISPLAY.H +++ b/REDALERT/DISPLAY.H @@ -311,6 +311,8 @@ public: //ST - 1/21/2019 11:59AM void Redraw_OIcons(void); void Redraw_Shadow(void); + void Update_Placement_Cursor(void); + /* ** This bit array is used to flag cells to be redrawn. If the icon needs to ** be redrawn for a cell, then the corresponding flag will be true. diff --git a/REDALERT/DLLInterface.cpp b/REDALERT/DLLInterface.cpp index ec18571ab..14541acc2 100644 --- a/REDALERT/DLLInterface.cpp +++ b/REDALERT/DLLInterface.cpp @@ -6498,8 +6498,35 @@ if (debug_output) { } } - -} + + /* cfehunter 12/06/2020 + ** Render wall placement markers. + ** Special thanks to pchote for this, getting the cursor rendering in classic was easy + ** getting it to render in glyphX has been difficult + */ + if (cell_ptr->IsCursorHere && Map.PendingObject && CFE_Patch_Is_Wall(*Map.PendingObject) && Map.ZoneCell != cell_ptr->Cell_Number()) { + CNCDynamicMapEntryStruct& cursorEntry = dynamic_map->Entries[entry_index++]; + + strncpy(cursorEntry.AssetName, cell_ptr->Is_Clear_To_Build(SPEED_NONE) ? "PLACEMENT_EXTRA" : "PLACEMENT_BAD", CNC_OBJECT_ASSET_NAME_LENGTH); + cursorEntry.AssetName[CNC_OBJECT_ASSET_NAME_LENGTH - 1] = 0; + cursorEntry.Type = -1; + cursorEntry.Owner = (char)cell_ptr->Owner; + cursorEntry.DrawFlags = SHAPE_CENTER | SHAPE_GHOST | SHAPE_COLOR; + cursorEntry.PositionX = xpixel + (ICON_PIXEL_W / 2); + cursorEntry.PositionY = ypixel + (ICON_PIXEL_H / 2); + cursorEntry.Width = 24; + cursorEntry.Height = 24; + cursorEntry.CellX = Cell_X(cell); + cursorEntry.CellY = Cell_Y(cell); + cursorEntry.ShapeIndex = 0; + cursorEntry.IsSmudge = true; + cursorEntry.IsOverlay = false; + cursorEntry.IsResource = false; + cursorEntry.IsSellable = false; + cursorEntry.IsTheaterShape = false; + cursorEntry.IsFlag = false; + } +} diff --git a/REDALERT/EVENT.CPP b/REDALERT/EVENT.CPP index 8af0c751e..f37f6bffb 100644 --- a/REDALERT/EVENT.CPP +++ b/REDALERT/EVENT.CPP @@ -829,6 +829,9 @@ void EventClass::Execute(void) ((FootClass *)techno)->Clear_Navigation_List(); } } + else if (techno && CFE_Patch_Can_Have_Rally_Point(*techno)) { + techno->Assign_Target(TARGET_NONE); + } break; /* @@ -1054,9 +1057,14 @@ void EventClass::Execute(void) case SET_RALLY: if (BuildingClass* const building = Data.NavCom.Whom.As_Building()) { - if (building->RallyPoint != Data.NavCom.Where.As_Target()) + TARGET newTarget = Data.NavCom.Where.As_Target(); + if (building->RallyPoint != newTarget) { - building->RallyPoint = Data.NavCom.Where.As_Target(); + if (building->As_Target() != newTarget) + building->RallyPoint = newTarget; + else + building->RallyPoint = TARGET_NONE; + Map.Flag_To_Redraw(true); } } diff --git a/REDALERT/EXTERNS.H b/REDALERT/EXTERNS.H index d213daeb8..5698fb4ac 100644 --- a/REDALERT/EXTERNS.H +++ b/REDALERT/EXTERNS.H @@ -101,6 +101,7 @@ struct CFEPatchConfig int HarvyQueueJumpCutoff = 3; int OreGrowthScale = 1; + int WallBuildLength = 5; }; extern CFEPatchConfig ActiveCFEPatchConfig; @@ -407,6 +408,7 @@ extern unsigned char const Pixel2Lepton[24]; extern COORDINATE const StoppingCoordAbs[5]; extern CELL const AdjacentCell[FACING_COUNT]; extern COORDINATE const AdjacentCoord[FACING_COUNT]; +extern const FacingType FacingCardinals[4]; extern unsigned char const RemapCiv2[]; extern unsigned char const RemapCiv4[]; extern unsigned char const RemapCiv5[]; diff --git a/REDALERT/FINDPATH.CPP b/REDALERT/FINDPATH.CPP index 795df0490..cb7b9e3c6 100644 --- a/REDALERT/FINDPATH.CPP +++ b/REDALERT/FINDPATH.CPP @@ -571,7 +571,8 @@ bool FootClass::Find_Path_AStar(PathType& resultPath, const CELL source, CELL de } std::unordered_map visited_cells; - std::vector open_list; + static std::vector open_list; + open_list.clear(); //Add the source cell to the open list open_list.push_back(&visited_cells.emplace(source, AStarCell()).first->second); diff --git a/REDALERT/FUNCTION.H b/REDALERT/FUNCTION.H index 4eff0073c..7a14339f8 100644 --- a/REDALERT/FUNCTION.H +++ b/REDALERT/FUNCTION.H @@ -1088,4 +1088,9 @@ void Enable_Uncompressed_Shapes (void); */ void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_type, const char *achievement_reason); +bool CFE_Patch_Is_Wall(const ObjectTypeClass& object); +bool CFE_Patch_Can_Have_Rally_Point(const ObjectClass& object); + +void Initialise_CFE_Patch_Config(); + #endif \ No newline at end of file diff --git a/REDALERT/HOUSE.CPP b/REDALERT/HOUSE.CPP index a228ab64b..c45d6b1e8 100644 --- a/REDALERT/HOUSE.CPP +++ b/REDALERT/HOUSE.CPP @@ -4406,7 +4406,10 @@ void HouseClass::Sell_Wall(CELL cell) Sound_Effect(VOC_CASHTURN); } - Refund_Money(btype->Raw_Cost() * Rule.RefundPercent); + // Disable wall refunds if the player has a wall build length > 1 + if (!IsHuman || ActiveCFEPatchConfig.WallBuildLength <= 1) + Refund_Money(btype->Raw_Cost() * Rule.RefundPercent); + Map[cell].Overlay = OVERLAY_NONE; Map[cell].OverlayData = 0; Map[cell].Owner = HOUSE_NONE; diff --git a/REDALERT/INIT.CPP b/REDALERT/INIT.CPP index 247114854..6f994bb28 100644 --- a/REDALERT/INIT.CPP +++ b/REDALERT/INIT.CPP @@ -172,31 +172,6 @@ static void Load_Prolog_Page(void) Show_Mouse(); } -void Init_CFEPatch() -{ - char* const fileBuffer = _ShapeBuffer; - CCFileClass file("CFEPATCH_RA.INI"); - memset(fileBuffer, '\0', _ShapeBufferSize); - - if (file.Is_Available()) - { - file.Read(fileBuffer, _ShapeBufferSize - 1); - file.Close(); - - ActiveCFEPatchConfig.EnableASPathing = WWGetPrivateProfileInt("FEATURES", "ASTAR_PATHING", 0, fileBuffer); - ActiveCFEPatchConfig.EnableOOMRepair = WWGetPrivateProfileInt("FEATURES", "OOM_REPAIR", 0, fileBuffer); - ActiveCFEPatchConfig.EnableRallyPoints = WWGetPrivateProfileInt("FEATURES", "RALLY_POINTS", 0, fileBuffer); - ActiveCFEPatchConfig.EnableHarvyQueueJump = WWGetPrivateProfileInt("FEATURES", "HARVY_QUEUE_JUMP", 0, fileBuffer); - ActiveCFEPatchConfig.EnableInstantCapture = WWGetPrivateProfileInt("FEATURES", "INSTANT_CAPTURE", 0, fileBuffer); - - ActiveCFEPatchConfig.HarvyQueueJumpCutoff = WWGetPrivateProfileInt("SETTINGS", "QUEUE_JUMP_CUTOFF", 3, fileBuffer); - ActiveCFEPatchConfig.OreGrowthScale = WWGetPrivateProfileInt("SETTINGS", "ORE_GROWTH_SCALE", 1, fileBuffer); - } - else { - throw; - } -} - /*********************************************************************************************** * Init_Game -- Main game initialization routine. * * * @@ -458,7 +433,7 @@ bool Init_Game(int , char * []) ChronalVortex.Stop(); ChronalVortex.Setup_Remap_Tables(Scen.Theater); - Init_CFEPatch(); + Initialise_CFE_Patch_Config(); return(true); } diff --git a/REDALERT/MAP.CPP b/REDALERT/MAP.CPP index 43f1f5f74..b64aca5c0 100644 --- a/REDALERT/MAP.CPP +++ b/REDALERT/MAP.CPP @@ -2281,4 +2281,49 @@ void MapClass::Shroud_The_Map(void) } Flag_To_Redraw(true); } -#endif \ No newline at end of file +#endif + +/*********************************************************************************************** + * DisplayClass::Scan_For_Overlay -- scans from a cell for a clear path to a specified overlay * + * * + * This routine will find the nearest instance of the overlay in the given direction from * + * the origin. It will abort if it comes across any obstacle. * + * * + * INPUT: origin - starting cell * + * dir - direction to search in * + * overlay - overlay to search for * + * maxLength - length to stop searching at * + * * + * OUTPUT: Either the cell with the overlay, or -1 if not found * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 12/06/2020 cfehunter: created * + *=============================================================================================*/ +int MapClass::Scan_For_Overlay(const CELL origin, const FacingType dir, const OverlayType overlay, const int maxLength) const +{ + if (In_Radar(origin)) + { + CELL current = origin; + for (int i = 0; i < maxLength; ++i) + { + current = Adjacent_Cell(current, dir); + if (In_Radar(current)) + { + const CellClass& mapCell = Map[current]; + + //Found a match + if (mapCell.Overlay == overlay) + return i; + //Blocked by an object + else if (!Map[current].Is_Clear_To_Build(SPEED_NONE)) + break; + } + else + break; + } + } + + return -1; +} diff --git a/REDALERT/MAP.H b/REDALERT/MAP.H index 58201873d..e145654b6 100644 --- a/REDALERT/MAP.H +++ b/REDALERT/MAP.H @@ -102,6 +102,8 @@ class MapClass: public GScreenClass */ int Validate(void); + int Scan_For_Overlay(const CELL origin, const FacingType dir, const OverlayType overlay, const int maxLength) const; + /* ** This is the dimensions and position of the sub section of the global map. ** It is this region that appears on the radar map and constrains normal diff --git a/REDALERT/RADIO.CPP b/REDALERT/RADIO.CPP index 8c8291072..df07e56e3 100644 --- a/REDALERT/RADIO.CPP +++ b/REDALERT/RADIO.CPP @@ -77,7 +77,8 @@ char const * RadioClass::Messages[RADIO_COUNT] = { "Circumstances prevent success.", "All done with the request.", "Do you need service depot work?", - "Are you sitting on service depot?" + "Are you sitting on service depot?", + "Scratch that" }; diff --git a/REDALERT/RedAlert.vcxproj b/REDALERT/RedAlert.vcxproj index 148f8d77c..c584f3eb5 100644 --- a/REDALERT/RedAlert.vcxproj +++ b/REDALERT/RedAlert.vcxproj @@ -83,7 +83,7 @@ false ..\bin\$(PlatformName)\ $(PlatformName)\$(Configuration)\ - $(ProjectName)I + $(ProjectName) @@ -123,7 +123,7 @@ false true ProgramDatabase - TRUE_FALSE_DEFINED;ENGLISH;WIN32;_DEBUG;_WINDOWS;_USRDLL;REDALERT_EXPORTS;%(PreprocessorDefinitions) + _HAS_EXCEPTIONS=0;TRUE_FALSE_DEFINED;ENGLISH;WIN32;_DEBUG;_WINDOWS;_USRDLL;REDALERT_EXPORTS;%(PreprocessorDefinitions) false MultiThreadedDebug 1Byte @@ -197,6 +197,8 @@ + + @@ -485,6 +487,7 @@ + diff --git a/REDALERT/RedAlert.vcxproj.filters b/REDALERT/RedAlert.vcxproj.filters index 39e5841b6..28d50cf2a 100644 --- a/REDALERT/RedAlert.vcxproj.filters +++ b/REDALERT/RedAlert.vcxproj.filters @@ -122,6 +122,12 @@ Source Files + + + Source Files + + + Source Files Source Files @@ -985,6 +991,9 @@ Source Files + + + Source Files Source Files diff --git a/REDALERT/TYPE.H b/REDALERT/TYPE.H index fb56bb990..1cc106d45 100644 --- a/REDALERT/TYPE.H +++ b/REDALERT/TYPE.H @@ -812,6 +812,7 @@ class BuildingTypeClass : public TechnoTypeClass { */ static void const * WarFactoryOverlay; + OverlayType Overlay_Type() const; private: /* diff --git a/REDALERT/VECTOR.H b/REDALERT/VECTOR.H index 2500f86bb..c6b9cd90f 100644 --- a/REDALERT/VECTOR.H +++ b/REDALERT/VECTOR.H @@ -96,6 +96,11 @@ class VectorClass virtual int ID(T const * ptr); // Pointer based identification. virtual int ID(T const & ptr); // Value based identification. + // cfehunter 12/06/2020 Make vectors standard begin/end compliant + T* begin() { return Vector; } + T* end() { return Vector + Length(); } + const T* begin() const { return Vector; } + const T* end() const { return Vector + VectorMax; } protected: /* @@ -162,6 +167,11 @@ class DynamicVectorClass : public VectorClass virtual int ID(T const * ptr) {return(VectorClass::ID(ptr));}; virtual int ID(T const & ptr); + T* begin() { return Vector; } + T* end() { return begin() + Count(); } + const T* begin() const { return Vector; } + const T* end() const { return begin() + Count(); } + protected: /* diff --git a/REDALERT/WIN32LIB/DSOUND.H b/REDALERT/WIN32LIB/DSOUND.H index 5c31e9bec..17106b8e8 100644 --- a/REDALERT/WIN32LIB/DSOUND.H +++ b/REDALERT/WIN32LIB/DSOUND.H @@ -28,6 +28,7 @@ #ifdef _WIN32 #define COM_NO_WINDOWS_H #include +#include #endif #define _FACDS 0x878