OR  Zero-K Name:    Password:   

modifying caretaker behavior

24 posts, 951 views
Post comment
Filter:    Player:  
Page of 2 (24 records)

3 years ago
I'm regularly frustrated by caretakers that assist in some construction when I'm already metal-starved, instead of reclaiming a wreck that's sitting right in front of them. I'd like to change caretakers to do the following:

1. When metal reserves are below 1/3 and there is something to be reclaimed, start reclaiming it.
2. When metal reserves are above 2/3 and assisting in production/repairing is possible, do that.
3. When metal reserves are between 1/3 and 2/3, continue doing what you're doing.

I'm curious what people think about this, and also if you can point me in the right direction as to where I'd make this change. I took a look at https://github.com/ZeroK-RTS/Zero-K but it just appears to define units in terms of values, but does not contain the actual code that controls their behavior. (Or maybe I missed it.)

Thanks for an awesome game.
+5 / -0

3 years ago
pls pls finally make caretakers not reclaim automatically when you are excessing.
this is one of the most frustrating behaviours in the whole game for me.
+5 / -0

3 years ago
> pls pls finally make caretakers not reclaim automatically when you are excessing.

That's a good idea, and I should be able to get that in as well, assuming I figure this out at all.
+1 / -0
3 years ago
Nerf them all!
+0 / -0

3 years ago
As you found, the unit files only define some values that other stuff (largely engine stuff) uses, rather than any unit scripts. The thing is, making units work smartly is generally done more globally, rather than per-unit.

Basically, you're going to have to make either a gadget or a widget for this. The downside is that it does mean you are potentially running into some unexpected interactions with other gadgets or widgets that also control caretaker behaviour, but the upside is that since it could work as a widget you can test it locally, and even do live-fire tests in online games while you are building it.
+0 / -0
Since we are at these kinds of topic I also want defensive structures to prioritize specific targets. This way my artemis won't fire at blastwings :D.
+1 / -0
3 years ago
ROrankForever, pretty sure there's already a widget for that. Check Springfiles?
+0 / -0
1/3 and 2/3 are too arbitrary. What you want is something based on whether you have 0 metal/energy or maxed metal/energy:

  • Say you have 0 metal. You want the caretakers to reclaim metal (first priority) or to repair your units (second priority) instead of trying to spend metal you don't have by building units (third priority).
  • Say you have 0 energy. You don't want the caretakers automatically repairing things because you need that energy to build. So with 0 energy you want the caretakers to reclaim energy (first priority), or build units (second priority).
  • Say you have maxed out metal and some energy. You don't want the caretakers to reclaim metal that would be wasted. Instead you want them to spend the metal by building units (first priority) or repair your units (second priority).

In all other cases I think you want to reclaim metal first, repair your units second, and build units third (same as the 0 metal case). Reclaiming energy would be in fourth place.

And ideally this behavior would also be applied to the auto repair/reclaim widget so that your workers and comm could do the same.
+1 / -0

3 years ago
Thanks for pointing me at widgets. I had no idea those existed! Is it OK to just use those in multi-player? I came across unit_auto_reclaim_heal_assist.lua, which seems like a big advantage when enabled. Is it fair if one player has it enabled while another doesn't? Why is it not automatically enabled? (Looks like it was disabled recently, in https://github.com/ZeroK-RTS/Zero-K/commit/d7391fdbe8e67274f11699a4a7e145ff8552d055.)

I think I might just try to modify this widget to also control caretakers, and then all construction units can use the same (hopefully smarter) behavior. I'm sure I'm missing something, but it seems like a good starting point to learn how things work.
+0 / -0

3 years ago
Woah, LuaUI/Widgets/unit_auto_patrol_nanos.lua is exactly the thing I want to modify. I imagine I can implement what I want in this widget. The alternative is to make "patrol" on construction units smarter. That presumably lives somewhere in the Spring engine itself.
+2 / -0

3 years ago
I just discovered unit_smart_nanos.lua, which contains some pretty sophisticated caretaker behavior.

I didn't read it in detail, but it looks like it at least:

1. Doesn't repair if there is insufficient metal/energy.
2. Repairs caretakers before anything else.
3. Repairs more expensive units before cheaper ones.

Why is this not the default behavior, instead of unit_auto_patrol_nanos.lua?
+1 / -0

