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

Conversation

psi-cmd
Copy link

@psi-cmd psi-cmd commented Dec 14, 2024

For selected units, pair the transport with infantry/vehicle.

  1. If vehicle loadable transports selected, it won't be loaded if there are vehicles that can only be paired with infantry within selection.
  2. It will always try to load the unit type with least amount of units first, 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 those seats "diversely".

@psi-cmd
Copy link
Author

psi-cmd commented Dec 14, 2024

(Falsely include irrelevent file that belongs to another feature)

@Aephiex
Copy link
Contributor

Aephiex commented Dec 14, 2024

  1. Is it possible we can have a default shortcut key for the autoload command?
  2. This doesn't seem to support garrisonable buildings and bio reactors. However if we add such support to it, we have to tell apart grinders from bio reactors, and we have to distinguish InfantryAbsorb and UnitAbsorb.
  3. This doesn't seem to support Ares features Passengers.Allowed=, Passengers.Disallowed=, Passengers.BySize=, and NoManualEnter= as well as it will still try to assign size 6 vehicles into a transport with size limit 3.
  4. Terror drones may not be auto-loaded into Flak Tracks this way. Maybe treat smaller units like they are infantries?
  5. It doesn't check if the passengers are mind controlled or are mind controlling something, in which case it is not allowed to enter a transport.

I solved 3, 4, and 5. See below for details.

@Aephiex
Copy link
Contributor

Aephiex commented Dec 14, 2024

When I select an amphibious transport on water and units on land at a same time and press the auto load button, the amphibious transport will move ashore all the way to the land unit and stop there. The land unit doesn't get into the transport. However, this is exactly the same behavior when I call a land unit to get into an amphibious transport on water, so this isn't really an issue.

The implementation is mindful. It directly gives enter order to selected units so everything will behave the same as if the player explicitly gave an enter order. It is very convenient to use, even I can now enjoy playing the Allies.

Copy link

github-actions bot commented Dec 14, 2024

Nightly build for this pull request:

This comment is automatic and is meant to allow guests to get latest nightly builds for this pull request without registering. It is updated on every successful build.

@Aephiex
Copy link
Contributor

Aephiex commented Dec 14, 2024

Hello, made Ares tag support for you, and more.

psi-cmd#1

Effects:

  1. Transports with NoManualEnter=true will be viewed as if they didn't have passenger slots;
  2. If a transport has Passengers.BySize=false then every potential passenger is viewed as size 1;
  3. If a transport has Passengers.Allowed= and Passengers.Disallowed= defined then passengers unable to enter it will not try to get into it, as well as the auto assignment will never try to violent the SizeLimit=;
  4. Vehicle types with size <= 2 and passenger <= 0 are viewed as standard passengers like infantry types, and infantry types with size >= 3 are viewed as larger passengers like tanks;
  5. If a standard transport can't actually load anything due to its passenger filters, then just try to load it into larger transports;
  6. Mind controlled units and units mind controlling something can't be passengers at all.

Maybe we have to detect if Ares is present before we take compatibility with Ares tags?

@NetsuNegi
Copy link
Contributor

have you test in multiplayer game?

@psi-cmd
Copy link
Author

psi-cmd commented Dec 14, 2024

Hello, made Ares tag support for you, and more.

psi-cmd#1

Effects:

  1. Transports with NoManualEnter=true will be viewed as if they didn't have passenger slots;
  2. If a transport has Passengers.BySize=false then every potential passenger is viewed as size 1;
  3. If a transport has Passengers.Allowed= and Passengers.Disallowed= defined then passengers unable to enter it will not try to get into it, as well as the auto assignment will never try to violent the SizeLimit=;
  4. Vehicle types with size <= 2 and passenger <= 0 are viewed as standard passengers like infantry types, and infantry types with size >= 3 are viewed as larger passengers like tanks;
  5. If a standard transport can't actually load anything due to its passenger filters, then just try to load it into larger transports;
  6. Mind controlled units and units mind controlling something can't be passengers at all.

Maybe we have to detect if Ares is present before we take compatibility with Ares tags?

Wow, thanks for your work. I am not famillar with other features on transport loading and you help with the corner cases.
And also thanks for a clear documentation that explain things into details. 👍

