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

Update transporters to be more effective with the area load command. #1946

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

lostsquirrel1
Copy link
Collaborator

Adds a unitDef to allow transporters to pick up units without trying to turn to face the same heading.

Fixes #1945

@sprunk
Copy link
Collaborator

sprunk commented Feb 12, 2025

What about the other hardcoded behaviours (e.g. set altitude to target unit height, what if I'd rather the unit was beamed up via tractor beams like an UFO?)
What about these hardcoded constants?

static constexpr float AIRTRANSPORT_DOCKING_RADIUS = 16.0f;
static constexpr float AIRTRANSPORT_DOCKING_ANGLE = 50.0f;


gadget:AllowUnitTransportLoad can already implement whatever arbitrary checks the game desires, so how about:

  • a bool that turns off all behaviours (i.e. rotating heading to match the target, set wanted altitude, set allow landing to false, maxdrift = 1.0f) and instead makes it so that the engine only handles moving inside loadRadius and then relegates everything to the game by starting to call gadget:AllowUnitTransportLoad when inside the radius.
  • pass the existing logic-related vars (heading diff, position) to gadget:AllowUnitTransportLoad if not there already, to facilitate games building upon the defaults.

@lostsquirrel1
Copy link
Collaborator Author

lostsquirrel1 commented Feb 12, 2025

What about the other hardcoded behaviours (e.g. set altitude to target unit height, what if I'd rather the unit was beamed up via tractor beams like an UFO?) What about these hardcoded constants?

static constexpr float AIRTRANSPORT_DOCKING_RADIUS = 16.0f;
static constexpr float AIRTRANSPORT_DOCKING_ANGLE = 50.0f;

gadget:AllowUnitTransportLoad can already implement whatever arbitrary checks the game desires, so how about:

* a bool that turns off all behaviours (i.e. rotating heading to match the target, set wanted altitude, set allow landing to false, maxdrift = 1.0f) and instead makes it so that the engine only handles moving inside loadRadius and then relegates everything to the game by starting to call `gadget:AllowUnitTransportLoad` when inside the radius.

* pass the existing logic-related vars (heading diff, position) to `gadget:AllowUnitTransportLoad` if not there already, to facilitate games building upon the defaults.

Those values are only used as inputs in the Lua callback AllowUnitTransportLoad, which to my knowledge is ignored by every game. The engine does NOT use the values itself. I've raised a question with the game devs whether any use it. If not, then it would make more sense to remove them completely. These values are there to allow games to keep the original engine behaviour (prior to the inclusion of AllowUnitTransportLoad)

@sprunk
Copy link
Collaborator

sprunk commented Feb 12, 2025

Those values are only used as inputs in the Lua callback AllowUnitTransportLoad, which to my knowledge is ignored by every game.

They aren't, they are completely lost in a bunch of steps. First the raw values most useful for games (heading diff, distance) aren't passed anywhere, they are only used to produce the "is above arbitrary threshold" bools.

const bool isInRange = (owner->pos.SqDistance(wantedPos) < Square(AIRTRANSPORT_DOCKING_RADIUS));
const bool isAligned = (std::abs(owner->heading - unit->heading) < AIRTRANSPORT_DOCKING_ANGLE);
const bool isUpright = (owner->updir.dot(UpVector) > 0.995f);

The bools aren't ever used separately, they are ANDed together into a single bool.

if (!eventHandler.AllowUnitTransportLoad(owner, unit, wantedPos, isInRange && isAligned && isUpright))

That bool is passed to the C++ side of the callin (called allowed below), but never lua_pushboolean'd there.

// use engine default if callin does not exist
if (!cmdStr.GetGlobalFunc(L))
return allowed;
lua_pushnumber(L, transporter->id);
lua_pushnumber(L, transporter->unitDef->id);
lua_pushnumber(L, transporter->team);
lua_pushnumber(L, transportee->id);
lua_pushnumber(L, transportee->unitDef->id);
lua_pushnumber(L, transportee->team);
lua_pushnumber(L, loadPos.x);
lua_pushnumber(L, loadPos.y);
lua_pushnumber(L, loadPos.z);
if (!RunCallIn(L, cmdStr, 9, 1))
return true;

So Lua does not actually receive any of those. It would be nice if it received number headingDiff and number distance, though it can already calculate these itself by polling the relevant units for position/heading so it's not that important, just inconvenient.

The engine does NOT use the values itself. I've raised a question with the game devs whether any use it. If not, then it would make more sense to remove them completely.

The boolean check(s) are used as the default, in case Lua does not return anything. So they must stay, at least for now. But indeed they aren't used if Lua returns something, so I now think it's fine if they are arbitrarily hardcoded since Lua can just return something if it doesn't like them.

The game can do nothing about the other behaviours though (set altitude, set wanted rotation etc) so there would ideally be a way to opt out of those.

@lostsquirrel1
Copy link
Collaborator Author

lostsquirrel1 commented Feb 12, 2025

Thanks for the clarification that the Lua event handler doesn't push the allowed parameter.

How is the allowed parameter being used as the default value for the call? As far as I see, maybe I'm missing something, CEventHandler::AllowUnitTransportLoad calls ControlIterateDefTrue which always defaults to true - I can't see how it uses allowed as the default value for the return.

@sprunk
Copy link
Collaborator

sprunk commented Feb 12, 2025

calls ControlIterateDefTrue which always defaults to true

This is only the default if there is no C++ side event client. If there are event clients, any of them returning false is sufficient to make the whole call return false thanks to the &= operator:

bool result = true;
for (size_t i = 0; i < list.size(); ) {
CEventClient* ec = list[i];
result &= (ec->*func)(std::forward<A>(args)...);

The C++ side of LuaHandleSynced::AllowUnitTransportLoad returns the default itself, if Lua didn't:

// ditto if it does but returns nothing
const bool allow = luaL_optboolean(L, -1, allowed);
lua_pop(L, 1);
return allow;

@lostsquirrel1
Copy link
Collaborator Author

Gotcha.

@lostsquirrel1
Copy link
Collaborator Author

In that case I'll leave it for now.

@sprunk
Copy link
Collaborator

sprunk commented Feb 12, 2025

Having thought about it, the PR is basically acceptable already, perhaps except it would be nice to name the new unit def tag something futureproof like useBuiltinLoadingBehaviours that will be seamlessly extensible to be the single toggle that means "please let me implement my own game design, thank you" when the engine also decides to let games control the other hardcoded behaviours like setting altitude. Not a big deal either way though.

@lostsquirrel1 lostsquirrel1 changed the title Allow transporters to pick up units without trying to turn to face the same heading. Update transporters to be more effective with the are load command. Feb 17, 2025
@lostsquirrel1 lostsquirrel1 changed the title Update transporters to be more effective with the are load command. Update transporters to be more effective with the area load command. Feb 17, 2025
@lostsquirrel1 lostsquirrel1 marked this pull request as draft February 20, 2025 10:49
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.

Less strict requirements for transports loading units
2 participants