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:
Anuken
2026-08-06 21:54:58 -04:00
90 changed files with 631 additions and 292 deletions
@@ -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,
+4
View File
@@ -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
+4
View File
@@ -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]Уключана
+4
View File
@@ -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]Активиран
+4
View File
@@ -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
+4
View File
@@ -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[]
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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é
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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]有効
+4
View File
@@ -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]활성화됨
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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]Включено
+4
View File
@@ -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
+4
View File
@@ -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
+4
View File
@@ -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]เปิดใช้งาน
+4
View File
@@ -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
+4
View File
@@ -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]Увімкнено
+4
View File
@@ -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]已啟用
+1 -1
View File
@@ -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;
+14 -7
View File
@@ -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();
+1
View File
@@ -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();
}
}
+1
View File
@@ -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;
+2 -4
View File
@@ -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;
}
+1
View File
@@ -22,6 +22,7 @@ public class EventType{
public enum Trigger{
shock,
cannotUpgrade,
fireCreate,
openConsole,
blastFreeze,
impactPower,
+27 -4
View File
@@ -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++){
+2 -1
View File
@@ -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;
}
+11
View File
@@ -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){
+1 -1
View File
@@ -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;
}
+2 -2
View File
@@ -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); }
}
}
+30 -11
View File
@@ -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 -2
View File
@@ -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
View File
@@ -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
+2
View File
@@ -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
View File
@@ -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
View File
@@ -6,7 +6,7 @@
<iosInfoPList>Info.plist.xml</iosInfoPList>
<resources>
<resource>
<directory>../ios/assets</directory>
<directory>../core/assets</directory>
<includes>
<include>**</include>
</includes>
+3 -8
View File
@@ -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."
}
}
+4 -9
View File
@@ -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."
}
}
+4 -9
View File
@@ -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."
}
}
+2 -6
View File
@@ -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 -6
View File
@@ -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."
}
+3 -8
View File
@@ -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."
}
}
+4 -9
View File
@@ -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."
}
}
+2 -7
View File
@@ -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."
}
}
+2 -6
View File
@@ -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 -6
View File
@@ -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."
}
+2 -6
View File
@@ -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"
}
+2 -7
View File
@@ -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."
}
}
+3 -8
View File
@@ -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'."
}
}
+2 -7
View File
@@ -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."
}
}
+4 -9
View File
@@ -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."
}
}
+2 -6
View File
@@ -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"
}
+2 -6
View File
@@ -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"
}
+2 -7
View File
@@ -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."
}
}
+4 -9
View File
@@ -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 -6
View File
@@ -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."
}
+2 -6
View File
@@ -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"
}
+4 -9
View File
@@ -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."
}
}
+2 -6
View File
@@ -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"
}
+2 -6
View File
@@ -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"
}