Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,76 @@ Until [the UI fix](https://github.com/gynt/ucp-extension-ui/pull/6) reaches the
store, import its unsigned `ui-1.0.1.zip` before this candidate. The tester bundle
includes that dependency built from commit `d3a807cfee70308f707ea81ddb92ac8b1d375bd9`.

## Quick configuration guide

In VS Code, install the **YAML extension by Red Hat**. Keep
`projectile-config.schema.json` beside your editable YAML and put this on its
first line for completion, allowed values and validation:

```yaml
# yaml-language-server: $schema=./projectile-config.schema.json
```

Use the schema from the **same module build** as your configuration. The detailed
field table below explains every supported setting; the schema also catches
misspelled names and invalid types. Additional cross-field checks run at startup.

**Automatic attack is per unit type**, under `units`, not a separate GUI category.
The only GUI control is the file picker in **Customizations ? Balance Changes**.
A setting under `Catapult` applies to all catapults (or AI-owned catapults only
with `ai_only: true`), not one individually selected catapult.

| What you want | What to edit |
|---|---|
| Change ordinary ammunition | `projectile`; omit `cow_projectile` to leave cows alone |
| Change cow ammunition separately | `cow_projectile` and `cow_count` |
| More shots in one volley | `count`; `spread` offsets additional simultaneous shots |
| Longer time between volleys | `interval`, measured in simulation ticks, not milliseconds; game speed changes real elapsed time |
| Fire only while standing | `interval_standing: 700`, `interval_moving: 0` |
| A fallback rate for other stances | `interval`; moving/standing/docked overrides take precedence; zero on an override holds fire |
| Automatically search for buildings | `targets: [buildings, units]`; first kind that finds a target wins; boulders alone do not enable building searches |
| Search distance | `range` in tiles for the module's automatic search; this is not the native manual-order range override |
| Exact aim | `inaccuracy: 0` and `spread: 0`; moving targets can still move before impact |
| An eighth-tile aim-error radius | `inaccuracy: 1`; **8 native coordinate units = 1 tile**; use whole numbers |
| Restrict changes to AI owners | `ai_only: true`; this controls whose settings change, not an autonomous-fire toggle |
| Different behavior on walls | Put sparse overrides under `on_fortification` |
| Fire near a decoration | Add an ordered `near_decorations` rule; see the GM1 example below |

This complete example replaces catapult stones with three mangonel pebbles,
leaves cow ammunition unchanged, and enables automatic building-then-unit
searches while stationary:

```yaml
# yaml-language-server: $schema=./projectile-config.schema.json
units:
Catapult:
projectile: mangonel_pebble
count: 3
interval_standing: 700
interval_moving: 0
targets: [buildings, units]
range: 30
inaccuracy: 0
spread: 0
```

Keep native animation synchronization enabled for siege engines. A short interval
cannot skip native aiming/reload/firing frames; turning or missing crew may delay
release. A human's explicit native attack order takes priority over automatic
search priorities. `cluster` adds a density threshold for AI owners.

**Build distinction:** the existing 1.8.6 tester uses omission or `{}` to leave
settings alone. The follow-up development branch adds `native` for dynamic rules,
`auto_targeting: false` to disable autonomous attacks per unit type while retaining
human manual orders, and `strict_range`. Those fields require that follow-up
build and its schema; they are not supported by the old downloadable 1.8.6 ZIP.
Neither source merging nor committing the pending movement/aiming fixes publishes
a tested release. See [VALIDATION.md](VALIDATION.md) for acceptance limits.

Only `units`, `projectiles` and `decorations` belong at the top of a projectile
file. Do not paste GitHub workflow keys (`name`, `on`) or UCP `config-sparse`
wrappers into it. Use spaces for indentation and restart the game after edits.

## Installation and use

Import `custom-projectiles-1.8.6.zip` into the launcher and enable the module and its dependencies. This unsigned local test candidate requires development module
Expand Down
4 changes: 4 additions & 0 deletions definition.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ dependencies:
ui: '^1.0.1'
meta:
version: 1.0.0
tags:
- behavior
- textures
- tools
3 changes: 3 additions & 0 deletions locale/ch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@ balance_changes: Balance Changes
config_file: 弹射物配置文件(YAML)
config_help: 将模块 ZIP 中的 vanilla-projectiles.yml 复制到 ucp/resources/custom-projectiles/,编辑副本后在此选择。省略的设置保持不变;路径为空时不做任何更改。UCP
的必需值和建议值规则适用于整个文件选择项。修改后请重新启动游戏。
tags.behavior: 行为
tags.textures: 纹理
tags.tools: 工具
3 changes: 3 additions & 0 deletions locale/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ config_help: vanilla-projectiles.yml aus dem Modul-ZIP nach ucp/resources/custom
und hier auswählen. Ausgelassene Einstellungen bleiben unverändert; ein leerer Pfad bewirkt keine Änderungen.
Die UCP-Regeln für erforderliche und vorgeschlagene Werte gelten für die gesamte Dateiauswahl. Nach Änderungen
das Spiel neu starten.
tags.behavior: Verhalten
tags.textures: Texturen
tags.tools: Werkzeuge
3 changes: 3 additions & 0 deletions locale/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ config_file: Projectile configuration file (YAML)
config_help: Copy vanilla-projectiles.yml from the module ZIP into ucp/resources/custom-projectiles/, edit the copy
and select it here. Omitted settings stay unchanged; an empty path makes no changes. UCP required/suggested rules
apply to the selected file as a whole. Restart the game after changes.
tags.behavior: Behavior
tags.textures: Textures
tags.tools: Tools
3 changes: 3 additions & 0 deletions locale/es.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ config_file: Archivo de configuración de proyectiles (YAML)
config_help: Copia vanilla-projectiles.yml del ZIP del módulo a ucp/resources/custom-projectiles/, edita la copia
y selecciónala aquí. Los ajustes omitidos no cambian; una ruta vacía no aplica cambios. Las reglas UCP de valores
obligatorios o sugeridos se aplican al archivo seleccionado en su conjunto. Reinicia el juego tras los cambios.
tags.behavior: Comportamiento
tags.textures: Texturas
tags.tools: Herramientas
3 changes: 3 additions & 0 deletions locale/fa.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ config_file: فایل تنظیمات پرتابه (YAML)
config_help: فایل vanilla-projectiles.yml را از ZIP ماژول به ucp/resources/custom-projectiles/ کپی کنید، نسخهٔ کپی
را ویرایش و اینجا انتخاب کنید. تنظیمات حذف‌شده تغییر نمی‌کنند؛ مسیر خالی هیچ تغییری ایجاد نمی‌کند. قواعد مقدار
الزامی یا پیشنهادی UCP به کل فایل انتخاب‌شده اعمال می‌شوند. پس از تغییرات، بازی را دوباره اجرا کنید.
tags.behavior: رفتار
tags.textures: بافت‌ها
tags.tools: ابزارها
3 changes: 3 additions & 0 deletions locale/fr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ config_help: Copiez vanilla-projectiles.yml depuis le ZIP du module dans ucp/res
la copie et sélectionnez-la ici. Les réglages omis restent inchangés ; un chemin vide ne modifie rien. Les règles
UCP de valeurs requises ou suggérées portent sur le fichier choisi dans son ensemble. Relancez le jeu après les
modifications.
tags.behavior: Comportement
tags.textures: Textures
tags.tools: Outils
3 changes: 3 additions & 0 deletions locale/hu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ config_help: Másold a vanilla-projectiles.yml fájlt a modul ZIP-jéből az ucp
szerkeszd a másolatot, majd válaszd ki itt. A kihagyott beállítások változatlanok; az üres útvonal nem módosít
semmit. Az UCP kötelező és javasolt értékeinek szabályai az egész kiválasztott fájlra vonatkoznak. Módosítás után
indítsd újra a játékot.
tags.behavior: Viselkedés
tags.textures: Textúrák
tags.tools: Eszközök
3 changes: 3 additions & 0 deletions locale/ru.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ config_file: Файл настроек снарядов (YAML)
config_help: Скопируйте vanilla-projectiles.yml из ZIP модуля в ucp/resources/custom-projectiles/, отредактируйте
копию и выберите её здесь. Пропущенные настройки не меняются; пустой путь не вносит изменений. Правила UCP для
обязательных и рекомендуемых значений применяются ко всему выбранному файлу. После изменений перезапустите игру.
tags.behavior: Поведение
tags.textures: Текстуры
tags.tools: Инструменты
3 changes: 3 additions & 0 deletions locale/tr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@ config_file: Mermi yapılandırma dosyası (YAML)
config_help: Modül ZIP dosyasındaki vanilla-projectiles.yml dosyasını ucp/resources/custom-projectiles/ klasörüne
kopyalayın, kopyayı düzenleyip burada seçin. Atlanan ayarlar değişmez; boş yol hiçbir değişiklik yapmaz. UCP zorunlu
ve önerilen değer kuralları seçilen dosyanın tamamına uygulanır. Değişikliklerden sonra oyunu yeniden başlatın.
tags.behavior: Davranış
tags.textures: Dokular
tags.tools: Araçlar
4 changes: 3 additions & 1 deletion tests/archive-locales.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ for(const lang of languages) {
expect(await legacyHandle.doesEntryExist('locale/')).toBe(true);
const locales=await readLocales(handle,definition.name,languages);
expect(Object.keys(locales).sort()).toEqual([...languages].sort());
expect(Object.keys(locales[lang])).toHaveLength(3);
expect(Object.keys(locales[lang]).sort()).toEqual(
['balance_changes','config_file','config_help',...definition.tags.map((tag:string)=>'tags.'+tag)].sort());
for(const tag of definition.tags) expect(locales[lang]['tags.'+tag].trim()).not.toBe('');
const ui=(await readUISpec(handle)).options;
const localized=applyLocale({ui} as any,locales[lang]);
expect(JSON.stringify(localized)).not.toContain('{{');
Expand Down
2 changes: 2 additions & 0 deletions tests/test_projectiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,8 @@ def visit(node):
def test_every_gui_string_is_localized_and_categories_match_legacy(self):
options=(ROOT/'options.yml').read_text(encoding='utf-8')
keys=set(re.findall(r'\{\{([^}]+)\}\}',options))
definition=yaml.safe_load((ROOT/'definition.yml').read_text(encoding='utf-8'))
keys.update('tags.' + tag for tag in definition.get('tags', []))
languages=yaml.safe_load((ROOT.parent/'UCP3-GUI/resources/lang/languages.yaml').read_text(encoding='utf-8'))
self.assertEqual(set(languages),{p.stem for p in (ROOT/'locale').glob('*.yml')})
for lang in languages:
Expand Down
2 changes: 2 additions & 0 deletions tools/generate_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ def generate():
document=yaml.safe_dump(dict(meta=dict(version='1.0.0'), options=build_options()),
allow_unicode=True, sort_keys=False, width=110)
keys=set(re.findall(r'\{\{([^}]+)\}\}', document))
definition=yaml.safe_load((ROOT/'definition.yml').read_text(encoding='utf-8'))
keys.update('tags.' + tag for tag in definition.get('tags', []))
for lang in LANGUAGES:
locale=yaml.safe_load((ROOT/'locale'/f'{lang}.yml').read_text(encoding='utf-8'))
if set(locale)!=keys or any(not isinstance(v,str) or not v.strip() for v in locale.values()):
Expand Down