Skip to content

Commit

Permalink
feat!(selection): Make IdMatches select filter OR composable (#1062)
Browse files Browse the repository at this point in the history
* feat!(selection): Make IdMatches select filter OR composable

Previously IdMatches would compose as AND, e.g.:

`_IdMatches_armpw_IdMatches_armflea` would keep only units that have the
unitdef name armpw AND armflea at the same time, which was silly.

This commit makes it so that the previous filter keeps only units that
have unitdef name armpw OR armflea.

Respectively `_Not_IdMatches_armpw_Not_IdMatches_armflea` maintains
previous behavior, keep only units that don't have unitdef name armpw OR
armflea.

Co-authored-by: sprunk <[email protected]>

* Add changelog stuff

* Make idMatchesSet a non member variable

* Allocate idMatches in local scope

---------

Co-authored-by: sprunk <[email protected]>
  • Loading branch information
badosu and sprunk authored Mar 18, 2024
1 parent 48bc18e commit 34c88a8
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 4 deletions.
5 changes: 4 additions & 1 deletion doc/site/articles/select-command.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,18 @@ Here are the filters. Note that "units" generally means both buildings and mobil

### `IdMatches_<string>`

Keep only units whose internal name (unitDef name) matches `<string>` **exactly**.
Keep only units whose internal name (unitDef name) matches `<string>` **exactly**. Differently from other filters further invocations will match units matching one name **OR** another.

- `IdMatches_armcom`: keep only Armada Commanders (internally named `armcom`).
- `IdMatches_armcom_IdMatches_armflea`: keep only Armada Commanders or Fleas.
- `Not_IdMatches_armcom_Not_IdMatches_armflea`: keep all units that are not Armada Commanders or Fleas.

### `Idle`

Keep only units that are currently idle, i.e. do not have any active order.

### `InGroup_<int>`

Keep only units that are in control group `<int>`.

- `Not_InGroup_<int>`: keep all units that are **not** currently in control group `<int>`.
Expand Down
3 changes: 2 additions & 1 deletion doc/site/changelogs/running-changelog.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ These are the entries which may require special attention when migrating:
* instead of 4 default explosion decals in basecontent, there's 2 new normal-mapped ones. Might want to produce more for variety and/or check your `gamedata/resources.lua` to see if you're referencing them.

# Features
* The `select` action now composes `IdMatches` filters as *OR* statements see [The select command]({{ site.baseurl }}{% link articles/select-command.markdown %}#idmatches_string) for further reference.
* added a new optional boolean parameter to `Spring.GetUnitHeading`, default false. If true, the value returned is in radians instead of the TA 16-bit angular unit.
* added a new callin, `GameFramePost(number frame)`. This is the last callin in a sim frame (regular `GameFrame` is the first).
Use for batching events that happened during the frame to be sent to unsynced for use in draw frames before the next sim frame.
Expand Down Expand Up @@ -88,4 +89,4 @@ Use for example to unattach units from the grounds and trigger skidding.
* fix an issue where a unit that kills something via `SFX.FIRE_WEAPON` would sometimes continue to shoot at the location it was standing at at the time.
* fixed `VFS` functions that deal with file paths being exceedingly slow.
* fixed `Spring.GetTeamUnitsByDefs` revealing much more information than it should.
* `Spring.GetUnitWeaponState(unitID, "burstRate")` now correctly returns fractional values (was only full integers before).
* `Spring.GetUnitWeaponState(unitID, "burstRate")` now correctly returns fractional values (was only full integers before).
30 changes: 28 additions & 2 deletions rts/Game/UI/SelectionKeyHandler.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
/* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */

#include <fstream>

#include "SelectionKeyHandler.h"
#include "Game/Camera/CameraController.h"
#include "Game/Camera.h"
Expand Down Expand Up @@ -198,6 +196,7 @@ namespace {
},
)

// Only used for the _Not_IdMatches case, the positive case is handled differently
DECLARE_FILTER_EX(IdMatches, 1, unit->unitDef->name.compare(name) == 0,
std::string name;
void SetParam(int index, const std::string& value) override {
Expand Down Expand Up @@ -389,6 +388,10 @@ void CSelectionKeyHandler::DoSelection(std::string selectString)

ReadDelimiter(selectString);

// Store positive (not prefixed by Not) IdMatches tokens for OR composition at end
// (can't be done serially, like all others)
std::unordered_set<std::string> idMatchesSet;

while (true) {
std::string filter = ReadDelimiter(selectString);

Expand All @@ -405,6 +408,16 @@ void CSelectionKeyHandler::DoSelection(std::string selectString)
filter = ReadToken(selectString);
}

/* Positive IdMatches use OR instead of AND,
* because that is intuitive and it's not possible
* for a unit to match two names anyway. */
if (filter == "IdMatches" && !_not) {
ReadDelimiter(selectString);

idMatchesSet.insert(ReadToken(selectString));

continue;
}

using FilterPair = Filter::Pair;

Expand Down Expand Up @@ -436,6 +449,19 @@ void CSelectionKeyHandler::DoSelection(std::string selectString)
}
}

if (!idMatchesSet.empty()) {
auto ui = selection.begin();
while (ui != selection.end()) {
if (idMatchesSet.contains((*ui)->unitDef->name)) {
++ui;
} else {
// erase, order is not relevant
*ui = selection.back();
selection.pop_back();
}
}
}

ReadDelimiter(selectString);
s = ReadToken(selectString);

Expand Down

0 comments on commit 34c88a8

Please sign in to comment.