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

[New Feature] Autoload Command Shortcut #1452

Open
wants to merge 66 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
0e604e4
max variety autoload and recursive autoload
psi-cmd Dec 13, 2024
7bc526b
Implement autoload hotkey
psi-cmd Dec 14, 2024
831097e
Implement Autoload Shortcut
psi-cmd Dec 14, 2024
ea03a24
ares tags support
Aephiex Dec 14, 2024
1fba9d4
smarter passenger and vehicle distinction by size
Aephiex Dec 14, 2024
7b5dfad
fix not working
Aephiex Dec 14, 2024
142e751
remove redundant check
Aephiex Dec 14, 2024
bde01c4
comments
Aephiex Dec 14, 2024
caa7a13
mind control support
Aephiex Dec 14, 2024
8f3a38a
mind control support part 2
Aephiex Dec 14, 2024
817147c
prevent CaptureManager null pointer
Aephiex Dec 14, 2024
2e143f0
Merge pull request #1 from Aephiex/autoload
psi-cmd Dec 14, 2024
91f51d2
fix nullptr case not tested
psi-cmd Dec 14, 2024
d57a878
merge with latest develop
psi-cmd Dec 14, 2024
873ff7c
bracket style adjustment
psi-cmd Dec 14, 2024
c17d0b3
Fix transport remaining size calculation
Aephiex Dec 15, 2024
3a6163e
fix comments misleading
Aephiex Dec 15, 2024
01a0c42
Merge pull request #2 from Aephiex/patch-1
psi-cmd Dec 15, 2024
c17f008
code style repair
psi-cmd Dec 15, 2024
6f93c6f
merge fix
psi-cmd Dec 15, 2024
32d0cd7
Add support for bio reactors and tank bunkers
Aephiex Dec 15, 2024
e08b42d
name convention fix after merge
psi-cmd Dec 15, 2024
328ef01
fix no manual enter
Aephiex Dec 15, 2024
ab09055
Update AutoLoad.cpp
Aephiex Dec 15, 2024
0db3a2b
Merge branch 'autoload' into patch-1
Aephiex Dec 15, 2024
219a181
Update AutoLoad.cpp
Aephiex Dec 15, 2024
6cd4cdc
Update Body.h
Aephiex Dec 15, 2024
d9c89a5
Update Body.cpp
Aephiex Dec 15, 2024
3f565c6
Update AutoLoad.cpp
Aephiex Dec 15, 2024
aaff634
by size should be default true
Aephiex Dec 15, 2024
21ec9f3
maybe we should use the "second" as sort of budget value so it is sim…
Aephiex Dec 15, 2024
656121f
oh, one line left
Aephiex Dec 15, 2024
1f121c6
update comment
Aephiex Dec 15, 2024
7a2bd49
larger passengers are loaded first
Aephiex Dec 16, 2024
e495829
Merge pull request #3 from Aephiex/patch-1
psi-cmd Dec 16, 2024
8e40291
solve multiplayer desync
Aephiex Dec 16, 2024
2bc4855
Merge pull request #4 from Aephiex/patch-1
psi-cmd Dec 16, 2024
d062623
Update CREDITS.md
Aephiex Dec 16, 2024
eb1e5e1
Update Whats-New.md
Aephiex Dec 16, 2024
f5dbb3b
Update User-Interface.md
Aephiex Dec 16, 2024
75eb1aa
Merge pull request #5 from Aephiex/patch-1
psi-cmd Dec 16, 2024
b8aed4d
Ignore Force for Clicked Action
psi-cmd Dec 16, 2024
4aa1c0f
get rid of type warning
psi-cmd Dec 16, 2024
bf06f8f
Update User-Interface.md
Aephiex Dec 17, 2024
370fa70
Update Body.cpp
Aephiex Dec 17, 2024
1550d31
Update Body.cpp
Aephiex Dec 17, 2024
836722b
move out from development commands and code clean.
psi-cmd Dec 18, 2024
0f23cca
Merge pull request #6 from Aephiex/patch-1
psi-cmd Dec 18, 2024
f121f60
style fix
psi-cmd Dec 20, 2024
dd403ec
Update CREDITS.md
psi-cmd Dec 20, 2024
36456fd
Update AutoLoad.cpp
Aephiex Dec 21, 2024
8520a5f
Update AutoLoad.cpp
Aephiex Dec 21, 2024
d94a680
Update Body.h
Aephiex Dec 21, 2024
de34130
Update Body.cpp
Aephiex Dec 21, 2024
f3b2029
Update User-Interface.md
Aephiex Dec 21, 2024
5a25372
Update AutoLoad.cpp
Aephiex Dec 21, 2024
2f78fa0
Update AutoLoad.cpp
Aephiex Dec 21, 2024
5b0dd74
Merge pull request #7 from Aephiex/patch-1
psi-cmd Dec 21, 2024
6385f63
Update AutoLoad.cpp
Aephiex Dec 21, 2024
da540d6
Update AutoLoad.cpp
Aephiex Dec 21, 2024
df6ce6e
Merge pull request #8 from Aephiex/patch-1
psi-cmd Dec 23, 2024
0f6c255
fix tank bunker filter
Aephiex Dec 23, 2024
bba2745
pBuilding->Type
Aephiex Dec 23, 2024
6aef490
style and details
Aephiex Dec 24, 2024
117add1
typo
Aephiex Dec 24, 2024
2804cdc
tank bunker check use YR function
Aephiex Dec 24, 2024
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
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\AutoLoad.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\AutoLoad.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
270 changes: 270 additions & 0 deletions src/Commands/AutoLoad.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
// For selected units, pair the loadable vehicle and infantry.
// 1. If transport can load vehicle, it won't load if the vehicle can be paired with infantry.
// 2. It will always try to load the unit type with least amount of units, eg. 2 GI and 3 GGI, it will match 2 GI to transport first.
// 3. For transport with multiple seats, it will try to fill the seats diversely.

