Compare commits
158 Commits
build-8
...
v0.4.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
| c525feb465 | |||
| e7934a1ebb | |||
| 8fc10c07a1 | |||
| 0b645d77e9 | |||
| 56093b3a55 | |||
| 1261c30724 | |||
| a592ece6e3 | |||
| a47812aadd | |||
| 6f7b041c8b | |||
| dc36d67eb7 | |||
| d45629ae29 | |||
| 61aaa0bf35 | |||
| 9217f9a5cc | |||
| 46d2274c8d | |||
| 325119b726 | |||
| 0937ac9e05 | |||
| ead87343d2 | |||
| abf3d76fdb | |||
| e6a7000440 | |||
| cec7257ea1 | |||
| 503e6e29af | |||
| 675967ce42 | |||
| 632061d2ad | |||
| 56339b076e | |||
| c1a595b773 | |||
| 914f723d1f | |||
| d45d59df9f | |||
| cfae0c1253 | |||
| 53669887dd | |||
| 4c7c887917 | |||
| daa9ed7cbc | |||
| 1553976484 | |||
| 0c870c10d5 | |||
| eea1b3692e | |||
| cb73cb7ccc | |||
| 375fdf2d80 | |||
| 63aefc7621 | |||
| d410e2c92d | |||
| ab737e7390 | |||
| 3b470ddc8a | |||
| cbd6067e8f | |||
| cc48302b36 | |||
| 1f28f8361f | |||
| 10f24b5df8 | |||
| 293236490b | |||
| bf8551e1a8 | |||
| 3afebae6bd | |||
| 326817db9a | |||
| 8f5603847e | |||
| bd091e60df | |||
| 296a64ffaa | |||
| a41a4d17cf | |||
| 692ae77af7 | |||
| 19c53fda4c | |||
| aab3e784b8 | |||
| 8d5d0d1dd7 | |||
| 910f02ca92 | |||
| 4dddea2e8c | |||
| b0085cb3e8 | |||
| e9fbaba1d6 | |||
| bb931ab95c | |||
| d3378b37a2 | |||
| e70d7a667b | |||
| 6e2d661b39 | |||
| a7b8cb980a | |||
| ce76f20074 | |||
| e38ca0d9ab | |||
| e3ca3fbd8c | |||
| 44a023006d | |||
| f3985d5363 | |||
| 6b6293f353 | |||
| 932a8b7761 | |||
| 143d9b2858 | |||
| b377a5d8d2 | |||
| 9e1e0f5543 | |||
| 88f940546b | |||
| 0df225a2de | |||
| 26b258f00f | |||
| 952a10f360 | |||
| c775f31544 | |||
| c96676f3e5 | |||
| a7d400ea7e | |||
| 2689d933e7 | |||
| 966b52f9fc | |||
| d4adbb1e47 | |||
| e84a5e9fa7 | |||
| c4b72da3ff | |||
| ac866d64a0 | |||
| 5620347ffe | |||
| 4a3500fdc0 | |||
| dedb0213c7 | |||
| ad96018c0a | |||
| efb2f20e58 | |||
| 29b047abaf | |||
| 814f8c7e83 | |||
| f28e68db62 | |||
| 63972995a1 | |||
| 5ad6ea8ad9 | |||
| 657d8f16c6 | |||
| e7c00267b1 | |||
| 5f07027faa | |||
| 4761ae7d70 | |||
| afe320786d | |||
| 733dc07863 | |||
| 5860c5009f | |||
| cd3c4ed76d | |||
| 31d967b036 | |||
| 1a693763fd | |||
| 557346548a | |||
| 38e515dea9 | |||
| 57a5426e65 | |||
| ccc3e20902 | |||
| 5003376e70 | |||
| 29d43071b5 | |||
| 318a161a3d | |||
| 766b292aec | |||
| ab6b61db59 | |||
| 4d4e1682f5 | |||
| 545d4777e8 | |||
| f16bc2387f | |||
| 9bc94fa8aa | |||
| 0f5c4136f3 | |||
| 51f9609327 | |||
| 3265eb63d2 | |||
| b43d471706 | |||
| 2eb6763d03 | |||
| 438d68d50f | |||
| 76f680eba6 | |||
| 8a12225ba1 | |||
| 4bab572293 | |||
| f43fa5ee66 | |||
| 336ae9f7cc | |||
| 5f8bff31b9 | |||
| 3ced60e84f | |||
| 6f3fc644bc | |||
| 9075717872 | |||
| e3148bf458 | |||
| d0b9498aaf | |||
| 8065f2225c | |||
| a8551b5c28 | |||
| f443f0a94a | |||
| e33daac636 | |||
| 661c852bbf | |||
| 428713d4bc | |||
| 5839da129a | |||
| ab4a7899af | |||
| 65dd84646a | |||
| ed2db8523b | |||
| 67a2a523b4 | |||
| a806635681 | |||
| 0092ec64a9 | |||
| e08fc65ac8 | |||
| 90379f6e17 | |||
| bb35975642 | |||
| f2838a8f08 | |||
| c42d97af08 | |||
| 6efec3e546 | |||
| d3359f4b39 |
@@ -4,3 +4,4 @@
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.hmod binary
|
||||
*.patch -text
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# На каждый пуш: только мод (.hmod, один для всех ОС) и релиз build-<номер> с ним.
|
||||
# Раннер живёт на одной машине с Gitea и другими сервисами (2 ядра, 4 ГБ): сборка мода лёгкая —
|
||||
# один поток компиляции, низший приоритет, без системных пакетов и без кэша target.
|
||||
# Игра-форк собирается отдельно и только вручную: game.yml (Linux) и desktop.yml (macOS/Windows).
|
||||
# Игра-форк здесь не собирается: только по тегу версии (release.yml, Windows) и вручную (game.yml Linux, desktop.yml macOS).
|
||||
name: build
|
||||
|
||||
on:
|
||||
@@ -20,18 +20,35 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Rust
|
||||
run: |
|
||||
curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --target wasm32-unknown-unknown
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
# Инструмент упаковки (cargo-horizon-mod без пробного запуска) — единственное, что стоит кэшировать.
|
||||
- uses: actions/cache@v3
|
||||
continue-on-error: true
|
||||
with:
|
||||
path: ~/.cargo/bin/cargo-horizon-mod
|
||||
key: tool-${{ hashFiles('HORIZON_COMMIT', 'horizon-patches/*') }}
|
||||
# Без actions/cache: кэш-сервер раннера из контейнеров задач недоступен — восстановление
|
||||
# падало по таймауту, а сохранение вешало «Complete job» на пару минут. Инструмент собирается заново.
|
||||
- name: Форк Horizon
|
||||
run: nice -n 19 python3 tools/airdef.py fork
|
||||
- name: Проверка закреплённого checkout
|
||||
run: python3 -m unittest discover -s tools -p test_engine_checkout.py
|
||||
- name: Проверка generated assets
|
||||
run: |
|
||||
python3 -m pip install --user -r tools/requirements.txt
|
||||
python3 tools/gen_catalog.py --check
|
||||
python3 -m unittest discover -s tools -p test_sound_catalog.py
|
||||
python3 tools/gen_models.py --check
|
||||
python3 -m unittest discover -s tools -p test_model_bounds.py
|
||||
- name: Тесты ядра без SDK
|
||||
working-directory: airdef-core
|
||||
run: |
|
||||
HOST=$(rustc -vV | sed -n 's/^host: //p')
|
||||
nice -n 19 cargo test --target "$HOST"
|
||||
- name: Тесты мода
|
||||
working-directory: airdef-mod
|
||||
run: |
|
||||
HOST=$(rustc -vV | sed -n 's/^host: //p')
|
||||
nice -n 19 cargo test --target "$HOST"
|
||||
- name: Мод
|
||||
run: nice -n 19 python3 tools/airdef.py mod
|
||||
- name: Релиз
|
||||
@@ -43,8 +60,10 @@ jobs:
|
||||
SHA: ${{ github.sha }}
|
||||
run: |
|
||||
set -e
|
||||
mkdir -p out && cp airdef-mod/target/horizon-mod/airdef.hmod out/airdef.hmod
|
||||
BODY=$(printf 'Сборка %s (%s).\n\n- airdef.hmod — мод, для всех ОС.\n\nИгра-форк Horizon (нужна для мода): собрать у себя — python3 tools/airdef.py all, или запустить вручную workflow game (Linux) / desktop (macOS, Windows) с этим тегом.\nКак запускать: docs/INSTALL.md.' "$TAG" "$SHA")
|
||||
mkdir -p out
|
||||
cp airdef-mod/target/horizon-mod/airdef.hmod out/airdef.hmod
|
||||
cp airdef-mod/target/horizon-mod/build-identity.json out/build-identity.json
|
||||
BODY=$(printf 'Сборка %s (%s).\n\n- airdef.hmod — мод, для всех ОС.\n\nИгра-форк Horizon (нужна для мода): собрать у себя — python3 tools/airdef.py all, или скачать из большого релиза v* (Windows), или запустить вручную workflow game (Linux) / desktop (macOS) с этим тегом.\nКак запускать: docs/INSTALL.md.' "$TAG" "$SHA")
|
||||
DATA=$(BODY="$BODY" python3 -c 'import json,os;print(json.dumps({"tag_name":os.environ["TAG"],"target_commitish":os.environ["SHA"],"name":os.environ["TAG"],"body":os.environ["BODY"]}))')
|
||||
if [ -z "$TOKEN" ]; then echo "::error::нет токена: добавь секрет RELEASE_TOKEN"; exit 1; fi
|
||||
CODE=$(curl -sS -o release.json -w '%{http_code}' -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" "$API/releases" -d "$DATA")
|
||||
@@ -54,3 +73,4 @@ jobs:
|
||||
fi
|
||||
ID=$(python3 -c 'import json;print(json.load(open("release.json"))["id"])')
|
||||
curl -sf -X POST -H "Authorization: token $TOKEN" -F "attachment=@out/airdef.hmod" "$API/releases/$ID/assets?name=airdef.hmod" > /dev/null
|
||||
curl -sf -X POST -H "Authorization: token $TOKEN" -F "attachment=@out/build-identity.json" "$API/releases/$ID/assets?name=build-identity.json" > /dev/null
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Сборки игры-форка для macOS и Windows: запускаются вручную (Actions → desktop → Run workflow)
|
||||
# с тегом готового релиза (build-N) и дописывают свои архивы в него. Нужны раннеры с метками
|
||||
# macos-latest / windows-latest (например, act_runner в режиме host на своём Маке или ПК).
|
||||
# Сборка игры-форка для macOS: запускается вручную (Actions → desktop → Run workflow) с тегом готового
|
||||
# релиза (build-N) и дописывает свой архив в него. Нужен раннер с меткой macos-latest (например, act_runner
|
||||
# в режиме host на своём Маке). Windows собирается на сервере по тегу версии: release.yml.
|
||||
name: desktop
|
||||
|
||||
on:
|
||||
@@ -9,20 +9,48 @@ on:
|
||||
tag:
|
||||
description: Тег релиза, например build-3
|
||||
required: true
|
||||
os:
|
||||
description: macos, windows или both
|
||||
required: true
|
||||
default: both
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: never
|
||||
|
||||
jobs:
|
||||
game-macos:
|
||||
if: inputs.os == 'macos' || inputs.os == 'both'
|
||||
runs-on: macos-latest
|
||||
steps:
|
||||
- name: Проверка релиза
|
||||
env:
|
||||
TOKEN: ${{ secrets.RELEASE_TOKEN || gitea.token }}
|
||||
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -e
|
||||
if ! [[ "$TAG" =~ ^build-[0-9]+$ ]]; then echo "::error::тег '$TAG' не похож на build-N"; exit 1; fi
|
||||
REL_JSON=$(mktemp)
|
||||
CODE=$(curl -s -o "$REL_JSON" -w '%{http_code}' -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG")
|
||||
if [ "$CODE" != "200" ]; then echo "::error::релиза с тегом $TAG нет (HTTP $CODE): сначала дождись сборки build.yml на main"; exit 1; fi
|
||||
python3 - "$TAG" "$REL_JSON" <<'PY'
|
||||
import json, os, sys
|
||||
tag, path = sys.argv[1], sys.argv[2]
|
||||
r = json.load(open(path))
|
||||
assets = [a.get('name') for a in (r.get('assets') or [])]
|
||||
if r.get('tag_name') != tag or r.get('name') != tag or 'airdef.hmod' not in assets:
|
||||
print('::error::релиз %s не создан build.yml (нужны имя = тег и вложение airdef.hmod): чужой релиз не трогаем' % tag)
|
||||
sys.exit(1)
|
||||
with open(os.environ['GITHUB_ENV'], 'a') as f:
|
||||
f.write('RELEASE_ID=%s\nRELEASE_SHA=%s\n' % (r['id'], r.get('target_commitish', '')))
|
||||
PY
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: refs/tags/${{ inputs.tag }}
|
||||
- name: Проверка коммита
|
||||
env:
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -e
|
||||
HEAD_SHA=$(git rev-parse HEAD)
|
||||
if [[ "$RELEASE_SHA" =~ ^[0-9a-f]{40}$ ]] && [ "$HEAD_SHA" != "$RELEASE_SHA" ]; then echo "::error::тег $TAG указывает на $HEAD_SHA, а релиз создан из $RELEASE_SHA"; exit 1; fi
|
||||
echo "собираю $TAG = $HEAD_SHA"
|
||||
- name: Rust
|
||||
run: |
|
||||
command -v cargo || curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
|
||||
@@ -42,55 +70,16 @@ jobs:
|
||||
D=airdef-macos-arm64
|
||||
mkdir -p "$D"
|
||||
cp horizon/target/fast/horizon-client horizon/target/fast/horizon-ctl out/airdef.hmod dist/play.sh docs/INSTALL.md "$D/"
|
||||
cp airdef-mod/target/horizon-mod/build-identity.json "$D/"
|
||||
cp -R docs/licenses "$D/"
|
||||
cp horizon/THIRD_PARTY_NOTICES.md "$D/licenses/HORIZON-THIRD-PARTY-NOTICES.md"
|
||||
cp horizon/crates/horizon-client/src/acoustics/THIRD_PARTY_SPR.md "$D/licenses/THIRD_PARTY_SPR.md"
|
||||
tar czf "out/$D.tar.gz" "$D"
|
||||
- name: В релиз
|
||||
env:
|
||||
TOKEN: ${{ secrets.RELEASE_TOKEN || gitea.token }}
|
||||
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -e
|
||||
ID=$(curl -sf -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG" | python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])')
|
||||
curl -sf -X POST -H "Authorization: token $TOKEN" -F "attachment=@out/airdef-macos-arm64.tar.gz" "$API/releases/$ID/assets?name=airdef-macos-arm64.tar.gz" > /dev/null
|
||||
|
||||
game-windows:
|
||||
if: inputs.os == 'windows' || inputs.os == 'both'
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: pwsh
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Rust
|
||||
run: |
|
||||
if (-not (Get-Command cargo -ErrorAction SilentlyContinue)) {
|
||||
Invoke-WebRequest https://win.rustup.rs/x86_64 -OutFile rustup-init.exe
|
||||
.\rustup-init.exe -y --profile minimal
|
||||
}
|
||||
"$env:USERPROFILE\.cargo\bin" | Out-File -Append -Encoding utf8 $env:GITHUB_PATH
|
||||
- name: Форк Horizon
|
||||
run: python tools/airdef.py fork
|
||||
- name: Игра
|
||||
run: python tools/airdef.py game
|
||||
- name: Мод
|
||||
run: |
|
||||
rustup target add wasm32-unknown-unknown
|
||||
python tools/airdef.py mod
|
||||
New-Item -ItemType Directory -Force out | Out-Null
|
||||
Copy-Item airdef-mod\target\horizon-mod\airdef.hmod out\
|
||||
- name: Архив
|
||||
run: |
|
||||
$D = "airdef-windows-x86_64"
|
||||
New-Item -ItemType Directory -Force $D | Out-Null
|
||||
Copy-Item horizon\target\fast\horizon-client.exe, horizon\target\fast\horizon-ctl.exe, out\airdef.hmod, dist\play.bat, docs\INSTALL.md $D
|
||||
Compress-Archive -Path $D -DestinationPath "out\$D.zip" -Force
|
||||
- name: В релиз
|
||||
env:
|
||||
TOKEN: ${{ secrets.RELEASE_TOKEN || gitea.token }}
|
||||
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
$h = @{ Authorization = "token $env:TOKEN" }
|
||||
$id = (Invoke-RestMethod -Headers $h "$env:API/releases/tags/$env:TAG").id
|
||||
curl.exe -sf -X POST -H "Authorization: token $env:TOKEN" -F "attachment=@out\airdef-windows-x86_64.zip" "$env:API/releases/$id/assets?name=airdef-windows-x86_64.zip"
|
||||
|
||||
# RELEASE_ID взят в шаге «Проверка релиза» (релиз build-N, созданный build.yml)
|
||||
curl -sf -X POST -H "Authorization: token $TOKEN" -F "attachment=@out/airdef-macos-arm64.tar.gz" "$API/releases/$RELEASE_ID/assets?name=airdef-macos-arm64.tar.gz" > /dev/null
|
||||
|
||||
@@ -25,7 +25,40 @@ jobs:
|
||||
FREE=$(df --output=avail -BG / | tail -1 | tr -dc 0-9)
|
||||
echo "свободно: ${FREE} ГБ"
|
||||
if [ "$FREE" -lt 15 ]; then echo "::error::на сервере свободно ${FREE} ГБ, нужно хотя бы 15: сборка игры забьёт диск и уронит Gitea"; exit 1; fi
|
||||
- name: Проверка релиза
|
||||
env:
|
||||
TOKEN: ${{ secrets.RELEASE_TOKEN || gitea.token }}
|
||||
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -e
|
||||
if ! [[ "$TAG" =~ ^build-[0-9]+$ ]]; then echo "::error::тег '$TAG' не похож на build-N"; exit 1; fi
|
||||
REL_JSON=$(mktemp)
|
||||
CODE=$(curl -s -o "$REL_JSON" -w '%{http_code}' -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG")
|
||||
if [ "$CODE" != "200" ]; then echo "::error::релиза с тегом $TAG нет (HTTP $CODE): сначала дождись сборки build.yml на main"; exit 1; fi
|
||||
python3 - "$TAG" "$REL_JSON" <<'PY'
|
||||
import json, os, sys
|
||||
tag, path = sys.argv[1], sys.argv[2]
|
||||
r = json.load(open(path))
|
||||
assets = [a.get('name') for a in (r.get('assets') or [])]
|
||||
if r.get('tag_name') != tag or r.get('name') != tag or 'airdef.hmod' not in assets:
|
||||
print('::error::релиз %s не создан build.yml (нужны имя = тег и вложение airdef.hmod): чужой релиз не трогаем' % tag)
|
||||
sys.exit(1)
|
||||
with open(os.environ['GITHUB_ENV'], 'a') as f:
|
||||
f.write('RELEASE_ID=%s\nRELEASE_SHA=%s\n' % (r['id'], r.get('target_commitish', '')))
|
||||
PY
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: refs/tags/${{ inputs.tag }}
|
||||
- name: Проверка коммита
|
||||
env:
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -e
|
||||
HEAD_SHA=$(git rev-parse HEAD)
|
||||
if [[ "$RELEASE_SHA" =~ ^[0-9a-f]{40}$ ]] && [ "$HEAD_SHA" != "$RELEASE_SHA" ]; then echo "::error::тег $TAG указывает на $HEAD_SHA, а релиз создан из $RELEASE_SHA"; exit 1; fi
|
||||
echo "собираю $TAG = $HEAD_SHA"
|
||||
- name: Системные библиотеки
|
||||
run: |
|
||||
SUDO=$(command -v sudo || true)
|
||||
@@ -45,12 +78,15 @@ jobs:
|
||||
env:
|
||||
TOKEN: ${{ secrets.RELEASE_TOKEN || gitea.token }}
|
||||
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
TAG: ${{ inputs.tag }}
|
||||
run: |
|
||||
set -e
|
||||
D=airdef-linux-x86_64
|
||||
mkdir -p "$D" out
|
||||
cp horizon/target/fast/horizon-client horizon/target/fast/horizon-ctl airdef-mod/target/horizon-mod/airdef.hmod dist/play.sh docs/INSTALL.md "$D/"
|
||||
cp airdef-mod/target/horizon-mod/build-identity.json "$D/"
|
||||
cp -R docs/licenses "$D/"
|
||||
cp horizon/THIRD_PARTY_NOTICES.md "$D/licenses/HORIZON-THIRD-PARTY-NOTICES.md"
|
||||
cp horizon/crates/horizon-client/src/acoustics/THIRD_PARTY_SPR.md "$D/licenses/THIRD_PARTY_SPR.md"
|
||||
tar czf "out/$D.tar.gz" "$D"
|
||||
ID=$(curl -sf -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG" | python3 -c 'import json,sys;print(json.load(sys.stdin)["id"])')
|
||||
curl -sf -X POST -H "Authorization: token $TOKEN" -F "attachment=@out/$D.tar.gz" "$API/releases/$ID/assets?name=$D.tar.gz" > /dev/null
|
||||
# RELEASE_ID взят в шаге «Проверка релиза» (релиз build-N, созданный build.yml)
|
||||
curl -sf -X POST -H "Authorization: token $TOKEN" -F "attachment=@out/$D.tar.gz" "$API/releases/$RELEASE_ID/assets?name=$D.tar.gz" > /dev/null
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# Большой релиз: только по тегу vX.Y.Z (git tag v0.3.0 && git push origin v0.3.0) или вручную с готовым тегом.
|
||||
# Игра-форк для Windows собирается на Linux-сервере кросс-компиляцией (x86_64-pc-windows-gnu), без Windows-раннера.
|
||||
# Сервер общий (2 ядра, 4 ГБ, Gitea и боты), поэтому контейнер ограничен: при нехватке памяти убивается сборка, а не
|
||||
# сервисы. Замер 2026-10-30: 20,5 минуты, 2 ГБ памяти постоянно в потолке, у сервера не меньше 726 МБ свободно.
|
||||
# Обычные коммиты сюда не попадают: build.yml собирает только мод.
|
||||
name: release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: Существующий тег, например v0.3.0
|
||||
required: true
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: never
|
||||
CARGO_BUILD_JOBS: "1"
|
||||
CARGO_INCREMENTAL: "0"
|
||||
CARGO_PROFILE_FAST_INCREMENTAL: "false"
|
||||
CARGO_PROFILE_FAST_DEBUG: "false"
|
||||
CARGO_PROFILE_FAST_STRIP: "true"
|
||||
TAG: ${{ inputs.tag || github.ref_name }}
|
||||
|
||||
jobs:
|
||||
windows:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
image: catthehacker/ubuntu:act-latest
|
||||
options: --memory=2g --memory-swap=4500m --cpus=1.5 --cpu-shares=128 --blkio-weight=100 --oom-score-adj=800
|
||||
steps:
|
||||
- name: Место на диске
|
||||
run: |
|
||||
FREE=$(df --output=avail -BG / | tail -1 | tr -dc 0-9)
|
||||
echo "свободно: ${FREE} ГБ"
|
||||
if [ "$FREE" -lt 12 ]; then echo "::error::на сервере свободно ${FREE} ГБ, нужно хотя бы 12"; exit 1; fi
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
ref: refs/tags/${{ inputs.tag || github.ref_name }}
|
||||
- name: Инструменты
|
||||
run: |
|
||||
SUDO=$(command -v sudo || true)
|
||||
$SUDO apt-get update -qq
|
||||
$SUDO apt-get install -y -qq --no-install-recommends gcc-mingw-w64-x86-64 g++-mingw-w64-x86-64 zip
|
||||
curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain none
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
# Версия Rust берётся из rust-toolchain.toml.
|
||||
- name: Цели Rust
|
||||
run: rustup target add x86_64-pc-windows-gnu wasm32-unknown-unknown
|
||||
- name: Форк Horizon
|
||||
run: nice -n 19 python3 tools/airdef.py fork
|
||||
- name: Мод
|
||||
run: nice -n 19 python3 tools/airdef.py mod
|
||||
- name: Игра для Windows
|
||||
working-directory: horizon
|
||||
run: |
|
||||
# Сторож: сервер важнее сборки. Память контейнера ограничена, это на случай, если её съело что-то ещё.
|
||||
( while sleep 10; do
|
||||
AVAIL=$(awk '/MemAvailable/{print int($2/1024)}' /proc/meminfo)
|
||||
DISK=$(df --output=avail -BG / | tail -1 | tr -dc 0-9)
|
||||
if [ "$AVAIL" -lt 250 ] || [ "$DISK" -lt 6 ]; then
|
||||
echo "::error::сборка остановлена: свободно ${AVAIL} МБ памяти и ${DISK} ГБ диска"
|
||||
pkill -9 cargo; pkill -9 rustc; exit 1
|
||||
fi
|
||||
done ) &
|
||||
WATCH=$!
|
||||
nice -n 19 ionice -c3 cargo build --profile fast -p horizon-client --features mods --target x86_64-pc-windows-gnu
|
||||
kill "$WATCH"
|
||||
- name: Архив
|
||||
run: |
|
||||
set -e
|
||||
D=airdef-windows-x86_64
|
||||
T=horizon/target/x86_64-pc-windows-gnu/fast
|
||||
mkdir -p "$D" out
|
||||
cp "$T/horizon-client.exe" "$T/horizon-ctl.exe" airdef-mod/target/horizon-mod/airdef.hmod dist/play.bat docs/INSTALL.md "$D/"
|
||||
cp airdef-mod/target/horizon-mod/build-identity.json "$D/"
|
||||
cp -R docs/licenses "$D/"
|
||||
cp horizon/THIRD_PARTY_NOTICES.md "$D/licenses/HORIZON-THIRD-PARTY-NOTICES.md"
|
||||
cp horizon/crates/horizon-client/src/acoustics/THIRD_PARTY_SPR.md "$D/licenses/THIRD_PARTY_SPR.md"
|
||||
zip -qr "out/$D.zip" "$D"
|
||||
cp airdef-mod/target/horizon-mod/airdef.hmod out/
|
||||
- name: Релиз
|
||||
env:
|
||||
TOKEN: ${{ secrets.RELEASE_TOKEN || gitea.token }}
|
||||
API: ${{ github.server_url }}/api/v1/repos/${{ github.repository }}
|
||||
run: |
|
||||
set -e
|
||||
if [ -z "$TOKEN" ]; then echo "::error::нет токена: добавь секрет RELEASE_TOKEN"; exit 1; fi
|
||||
# Тег с дефисом (v0.3.0-rc1) выходит как предварительный релиз; уже созданный релиз тега дополняется.
|
||||
DATA=$(python3 -c 'import json,os;t=os.environ["TAG"];print(json.dumps({"tag_name":t,"name":t,"prerelease":"-" in t,"body":"Windows: распакуй airdef-windows-x86_64.zip и запусти play.bat. Нужен Minecraft Java 26.3 и лаунчер Horizon, запущенный один раз. Подробнее: docs/INSTALL.md.\n\nmacOS: сборка из исходников, python3 tools/airdef.py all."}))')
|
||||
CODE=$(curl -sS -o release.json -w '%{http_code}' -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG")
|
||||
if [ "$CODE" != "200" ]; then
|
||||
CODE=$(curl -sS -o release.json -w '%{http_code}' -X POST -H "Authorization: token $TOKEN" -H "Content-Type: application/json" "$API/releases" -d "$DATA")
|
||||
if [ "$CODE" != "201" ]; then echo "::error::Gitea не создала релиз (HTTP $CODE): $(cat release.json)"; exit 1; fi
|
||||
fi
|
||||
ID=$(python3 -c 'import json;print(json.load(open("release.json"))["id"])')
|
||||
for F in airdef-windows-x86_64.zip airdef.hmod; do
|
||||
curl -sf -X POST -H "Authorization: token $TOKEN" -F "attachment=@out/$F" "$API/releases/$ID/assets?name=$F" > /dev/null
|
||||
done
|
||||
@@ -0,0 +1,27 @@
|
||||
# Opt-in heavy sandbox runtime tier; separate from the routine WASM contract.
|
||||
name: wasm-budget
|
||||
on:
|
||||
workflow_dispatch:
|
||||
env:
|
||||
CARGO_BUILD_JOBS: "1"
|
||||
CARGO_INCREMENTAL: "0"
|
||||
jobs:
|
||||
budget:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Rust
|
||||
run: |
|
||||
curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --target wasm32-unknown-unknown
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
- name: Actual WASM budget scenario
|
||||
run: nice -n 19 python3 tools/airdef.py test-budgets
|
||||
- name: Preserve machine-readable provenance
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wasm-budget-provenance
|
||||
path: .horizon-cache/wasm-budget-report.json
|
||||
if-no-files-found: warn
|
||||
@@ -0,0 +1,20 @@
|
||||
# Heavy sandbox runtime is opt-in on the shared 2-core runner.
|
||||
name: wasm-contract
|
||||
on:
|
||||
workflow_dispatch:
|
||||
env:
|
||||
CARGO_BUILD_JOBS: "1"
|
||||
CARGO_INCREMENTAL: "0"
|
||||
jobs:
|
||||
contract:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Rust
|
||||
run: |
|
||||
curl -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --target wasm32-unknown-unknown
|
||||
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||
- name: Actual WASM contract
|
||||
run: nice -n 19 python3 tools/airdef.py test-wasm
|
||||
+3
-1
@@ -1,7 +1,9 @@
|
||||
horizon/
|
||||
run-home/
|
||||
.horizon-cache/
|
||||
airdef-mod/target/
|
||||
airdef-core/target/
|
||||
__pycache__/
|
||||
/out/
|
||||
.DS_Store
|
||||
run-home-bench/
|
||||
run-home-smoke/
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[submodule "horizon"]
|
||||
path = horizon
|
||||
url = https://gitea.maoniao.team/chexit/horizon-airdef.git
|
||||
@@ -1 +0,0 @@
|
||||
b6c936f82b08798057161bdfa577e3aac53fb91e
|
||||
@@ -1,31 +1,53 @@
|
||||
# AirDef — дальнобойные дроны и ПВО для Horizon
|
||||
|
||||
Мод для Horizon (Minecraft 26.3 на Rust): Шахед-136, Герань-3, Patriot и Гепард с 3D-моделями,
|
||||
наведением ракет, слежением башен, падением сбитых дронов и эффектами взрывов.
|
||||
Клиентский sandbox-мод для Horizon (Minecraft 26.3 на Rust): налёты дронов и ракет,
|
||||
эшелонированная ПВО, раздельные батареи, наземная логистика, разведка, РЭБ, повреждения,
|
||||
повторы и экспорт видео. Характеристики и временные масштабы игровые; это не тренажёр реальных комплексов.
|
||||
|
||||
Работает на **своём форке Horizon** (`horizon/`, ветка `airdef`): в нём добавлен API отрисовки
|
||||
Работает на **[своём форке Horizon](https://gitea.maoniao.team/chexit/horizon-airdef)**
|
||||
(`horizon/` — сабмодуль, закреплённый SHA): в нём добавлен API отрисовки
|
||||
3D-моделей, которого нет в основном Horizon.
|
||||
|
||||
Рабочая ветка — **`kimi-work`**. Подробный проверенный объём и оставшаяся приёмка:
|
||||
[состояние задач](docs/IMPLEMENTATION-STATUS.md). Для передачи разработки:
|
||||
[технический журнал и handoff](docs/CODEX-HANDOFF.md). Открытый issue не означает отсутствие
|
||||
реализации: по просьбе владельца агенты оставляют комментарии, но не закрывают задачи.
|
||||
|
||||

|
||||
|
||||
## Запуск
|
||||
|
||||
Пошаговый гайд для Windows и macOS (готовая сборка из релиза или сборка из исходников): [docs/INSTALL.md](docs/INSTALL.md).
|
||||
Устройство HD-рендера, его статус и план доработки: [docs/HD-RENDERER.md](docs/HD-RENDERER.md).
|
||||
|
||||
Коротко, из исходников:
|
||||
|
||||
```bash
|
||||
python3 tools/airdef.py all # скачать Horizon, наложить патчи форка, собрать игру и мод, установить мод
|
||||
git switch kimi-work
|
||||
git pull --ff-only
|
||||
git submodule update --init --recursive # также выполняется командой all
|
||||
python3 tools/airdef.py all # получить закреплённый движок, собрать игру и мод, установить мод
|
||||
python3 tools/airdef.py play # запустить (аргументы игры — после: --world "Имя мира")
|
||||
```
|
||||
|
||||
`all` синхронизирует URL сабмодуля из `.gitmodules`, включая обновление со старого upstream.
|
||||
Продолжение 4 ноября: [модели, границы ядра, общий каталог, API29 и массовая акустика](docs/CONTINUATION-2026-11-04.md).
|
||||
|
||||
Диагностика в игре: `/airdef capabilities` показывает доступность API, принятие запроса и
|
||||
подготовку дыма, акустики и захвата именно этого мода. Подготовка не означает готовый GPU-кадр или MP4.
|
||||
Для разработчика: `python3 tools/airdef.py test-wasm` — лёгкий ABI-тест;
|
||||
`python3 tools/airdef.py test-budgets` — отдельный тяжёлый сценарий с JSON-отчётом.
|
||||
|
||||
Для разработки: `./install.sh` перегенерирует модели (`tools/gen_models.py`, нужен Pillow) и звуки
|
||||
(`tools/gen_sounds.py`, нужны numpy и ffmpeg), собирает и ставит мод. Сгенерированные файлы лежат в репозитории,
|
||||
так что для обычной сборки нужен только Rust.
|
||||
|
||||
CI: `.gitea/workflows/build.yml` на каждый пуш собирает мод и игру для Linux, а пуш в `main` публикует релиз
|
||||
`build-N`; `.gitea/workflows/desktop.yml` (запуск вручную с тегом релиза) собирает игру для macOS и Windows на
|
||||
раннерах с метками `macos-latest` / `windows-latest` и дописывает архивы в релиз.
|
||||
CI: `.gitea/workflows/build.yml` на каждый пуш собирает только мод (`airdef.hmod`), а пуш в `main` публикует его
|
||||
релизом `build-N`. Игру-форк собирают вручную: `.gitea/workflows/game.yml` (Linux) и `.gitea/workflows/desktop.yml`
|
||||
(macOS и Windows, на раннерах с метками `macos-latest` / `windows-latest`); оба запускаются из Actions с тегом
|
||||
готового релиза `build-N`. Перед сборкой workflow проверяет, что такой релиз создан `build.yml` (имя релиза = тег,
|
||||
есть `airdef.hmod`), иначе падает с ошибкой; затем выкачивает ровно коммит этого тега (а не ветку, с которой запущен),
|
||||
сверяет его с коммитом релиза и дописывает архивы только в этот релиз.
|
||||
|
||||
## Как играть
|
||||
|
||||
@@ -48,10 +70,11 @@ CI: `.gitea/workflows/build.yml` на каждый пуш собирает мо
|
||||
волнами по типам), дальность старта, цели и **ПУСК**. Цели:
|
||||
- *только объекты* — всё летит по отмеченным объектам (или по игроку, если их нет);
|
||||
- *объекты и ПВО* — треть ударных средств бьёт по случайным комплексам;
|
||||
- *умный противник* — планировщик знает, где стоят комплексы и что видят их РЛС: ведёт дроны и крылатые ракеты
|
||||
- *умный противник* — планировщик использует наблюдённые контакты и последние известные позиции комплексов: ведёт дроны и крылатые ракеты
|
||||
маршрутами в обход зон поражения, низко, за рельефом и через мёртвые зоны; «Герберы» прилетают на 15 с раньше,
|
||||
чтобы оттянуть ракеты; «Ланцеты» идут по Гепардам и Панцирям, Х-22/Циркон и часть баллистики — по дальнобойным
|
||||
комплексам; всё остальное стартует со сдвигом так, чтобы прилететь одновременно.
|
||||
комплексам; на наблюдённые РЛС дальнего обнаружения выделяются 2–3 быстрых ударника (Х-22, Циркон, Искандер, Кинжал, затем Х-101,
|
||||
Герань-3, Ланцет, Шахед; не больше четверти налёта); всё остальное стартует со сдвигом так, чтобы прилететь одновременно.
|
||||
|
||||
После налёта приходит отчёт: сколько сбито и прорвалось, ракет ПВО потрачено, комплексов потеряно, урон объектам.
|
||||
|
||||
@@ -67,6 +90,75 @@ CI: `.gitea/workflows/build.yml` на каждый пуш собирает мо
|
||||
|
||||
**Сохранение.** Комплексы, объекты, их повреждения и настройки сохраняются отдельно для каждого мира (и для каждого
|
||||
сервера); уничтоженный комплекс остаётся остовом и после перезахода, пока его не отремонтируют.
|
||||
Налёт в воздухе тоже сохраняется: дроны, очередь их запуска и незавершённые задания на маршрут
|
||||
восстанавливаются, и перезаход не обрывает перенацеливание. Ракеты-перехватчики в воздухе и площадки
|
||||
запуска не сохраняются; сценарий — стартовое состояние и налёт не несёт.
|
||||
|
||||
**Сеть ПВО.** Единицы обмениваются измеренными треками в пределах игровой связи; повреждение связи
|
||||
сокращает её дальность. Можно поставить несколько С-400/С-500 без собственной РЛС и общие радары
|
||||
поиска, сопровождения и мачтового обзора: N → «Разместить элементы батареи…».
|
||||
Поисковый радар даёт подсказку; огневой должен сам измерить цель, прежде чем разрешить пуск.
|
||||
Старая сборная конфигурация сохранена. [Команды, источники и ограничения](docs/BATTERIES-AND-COLD-LAUNCH.md).
|
||||
P1-SUN также использует чужие данные. Пушкам (Гепард,
|
||||
пушки Панциря) нужна своя цель: они стреляют только по тому, что видят сами. Уничтоженная РЛС — дыра в сети.
|
||||
|
||||
**Радиомолчание.** `/airdef radar <номер> off|on` или кнопка в карточке техники на карте
|
||||
выключает/включает собственный радар. Приём разрешённых внешних треков сохраняется; чужой трек
|
||||
не заменяет обязательное собственное сопровождение. Выключение не стирает старые сведения противника:
|
||||
он продолжает использовать последнюю наблюдённую позицию, а пуски и РЭБ могут снова раскрыть технику.
|
||||
|
||||
**Логистика.** Пока нет ни одного склада, магазины пополняются сами, как раньше. Как только поставлен склад,
|
||||
боекомплект считается: комплекс без ракет пишет «ЖДЁТ БК»; в 60 м от склада он перезаряжается прямо с него, а
|
||||
дальние обслуживают **ТЗМ**: заправляются на складе (2 перезарядки), сами едут к ждущим комплексам, ближайшим
|
||||
первым, и возвращаются. Запас склада — 30 перезарядок, он виден в сводке. Склад и ТЗМ детонируют всем грузом.
|
||||
|
||||
Ударная **RM48U у С-400** — отдельный условный игровой боеприпас, который делит четыре ячейки
|
||||
с ракетами ПВО. `/airdef ground-stock <склад> <количество>` задаёт конечный начальный запас
|
||||
sandbox-сценария, `/airdef ground-load <С-400>` загружает одну ракету рядом со складом либо
|
||||
создаёт заявку на доставку ТЗМ. Груз едет физически; снятая ракета ПВО возвращается на склад.
|
||||
Заявка и груз сохраняются в пути. `/airdef ground-unload <С-400>` выгружает ракету у склада,
|
||||
а вдали создаёт заявку на обратный вывоз ТЗМ. Одна ракета резервируется от пуска до забора;
|
||||
`/airdef ground-cancel <С-400>` отменяет ещё ожидающую доставку или вывоз.
|
||||
После забора ракета находится в машине и едет на склад. Отмена заявки не возвращает уже вывезенный груз в ПУ.
|
||||
Без склада этот ударный боеприпас сам не появляется. Бук и остальные комплексы такого профиля пока не имеют.
|
||||
Порядок подготовки, разведка и номера двух списков: [как ударить по ПУ](docs/GROUND-STRIKES.md).
|
||||
|
||||
**Перемещение.** **Пульт перемещения**: навестись на технику (выбрать), затем на точку на земле — она
|
||||
сворачивается (С-400 и другие с вертикальным пуском дольше, Гепард и Панцирь быстро), едет по рельефу и
|
||||
разворачивается; всё это время не стреляет и не светит РЛС. Или `/airdef move <номер>` — к тому месту, где ты стоишь.
|
||||
|
||||
**РЭБ.** Клавиша **U** (или `/airdef ew on|off`) включает все станции. Станция в радиусе 350 м сбивает
|
||||
спутниковую навигацию у Шахедов, Герань, Гербер, Х-101 и Калибров: они теряют маршрут и уводятся с цели
|
||||
(баллистике и Ланцетам с оптикой всё равно). Передатчик работает до 20 с и перегревается; остывает ~35 с,
|
||||
пока не остынет — не включить. Включённая станция себя выдаёт: умный противник запоминает её и шлёт на неё
|
||||
Ланцеты и Х-22, а маршруты спутниковых дронов прокладывает в обход.
|
||||
|
||||
**Умный противник учится.** Он запоминает, откуда его налёты сбивали, а откуда они прорывались, и следующий
|
||||
налёт ведёт с лучшего направления; помнит комплексы, выдавшие себя пуском, и станции РЭБ, и во время налёта
|
||||
перенацеливает ещё не взлетевшие Ланцеты и Шахеды на них; если цель-комплекс уничтожен, дроны идут к
|
||||
следующему известному. Маршруты к новым целям достраиваются по бюджету планировщика в следующих тиках:
|
||||
необработанное задание не теряется, старый безопасный участок остаётся до готовности нового, а дрон без
|
||||
маршрута летит прямо только до ближайшего бюджета. Дроны под обстрелом прижимаются к земле.
|
||||
|
||||
**Ночь.** Исправные пушки с собственным свежим радарным сопровождением не теряют точность из-за темноты.
|
||||
Цели освещают **прожекторы** (луч ведёт ближайшую
|
||||
цель из сети, приоритет — цели пушек) и **осветительные ракеты**: пушечные комплексы пускают их сами (раз в
|
||||
25 с на цель в темноте), игрок — **осветительным пистолетом** или `/airdef flare`. Ракета висит ~30 с и
|
||||
освещает всё вокруг на ~60–90 м.
|
||||
|
||||
**Графика: три пресета** (меню N → «Огонь, ремонт, эффекты…» → «Эффекты», или `/airdef shader off|on|hd`):
|
||||
выкл — ванильная картинка; обычные — шейдерные огненные шары, ударная волна и подсветка; **HD** — свой рендер
|
||||
освещения в форке: мир дополнительно рисуется в G-буфер (цвет без света и нормали), и все источники света мода
|
||||
освещают его попиксельно в HDR — факелы ракет и двигатели, раскалённые боевые блоки, осветительные ракеты, вспышки
|
||||
выстрелов, трассеры, взрывы (вспышка и догорающий шар), горящие остовы, пятна прожекторов, — затем bloom и мягкий
|
||||
тонмаппинг (ванильный диапазон не трогается, сжимается только то, что ярче). Источники передаются движку новым
|
||||
API мода `render.set-lights` (до 512, плавно интерполируются), режим — `render.set-lighting`.
|
||||
|
||||
В HD дымовые следы ракет немного уплотнены; число сегментов и время появления/выгорания сохранены.
|
||||
Это относится к ракетам ПВО, RM48U, крылатым, сверхзвуковым и баллистическим средствам;
|
||||
дым пожаров и слабая дымка винтовых дронов остались прежними.
|
||||
|
||||
**Сетка J** после сбития ещё 3,5 с держит камеру на сбитой цели, и комплекс в это время не пропадает из сетки.
|
||||
|
||||
**Повреждения.** Взрыв рядом с комплексом повреждает его: он хуже поворачивается, хуже видит и попадает,
|
||||
дымит, ниже 35% горит. Уничтоженный остаётся горящим остовом, его боекомплект детонирует: чем больше ракет
|
||||
@@ -74,6 +166,11 @@ CI: `.gitea/workflows/build.yml` на каждый пуш собирает мо
|
||||
пострадать. Объекты (Целеуказатель или `/airdef target here`) имеют прочность, она видна в сводке. Ремонт —
|
||||
кнопкой в меню или `/airdef repair`.
|
||||
|
||||
Известные стены и рельеф ослабляют **непрямой** урон корпусу, узлам и объектам; проёмы дают
|
||||
частичное воздействие. Используется геометрия, известная перед разрушением. При неизвестной или
|
||||
устаревшей геометрии сохраняется прежний урон без защиты; поздний ответ его повторно не наносит.
|
||||
Прямое вооружённое попадание этим ослаблением не отменяется.
|
||||
|
||||
**Монитор комплекса:** клавиша **K** листает комплексы по очереди, после последнего монитор скрывается. Слева вверху показано:
|
||||
- состояние комплекса (поиск, сопровождение, доворот, захват, пуск, огонь, перезарядка, огонь запрещён);
|
||||
- его РЛС: цели, по которым он работает, красным, остальные серым, текущая цель жёлтым;
|
||||
@@ -91,7 +188,7 @@ CI: `.gitea/workflows/build.yml` на каждый пуш собирает мо
|
||||
перехватчиков (жёлтые), сбития (x), прилёты, шкала времени с событиями; время идёт само, колесо мыши перематывает;
|
||||
- **повтор момента** — клик по событию в списке: «призраки» дронов и ракет снова летят в мире, камера идёт за
|
||||
целью, потом за перехватчиком (или смотрит из пушки, со стороны на прилёт), в момент сбития время замедляется;
|
||||
- **полный повтор** — весь налёт, камера-режиссёр сама выбирает, за чем следить; колесо — скорость (×0.5–×16);
|
||||
- **полный повтор** — весь налёт, камера-режиссёр сама выбирает, за чем следить; колесо — перемотка, стрелки — скорость;
|
||||
- **лучшие моменты** — нарезка до 8 самых интересных событий (перехват баллистики и гиперзвука, поражения
|
||||
объектов, потерянные комплексы, разделение «Орешника») с титрами и замедлением; колесо листает моменты.
|
||||
|
||||
@@ -103,14 +200,22 @@ CI: `.gitea/workflows/build.yml` на каждый пуш собирает мо
|
||||
Чтобы в повторах и камерах комплексов была видна земля далеко от игрока, мод включает дальний ландшафт
|
||||
(48 чанков, упрощённый); выключается в меню N → «Огонь, ремонт, эффекты…» → «Дальний ландшафт».
|
||||
|
||||
Клавиши N, K, J и R можно переназначить в «Настройки → Моды».
|
||||
Клавиши N, K, J, R и U можно переназначить в «Настройки → Моды».
|
||||
|
||||
Команды:
|
||||
- `/airdef wave <тип> [кол-во] [дальность]` — налёт на ближайшую цель. Типы: `shahed`, `geran3`, `lancet`, `gerbera`, `kh101`, `kalibr`, `kh22`, `zircon`, `iskander`, `kinzhal`, `oreshnik` и `mix` (комбинированный удар двумя секторами: «Герберы», шахеды, Герани-3, Х-101, «Калибры», Х-22, Циркон, «Искандер», «Кинжал», при 30+ — «Орешник»);
|
||||
- `/airdef wave <тип> [кол-во] [дальность]` — налёт на ближайшую цель. Дальность старта — от 60 до 4000 блоков (в меню: 200–4000); землю для площадок, которой нет в загруженных чанках, игра подгружает сама (API 22, `render.load-chunks`, пока идёт поиск), а где подгрузить нельзя (сервер) или нет пригодного места, дроны всё равно стартуют: «за картой», с высоты полёта на заказанной дальности, без площадки. Типы: `shahed`, `geran3`, `lancet`, `gerbera`, `kh101`, `kalibr`, `kh22`, `zircon`, `iskander`, `kinzhal`, `oreshnik` и `mix` (комбинированный удар двумя секторами: «Герберы», шахеды, Герани-3, Х-101, «Калибры», Х-22, Циркон, «Искандер», «Кинжал», при 30+ — «Орешник»);
|
||||
- `/airdef replay [map|full|reel|stop]` — повтор последнего налёта;
|
||||
- `/airdef replay film [best|all|N]` — фильм режиссёра: для каждого перехвата/прилёта несколько планов (комплекс, пуск, погоня за ракетой, взгляд с цели, замедленный крупный план поражения, общий план после), склейки, плавные движения камеры, камера не уходит под землю; играется в максимальном качестве (HD, мир в полном разрешении, больше шагов облаков и тумана; настройки возвращаются после);
|
||||
- `/airdef replay export [имя] [best|all|N] [fps=60] [size=1920x1080] [crf=14] [sec=N] [noaudio]` — тот же фильм кадр за кадром с фиксированным шагом (скорость компьютера не важна) в MP4 с звуком: `<HORIZON_HOME>/airdef-films/<имя>.mp4` (нужен `ffmpeg` в PATH или `HORIZON_FFMPEG`; без него — PNG-последовательность и готовая команда ffmpeg). Прогресс в чате, отмена — `/airdef replay stop`. Окно игры держите в фокусе (пауза останавливает съёмку); чат попадает в кадр.
|
||||
- `/airdef import` — перенести в текущий мир комплексы и цели из старой версии мода (до 0.2.x они были общими для всех миров);
|
||||
- `/airdef raid` — налёт, настроенный в меню; `/airdef target here` — отметить объект под ногами; `/airdef repair` — ремонт;
|
||||
- `/airdef lastchance <on|off>` — «последний шанс»: комплекс стреляет и по классу вне ролей, если цель вот-вот попадёт (по умолчанию вкл; сохраняется);
|
||||
- `/airdef sites` — известные пусковые площадки (ПУ). РЛС с прямой видимостью может заметить сам пуск и получить неточную позицию; несколько наблюдённых пусков уточняют её. Обнаруженный позже дрон даёт только слабое предположение об обратном курсе: скрытые координаты площадки и время невидимого старта не используются. Повторные треки одного полёта не создают ложную точность.
|
||||
- `/airdef strike <номер ПУ>` — ручной удар по разведанной площадке из готового физического С-400 с отдельно загруженной RM48U (игровой профиль, #118). Четыре ячейки делятся с боезапасом ПВО; приказ резервирует носитель и ячейку, открывает крышку и списывает одну ударную ракету при выбросе. Выбирается готовый комплекс с наибольшим остатком RM48U, затем ближайший к цели. Есть открытие ТПК, выброс, отложенное включение двигателя и разгон, ограниченная игровая траектория, столкновения и запись пуска. Несуществующее «ПВО площадки» не перехватывает ракету по броску вероятности.
|
||||
- `/airdef ground-stock <номер склада> <количество>` — задать остаток RM48U для sandbox-сценария (по умолчанию 0). `/airdef ground-load <номер С-400>` загружает одну ракету у склада либо заказывает ТЗМ; `ground-unload` возвращает её у склада либо заказывает вывоз; `ground-cancel` отменяет ожидающую заявку. Доставка и обратный вывоз через ТЗМ реализованы, запас и груз сохраняются. [Пошаговый пример](docs/GROUND-STRIKES.md). Совместимость профиля основана на [публикации ГУР](https://gur.gov.ua/content/warsanctions-hur-mo-rozkryvaie-budovu-komponentnu-bazu-ta-pidpryiemstva-kooperatsii-z-vyrobnytstva-udarnykh-raket-rm48u.html); это сообщение ведомства, не независимая сертификация. Дальность, форма полёта и оформление условны для Minecraft.
|
||||
- `/airdef deploy s400_launcher` и `s500_launcher` — отдельные пусковые без своей РЛС; `radar_search`, `radar_track`, `radar_height` — обзорная, сопровождающая и мачтовая игровые РЛС. Они также доступны через меню N → «Разместить элементы батареи…». `/airdef battery` показывает состав и связь; `/airdef battery <номер> integrated|launcher` меняет конфигурацию, `/airdef radar <номер> multi|search|engagement|height` — роль отдельной РЛС. Обзорная РЛС передаёт подсказки; станция сопровождения должна сама получить свежее измерение, чтобы разрешить пуск. Старые С-400/С-500 сохраняют упрощённую сборную конфигурацию. [Устройство игровой батареи и пуска](docs/BATTERIES-AND-COLD-LAUNCH.md).
|
||||
- `/airdef roles` — показать, по каким классам целей работает каждый комплекс; `/airdef roles <patriot|gepard|pantsir|s400|s500|all> <классы|default>` — задать (классы через запятую: `drone`, `jet`, `cruise`, `supersonic`, `ballistic` или `all`), например `/airdef roles s400 cruise,ballistic`;
|
||||
- `/airdef doctrine` — доктрины комплексов (#47; в меню N: «Доктрины…»): `/airdef doctrine <номер|all> reserve <K>` — держать K ракет для срочных целей (баллистика или до удара меньше 12 с); `salvo eco|normal|heavy` — сколько перехватчиков на цель (только базовые / как обычно / на один больше); `sector <центр°> <полуширина°>` или `sector off` — сектор ответственности (0° — север); `guard <объект№>[,<№>]` или `guard off` — стрелять только по тому, что идёт на охраняемые объекты (отметка: `/airdef target here`). По умолчанию ограничений нет; последний шанс сильнее доктрины; `/airdef why <номер>` называет, какая настройка остановила или разрешила пуск; сохраняется с миром и сценариями. Карта охраны и пробелов в ней — вместе с картой (#48).
|
||||
- `/airdef deploy <patriot|gepard|pantsir|s400|s500>` — поставить комплекс в 8 блоках перед собой;
|
||||
- `/airdef hold on|off` — запретить или разрешить ПВО огонь, удобно для съёмки ударов;
|
||||
- `/airdef damage on|off` — разрушать ли ландшафт взрывами (через TNT, нужны читы);
|
||||
@@ -147,15 +252,42 @@ CI: `.gitea/workflows/build.yml` на каждый пуш собирает мо
|
||||
|
||||
| ПВО | Вооружение | Дальность |
|
||||
|---|---|---|
|
||||
| Patriot | 4 × PAC-3, пусковая разворачивается на цель | 10–300 |
|
||||
| Гепард | 2 × 35 мм, башня и стволы со слежением и упреждением, 160 очередей | 90 (слежение с 130) |
|
||||
| Панцирь-С1 | 12 × 57Э6 и 2 × 30 мм; модуль поворачивается, пакеты и стволы поднимаются | ракеты 25–150, пушки 60 |
|
||||
| С-400 | 4 × 48Н6, вертикальный холодный старт, РЛС обзора на прицепе | 30–480 |
|
||||
| С-500 | 4 × 77Н6, холодный старт, самые быстрые ракеты | 40–700 |
|
||||
| Patriot | 4 × PAC-3, пусковая разворачивается на цель | 10–450 |
|
||||
| Гепард | 2 × 35 мм, башня и стволы со слежением и упреждением, 160 очередей | 135 (слежение с 195) |
|
||||
| Панцирь-С1 | 12 × 57Э6 и 2 × 30 мм; модуль поворачивается, пакеты и стволы поднимаются | ракеты 25–225, пушки 90 |
|
||||
| С-400 | 4 × 48Н6, вертикальный холодный старт, РЛС обзора на прицепе | 30–720 |
|
||||
| С-500 | 2 × 77Н6, холодный старт, самые быстрые ракеты | 40–1050 |
|
||||
| IRIS-T SLM | 8 ракет, вертикальный пуск, быстрые и манёвренные | 15–600 |
|
||||
| NASAMS | 6 × AMRAAM, наводимая пусковая на прицепе | 10–450 |
|
||||
| Бук-М2 | 4 × 9М317 на направляющих, башня с РЛС подсвета | 20–675 |
|
||||
| Тор-М2 | 8 × 9М338 вертикально из башни, быстрая реакция против дронов | 8–240 |
|
||||
| P1-SUN | 8 дронов-перехватчиков против Шахедов и Герань; своей РЛС нет — стреляет по данным сети | 5–675 |
|
||||
|
||||
**Согласование огня.** Комплексы делят цели между собой: на обычную цель идёт одна ракета, на баллистическую две,
|
||||
пушки расходятся по разным целям. Первыми бьют по самым опасным (быстрым и тяжёлым) целям, при этом ложные
|
||||
«Герберы» выглядят для них как настоящие. Шанс поражения падает, если цель быстрее перехватчика.
|
||||
Обслуживающая техника (тоже пультами или `/airdef deploy <ключ>`): **РЛС дальнего обнаружения** (`radar`,
|
||||
видит на 2,5 км с высокой мачты, хуже прячутся за рельефом), **склад боеприпасов** (`depot`), **ТЗМ** (`tzm`),
|
||||
**станция РЭБ** (`ew`), **прожектор** (`light`). Новые комплексы: `irist`, `nasams`, `buk`, `tor`, `p1sun`.
|
||||
|
||||
**Согласование огня.** Комплексы делят цели между собой: на обычную цель идёт одна ракета, на баллистическую две; чем ближе цель к своей точке попадания,
|
||||
тем больше перехватчиков разрешено на неё сразу (за ~12 с до удара ещё один, за ~5 с — ещё один). Комплекс, у которого кончились ракеты
|
||||
или идёт перезарядка, цель не держит; тот, кто не смог навестись за ~2 с, отпускает её другим. Пушки расходятся по разным целям. Первыми бьют по самым опасным (быстрым и тяжёлым) целям, при этом ложные
|
||||
«Герберы» выглядят для них как настоящие. Геометрия встречи проверяется по движению обоих объектов
|
||||
за тик, а остаточный шанс учитывает фактическую скорость перехватчика.
|
||||
|
||||
**Энергетика перехватчика.** Сначала ракета выходит из ТПК без двигателя: у С-400 это 1,0 с,
|
||||
у С-500 — 1,2 с; только потом в её текущей точке зажигается факел, и тяга плавно нарастает.
|
||||
После выброса ракета разгоняется, но двигатель работает конечное игровое время. После выгорания
|
||||
ракета летит по инерции, теряет скорость и манёвренность; при недостаточной скорости сопровождение
|
||||
срывается. Факел, звук двигателя и новый дым прекращаются при выгорании и в live, и в повторе;
|
||||
уже созданный дым продолжает рассеиваться. У ракет с горячим (рельсовым) пуском тяга тоже
|
||||
нарастает плавно, но холодного выброса нет; электрический P1-SUN не получает ракетную струю.
|
||||
|
||||
**Последний шанс** (`/airdef lastchance on|off`, по умолчанию вкл). Комплекс стреляет и по классу целей вне своих ролей
|
||||
(С-400, С-500 и Patriot по умолчанию не бьют дроны), если цель долетит до своей точки меньше чем за 10 с и на ней ещё нет
|
||||
ничьего перехватчика. Комплексу с пустыми ролями (огонь запрещён) это не касается. `/airdef why` пишет «последний шанс».
|
||||
|
||||
**Дальний пуск по РЛС.** С-400, С-500 и Patriot могут стрелять дальше своей дальности (до 1,6×) по цели, которую держит
|
||||
свежим треком РЛС дальнего обнаружения в радиусе связи: не чаще раза в 15 с, при не менее чем половине боезапаса, только по
|
||||
своим ролям и только пока в обычной дальности нет цели; шанс поражения на краю вдвое ниже. `/airdef why`: «дальний пуск по РЛС».
|
||||
|
||||
**Роли по умолчанию** (в сводке в квадратных скобках: Д — дроны, Г — реактивные, К — крылатые, С — сверхзвуковые,
|
||||
Б — баллистика): Гепард ДГК, Панцирь ДГКС, Patriot ГКСБ, С-400 ГКСБ, С-500 СБ. Сбитый дрон падает с остатком скорости, и его боевая часть
|
||||
@@ -167,6 +299,12 @@ CI: `.gitea/workflows/build.yml` на каждый пуш собирает мо
|
||||
У каждого объекта свой звук и своя дальность слышимости (`attenuation_distance` в `sounds.json`):
|
||||
- **двигатели:** двухтактный мопед шахеда (220 м), турбина Герани-3, электромотор Ланцета, тяжёлый рёв крылатых ракет (320 м),
|
||||
ракетный рёв Х-22, Циркона и перехватчиков, нарастающий визг падающей баллистики и её ударная волна (700–900 м);
|
||||
Двигатели дронов и ракет — **постоянные источники звука** (API 20, `render.sound-sources`, #95): один источник на
|
||||
объект идёт за его настоящей позицией, зажигается с нарастанием, при сбитии «захлёбывается» (тон падает, громкость
|
||||
гаснет за секунду) и замолкает вместе с объектом; в повторе и экспорте источники берутся из записанных траекторий и
|
||||
времени повтора (перемотка и смена плана пересоздают их). Одновременно звучат 24 ближайших и громких, остальные
|
||||
«виртуальные». Взрывы, пуски и выстрелы остались разовыми, с задержкой по скорости звука. Баллистика (визг при
|
||||
падении) тоже разовая. Зацикливается устойчивая часть файла (начало и затухание в конце обрезаны), стык — 50 мс.
|
||||
- **пуски:** хлопок и рвущийся рёв Patriot, свистящий старт Панциря, глухой газовый выброс и затем огромный рёв С-400,
|
||||
у С-500 ещё ниже и дольше (до 1 км);
|
||||
- **пушки:** тяжёлые 35-мм очереди Гепарда и частые 30-мм Панциря;
|
||||
@@ -192,16 +330,18 @@ CI: `.gitea/workflows/build.yml` на каждый пуш собирает мо
|
||||
## Устройство
|
||||
|
||||
```
|
||||
HORIZON_COMMIT коммит апстрима Horizon, на который накладываются патчи
|
||||
horizon-patches/ изменения форка (git format-patch); tools/airdef.py fork собирает из них horizon/
|
||||
horizon/ форк (не в репозитории; ветка airdef). Что в нём меняется:
|
||||
.gitmodules URL отдельного репозитория движка
|
||||
horizon/ git submodule: точный SHA форка Horizon. Что в нём меняется:
|
||||
crates/horizon-mod-sdk/wit/horizon.wit render.set-models, spawn-particles, play-sound,
|
||||
query-ground / ground
|
||||
crates/horizon-mods/src/runtime.rs хост этих функций
|
||||
crates/horizon-client/src/game/mod_models.rs интерполяция и отрисовка моделей, частицы
|
||||
crates/horizon-client/src/gfx/mod_post.rs пост-проход мода: render.set-post / set-params
|
||||
airdef-mod/ сам мод: Rust → WebAssembly
|
||||
src/sim.rs симуляция: дроны, ракеты, ПВО, эффекты (20 тиков/с)
|
||||
airdef-core/ SDK-free сборка общего ядра; использует те же исходники sim, без копии
|
||||
airdef-mod/ адаптер: Rust → WebAssembly
|
||||
src/sim/mod.rs симуляция: дроны, ракеты, ПВО, эффекты (20 тиков/с)
|
||||
src/sim/battery.rs конфигурации пусковых и роли общей РЛС
|
||||
src/sim/launch.rs резервирование ячеек и анимация крышек ТПК
|
||||
src/lib.rs предметы, команды, сохранение, передача в движок
|
||||
src/panel.rs сводка, src/radar.rs — радар, src/monitor.rs — монитор комплекса
|
||||
assets/airdef/sounds звуки (генерирует tools/gen_sounds.py)
|
||||
@@ -221,11 +361,17 @@ run-home/ тестовая игровая папка (для пров
|
||||
Модели — обычные блочные JSON-модели, по одной на часть: корпус, винт, башня, стволы. Их генерирует
|
||||
`tools/gen_models.py`. Координаты элементов должны быть в пределах −16..32, иначе Horizon отбросит модель.
|
||||
|
||||
Рельеф мод узнаёт через `render.query-ground`: присылает колонки, а движок отвечает высотой по карте
|
||||
высот на следующем тике.
|
||||
Рельеф и коллизии приходят асинхронно через ground/ray/section queries. Высотной карты недостаточно
|
||||
для точного попадания: известные блоки и формы проверяются отдельно; ожидание/Unknown не считается
|
||||
доказанным свободным путём. Историческое отображение повтора отделено от live-геометрии симуляции.
|
||||
|
||||
## Известные ограничения
|
||||
|
||||
- Для [#122](https://gitea.maoniao.team/chexit/airdef/issues/122) воспроизведено и исправлено исчерпание fuel при массовых взрывах:
|
||||
каждый взрыв больше не обходит всю карту высот. Проверка на конкретном Windows-компьютере ещё нужна;
|
||||
прирост FPS этим исправлением не обещается.
|
||||
- Мод клиентский: дроны и ПВО видит только тот, у кого он стоит. Мультиплеер не поддерживается.
|
||||
- Окно Horizon на macOS не рисует кадры, пока оно перекрыто другими окнами (это поведение движка).
|
||||
- Шейдер работает, только если не включён шейдер-пак Horizon (они взаимоисключающие).
|
||||
|
||||
Разработка и обновление закреплённого движка: [docs/ENGINE.md](docs/ENGINE.md).
|
||||
|
||||
Generated
+16
@@ -0,0 +1,16 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "airdef-core"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.30.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19fc433e8437a212d1b6f1e68c7824af3aed907da60afa994e7f542d18d12aa9"
|
||||
@@ -0,0 +1,13 @@
|
||||
[package]
|
||||
name = "airdef-core"
|
||||
version = "0.2.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
glam = "0.30"
|
||||
|
||||
[features]
|
||||
test-fixtures = []
|
||||
|
||||
[workspace]
|
||||
@@ -0,0 +1,13 @@
|
||||
# AirDef headless simulation
|
||||
|
||||
This package compiles the same `airdef-mod/src/sim` source as the game adapter, with `glam` as its only dependency. A checkout does not need the Horizon submodule to build or run it:
|
||||
|
||||
```sh
|
||||
cargo test --manifest-path airdef-core/Cargo.toml
|
||||
cargo run --manifest-path airdef-core/Cargo.toml --release --example sim_run -- --list
|
||||
cargo run --manifest-path airdef-core/Cargo.toml --release --example sim_run -- --scenario big-smart
|
||||
```
|
||||
|
||||
The shared scenario harness runs for both packages. Adapter integration tests for layout and smoke stay in `airdef-mod`; `core_boundary` verifies the public type facade and deterministic combat trace.
|
||||
|
||||
This is an incremental boundary: the simulation still produces rendering and audio declarations and formats some UI text. Moving those outputs and diagnostics to adapters, remain follow-up work. The mod re-exports `airdef_core::sim` as its existing public facade.
|
||||
@@ -0,0 +1,8 @@
|
||||
//! The enemy-picture bench (#127) without building the Horizon SDK.
|
||||
extern crate airdef_core as airdef;
|
||||
#[path = "../../airdef-mod/examples/intel_bench.rs"]
|
||||
mod bench;
|
||||
|
||||
fn main() {
|
||||
bench::main();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
//! Run the same native scenario CLI without building the Horizon SDK.
|
||||
extern crate airdef_core as airdef;
|
||||
#[path = "../../airdef-mod/examples/sim_run/main.rs"]
|
||||
mod runner;
|
||||
|
||||
fn main() {
|
||||
runner::main();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
//! SDK-independent build of the same simulation used by the AirDef adapter.
|
||||
//!
|
||||
//! The source remains shared while the domain/presentation boundary is extracted;
|
||||
//! no generated copy can drift from the mod's simulation.
|
||||
#[path = "../../airdef-mod/src/sim/mod.rs"]
|
||||
pub mod sim;
|
||||
@@ -0,0 +1,166 @@
|
||||
//! The mod's headless scenarios run against the SDK-independent package too.
|
||||
extern crate airdef_core as airdef;
|
||||
#[path = "../../airdef-mod/examples/sim_run/harness.rs"]
|
||||
mod harness;
|
||||
|
||||
fn check(sc: harness::Scenario) -> harness::Report {
|
||||
let r = harness::run(&sc);
|
||||
let problems = r.problems();
|
||||
assert!(problems.is_empty(), "scenario {} failed: {problems:?}\n{}", r.name, r.text());
|
||||
assert!(r.launched > 0);
|
||||
r
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn big_smart_raid_on_61_systems() {
|
||||
let r = check(harness::big_smart());
|
||||
assert_eq!(r.units, 61);
|
||||
assert!(r.fired > 0 && r.ground_queries > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn logistics_trucks_depot_and_raid() {
|
||||
let r = check(harness::logistics());
|
||||
assert!(r.truck_busy_ticks > 0 && r.fired > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ew_and_adaptive_enemy_over_three_raids() {
|
||||
assert_eq!(check(harness::ew_adaptive()).raids_ended, 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_seed_same_result() {
|
||||
let a = harness::run(&harness::logistics());
|
||||
let b = harness::run(&harness::logistics());
|
||||
assert_eq!((a.ticks_run, a.launched, a.kills, a.hits, a.fired, a.ground_queries), (b.ticks_run, b.launched, b.kills, b.hits, b.fired, b.ground_queries));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlapping_mass_strikes_on_flat_world() {
|
||||
use airdef::sim::{Aim, Approach, DroneKind as D, Ground, Out, RaidOrder, Sam, Sim};
|
||||
use glam::DVec3;
|
||||
let sc = harness::big_smart();
|
||||
let terrain = harness::Terrain::Flat(70);
|
||||
let mut sim = Sim::new(sc.seed);
|
||||
sim.damage = false;
|
||||
for unit in &sc.units {
|
||||
let id = sim.id();
|
||||
sim.sams.push(Sam::new(id, unit.kind, DVec3::new(unit.pos.x, 70.0, unit.pos.z), unit.heading));
|
||||
}
|
||||
sim.targets = vec![DVec3::new(0.0, 70.0, 0.0)];
|
||||
sim.target_health = vec![100.0];
|
||||
// Exact composition of the menu's "Массированный удар" preset.
|
||||
let order = RaidOrder { kinds: vec![(D::Gerbera, 8), (D::Shahed, 12), (D::Geran3, 4), (D::Kh101, 4), (D::Kalibr, 4), (D::Kh22, 2), (D::Iskander, 2)], approach: Approach::AllSides, aim: Aim::Smart, dist: 4000.0 };
|
||||
let mut ground = Ground::default();
|
||||
let mut pending = Vec::new();
|
||||
let mut out = Out::default();
|
||||
let targets = sim.targets.clone();
|
||||
assert_eq!(sim.order_raid(&order, &targets, &mut out), 36);
|
||||
let mut second = false;
|
||||
let mut peak = 0;
|
||||
for now in 1..=12000 {
|
||||
if !second && !sim.planning() {
|
||||
assert_eq!(sim.order_raid(&order, &targets, &mut out), 36);
|
||||
second = true;
|
||||
}
|
||||
ground.answer(std::mem::take(&mut pending), now);
|
||||
out = Out::default();
|
||||
sim.step(&mut ground, None, &mut out);
|
||||
let wanted = ground.take_wanted();
|
||||
pending = wanted.into_iter().map(|(x, z)| (x, z, 70)).collect();
|
||||
let sections = ground.world.take_wanted(now);
|
||||
ground.world.serve(&terrain, sections, now);
|
||||
peak = peak.max(sim.drones.iter().filter(|d| d.alive()).count());
|
||||
assert!(sim.drones.iter().all(|d| d.pos.is_finite() && d.vel.is_finite()));
|
||||
if sim.launched >= 72 && peak > 36 { break; }
|
||||
}
|
||||
eprintln!("overlapping mass strikes: launched {}, peak airborne {peak}", sim.launched);
|
||||
assert!(second && sim.launched >= 72, "second raid failed to launch; launched {}", sim.launched);
|
||||
assert!(peak > 36, "raids never overlapped; peak {peak}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn overlapping_oreshnik_splits_are_recorded() {
|
||||
use airdef::sim::{Aim, Approach, DroneKind, Ground, Out, RaidOrder, Sim};
|
||||
use glam::DVec3;
|
||||
let mut sim = Sim::new(1);
|
||||
sim.damage = false;
|
||||
let targets = vec![DVec3::new(0.0, 70.0, 0.0)];
|
||||
sim.targets = targets.clone();
|
||||
sim.target_health = vec![100.0];
|
||||
let order = RaidOrder { kinds: vec![(DroneKind::Oreshnik, 40)], approach: Approach::Sector, aim: Aim::Objects, dist: 2600.0 };
|
||||
let terrain = harness::Terrain::Flat(70);
|
||||
let mut ground = Ground::default();
|
||||
let mut pending = Vec::new();
|
||||
let mut out = Out::default();
|
||||
assert_eq!(sim.order_raid(&order, &targets, &mut out), 40);
|
||||
let mut second = false;
|
||||
let mut peak = 0;
|
||||
let mut peak_subs = 0;
|
||||
for now in 1..=10000 {
|
||||
if !second && !sim.planning() {
|
||||
assert_eq!(sim.order_raid(&order, &targets, &mut out), 40);
|
||||
second = true;
|
||||
}
|
||||
ground.answer(std::mem::take(&mut pending), now);
|
||||
out = Out::default();
|
||||
sim.step(&mut ground, None, &mut out);
|
||||
pending = ground.take_wanted().into_iter().map(|(x, z)| (x, z, 70)).collect();
|
||||
let sections = ground.world.take_wanted(now);
|
||||
ground.world.serve(&terrain, sections, now);
|
||||
peak = peak.max(sim.drones.iter().filter(|d| d.alive()).count());
|
||||
peak_subs = peak_subs.max(sim.drones.iter().filter(|d| d.alive() && d.kind == DroneKind::OreshnikSub).count());
|
||||
assert!(sim.drones.iter().all(|d| d.pos.is_finite() && d.vel.is_finite()));
|
||||
if second && sim.launched >= 80 && !sim.replays.is_empty() { break; }
|
||||
}
|
||||
eprintln!("overlapping oreshnik: launched {}, peak airborne {peak}, peak submunitions {peak_subs}, replays {}", sim.launched, sim.replays.len());
|
||||
assert_eq!(sim.launched, 2880);
|
||||
assert!(peak_subs > 36, "expected many simultaneous submunitions");
|
||||
assert!(!sim.replays.is_empty(), "recording did not finish");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_maximum_menu_raids_with_answered_flat_terrain() {
|
||||
use airdef::sim::{Aim, Approach, Ground, Out, RaidOrder, Sam, Sim, catalog::RAID_KINDS};
|
||||
use glam::DVec3;
|
||||
let sc = harness::big_smart();
|
||||
let terrain = harness::Terrain::Flat(70);
|
||||
let mut sim = Sim::new(sc.seed);
|
||||
for unit in &sc.units {
|
||||
let id = sim.id();
|
||||
sim.sams.push(Sam::new(id, unit.kind, DVec3::new(unit.pos.x, 70.0, unit.pos.z), unit.heading));
|
||||
}
|
||||
let targets = vec![DVec3::new(0.0, 70.0, 0.0)];
|
||||
sim.targets = targets.clone();
|
||||
sim.target_health = vec![100.0];
|
||||
// The maximum selectable by the menu: 40 for each raid kind.
|
||||
let order = RaidOrder { kinds: RAID_KINDS.iter().copied().map(|kind| (kind, 40)).collect(), approach: Approach::AllSides, aim: Aim::Smart, dist: 300.0 };
|
||||
let expected = RAID_KINDS.len() as u32 * 40;
|
||||
let mut ground = Ground::default();
|
||||
let mut pending = Vec::new();
|
||||
let mut out = Out::default();
|
||||
assert_eq!(sim.order_raid(&order, &targets, &mut out), expected);
|
||||
let mut second = false;
|
||||
let mut overlap = false;
|
||||
let mut peak = 0;
|
||||
for now in 1..=5000 {
|
||||
if !second && !sim.planning() {
|
||||
overlap = !sim.queue.is_empty() || sim.drones.iter().any(|d| d.alive());
|
||||
assert_eq!(sim.order_raid(&order, &targets, &mut out), expected);
|
||||
second = true;
|
||||
}
|
||||
ground.answer(std::mem::take(&mut pending), now);
|
||||
out = Out::default();
|
||||
sim.step(&mut ground, None, &mut out);
|
||||
pending = ground.take_wanted().into_iter().map(|(x, z)| (x, z, 70)).collect();
|
||||
let sections = ground.world.take_wanted(now);
|
||||
ground.world.serve(&terrain, sections, now);
|
||||
peak = peak.max(sim.drones.iter().filter(|d| d.alive()).count());
|
||||
assert!(sim.drones.iter().all(|d| d.pos.is_finite() && d.vel.is_finite()));
|
||||
if second && sim.launched >= expected * 2 && !sim.replays.is_empty() { break; }
|
||||
}
|
||||
eprintln!("max menu raids: requested {}, launched {}, peak {peak}, replays {}", expected * 2, sim.launched, sim.replays.len());
|
||||
assert!(second && overlap && sim.launched >= expected * 2 && peak > 40);
|
||||
assert!(!sim.replays.is_empty(), "raid recording did not finish");
|
||||
}
|
||||
Generated
+8
@@ -6,10 +6,18 @@ version = 4
|
||||
name = "airdef"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"airdef-core",
|
||||
"glam",
|
||||
"horizon-mod-sdk",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "airdef-core"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"glam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.104"
|
||||
|
||||
@@ -9,9 +9,13 @@ description = "AirDef: long-range drones and air defence for the AirDef fork of
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
airdef-core = { path = "../airdef-core" }
|
||||
horizon-mod-sdk = { path = "../horizon/crates/horizon-mod-sdk" }
|
||||
glam = "0.30"
|
||||
|
||||
[dev-dependencies]
|
||||
airdef-core = { path = "../airdef-core", features = ["test-fixtures"] }
|
||||
|
||||
[profile.release]
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
Status of terrain-aware flight (not committed):
|
||||
- Done: sim/terrain.rs (look-ahead terrain following, dive only down a clear line, interceptor steer/loft/self-destruct, launch predicate), planner relief pricing + spawn above land (raid.rs), SAM launch check + why line (mod.rs, why.rs), Tally.scrubbed, world.probe, harness terrain fixes, 6 new tests in regress.rs. Build has no warnings; cargo test passes (43 + 4).
|
||||
- Numbers (6 seeds, sim_run big-smart): hills:70:100:140 crashes 13 -> 3, hills:70:150:120 29 -> 2, default hills 2 -> 2 (all terminal-dive near-target by Kh101/Kalibr).
|
||||
- Half-done / known: terminal dive of slow-turning cruise missiles still sometimes hits ground 7-20 blocks short of target; hills:70:150:120 tick max 38 ms / p95 2 ms (planner terrain phase + ground queries) not profiled; not tested in game.
|
||||
- Next: tune dive geometry for Kh101/Kalibr, profile the planner's 1024-columns-per-tick height requests, optional in-game check.
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/amraam_missile"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/barge"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/beam"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/buk_chassis"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/buk_chassis_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/buk_rails"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/buk_rails_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/buk_turret"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/buk_turret_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/depot"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/depot_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/ew_truck"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/ew_truck_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/flare"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/gepard_guns_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/gepard_hull_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/gepard_turret_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/irist_chassis"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/irist_chassis_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/irist_missile"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/irist_tubes"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/irist_tubes_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/launch_ramp"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/launcher_truck"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/nasams_launcher"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/nasams_launcher_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/nasams_trailer"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/nasams_trailer_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/p1sun"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/p1sun_launcher"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/p1sun_launcher_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/pantsir_pack_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/pantsir_truck_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/pantsir_turret_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/patriot_launcher_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/patriot_trailer_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/radar_mast"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/radar_mast_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/radar_truck"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/radar_truck_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/rm48u"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/s400_chassis_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/s400_tubes_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/s500_chassis_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/s500_tubes_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/searchlight"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/searchlight_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/searchlight_head"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/searchlight_head_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/tor_chassis"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/tor_chassis_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/tor_turret"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/tor_turret_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/tpk_cap"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/tpk_cap_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/tzm_truck"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"variants": {
|
||||
"": {
|
||||
"model": "airdef:block/tzm_truck_burnt"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_buk"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_depot"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_ew"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_irist"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_light"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_nasams"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_p1sun"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_radar"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_tor"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/deploy_tzm"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/flare_gun"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"model": {
|
||||
"type": "minecraft:model",
|
||||
"model": "airdef:item/mover"
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@
|
||||
"block.airdef.geran3": "Герань-3",
|
||||
"block.airdef.geran3_flame": "Факел",
|
||||
"block.airdef.pac3": "PAC-3 MSE",
|
||||
"block.airdef.irist_missile": "IRIS-T SLM",
|
||||
"block.airdef.amraam_missile": "AMRAAM",
|
||||
"block.airdef.flame": "Факел",
|
||||
"block.airdef.patriot_launcher": "ПУ M903",
|
||||
"block.airdef.patriot_truck": "Тягач",
|
||||
@@ -28,10 +30,13 @@
|
||||
"block.airdef.s400_tubes": "ТПК 48Н6",
|
||||
"block.airdef.s500_chassis": "Шасси С-500",
|
||||
"block.airdef.s500_tubes": "ТПК 77Н6",
|
||||
"block.airdef.tpk_cap": "Крышка ТПК (анимация)",
|
||||
"block.airdef.radar_mast": "Радиомачта (секция высоты)",
|
||||
"block.airdef.radar_trailer": "Прицеп РЛС",
|
||||
"block.airdef.radar_panel": "РЛС обзора",
|
||||
"block.airdef.m57e6": "57Э6",
|
||||
"block.airdef.m48n6": "48Н6",
|
||||
"block.airdef.rm48u": "RM48U (игровой профиль)",
|
||||
"block.airdef.m77n6": "77Н6",
|
||||
"block.airdef.kalibr": "Калибр",
|
||||
"block.airdef.kh22": "Х-22",
|
||||
@@ -39,7 +44,59 @@
|
||||
"block.airdef.iskander": "Искандер-М",
|
||||
"block.airdef.kinzhal": "Кинжал",
|
||||
"block.airdef.tracer": "Трассер",
|
||||
"block.airdef.irist_chassis": "Шасси IRIS-T SLM",
|
||||
"block.airdef.irist_tubes": "Пакет IRIS-T",
|
||||
"block.airdef.nasams_trailer": "Прицеп NASAMS",
|
||||
"block.airdef.nasams_launcher": "Пусковая NASAMS",
|
||||
"block.airdef.buk_chassis": "Шасси Бука",
|
||||
"block.airdef.buk_turret": "Башня Бука",
|
||||
"block.airdef.buk_rails": "Ракеты Бука",
|
||||
"block.airdef.tor_chassis": "Шасси Тора",
|
||||
"block.airdef.tor_turret": "Башня Тора",
|
||||
"block.airdef.p1sun_launcher": "Пусковая P1-SUN",
|
||||
"block.airdef.p1sun": "Дрон-перехватчик P1-SUN",
|
||||
"block.airdef.radar_truck": "Шасси РЛС",
|
||||
"block.airdef.depot": "Склад боеприпасов",
|
||||
"block.airdef.tzm_truck": "Транспортно-заряжающая машина",
|
||||
"block.airdef.barge": "Баржа",
|
||||
"block.airdef.launch_ramp": "Рампа запуска дронов",
|
||||
"block.airdef.launcher_truck": "Пусковая машина ракет",
|
||||
"block.airdef.ew_truck": "Станция РЭБ",
|
||||
"block.airdef.searchlight": "Прожектор",
|
||||
"block.airdef.searchlight_head": "Голова прожектора",
|
||||
"block.airdef.beam": "Луч прожектора",
|
||||
"block.airdef.flare": "Осветительная ракета",
|
||||
"block.airdef.reentry": "Боевой блок",
|
||||
"block.airdef.patriot_trailer_burnt": "Полуприцеп (остов)",
|
||||
"block.airdef.patriot_launcher_burnt": "ПУ M903 (остов)",
|
||||
"block.airdef.gepard_hull_burnt": "Корпус Гепарда (остов)",
|
||||
"block.airdef.gepard_turret_burnt": "Башня Гепарда (остов)",
|
||||
"block.airdef.gepard_guns_burnt": "Пушки Гепарда (остов)",
|
||||
"block.airdef.pantsir_truck_burnt": "Шасси Панциря (остов)",
|
||||
"block.airdef.pantsir_turret_burnt": "Модуль Панциря (остов)",
|
||||
"block.airdef.pantsir_pack_burnt": "Ракеты и пушки Панциря (остов)",
|
||||
"block.airdef.s400_chassis_burnt": "Шасси С-400 (остов)",
|
||||
"block.airdef.s400_tubes_burnt": "ТПК 48Н6 (остов)",
|
||||
"block.airdef.s500_chassis_burnt": "Шасси С-500 (остов)",
|
||||
"block.airdef.s500_tubes_burnt": "ТПК 77Н6 (остов)",
|
||||
"block.airdef.tpk_cap_burnt": "Крышка ТПК (анимация) (остов)",
|
||||
"block.airdef.radar_mast_burnt": "Радиомачта (секция высоты) (остов)",
|
||||
"block.airdef.irist_chassis_burnt": "Шасси IRIS-T SLM (остов)",
|
||||
"block.airdef.irist_tubes_burnt": "Пакет IRIS-T (остов)",
|
||||
"block.airdef.nasams_trailer_burnt": "Прицеп NASAMS (остов)",
|
||||
"block.airdef.nasams_launcher_burnt": "Пусковая NASAMS (остов)",
|
||||
"block.airdef.buk_chassis_burnt": "Шасси Бука (остов)",
|
||||
"block.airdef.buk_turret_burnt": "Башня Бука (остов)",
|
||||
"block.airdef.buk_rails_burnt": "Ракеты Бука (остов)",
|
||||
"block.airdef.tor_chassis_burnt": "Шасси Тора (остов)",
|
||||
"block.airdef.tor_turret_burnt": "Башня Тора (остов)",
|
||||
"block.airdef.p1sun_launcher_burnt": "Пусковая P1-SUN (остов)",
|
||||
"block.airdef.radar_truck_burnt": "Шасси РЛС (остов)",
|
||||
"block.airdef.depot_burnt": "Склад боеприпасов (остов)",
|
||||
"block.airdef.tzm_truck_burnt": "Транспортно-заряжающая машина (остов)",
|
||||
"block.airdef.ew_truck_burnt": "Станция РЭБ (остов)",
|
||||
"block.airdef.searchlight_burnt": "Прожектор (остов)",
|
||||
"block.airdef.searchlight_head_burnt": "Голова прожектора (остов)",
|
||||
"item.airdef.designator": "Целеуказатель",
|
||||
"item.airdef.launch_shahed": "Пуск: Шахед-136",
|
||||
"item.airdef.launch_geran3": "Пуск: Герань-3",
|
||||
@@ -56,6 +113,18 @@
|
||||
"item.airdef.launch_iskander": "Пуск: Искандер-М",
|
||||
"item.airdef.launch_kinzhal": "Пуск: Кинжал",
|
||||
"item.airdef.launch_oreshnik": "Пуск: Орешник",
|
||||
"item.airdef.deploy_irist": "Развернуть IRIS-T SLM",
|
||||
"item.airdef.deploy_nasams": "Развернуть NASAMS",
|
||||
"item.airdef.deploy_buk": "Развернуть Бук-М2",
|
||||
"item.airdef.deploy_tor": "Развернуть Тор-М2",
|
||||
"item.airdef.deploy_p1sun": "Развернуть P1-SUN",
|
||||
"item.airdef.deploy_radar": "Развернуть РЛС дальнего обнаружения",
|
||||
"item.airdef.deploy_depot": "Развернуть склад боеприпасов",
|
||||
"item.airdef.deploy_tzm": "Развернуть ТЗМ",
|
||||
"item.airdef.deploy_ew": "Развернуть станцию РЭБ",
|
||||
"item.airdef.deploy_light": "Развернуть прожектор",
|
||||
"item.airdef.mover": "Пульт перемещения",
|
||||
"item.airdef.flare_gun": "Осветительная ракета",
|
||||
"item.airdef.remover": "Свернуть ПВО",
|
||||
"item.airdef.cam_shahed": "Шахед-136",
|
||||
"item.airdef.cam_geran3": "Герань-3",
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
"block.airdef.geran3": "Герань-3",
|
||||
"block.airdef.geran3_flame": "Факел",
|
||||
"block.airdef.pac3": "PAC-3 MSE",
|
||||
"block.airdef.irist_missile": "IRIS-T SLM",
|
||||
"block.airdef.amraam_missile": "AMRAAM",
|
||||
"block.airdef.flame": "Факел",
|
||||
"block.airdef.patriot_launcher": "ПУ M903",
|
||||
"block.airdef.patriot_truck": "Тягач",
|
||||
@@ -28,10 +30,13 @@
|
||||
"block.airdef.s400_tubes": "ТПК 48Н6",
|
||||
"block.airdef.s500_chassis": "Шасси С-500",
|
||||
"block.airdef.s500_tubes": "ТПК 77Н6",
|
||||
"block.airdef.tpk_cap": "Крышка ТПК (анимация)",
|
||||
"block.airdef.radar_mast": "Радиомачта (секция высоты)",
|
||||
"block.airdef.radar_trailer": "Прицеп РЛС",
|
||||
"block.airdef.radar_panel": "РЛС обзора",
|
||||
"block.airdef.m57e6": "57Э6",
|
||||
"block.airdef.m48n6": "48Н6",
|
||||
"block.airdef.rm48u": "RM48U (игровой профиль)",
|
||||
"block.airdef.m77n6": "77Н6",
|
||||
"block.airdef.kalibr": "Калибр",
|
||||
"block.airdef.kh22": "Х-22",
|
||||
@@ -39,7 +44,59 @@
|
||||
"block.airdef.iskander": "Искандер-М",
|
||||
"block.airdef.kinzhal": "Кинжал",
|
||||
"block.airdef.tracer": "Трассер",
|
||||
"block.airdef.irist_chassis": "Шасси IRIS-T SLM",
|
||||
"block.airdef.irist_tubes": "Пакет IRIS-T",
|
||||
"block.airdef.nasams_trailer": "Прицеп NASAMS",
|
||||
"block.airdef.nasams_launcher": "Пусковая NASAMS",
|
||||
"block.airdef.buk_chassis": "Шасси Бука",
|
||||
"block.airdef.buk_turret": "Башня Бука",
|
||||
"block.airdef.buk_rails": "Ракеты Бука",
|
||||
"block.airdef.tor_chassis": "Шасси Тора",
|
||||
"block.airdef.tor_turret": "Башня Тора",
|
||||
"block.airdef.p1sun_launcher": "Пусковая P1-SUN",
|
||||
"block.airdef.p1sun": "Дрон-перехватчик P1-SUN",
|
||||
"block.airdef.radar_truck": "Шасси РЛС",
|
||||
"block.airdef.depot": "Склад боеприпасов",
|
||||
"block.airdef.tzm_truck": "Транспортно-заряжающая машина",
|
||||
"block.airdef.barge": "Баржа",
|
||||
"block.airdef.launch_ramp": "Рампа запуска дронов",
|
||||
"block.airdef.launcher_truck": "Пусковая машина ракет",
|
||||
"block.airdef.ew_truck": "Станция РЭБ",
|
||||
"block.airdef.searchlight": "Прожектор",
|
||||
"block.airdef.searchlight_head": "Голова прожектора",
|
||||
"block.airdef.beam": "Луч прожектора",
|
||||
"block.airdef.flare": "Осветительная ракета",
|
||||
"block.airdef.reentry": "Боевой блок",
|
||||
"block.airdef.patriot_trailer_burnt": "Полуприцеп (остов)",
|
||||
"block.airdef.patriot_launcher_burnt": "ПУ M903 (остов)",
|
||||
"block.airdef.gepard_hull_burnt": "Корпус Гепарда (остов)",
|
||||
"block.airdef.gepard_turret_burnt": "Башня Гепарда (остов)",
|
||||
"block.airdef.gepard_guns_burnt": "Пушки Гепарда (остов)",
|
||||
"block.airdef.pantsir_truck_burnt": "Шасси Панциря (остов)",
|
||||
"block.airdef.pantsir_turret_burnt": "Модуль Панциря (остов)",
|
||||
"block.airdef.pantsir_pack_burnt": "Ракеты и пушки Панциря (остов)",
|
||||
"block.airdef.s400_chassis_burnt": "Шасси С-400 (остов)",
|
||||
"block.airdef.s400_tubes_burnt": "ТПК 48Н6 (остов)",
|
||||
"block.airdef.s500_chassis_burnt": "Шасси С-500 (остов)",
|
||||
"block.airdef.s500_tubes_burnt": "ТПК 77Н6 (остов)",
|
||||
"block.airdef.tpk_cap_burnt": "Крышка ТПК (анимация) (остов)",
|
||||
"block.airdef.radar_mast_burnt": "Радиомачта (секция высоты) (остов)",
|
||||
"block.airdef.irist_chassis_burnt": "Шасси IRIS-T SLM (остов)",
|
||||
"block.airdef.irist_tubes_burnt": "Пакет IRIS-T (остов)",
|
||||
"block.airdef.nasams_trailer_burnt": "Прицеп NASAMS (остов)",
|
||||
"block.airdef.nasams_launcher_burnt": "Пусковая NASAMS (остов)",
|
||||
"block.airdef.buk_chassis_burnt": "Шасси Бука (остов)",
|
||||
"block.airdef.buk_turret_burnt": "Башня Бука (остов)",
|
||||
"block.airdef.buk_rails_burnt": "Ракеты Бука (остов)",
|
||||
"block.airdef.tor_chassis_burnt": "Шасси Тора (остов)",
|
||||
"block.airdef.tor_turret_burnt": "Башня Тора (остов)",
|
||||
"block.airdef.p1sun_launcher_burnt": "Пусковая P1-SUN (остов)",
|
||||
"block.airdef.radar_truck_burnt": "Шасси РЛС (остов)",
|
||||
"block.airdef.depot_burnt": "Склад боеприпасов (остов)",
|
||||
"block.airdef.tzm_truck_burnt": "Транспортно-заряжающая машина (остов)",
|
||||
"block.airdef.ew_truck_burnt": "Станция РЭБ (остов)",
|
||||
"block.airdef.searchlight_burnt": "Прожектор (остов)",
|
||||
"block.airdef.searchlight_head_burnt": "Голова прожектора (остов)",
|
||||
"item.airdef.designator": "Целеуказатель",
|
||||
"item.airdef.launch_shahed": "Пуск: Шахед-136",
|
||||
"item.airdef.launch_geran3": "Пуск: Герань-3",
|
||||
@@ -56,6 +113,18 @@
|
||||
"item.airdef.launch_iskander": "Пуск: Искандер-М",
|
||||
"item.airdef.launch_kinzhal": "Пуск: Кинжал",
|
||||
"item.airdef.launch_oreshnik": "Пуск: Орешник",
|
||||
"item.airdef.deploy_irist": "Развернуть IRIS-T SLM",
|
||||
"item.airdef.deploy_nasams": "Развернуть NASAMS",
|
||||
"item.airdef.deploy_buk": "Развернуть Бук-М2",
|
||||
"item.airdef.deploy_tor": "Развернуть Тор-М2",
|
||||
"item.airdef.deploy_p1sun": "Развернуть P1-SUN",
|
||||
"item.airdef.deploy_radar": "Развернуть РЛС дальнего обнаружения",
|
||||
"item.airdef.deploy_depot": "Развернуть склад боеприпасов",
|
||||
"item.airdef.deploy_tzm": "Развернуть ТЗМ",
|
||||
"item.airdef.deploy_ew": "Развернуть станцию РЭБ",
|
||||
"item.airdef.deploy_light": "Развернуть прожектор",
|
||||
"item.airdef.mover": "Пульт перемещения",
|
||||
"item.airdef.flare_gun": "Осветительная ракета",
|
||||
"item.airdef.remover": "Свернуть ПВО",
|
||||
"item.airdef.cam_shahed": "Шахед-136",
|
||||
"item.airdef.cam_geran3": "Герань-3",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,896 @@
|
||||
{
|
||||
"ambientocclusion": false,
|
||||
"textures": {
|
||||
"dark_metal": "airdef:block/dark_metal",
|
||||
"metal": "airdef:block/metal",
|
||||
"ru_khaki_s31": "airdef:block/ru_khaki_s31",
|
||||
"ru_khaki_s33": "airdef:block/ru_khaki_s33",
|
||||
"particle": "airdef:block/dark_metal"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [
|
||||
-16.0,
|
||||
0.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
32.0,
|
||||
3.0,
|
||||
32.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-16.0,
|
||||
3.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
32.0,
|
||||
4.0,
|
||||
32.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
1.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
1.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
1.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
1.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-15.0,
|
||||
3.0,
|
||||
-15.0
|
||||
],
|
||||
"to": [
|
||||
31.0,
|
||||
4.4,
|
||||
31.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
1.4000000000000004
|
||||
],
|
||||
"texture": "#ru_khaki_s31"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
1.4000000000000004
|
||||
],
|
||||
"texture": "#ru_khaki_s31"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
1.4000000000000004
|
||||
],
|
||||
"texture": "#ru_khaki_s31"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
1.4000000000000004
|
||||
],
|
||||
"texture": "#ru_khaki_s31"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#ru_khaki_s33"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#ru_khaki_s33"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-16.0,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
-14.0,
|
||||
6.0,
|
||||
32.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
30.0,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
32.0,
|
||||
6.0,
|
||||
32.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-16.0,
|
||||
4.0,
|
||||
30.0
|
||||
],
|
||||
"to": [
|
||||
32.0,
|
||||
6.0,
|
||||
32.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-16.0,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
32.0,
|
||||
6.0,
|
||||
-14.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-12.0,
|
||||
4.4,
|
||||
-10.0
|
||||
],
|
||||
"to": [
|
||||
-9.0,
|
||||
6.5,
|
||||
-7.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
25.0,
|
||||
4.4,
|
||||
-10.0
|
||||
],
|
||||
"to": [
|
||||
28.0,
|
||||
6.5,
|
||||
-7.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-12.0,
|
||||
4.4,
|
||||
8.0
|
||||
],
|
||||
"to": [
|
||||
-9.0,
|
||||
6.5,
|
||||
11.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
25.0,
|
||||
4.4,
|
||||
8.0
|
||||
],
|
||||
"to": [
|
||||
28.0,
|
||||
6.5,
|
||||
11.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-12.0,
|
||||
4.4,
|
||||
26.0
|
||||
],
|
||||
"to": [
|
||||
-9.0,
|
||||
6.5,
|
||||
29.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
25.0,
|
||||
4.4,
|
||||
26.0
|
||||
],
|
||||
"to": [
|
||||
28.0,
|
||||
6.5,
|
||||
29.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
2.0999999999999996
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"ambientocclusion": false,
|
||||
"textures": {
|
||||
"beam": "airdef:block/beam",
|
||||
"particle": "airdef:block/beam"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [
|
||||
6.5,
|
||||
6.5,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
9.5,
|
||||
9.5,
|
||||
32.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#beam"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#beam"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#beam"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#beam"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#beam"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#beam"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,896 @@
|
||||
{
|
||||
"ambientocclusion": false,
|
||||
"textures": {
|
||||
"missile_ru": "airdef:block/missile_ru",
|
||||
"booster": "airdef:block/booster",
|
||||
"dark_metal": "airdef:block/dark_metal",
|
||||
"pilot_olive": "airdef:block/pilot_olive",
|
||||
"particle": "airdef:block/missile_ru"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [
|
||||
-10.5,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
-7.5,
|
||||
7.0,
|
||||
26.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-10.8,
|
||||
3.5,
|
||||
20.0
|
||||
],
|
||||
"to": [
|
||||
-7.2,
|
||||
7.5,
|
||||
27.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000005,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000005,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000005,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000005,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-9.5,
|
||||
2.0,
|
||||
-6.0
|
||||
],
|
||||
"to": [
|
||||
-8.5,
|
||||
4.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-4.5,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
-1.5,
|
||||
7.0,
|
||||
26.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-4.8,
|
||||
3.5,
|
||||
20.0
|
||||
],
|
||||
"to": [
|
||||
-1.2,
|
||||
7.5,
|
||||
27.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.5999999999999996,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.5999999999999996,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.5999999999999996,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.5999999999999996,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-3.5,
|
||||
2.0,
|
||||
-6.0
|
||||
],
|
||||
"to": [
|
||||
-2.5,
|
||||
4.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
11.5,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
14.5,
|
||||
7.0,
|
||||
26.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
11.2,
|
||||
3.5,
|
||||
20.0
|
||||
],
|
||||
"to": [
|
||||
14.8,
|
||||
7.5,
|
||||
27.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
12.5,
|
||||
2.0,
|
||||
-6.0
|
||||
],
|
||||
"to": [
|
||||
13.5,
|
||||
4.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
17.5,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
20.5,
|
||||
7.0,
|
||||
26.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
17.2,
|
||||
3.5,
|
||||
20.0
|
||||
],
|
||||
"to": [
|
||||
20.8,
|
||||
7.5,
|
||||
27.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
18.5,
|
||||
2.0,
|
||||
-6.0
|
||||
],
|
||||
"to": [
|
||||
19.5,
|
||||
4.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-12.0,
|
||||
0.0,
|
||||
-2.0
|
||||
],
|
||||
"to": [
|
||||
28.0,
|
||||
2.0,
|
||||
14.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,896 @@
|
||||
{
|
||||
"ambientocclusion": false,
|
||||
"textures": {
|
||||
"missile_ru_b": "airdef:block/missile_ru_b",
|
||||
"booster_b": "airdef:block/booster_b",
|
||||
"dark_metal_b": "airdef:block/dark_metal_b",
|
||||
"pilot_olive_b": "airdef:block/pilot_olive_b",
|
||||
"particle": "airdef:block/missile_ru_b"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [
|
||||
-10.5,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
-7.5,
|
||||
7.0,
|
||||
26.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-10.8,
|
||||
3.5,
|
||||
20.0
|
||||
],
|
||||
"to": [
|
||||
-7.2,
|
||||
7.5,
|
||||
27.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000005,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000005,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000005,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000005,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-9.5,
|
||||
2.0,
|
||||
-6.0
|
||||
],
|
||||
"to": [
|
||||
-8.5,
|
||||
4.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-4.5,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
-1.5,
|
||||
7.0,
|
||||
26.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-4.8,
|
||||
3.5,
|
||||
20.0
|
||||
],
|
||||
"to": [
|
||||
-1.2,
|
||||
7.5,
|
||||
27.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.5999999999999996,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.5999999999999996,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.5999999999999996,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.5999999999999996,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-3.5,
|
||||
2.0,
|
||||
-6.0
|
||||
],
|
||||
"to": [
|
||||
-2.5,
|
||||
4.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
11.5,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
14.5,
|
||||
7.0,
|
||||
26.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
11.2,
|
||||
3.5,
|
||||
20.0
|
||||
],
|
||||
"to": [
|
||||
14.8,
|
||||
7.5,
|
||||
27.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
12.5,
|
||||
2.0,
|
||||
-6.0
|
||||
],
|
||||
"to": [
|
||||
13.5,
|
||||
4.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
17.5,
|
||||
4.0,
|
||||
-16.0
|
||||
],
|
||||
"to": [
|
||||
20.5,
|
||||
7.0,
|
||||
26.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.0,
|
||||
16
|
||||
],
|
||||
"texture": "#missile_ru_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
17.2,
|
||||
3.5,
|
||||
20.0
|
||||
],
|
||||
"to": [
|
||||
20.8,
|
||||
7.5,
|
||||
27.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
3.6000000000000014,
|
||||
7.0
|
||||
],
|
||||
"texture": "#booster_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
18.5,
|
||||
2.0,
|
||||
-6.0
|
||||
],
|
||||
"to": [
|
||||
19.5,
|
||||
4.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
1.0,
|
||||
16
|
||||
],
|
||||
"texture": "#dark_metal_b"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-12.0,
|
||||
0.0,
|
||||
-2.0
|
||||
],
|
||||
"to": [
|
||||
28.0,
|
||||
2.0,
|
||||
14.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#pilot_olive_b"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#pilot_olive_b"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#pilot_olive_b"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#pilot_olive_b"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#pilot_olive_b"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#pilot_olive_b"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
{
|
||||
"ambientocclusion": false,
|
||||
"textures": {
|
||||
"pilot_olive": "airdef:block/pilot_olive",
|
||||
"radar_face": "airdef:block/radar_face",
|
||||
"dark_metal": "airdef:block/dark_metal",
|
||||
"metal": "airdef:block/metal",
|
||||
"particle": "airdef:block/pilot_olive"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"from": [
|
||||
-4.0,
|
||||
0.0,
|
||||
-4.0
|
||||
],
|
||||
"to": [
|
||||
20.0,
|
||||
7.0,
|
||||
20.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
7.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
7.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
7.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
7.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-2.0,
|
||||
7.0,
|
||||
2.0
|
||||
],
|
||||
"to": [
|
||||
18.0,
|
||||
10.0,
|
||||
18.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
3.0
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
16
|
||||
],
|
||||
"texture": "#pilot_olive"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
1.0,
|
||||
2.0,
|
||||
-10.0
|
||||
],
|
||||
"to": [
|
||||
11.0,
|
||||
14.0,
|
||||
-4.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
10.0,
|
||||
12.0
|
||||
],
|
||||
"texture": "#radar_face"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
10.0,
|
||||
12.0
|
||||
],
|
||||
"texture": "#radar_face"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
6.0,
|
||||
12.0
|
||||
],
|
||||
"texture": "#radar_face"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
6.0,
|
||||
12.0
|
||||
],
|
||||
"texture": "#radar_face"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
10.0,
|
||||
6.0
|
||||
],
|
||||
"texture": "#radar_face"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
10.0,
|
||||
6.0
|
||||
],
|
||||
"texture": "#radar_face"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
0.0,
|
||||
1.0,
|
||||
-5.0
|
||||
],
|
||||
"to": [
|
||||
16.0,
|
||||
3.0,
|
||||
-3.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
16,
|
||||
2.0
|
||||
],
|
||||
"texture": "#dark_metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
-4.0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"to": [
|
||||
-2.0,
|
||||
9.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
12.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
12.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
12.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
12.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [
|
||||
18.0,
|
||||
7.0,
|
||||
4.0
|
||||
],
|
||||
"to": [
|
||||
20.0,
|
||||
9.0,
|
||||
16.0
|
||||
],
|
||||
"faces": {
|
||||
"north": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"south": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"east": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
12.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"west": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
12.0,
|
||||
2.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"up": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
12.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
},
|
||||
"down": {
|
||||
"uv": [
|
||||
0,
|
||||
0,
|
||||
2.0,
|
||||
12.0
|
||||
],
|
||||
"texture": "#metal"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user