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 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,7 @@ This page lists all the individual contributions to the project by their author.
- Allow to change the speed of gas particles
- **CrimRecya**
- Fix `LimboKill` not working reliably
- 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 @@ -196,6 +197,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
11 changes: 11 additions & 0 deletions docs/User-Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,17 @@ SelectionFlashDuration=0 ; integer, number of frames
- Switches on/off [frame by frame mode](Miscellanous.html#frame-step-in).
- For localization add `TXT_FRAME_BY_FRAME` and `TXT_FRAME_BY_FRAME_DESC` into your `.csf` file.

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

- Change the click action.
- 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)
- When the filter is `None`, it is the default behavior of the game. 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.
- `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.
- `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.
- For localization add `TXT_DISTR_SPREAD`, `TXT_DISTR_FILTER`, `TXT_DISTR_SPREAD_DESC` and `TXT_DISTR_FILTER_DESC` into your `.csf` file.

## 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 @@ -469,6 +469,7 @@ New:
- Allow infantry to use land sequences in water (by Starkku)
- `<Player @ X>` can now be used as owner for pre-placed objects on skirmish and multiplayer maps (by Starkku)
- Allow customizing charge turret delays per burst on a weapon (by Starkku)
- Distribution click action mode (by CrimRecya)
- Unit `Speed` setting now accepts floating point values (by Starkku)

Vanilla fixes:
Expand Down
3 changes: 3 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 @@ -19,6 +20,8 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6)
MakeCommand<QuickSaveCommandClass>();
MakeCommand<ToggleDigitalDisplayCommandClass>();
MakeCommand<ToggleDesignatorRangeCommandClass>();
MakeCommand<DistributionMode1CommandClass>();
MakeCommand<DistributionMode2CommandClass>();

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

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

#include <HouseClass.h>
#include <WWMouseClass.h>

int DistributionMode1CommandClass::Mode = 0;
int DistributionMode2CommandClass::Mode = 0;

void __fastcall PrintDistributionModeMessage()
{
wchar_t text[0x40];
swprintf_s(text, L"Distribution Mode < Spread: r=%2d , Filter: %s >",
(DistributionMode1CommandClass::Mode ? (2 << DistributionMode1CommandClass::Mode) : 0),
((DistributionMode2CommandClass::Mode > 1) ? ((DistributionMode2CommandClass::Mode == 3) ? L"Name" : L"Type") : (DistributionMode2CommandClass::Mode == 1) ? L"Auto" : L"None")
);
MessageListClass::Instance->PrintMessage(text, 200, 5, true);
}

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);
PrintDistributionModeMessage();
}

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);
PrintDistributionModeMessage();
}

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 (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;
int current = 1;

for (const auto& pItem : pItems)
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(0x4F4596, GScreenClass_DrawOnTop_DrawDistributionModeShape, 0x7)
{
const auto mode1 = DistributionMode1CommandClass::Mode;
const auto mode2 = DistributionMode2CommandClass::Mode;

if (mode1 || mode2)
{
auto position = WWMouseClass::Instance->XY1;
RectangleStruct rect = DSurface::Composite->GetRect();
rect.Height -= 32;

if (position.X < rect.Width && position.Y < rect.Height)
{
position.Y += 20;
const auto mode2Text = ((mode2 > 1) ? ((mode2 == 3) ? L"Name" : L"Type") : (mode2 == 1) ? L"Auto" : L"None");
wchar_t text[0x20];
swprintf_s(text, L"%s:%2d", mode2Text, (mode1 ? (2 << mode1) : 0));
RectangleStruct dim = Drawing::GetTextDimensions(text, Point2D::Empty, 0);
dim.Width += 4;
const int dX = rect.Width - dim.Width;
const int dY = rect.Height - dim.Height;

if (position.X >= dX)
position.X = dX;

if (position.Y >= dY)
position.Y = dY;

dim.X = position.X;
dim.Y = position.Y;
ColorStruct color { 0, 0, 0 };
DSurface::Composite->FillRectTrans(&dim, &color, 40);
position.X += 2;
DSurface::Composite->DrawTextA(text, &rect, &position, COLOR_WHITE, COLOR_BLACK, TextPrintType::LightShadow);
}
}

return 0;
}
*/
27 changes: 27 additions & 0 deletions src/Commands/DistributionMode.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#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;
};
Loading