mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-10-02 11:10:50 +03:00
Merge branch 'master' of https://github.com/Anuken/Mindustry into v9
# Conflicts: # core/src/mindustry/mod/DataImagePacker.java # gradle.properties
This commit is contained in:
@@ -244,7 +244,7 @@ public class EntityProcess extends BaseProcessor{
|
||||
Stype repr = types.first();
|
||||
String groupType = repr.annotation(Component.class).base() ? baseName(repr) : interfaceName(repr);
|
||||
|
||||
String name = group.name().startsWith("g") ? group.name().substring(1) : group.name();
|
||||
String name = group.name();
|
||||
|
||||
boolean collides = an.collide();
|
||||
groupDefs.add(new GroupDefinition(name,
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = \u26a0 Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installed]
|
||||
mod.display = [gray]Mod:[accent] {0}
|
||||
mod.enabled = [lightgray]Enabled
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Усталявана]
|
||||
mod.display = [gray]Мадыфікацыя:[orange] {0}
|
||||
mod.enabled = [lightgray]Уключана
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Достъпно обновление: {0}
|
||||
mods.update.all = Обнови всички модове
|
||||
mods.downloading = [accent]Изтегляне на мод: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Инсталиран]
|
||||
mod.display = [gray]Мод:[orange] {0}
|
||||
mod.enabled = [lightgray]Активиран
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Instal·lat]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Activat
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Instalováno]
|
||||
mod.display = [gray]Modifikace:[][orange] {0}[]
|
||||
mod.enabled = [lightgray]Povoleno[]
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installed]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Aktiveret
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installiert]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Aktiviert
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Instalado]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Activado
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installed]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Enabled
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Instalatuta]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Gaituta
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Asennettu]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Käytössä
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Nakuha]
|
||||
mod.display = [gray]Pangbago:[orange] {0}
|
||||
mod.enabled = [lightgray]Gumagana
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installé]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Activé
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Frissítés érhető el: {0}
|
||||
mods.update.all = Összes mod frissítése
|
||||
mods.downloading = [accent]Mod letöltése: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Telepítve]
|
||||
mod.display = [gray]Mod:[accent] {0}
|
||||
mod.enabled = [lightgray]Aktív
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Pembaruan Tersedia: {0}
|
||||
mods.update.all = Perbarui Semua Mod
|
||||
mods.downloading = [accent]Mengunduh Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Terpasang]
|
||||
mod.display = [gray]Mod:[accent] {0}
|
||||
mod.enabled = [lightgray]Aktif
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installed]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Abilitato
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[インストール済み]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]有効
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [설치됨]
|
||||
mod.display = [gray]모드:[orange] {0}
|
||||
mod.enabled = [lightgray]활성화됨
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Įdiegta]
|
||||
mod.display = [gray]Modifikacijos:[orange] {0}
|
||||
mod.enabled = [lightgray]Įjungta
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installed]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Ingeschakeld
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installed]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Ingeschakeld
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Zainstalowany]
|
||||
mod.display = [gray]Modyfikacja:[orange] {0}
|
||||
mod.enabled = [lightgray]Włączony
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Instalado]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Ativado
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Instalado]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Ativado
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Instalat]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Activat
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Доступно обновление: {0}
|
||||
mods.update.all = Обновить все модификации
|
||||
mods.downloading = [accent]Загрузка модификации: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Установлено]
|
||||
mod.display = [gray]Модификация:[accent] {0}
|
||||
mod.enabled = [lightgray]Включено
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [Instalirano]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Omogućeno
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installed]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Aktiverad
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[ติดตั้งแล้ว]
|
||||
mod.display = [gray]ม็อด:[orange] {0}
|
||||
mod.enabled = [lightgray]เปิดใช้งาน
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Installed]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Enabled
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Yüklendi]
|
||||
mod.display = [gray]Mod:[orange] {0}
|
||||
mod.enabled = [lightgray]Etkin
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Доступне оновлення: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Установлено]
|
||||
mod.display = [gray]Модифікація:[orange] {0}
|
||||
mod.enabled = [lightgray]Увімкнено
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Cập Nhật Có Sẵn: {0}
|
||||
mods.update.all = Cập Nhật Tất Cả Bản Mod
|
||||
mods.downloading = [accent]Đang Tải Xuống Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[Đã cài đặt]
|
||||
mod.display = [gray]Mod:[accent] {0}
|
||||
mod.enabled = [lightgray]Đã bật
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]有可用更新:{0}
|
||||
mods.update.all = 更新所有模组
|
||||
mods.downloading = [accent]正在下载模组:{0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[已安装]
|
||||
mod.display = [gray]模组:[orange] {0}
|
||||
mod.enabled = [lightgray]已启用
|
||||
|
||||
@@ -154,6 +154,10 @@ mods.update.available = [lightgray]Update Available: {0}
|
||||
mods.update.all = Update All Mods
|
||||
mods.downloading = [accent]Downloading Mod: {0}
|
||||
|
||||
mod.warning = ⚠ Mods can execute arbitrary code.\n\nNever download mods from sources you don't trust - they could steal data or compromise your computer.
|
||||
mod.warning.read = I actually read this
|
||||
mod.warning.accept = I accept the responsibility
|
||||
mod.warning.deny = Disable my mods
|
||||
mod.installed = [[已安裝]
|
||||
mod.display = [gray]模組:[orange]{0}
|
||||
mod.enabled = [lightgray]已啟用
|
||||
|
||||
@@ -175,7 +175,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
//autohost for pvp maps
|
||||
Events.on(WorldLoadEvent.class, event -> app.post(() -> {
|
||||
if(state.rules.pvp && !net.active() && !state.rules.pauseDisabled){
|
||||
if(state.rules.pvp && !net.active() && !state.rules.pauseDisabled && !state.isEditor() && !ui.editor.isShown()){
|
||||
try{
|
||||
net.host(port);
|
||||
player.admin = true;
|
||||
|
||||
@@ -461,31 +461,38 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
|
||||
protected void updateEntities(){
|
||||
boolean editor = state.isEditor();
|
||||
|
||||
PerfCounter.entityUpdate.begin();
|
||||
|
||||
PerfCounter.entityMisc.begin();
|
||||
Groups.updatePooling();
|
||||
Groups.bullet.updatePhysics();
|
||||
Groups.unit.updatePhysics();
|
||||
Groups.all.update();
|
||||
Groups.player.update();
|
||||
Groups.effect.update();
|
||||
if(!editor) Groups.all.update();
|
||||
PerfCounter.entityMisc.end();
|
||||
|
||||
PerfCounter.unitUpdate.begin();
|
||||
Groups.unit.update();
|
||||
if(editor){
|
||||
Groups.unit.update(Unitc::isPlayer);
|
||||
}else{
|
||||
Groups.unit.update();
|
||||
}
|
||||
PerfCounter.unitUpdate.end();
|
||||
|
||||
PerfCounter.powerUpdate.begin();
|
||||
if(!state.isEditor()) Groups.powerGraph.update();
|
||||
if(!editor) Groups.powerGraph.update();
|
||||
PerfCounter.powerUpdate.end();
|
||||
|
||||
PerfCounter.buildingUpdate.begin();
|
||||
if(!state.isEditor()) Groups.build.update();
|
||||
if(!editor) Groups.build.update();
|
||||
PerfCounter.buildingUpdate.end();
|
||||
|
||||
PerfCounter.bulletUpdate.begin();
|
||||
Groups.bullet.update();
|
||||
|
||||
Groups.bullet.collide();
|
||||
if(!editor) Groups.bullet.update();
|
||||
if(!editor) Groups.bullet.collide();
|
||||
PerfCounter.bulletUpdate.end();
|
||||
|
||||
PerfCounter.entityUpdate.end();
|
||||
|
||||
@@ -992,6 +992,7 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
public boolean isWaitingForPlayers(){
|
||||
if(state.is(State.menu)) return false;
|
||||
if(state.rules.pvp && !state.gameOver){
|
||||
int used = 0;
|
||||
for(TeamData t : state.teams.getActive()){
|
||||
|
||||
@@ -75,8 +75,6 @@ public class MapAudioView implements AssetView{
|
||||
m.setPosition(value);
|
||||
}
|
||||
});
|
||||
//only ogg can seek
|
||||
slider.touchable = asset.path.endsWith(".ogg") ? Touchable.enabled : Touchable.disabled;
|
||||
slider.visible(() -> {
|
||||
boolean valid = lastPlaying == m && m.isPlaying();
|
||||
if(valid){
|
||||
|
||||
@@ -296,6 +296,7 @@ public class MapImagesView implements AssetView{
|
||||
b.button(Icon.trash, istyle, () -> {
|
||||
ui.showConfirm("@asset.image.delete.confirm", () -> {
|
||||
Core.atlas.getRegionMap().remove(regionPrefix + image.name);
|
||||
Core.atlas.getDrawables().remove(regionPrefix + image.name);
|
||||
getImages().remove(image);
|
||||
diag.rebuild();
|
||||
});
|
||||
|
||||
@@ -91,6 +91,13 @@ public class EntityGroup<T extends Entityc> implements Iterable<T>{
|
||||
}
|
||||
}
|
||||
|
||||
public void update(Boolf<T> filter){
|
||||
for(index = 0; index < array.size; index++){
|
||||
var item = array.items[index];
|
||||
if(filter.get(item)) array.items[index].update();
|
||||
}
|
||||
}
|
||||
|
||||
/** Calls {@link #fixedUpdate(int, int)} with a maximum of 10 updates per frame. */
|
||||
public void fixedUpdate(int targetFps){
|
||||
fixedUpdate(targetFps, 10);
|
||||
@@ -345,4 +352,9 @@ public class EntityGroup<T extends Entityc> implements Iterable<T>{
|
||||
public Iterator<T> iterator(){
|
||||
return array.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return array.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ public class Fires{
|
||||
fire.set(tile.worldx(), tile.worldy());
|
||||
fire.add();
|
||||
set(tile, fire);
|
||||
Events.fire(Trigger.fireCreate);
|
||||
}else{
|
||||
fire.lifetime = baseLifetime;
|
||||
fire.time = 0f;
|
||||
|
||||
@@ -4,16 +4,14 @@ import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
class GroupDefs<G>{
|
||||
@GroupDef(value = Entityc.class, exclude = {Unitc.class, PowerGraphUpdaterc.class, Bulletc.class}, update = true) G all;
|
||||
@GroupDef(value = Entityc.class, exclude = {Unitc.class, PowerGraphUpdaterc.class, Bulletc.class, EffectStatec.class, Playerc.class}, update = true) G all;
|
||||
@GroupDef(value = {EffectStatec.class}) G effect;
|
||||
@GroupDef(value = Playerc.class, mapping = true) G player;
|
||||
@GroupDef(value = Bulletc.class, spatial = true, collide = true) G bullet;
|
||||
@GroupDef(value = Unitc.class, spatial = true, mapping = true) G unit;
|
||||
@GroupDef(value = Buildingc.class, update = true) G build;
|
||||
@GroupDef(value = Syncc.class, mapping = true) G sync;
|
||||
@GroupDef(value = Drawc.class) G draw;
|
||||
@GroupDef(value = Firec.class) G fire;
|
||||
@GroupDef(value = Puddlec.class) G puddle;
|
||||
@GroupDef(value = WeatherStatec.class) G weather;
|
||||
@GroupDef(value = WorldLabelc.class, mapping = true) G label;
|
||||
@GroupDef(value = PowerGraphUpdaterc.class) G powerGraph;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ abstract class FireComp implements Timedc, Posc, Syncc, Drawc{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
|
||||
animation += Time.delta / ticksPerFrame;
|
||||
warmup += Time.delta;
|
||||
animation %= frames;
|
||||
|
||||
@@ -88,7 +88,6 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc, Syncc{
|
||||
//force removal without pool free
|
||||
Groups.all.remove(self());
|
||||
Groups.draw.remove(self());
|
||||
Groups.puddle.remove(self());
|
||||
added = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class EventType{
|
||||
public enum Trigger{
|
||||
shock,
|
||||
cannotUpgrade,
|
||||
fireCreate,
|
||||
openConsole,
|
||||
blastFreeze,
|
||||
impactPower,
|
||||
|
||||
@@ -6,6 +6,7 @@ import arc.struct.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.world.*;
|
||||
@@ -94,9 +95,10 @@ public class MapIO{
|
||||
CachedTile tile = new CachedTile(){
|
||||
@Override
|
||||
public void setBlock(Block type){
|
||||
super.setBlock(type);
|
||||
|
||||
int c = colorFor(block(), Blocks.air, Blocks.air, team());
|
||||
//do not super.setBlock as that affects the current world
|
||||
this.block = type;
|
||||
this.build = type.newBuilding().init(this, this.team(), block.update && !state.isEditor(), 0);
|
||||
int c = colorFor(type, Blocks.air, Blocks.air, team());
|
||||
if(c != black){
|
||||
walls.setRaw(x, floors.height - 1 - y, c);
|
||||
floors.set(x, floors.height - 1 - y + 1, shade);
|
||||
@@ -105,7 +107,7 @@ public class MapIO{
|
||||
};
|
||||
|
||||
if(ver.version >= 12) ver.skipChunk(stream);
|
||||
ver.readRegion("content", stream, counter, ver::readContentHeader);
|
||||
ver.readRegion("content", stream, counter, MapIO::readPreviewContentHeader);
|
||||
if(ver.version == 11) ver.skipChunk(stream);
|
||||
ver.readRegion("preview_map", stream, counter, in -> ver.readMap(in, new WorldContext(){
|
||||
@Override public void resize(int width, int height){}
|
||||
@@ -187,6 +189,27 @@ public class MapIO{
|
||||
}
|
||||
}
|
||||
|
||||
private static void readPreviewContentHeader(DataInput stream) throws IOException{
|
||||
//reads content header while refusing to fire patch loaded event
|
||||
int mapped = stream.readUnsignedByte();
|
||||
|
||||
MappableContent[][] map = new MappableContent[ContentType.all.length][0];
|
||||
|
||||
for(int i = 0; i < mapped; i++){
|
||||
ContentType type = ContentType.all[stream.readByte()];
|
||||
short total = stream.readShort();
|
||||
map[type.ordinal()] = new MappableContent[total];
|
||||
|
||||
for(int j = 0; j < total; j++){
|
||||
String name = stream.readUTF();
|
||||
//fallback only for blocks
|
||||
map[type.ordinal()][j] = content.getByName(type, type == ContentType.block ? SaveFileReader.fallback.get(name, name) : name);
|
||||
}
|
||||
}
|
||||
|
||||
content.setTemporaryMapper(map);
|
||||
}
|
||||
|
||||
public static Pixmap generatePreview(Tiles tiles){
|
||||
Pixmap pixmap = new Pixmap(tiles.width, tiles.height);
|
||||
for(int x = 0; x < pixmap.width; x++){
|
||||
|
||||
@@ -124,7 +124,8 @@ public class LExecutor{
|
||||
public void load(LAssembler builder){
|
||||
stop = false;
|
||||
nameMap = null;
|
||||
vars = builder.vars.values().toSeq().retainAll(var -> !var.constant).toArray(LVar.class);
|
||||
//retain constants that are links, which, by convention, don't start with @ (builtin) or _ (numeric constant)
|
||||
vars = builder.vars.values().toSeq().retainAll(var -> !var.constant || var.name.charAt(0) != '_' && var.name.charAt(0) != '@').toArray(LVar.class);
|
||||
for(int i = 0; i < vars.length; i++){
|
||||
vars[i].id = i;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,17 @@ public class LVar{
|
||||
isobj = true;
|
||||
}
|
||||
|
||||
public void setlink(Object value){
|
||||
isobj = true;
|
||||
if(value == null){
|
||||
objval = null;
|
||||
constant = false; //convert a former linked block into a regular variable
|
||||
}else{
|
||||
objval = value;
|
||||
constant = true; //make this variable a linked block
|
||||
}
|
||||
}
|
||||
|
||||
public void set(LVar other){
|
||||
isobj = other.isobj;
|
||||
// Setting a non-numeric value to @counter must preserve its numeric field
|
||||
|
||||
@@ -42,7 +42,7 @@ public abstract class FilterOption{
|
||||
boolean display = true;
|
||||
|
||||
SliderOption(String name, Floatp getter, Floatc setter, float min, float max){
|
||||
this(name, getter, setter, min, max, (max - min) / 200);
|
||||
this(name, getter, setter, min, max, 0.00001f);
|
||||
}
|
||||
|
||||
SliderOption(String name, Floatp getter, Floatc setter, float min, float max, float step){
|
||||
|
||||
@@ -147,7 +147,6 @@ public class DataImagePacker{
|
||||
packer.forceDispose();
|
||||
|
||||
//getRegions is intentionally not modified, it's a hassle to manage, O(n) to unapply, and not used anywhere important. there's no point.
|
||||
//the drawable map isn't used, and thus not modified either
|
||||
|
||||
Log.debug("[Patch Atlas] Time to pack: @ms", Time.elapsed());
|
||||
}
|
||||
@@ -156,6 +155,7 @@ public class DataImagePacker{
|
||||
if(addedRegions != null){
|
||||
for(var region : addedRegions){
|
||||
Core.atlas.getRegionMap().remove(region.name);
|
||||
Core.atlas.getDrawables().remove(region.name);
|
||||
}
|
||||
addedRegions = null;
|
||||
}
|
||||
|
||||
@@ -379,8 +379,8 @@ public class Menus{
|
||||
//internal use only
|
||||
@Remote(variants = Variant.both)
|
||||
public static void removeWorldLabel(int id){
|
||||
var label = Groups.label.getByID(id);
|
||||
if(label != null){
|
||||
var label = Groups.sync.getByID(id);
|
||||
if(label instanceof WorldLabelc){
|
||||
label.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.ui.builder;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
@@ -9,6 +10,7 @@ public class UiBuilder{
|
||||
|
||||
public static TableBuilder parse(String source){ return UiDslParser.parse(source); }
|
||||
public static TableBuilder table(){ return new TableBuilder(); }
|
||||
public static StackBuilder stack(){ return new StackBuilder(); }
|
||||
public static PaneBuilder pane(){ return new PaneBuilder(); }
|
||||
public static LabelBuilder label(String text){ return new LabelBuilder().text(text); }
|
||||
public static ImageBuilder image(){ return new ImageBuilder(); }
|
||||
@@ -68,6 +70,8 @@ public class UiBuilder{
|
||||
public T uniform(){ return prop(UiKey.uniform, true); }
|
||||
public T uniformX(){ return prop(UiKey.uniformX, true); }
|
||||
public T uniformY(){ return prop(UiKey.uniformY, true); }
|
||||
public T color(Color color){ return prop(UiKey.color, color.toString()); }
|
||||
public T color(String colorHex){ return prop(UiKey.color, colorHex); }
|
||||
|
||||
public void write(Writes out){
|
||||
out.b(type.ordinal());
|
||||
@@ -95,6 +99,7 @@ public class UiBuilder{
|
||||
return switch(type){
|
||||
case table -> new TableBuilder();
|
||||
case pane -> new PaneBuilder();
|
||||
case stack -> new StackBuilder();
|
||||
case label -> new LabelBuilder();
|
||||
case image -> new ImageBuilder();
|
||||
case button -> new ButtonBuilder();
|
||||
@@ -169,6 +174,11 @@ public class UiBuilder{
|
||||
for(Entry e : entries) if(e.key == key && e.value instanceof Boolean b) return b;
|
||||
return def;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return UiDslWriter.write(this);
|
||||
}
|
||||
}
|
||||
|
||||
static class Entry{
|
||||
@@ -212,6 +222,10 @@ public class UiBuilder{
|
||||
}
|
||||
}
|
||||
|
||||
public static class StackBuilder extends ContainerBuilder<StackBuilder>{
|
||||
StackBuilder(){ super(UiKey.stack); }
|
||||
}
|
||||
|
||||
public static class TableBuilder extends ContainerBuilder<TableBuilder>{
|
||||
TableBuilder(){ super(UiKey.table); }
|
||||
|
||||
@@ -249,6 +263,7 @@ public class UiBuilder{
|
||||
public ButtonBuilder icon(String icon){ return prop(UiKey.icon, icon); }
|
||||
public ButtonBuilder style(String style){ return prop(UiKey.style, style); }
|
||||
public ButtonBuilder clicked(String result){ return prop(UiKey.clicked, result); }
|
||||
public ButtonBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); }
|
||||
}
|
||||
|
||||
public static class ImageButtonBuilder extends NodeBuilder<ImageButtonBuilder>{
|
||||
@@ -257,6 +272,7 @@ public class UiBuilder{
|
||||
public ImageButtonBuilder icon(String icon){ return prop(UiKey.icon, icon); }
|
||||
public ImageButtonBuilder style(String style){ return prop(UiKey.style, style); }
|
||||
public ImageButtonBuilder clicked(String result){ return prop(UiKey.clicked, result); }
|
||||
public ImageButtonBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); }
|
||||
}
|
||||
|
||||
public static class FieldBuilder extends NodeBuilder<FieldBuilder>{
|
||||
@@ -267,6 +283,7 @@ public class UiBuilder{
|
||||
public FieldBuilder maxLength(int length){ return prop(UiKey.maxLength, (float)length); }
|
||||
public FieldBuilder style(String style){ return prop(UiKey.style, style); }
|
||||
public FieldBuilder enter(String result){ return prop(UiKey.enter, result); }
|
||||
public FieldBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); }
|
||||
}
|
||||
|
||||
public static class CheckBuilder extends NodeBuilder<CheckBuilder>{
|
||||
@@ -275,6 +292,7 @@ public class UiBuilder{
|
||||
public CheckBuilder text(String text){ return prop(UiKey.text, text); }
|
||||
public CheckBuilder checked(boolean checked){ return prop(UiKey.checked, checked); }
|
||||
public CheckBuilder style(String style){ return prop(UiKey.style, style); }
|
||||
public CheckBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); }
|
||||
}
|
||||
|
||||
public static class SliderBuilder extends NodeBuilder<SliderBuilder>{
|
||||
@@ -285,6 +303,7 @@ public class UiBuilder{
|
||||
public SliderBuilder step(float step){ return prop(UiKey.step, step); }
|
||||
public SliderBuilder defaultValue(float value){ return prop(UiKey.defaultValue, value); }
|
||||
public SliderBuilder style(String style){ return prop(UiKey.style, style); }
|
||||
public SliderBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); }
|
||||
}
|
||||
|
||||
public static class SpaceBuilder extends NodeBuilder<SpaceBuilder>{
|
||||
@@ -300,5 +319,7 @@ public class UiBuilder{
|
||||
|
||||
public ButtonTableBuilder style(String style){ return prop(UiKey.style, style); }
|
||||
public ButtonTableBuilder clicked(String result){ return prop(UiKey.clicked, result); }
|
||||
public ButtonTableBuilder margin(float m){ return prop(UiKey.margin, m); }
|
||||
public ButtonTableBuilder disabled(boolean disabled){ return prop(UiKey.disabled, disabled); }
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public class UiDslParser{
|
||||
UiDslParser p = new UiDslParser(source);
|
||||
p.parseStatementsInto(root);
|
||||
p.skipWs();
|
||||
if(!p.atEnd()) throw new SerializationException("Unexpected character '" + p.peek() + "' at index " + p.pos);
|
||||
if(!p.atEnd()) throw new SerializationException("Unexpected character '" + p.peek() + "' at line " + p.line(p.pos));
|
||||
return root;
|
||||
}
|
||||
|
||||
@@ -34,17 +34,21 @@ public class UiDslParser{
|
||||
|
||||
private void parseStatement(NodeBuilder<?> node){
|
||||
skipWs();
|
||||
if(atEnd() || peek() == '"') throw new SerializationException("Expected identifier at index " + pos);
|
||||
if(atEnd() || peek() == '"') throw new SerializationException("Expected identifier at line " + line(pos));
|
||||
String ident = readBareToken();
|
||||
if(ident.isEmpty()) throw new SerializationException("Unexpected character '" + peek() + "' at index " + pos);
|
||||
if(ident.isEmpty()) throw new SerializationException("Unexpected character '" + peek() + "' at line " + line(pos));
|
||||
|
||||
if(ident.equals("row")){
|
||||
node.entries.add(new Entry(UiKey.row, true));
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO: throws on the client upon invalid value, that's probably fine?
|
||||
UiKey key = UiKey.valueOf(ident);
|
||||
UiKey key;
|
||||
try{
|
||||
key = UiKey.valueOf(ident);
|
||||
}catch(IllegalArgumentException e){
|
||||
throw new SerializationException("Unknown property: \"" + ident + "\" at line " + line(pos));
|
||||
}
|
||||
|
||||
//TODO hack: everything before the row ordinal is a node type
|
||||
boolean isNodeType = key.ordinal() < UiKey.row.ordinal();
|
||||
@@ -74,7 +78,7 @@ public class UiDslParser{
|
||||
// "ident { ... }" - a node with a block
|
||||
if(!atEnd() && peek() == '{'){
|
||||
pos++;
|
||||
if(!isNodeType) throw new SerializationException("Unknown node type '" + ident + "' at index " + pos);
|
||||
if(!isNodeType) throw new SerializationException("Unknown node type '" + ident + "' at line " + line(pos));
|
||||
NodeBuilder<?> child = NodeBuilder.create(key);
|
||||
parseStatementsInto(child);
|
||||
expect('}');
|
||||
@@ -83,7 +87,7 @@ public class UiDslParser{
|
||||
}
|
||||
|
||||
// bare "ident" - childless node (e.g. "space")
|
||||
if(!isNodeType) throw new SerializationException("Unknown node type '" + ident + "' at index " + pos);
|
||||
if(!isNodeType) throw new SerializationException("Unknown node type '" + ident + "' at line " + line(pos));
|
||||
node.entries.add(new Entry(key, NodeBuilder.create(key)));
|
||||
}
|
||||
|
||||
@@ -117,7 +121,7 @@ public class UiDslParser{
|
||||
}
|
||||
lastQuoted = false;
|
||||
String tok = readBareToken();
|
||||
if(tok.isEmpty()) throw new SerializationException("Expected value at index " + pos);
|
||||
if(tok.isEmpty()) throw new SerializationException("Expected value at line " + line(pos));
|
||||
return tok;
|
||||
}
|
||||
|
||||
@@ -126,11 +130,16 @@ public class UiDslParser{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while(!atEnd() && peek() != '"'){
|
||||
char c = src.charAt(pos);
|
||||
if(c == '\\' && pos + 1 < src.length()){ sb.append(src.charAt(pos + 1)); pos += 2; continue; }
|
||||
if(c == '\\' && pos + 1 < src.length()){
|
||||
char next = src.charAt(pos + 1);
|
||||
sb.append(next == 'n' ? '\n' : next); //parse \n
|
||||
pos += 2;
|
||||
continue;
|
||||
}
|
||||
sb.append(c);
|
||||
pos++;
|
||||
}
|
||||
if(atEnd()) throw new SerializationException("Unterminated string");
|
||||
if(atEnd()) throw new SerializationException("Unterminated string starting at line " + line(pos));
|
||||
pos++; // closing quote
|
||||
return sb.toString();
|
||||
}
|
||||
@@ -159,7 +168,7 @@ public class UiDslParser{
|
||||
}
|
||||
|
||||
private void expect(char c){
|
||||
if(atEnd() || peek() != c) throw new SerializationException("Expected '" + c + "', found '" + (atEnd() ? "<eof>" : peek()) + "'");
|
||||
if(atEnd() || peek() != c) throw new SerializationException("Expected '" + c + "', found '" + (atEnd() ? "<eof>" : peek()) + "' at line " + line(pos));
|
||||
pos++;
|
||||
}
|
||||
|
||||
@@ -170,4 +179,14 @@ public class UiDslParser{
|
||||
private char peek(){
|
||||
return src.charAt(pos);
|
||||
}
|
||||
|
||||
// counts newlines up to (not including) the given index to determine the 1-based line number
|
||||
private int line(int index){
|
||||
int line = 1;
|
||||
int end = Math.min(index, src.length());
|
||||
for(int i = 0; i < end; i++){
|
||||
if(src.charAt(i) == '\n') line++;
|
||||
}
|
||||
return line;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package mindustry.ui.builder;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ui.builder.UiBuilder.*;
|
||||
|
||||
/** Converts a parsed NodeBuilder tree back into UI DSL source text. */
|
||||
public class UiDslWriter{
|
||||
|
||||
/** Writes the root node's entries (no wrapping block, since root is an implicit table body). */
|
||||
public static String write(NodeBuilder<?> root){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
writeEntries(sb, root.entries, 0);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void writeEntries(StringBuilder sb, Seq<Entry> entries, int depth){
|
||||
for(Entry e : entries){
|
||||
// "row" marker
|
||||
if(e.key == UiKey.row && e.value instanceof Boolean){
|
||||
indent(sb, depth);
|
||||
sb.append("row\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// nested node (child added via block, shorthand, or bare)
|
||||
if(e.value instanceof NodeBuilder<?> child){
|
||||
writeChildNode(sb, child, depth);
|
||||
continue;
|
||||
}
|
||||
|
||||
// plain property: key: value
|
||||
indent(sb, depth);
|
||||
sb.append(e.key.name()).append(": ").append(formatValue(e.value)).append('\n');
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeChildNode(StringBuilder sb, NodeBuilder<?> child, int depth){
|
||||
indent(sb, depth);
|
||||
sb.append(child.type.name());
|
||||
|
||||
UiKey shorthandKey = shorthandType(child.type);
|
||||
Entry shorthand = null;
|
||||
for(Entry e : child.entries){
|
||||
if(e.key == shorthandKey && e.value instanceof String){
|
||||
shorthand = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Seq<Entry> rest = new Seq<>();
|
||||
for(Entry e : child.entries){
|
||||
if(e != shorthand) rest.add(e);
|
||||
}
|
||||
|
||||
if(shorthand != null){
|
||||
sb.append(": ").append(formatValue(shorthand.value));
|
||||
}
|
||||
|
||||
if(rest.isEmpty()){
|
||||
sb.append('\n');
|
||||
}else{
|
||||
if(shorthand != null) sb.append(' ');
|
||||
sb.append("{\n");
|
||||
writeEntries(sb, rest, depth + 1);
|
||||
indent(sb, depth);
|
||||
sb.append("}\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** Mirrors UiDslParser's getShorthandType: image uses region, everything else uses text. */
|
||||
private static UiKey shorthandType(UiKey key){
|
||||
return switch(key){
|
||||
case image -> UiKey.region;
|
||||
default -> UiKey.text;
|
||||
};
|
||||
}
|
||||
|
||||
private static String formatValue(Object value){
|
||||
if(value instanceof Boolean b) return b ? "true" : "false";
|
||||
if(value instanceof Float f) return formatFloat(f);
|
||||
if(value instanceof String s) return needsQuotes(s) ? quote(s) : s;
|
||||
throw new IllegalArgumentException("Unsupported value type: " + value);
|
||||
}
|
||||
|
||||
private static String formatFloat(float f){
|
||||
if(f == (long)f) return Long.toString((long)f);
|
||||
return Float.toString(f);
|
||||
}
|
||||
|
||||
/** A bare token round-trips correctly only if the parser wouldn't reinterpret it as a bool/number/keyword. */
|
||||
private static boolean needsQuotes(String s){
|
||||
if(s.isEmpty()) return true;
|
||||
if(s.equals("true") || s.equals("false")) return true;
|
||||
if(!Float.isNaN(Strings.parseFloat(s, Float.NaN))) return true;
|
||||
|
||||
for(int i = 0; i < s.length(); i++){
|
||||
char c = s.charAt(i);
|
||||
if(Character.isWhitespace(c) || c == '{' || c == '}' || c == ':' || c == '"' || c == '\\'){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// avoid accidentally starting a line comment
|
||||
return s.startsWith("//");
|
||||
}
|
||||
|
||||
private static String quote(String s){
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append('"');
|
||||
for(int i = 0; i < s.length(); i++){
|
||||
char c = s.charAt(i);
|
||||
if(c == '"' || c == '\\'){
|
||||
sb.append('\\').append(c);
|
||||
}else if(c == '\n'){
|
||||
sb.append("\\n");
|
||||
}else{
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
sb.append('"');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private static void indent(StringBuilder sb, int depth){
|
||||
for(int i = 0; i < depth; i++) sb.append(" ");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package mindustry.ui.builder;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
/** Utility class for showing a MSUI dialog box that automatically reloads changes for a certain file.
|
||||
* Desktop only. Usage: Just run UiHotReload.show() in the JS console. */
|
||||
public class UiHotReload{
|
||||
|
||||
/** Shows a file picker for the dialog box. */
|
||||
public static void show(){
|
||||
|
||||
FileChooser.open("msui").submit(file -> {
|
||||
long[] lastUpdate = {file.lastModified()}, debounceTime = {0};
|
||||
boolean[] modified = {false};
|
||||
|
||||
BaseDialog dialog = new BaseDialog("Mindustry DSL Preview");
|
||||
dialog.update(() -> {
|
||||
if(lastUpdate[0] != file.lastModified()){
|
||||
lastUpdate[0] = file.lastModified();
|
||||
modified[0] = true;
|
||||
debounceTime[0] = Time.millis();
|
||||
}
|
||||
|
||||
if(modified[0] && Time.timeSinceMillis(debounceTime[0]) > 100){
|
||||
modified[0] = false;
|
||||
reload(file, dialog);
|
||||
}
|
||||
});
|
||||
reload(file, dialog);
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
});
|
||||
}
|
||||
|
||||
static void reload(Fi file, BaseDialog dialog){
|
||||
try{
|
||||
dialog.cont.clear();
|
||||
dialog.cont.background(null);
|
||||
UiTreeBuilder.build(dialog.cont, UiBuilder.parse(file.readString()), result -> {
|
||||
Vars.ui.showInfo(result.toString());
|
||||
});
|
||||
}catch(Exception e){
|
||||
dialog.cont.clearChildren();
|
||||
if(e.getMessage() != null && e.getMessage().contains("line ")){
|
||||
try{
|
||||
String text = file.readString();
|
||||
int actualLine = Strings.parseInt(e.getMessage().substring(e.getMessage().indexOf("line ") + 5));
|
||||
if(actualLine > 0){
|
||||
String[] lines = text.split("\n");
|
||||
if(actualLine < lines.length){
|
||||
dialog.cont.add("[red]Error:[] " + e.getMessage() + "\n\nLine: [red]" + lines[actualLine - 1].trim());
|
||||
}
|
||||
}
|
||||
}catch(Exception ignored){
|
||||
}
|
||||
}
|
||||
if(dialog.cont.getChildren().isEmpty()) dialog.cont.add("[red]Error reading file![] \n" + Strings.getStackTrace(e)).center().labelAlign(Align.center);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,11 @@ package mindustry.ui.builder;
|
||||
/** Enum used for encoding properties as single bytes instead of passing strings around. */
|
||||
public enum UiKey{
|
||||
// node types
|
||||
table, pane, label, image, button, imageButton, field, check, slider, space, defaults, buttonTable, row, //NOTE: all node types must be before 'row'
|
||||
table, pane, stack, label, image, button, imageButton, field, check, slider, space, defaults, buttonTable, row, //NOTE: all node types must be before 'row'
|
||||
|
||||
// node-specific properties
|
||||
text, wrap, region, icon, scaling, background, margin, id, hint, maxLength, checked, min, max, step, defaultValue, clicked, enter, style, group, condition,
|
||||
text, wrap, region, icon, scaling, background, margin, id, hint, maxLength, checked, min, max, step,
|
||||
defaultValue, clicked, enter, style, group, condition, color, disabled,
|
||||
|
||||
// cell properties
|
||||
grow, growX, growY, fill, fillX, fillY, expand, expandX, expandY,
|
||||
|
||||
@@ -2,6 +2,7 @@ package mindustry.ui.builder;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.*;
|
||||
import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
@@ -15,6 +16,7 @@ import arc.scene.ui.Slider.*;
|
||||
import arc.scene.ui.TextButton.*;
|
||||
import arc.scene.ui.TextField.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.scene.utils.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -51,7 +53,7 @@ public class UiTreeBuilder{
|
||||
String cond = node.str(UiKey.condition);
|
||||
if(cond != null && !evalCondition(cond)) continue; // condition false - node is not added at all
|
||||
|
||||
Cell<?> cell = addNode(table, entry.key, node, ctx);
|
||||
Cell<?> cell = entry.key == UiKey.space ? table.add() : table.add(addNode(entry.key, node, ctx));
|
||||
if(cell == null) continue; // unknown/unsupported node - skip for forward compat
|
||||
|
||||
for(Entry d : defaults) applyCellProp(cell, d.key, d.value);
|
||||
@@ -62,8 +64,7 @@ public class UiTreeBuilder{
|
||||
String id = node.str(UiKey.id);
|
||||
if(id != null){
|
||||
cell.name(id); // assigns element.name via Cell.name()
|
||||
Object element = cell.get();
|
||||
if(element instanceof Element el) ctx.idElements.put(id, el);
|
||||
ctx.idElements.put(id, cell.get());
|
||||
}
|
||||
|
||||
}else{
|
||||
@@ -73,7 +74,7 @@ public class UiTreeBuilder{
|
||||
}
|
||||
|
||||
/** Constructs the element for one node, adds it to the parent table, and returns its cell. */
|
||||
private static Cell<?> addNode(Table table, UiKey type, NodeBuilder<?> node, BuildContext ctx){
|
||||
private static Element addNode(UiKey type, NodeBuilder<?> node, BuildContext ctx){
|
||||
return switch(type){
|
||||
case table -> {
|
||||
String bg = node.str(UiKey.background);
|
||||
@@ -82,16 +83,38 @@ public class UiTreeBuilder{
|
||||
Float margin = node.num(UiKey.margin);
|
||||
if(margin != null) t.margin(margin);
|
||||
|
||||
Cell<Table> cell = table.add(t);
|
||||
buildInto(t, node.entries, ctx); // recurse with a fresh defaults scope
|
||||
yield cell;
|
||||
yield t;
|
||||
}
|
||||
case stack -> {
|
||||
Stack stack = new Stack();
|
||||
stack.touchable = Touchable.childrenOnly;
|
||||
|
||||
//simplified creation compared to table; no cells are involved
|
||||
for(Entry entry : node.entries){
|
||||
if(entry.value instanceof NodeBuilder<?> child){
|
||||
String cond = child.str(UiKey.condition);
|
||||
if(cond != null && !evalCondition(cond)) continue;
|
||||
|
||||
var element = addNode(entry.key, child, ctx);
|
||||
if(element == null) continue; // unknown/unsupported node - skip for forward compat
|
||||
String id = node.str(UiKey.id);
|
||||
|
||||
if(id != null){
|
||||
element.name = id;
|
||||
ctx.idElements.put(id, element);
|
||||
}
|
||||
stack.add(element);
|
||||
}
|
||||
}
|
||||
yield stack;
|
||||
}
|
||||
case pane -> {
|
||||
Table inner = new Table();
|
||||
buildInto(inner, node.entries, ctx);
|
||||
ScrollPane pane = new ScrollPane(inner);
|
||||
applyStyle(node, ScrollPaneStyle.class, pane::setStyle);
|
||||
yield table.add(pane);
|
||||
yield pane;
|
||||
}
|
||||
case label -> {
|
||||
Label label = new Label(node.str(UiKey.text, ""));
|
||||
@@ -99,7 +122,7 @@ public class UiTreeBuilder{
|
||||
String labelAlign = node.str(UiKey.labelAlign);
|
||||
if(labelAlign != null) label.setAlignment(parseAlign(labelAlign));
|
||||
applyStyle(node, LabelStyle.class, label::setStyle);
|
||||
yield table.add(label);
|
||||
yield label;
|
||||
}
|
||||
case image -> {
|
||||
Image image = new Image(findDrawable(node.str(UiKey.region, node.str(UiKey.icon, "error"))));
|
||||
@@ -110,7 +133,7 @@ public class UiTreeBuilder{
|
||||
image.setScaling(scaling);
|
||||
}catch(Exception ignored){}
|
||||
}
|
||||
yield table.add(image);
|
||||
yield image;
|
||||
}
|
||||
case button -> {
|
||||
TextButton btn = new TextButton(node.str(UiKey.text, ""));
|
||||
@@ -122,14 +145,14 @@ public class UiTreeBuilder{
|
||||
btn.getCells().reverse();
|
||||
}
|
||||
wireButton(btn, node, ctx);
|
||||
yield table.add(btn);
|
||||
yield btn;
|
||||
}
|
||||
case imageButton -> {
|
||||
String icon = node.str(UiKey.icon);
|
||||
ImageButton btn = new ImageButton(icon != null ? findDrawable(icon) : null);
|
||||
applyStyle(node, ImageButtonStyle.class, btn::setStyle);
|
||||
wireButton(btn, node, ctx);
|
||||
yield table.add(btn);
|
||||
yield btn;
|
||||
}
|
||||
case field -> {
|
||||
TextField field = new TextField(node.str(UiKey.text, ""));
|
||||
@@ -140,7 +163,7 @@ public class UiTreeBuilder{
|
||||
String enter = node.str(UiKey.enter);
|
||||
if(enter != null) field.keyDown(KeyCode.enter, () -> fireResult(ctx, enter));
|
||||
applyStyle(node, TextFieldStyle.class, field::setStyle);
|
||||
yield table.add(field);
|
||||
yield field;
|
||||
}
|
||||
case check -> {
|
||||
CheckBox box = new CheckBox(node.str(UiKey.text, ""));
|
||||
@@ -148,7 +171,7 @@ public class UiTreeBuilder{
|
||||
String group = node.str(UiKey.group);
|
||||
if(group != null) ctx.group(group).add(box);
|
||||
applyStyle(node, CheckBoxStyle.class, box::setStyle);
|
||||
yield table.add(box);
|
||||
yield box;
|
||||
}
|
||||
case slider -> {
|
||||
Slider slider = new Slider(node.num(UiKey.min, 0f), node.num(UiKey.max, 1f), node.num(UiKey.step, 0.1f), false);
|
||||
@@ -173,17 +196,17 @@ public class UiTreeBuilder{
|
||||
label.setAlignment(Align.center);
|
||||
label.touchable = Touchable.disabled;
|
||||
label.setStyle(Styles.outlineLabel);
|
||||
yield table.add(new Stack(slider, label));
|
||||
yield new Stack(slider, label);
|
||||
}
|
||||
case space -> table.add();
|
||||
case buttonTable -> {
|
||||
Button btn = new Button();
|
||||
//can use any button style
|
||||
if(!applyStyle(node, TextButtonStyle.class, btn::setStyle)) applyStyle(node, ImageButtonStyle.class, btn::setStyle);
|
||||
Cell<Button> cell = table.add(btn);
|
||||
Float margin = node.num(UiKey.margin);
|
||||
if(margin != null) btn.margin(margin);
|
||||
buildInto(btn, node.entries, ctx); // build the button's own contents into it, like table/pane
|
||||
wireButton(btn, node, ctx);
|
||||
yield cell;
|
||||
yield btn;
|
||||
}
|
||||
default -> null;
|
||||
};
|
||||
@@ -222,7 +245,7 @@ public class UiTreeBuilder{
|
||||
for(var entry : ctx.idElements){
|
||||
Element el = entry.value;
|
||||
if(el instanceof Slider s) res.values.put(entry.key, s.getValue());
|
||||
else if(el instanceof Stack stack && stack.getChildren().contains(e -> e instanceof Slider)) res.values.put(entry.key, ((Slider)stack.getChildren().find(e -> e instanceof Slider)).getValue());
|
||||
else if(el instanceof Stack stack && stack.getChildren().size > 0 && stack.getChildren().get(0) instanceof Slider slider) res.values.put(entry.key, slider.getValue());
|
||||
else if(el instanceof TextField f) res.values.put(entry.key, f.getText());
|
||||
else if(el instanceof CheckBox c) res.values.put(entry.key, c.isChecked());
|
||||
//the only thing distinguishing buttons that can be checked is that they have a style for it; it's just not visible otherwise.
|
||||
@@ -256,6 +279,8 @@ public class UiTreeBuilder{
|
||||
case uniform -> cell.uniform();
|
||||
case uniformX -> cell.uniformX();
|
||||
case uniformY -> cell.uniformY();
|
||||
//not a cell property, technically, only applies to buttons/sliders/text fields
|
||||
case disabled -> { if(cell.get() instanceof Disableable d) d.setDisabled(true); }
|
||||
default -> {}
|
||||
}
|
||||
}else{
|
||||
@@ -274,6 +299,9 @@ public class UiTreeBuilder{
|
||||
case padRight -> cell.padRight((Float)value);
|
||||
case align -> cell.align(parseAlign((String)value));
|
||||
case colspan -> cell.colspan(((Float)value).intValue());
|
||||
case color -> { //technically not a layout property, but all elements have it, so it's applied here
|
||||
if(value instanceof String s) cell.color(Strings.parseColor(s, Color.white));
|
||||
}
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ public class HintsFragment{
|
||||
});
|
||||
|
||||
Events.run(Trigger.cannotUpgrade, () -> events.add("cannotupgrade"));
|
||||
Events.run(Trigger.fireCreate, () -> events.add("fire"));
|
||||
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
placedBlocks.clear();
|
||||
@@ -248,7 +249,7 @@ public class HintsFragment{
|
||||
),
|
||||
|
||||
waveFire(
|
||||
() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(),
|
||||
() -> ui.hints.events.contains("fire") && Blocks.wave.unlockedNow(),
|
||||
() -> indexer.getFlagged(state.rules.defaultTeam, BlockFlag.extinguisher).size > 0
|
||||
),
|
||||
|
||||
|
||||
@@ -91,7 +91,11 @@ public class LogicBlock extends Block{
|
||||
int x = lbuild.tileX(), y = lbuild.tileY();
|
||||
int oldSize = entity.links.size;
|
||||
|
||||
entity.links.removeAll(l -> world.build(l.x, l.y) == lbuild);
|
||||
entity.links.removeAll(l -> {
|
||||
boolean remove = world.build(l.x, l.y) == lbuild;
|
||||
if(remove) l.trySet(entity.executor, null);
|
||||
return remove;
|
||||
});
|
||||
|
||||
if(oldSize > entity.links.size){ //check whether any were removed
|
||||
//re-enable the target when unlinking
|
||||
@@ -99,10 +103,12 @@ public class LogicBlock extends Block{
|
||||
lbuild.enabled = true;
|
||||
}
|
||||
}else{
|
||||
entity.links.add(new LogicLink(x, y, entity.findLinkName(lbuild.block), true));
|
||||
LogicLink link = new LogicLink(x, y, entity.findLinkName(lbuild.block), true);
|
||||
link.trySet(entity.executor, lbuild);
|
||||
entity.links.add(link);
|
||||
}
|
||||
|
||||
entity.updateCode(entity.code, true, null);
|
||||
entity.updateLinks();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -233,18 +239,10 @@ public class LogicBlock extends Block{
|
||||
|
||||
public void trySet(LExecutor exec, Object value){
|
||||
if(logicVar != null){
|
||||
logicVar.setconst(value);
|
||||
logicVar.setlink(value);
|
||||
}else{
|
||||
var foundVar = exec.optionalVar(name);
|
||||
if(foundVar != null){
|
||||
if(value != null){
|
||||
//should now become const as it is now a valid link
|
||||
//note: this will never become non-const even if invalidated
|
||||
//there isn't really a good reason to use these variables anyway, and it is an edge case
|
||||
foundVar.constant = true;
|
||||
}
|
||||
foundVar.setconst(value);
|
||||
}
|
||||
logicVar = exec.optionalVar(name);
|
||||
if(logicVar != null) logicVar.setlink(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -26,4 +26,4 @@ org.gradle.caching=true
|
||||
org.gradle.internal.http.socketTimeout=100000
|
||||
org.gradle.internal.http.connectionTimeout=100000
|
||||
android.enableR8.fullMode=false
|
||||
archash=c686a10dfc
|
||||
archash=0c76a2c13b
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${app.build}</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
|
||||
+3
-17
@@ -47,19 +47,6 @@ ext{
|
||||
mainClassName = "mindustry.ios.IOSLauncher"
|
||||
}
|
||||
|
||||
tasks.register('copyAssets'){
|
||||
doLast{
|
||||
delete{
|
||||
delete "assets/"
|
||||
}
|
||||
|
||||
copy{
|
||||
from "../core/assets"
|
||||
into "assets/"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('copyNatives'){
|
||||
doLast{
|
||||
copy{
|
||||
@@ -70,17 +57,16 @@ tasks.register('copyNatives'){
|
||||
}
|
||||
|
||||
tasks.register('deploy'){
|
||||
if(System.getProperty("os.name").contains("Mac")) dependsOn copyAssets
|
||||
dependsOn createIPA
|
||||
}
|
||||
|
||||
//must pack before deployment, as iOS never has the latest sprites.
|
||||
copyAssets.dependsOn ":tools:pack"
|
||||
copyAssets.dependsOn copyNatives
|
||||
launchIPhoneSimulator.dependsOn build
|
||||
launchIPadSimulator.dependsOn build
|
||||
launchIOSDevice.dependsOn build
|
||||
createIPA.dependsOn build
|
||||
createIPA.dependsOn copyNatives
|
||||
createIPA.dependsOn ":tools:pack"
|
||||
createIPA.dependsOn ":core:preGen"
|
||||
|
||||
robovm{
|
||||
archs = "arm64"
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
<iosInfoPList>Info.plist.xml</iosInfoPList>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>../ios/assets</directory>
|
||||
<directory>../core/assets</directory>
|
||||
<includes>
|
||||
<include>**</include>
|
||||
</includes>
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
{
|
||||
"doc": "Applies an absolute-value sine wave to progress.",
|
||||
"superclass": "PartProgress",
|
||||
"scl":{
|
||||
"doc": "Applies an absolute-value sine wave to progress. Applied via PartProgress's 'op'/'operation' field (e.g. `op: absin`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"scl": {
|
||||
"type": "float",
|
||||
"doc": "Scale (frequency) of the wave."
|
||||
},
|
||||
"mag":{
|
||||
"mag": {
|
||||
"type": "float",
|
||||
"doc": "Magnitude (amplitude) of the wave."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
{
|
||||
"doc": "Adds a constant or another PartProgress's value to this one. Set exactly one of 'amount' or 'other'.",
|
||||
"superclass": "PartProgress",
|
||||
"amount":{
|
||||
"doc": "Adds a constant or another PartProgress's value to this one. Set exactly one of 'amount' or 'other'. Applied via PartProgress's 'op'/'operation' field (e.g. `op: add`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"amount": {
|
||||
"type": "float",
|
||||
"doc": "Constant amount to add."
|
||||
},
|
||||
"other":{
|
||||
"type": "PartProgress",
|
||||
"other": {
|
||||
"type": "mindustry.entities.part.DrawPart$PartProgress",
|
||||
"doc": "Another progress function whose value is added instead of a constant."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
{
|
||||
"doc": "Blends between this progress and another PartProgress by a fixed amount.",
|
||||
"superclass": "PartProgress",
|
||||
"other":{
|
||||
"type": "PartProgress",
|
||||
"doc": "Blends between this progress and another PartProgress by a fixed amount. Applied via PartProgress's 'op'/'operation' field (e.g. `op: blend`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"other": {
|
||||
"type": "mindustry.entities.part.DrawPart$PartProgress",
|
||||
"doc": "Progress function to blend with."
|
||||
},
|
||||
"amount":{
|
||||
"amount": {
|
||||
"type": "float",
|
||||
"doc": "Blend factor toward 'other'."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"doc": "Weapon charge, 0 when beginning to charge, 1 when finished.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Weapon charge, 0 when beginning to charge, 1 when finished. A sensor value for PartProgress.type - not an operation (see PartProgress and the 'op' field for those).",
|
||||
"superclass": "mindustry.entities.part.DrawPart$PartProgress"
|
||||
}
|
||||
@@ -1,8 +1,3 @@
|
||||
{
|
||||
"doc": "Clamps progress to the [0, 1] range.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Clamps progress to the [0, 1] range. Applied via PartProgress's 'op'/'operation' field (e.g. `op: clamp`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object."
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
{
|
||||
"doc": "Compresses progress into the [start, end] sub-range.",
|
||||
"superclass": "PartProgress",
|
||||
"start":{
|
||||
"doc": "Compresses progress into the [start, end] sub-range. Applied via PartProgress's 'op'/'operation' field (e.g. `op: compress`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"start": {
|
||||
"type": "float",
|
||||
"doc": "Start of the compressed range."
|
||||
},
|
||||
"end":{
|
||||
"end": {
|
||||
"type": "float",
|
||||
"doc": "End of the compressed range."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,15 @@
|
||||
{
|
||||
"doc": "Applies an interpolation curve to progress, or slices out a sub-range and interpolates within it. Set either 'interp', or both 'offset' and 'duration'.",
|
||||
"superclass": "PartProgress",
|
||||
"interp":{
|
||||
"doc": "Applies an interpolation curve to progress, or slices out a sub-range and interpolates within it. Set either 'interp', or both 'offset' and 'duration'. Applied via PartProgress's 'op'/'operation' field (e.g. `op: curve`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"interp": {
|
||||
"type": "arc.util.Interp",
|
||||
"doc": "Interpolation curve to apply directly to progress."
|
||||
},
|
||||
"offset":{
|
||||
"offset": {
|
||||
"type": "float",
|
||||
"doc": "Start of the sub-range to slice out."
|
||||
},
|
||||
"duration":{
|
||||
"duration": {
|
||||
"type": "float",
|
||||
"doc": "Length of the sub-range to slice out."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
{
|
||||
"doc": "Delays progress by a fixed amount.",
|
||||
"superclass": "PartProgress",
|
||||
"amount":{
|
||||
"doc": "Delays progress by a fixed amount. Applied via PartProgress's 'op'/'operation' field (e.g. `op: delay`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"amount": {
|
||||
"type": "float",
|
||||
"doc": "Amount to delay progress by."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"doc": "Weapon heat, 1 when just fired, 0 when it has cooled down (duration depends on weapon).",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Weapon heat, 1 when just fired, 0 when it has cooled down (duration depends on weapon). A sensor value for PartProgress.type - not an operation (see PartProgress and the 'op' field for those).",
|
||||
"superclass": "mindustry.entities.part.DrawPart$PartProgress"
|
||||
}
|
||||
@@ -1,8 +1,3 @@
|
||||
{
|
||||
"doc": "Inverts progress (1 - progress).",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Inverts progress (1 - progress). Applied via PartProgress's 'op'/'operation' field (e.g. `op: inv`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object."
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"doc": "Lifetime fraction, 0 to 1. Only for missiles.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Lifetime fraction, 0 to 1. Only for missiles. A sensor value for PartProgress.type - not an operation (see PartProgress and the 'op' field for those).",
|
||||
"superclass": "mindustry.entities.part.DrawPart$PartProgress"
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
{
|
||||
"doc": "Loops progress over a fixed time period.",
|
||||
"superclass": "PartProgress",
|
||||
"time":{
|
||||
"doc": "Loops progress over a fixed time period. Applied via PartProgress's 'op'/'operation' field (e.g. `op: loop`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"time": {
|
||||
"type": "float",
|
||||
"doc": "Length of one loop cycle."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
{
|
||||
"doc": "Takes the minimum of this progress and another PartProgress.",
|
||||
"superclass": "PartProgress",
|
||||
"other":{
|
||||
"type": "PartProgress",
|
||||
"doc": "Takes the minimum of this progress and another PartProgress. Applied via PartProgress's 'op'/'operation' field (e.g. `op: min`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"other": {
|
||||
"type": "mindustry.entities.part.DrawPart$PartProgress",
|
||||
"doc": "Progress function to compare against."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,19 @@
|
||||
{
|
||||
"doc": "Base class for progress functions used to animate part transforms. The concrete operation is chosen by 'type' (see PartProgress's static methods for the full list: inv, slope, clamp, delay, sustain, shorten, compress, add, blend, mul, min, sin, absin, mod, loop, curve).",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
"doc": "Base class for progress functions used to animate part transforms. This is NOT a normal polymorphic class: 'type' does not choose an operation - it chooses the progress SOURCE (a 'sensor': warmup, life, heat, time, reload, charge, recoil, smoothReload; see each for details). Separately, an object may apply an operation to that sensor value by naming it in 'op' (alias 'operation'), with the operation's own parameter fields (e.g. 'start'/'end' for compress) written flat alongside 'op' on the SAME object - not nested inside it. To chain several operations, use 'ops' (alias 'operations'): an array where each entry is its own flat {op, ...params} spec, applied in order to the sensor value. 'op'/'operation' and 'ops'/'operations' are mutually exclusive. Operations: inv, slope, clamp, delay, sustain, shorten, compress, add, blend, mul, min, sin, absin, mod, loop, curve (see each for its parameters).",
|
||||
"op": {
|
||||
"type": "String",
|
||||
"doc": "Name of a single operation to apply to the sensor value (alias: 'operation'). Write that operation's own parameter fields directly alongside this one, at the same level - not nested. Mutually exclusive with 'ops'/'operations'. One of: inv, slope, clamp, delay, sustain, shorten, compress, add, blend, mul, min, sin, absin, mod, loop, curve."
|
||||
},
|
||||
"operation": {
|
||||
"type": "String",
|
||||
"doc": "Alias for 'op'."
|
||||
},
|
||||
"ops": {
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress$Op>",
|
||||
"doc": "Chain of operations applied in sequence to the sensor value (alias: 'operations'). Each array entry is its own flat object: an 'op'/'operation' name plus that operation's own parameter fields - not a nested PartProgress. Mutually exclusive with a single 'op'/'operation'."
|
||||
},
|
||||
"operations": {
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress$Op>",
|
||||
"doc": "Alias for 'ops'."
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
{
|
||||
"doc": "Wraps progress modulo a fixed amount.",
|
||||
"superclass": "PartProgress",
|
||||
"amount":{
|
||||
"doc": "Wraps progress modulo a fixed amount. Applied via PartProgress's 'op'/'operation' field (e.g. `op: mod`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"amount": {
|
||||
"type": "float",
|
||||
"doc": "Modulus to wrap progress by."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
{
|
||||
"doc": "Multiplies by a constant or another PartProgress's value. Set exactly one of 'amount' or 'other'.",
|
||||
"superclass": "PartProgress",
|
||||
"amount":{
|
||||
"doc": "Multiplies by a constant or another PartProgress's value. Set exactly one of 'amount' or 'other'. Applied via PartProgress's 'op'/'operation' field (e.g. `op: mul`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"amount": {
|
||||
"type": "float",
|
||||
"doc": "Constant multiplier."
|
||||
},
|
||||
"other":{
|
||||
"type": "PartProgress",
|
||||
"other": {
|
||||
"type": "mindustry.entities.part.DrawPart$PartProgress",
|
||||
"doc": "Another progress function whose value is multiplied instead of a constant."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"doc": "Weapon recoil with no curve applied.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Weapon recoil with no curve applied. A sensor value for PartProgress.type - not an operation (see PartProgress and the 'op' field for those).",
|
||||
"superclass": "mindustry.entities.part.DrawPart$PartProgress"
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"doc": "Reload of the weapon - 1 right after shooting, 0 when ready to fire.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Reload of the weapon - 1 right after shooting, 0 when ready to fire. A sensor value for PartProgress.type - not an operation (see PartProgress and the 'op' field for those).",
|
||||
"superclass": "mindustry.entities.part.DrawPart$PartProgress"
|
||||
}
|
||||
@@ -1,12 +1,7 @@
|
||||
{
|
||||
"doc": "Shortens the progress range by a fixed amount.",
|
||||
"superclass": "PartProgress",
|
||||
"amount":{
|
||||
"doc": "Shortens the progress range by a fixed amount. Applied via PartProgress's 'op'/'operation' field (e.g. `op: shorten`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"amount": {
|
||||
"type": "float",
|
||||
"doc": "Amount to shorten progress by."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,16 @@
|
||||
{
|
||||
"doc": "Applies a sine wave to progress.",
|
||||
"superclass": "PartProgress",
|
||||
"offset":{
|
||||
"doc": "Applies a sine wave to progress. Applied via PartProgress's 'op'/'operation' field (e.g. `op: sin`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"offset": {
|
||||
"type": "float",
|
||||
"doc": "Phase offset.",
|
||||
"default": "0"
|
||||
},
|
||||
"scl":{
|
||||
"scl": {
|
||||
"type": "float",
|
||||
"doc": "Scale (frequency) of the wave."
|
||||
},
|
||||
"mag":{
|
||||
"mag": {
|
||||
"type": "float",
|
||||
"doc": "Magnitude (amplitude) of the wave."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,3 @@
|
||||
{
|
||||
"doc": "Slopes progress: rises to 1 then falls back to 0.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Slopes progress: rises to 1 then falls back to 0. Applied via PartProgress's 'op'/'operation' field (e.g. `op: slope`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object."
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"doc": "Reload, but smoothed out, so there is no sudden jump between 0-1.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Reload, but smoothed out, so there is no sudden jump between 0-1. A sensor value for PartProgress.type - not an operation (see PartProgress and the 'op' field for those).",
|
||||
"superclass": "mindustry.entities.part.DrawPart$PartProgress"
|
||||
}
|
||||
@@ -1,22 +1,17 @@
|
||||
{
|
||||
"doc": "Rises to and sustains progress at a plateau.",
|
||||
"superclass": "PartProgress",
|
||||
"offset":{
|
||||
"doc": "Rises to and sustains progress at a plateau. Applied via PartProgress's 'op'/'operation' field (e.g. `op: sustain`), NOT 'type' - its parameter fields (if any) are written flat alongside 'op', on the same object.",
|
||||
"offset": {
|
||||
"type": "float",
|
||||
"doc": "Progress offset before rising.",
|
||||
"default": "0"
|
||||
},
|
||||
"grow":{
|
||||
"grow": {
|
||||
"type": "float",
|
||||
"doc": "Duration of the rise to the plateau.",
|
||||
"default": "0"
|
||||
},
|
||||
"sustain":{
|
||||
"sustain": {
|
||||
"type": "float",
|
||||
"doc": "Length of the sustained plateau."
|
||||
},
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"doc": "Current unscaled value of Time.time.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Current unscaled value of Time.time. A sensor value for PartProgress.type - not an operation (see PartProgress and the 'op' field for those).",
|
||||
"superclass": "mindustry.entities.part.DrawPart$PartProgress"
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
{
|
||||
"doc": "Weapon warmup, 0 when not firing, 1 when actively shooting. Not equivalent to heat.",
|
||||
"superclass": "PartProgress",
|
||||
"ops":{
|
||||
"type": "Seq<mindustry.entities.part.DrawPart$PartProgress>",
|
||||
"doc": "Consecutive operations."
|
||||
}
|
||||
"doc": "Weapon warmup, 0 when not firing, 1 when actively shooting. Not equivalent to heat. A sensor value for PartProgress.type - not an operation (see PartProgress and the 'op' field for those).",
|
||||
"superclass": "mindustry.entities.part.DrawPart$PartProgress"
|
||||
}
|
||||
Reference in New Issue
Block a user