Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Highly Customized] Distribution click action mode #1453

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ This page lists all the individual contributions to the project by their author.
- Fix for sidebar not updating queued unit numbers when on hold
- New Parabola trajectory
- Enhanced Bombard trajectory
- Distribution click action mode
- **Ollerus**
- Build limit group enhancement
- Customizable rocker amplitude
Expand Down
2 changes: 2 additions & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
<ClCompile Include="src\Commands\ToggleDesignatorRange.cpp" />
<ClCompile Include="src\Commands\ToggleDigitalDisplay.cpp" />
<ClCompile Include="src\Commands\SaveVariablesToFile.cpp" />
<ClCompile Include="src\Commands\DistributionMode.cpp" />
<ClCompile Include="src\Ext\Anim\Body.cpp" />
<ClCompile Include="src\Ext\Anim\Hooks.cpp" />
<ClCompile Include="src\Ext\Anim\Hooks.AnimCreateUnit.cpp" />
Expand Down Expand Up @@ -197,6 +198,7 @@
<ClInclude Include="src\Commands\ToggleDesignatorRange.h" />
<ClInclude Include="src\Commands\ToggleDigitalDisplay.h" />
<ClInclude Include="src\Commands\SaveVariablesToFile.h" />
<ClInclude Include="src\Commands\DistributionMode.h" />
<ClInclude Include="src\Ext\Bullet\Trajectories\BombardTrajectory.h" />
<ClInclude Include="src\Ext\Bullet\Trajectories\PhobosTrajectory.h" />
<ClInclude Include="src\Ext\Bullet\Trajectories\StraightTrajectory.h" />
Expand Down
17 changes: 17 additions & 0 deletions docs/User-Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,23 @@ ShowFlashOnSelecting=false ; boolean
- Switches on/off [frame by frame mode](Miscellanous.md#frame-step-in).
- For localization add `TXT_FRAME_BY_FRAME` and `TXT_FRAME_BY_FRAME_DESC` into your `.csf` file.

### `[ ]` Distribution Mode Spread / Filter / Enable

- Change the click action when hold down the specific hotkey if enabled `AllowDistributionCommand`.
- When the range is 0, it is the original default behavior of the game. The range can be adjusted to 4, 8 or 16 cells by shortcut keys.
- The targets within the range will be allocated equally to the selected technos. Only when the behavior to be performed by the current techno is the same as that displayed by the mouse will it be allocated. Otherwise, it will return to the original default behavior of the game (it will not be effective for technos in the air). This will display a range ring.
- When the filter is `None`, it is the default behavior of the game. If the range is not zero at this time, a green ring will be displayed. You can adjust the filter mode to:
- `Auto` - if the behavior to be executed by the current techno is different from the behavior displayed by the mouse, and the behavior to be executed will make the techno move near the target, the behavior will be replaced with area guard. At this time, a blue ring will be displayed.
- `Type` - on the basis of `Auto`, only targets of the same type (like infantries, vehicles or buildings) will be selected among the targets allocated in the range. At this time, a yellow ring will be displayed.
- `Name` - on the basis of `Type`, only targets of the same name (or with the same `GroupAs`) will be selected among the targets allocated in the range. At this time, a red ring will be displayed.
- For localization add `TXT_DISTR_SPREAD`, `TXT_DISTR_FILTER`, `TXT_DISTR_HOLDDOWN`, `TXT_DISTR_SPREAD_DESC`, `TXT_DISTR_FILTER_DESC` and `TXT_DISTR_HOLDDOWN_DESC` into your `.csf` file.

In `rulesmd.ini`:
```ini
[GlobalControls]
AllowDistributionCommand=false ; boolean
```

## Loading screen

- PCX files can now be used as loadscreen images.
Expand Down
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ New:
- Enhanced Bombard trajectory (by CrimRecya & Ollerus, based on knowledge of NaotoYuuki)
- Toggle waypoint for building (by TaranDahl)
- Bunkerable checks dehardcode (by TaranDahl)
- Distribution click action mode (by CrimRecya)

Vanilla fixes:
- Aircraft will now behave as expected according to it's `MovementZone` and `SpeedType` when moving onto different surfaces. In particular, this fixes erratic behavior when vanilla aircraft is ordered to move onto water surface and instead the movement order changes to a shore nearby (by CrimRecya)
Expand Down
8 changes: 8 additions & 0 deletions src/Commands/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "ToggleDigitalDisplay.h"
#include "ToggleDesignatorRange.h"
#include "SaveVariablesToFile.h"
#include "DistributionMode.h"

DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6)
{
Expand All @@ -20,6 +21,13 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6)
MakeCommand<ToggleDigitalDisplayCommandClass>();
MakeCommand<ToggleDesignatorRangeCommandClass>();

