Skip to content

Fix wrongly shown soldiers & Refactor HQ start wares and inventory handling#1910

Merged
Flow86 merged 19 commits into
Return-To-The-Roots:masterfrom
Flamefire:soldier-inventory
Apr 8, 2026
Merged

Fix wrongly shown soldiers & Refactor HQ start wares and inventory handling#1910
Flow86 merged 19 commits into
Return-To-The-Roots:masterfrom
Flamefire:soldier-inventory

Conversation

@Flamefire

Copy link
Copy Markdown
Member

When soldiers in the leave queue get cancelled, e.g. because a fight
started so we re-add all leaving aggressive defenders, the counts of
soldiers gets off:

  • Adding a soldier to leave queue will remove it from the real count
    only, not the visual count: He is still in there
  • When he is removed from the queue after going out the visual count is decreased
  • When adding it back before he left we must not increase the visual count

Currently it checked if the soldier is in the leave queue but at that
point he isn't anymore so the check never succeeds.

Add new callback to notify when a figure has left the queue.

Fixes #1907

I added tests for this issue and possible related issues I could see by inspecting the called code.
For that some additional changes were required:

  1. In replay mode the "pause" command needs to be ignored. We store that as a "GameCommand" so jumping through the provided replay stopped in the middle which confused the remaining code which looked like the whole game froze (some code assumed it is still fast-forwarding)
  2. For testing I needed a HQ with a given number of soldiers but adding start wares when creating the HQ instance means I get some unknown number of soldiers that I could not remove easily. So factor out a method for adding start wares and do it where and only when we need to (at the start of the game). This also avoids the "issue" where the place-HQ cheat added wares which it shouldn't
  3. Adding wares and/or people to warehouses was awkward: Through Inventory via AddGoods. So you needed to create an "inventory" of soldiers to pass to AddGoods?
    GoodsAndPeopleCountsis now both aGoodCountsandPeopleCountsallowing them to be used as required. RenamedAddGoodsto 3AddToInventory` methods to avoid parts of the work if only people or goods are added.
    Factory functions for adding single job/people types

The additional tests yielded further changes to address this:
When a defender is called it stops leaving attackers and aggressive defenders (interception enemy attackers) from leaving the house.
The callback to provide a defender will use leaving soldiers if necessary. So we can stop them first to always have those we will stop anyway.
As that method to stop them was hard to name and was now only called in a single place with assumptions at both sides I eventually just inlined it.
And it turns out the check for figureState == jobState isn't enough: It would also trigger for trade donkeys.
So check for (active) soldiers directly.

WEIRD: When we call a defender we cancel all attacking soldiers (normal attackers and aggressive defenders) but when a defender is called it is still possible that new aggressive defenders are called and leaving. But it seems this is the original behavior. cc @Spikeone after Discord discussion

This is especially important during skipping which expects that each
gameframe is actually executed.
@Flamefire Flamefire requested a review from Flow86 April 6, 2026 12:37
When soldiers in the leave queue get canceled, e.g. because a fight
started so we re-add all leaving aggressive defenders, the counts of
soldiers gets off:

- Adding a soldier to leave queue will remove it from the real count
  only, not the visual count: He is still in there
- When he is removed from the queue after going out the visual count is decreased
- When adding it back before he left we must not increase the visual count

Currently it checked if the soldier is in the leave queue but at that
point he isn't anymore so the check never succeeds.

Add new callback to notify when a figure has left the queue.
When we clear the inventory or start with an empty one there is no need
to call this.
`GoodsAndPeopleCounts` is now both a `GoodCounts` and `PeopleCounts`
allowing them to be used as required.
Refactor the misnamed `AddGoods` to 3 `AddToInventory` methods to avoid
parts of the work if only people or goods are added.
Ensure visibility of write-accessors for `Inventory` is protecting
against mistakes e.g. with armored soldier counts.
@Flamefire Flamefire force-pushed the soldier-inventory branch 2 times, most recently from 270380f to d9a95b2 Compare April 6, 2026 14:42
This is not always desired, especially in the tests.
So add an extra method for that to allow adding only wares/people when desired.
…roops

When a defender is requested then
a) Leaving agg. defenders are stopped
b) A defender is chosen from remaining troops

This might "convert" and agg. defender to a defender.
As this is kind of a sub-case of the existing one, merge them with a
data-param.
Similar the handling for warehouses and military buildings needs to be
checked, so make that another param.
Soldiers are stopped when a defender is coming out.
If no defender is found the leaving soldiers are checked.
So it is better to first stop the leaving soldiers and then choose from
all of them.

Also remove `nofAggressiveDefender::NeedForHomeDefence` which is a
wrapper to `InformTargetsAboutCancelling` only used in a single place
which is now redundant.
The check assumes only active soldiers start in "Job-state".
This state is set when a figure arrived at its goal, and immediately for
those not having a "fixed" goal, like attacking soldiers.
But it also applies to e.g. trade-donkeys.

So check for soldiers directly.
Also rename `NoDefender` to `ResetDefender` to clarify meaning and
remove the check for leaving defenders which cannot be met:
The method is called before the defender is called.
This is so specific to the call site that it should be inline which
explains the reasoning better.
@Flamefire Flamefire force-pushed the soldier-inventory branch from d9a95b2 to 8d86aac Compare April 6, 2026 17:10
- Pin used actions
- Add explicit permissions
- Update actions where required & possible for Node 20 deprecation
Comment thread libs/s25main/gameTypes/Inventory.h
Comment thread libs/s25main/world/MapLoader.cpp Outdated
Comment on lines +82 to +85
std::vector<MapPoint> hqPositions = hqPositions_;
if(world_.GetGGS().randomStartPosition)
RANDOM_SHUFFLE2(hqPositions, 0);
if(!PlaceHQs())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

that RANDOM_SHUFFLE2 only changes the copy of the hqPoisitions - not the ones used then in PlaceHQs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh, that was supposed to go one method deeper, yes.
Fixed

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I still have one question here. hqPositions_ is now not "in sync" with the positions "used" by PlaceHQs later.

I know its initialized via PlaceObjects and then read here in PlaceHQs. I only ask since its maybe "later" used and creates weirdness since they're not the positions used to place?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That method is static and accepts the HQ positions as a parameter, so it doesn't care about the member variable.
If this was an issue it would already have been: We didn't shuffle the member variable before either, only a copy (the parameter passed to the static method)

@Flamefire Flamefire Apr 7, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I renamed the method to get HQ positions from the loader to reflect what it actually returns and added a test which would have detected the shuffle issue

@Flamefire Flamefire force-pushed the soldier-inventory branch from 2f50d23 to 667b4b3 Compare April 7, 2026 17:51
@Flamefire Flamefire force-pushed the soldier-inventory branch from 667b4b3 to 4c001d1 Compare April 7, 2026 18:44
@Flamefire Flamefire force-pushed the soldier-inventory branch from 4c001d1 to a411a93 Compare April 7, 2026 18:48
@Flow86 Flow86 merged commit 58cb947 into Return-To-The-Roots:master Apr 8, 2026
19 of 20 checks passed
@Flamefire Flamefire deleted the soldier-inventory branch April 8, 2026 18:35
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.

Inventory Calculation gone wrong

2 participants