diff --git a/Mod Data/CCDATA/DEFAULT.CFEPATCH.INI b/Mod Data/CCDATA/DEFAULT.CFEPATCH.INI index f4f49df1f..8656c432a 100644 --- a/Mod Data/CCDATA/DEFAULT.CFEPATCH.INI +++ b/Mod Data/CCDATA/DEFAULT.CFEPATCH.INI @@ -12,3 +12,4 @@ BUILDING_GAP=2 QUEUE_JUMP_CUTOFF=3 TIB_GROWTH_SCALE=1 WALL_BUILD_LENGTH=5 +BUILDING_GAP_OFFSET=0 diff --git a/Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI b/Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI index aeb7bb1ee..dd1718131 100644 --- a/Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI +++ b/Mod Data/CCDATA/DEFAULT.CFEPATCH_RA.INI @@ -9,3 +9,4 @@ INSTANT_CAPTURE=1 QUEUE_JUMP_CUTOFF=3 ORE_GROWTH_SCALE=1 WALL_BUILD_LENGTH=5 +BUILDING_GAP_OFFSET=0 diff --git a/REDALERT/AIRCRAFT.CPP b/REDALERT/AIRCRAFT.CPP index 7722529e2..a53708d59 100644 --- a/REDALERT/AIRCRAFT.CPP +++ b/REDALERT/AIRCRAFT.CPP @@ -2449,7 +2449,7 @@ int AircraftClass::Mission_Attack(void) ** Double check target and validate the attack zone. */ case VALIDATE_AZ: - if (!Target_Legal(TarCom)) { + if (!Target_Legal(TarCom) || !Ammo) { Status = RETURN_TO_BASE; } else { Status = PICK_ATTACK_LOCATION; diff --git a/REDALERT/BUILDING.CPP b/REDALERT/BUILDING.CPP index bca438a62..41989eff8 100644 --- a/REDALERT/BUILDING.CPP +++ b/REDALERT/BUILDING.CPP @@ -433,15 +433,8 @@ RadioMessageType BuildingClass::Receive_Message(RadioClass * from, RadioMessageT } TechnoClass::Receive_Message(from, message, param); - //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; - } - + //Rally the unit + Rally_Unit(static_cast(*from)); return(RADIO_ROGER); default: @@ -904,7 +897,7 @@ bool BuildingClass::Unlimbo_Wall(COORDINATE coord) const OverlayTypeClass& oclass = OverlayTypeClass::As_Reference(otype); if (Create_Overlay_At(oclass, placecell)) { - if (ActiveCFEPatchConfig.WallBuildLength > 1) + if (CFE_Patch_Should_Extend_Walls()) { /* cfehunter 12/06/2020 * Walk through every cardinal direction and run the same scanning logic as the build cursor @@ -2113,11 +2106,7 @@ int BuildingClass::Exit_Object(TechnoClass * base) ScenarioInit++; cell = Find_Exit_Cell(base); if (cell != 0 && base->Unlimbo(Cell_Coord(cell), Direction(Cell_Coord(cell)))) { - if (Can_Have_Rally_Point() && RallyPoint) { - base->Set_Mission(MISSION_MOVE); - base->Assign_Destination(Target_For_Rally_Point(base->Techno_Type_Class()->Speed)); - } - else { + if (!Rally_Unit(static_cast(*base))) { base->Assign_Mission(MISSION_GUARD); } ScenarioInit--; @@ -4383,6 +4372,10 @@ int BuildingClass::Mission_Repair(void) ** The repair step resulted in a completely repaired unit. */ case RADIO_ALL_DONE: + if (!Rally_Unit(static_cast(*Contact_With_Whom()))) { + Transmit_Message(RADIO_RUN_AWAY); + Transmit_Message(RADIO_OVER_OUT); + } // MBL 04.27.2020: Make only audible to the correct player // if (IsOwnedByPlayer) Speak(VOX_UNIT_REPAIRED); @@ -6186,4 +6179,33 @@ unsigned BuildingClass::Spied_By() const } return spiedby; -} \ No newline at end of file +} + +/*********************************************************************************************** + * BuildingClass::Rally_Unit -- Order a unit to the buildings rally point * + * * + * This routine will attempt to order a unit to move the the buildings rally point. * + * If the building has a rally point, and the unit should be rallyed. * + * * + * INPUT: unit - unit to rally * + * * + * OUTPUT: true if successful * + * * + * WARNINGS: none * + * * + * HISTORY: * + * 14/06/2020 cfehunter : Created. * + *=============================================================================================*/ +bool BuildingClass::Rally_Unit(FootClass& unit) +{ + if (Can_Have_Rally_Point() && CFE_Patch_Unit_Should_Rally(unit) && Target_Legal(RallyPoint)) + { + //SpeedType speed = from->Is_Techno() ? ((TechnoClass const*)from)->Techno_Type_Class()->Speed : SPEED_TRACK; + const TARGET rallyTarget = Target_For_Rally_Point(unit.Techno_Type_Class()->Speed); + + //Make sure units dont rally to themselves. Only a problem for repair/reload pads + long move_target = (long)(unit.As_Target() != rallyTarget ? rallyTarget : ::As_Target(Nearby_Location())); + return Target_Legal(move_target) && Transmit_Message(RADIO_MOVE_HERE, move_target, &unit) == RADIO_ROGER; + } + return false; +} diff --git a/REDALERT/BUILDING.H b/REDALERT/BUILDING.H index 4ef841b45..9f24712dd 100644 --- a/REDALERT/BUILDING.H +++ b/REDALERT/BUILDING.H @@ -356,9 +356,10 @@ class BuildingClass : public TechnoClass virtual unsigned Spied_By() const; - bool Can_Have_Rally_Point() const; + bool Can_Have_Rally_Point(void) const; virtual void Set_Unselected_By_Player(HouseClass* player = nullptr) override; + bool Rally_Unit(FootClass& unit); private: void Drop_Debris(TARGET source = TARGET_NONE); void Player_Set_Rally_Point(TARGET target); diff --git a/REDALERT/CFEDEBUG.H b/REDALERT/CFEDEBUG.H new file mode 100644 index 000000000..3ab282c44 --- /dev/null +++ b/REDALERT/CFEDEBUG.H @@ -0,0 +1,15 @@ +#pragma once + +#include +#include "FUNCTION.H" + +//Prints to steamapps\common\CnCRemastered\log\CnCDLL_Log_16000.txt +template +void CFE_Debug_Printf(const char* const format, const ARGS&... args) { + const int bufferSize = snprintf(nullptr, 0, format, args...); + char* printed = (char*)malloc(bufferSize + 1); + snprintf(printed, bufferSize + 1, format, args...); + + GlyphX_Debug_Print(printed); + delete printed; +} diff --git a/REDALERT/CFEPATCHQUERY.CPP b/REDALERT/CFEPATCHQUERY.CPP new file mode 100644 index 000000000..fd10e29b1 --- /dev/null +++ b/REDALERT/CFEPATCHQUERY.CPP @@ -0,0 +1,29 @@ +#include "FUNCTION.H" + +extern bool DLL_Export_Get_Input_Key_State(KeyNumType key); + +bool CFE_Patch_Is_Wall(const ObjectTypeClass& object) +{ + return object.What_Am_I() == RTTI_BUILDINGTYPE && static_cast(object).IsWall; +} + +bool CFE_Patch_Can_Have_Rally_Point(const ObjectClass& object) +{ + return object.What_Am_I() == RTTI_BUILDING && static_cast(object).Can_Have_Rally_Point(); +} + +bool CFE_Patch_Should_Extend_Walls() +{ + return ActiveCFEPatchConfig.WallBuildLength > 1 && !DLL_Export_Get_Input_Key_State(KN_LCTRL); +} + +bool CFE_Patch_Unit_Should_Rally(const FootClass& object) +{ + switch (object.What_Am_I()) + { + case RTTI_UNIT: + return !static_cast(object).Class->IsToHarvest; + default: + return true; + } +} diff --git a/REDALERT/CFEUTIL.CPP b/REDALERT/CFEUTIL.CPP new file mode 100644 index 000000000..f6a522496 --- /dev/null +++ b/REDALERT/CFEUTIL.CPP @@ -0,0 +1,149 @@ +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include + +#include "FUNCTION.H" +#include "INI.H" +#include "RANGEWRAPPER.H" + +std::string Get_User_Mod_Dir() +{ + std::string dirPath; + char documentsPath[1024]; + HRESULT result = SHGetFolderPathA(NULL, CSIDL_MYDOCUMENTS, NULL, NULL, documentsPath); + if (SUCCEEDED(result)) + { + dirPath = documentsPath; + dirPath += "\\CnCRemastered\\Mods\\Red_Alert"; + } + return dirPath; +} + +bool Load_INI_File(INIClass& ini, const char* path) +{ + CCFileClass file(path); + if (file.Is_Available()) + { + ini.Clear(); + return ini.Load(file); + } + return false; +} + +void Populate_CFE_Patch_Config_From_INI(const INIClass& ini) +{ + static constexpr int InvalidSetting = std::numeric_limits::lowest(); + const auto GetSetting = [&ini](auto& target, const char* section, const char* entry) + { + const int value = ini.Get_Int(section, entry, InvalidSetting); + if (value != InvalidSetting) + target = value; + }; + + + + //cfehunter 12/06/2020 It's really starting to get to the point where I should make feature flags + //Features + GetSetting(ActiveCFEPatchConfig.EnableASPathing, "FEATURES", "ASTAR_PATHING"); + GetSetting(ActiveCFEPatchConfig.EnableOOMRepair, "FEATURES", "OOM_REPAIR"); + GetSetting(ActiveCFEPatchConfig.EnableRallyPoints, "FEATURES", "RALLY_POINTS"); + GetSetting(ActiveCFEPatchConfig.EnableHarvyQueueJump, "FEATURES", "HARVY_QUEUE_JUMP"); + GetSetting(ActiveCFEPatchConfig.EnableInstantCapture, "FEATURES", "INSTANT_CAPTURE"); + + //Settings + GetSetting(ActiveCFEPatchConfig.HarvyQueueJumpCutoff, "SETTINGS", "QUEUE_JUMP_CUTOFF"); + GetSetting(ActiveCFEPatchConfig.OreGrowthScale, "SETTINGS", "ORE_GROWTH_SCALE"); + GetSetting(ActiveCFEPatchConfig.WallBuildLength, "SETTINGS", "WALL_BUILD_LENGTH"); + GetSetting(ActiveCFEPatchConfig.BuildingGapOffset, "SETTINGS", "BUILDING_GAP_OFFSET"); + + //Clamp Wall Length between 1 (build at all) and 10 (buffer limits) + ActiveCFEPatchConfig.WallBuildLength = (std::max)(1, (std::min)(ActiveCFEPatchConfig.WallBuildLength, 10)); +} + +bool Create_CFE_Patch_Directory(const char* path) +{ + const int directory_result = SHCreateDirectoryExA(NULL, path, NULL); + return directory_result == ERROR_SUCCESS || directory_result == ERROR_ALREADY_EXISTS; +} + +bool Reconcile_Config_With_Default(const INIClass& default, INIClass& config) +{ + bool changed = false; + using KVPair = std::pair; + for (const char* section : default) + { + for (KVPair entry : make_iterator_range(default.section_begin(section), default.section_end(section))) + { + if (!config.Is_Present(section, entry.first)) + { + config.Put_String(section, entry.first, entry.second); + changed = true; + } + } + } + + return changed; +} + +//cfehunter 12/06/2020 I really miss std::filesystem +void Initialise_CFE_Patch_Config() +{ + static constexpr const char* DefaultININame = "DEFAULT.CFEPATCH_RA.INI"; + + const std::string ini_dir = Get_User_Mod_Dir() + "\\CFEPatch"; + const std::string ini_path = ini_dir + "\\CFEPATCH_RA.INI"; + + INIClass default_ini; + Load_INI_File(default_ini, DefaultININame); + + + + //Initialise from the default config + Populate_CFE_Patch_Config_From_INI(default_ini); + + //Overwrite settings from the user config + INIClass user_ini; + if (Load_INI_File(user_ini, ini_path.c_str())) + { + Populate_CFE_Patch_Config_From_INI(user_ini); + + //Add any missing settings + if (Reconcile_Config_With_Default(default_ini, user_ini)) + { + //Create the target directory + if (Create_CFE_Patch_Directory(ini_dir.c_str())) + { + //Write the modified ini to a temp file + const std::string write_path = ini_path + ".temp"; + if (FILE* target_file = fopen(write_path.c_str(), "w")) + { + fclose(target_file); + + //If it was written successfully, overwrite the existing config + CCFileClass write_file(write_path.c_str()); + if (user_ini.Save(write_file)) + { + write_file.Close(); + CopyFileA(write_path.c_str(), ini_path.c_str(), FALSE); + } + } + } + } + } + else + { + //Clone the default + CCFileClass defaultConfig(DefaultININame); + if (defaultConfig.Is_Available()) + { + //Create directories and clone the default config + if (Create_CFE_Patch_Directory(ini_dir.c_str())) + { + //Copy the default ini to the user directory so they can modify it later + CopyFileA(defaultConfig.File_Name(), ini_path.c_str(), TRUE); + } + } + } +} diff --git a/REDALERT/DISPLAY.CPP b/REDALERT/DISPLAY.CPP index 469f69d7a..ff46c72f9 100644 --- a/REDALERT/DISPLAY.CPP +++ b/REDALERT/DISPLAY.CPP @@ -5155,13 +5155,12 @@ ActionType Best_Object_Action(CELL cell) *=============================================================================================*/ void DisplayClass::Update_Placement_Cursor() { - if (ActiveCFEPatchConfig.WallBuildLength > 1 && In_Radar(ZoneCell)) + const BuildingTypeClass* const placement_building = PendingObject && PendingObject->What_Am_I() == RTTI_BUILDINGTYPE ? static_cast(PendingObject) : nullptr; + if (placement_building && placement_building->IsWall) { - const BuildingTypeClass* const placementBuilding = PendingObject && PendingObject->What_Am_I() == RTTI_BUILDINGTYPE ? static_cast(PendingObject) : nullptr; - - if (placementBuilding && placementBuilding->IsWall) + if (CFE_Patch_Should_Extend_Walls() && In_Radar(ZoneCell)) { - const OverlayType wallOverlay = placementBuilding->Overlay_Type(); + const OverlayType wallOverlay = placement_building->Overlay_Type(); static short offsetlist[50]; static constexpr int MaxOffsets = sizeof(offsetlist) / sizeof(offsetlist[0]) - 1; @@ -5186,5 +5185,8 @@ void DisplayClass::Update_Placement_Cursor() offsetlist[offsetslength] = REFRESH_EOL; Set_Cursor_Shape(offsetlist); } + else if (CursorSize[1] != REFRESH_EOL) { + Set_Cursor_Shape(placement_building->Occupy_List(true)); + } } } diff --git a/REDALERT/EXTERNS.H b/REDALERT/EXTERNS.H index 5698fb4ac..52cecad8d 100644 --- a/REDALERT/EXTERNS.H +++ b/REDALERT/EXTERNS.H @@ -102,6 +102,7 @@ struct CFEPatchConfig int HarvyQueueJumpCutoff = 3; int OreGrowthScale = 1; int WallBuildLength = 5; + int BuildingGapOffset = 0; }; extern CFEPatchConfig ActiveCFEPatchConfig; diff --git a/REDALERT/FINDPATH.CPP b/REDALERT/FINDPATH.CPP index cb7b9e3c6..d0f4ff911 100644 --- a/REDALERT/FINDPATH.CPP +++ b/REDALERT/FINDPATH.CPP @@ -473,7 +473,7 @@ 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) +CELL FootClass::Find_Passable_Position_Near(const CELL target, const int maxRadius, const MoveType threshhold, const int threat) const { if (Passable_Cell(target, FACING_NONE, threat, threshhold)) { return target; @@ -530,7 +530,7 @@ CELL FootClass::Find_Passable_Position_Near(const CELL target, const int maxRadi return 0; } -bool FootClass::Find_Path_AStar(PathType& resultPath, const CELL source, CELL dest, const int maxLen, const MoveType threshhold, const int threat) +int FootClass::Find_Path_AStar(PathType* const resultPath, const CELL source, CELL dest, const int maxLen, const MoveType threshhold, const int threat) const { struct AStarCell { AStarCell* prev = nullptr; @@ -550,7 +550,7 @@ bool FootClass::Find_Path_AStar(PathType& resultPath, const CELL source, CELL de //General error case early exits if (maxLen <= 0 || source == dest) { - return false; + return 0; } //Target is impassable, try to account for that by finding a position nearby, or staying still if already close @@ -562,11 +562,11 @@ bool FootClass::Find_Path_AStar(PathType& resultPath, const CELL source, CELL de //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; + return 0; } } else { - return false; + return 0; } } @@ -615,33 +615,40 @@ bool FootClass::Find_Path_AStar(PathType& resultPath, const CELL source, CELL de } - if (dest_result) { + if (dest_result) + { + const int total_path_length = dest_result->cell_distance_from_start; + + //If we don't have a path to fill in, then just return here + if (resultPath == nullptr) + return total_path_length; + //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; + 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; + 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); + resultPath->Command[prev_node.cell_distance_from_start] = CELL_FACING(prev_node.position, dest_result->position); } - Optimize_Moves(&resultPath, threshhold); + Optimize_Moves(resultPath, threshhold); if (Debug_Flag || Debug_Find_Path) { - Debug_Draw_Path(&resultPath); + Debug_Draw_Path(resultPath); } - return true; + return total_path_length; } - return false; + return 0; } /*********************************************************************************************** @@ -727,7 +734,7 @@ PathType * FootClass::Find_Path(CELL dest, FacingType * final_moves, int maxlen, bool result = false; do { - result = Find_Path_AStar(path, source, dest, maxlen, threshhold, threat); + result = Find_Path_AStar(&path, source, dest, maxlen, threshhold, threat); if (!result && asThreat != -1) { switch (++threat_stage) { @@ -1312,7 +1319,7 @@ bool FootClass::Follow_Edge(CELL start, CELL target, PathType * path, FacingType * 06/01/1992 JLB : Optimized and commented. * *=============================================================================================*/ #define EMPTY (FacingType)-2 -int FootClass::Optimize_Moves(PathType * path, MoveType threshhold) +int FootClass::Optimize_Moves(PathType * path, MoveType threshhold) const //int Optimize_Moves(PathType *path, int (*callback)(CELL, FacingType), int threshold) { /* @@ -1540,7 +1547,7 @@ CELL FootClass::Safety_Point(CELL src, CELL dst, int start, int max) -int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) +int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) const { MoveType move = Can_Enter_Cell(cell, face); @@ -1569,7 +1576,7 @@ int FootClass::Passable_Cell(CELL cell, FacingType face, int threat, MoveType th return(_value[move]); } -void FootClass::Debug_Draw_Path(PathType* path) +void FootClass::Debug_Draw_Path(PathType* path) const { if (!path) return; diff --git a/REDALERT/FOOT.CPP b/REDALERT/FOOT.CPP index 3ef0bdbc4..1541cde69 100644 --- a/REDALERT/FOOT.CPP +++ b/REDALERT/FOOT.CPP @@ -79,6 +79,7 @@ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "function.h" +#include /*********************************************************************************************** @@ -2595,3 +2596,67 @@ int FootClass::Mission_Retreat(void) return(MissionControl[Mission].Normal_Delay() + Random_Pick(0, 2)); } + +/*********************************************************************************************** + * FootClass::Find_Docking_Bay -- Searches for a close docking bay. * + * * + * This routine will be used to find a building that can serve as a docking bay. The * + * closest building that qualifies will be returned. If no building could be found then * + * return with NULL. Unlike the version in Techo, this checks if the bay is reachable * + * * + * INPUT: b -- The structure type to look for. * + * * + * friendly -- Allow searching for allied buildings as well. * + * * + * OUTPUT: Returns with a pointer to the building that can serve as the best docking bay. * + * * + * WARNINGS: This routine might return NULL even if there are buildings of the specified * + * type available. This is the case when the building(s) are currently busy and * + * cannot serve as a docking bay at the moment. * + * * + * HISTORY: * + * 13/06/2020 cfehunter : Created. * + *=============================================================================================*/ +BuildingClass* FootClass::Find_Docking_Bay(StructType b, bool friendly) const +{ + BuildingClass* best = 0; + /* + ** First check to see if there are ANY buildings of the specified + ** type in thi house's inventory. If not, then don't bother to scan + ** for one. + */ + if (House->BScan & (1L << b)) { + int bestval = (std::numeric_limits::max)();; + + const bool is_aircraft = What_Am_I() == RTTI_AIRCRAFT; + + /* + ** Loop through all the buildings and find the one that matches the specification + ** and is willing to dock with this object. + */ + for (void* raw_building : Buildings.ActivePointers) { + BuildingClass* building = reinterpret_cast(raw_building); + + /* + ** Check to see if the building qualifies (preliminary scan). + */ + if (!building->IsInLimbo && + *building == b && + (friendly ? building->House->Is_Ally(this) : building->House == House) && + const_cast(*this).TechnoClass::Transmit_Message(RADIO_CAN_LOAD, building) == RADIO_ROGER) { + + /* + ** If the building qualifies and this building is better than the + ** last qualifying building (as rated by distance), then record + ** this building and keep scanning. + */ + const int distance = is_aircraft ? Distance(building) : Find_Path_AStar(nullptr, Coord_Cell(Center_Coord()), building->Nearby_Location(), 1, MOVE_TEMP, -1); + if (distance && (distance < bestval || building->IsLeader)) { + best = building; + bestval = distance; + } + } + } + } + return best; +} diff --git a/REDALERT/FOOT.H b/REDALERT/FOOT.H index 24e6fd14c..7cceefbee 100644 --- a/REDALERT/FOOT.H +++ b/REDALERT/FOOT.H @@ -347,7 +347,7 @@ class FootClass : public TechnoClass virtual void Fixup_Path(PathType *) {}; virtual void Set_Speed(int speed); virtual MoveType Can_Enter_Cell(CELL cell, FacingType from=FACING_NONE) const; - int Optimize_Moves(PathType *path, MoveType threshhold); + int Optimize_Moves(PathType *path, MoveType threshhold) const; virtual void Override_Mission(MissionType mission, TARGET tarcom, TARGET navcom); virtual bool Restore_Mission(void); CELL Adjust_Dest(CELL cell) const; @@ -361,13 +361,15 @@ class FootClass : public TechnoClass CELL Safety_Point(CELL src, CELL dst, int start, int max); int Rescue_Mission(TARGET tarcom); + virtual BuildingClass* Find_Docking_Bay(StructType b, bool friendly) const override; + private: - int Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold); + int Passable_Cell(CELL cell, FacingType face, int threat, MoveType threshhold) const; 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); + int Find_Path_AStar(PathType* const resultPath, const CELL source, CELL dest, const int maxLen, const MoveType threshhold, const int threat) const; + CELL Find_Passable_Position_Near(const CELL target, const int maxRadius, const MoveType threshhold, const int threat) const; void Debug_Draw_Map(char const * txt, CELL start, CELL dest, bool pause); - void Debug_Draw_Path(PathType *path); + void Debug_Draw_Path(PathType *path) const; bool Follow_Edge(CELL start, CELL target, PathType *path, FacingType search, FacingType olddir, int threat, int threat_stage, int max_cells, MoveType threshhold); bool Register_Cell(PathType *path, CELL cell, FacingType dir, int cost, MoveType threshhold); bool Unravel_Loop(PathType *path, CELL &cell, FacingType &dir, int sx, int sy, int dx, int dy, MoveType threshhold); diff --git a/REDALERT/FUNCTION.H b/REDALERT/FUNCTION.H index 7a14339f8..bcbe27e67 100644 --- a/REDALERT/FUNCTION.H +++ b/REDALERT/FUNCTION.H @@ -1090,7 +1090,8 @@ void On_Achievement_Event(const HouseClass* player_ptr, const char *achievement_ bool CFE_Patch_Is_Wall(const ObjectTypeClass& object); bool CFE_Patch_Can_Have_Rally_Point(const ObjectClass& object); - -void Initialise_CFE_Patch_Config(); +bool CFE_Patch_Should_Extend_Walls(void); +bool CFE_Patch_Unit_Should_Rally(const FootClass& object); +void Initialise_CFE_Patch_Config(void); #endif \ No newline at end of file diff --git a/REDALERT/INI.CPP b/REDALERT/INI.CPP index c195c32b9..1eac60ddd 100644 --- a/REDALERT/INI.CPP +++ b/REDALERT/INI.CPP @@ -1285,3 +1285,26 @@ void INIClass::Strip_Comments(char * buffer) } } } + +const char* INIClass::INISectionIterator::operator*() +{ + return Base::operator->()->Section; +} + +std::pair INIClass::INIEntryIterator::operator*() +{ + const INIEntry* const entry = NodeIterator::operator->(); + return { entry->Entry, entry->Value }; +} + +INIClass::INIEntryIterator INIClass::section_begin(const char* section) const +{ + const INISection* s = Find_Section(section); + return s ? s->EntryList.begin() : INIEntryIterator(); +} + +INIClass::INIEntryIterator INIClass::section_end(const char* section) const +{ + const INISection* s = Find_Section(section); + return s ? s->EntryList.end() : INIEntryIterator(); +} diff --git a/REDALERT/INI.H b/REDALERT/INI.H index 50e04c895..70a6ee555 100644 --- a/REDALERT/INI.H +++ b/REDALERT/INI.H @@ -37,6 +37,7 @@ #define INI_H #include +#include #include "listnode.h" #include "pipe.h" #include "wwfile.h" @@ -45,11 +46,17 @@ #include "crc.h" #include "search.h" +class Straw; + /* ** This is an INI database handler class. It handles a database with a disk format identical ** to the INI files commonly used by Windows. */ class INIClass { + protected: + struct INISection; + struct INIEntry; + public: INIClass(void) {} ~INIClass(void); @@ -109,6 +116,55 @@ class INIClass { bool Put_UUBlock(char const * section, void const * block, int len); bool Put_PKey(PKey const & key); + /* cfehunter 14/06/2020 + ** I needed to be able to iterate sections to reconcile ini files + ** so just some simple iterator to facilitate that + */ + struct INISectionIterator + : public NodeIterator + { + using Base = typename NodeIterator; + using Base::iterator_category; + + INISectionIterator() = default; + INISectionIterator(Base node) + : Base(node) + {} + + //Westwood have global overrides for ++,--,+= and -=. + //This causes name resolution to fail if the operators aren't explicitly brought into derived class scope + using Base::operator++; + using Base::operator--; + + const char* operator->() { return **this; } + const char* operator*(); + }; + + struct INIEntryIterator + : public NodeIterator + { + using Base = typename NodeIterator; + using Base::iterator_category; + + INIEntryIterator() = default; + INIEntryIterator(Base node) + : Base(node) + { } + + //Westwood have global overrides for ++,--,+= and -=. + //This causes name resolution to fail if the operators aren't explicitly brought into derived class scope + using Base::operator++; + using Base::operator--; + + std::pair operator->() { return **this; } + std::pair operator*(); + }; + + INISectionIterator begin() const { return SectionList.begin(); } + INISectionIterator end() const { return SectionList.end(); } + INIEntryIterator section_begin(const char* section) const; + INIEntryIterator section_end(const char* section) const; + protected: enum {MAX_LINE_LENGTH=128}; @@ -135,6 +191,9 @@ class INIClass { INIEntry * Find_Entry(char const * entry) const; int Index_ID(void) const {return(CRCEngine()(Section, strlen(Section)));}; + INIEntryIterator begin() const { return INIEntryIterator(EntryList.First()); } + INIEntryIterator end() const { return INIEntryIterator(); } + char * Section; List EntryList; IndexClassEntryIndex; diff --git a/REDALERT/INIT.CPP b/REDALERT/INIT.CPP index 6f994bb28..4902e31e5 100644 --- a/REDALERT/INIT.CPP +++ b/REDALERT/INIT.CPP @@ -427,14 +427,25 @@ bool Init_Game(int , char * []) */ Options.Load_Settings(); + Initialise_CFE_Patch_Config(); + + if (ActiveCFEPatchConfig.BuildingGapOffset) { + for (int bindex = 0; bindex < BuildingTypes.Count(); ++bindex) { + BuildingTypeClass *building = BuildingTypes.Ptr(bindex); + + building->Adjacent += ActiveCFEPatchConfig.BuildingGapOffset; + if (building->Adjacent < 0) { + building->Adjacent = 0; + } + } + } + /* ** Initialise the color lookup tables for the chronal vortex */ ChronalVortex.Stop(); ChronalVortex.Setup_Remap_Tables(Scen.Theater); - Initialise_CFE_Patch_Config(); - return(true); } diff --git a/REDALERT/LISTNODE.H b/REDALERT/LISTNODE.H index 679f91da8..e74c3f57a 100644 --- a/REDALERT/LISTNODE.H +++ b/REDALERT/LISTNODE.H @@ -38,6 +38,7 @@ #include #include +#include /* ** The "bool" integral type was defined by the C++ comittee in @@ -154,6 +155,50 @@ class Node : public GenericNode { bool Is_Valid(void) const {return(GenericNode::Is_Valid());} }; +/* 14/06/2020 cfehunter +** Adding iterators for lists. They're annoying to work with without them +*/ +template +struct NodeIterator +{ + using iterator_category = std::bidirectional_iterator_tag; + + NodeIterator() + : CurrentNode(nullptr) + {} + + NodeIterator(T* node) + : CurrentNode(node) + {} + + T* operator->() { return CurrentNode; } + + T* operator*() { return CurrentNode; } + + NodeIterator& operator++() + { + CurrentNode = static_cast(CurrentNode->Next()); + return *this; + } + + NodeIterator& operator--() + { + CurrentNode = static_cast(CurrentNode->Prev()); + return *this; + } + + bool operator==(const NodeIterator& rhs) + { + return CurrentNode == rhs.CurrentNode; + } + + bool operator!=(const NodeIterator& rhs) + { + return CurrentNode != rhs.CurrentNode; + } + + T* CurrentNode; +}; /* ** This is an "interface class" for a list of nodes. The rules for the class T object @@ -162,9 +207,18 @@ class Node : public GenericNode { template class List : public GenericList { public: + using Iterator = NodeIterator; + using ConstIterator = NodeIterator; + T * First(void) const {return((T*)GenericList::First());} T * Last(void) const {return((T*)GenericList::Last());} -}; + Iterator begin() { return Iterator(First()); } + Iterator end() { return Iterator(reinterpret_cast(&LastNode)); } + ConstIterator cbegin() const { return ConstIterator(First()); } + ConstIterator cend() const { return ConstIterator(reinterpret_cast(&LastNode)); } + ConstIterator begin() const { return cbegin(); } + ConstIterator end() const { return cend(); } +}; #endif diff --git a/REDALERT/RANGEWRAPPER.H b/REDALERT/RANGEWRAPPER.H new file mode 100644 index 000000000..536426e5f --- /dev/null +++ b/REDALERT/RANGEWRAPPER.H @@ -0,0 +1,22 @@ +#pragma once + +#include + +template +struct RangeWrapper +{ + RangeWrapper(T_ITR&& begin, T_ITR&& end) + : Begin(std::forward(begin)) + , End(std::forward(end)) + { } + + T_ITR begin() const { return Begin; } + T_ITR end() const { return End; } + + T_ITR Begin; + T_ITR End; +}; + + +template +RangeWrapper make_iterator_range(T_ITR&& begin, T_ITR&& end) { return RangeWrapper(std::forward(begin), std::forward(end)); } diff --git a/REDALERT/RedAlert.vcxproj b/REDALERT/RedAlert.vcxproj index 4b5f688cc..83b63ab5b 100644 --- a/REDALERT/RedAlert.vcxproj +++ b/REDALERT/RedAlert.vcxproj @@ -554,6 +554,7 @@ + diff --git a/REDALERT/RedAlert.vcxproj.filters b/REDALERT/RedAlert.vcxproj.filters index 28d50cf2a..a616db797 100644 --- a/REDALERT/RedAlert.vcxproj.filters +++ b/REDALERT/RedAlert.vcxproj.filters @@ -1379,6 +1379,9 @@ Source Files + + Source Files + Source Files