beyond-all-reason / spring

A powerful free cross-platform RTS game engine
https://beyond-all-reason.github.io/spring/
Other
217 stars 102 forks source link

Feature: Masks/categories for allowing inbuilt ally-helping behaviour #676

Open GoogleFrog opened 1 year ago

GoogleFrog commented 1 year ago

MaDDoX wants to make a constructor that has the following abilities:

This is difficult to do at the moment since commands like Patrol, Area Repair, Fight and Guard give constructors a bunch of inbuilt behaviors that lua is ill-suited to overriding. Eg, if a constructor is on Fight and gives itself a Repair command to a nearby unit, lua can detect and remove the command, but it can't cleanly make the unit stop reissuing the command (and even finding and removing commands is pretty ugly).

I suggest solving this with some sort of mask or category system for automatically giving and receiving help from allies. One implementation could work like the weapondefs ShieldInterceptType and InterceptedByShieldType. Units would have a helpiee mask and a helper mask. If the helpiee mask and helper masks don't align, then the helper ignores the helpiee for all sorts of engine-internal helping checks (ie the helper generating a Repair command). Lua would be able to set these masks dynamically. Then MaDDoX would write the following gadgetry.

There is potential to add two types of mask, one that is used for automatically generating commands (unit AI), and another which is used for outright blocking the ability to Repair certain targets (behaviour). This would be akin to how BadTargetCategory and OnlyTargetCategory let units be configured to shoot at particular enemies without automatically acquiring them as command targets when idle or on orders like Fight.

Of course, maybe masks are wrong and it should be done with a list of categories. That's an implementation detail.

MaDDoXbr commented 1 year ago

Thank you very much @GoogleFrog, outstanding writeup. Exactly, 'canbeassisted' is too limited, as it blocks assistance by all units, and trying to use existing callins to filter out those actions in a gadget is extremely hacky, demanding the removal of Patrol and Guard (+area guard) commands from those units (as they turn into repair, which then "assist builds" what I don't want). If we have these maks (named "assisterMask" and "assistedMask" maybe?) both within the unitDefs and updatable by lua, it would be trivial to simply set some unit "unrepairable" by certain builder during construction and then "repairable" by all units once they're fully built (from addon:UnitFinished).

My exact use-case is (and with masks could be made flexible for any games):

Krogoth100 commented 1 year ago

Few words of mine. I think having such masks will be very useful for factions with strong difference in construction method. Think of WarCraft 3: a night elf wisp cannot assist-build a human structure, because it grows a tree; it cannot assist at all. Humans obviously can assist each other, but not an orc. And so on. Clearly, this feature is useful even on a fundamental level of implementing most basic interactions, which happens when you go outside of a classic Spring faction concept borders.

Krogoth100 commented 1 year ago

What if we could assign a bit mask reusing same canAssist, canRepair and canBeAssisted fields of a unitDef? Like canAssist = true --(can assist anything) canAssist = bits(1,0,1) --(bit mask) canAssist = false --(same as bits(0))

MaDDoXbr commented 1 year ago

@Krogoth100 That would work, as long as we also get the ability to change it dynamically through lua, otherwise we can't block the assistance-mask in specific situations, like when it's being built or when it's paralyzed. Something like Spring.SetAssistMask(unitID, mask) and Spring.SetAssistedMask(unitID,mask) would do fine.

Krogoth100 commented 1 year ago

@Krogoth100 That would work, as long as we also get the ability to change it dynamically through lua, otherwise we can't block the assistance-mask in specific situations, like when it's being built or when it's paralyzed. Something like Spring.SetAssistMask(unitID, mask) and Spring.SetAssistedMask(unitID,mask) would do fine.

Of course. As an option, this functionality can be made a part of SetUnitBuildParams(), like SetUnitBuildParams(unit, "canAssist", bits(1,0,1)) Because I can anticipate a growing list of such fields (capture mask, resurrect mask - who knows), so thematic function seems good.

Edit: And maybe another collective function like that for "canBeAssisted/canBeRepaired/...". Like "SetUnitBuildeeParams", but something more normal, probably : D

Edit: Or just put that into SetUnitBuildParams, because the name is a bit wavy and may suggest that as well.