#include "AutoLoad.h"
#include "Utilities/GeneralUtils.h"
#include "Ext/Techno/Body.h"
#include <unordered_map>

const char *AutoLoadCommandClass::GetName() const
{
return "AutoLoad";
}

const wchar_t *AutoLoadCommandClass::GetUIName() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_AUTO_LOAD", L"Auto Load");
}

const wchar_t *AutoLoadCommandClass::GetUICategory() const
{
return CATEGORY_SELECTION;
}

const wchar_t *AutoLoadCommandClass::GetUIDescription() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_AUTO_LOAD_DESC", L"Auto Load");
}

void DebugPrintTransport(std::vector<std::pair<TechnoClass *, int>> &tTransports)
{
Debug::Log("AutoLoadCommandClass::DebugPrintTransport: Transport count: %d\n", tTransports.size());
// print address of each transport and its passengers
for (auto transport : tTransports)
{
Debug::Log("AutoLoadCommandClass::DebugPrintTransport: Transport address: %p, Now size: %d, Virtual size: %d\n", transport.first, transport.first->Passengers.GetTotalSize(), transport.second);
}
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
}

void DebugPrintPassenger(std::vector<TechnoClass *> &pPassengers)
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
{
Debug::Log("AutoLoadCommandClass::DebugPrintPassenger: Passenger count: %d\n", pPassengers.size());
// print address of each passenger
for (auto passenger : pPassengers)
{
Debug::Log("AutoLoadCommandClass::DebugPrintPassenger: Passenger address: %p\n", passenger);
}
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
}

