diff --git a/CMakeSettings.json b/CMakeSettings.json new file mode 100644 index 000000000..a3f08f761 --- /dev/null +++ b/CMakeSettings.json @@ -0,0 +1,15 @@ +{ + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x86" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "", + "buildCommandArgs": "", + "ctestCommandArgs": "" + } + ] +} \ No newline at end of file diff --git a/src/extensions/command/commandext.cpp b/src/extensions/command/commandext.cpp index 0f4f6ce3e..b62c981fb 100644 --- a/src/extensions/command/commandext.cpp +++ b/src/extensions/command/commandext.cpp @@ -73,7 +73,7 @@ #include "miscutil.h" #include "debughandler.h" #include "asserthandler.h" - +#include "event.h" /** * Handy defines for handling any adjustments. @@ -1213,6 +1213,387 @@ bool CaptureObjectCommandClass::Process() } +/** + * Force selected units to hold position. + * + * @author: hacklex + */ +const char* HoldPositionCommandClass::Get_Name() const +{ + return "HoldPosition"; +} + +const char* HoldPositionCommandClass::Get_UI_Name() const +{ + return "Hold Position"; +} + +const char* HoldPositionCommandClass::Get_Category() const +{ + return "Control"; +} + +const char* HoldPositionCommandClass::Get_Description() const +{ + return "Force unit to stop and only attack those already in range"; +} + +bool HoldPositionCommandClass::Process() +{ + bool areAllAlreadySticky = true; + + for (int i = 0; i < CurrentObjects.Count(); ++i) { + ObjectClass* object = CurrentObjects[i]; + if (!object || !object->Is_Techno()) { + continue; + } + if (object->Owning_House() == PlayerPtr && object->Can_Player_Move()) { + TechnoClass* techno = dynamic_cast(object); + if (techno->Mission != MISSION_STICKY) { + areAllAlreadySticky = false; + break; + } + } + } + + MissionType targetMissionType = areAllAlreadySticky ? MISSION_STOP : MISSION_STICKY; + + /** + * Iterate over all currently selected objects force them to hold position + */ + for (int i = 0; i < CurrentObjects.Count(); ++i) { + ObjectClass* object = CurrentObjects[i]; + if (!object || !object->Is_Techno()) { + continue; + } + if (object->Owning_House() == PlayerPtr && object->Can_Player_Move()) { + UnitClass* unit = dynamic_cast(object); + bool skipThis = unit == NULL || unit->Class->DeploysInto != NULL; + if (!skipThis) { + //printf current mission + printf("HOLD: Current mission: %d\n", unit->Mission); + printf("HOLD: Target mission: %d\n", targetMissionType); + // if we don't skip deployable units like this, MCVs will be rendered unusable. + auto currentUnitCell = unit->Get_Cell(); + + OutList.Add(EventClass(unit->Owning_House()->ID_Number(), unit, targetMissionType, TARGET_NONE, currentUnitCell)); + auto myEvent = EventClass(unit->Owning_House()->ID_Number(), unit, targetMissionType, TARGET_NONE, currentUnitCell); + myEvent.Execute(); + //OutList.Add(EventClass((unsigned long)(unit->Owning_House()->ID_Number()), (TargetClass)object, targetMissionType)); + //unit->Mission = MISSION_SLEEP; + + //OutList.Add(EventClass(unit->Owning_House()->ID, object, targetMissionType, TARGET_NONE, TARGET_NONE)); + //unit->Assign_Mission(targetMissionType); + printf("HOLD: After hold mission: %d\n", unit->Mission); + + if (targetMissionType == MISSION_STICKY) { + unit->Response_Select(); + } + else { + unit->Response_Move(); + } + } + } + } + + Map.Recalc(); + + return true; +} + +int ClassifyVeterancy(TechnoClass* techno) +{ + if (techno->Veterancy.Is_Elite()) { + return 0; + } + else if (techno->Veterancy.Is_Veteran()) { + return 1; + } + else { + return 2; + } +} + +/** + * Promote selected units to higher veterancy level + * + * @author: hacklex + */ +const char* VeterancyPromoteCommandClass::Get_Name() const +{ + return "PromoteSelected"; +} + +const char* VeterancyPromoteCommandClass::Get_UI_Name() const +{ + return "Promote Selected"; +} + +const char* VeterancyPromoteCommandClass::Get_Category() const +{ + return CATEGORY_DEVELOPER; +} + +const char* VeterancyPromoteCommandClass::Get_Description() const +{ + return "Promote selected units to higher veterancy level"; +} + +bool VeterancyPromoteCommandClass::Process() +{ + if (!Session.Singleplayer_Game()) { + return false; + } + + /** + * Iterate over all currently selected objects force them to hold position + */ + for (int i = 0; i < CurrentObjects.Count(); ++i) { + ObjectClass* object = CurrentObjects[i]; + if (!object || !object->Is_Techno()) { + continue; + } + if (object->Owning_House() == PlayerPtr) { + TechnoClass* techno = dynamic_cast(object); + if(ClassifyVeterancy(techno) == 2) { + techno->Veterancy.Set_Veteran(true); + } + else if(ClassifyVeterancy(techno) == 1) { + techno->Veterancy.Set_Elite(true); + } + } + } + + Map.Recalc(); + + return true; +} + +bool SetEquals(DynamicVectorClass& a, DynamicVectorClass& b) +{ + if (a.Count() != b.Count()) { + return false; + } + for (int i = 0; i < a.Count(); ++i) { + if (!a.Is_Present(b[i])) { + return false; + } + } + return true; +} + +bool EqualsUnionOfTwoOtherSets(DynamicVectorClass& current, DynamicVectorClass& a, DynamicVectorClass& b) +{ + if (current.Count() != a.Count() + b.Count()) { + return false; + } + for (int i = 0; i < current.Count(); ++i) { + if (!a.Is_Present(current[i]) && !b.Is_Present(current[i])) { + return false; + } + } + return true; +} + +bool EqualsUnionOfThreeOtherSets(DynamicVectorClass& current, DynamicVectorClass& a, DynamicVectorClass& b, DynamicVectorClass& c) +{ + if (current.Count() != a.Count() + b.Count() + c.Count()) { + return false; + } + for (int i = 0; i < current.Count(); ++i) { + if (!a.Is_Present(current[i]) && !b.Is_Present(current[i]) && !c.Is_Present(current[i])) { + return false; + } + } + return true; +} + +int GetThirdVeterancy(int first, int second) +{ + if (first == 0 && second == 1) { + return 2; + } + if (first == 0 && second == 2) { + return 1; + } + if (first == 1 && second == 2) { + return 0; + } + return -1; +} + +bool ProcessVeterancyFilter(bool isShiftPressed) +{ + if (!Session.Singleplayer_Game()) { + return false; + } + + static DynamicVectorClass lastSelection[3]; + static DynamicVectorClass lastFullSelection; + bool isHeterogenous = false; + DynamicVectorClass currentSelection[3]; + + currentSelection[0].Clear(); + currentSelection[1].Clear(); + currentSelection[2].Clear(); + + DynamicVectorClass currentTechnos; + + int highestSelectedVeterancy = 3; + int lowestSelectedVeterancy = -1; + + for (int i = 0; i < CurrentObjects.Count(); ++i) { + ObjectClass* object = CurrentObjects[i]; + if (!object || object->Owning_House() != PlayerPtr || !object->Is_Techno()) { + return true; + } + TechnoClass* techno = dynamic_cast(object); + currentTechnos.Add(techno); + int veterancy = ClassifyVeterancy(techno); + if (veterancy < highestSelectedVeterancy) { + highestSelectedVeterancy = veterancy; + } + if (veterancy > lowestSelectedVeterancy) { + lowestSelectedVeterancy = veterancy; + } + if (veterancy >= 0 && veterancy < 3) + currentSelection[veterancy].Add(techno); + } + + if (!SetEquals(currentTechnos, lastFullSelection) && + !SetEquals(currentTechnos, lastSelection[0]) && + !SetEquals(currentTechnos, lastSelection[1]) && + !SetEquals(currentTechnos, lastSelection[2]) && + !EqualsUnionOfTwoOtherSets(currentTechnos, lastSelection[0], lastSelection[1]) && + !EqualsUnionOfTwoOtherSets(currentTechnos, lastSelection[0], lastSelection[2]) && + !EqualsUnionOfTwoOtherSets(currentTechnos, lastSelection[1], lastSelection[2])) { + if (isShiftPressed || highestSelectedVeterancy == lowestSelectedVeterancy) { + return true; // can't add anything if we haven't filtered yet or the new selection is already of same rank + } + lastFullSelection.Clear(); + lastSelection[0].Clear(); + lastSelection[1].Clear(); + lastSelection[2].Clear(); + for (int i = 0; i < currentTechnos.Count(); ++i) { + lastFullSelection.Add(currentTechnos[i]); + lastSelection[ClassifyVeterancy(currentTechnos[i])].Add(currentTechnos[i]); + } + for (int i = highestSelectedVeterancy+1; i < 3; ++i) { + for (int k = 0; k < currentSelection[i].Count(); ++k) { + currentSelection[i][k]->Unselect(); + } + } + if (highestSelectedVeterancy >= 0 && highestSelectedVeterancy < 3) { + for (int k = 0; k < currentSelection[highestSelectedVeterancy].Count(); ++k) { + currentSelection[highestSelectedVeterancy][k]->Response_Select(); + } + } + } + else { + int nextTierVeterancy = lowestSelectedVeterancy; + int loopBreaker = 3; + if (highestSelectedVeterancy != lowestSelectedVeterancy) { + if (SetEquals(currentTechnos, lastFullSelection)) { + nextTierVeterancy = highestSelectedVeterancy; + } + else { + nextTierVeterancy = GetThirdVeterancy(highestSelectedVeterancy, lowestSelectedVeterancy); + } + } + else { + do { + loopBreaker--; + nextTierVeterancy = (nextTierVeterancy + 1) % 3; + } while (lastSelection[nextTierVeterancy].Count() == 0 && loopBreaker > 0); + } + if (nextTierVeterancy == -1) { + if (isShiftPressed) { + return true; + } + else { + nextTierVeterancy = highestSelectedVeterancy; + } + } + if (nextTierVeterancy >= 0 && nextTierVeterancy < 3 && lastSelection[nextTierVeterancy].Count() > 0) { + if (!isShiftPressed) { + for (int i = 0; i < currentTechnos.Count(); ++i) { + currentTechnos[i]->Unselect(); + } + } + for (int i = 0; i < lastSelection[nextTierVeterancy].Count(); ++i) { + lastSelection[nextTierVeterancy][i]->Select(); + } + } + } + + Map.Recalc(); + + return true; +} + + +/** + * Cycle through elite/veteran/green units among the last heterogenous selection. + * + * @author: hacklex + */ +const char* VeterancyFilterCommandClass::Get_Name() const +{ + return "VeterancyFilter"; +} + +const char* VeterancyFilterCommandClass::Get_UI_Name() const +{ + return "Veterancy Filter"; +} + +const char* VeterancyFilterCommandClass::Get_Category() const +{ + return "Selection"; +} + +const char* VeterancyFilterCommandClass::Get_Description() const +{ + return "Filter out elites/veterans/green units among the last mixed selection"; +} + +bool VeterancyFilterCommandClass::Process() +{ + return ProcessVeterancyFilter(false); +} + + +/** + * Cycle through elite/veteran/green units among the last heterogenous selection. + * + * @author: hacklex + */ +const char* VeterancyFilterAddLowerCommandClass::Get_Name() const +{ + return "VeterancyFilterAddLower"; +} + +const char* VeterancyFilterAddLowerCommandClass::Get_UI_Name() const +{ + return "Veterancy Filter/Add Lower"; +} + +const char* VeterancyFilterAddLowerCommandClass::Get_Category() const +{ + return "Selection"; +} + +const char* VeterancyFilterAddLowerCommandClass::Get_Description() const +{ + return "Add units of lower veterancy to the already filtered subset"; +} + +bool VeterancyFilterAddLowerCommandClass::Process() +{ + return ProcessVeterancyFilter(true); +} + + /** * Grants all available special weapons to the player. * diff --git a/src/extensions/command/commandext.h b/src/extensions/command/commandext.h index aab6dc4f4..dfc07f4ef 100644 --- a/src/extensions/command/commandext.h +++ b/src/extensions/command/commandext.h @@ -462,6 +462,76 @@ class CaptureObjectCommandClass : public ViniferaCommandClass }; +/** + * Forces selected units to hold position. + */ +class HoldPositionCommandClass : public ViniferaCommandClass +{ + public: + HoldPositionCommandClass() : ViniferaCommandClass() { IsDeveloper = false; } + virtual ~HoldPositionCommandClass() {} + + virtual const char *Get_Name() const override; + virtual const char *Get_UI_Name() const override; + virtual const char *Get_Category() const override; + virtual const char *Get_Description() const override; + virtual bool Process() override; + + virtual KeyNumType Default_Key() const override { return KeyNumType(KN_CTRL_BIT | KN_S); } +}; + +class VeterancyPromoteCommandClass : public ViniferaCommandClass +{ + public: + VeterancyPromoteCommandClass() : ViniferaCommandClass() { IsDeveloper = false; } + virtual ~VeterancyPromoteCommandClass() {} + + virtual const char *Get_Name() const override; + virtual const char *Get_UI_Name() const override; + virtual const char *Get_Category() const override; + virtual const char *Get_Description() const override; + virtual bool Process() override; + virtual KeyNumType Default_Key() const override { return KeyNumType(KN_CTRL_BIT | KN_Y); } +}; + +/** + * Cycles through green/veteran/elite units among the initially selected group + */ +class VeterancyFilterCommandClass : public ViniferaCommandClass +{ + public: + VeterancyFilterCommandClass() : ViniferaCommandClass() { IsDeveloper = false; } + virtual ~VeterancyFilterCommandClass() {} + + virtual const char *Get_Name() const override; + virtual const char *Get_UI_Name() const override; + virtual const char *Get_Category() const override; + virtual const char *Get_Description() const override; + virtual bool Process() override; + + virtual KeyNumType Default_Key() const override { return KeyNumType(KN_Y); } +}; + + +/** + * Adds lower-ranked units to already filtered veterans + */ +class VeterancyFilterAddLowerCommandClass : public ViniferaCommandClass +{ + public: + VeterancyFilterAddLowerCommandClass() : ViniferaCommandClass() { IsDeveloper = false; } + virtual ~VeterancyFilterAddLowerCommandClass() {} + + virtual const char *Get_Name() const override; + virtual const char *Get_UI_Name() const override; + virtual const char *Get_Category() const override; + virtual const char *Get_Description() const override; + virtual bool Process() override; + + virtual KeyNumType Default_Key() const override { return KeyNumType(KN_Y | KN_SHIFT_BIT); } +}; + + /** * Grants all available special weapons to the player. */ diff --git a/src/extensions/command/commandext_hooks.cpp b/src/extensions/command/commandext_hooks.cpp index 2703d806e..a4fb21ca5 100644 --- a/src/extensions/command/commandext_hooks.cpp +++ b/src/extensions/command/commandext_hooks.cpp @@ -246,6 +246,18 @@ void Init_Vinifera_Commands() cmdptr = new ToggleSuperTimersCommandClass; Commands.Add(cmdptr); + cmdptr = new HoldPositionCommandClass; + Commands.Add(cmdptr); + + cmdptr = new VeterancyPromoteCommandClass; + Commands.Add(cmdptr); + + cmdptr = new VeterancyFilterCommandClass; + Commands.Add(cmdptr); + + cmdptr = new VeterancyFilterAddLowerCommandClass; + Commands.Add(cmdptr); + /** * Next, initialised any new commands here if the developer mode is enabled. */ diff --git a/src/hooker/launcher.cpp.in b/src/hooker/launcher.cpp.in index 16c51322b..75b486d67 100644 --- a/src/hooker/launcher.cpp.in +++ b/src/hooker/launcher.cpp.in @@ -61,7 +61,7 @@ const bool CheckTargetBinaries = @CHECK_TARGET_BINARIES@; */ const char *BinaryHash[] = { "@TARGET_HASH@", - "@TARGET_FTS_HASH@" + "@TARGET_FTS_HASH@", "@TARGET_TUC_HASH@" }; @@ -70,7 +70,7 @@ const char *BinaryHash[] = { */ int BinarySize[] = { @TARGET_SIZE@, - @TARGET_FTS_SIZE@ + @TARGET_FTS_SIZE@, @TARGET_TUC_SIZE@ }; diff --git a/src/vinifera/vinifera_globals.cpp b/src/vinifera/vinifera_globals.cpp index 05a2c45ea..83522d837 100644 --- a/src/vinifera/vinifera_globals.cpp +++ b/src/vinifera/vinifera_globals.cpp @@ -58,8 +58,8 @@ bool Vinifera_Developer_FrameStep = false; int Vinifera_Developer_FrameStepCount = 0; bool Vinifera_Developer_AIControl = false; -bool Vinifera_SkipLogoMovies = false; -bool Vinifera_SkipStartupMovies = false; +bool Vinifera_SkipLogoMovies = true; +bool Vinifera_SkipStartupMovies = true; bool Vinifera_NoTacticalVersionString = false;