For ares tags, maybe we could have the "original" default value for them. For example, NoManualEnter=False, Passengers.BySize=True are the default behavior without any MOD.

Ares tags support and smarter unit size logic and more
@psi-cmd
Copy link
Author

psi-cmd commented Dec 14, 2024

have you test in multiplayer game?

Not yet, trying to find someone for help.

src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
src/Ext/TechnoType/Body.cpp Outdated Show resolved Hide resolved
src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
@Aephiex
Copy link
Contributor

Aephiex commented Dec 15, 2024

For ares tags, maybe we could have the "original" default value for them. For example, NoManualEnter=False, Passengers.BySize=True are the default behavior without any MOD.

Yes, I defaulted every vehicle to allow maual enter and to count passengers by size.

@psi-cmd psi-cmd marked this pull request as ready for review December 18, 2024 01:53
@Aephiex
Copy link
Contributor

Aephiex commented Dec 20, 2024

@chaserli Hello, this pull request seems ready for review. Maybe take a look when you have time?

@Metadorius Metadorius removed the Needs Rework The structure or methodology is "mise en question" label Dec 20, 2024
Copy link
Member

@Metadorius Metadorius left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, lemme fix some of the style stuff quickly.

- Larger passengers are loaded first, and transports with smaller size limits are used first.
- At a given unit size and size limit, passengers will be diversely distributed into transports if possible.
- Ares `Passengers.Allowed=` and `Passengers.Disallowed=` are taken into account.
- It also supports Bio Reactors and Tank Bunkers. Select valid candidates and multiple said buildings while pressing Shift, then press the auto load hotkey, the units will be distributed among these buildings and will be ordered to enter them.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about Soviet Battle Bunkers and other structures that you own and can select and can have garrisons?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Garrisonable structures are currently not supported. Neutral structures and owned troops can't be selected at a same time. Also can't read the user's intention when a bio reactor, a battle bunker, and a group of conscripts are selected at a same time. Maybe we need another shortcut key for that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also can't read the user's intention when a bio reactor, a battle bunker, and a group of conscripts are selected at a same time.

Everyone who can fit in bio reactor but not the garrison goes to bio reactor, everyone else is spread between bunkers and reactors. I think the situation when battle bunkers and reactors will be selected is rather rare so there's no need to do much thought about it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right. I will add such support later. However since neutral structures still can't be selected with owned troops at a same time, this will only ease the loading of battle bunkers, which is always owned by a player even when not garrisoned.

src/Commands/AutoLoad.cpp Outdated Show resolved Hide resolved
CREDITS.md Outdated Show resolved Hide resolved
psi-cmd and others added 2 commits December 20, 2024 14:51
Copy link
Contributor

@chaserli chaserli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redundant sanity checks such as misuse of abstract_cast and all kinds of sanity check on xxExt are not the most significant code style issues in the current code.

I have the impression that the entire selection-filtering is somehow repeated. I can think of 2 ways: either you just let the game tell which cursor to use and select the "load onto vehicle" cursors among them. Or just handle the network event directly.

If you don't fully understand how network events are handled, you can check the implementation of the virtual function you've chosen. Methods in EventClass.h in yrpp is also updated to handle them directly.

@Metadorius
Copy link
Member

Metadorius commented Dec 21, 2024

@chaserli it will be good to see the actual explanations of why do you think something is wrong (like the cast thing) and how and it should be done properly. Code reviews should be done so the author can learn and grow, saying "it is wrong" without explaining when you know the answer is not productive.

