diff --git a/src/Commands/AutoLoad.cpp b/src/Commands/AutoLoad.cpp new file mode 100644 index 0000000000..6b26926a40 --- /dev/null +++ b/src/Commands/AutoLoad.cpp @@ -0,0 +1,199 @@ +// for selected units, pair the loadable vehicle and units. +// first only apply to infantry. + +#include "AutoLoad.h" +#include "Utilities/GeneralUtils.h" +#include "Ext/Techno/Body.h" +#include + +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>& 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); + } +} + +void DebugPrintPassenger(std::vector& pPassengers) +{ + 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); + } +} + +template +void SpreadPassengersToTransports(std::vector

& pPassengers, std::vector>& tTransports) +{ + // 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> passengerMap; + std::unordered_map> passengerMapIdle; + + for (auto pPassenger : pPassengers) + { + auto pID = pPassenger->get_ID(); + if (passengerMap.find(pID) == passengerMap.end()) + { + passengerMap[pID] = std::vector

(); + } + passengerMap[pID].push_back(pPassenger); + } + + DebugPrintPassenger(pPassengers); + + while (true) + { + while (passengerMap.size() > 0 && tTransports.size() > 0) + { + const char* leastpID = nullptr; + unsigned int leastSize = std::numeric_limits::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++) + { + auto pPassenger = passengerMap[leastpID][index]; + auto pTransport = tTransports[index].first; + if (pPassenger->GetCurrentMission() != Mission::Enter) + { + pPassenger->QueueMission(Mission::Enter, false); + pPassenger->SetTarget(nullptr); + pPassenger->SetDestination(pTransport, true); + tTransports[index].second += static_cast(pPassenger->GetTechnoType()->Size); // increase the virtual size of transport + } + } + 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() + ); + DebugPrintTransport(tTransports); + + if (passengerMap[leastpID].size() == 0) + { + passengerMap.erase(leastpID); + } + else + { + passengerMapIdle[leastpID] = passengerMap[leastpID]; + } + } + if (passengerMapIdle.size() != 0 && tTransports.size() != 0) + { + std::swap(passengerMap, passengerMapIdle); + } else { + break; + } + } +} + +void AutoLoadCommandClass::Execute(WWKey eInput) const +{ + MapClass::Instance->SetTogglePowerMode(0); + MapClass::Instance->SetWaypointMode(0, false); + MapClass::Instance->SetRepairMode(0); + MapClass::Instance->SetSellMode(0); + + std::vector infantryIndexArray; + std::vector> vehicleIndexArray; + // vehicle that can hold size larger than 2 passenger index array + std::vector> largeVehicleIndexArray; + // full vehicle may be passenger. + std::vector mayBePassengerArray; + // get current selected units. + Debug::Log("AutoLoadCommandClass::Execute: Selected units count: %d\n", ObjectClass::CurrentObjects->Count); + for (int i = 0; i < ObjectClass::CurrentObjects->Count; i++) + { + auto pUnit = ObjectClass::CurrentObjects->GetItem(i); + // try to cast to TechnoClass + TechnoClass* pTechno = static_cast(pUnit); + + if (pTechno && pTechno->WhatAmI() == AbstractType::Infantry && !pTechno->IsInAir()) + { + infantryIndexArray.push_back(pTechno); + Debug::Log("AutoLoadCommandClass::Execute: Infantry index: %d\n", i); + } + else if (pTechno && pTechno->WhatAmI() == AbstractType::Unit && !pTechno->IsInAir()) + { + auto const pType = pTechno->GetTechnoType(); + if (pType->Passengers > 0 + && pTechno->Passengers.NumPassengers < pType->Passengers + && pTechno->Passengers.GetTotalSize() < pType->Passengers) + { + if (pTechno->GetTechnoType()->SizeLimit > 2) + { + largeVehicleIndexArray.push_back(std::make_pair(pTechno, pTechno->Passengers.GetTotalSize())); + Debug::Log("AutoLoadCommandClass::Execute: Large Vehicle index: %d, Passengers: %d\n", i, pTechno->Passengers.GetTotalSize()); + } + else + { + vehicleIndexArray.push_back(std::make_pair(pTechno, pTechno->Passengers.GetTotalSize())); + } + } + else + { + mayBePassengerArray.push_back(pTechno); + } + } + } + + // pair the infantry and vehicle + if (vehicleIndexArray.size() > 0 && infantryIndexArray.size() > 0) + { + SpreadPassengersToTransports(infantryIndexArray, vehicleIndexArray); + } + else if (largeVehicleIndexArray.size() > 0) + { + // load both infantry and vehicle into large vehicle + auto & passengerIndexArray = infantryIndexArray; + for (auto vehicle : vehicleIndexArray) + { + passengerIndexArray.push_back(vehicle.first); + } + for (auto mayBePassenger : mayBePassengerArray) + { + passengerIndexArray.push_back(mayBePassenger); + } + SpreadPassengersToTransports(passengerIndexArray, largeVehicleIndexArray); + } +} diff --git a/src/Commands/AutoLoad.h b/src/Commands/AutoLoad.h new file mode 100644 index 0000000000..4848d4c6c2 --- /dev/null +++ b/src/Commands/AutoLoad.h @@ -0,0 +1,17 @@ +#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; + +}; diff --git a/src/Commands/Commands.cpp b/src/Commands/Commands.cpp index 712a596467..f706adc7e5 100644 --- a/src/Commands/Commands.cpp +++ b/src/Commands/Commands.cpp @@ -10,6 +10,7 @@ #include "ToggleDigitalDisplay.h" #include "ToggleDesignatorRange.h" #include "SaveVariablesToFile.h" +#include "AutoLoad.h" DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) { @@ -32,6 +33,7 @@ DEFINE_HOOK(0x533066, CommandClassCallback_Register, 0x6) MakeCommand>(); // Speed 3 MakeCommand>(); // Speed 4 MakeCommand>(); // Speed 5 + MakeCommand(); } return 0;