template <typename P, typename T>
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
void SpreadPassengersToTransports(std::vector<P> &pPassengers, std::vector<std::pair<T, int>> &tTransports)
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
{
// 1. Get the least kind of passengers
// 2. Send the passengers to the transport in round robin, if the transport is full, remove it from the vector tTransports;
// if the pID vector is empty, remove it from the map, if not, move it to passengerMapIdle. We try to fill the transport evenly for each kind of passengers.
// 3. Repeat until all passengers are sent or the vector tTransports is empty.
std::unordered_map<const char *, std::vector<P>> passengerMap;
std::unordered_map<const char *, std::vector<P>> passengerMapIdle;

for (auto pPassenger : pPassengers)
{
auto pID = pPassenger->get_ID();
if (passengerMap.find(pID) == passengerMap.end())
{
passengerMap[pID] = std::vector<P>();
}
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
passengerMap[pID].push_back(pPassenger);
}

while (true)
{
while (passengerMap.size() > 0 && tTransports.size() > 0)
{
const char *leastpID = nullptr;
unsigned int leastSize = std::numeric_limits<unsigned int>::max();
for (auto const &[pID, pPassenger] : passengerMap)
{
if (pPassenger.size() < leastSize)
{
leastSize = pPassenger.size();
leastpID = pID;
}
}

{
unsigned int index = 0;
for (; index < leastSize && index < tTransports.size(); index++)
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
{
auto pPassenger = passengerMap[leastpID][index];
auto pTransport = tTransports[index].first;
auto pTypeExt = TechnoTypeExt::ExtMap.Find(pTransport->GetTechnoType());
bool bySize = true;
// Check passenger filter here.
if (pTypeExt)
{
if (!pTypeExt->CanLoadPassenger(pTransport, pPassenger))
{
continue;
}
bySize = pTypeExt->Passengers_BySize;
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
}
if (pPassenger->GetCurrentMission() != Mission::Enter)
{
pPassenger->QueueMission(Mission::Enter, false);
pPassenger->SetTarget(nullptr);
pPassenger->SetDestination(pTransport, true);
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
if (bySize)
{
tTransports[index].second += abstract_cast<TechnoClass*>(pPassenger)->GetTechnoType()->Size; // increase the virtual size of transport

Check warning on line 110 in src/Commands/AutoLoad.cpp

View workflow job for this annotation

GitHub Actions / build

'+=': conversion from 'double' to '_Ty2', possible loss of data [D:\a\Phobos\Phobos\Phobos.vcxproj]

Check warning on line 110 in src/Commands/AutoLoad.cpp

View workflow job for this annotation

GitHub Actions / build

with [D:\a\Phobos\Phobos\Phobos.vcxproj]

Check warning on line 110 in src/Commands/AutoLoad.cpp

View workflow job for this annotation

GitHub Actions / build

[ [D:\a\Phobos\Phobos\Phobos.vcxproj]

Check warning on line 110 in src/Commands/AutoLoad.cpp

View workflow job for this annotation

GitHub Actions / build

_Ty2=int [D:\a\Phobos\Phobos\Phobos.vcxproj]

Check warning on line 110 in src/Commands/AutoLoad.cpp

View workflow job for this annotation

GitHub Actions / build

] [D:\a\Phobos\Phobos\Phobos.vcxproj]

Check warning on line 110 in src/Commands/AutoLoad.cpp

View workflow job for this annotation

GitHub Actions / build

tTransports[index].second += abstract_cast<TechnoClass*>(pPassenger)->GetTechnoType()->Size; // increase the virtual size of transport [D:\a\Phobos\Phobos\Phobos.vcxproj]

Check warning on line 110 in src/Commands/AutoLoad.cpp

View workflow job for this annotation

GitHub Actions / build

^ [D:\a\Phobos\Phobos\Phobos.vcxproj]
}
else
{
// If "Passengers.BySize=false" then only the number of passengers matter.
tTransports[index].second++;
}
}
}
passengerMap[leastpID].erase(passengerMap[leastpID].begin(), passengerMap[leastpID].begin() + index);
}

tTransports.erase(
std::remove_if(tTransports.begin(), tTransports.end(),
[](auto transport)
{
return transport.second == transport.first->GetTechnoType()->Passengers;
}),
tTransports.end());

if (passengerMap[leastpID].size() == 0)
{
passengerMap.erase(leastpID);
}
else
{
passengerMapIdle[leastpID] = passengerMap[leastpID];
}
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
}
if (passengerMapIdle.size() != 0 && tTransports.size() != 0)
{
std::swap(passengerMap, passengerMapIdle);
}
else
{
break;
}
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
}
}

void AutoLoadCommandClass::Execute(WWKey eInput) const
{
MapClass::Instance->SetTogglePowerMode(0);
MapClass::Instance->SetWaypointMode(0, false);
MapClass::Instance->SetRepairMode(0);
MapClass::Instance->SetSellMode(0);

// This array is for the standard passengers, most Infantry and small vehicles like terror drones go here.
// A techno is added to this array if:
// 1. It's an Infantry with size <= 2;
// 2. It's a Vehicle with size <= 2, and either has 0 passenger slots, or disallow manual loading.
std::vector<TechnoClass *> passengerArray;

// This array is for larger passengers like tanks. It is not necessarily larger, but has lower loading priority.
// A techno is added to this array if:
// 1. It's an Infantry with size >= 3;
// 2. It's a Vehicle with size >= 3, and either has 0 passenger slots, or disallow manual loading;
// 3. It's a Vehicle with passenger slots, allows manual loading, and has size limit <= 2.
//
// This array is only loaded into large vehicles if:
// - either "passengerArray" is empty;
// - or nothing in the "passengerArray" can actually be loaded into non-large vehicles.
std::vector<TechnoClass *> largePassengerArray;

// vehicles that can hold size <= 2
std::vector<std::pair<TechnoClass *, int>> vehicleIndexArray;
// vehicles that can hold size >= 3
std::vector<std::pair<TechnoClass *, int>> largeVehicleIndexArray;
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved

// Get current selected units.
// The first iteration, we find units that can't be transports, and add them to the passenger arrays.
for (int i = 0; i < ObjectClass::CurrentObjects->Count; i++)
{
auto pUnit = ObjectClass::CurrentObjects->GetItem(i);
// try to cast to TechnoClass
TechnoClass *pTechno = abstract_cast<TechnoClass *>(pUnit);
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved

// If not a techno, or is in air, or is MCed, or MCs anything, then it can't be a passenger.
if (!pTechno || pTechno->IsInAir()
|| pTechno->IsMindControlled()
|| (pTechno->CaptureManager && pTechno->CaptureManager->IsControllingSomething()))
{
continue;
}

auto pTechnoType = pTechno->GetTechnoType();
auto pTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType);

// If it's an Infantry, or it's a Unit with no passenger slots or unable to manually load, add it to the passenger arrays.
if ((pTechno->WhatAmI() == AbstractType::Infantry || (pTechno->WhatAmI() == AbstractType::Unit && (pTechnoType->Passengers <= 0 || (pTypeExt && pTypeExt->NoManualEnter)))))
{
if (pTechnoType->Size <= 2)
passengerArray.push_back(pTechno);
else
largePassengerArray.push_back(pTechno);
}
}

// Get current selected units.
// The second iteration, we find units that can be transports.
for (int i = 0; i < ObjectClass::CurrentObjects->Count; i++)
{
auto pUnit = ObjectClass::CurrentObjects->GetItem(i);
// try to cast to TechnoClass
TechnoClass *pTechno = abstract_cast<TechnoClass *>(pUnit);

// If not a techno, or is in air, then it can't be a vehicle.
if (!pTechno || pTechno->IsInAir())
{
continue;
}
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved

auto pTechnoType = pTechno->GetTechnoType();
auto pTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType);
if (pTechno->WhatAmI() == AbstractType::Unit)
{
bool bySize = pTypeExt && pTypeExt->Passengers_BySize;

// If "Passengers.BySize=false" then only the number of passengers matter.
if (pTechnoType->Passengers > 0
&& pTechno->Passengers.NumPassengers < pTechnoType->Passengers
&& (!bySize || pTechno->Passengers.GetTotalSize() < pTechnoType->Passengers))
{
auto const transportTotalSize = bySize ? pTechno->Passengers.GetTotalSize() : pTechno->Passengers.NumPassengers;
if (pTechnoType->SizeLimit > 2)
{
largeVehicleIndexArray.push_back(std::make_pair(pTechno, transportTotalSize));
continue;
}
else if (!pTypeExt || pTypeExt->CanLoadAny(pTechno, passengerArray))
{
vehicleIndexArray.push_back(std::make_pair(pTechno, transportTotalSize));
continue;
}
}

// If MCed, or MCs anything, then it can't be a passenger.
if (pTechno->IsMindControlled() || (pTechno->CaptureManager && pTechno->CaptureManager->IsControllingSomething()))
{
continue;
}
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
largePassengerArray.push_back(pTechno);
}
}