if (Phobos::Config::AllowDistributionCommand)
{
MakeCommand<DistributionMode1CommandClass>();
MakeCommand<DistributionMode2CommandClass>();
MakeCommand<DistributionMode3CommandClass>();
}

if (Phobos::Config::DevelopmentCommands)
{
MakeCommand<DamageDisplayCommandClass>();
Expand Down
237 changes: 237 additions & 0 deletions src/Commands/DistributionMode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
#include "DistributionMode.h"

#include <Ext/TechnoType/Body.h>
#include <Utilities/Helpers.Alex.h>
#include <Helpers/Macro.h>

#include <HouseClass.h>

int DistributionMode1CommandClass::Mode = 0;
int DistributionMode2CommandClass::Mode = 0;
bool DistributionMode3CommandClass::Enabled = false;
int DistributionMode3CommandClass::ShowTime = 0;

const char* DistributionMode1CommandClass::GetName() const
{
return "Distribution Mode Spread";
}

const wchar_t* DistributionMode1CommandClass::GetUIName() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_SPREAD", L"Distribution spread");
}

const wchar_t* DistributionMode1CommandClass::GetUICategory() const
{
return CATEGORY_CONTROL;
}

const wchar_t* DistributionMode1CommandClass::GetUIDescription() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_SPREAD_DESC", L"Automatically and averagely select similar targets around the original target. This is for changing the search range");
}

void DistributionMode1CommandClass::Execute(WWKey eInput) const
{
DistributionMode1CommandClass::Mode = ((DistributionMode1CommandClass::Mode + 1) & 3);
DistributionMode3CommandClass::ShowTime = SystemTimer::GetTime();
}

const char* DistributionMode2CommandClass::GetName() const
{
return "Distribution Mode Filter";
}

const wchar_t* DistributionMode2CommandClass::GetUIName() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_FILTER", L"Distribution filter");
}

const wchar_t* DistributionMode2CommandClass::GetUICategory() const
{
return CATEGORY_CONTROL;
}

const wchar_t* DistributionMode2CommandClass::GetUIDescription() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_FILTER_DESC", L"Automatically and averagely select similar targets around the original target. This is for changing the filter criteria");
}

void DistributionMode2CommandClass::Execute(WWKey eInput) const
{
DistributionMode2CommandClass::Mode = ((DistributionMode2CommandClass::Mode + 1) & 3);
DistributionMode3CommandClass::ShowTime = SystemTimer::GetTime();
}

const char* DistributionMode3CommandClass::GetName() const
{
return "Distribution Mode Hold Down";
}

const wchar_t* DistributionMode3CommandClass::GetUIName() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_HOLDDOWN", L"Distribution hold down");
}

const wchar_t* DistributionMode3CommandClass::GetUICategory() const
{
return CATEGORY_CONTROL;
}

const wchar_t* DistributionMode3CommandClass::GetUIDescription() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_DISTR_HOLDDOWN_DESC", L"Automatically and averagely select similar targets around the original target. This is for holding down to toggle on/off");
}

bool DistributionMode3CommandClass::ExtraTriggerCondition(WWKey eInput) const
{
return true;
}

void DistributionMode3CommandClass::Execute(WWKey eInput) const
{
if (eInput & WWKey::Release)
DistributionMode3CommandClass::Enabled = false;
else
DistributionMode3CommandClass::Enabled = true;
}