Aephiex and others added 4 commits December 22, 2024 01:17
// If return value <= 0 then the building can't be a "transport".
inline static const int GetBuildingPassengerBudget(BuildingClass* pBuilding)
{
auto pBuildingType = abstract_cast<BuildingTypeClass*>(pBuilding->GetTechnoType());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can directly use pBuilding->Type.

else if (pPassenger->WhatAmI() == AbstractType::Unit)
{
// Tank Bunker
return pPassenger->GetTechnoType()->Turret
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed a lot of checks. See sub_70FB50.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

如果你不想仔细检查每个可能的进入关系是否成立的话,可以试试使用MouseOverObject。它会返回一个Action,只要返回的Action是Enter那应该就是可以进入的。不过这个函数会进行很多很多判断,如果你要用的话最好实际测试一下会不会造成卡顿。
If you don't want to carefully check whether each possible entry relationship is established, you can try using MouseOverObject. It will return an Action. As long as the returned Action is Enter, it should be possible to enter. However, this function will make many, many checks. If you decide to use it, it is best to actually test whether it will cause lag.

Copy link
Contributor

@Aephiex Aephiex Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried before you say, didn't work. Most objects don't yield Action::Enter when selected by the player, only Bio Reactors do. Meanwhile, if we simply invoke ObjectClickedAction, most objects can be entered when Action::Enter is given, even when the object is selected by the player, only Tank Bunkers can't be entered this way.

Copy link
Contributor

@Aephiex Aephiex Dec 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed a lot of checks. See sub_70FB50.

As far as I read from the assembly, it seems hovered units, parasited units, and those without a "turret changer" weapon, are never eligible to enter a Tank Bunker, right?
I don't know what is a turret changer. The rest can be added to the filter.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can directly reference that function by using reinterpret_cast. The use cases can be found in the project.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, didn't know that, thank you.

if (pTransport->WhatAmI() == AbstractType::Building)
{
auto pBuilding = abstract_cast<BuildingClass*>(pTransport);
auto pBuildingType = abstract_cast<BuildingTypeClass*>(pBuilding->GetTechnoType());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pBuilding->Type

@Aephiex
Copy link
Contributor

Aephiex commented Dec 23, 2024

@chaserli Hello, redundancy have been migrated to inline functions, so it doesn't look as redundant as before. Maybe you can take a look again when you have time. Also I don't know what's wrong with the "misuse" of abstract_cast, maybe you will elaborate.

Copy link
Contributor

@CrimRecya CrimRecya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that you understand the abstract_cast. But I found some parts that can be omitted to avoid repeating check.

return pPassenger->GetTechnoType()->Turret
&& pPassenger->GetTechnoType()->Bunkerable
&& pPassenger->GetTechnoType()->SpeedType != SpeedType::Hover
&& !abstract_cast<FootClass*>(pPassenger)->ParasiteEatingMe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!static_cast<FootClass*>(pPassenger)->ParasiteEatingMe

}
else if (pTransport->WhatAmI() == AbstractType::Building)
{
auto pBuilding = abstract_cast<BuildingClass*>(pTransport);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

else if (auto pBuilding = abstract_cast<BuildingClass*>(pTransport))

// the check of MCing is redundant here, see inline function "CanBeBuildingPassenger".
return pPassenger->WhatAmI() == AbstractType::Infantry
&& !pPassenger->IsMindControlled()
&& abstract_cast<InfantryTypeClass*>(pPassenger->GetTechnoType())->Occupier
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

static_cast<InfantryTypeClass*>(pPassenger->GetTechnoType())->Occupier

{
if (pTransport->WhatAmI() == AbstractType::Unit)
{
auto pType = pTransport->GetTechnoType();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (auto pUnit = abstract_cast<UnitClass*>(pTransport))
{
  auto pType = pUnit->Type;
  ...
}

{
if (pTransport->WhatAmI() == AbstractType::Building)
{
auto pBuilding = abstract_cast<BuildingClass*>(pTransport);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (auto pBuilding = abstract_cast<BuildingClass*>(pTransport))

// Detect enterable buildings.
if (pTechno->WhatAmI() == AbstractType::Building)
{
auto pBuilding = abstract_cast<BuildingClass*>(pTechno);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (auto pBuilding = abstract_cast<BuildingClass*>(pTechno))

{
// If by size then get the actual size of the potential passenger.
// Otherwise, every potential passenger counts as size 1.
passengerSize = static_cast<int>(abstract_cast<TechnoClass*>(pPassenger)->GetTechnoType()->Size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is determined that pPassenger is a TechnoClass*, you can replace the redundant abstract_cast with reinterpret_cast

@Aephiex
Copy link
Contributor

Aephiex commented Dec 24, 2024

It seems that you understand the abstract_cast. But I found some parts that can be omitted to avoid repeating check.

Thank you for your detailed review. I appreciate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants