1.5.1.3
-------
**Added: 14 late-added AoE2 Definitive Edition civilizations**
- **Problem**: The 14 civilizations added later to AoE2 DE (Goths, Persians, Slavs, Cumans, Huns, Koreans, Mayans, Hindustanis, Spanish, Magyars, Poles, Italians, Bohemians, Turks) had only scattered field overrides in ``res/rules.txt`` in 1.5.1.2 — no complete ``def`` blocks, no tech tree, no unique units, no unique techs, no faction voice. Players picking them in DE-style campaigns or random maps would immediately notice missing units, missing techs, and silent voice.
- **Fix**: In ``mods/aoe2/rules.txt``, fully implement the 14 civilizations (38 def blocks total: 14 civ identity + 9 shared building/tech defs) with all ``on_phase`` bonuses (dark/feudal/castle/imperial/team), ``can_research``, ``can_train``, ``is_a`` relations per the DE design. Unique units (Huskarl, War Elephant, Boyar, Kipchak, Tarkan, War Wagon, Plumed Archer, Conquistador, Magyar Huszar, Winged Hussar, Genoese Crossbowman, Hussite Wagon, Janissary) live in ``mods/aoe2/ui/style.txt`` as ``def <unit>`` + ``title 9014+``, fully aligned with the original 13 civs' "unit + elite unit sharing title 8000+" design. Villagers take the "``def <civ>_villager`` independent def + ``title 8159`` shared villager label" approach: the 14 civs' ``def`` blocks give ``rules.txt``'s ``peasant <civ>_villager`` / ``on_phase`` references their own type_name, but voice playback uniformly uses 8159 ("villager"). ``mods/aoe2/ai.txt`` +2086 lines covers the 14 civs' dark/feudal/castle/imperial AI plans (villager allocation, military squads, research priorities). All 13 ``ui-xx/tts.txt`` mirror the 14-civ intros (IDs 8700-8713) and unique-unit/elite shared labels (IDs 8334-8345).
- **Scope**: ``mods/aoe2/rules.txt``, ``mods/aoe2/ai.txt``, ``mods/aoe2/ui/style.txt``, ``mods/aoe2/ui/tts.txt``, 13 ``mods/aoe2/ui-xx/tts.txt`` files.
**Added: 5 new rules-driven fields (end-to-end from engine to modding docs)**
- **Problem**: In 1.5.1.2 the engine had no rules-driven entry point for these 5 DE / late-added mechanics — modders had to write hardcoded workarounds in ``mods/aoe2/rules.txt`` (e.g. ``peasant_mod`` + ``worldaction`` subclassing), which were brittle and not reusable across mods: (1) DE Kipchak / Conquistador-style "fire while moving"; (2) DE guard units counterattack on sight (without auto-chasing across squares); (3) Cuman Mercenaries unique tech "build a 2nd Town Center in Feudal Age"; (4) Huns Atheism "relics never lost when monastery destroyed"; (5) DE trade-cart reward bonus (Silk Road-style percentage civilization effects).
- **Fix**: 5 new rules-driven fields, all following the "``definitions.py`` declaration + engine consumer + ``mods/aoe2/rules.txt`` landing + ``modding.rst`` doc sync" full chain:
- ``can_fire_on_move`` (``int`` 0/1, default 0): when a ranged unit on a ``MoveAction`` encounters an enemy inside ``rdg_range``, it does **not** stop to aim; it keeps closing the gap while auto-aiming at the nearest hostile in range. Implemented in ``world_movement.py`` (move-collision branch) + ``worldaction.py`` (new ``_fire_on_move_if_applicable``). DE Kipchak / Conquistador use this field.
- ``agro_on_sight`` (``int`` 0/1, default 1, on a unit ``def``): a guard unit (``ai_mode == "guard"``) with ``formations 1`` that spots an enemy takes ``_stand_ground_target()`` then ``_attack``, but does **not** chase across squares on its own; the ``last_attacker`` counterattack fallback is preserved. DE Goths / Teutons and other "stand ground on contact" civs use this field. (Already documented in 1.5.0.x modding.rst hunting section; this release only re-uses it.)
- ``allow_extra_town_center`` (``int`` 0/N, default 0, on an ``upgrade`` ``def``): after research, ``Upgrade.upgrade_player`` bumps ``player.town_center_max`` by N (default 1). Future TC build-gating reads ``player.town_center_max``; default 1 (unchanged). DE Cuman Mercenaries uses this field.
- ``relic_loss_immunity`` (``int`` 0/1, default 0, on an ``upgrade`` ``def``): after research, ``Upgrade.upgrade_player`` sets ``player.relic_loss_immunity = 1``; the monastery-loss path in ``world_relic.py`` consults this flag and keeps relics on hit. DE Huns Atheism uses this field.
- ``trade_reward_bonus_pct`` (``float``, percentage, on an ``upgrade`` ``def`` or ``on_phase`` chain): ``attribute_effects.AttributeEffectsMixin`` parses it as a per-player ``player.trade_reward_bonus_pct`` list (additive), consulted at market / trade-cart settlement. DE Italians Silk Road and similar trade-bonus civ effects use this field.
- **Scope**: ``soundrts/definitions.py`` (declarations), ``soundrts/worldunit/world_movement.py``, ``soundrts/worldunit/world_ai_decision.py``, ``soundrts/worldaction.py``, ``soundrts/worldupgrade/base.py``, ``soundrts/worldupgrade/attribute_effects.py``, ``soundrts/worldupgrade/effect_bonus_parse.py``; ``doc_src/src/zh/mod/modding.rst`` (4 new field write-ups with 4 ``def`` examples); ``mods/aoe2/rules.txt`` (14-civ field landings).
**Fix: Treaty-mode opening alerts "Treaty X minutes / Treaty in effect, no attacking" got cut off**
- **Problem**: In 1.5.1.2, ``srv_quit`` called ``voice.silent_flush()`` on exit, and ``silent_flush`` semantics is "drop the queue but do **not** wait for the remaining speech to finish" — which caused two visible bugs: (1) in treaty-mode openings, "Treaty 5 minutes / Treaty in effect, no attacking" was silently dropped by the silent-flush path right after the ``objective`` / faction voice, so players never heard the treaty alert before entering the game; (2) when players quit a game (saving progress), the "progress saved" alert was likewise swallowed, and the stats screen that follows ``_record_stats`` arrived on the audio device just as the world was tearing down, so the order of voice became garbled.
- **Fix**: (1) ``srv_quit`` now uses ``voice.flush()`` (drain the queue synchronously) before exiting, so the "progress saved" alert finishes in quit-with-save flows; (2) treaty-mode openings in ``srv_start_game`` synchronously enqueue the treaty alert ``TREATY + minutes + MINUTES + TREATY_ACTIVE`` and call ``voice.flush(interruptible=True)`` after the ``objective`` / faction voice but before ``world.loop`` starts — the player can press any key to skip but by default hears the full "Treaty X minutes, no attacking" announcement; (3) after ``game._record_stats`` and before ``game.post_run()``, call ``voice.flush()`` so the last segment (quit-saved or post-game voice) finishes before the world tears down.
- **Scope**: ``soundrts/clientgame/game_interface_base.py`` (``srv_quit``, ``srv_start_game``, ``post_run`` paths).
code:
https://github.com/tuohai/soundrts-ultimate-versiongame:
https://github.com/tuohai/soundrts-ultimate-version/releases/download/v1.5.1.3/soundrts-1.5.1.3-windows.zipdiscord server:
https://discord.gg/4pBJU8TKJ