3 years ago
Why is (unit_auto_reclaim_heal_assist) not automatically enabled? (...) Why is (unit_smart_nanos) not the default behavior, instead of unit_auto_patrol_nanos.lua?

Probably some sort of combination of being opaque, performance worries, and bugs where the automated behaviour overrides direct player commands.

Is it OK to just use those in multi-player?

Built-in: yes.
Your own: generally yes but read https://zero-k.info/Forum/Thread/31189
+0 / -0

3 years ago
The autoheal,reclaim,etc widget also breaks horrifyingly with commshare.
+1 / -0

3 years ago
That was a thread-and-a-half! I didn't read it all, but for now that's a debate I don't want to step into.

Ideally I could improve caretaker behavior with a widget that is accepted as default on by the devs. Is there a checklist/guidelines what that would entail? PLrankAdminSprung and USrankShaman listed several considerations, which seem like they ought to be manageable.

USrankShaman, how exactly does commander sharing break things? Is it simply a problem of the widgets that each individual player has enabled fighting each other?
+1 / -0
well, units do what they want basically and will neither listen to you nor your partners.
+0 / -0
Basically the nonhost players cannot issue orders to the group's constructors unless they issue rapid move orders in succession (taking 2-3s to unjam a single con).

This behavior lead me to remove the widget upon the user commsharing. However that was reverted due to a typo in the commshare gadget's improvements causing some errors to pop up when a spectator joined in. This is resolved in this pr though. GBC also breaks with commshare for the same reason.

Ideally I could improve caretaker behavior with a widget that is accepted as default on by the devs.

Better for it to be a gadget imo. Though this could lead to state clutter.
+0 / -0

3 years ago
Caretakers seem to do the following with a Patrol or Attack Move command.
  • Repair
  • Assist
  • Reclaim (ignores low priority targets such as trees)
Once they have a target they keep working on the target until it is done.

The AI for area-repair seems to mostly be a bunch of hardcoded logic based on movestate. However, builtOnly can be set by issuing the command with the CTRL option.
// don't help allies build unless set on roam
if (unit->beingBuilt && owner->team != unit->team && (owner->moveState != MOVESTATE_ROAM))

// don't help factories produce units when set on hold pos
if (unit->beingBuilt && unit->moveDef != nullptr && (owner->moveState == MOVESTATE_HOLDPOS))

// don't assist or repair if can't assist or repair
if (!ownerBuilder->CanAssistUnit(unit) && !ownerBuilder->CanRepairUnit(unit))

