soundrts-1.4.5.1

10 views
Skip to first unread message

修君

unread,
Jul 9, 2026, 1:13:34 PM (16 hours ago) Jul 9
to soundRTSChat
.. contents::


1.4.5.1
--------

Bug fixes and voice/audio UX improvements:

**Fix: nameless fog ghost after unit suicide**

- **Symptom**: After a unit suicides, Tab-cycling targets in the same square could still select an object with no readable name.
- **Cause**: After death ``place is None``, fog-of-war memory was not cleared in time; memory objects could have a ``title`` (fog suffix) but an empty ``short_title``, yet Tab still treated them as selectable.
- **Fix**: ``perception.py`` forgets memory when ``initial_model.place is None``; units leaving perception are not memorized when ``place is None`` or when they are the player's own dead units; ``game_unit_control.py`` ``is_visible`` requires a non-empty ``short_title``.
- **Tests**: ``test_suicide_fog_ghost.py`` (corpse fog memory and ambient audio paths preserved).

**Fix: wall HP flickering up and down while attacking**

- **Symptom**: Attacking ``wall`` and other ``is_repairable`` buildings could make HP or life-change sounds rise and fall intermittently.
- **Cause**: Walls inherit ``is_repairable=True`` from buildings, so attack / repair / capture-threshold logic could interact; fog HP sync (``_sync_memory_hp_from_live``) without carrying ``previous_hp`` across perception/memory view swaps caused false life-change feedback.
- **Fix**: ``world_order.py`` / ``worldcreature.py`` / ``worldworker.py`` — enemy repairable buildings default to ``go``, imperative default to ``attack``; repair paths guarded with ``not is_an_enemy(target)``; ``game_navigation.py`` preserves HP tracking on fog updates (``_take_hp_tracking`` / ``_apply_hp_tracking``).
- **Tests**: ``test_imperative_attack.py`` (imperative attack on walls).

**Improvement: unit behavior voice descriptions**

- After Tab-selecting a target, Ctrl+Backspace or go + Ctrl+Enter confirms "attack \<target\>" instead of "go" for enemy units/buildings.
- Hotkey group select (e.g. F for footmen): "You control N footmen attacking the town hall"; if moving while fighting, appends "go to c6".
- **Code**: ``clientgameentity/base.py`` ``_attack_action_title_msg``; ``properties.py`` ``orders_txt``; ``game_orders.py`` ``_say_validate_confirmation`` / ``_say_default_confirmation``; ``game_unit_control.py`` ``say_group``.
- **Tests**: ``test_attack_orders_txt.py``, ``test_imperative_attack.py``.

**Improvement: layered battle shouts**

- Three layers: ``shout_bg`` (battlefield background), ``shout_unit`` (unit voice), ``shout_event`` (first clash / charge / crit highlights); global and per-square cooldowns; ``formation_sound_queue`` staggers bursts so shouts do not stack with hit sounds in the same frame.
- **Code**: ``battle_shout_audio.py``, ``combat.py``, ``formation_sound_queue.py``.
- **Docs**: ``mod/battle-shouts.rst``.
- **Tests**: ``test_battle_shout_audio.py``.

**Improvement: P0–P2 audio priority scheme**

- **P0 ambient** (negative to low positive, e.g. -20, -10): footsteps, looping ambience, background shouts; may be preempted by higher layers.
- **P1 combat** (0–14, ``shout_combat_priority`` scales with headcount): hits, wounds, unit shouts.
- **P2 alerts** (10–16): level-up, morph, event shouts; kept when channels are scarce.
- **Code**: ``lib/sound.py`` ``SoundManager.find_a_channel`` preempts lower-priority sources; ``audio.py`` footsteps at ``priority=-10``; TTS stays on channel 0.

**Attempted to fix the Windows and Mac builds on GitHub CI, thanks to fcnjd for the contribution.

github

https://github.com/tuohai/soundrts-ultimate-version

Gabriele Battaglia

unread,
Jul 9, 2026, 1:25:53 PM (16 hours ago) Jul 9
to soundr...@googlegroups.com
Hi,