// pair the infantry and vehicle
if (vehicleIndexArray.size() > 0 && passengerArray.size() > 0)
{
SpreadPassengersToTransports(passengerArray, vehicleIndexArray);
}
else if (largeVehicleIndexArray.size() > 0)
{
// load both infantry and vehicle into large vehicle
auto &passengerIndexArray = passengerArray;
for (auto largePassenger : largePassengerArray)
{
passengerIndexArray.push_back(largePassenger);
}
SpreadPassengersToTransports(passengerIndexArray, largeVehicleIndexArray);
}
}
16 changes: 16 additions & 0 deletions src/Commands/AutoLoad.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include "Commands.h"

// Select next idle harvester
class AutoLoadCommandClass : public CommandClass
{
public:
// CommandClass
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;
// virtual bool ExtraTriggerCondition(WWKey eInput) const override;
};
2 changes: 2 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 "AutoLoad.h"

DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6)
{
Expand All @@ -32,6 +33,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6)
MakeCommand<FrameStepCommandClass<15>>(); // Speed 3
MakeCommand<FrameStepCommandClass<30>>(); // Speed 4
MakeCommand<FrameStepCommandClass<60>>(); // Speed 5
MakeCommand<AutoLoadCommandClass>();
}

return 0;
Expand Down
37 changes: 37 additions & 0 deletions src/Ext/TechnoType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,33 @@ void TechnoTypeExt::ExtData::ApplyTurretOffset(Matrix3D* mtx, double factor)
mtx->Translate(x, y, z);
}

// Checks if a transport can load a passenger.
// Note that this function only checks the size limit and Ares passenger whitelist and blacklist,
// it doesn't check if this transport is actually a transport or not.
bool TechnoTypeExt::ExtData::CanLoadPassenger(TechnoClass* pTransport, TechnoClass* pPassenger) const
{
auto const pTransportType = pTransport->GetTechnoType();
auto const pPassengerType = pPassenger->GetTechnoType();
return (pTransportType->SizeLimit <= 0 || pTransportType->SizeLimit >= pPassengerType->Size)
&& (!this->Passengers_BySize || (pTransport->Passengers.GetTotalSize() + pPassengerType->Size) <= pTransportType->Passengers)
&& (this->PassengersWhitelist.empty() || this->PassengersWhitelist.Contains(pPassengerType))
&& !this->PassengersBlacklist.Contains(pPassengerType);
}

bool TechnoTypeExt::ExtData::CanLoadAny(TechnoClass* pTransport, std::vector<TechnoClass*> pPassengerList) const
{
for (auto pPassenger : pPassengerList)
{
if (this->CanLoadPassenger(pTransport, pPassenger))
{
return true;
}
}
psi-cmd marked this conversation as resolved.
Show resolved Hide resolved
return false;
}



// Ares 0.A source
const char* TechnoTypeExt::ExtData::GetSelectionGroupID() const
{
Expand Down Expand Up @@ -391,6 +418,11 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
this->Passengers_SyncOwner.Read(exINI, pSection, "Passengers.SyncOwner");
this->Passengers_SyncOwner_RevertOnExit.Read(exINI, pSection, "Passengers.SyncOwner.RevertOnExit");

this->PassengersWhitelist.Read(exINI, pSection, "Passengers.Allowed");
this->PassengersBlacklist.Read(exINI, pSection, "Passengers.Disallowed");
this->Passengers_BySize.Read(exINI, pSection, "Passengers.BySize");
this->NoManualEnter.Read(exINI, pSection, "NoManualEnter");

this->IronCurtain_KeptOnDeploy.Read(exINI, pSection, "IronCurtain.KeptOnDeploy");
this->IronCurtain_Effect.Read(exINI, pSection, "IronCurtain.Effect");
this->IronCurtain_KillWarhead.Read<true>(exINI, pSection, "IronCurtain.KillWarhead");
Expand Down Expand Up @@ -744,6 +776,11 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->Passengers_SyncOwner)
.Process(this->Passengers_SyncOwner_RevertOnExit)

.Process(this->PassengersWhitelist)
.Process(this->PassengersBlacklist)
.Process(this->Passengers_BySize)
.Process(this->NoManualEnter)

.Process(this->OnlyUseLandSequences)

.Process(this->PronePrimaryFireFLH)
Expand Down
Loading
Loading