1. config.cpp: replace angle quotes with typographic single quotes
2. config.hpp: typo fix
3. widget_definition.cpp, window_builder.cpp: show id for window/widget
4. wml_exception: implement missing tag message, use typographic single quotes in missing key/tag messages
* Death Squire optional advancement and damage nerf
The existing {ENABLE_DEATH_KNIGHT} macro allows Revenants to advance to Death Knights. The newly core-ed Death Squire also advances to Death Knight, but has no {ENABLE_DEATH_SQUIRE} macro.
I suggest adding a new {ENABLE_DEATH_SQUIRE} macro, while keeping the old {ENABLE_DEATH_KNIGHT} for backwards compatibility.
I also propose reducing the Death Squire's attack from 8x4 to 9x3, to make the Revenant a more competitive advancement.
Who's the current maintainer of SotA? Should {ENABLE_DEATH_SQUIRE} be used there, or should we continue using {ENABLE_DEATH_KNIGHT}?
Hejnewar, if you approve, what cost should the Death Squire be after this change?
* Reduce Death Squire damage from 8x4 to 9x3
* Update Skele_Death_Squire.cfg
Without the change build fails on upcoming `gcc-15` as:
In file included from src/desktop/paths.cpp:20:
src/filesystem.hpp:232:13: error: 'uint8_t' was not declared in this scope
232 | std::vector<uint8_t> read_file_binary(const std::string& fname);
| ^~~~~~~
Using four abilities instead of two means the C++ checking_tag
mechanism needs to handle multiple values, or needs to be supported
by recursion counting.
The branching test lets one level of recursion finish, and then tries
to go deeper. This tests for bugs where a recursion detection tool in
the engine gets its count reset when exiting one level of recursion.
This adds a recursion counter which runs on a per-weapon basis; if the
recursion limit is reached then the last level of recursion is assumed
to have returned false. A warning is printed (with error severity).
At the user-visible layer, that gives the following logic:
* Abilities that depend on another ability being active will be
inactive.
* Pairs of abilities, where X depends on Y being inactive and Y
depends on X being inactive, will end up with them both inactive.
The limit is defined by ATTACK_RECURSION_LIMIT in attack_type.cpp.
The minimum for passing all the unit tests is 2, but I've left some
headroom by setting it to 4.
With the recursion limit set to 1, the following tests fail, which
seems reasonable because they're checking that the special is on
a particular type of weapon.
* event_test_filter_attack_specials
* event_test_filter_attack_opponent_weapon_condition
* event_test_filter_attack_student_weapon_condition
Output from the four_cycle_recursion_branching test:
```
error unit: Recursion limit reached for weapon 'melee_attack' while checking filter 'special_type_active = parry'
error unit: Recursion limit reached for weapon 'melee_attack' while checking filter 'special_type_active = poison'
error unit: Recursion limit reached for weapon 'melee_attack' while checking filter 'special_type_active = damage'
```
There's deduplication to prevent the logfiles getting spammed during
AI turns. As implemented, the two tests mentioned below print 3
messages each; these tests are added in separate commits because the
tests are shared between PRs while we discuss the right mechanism for
guarding against recursion.
With no rate limit:
* four_cycle_recursion_branching prints 1280 logs
* event_test_filter_attack_student_weapon_condition prints 320
With a rate limit via a flag that's reset in recursion_guard's destructor:
* four_cycle_recursion_branching prints 80 logs
* event_test_filter_attack_student_weapon_condition prints 160
Resetting the flag in specials_context_t's destructor gets better numbers,
but splits the logic across .cpp files. I think the trade-off isn't worth it:
* four_cycle_recursion_branching prints 40 logs
* event_test_filter_attack_student_weapon_condition prints 132
Co-authored-by: newfrenchy83 <eric.belleux@gmail.com>