if (unit == owner) {
	trySelfRepair = true;
// repair stationary targets first
if (unit->IsMoving() && stationary)

if (builtOnly && unit->beingBuilt)

float dist = f3SqDist(unit->pos, owner->pos);

// avoid targets that are faster than our max speed
if (unit->IsMoving()) {
	unitSpeed = unit->speed.Length2D();
	dist *= (1.0f + std::max(unitSpeed - maxSpeed, 0.0f));

Overriding this behaviour has been on the todo list for a very long time, but nobody has gotten around to it.
+1 / -0
Ideally I could improve caretaker behavior with a widget that is accepted as default on by the devs. Is there a checklist/guidelines what that would entail?

The general requirements for a default widget (or UI/AI gadget) are as follows:
  • Do not hinder or make the UI worse for players that are unaware of the widget.
  • Pay attention to the performance cost in relation to the benefit.
  • Write a gadget instead of a widget if gadget-space allows for a cleaner solution, or perhaps to remove the decision entirely.

The UI is often made worse through clutter or when units appear to act against what the player thought they told their units to do. Note that this is based on what the player thinks, not what actually happens, so even a widget that implements good behaviour can be bad if players often get the feeling of fighting against it. This can be a challenging requirement because, taken to the extreme, you would essentially have to read the players mind. If someone puts a Caretaker down on the frontline, and gives it no orders, did they intend for it to repair damaged units or reclaim? The UI has to guess.

Here are three good ways to avoid making the UI worse:
  • Don't override direct orders from the player. If the player orders a Caretaker to reclaim while their storage is full then trust that they did it for a reason. Even if they simply did not notice their metal bar, it is probably better to let them issue the order and move on rather than have the unit appear to fight against the order.
  • If implementing an active behaviour (such as Attack Move skirmishing) then keep it as simple as possible. This allows the player to learn what to expect when they issue the command. Teaching the player what to expect when they issue a command is basically as good as mind reading.
  • Limit the AI to only handle the dumbest and most common cases. I think players are fairly happy for unit AI to step in and stop unambiguously stupid automated behaviour, but when the UI tries to be smarter it risks fighting the player. For example, automatic reclaim at full storage is obviously bad in a way that reclaiming at 80% storage is not. Being a bit stupid in the edge cases, in the pursuit of reliability, is fine.

Performance is obviously important, and it depends on what the widget does. Even a widget with a small performance impact may be no good if I can't find any evidence of the problem it solves ever coming up in real game. In my experience Spring.Get{Thing}In{Volume} can quickly result in very poor performance so I am wary of widgets that use it frequently. In gadget-space I'm wary of anything that reads or handles projectiles. The current smart nanos widget gets units within a volume so I would need to test its frequency. Also, don't use the iterators pairs or ipairs (I made a file called IterableMap to replace them if you want).

Writing a gadget instead is often a solution to one or both of the first two issues. Gadgets,
  • are latency-free,
  • have access to the entire gamestate,
  • allow for cleaner implementations of unit states, and
  • have many more callins and hooks into the engine.
The downsides are that they can be a bit harder to open up to player configuration, and their performance impact is felt for all players. Sometimes the possibility of a bit of unit AI reveals that the mechanic that necessitates the AI should not exist in the first place. In this case "writing a gadget" would be changing the game such that the widget can't exist. For example, imagine if cloak disabled when you have less than 100 energy. Someone could write a widget to manage the economy such that you don't drop below 100 energy. At this point the mechanic is rendered pointless so may have been removed in the first place.

To give some examples, the Kodachi AI widget was not included because its effectiveness was highly dependent on latency and it fought the player for control of the set target command. Similarly, the Artemis AI is vulnerable to latency and fights the player for control of firestates. Moreover, when I last checked, it uses massive Spring.GetUnitsInCylinder and similar, much cheaper, functionality exists in the target priority, overkill prevention, and "Don't fire at radar" gadgets.

Caretaker AI as a widget sounds fine if you are smart about it. It should probably be added to "Auto Patrol Nanos" since this is the only widget that handles Caretakers. The proposed widget is implementing an idle behaviour so to start with I would focus on the simplest, dumbest, behaviors. More can be added if further issues arise. I would target these two cases:
  • Don't reclaim automatically if metal storage is full.
  • Don't build automatically if your storage is empty, provided there is something to reclaim or repair nearby.
Note that "Don't build automatically" is potentially too much automation, since I expect that many players manage their build priority partially via overbuilding Caretakers near their factory. As long as it is limited to cases where Caretakers could do something else then it is probably fine.

I would try using area commands to avoid all the target selection issues. Here is an approach:
  • If you are out of metal, intermittently insert large area-reclaim and repair-only area-repair commands at the start of the queue. Insert reclaim before repair, and stagger the Caretaker updates over multiple seconds to make them naturally balance their reclaim. Skip inserting repair if you are also low on energy.
  • If your metal storage is full, replace the Caretaker patrol command with area-repair. This update could be checked faster as balancing is less useful.

When using the area commands there are two things to consider:
  • If a Caretaker is already doing an appropriate command when one of the above conditions is triggered, then modify its command queue in such a way to not interrupt its current command.
  • Make the radius of the command extremely large (if this doesn't cause bugs) because drawing circles everywhere is ugly.
+4 / -0

3 years ago
k, i reformulate:

pls make caretakers not reclaim ON THEIR OWN when you are excessing :)
+0 / -0

3 years ago
Thanks for your input, AUrankAdminGoogleFrog.

I just spent some time looking at the spring engine code itself. Implementing the changes I want would be pretty straightforward, but I can't convince myself that these changes would be desirable for every game using the spring engine. It's probably not the right place to put this code, unless somebody has a good idea for how to configure it. Adding a simple boolean that's only useful for Zero-K is probably a non-starter for the spring project.

When writing widgets, how can I differentiate between a unit that is doing something that the player explicitly requested, versus a unit that is doing something because my widget (that only wants to act on a unit when the player left it alone) told it to take an action earlier?

Your idea with area commands sounds good, but in that case I probably want to re-evaluate every 10-30 seconds. E.g. when a caretaker switches to reclaim, we need it to wake up periodically to determine whether that's still the right thing to do. It's quite possible that it should go back to assisting with construction again before it's reclaimed all the resources in an area.
+0 / -0
Page of 2 (24 records)