DEFINE_HOOK(0x4AE818, DisplayClass_sub_4AE750_AutoDistribution, 0xA)
{
enum { SkipGameCode = 0x4AE85C };

GET(ObjectClass* const, pTarget, EBP);
GET_STACK(const Action, mouseAction, STACK_OFFSET(0x20, 0xC));

const auto count = ObjectClass::CurrentObjects->Count;

if (count > 0)
{
const auto mode1 = DistributionMode1CommandClass::Mode;
const auto mode2 = DistributionMode2CommandClass::Mode;

// Distribution mode main
if (DistributionMode3CommandClass::Enabled && mode1 && count > 1 && mouseAction != Action::NoMove && !PlanningNodeClass::PlanningModeActive
&& (pTarget->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None && !pTarget->IsInAir())
{
const auto pSpecial = HouseClass::FindSpecial();
const auto pCivilian = HouseClass::FindCivilianSide();
const auto pNeutral = HouseClass::FindNeutral();

const auto pTargetHouse = static_cast<TechnoClass*>(pTarget)->Owner;
const bool targetIsNeutral = pTargetHouse == pSpecial || pTargetHouse == pCivilian || pTargetHouse == pNeutral;

const auto range = (2 << mode1);
const auto pItems = Helpers::Alex::getCellSpreadItems(pTarget->Location, range);
std::map<TechnoClass*, int> record;
record[static_cast<TechnoClass*>(pTarget)] = 0;
int current = 1;

for (const auto& pItem : pItems)
{
if ((pItem->CloakState != CloakState::Cloaked || pItem->GetCell()->Sensors_InclHouse(HouseClass::CurrentPlayer->ArrayIndex))
&& !pItem->IsDisguisedAs(HouseClass::CurrentPlayer))
{
auto coords = pItem->GetCoords();

if (!MapClass::Instance->IsWithinUsableArea(coords))
continue;

coords.Z = MapClass::Instance->GetCellFloorHeight(coords);

if (MapClass::Instance->GetCellAt(coords)->ContainsBridge())
coords.Z += CellClass::BridgeHeight;

if (!MapClass::Instance->IsLocationShrouded(coords))
record[pItem] = 0;
}
}

for (const auto& pSelect : ObjectClass::CurrentObjects())
{
TechnoClass* pCanTarget = nullptr;
TechnoClass* pNewTarget = nullptr;

for (const auto& [pItem, num] : record)
{
if (pSelect->MouseOverObject(pItem) == mouseAction && (targetIsNeutral || (pItem->Owner != pSpecial && pItem->Owner != pCivilian && pItem->Owner != pNeutral))
&& (mode2 < 2 || (pItem->WhatAmI() == pTarget->WhatAmI()
&& (mode2 < 3 || TechnoTypeExt::GetSelectionGroupID(pItem->GetTechnoType()) == TechnoTypeExt::GetSelectionGroupID(pTarget->GetTechnoType())))))
{
pCanTarget = pItem;

if (num < current)
{
pNewTarget = pCanTarget;
break;
}
}
}

if (!pNewTarget)
{
if (pCanTarget)
{
++current;
pNewTarget = pCanTarget;
}
}

if (pNewTarget)
{
if (record.contains(pNewTarget))
++record[pNewTarget];

pSelect->ObjectClickedAction(mouseAction, pNewTarget, false);
}
else
{
const auto currentAction = pSelect->MouseOverObject(pTarget);

if (mode2 && currentAction == Action::NoMove && (pSelect->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None)
static_cast<TechnoClass*>(pSelect)->ClickedMission(Mission::Area_Guard, reinterpret_cast<ObjectClass*>(pSelect->GetCellAgain()), nullptr, nullptr);
else
pSelect->ObjectClickedAction(currentAction, pTarget, false);
}

Unsorted::MoveFeedback = false;
}
}
else // Vanilla
{
for (const auto& pSelect : ObjectClass::CurrentObjects())
{
const auto currentAction = pSelect->MouseOverObject(pTarget);

if (mode2 && mouseAction != Action::NoMove && currentAction == Action::NoMove && (pSelect->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None)
static_cast<TechnoClass*>(pSelect)->ClickedMission(Mission::Area_Guard, reinterpret_cast<ObjectClass*>(pSelect->GetCellAgain()), nullptr, nullptr);
else
pSelect->ObjectClickedAction(currentAction, pTarget, false);

Unsorted::MoveFeedback = false;
}
}
}

return SkipGameCode;
}

DEFINE_HOOK(0x6DBE74, TacticalClass_DrawAllRadialIndicators_DrawDistributionRange, 0x7)
{
if (!DistributionMode3CommandClass::Enabled && SystemTimer::GetTime() - DistributionMode3CommandClass::ShowTime > 30)
return 0;

const auto mode1 = DistributionMode1CommandClass::Mode;
const auto mode2 = DistributionMode2CommandClass::Mode;

if (mode1 || mode2)
{
const auto pCell = MapClass::Instance->GetCellAt(DisplayClass::Instance->CurrentFoundation_CenterCell);
const auto color = ((mode2 > 1)
? ((mode2 == 3) ? ColorStruct { 255, 0, 0 } : ColorStruct { 200, 200, 0 })
: (mode2 == 1) ? ColorStruct { 0, 100, 255 } : ColorStruct { 0, 255, 50 });
Game::DrawRadialIndicator(false, true, pCell->GetCoords(), color, static_cast<float>(mode1 ? (2 << mode1) : 0.5), false, true);
}

return 0;
}
41 changes: 41 additions & 0 deletions src/Commands/DistributionMode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#pragma once

#include "Commands.h"

class DistributionMode1CommandClass : public CommandClass
{
public:
static int Mode;

virtual const char* GetName() const override;
virtual const wchar_t* GetUIName() const override;
virtual const wchar_t* GetUICategory() const override;
virtual const wchar_t* GetUIDescription() const override;
virtual void Execute(WWKey eInput) const override;
};

class DistributionMode2CommandClass : public CommandClass
{
public:
static int Mode;

virtual const char* GetName() const override;
virtual const wchar_t* GetUIName() const override;
virtual const wchar_t* GetUICategory() const override;
virtual const wchar_t* GetUIDescription() const override;
virtual void Execute(WWKey eInput) const override;
};

class DistributionMode3CommandClass : public CommandClass
{
public:
static bool Enabled;
static int ShowTime;

virtual const char* GetName() const override;
virtual const wchar_t* GetUIName() const override;
virtual const wchar_t* GetUICategory() const override;
virtual const wchar_t* GetUIDescription() const override;
virtual bool ExtraTriggerCondition(WWKey eInput) const override;
virtual void Execute(WWKey eInput) const override;
};
3 changes: 3 additions & 0 deletions src/Phobos.INI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ bool Phobos::Config::ShowWeedsCounter = false;
bool Phobos::Config::HideLightFlashEffects = true;
bool Phobos::Config::ShowFlashOnSelecting = false;
bool Phobos::Config::UnitPowerDrain = false;
bool Phobos::Config::AllowDistributionCommand = false;

bool Phobos::Misc::CustomGS = false;
int Phobos::Misc::CustomGS_ChangeInterval[7] = { -1, -1, -1, -1, -1, -1, -1 };
Expand Down Expand Up @@ -222,6 +223,8 @@ DEFINE_HOOK(0x52D21F, InitRules_ThingsThatShouldntBeSerailized, 0x6)
#endif
Phobos::Config::ShowPlanningPath = pINI_RULESMD->ReadBool("GlobalControls", "DebugPlanningPaths", Phobos::Config::ShowPlanningPath);

Phobos::Config::AllowDistributionCommand = pINI_RULESMD->ReadBool("GlobalControls", "AllowDistributionCommand", Phobos::Config::AllowDistributionCommand);

return 0;
}

Expand Down
1 change: 1 addition & 0 deletions src/Phobos.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ class Phobos
static bool HideLightFlashEffects;
static bool ShowFlashOnSelecting;
static bool UnitPowerDrain;
static bool AllowDistributionCommand;
};

class Misc
Expand Down