Thanks for these updates! You are doing a great job with this game.

I wanted to ask if there is a plan to localize the game into different languages. I'm not sure if the Italian translation is currently up to date, but I suspect it might need some work.

Since I also develop in Python, I wanted to suggest a method I use daily in my projects: you could easily create a tool that uses Google APIs to automatically translate all the strings in the project. It’s a very efficient way to handle localization.

Thanks for your time and for all your hard work.

Best regards,

Gabriele Battaglia

Inviato dalla nave Gabryphone17ProMax.

--
You received this message because you are subscribed to the Google Groups "soundRTSChat" group.
To unsubscribe from this group and stop receiving emails from it, send an email to soundrtschat...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/soundrtschat/3df153df-3c2b-49a3-bee4-afeb2c7e845en%40googlegroups.com.

修君

unread,
2:45 AM (3 hours ago) 2:45 AM
to soundRTSChat
ok, I will try.

Gabriele Battaglia

unread,
3:04 AM (2 hours ago) 3:04 AM
to 修君, soundr...@googlegroups.com

Il 10/07/2026 08:45, 修君 ha scritto:
> ok, I will try.


Here is the code:


import os
import re
import sys
import time

try:
    import polib
    from deep_translator import GoogleTranslator
except ImportError:
    print("Errore: Librerie mancanti. Installa con: pip install polib
deep_translator")
    sys.exit(1)


def translate_po_file(po_file_path, target_lang):
    """
    Traduce tutte le stringhe non ancora tradotte in un file .po,
    preservando i segnaposto tipo {player} o {t}.
    """
    if not os.path.exists(po_file_path):
        print(f"Errore: File {po_file_path} non trovato.")
        return

    print(f"Caricamento file: {po_file_path}...")
    po = polib.pofile(po_file_path)
    translator = GoogleTranslator(source="it", target=target_lang)

    # Regex per trovare i segnaposto tipo {nome_variabile}
    placeholder_regex = re.compile(r"\{[^}]+\}")

    untranslated_entries = [e for e in po if not e.msgstr and e.msgid]
    total = len(untranslated_entries)

    if total == 0:
        print("Tutte le stringhe sono già tradotte.")
        return

    print(
        f"Trovate {total} stringhe da tradurre in '{target_lang}'.
Inizio processo..."
    )

    count = 0
    for entry in untranslated_entries:
        original = entry.msgid

        # 1. Protezione segnaposto
        placeholders = placeholder_regex.findall(original)
        protected_text = original
        for i, ph in enumerate(placeholders):
            protected_text = protected_text.replace(ph, f"VAR{i}QQ")

        try:
            # 2. Traduzione
            translated_text = translator.translate(protected_text)

            if translated_text is None:
                print(f"\nAvviso: Traduzione vuota per '{original}'.")
                translated_text = ""

            # 3. Ripristino segnaposto
            for i, ph in enumerate(placeholders):
                translated_text = translated_text.replace(f"VAR{i}QQ", ph)

            entry.msgstr = translated_text
            count += 1

            if count % 10 == 0:
                print(f"Progresso: {count}/{total}...")
                # Piccolo delay per non farsi bannare dall'API gratuita
                time.sleep(0.5)

        except Exception as e:
            print(f"\nErrore durante la traduzione di '{original}': {e}")
            entry.msgstr = ""  # Previene crash di polib
            continue

    # Salvataggio
    po.save()
    print(f"\nLavoro completato! Tradotte {count} stringhe.")
    print(f"File salvato: {po_file_path}")
    print("Ricorda di compilare il file .mo usando: pybabel compile -d
locales")


if __name__ == "__main__":
    if len(sys.argv) < 3:
        print("Uso: python translator_tool.py <percorso_file_po>
<codice_lingua>")
        print(
            "Esempio: python translator_tool.py
locales/pt/LC_MESSAGES/messages.po pt"
        )
    else:
        path = sys.argv[1]
        lang = sys.argv[2]
        translate_po_file(path, lang)

Reply all
Reply to author
Forward
0 new messages