getActive(){
+ active.removeAll(t -> !t.active());
return active;
}
@@ -125,7 +129,7 @@ public class Teams{
}
private void updateEnemies(){
- if(!active.contains(get(state.rules.waveTeam))){
+ if(state.rules.waves && !active.contains(get(state.rules.waveTeam))){
active.add(get(state.rules.waveTeam));
}
@@ -164,6 +168,14 @@ public class Teams{
public CoreEntity core(){
return cores.first();
}
+
+ @Override
+ public String toString(){
+ return "TeamData{" +
+ "cores=" + cores +
+ ", team=" + team +
+ '}';
+ }
}
/** Represents a block made by this team that was destroyed somewhere on the map.
diff --git a/core/src/mindustry/graphics/BlockRenderer.java b/core/src/mindustry/graphics/BlockRenderer.java
index 41274e0349..a6455a03d6 100644
--- a/core/src/mindustry/graphics/BlockRenderer.java
+++ b/core/src/mindustry/graphics/BlockRenderer.java
@@ -168,7 +168,6 @@ public class BlockRenderer implements Disposable{
shadowEvents.clear();
Draw.proj(camera.projection());
- renderer.pixelator.rebind();
}
float ww = world.width() * tilesize, wh = world.height() * tilesize;
diff --git a/core/src/mindustry/graphics/Bloom.java b/core/src/mindustry/graphics/Bloom.java
deleted file mode 100644
index 1b7b8f89f9..0000000000
--- a/core/src/mindustry/graphics/Bloom.java
+++ /dev/null
@@ -1,382 +0,0 @@
-package mindustry.graphics;
-
-import arc.Core;
-import arc.graphics.*;
-import arc.graphics.Pixmap.Format;
-import arc.graphics.VertexAttributes.Usage;
-import arc.graphics.gl.FrameBuffer;
-import arc.graphics.gl.Shader;
-
-/**
- * Bloomlib allow easy but efficient way to add bloom effect as post process
- * effect
- *
- * @author kalle_h
- */
-public class Bloom{
- /**
- * To use implement bloom more like a glow. Texture alpha channel can be
- * used as mask which part are glowing and which are not. see more info at:
- * http://www.gamasutra.com/view/feature/2107/realtime_glow.php
- *
- * NOTE: need to be set before bloom instance is created. After that this
- * does nothing.
- */
- public static boolean useAlphaChannelAsMask = false;
-
- /** how many blur pass */
- public int blurPasses = 1;
-
- private Shader tresholdShader;
- private Shader bloomShader;
-
- private Mesh fullScreenQuad;
-
- private Texture pingPongTex1;
- private Texture pingPongTex2;
- private Texture original;
-
- private FrameBuffer frameBuffer;
- private FrameBuffer pingPongBuffer1;
- private FrameBuffer pingPongBuffer2;
-
- private Shader blurShader;
-
- private float bloomIntensity;
- private float originalIntensity;
- private float threshold;
- private int w;
- private int h;
- private boolean blending = false;
- private boolean capturing = false;
- private float r = 0f;
- private float g = 0f;
- private float b = 0f;
- private float a = 1f;
- private boolean disposeFBO = true;
-
- /**
- * IMPORTANT NOTE CALL THIS WHEN RESUMING
- */
- public void resume(){
- bloomShader.begin();
- bloomShader.setUniformi("u_texture0", 0);
- bloomShader.setUniformi("u_texture1", 1);
- bloomShader.end();
-
- setSize(w, h);
- setThreshold(threshold);
- setBloomIntesity(bloomIntensity);
- setOriginalIntesity(originalIntensity);
-
- original = frameBuffer.getTexture();
- pingPongTex1 = pingPongBuffer1.getTexture();
- pingPongTex2 = pingPongBuffer2.getTexture();
- }
-
- /**
- * Initialize bloom class that capsulate original scene capturate,
- * tresholding, gaussian blurring and blending. Default values: depth = true
- * blending = false 32bits = true
- */
- public Bloom(){
- initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4, null, false, false, true);
- }
-
- public Bloom(boolean useBlending){
- initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4, null, false, useBlending, true);
- }
-
- /**
- * Initialize bloom class that capsulate original scene capturate,
- * tresholding, gaussian blurring and blending.
- *
- * @param FBO_W
- * @param FBO_H how big fbo is used for bloom texture, smaller = more blur and
- * lot faster but aliasing can be problem
- * @param hasDepth do rendering need depth buffer
- * @param useBlending does fbo need alpha channel and is blending enabled when final
- * image is rendered. This allow to combine background graphics
- * and only do blooming on certain objects param use32bitFBO does
- * fbo use higher precision than 16bits.
- */
- public Bloom(int FBO_W, int FBO_H, boolean hasDepth, boolean useBlending, boolean use32bitFBO){
- initialize(FBO_W, FBO_H, null, hasDepth, useBlending, use32bitFBO);
-
- }
-
- /**
- * EXPERT FUNCTIONALITY. no error checking. Use this only if you know what
- * you are doing. Remember that bloom.capture() clear the screen so use
- * continue instead if that is a problem.
- *
- * Initialize bloom class that capsulate original scene capturate,
- * tresholding, gaussian blurring and blending.
- *
- * * @param sceneIsCapturedHere diposing is user responsibility.
- *
- * @param FBO_W
- * @param FBO_H how big fbo is used for bloom texture, smaller = more blur and
- * lot faster but aliasing can be problem
- * @param useBlending does fbo need alpha channel and is blending enabled when final
- * image is rendered. This allow to combine background graphics
- * and only do blooming on certain objects param use32bitFBO does
- * fbo use higher precision than 16bits.
- */
- public Bloom(int FBO_W, int FBO_H, FrameBuffer sceneIsCapturedHere, boolean useBlending, boolean use32bitFBO){
- initialize(FBO_W, FBO_H, sceneIsCapturedHere, false, useBlending, use32bitFBO);
- disposeFBO = false;
- }
-
- private void initialize(int FBO_W, int FBO_H, FrameBuffer fbo, boolean hasDepth, boolean useBlending, boolean use32bitFBO){
- blending = useBlending;
- Format format;
-
- if(use32bitFBO){
- if(useBlending){
- format = Format.RGBA8888;
- }else{
- format = Format.RGB888;
- }
-
- }else{
- if(useBlending){
- format = Format.RGBA4444;
- }else{
- format = Format.RGB565;
- }
- }
- if(fbo == null){
- frameBuffer = new FrameBuffer(format, Core.graphics.getWidth(), Core.graphics.getHeight(), hasDepth);
- }else{
- frameBuffer = fbo;
- }
-
- pingPongBuffer1 = new FrameBuffer(format, FBO_W, FBO_H, false);
-
- pingPongBuffer2 = new FrameBuffer(format, FBO_W, FBO_H, false);
-
- original = frameBuffer.getTexture();
- pingPongTex1 = pingPongBuffer1.getTexture();
- pingPongTex2 = pingPongBuffer2.getTexture();
-
- fullScreenQuad = createFullScreenQuad();
- final String alpha = useBlending ? "alpha_" : "";
-
- bloomShader = createShader("screenspace", alpha + "bloom");
-
- if(useAlphaChannelAsMask){
- tresholdShader = createShader("screenspace", "maskedtreshold");
- }else{
- tresholdShader = createShader("screenspace", alpha + "threshold");
- }
-
- blurShader = createShader("blurspace", alpha + "gaussian");
-
- setSize(FBO_W, FBO_H);
- setBloomIntesity(2.5f);
- setOriginalIntesity(0.8f);
- setThreshold(0.5f);
-
- bloomShader.begin();
- bloomShader.setUniformi("u_texture0", 0);
- bloomShader.setUniformi("u_texture1", 1);
- bloomShader.end();
- }
-
- /**
- * Set clearing color for capturing buffer
- *
- * @param r
- * @param g
- * @param b
- * @param a
- */
- public void setClearColor(float r, float g, float b, float a){
- this.r = r;
- this.g = g;
- this.b = b;
- this.a = a;
- }
-
- /**
- * Call this before rendering scene.
- */
- public void capture(){
- if(!capturing){
- capturing = true;
- frameBuffer.begin();
- Gl.clearColor(r, g, b, a);
- Gl.clear(Gl.colorBufferBit);
-
- }
- }
-
- /**
- * Pause capturing to fbo.
- */
- public void capturePause(){
- if(capturing){
- capturing = false;
- frameBuffer.end();
- }
- }
-
- /** Start capturing again after pause, no clearing is done to framebuffer */
- public void captureContinue(){
- if(!capturing){
- capturing = true;
- frameBuffer.begin();
- }
- }
-
- /**
- * Call this after scene. Renders the bloomed scene.
- */
- public void render(){
- if(capturing){
- capturing = false;
- frameBuffer.end();
- }
-
- Gl.disable(Gl.blend);
- Gl.disable(Gl.depthTest);
- Gl.depthMask(false);
-
- gaussianBlur();
-
- if(blending){
- Gl.enable(Gl.blend);
- Gl.blendFunc(Gl.srcAlpha, Gl.oneMinusSrcAlpha);
- }
-
- pingPongTex1.bind(1);
- original.bind(0);
-
- bloomShader.begin();
- fullScreenQuad.render(bloomShader, Gl.triangleFan);
- bloomShader.end();
-
- }
-
- private void gaussianBlur(){
-
- // cut bright areas of the picture and blit to smaller fbo
-
- original.bind(0);
- pingPongBuffer1.begin();
- tresholdShader.begin();
- fullScreenQuad.render(tresholdShader, Gl.triangleFan, 0, 4);
- tresholdShader.end();
- pingPongBuffer1.end();
-
- for(int i = 0; i < blurPasses; i++){
-
- pingPongTex1.bind(0);
-
- // horizontal
- pingPongBuffer2.begin();
- blurShader.begin();
- blurShader.setUniformf("dir", 1f, 0f);
- fullScreenQuad.render(blurShader, Gl.triangleFan, 0, 4);
- blurShader.end();
- pingPongBuffer2.end();
-
- pingPongTex2.bind(0);
- // vertical
- pingPongBuffer1.begin();
- blurShader.begin();
- blurShader.setUniformf("dir", 0f, 1f);
- fullScreenQuad.render(blurShader, Gl.triangleFan, 0, 4);
- blurShader.end();
- pingPongBuffer1.end();
- }
- }
-
- /**
- * set intensity for bloom. higher mean more brightening for spots that are
- * over threshold
- *
- * @param intensity multiplier for blurred texture in combining phase. must be
- * positive.
- */
- public void setBloomIntesity(float intensity){
- bloomIntensity = intensity;
- bloomShader.begin();
- bloomShader.setUniformf("BloomIntensity", intensity);
- bloomShader.end();
- }
-
- /**
- * set intensity for original scene. under 1 mean darkening and over 1 means
- * lightening
- *
- * @param intensity multiplier for captured texture in combining phase. must be
- * positive.
- */
- public void setOriginalIntesity(float intensity){
- originalIntensity = intensity;
- bloomShader.begin();
- bloomShader.setUniformf("OriginalIntensity", intensity);
- bloomShader.end();
- }
-
- /**
- * Treshold for bright parts. everything under threshold is clamped to 0
- *
- * @param threshold must be in range 0..1
- */
- public void setThreshold(float threshold){
- this.threshold = threshold;
- tresholdShader.begin();
- tresholdShader.setUniformf("threshold", threshold, 1f / (1 - threshold));
- tresholdShader.end();
- }
-
- private void setSize(int FBO_W, int FBO_H){
- w = FBO_W;
- h = FBO_H;
- blurShader.begin();
- blurShader.setUniformf("size", FBO_W, FBO_H);
- blurShader.end();
- }
-
- /**
- * Call this when application is exiting.
- */
- public void dispose(){
- try{
- if(disposeFBO) frameBuffer.dispose();
-
- fullScreenQuad.dispose();
-
- pingPongBuffer1.dispose();
- pingPongBuffer2.dispose();
-
- blurShader.dispose();
- bloomShader.dispose();
- tresholdShader.dispose();
- }catch(Throwable ignored){
-
- }
- }
-
- private static Mesh createFullScreenQuad(){
- float[] verts = {-1, -1, 0, 0, 1, -1, 1, 0, 1, 1, 1, 1, -1, 1, 0, 1};
- Mesh tmpMesh = new Mesh(true, 4, 0,
- new VertexAttribute(Usage.position, 2, "a_position"),
- new VertexAttribute(Usage.textureCoordinates, 2, "a_texCoord0")
- );
-
- tmpMesh.setVertices(verts);
- return tmpMesh;
-
- }
-
- private static Shader createShader(String vertexName, String fragmentName){
- String vertexShader = Core.files.internal("bloomshaders/" + vertexName + ".vertex.glsl").readString();
- String fragmentShader = Core.files.internal("bloomshaders/" + fragmentName + ".fragment.glsl").readString();
- return new Shader(vertexShader, fragmentShader);
- }
-
-}
diff --git a/core/src/mindustry/graphics/LightRenderer.java b/core/src/mindustry/graphics/LightRenderer.java
index 0c0f94e02c..58c91b4f30 100644
--- a/core/src/mindustry/graphics/LightRenderer.java
+++ b/core/src/mindustry/graphics/LightRenderer.java
@@ -24,6 +24,10 @@ public class LightRenderer{
lights.add(run);
}
+ public void add(Position pos, float radius, Color color, float opacity){
+ add(pos.getX(), pos.getY(), radius, color, opacity);
+ }
+
public void add(float x, float y, float radius, Color color, float opacity){
if(!enabled()) return;
@@ -180,13 +184,14 @@ public class LightRenderer{
Draw.color();
buffer.beginDraw(Color.clear);
- Draw.blend(Blending.normal);
+ Gl.blendEquationSeparate(Gl.funcAdd, Gl.max);
+
for(Runnable run : lights){
run.run();
}
Draw.reset();
- Draw.blend();
buffer.endDraw();
+ Gl.blendEquationSeparate(Gl.funcAdd, Gl.funcAdd);
Draw.color();
Shaders.light.ambient.set(state.rules.ambientLight);
@@ -196,4 +201,4 @@ public class LightRenderer{
lights.clear();
}
-}
+}
\ No newline at end of file
diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java
index 6cb4348c23..a1d01b1fc1 100644
--- a/core/src/mindustry/graphics/OverlayRenderer.java
+++ b/core/src/mindustry/graphics/OverlayRenderer.java
@@ -125,7 +125,7 @@ public class OverlayRenderer{
if(tile != null && tile.block() != Blocks.air && tile.getTeam() == player.getTeam()){
tile.block().drawSelect(tile);
- if(Core.input.keyDown(Binding.rotateplaced) && tile.block().rotate){
+ if(Core.input.keyDown(Binding.rotateplaced) && tile.block().rotate && tile.interactable(player.getTeam())){
control.input.drawArrow(tile.block(), tile.x, tile.y, tile.rotation(), true);
Draw.color(Pal.accent, 0.3f + Mathf.absin(4f, 0.2f));
Fill.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize/2f);
diff --git a/core/src/mindustry/graphics/Pixelator.java b/core/src/mindustry/graphics/Pixelator.java
index f746ca69e3..8b8fce9786 100644
--- a/core/src/mindustry/graphics/Pixelator.java
+++ b/core/src/mindustry/graphics/Pixelator.java
@@ -28,10 +28,6 @@ public class Pixelator implements Disposable{
camera.width = (int)camera.width;
camera.height = (int)camera.height;
- boolean hadShields = Core.settings.getBool("animatedshields");
- boolean hadWater = Core.settings.getBool("animatedwater");
- Core.settings.put("animatedwater", false);
- Core.settings.put("animatedshields", false);
graphics.clear(0f, 0f, 0f, 1f);
float px = Core.camera.position.x, py = Core.camera.position.y;
@@ -58,17 +54,9 @@ public class Pixelator implements Disposable{
playerGroup.draw(p -> !p.isDead(), Player::drawName);
Core.camera.position.set(px, py);
- Core.settings.put("animatedwater", hadWater);
- Core.settings.put("animatedshields", hadShields);
renderer.setScale(pre);
}
- public void rebind(){
- if(enabled()){
- buffer.begin();
- }
- }
-
public boolean enabled(){
return Core.settings.getBool("pixelate");
}
diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java
index 0776d9e33e..b5c8f4a823 100644
--- a/core/src/mindustry/input/DesktopInput.java
+++ b/core/src/mindustry/input/DesktopInput.java
@@ -222,7 +222,7 @@ public class DesktopInput extends InputHandler{
cursorType = ui.unloadCursor;
}
- if(!isPlacing() && Math.abs(Core.input.axisTap(Binding.rotate)) > 0 && Core.input.keyDown(Binding.rotateplaced) && cursor.block().rotate){
+ if(cursor.interactable(player.getTeam()) && !isPlacing() && Math.abs(Core.input.axisTap(Binding.rotate)) > 0 && Core.input.keyDown(Binding.rotateplaced) && cursor.block().rotate){
Call.rotateBlock(player, cursor, Core.input.axisTap(Binding.rotate) > 0);
}
}
diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java
index 177ecd78b2..5c8619df48 100644
--- a/core/src/mindustry/input/MobileInput.java
+++ b/core/src/mindustry/input/MobileInput.java
@@ -156,7 +156,7 @@ public class MobileInput extends InputHandler implements GestureListener{
}
void removeRequest(BuildRequest request){
- selectRequests.removeValue(request, true);
+ selectRequests.remove(request, true);
if(!request.breaking){
removals.add(request);
}
diff --git a/core/src/mindustry/io/MapIO.java b/core/src/mindustry/io/MapIO.java
index 85c5cc9cb7..10bcd5cda9 100644
--- a/core/src/mindustry/io/MapIO.java
+++ b/core/src/mindustry/io/MapIO.java
@@ -176,4 +176,4 @@ public class MapIO{
interface TileProvider{
Tile get(int x, int y);
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/maps/Maps.java b/core/src/mindustry/maps/Maps.java
index d94852cf0d..ec9e033eea 100644
--- a/core/src/mindustry/maps/Maps.java
+++ b/core/src/mindustry/maps/Maps.java
@@ -501,4 +501,4 @@ public class Maps{
return provider.next(previous);
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/maps/filters/TerrainFilter.java b/core/src/mindustry/maps/filters/TerrainFilter.java
index d8ad659f8a..f35bf2b3c6 100644
--- a/core/src/mindustry/maps/filters/TerrainFilter.java
+++ b/core/src/mindustry/maps/filters/TerrainFilter.java
@@ -39,4 +39,4 @@ public class TerrainFilter extends GenerateFilter{
in.block = Blocks.air;
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java
index aa6048d833..65b68b3423 100644
--- a/core/src/mindustry/mod/ContentParser.java
+++ b/core/src/mindustry/mod/ContentParser.java
@@ -3,7 +3,7 @@ package mindustry.mod;
import arc.*;
import arc.assets.*;
import arc.audio.*;
-import arc.audio.mock.*;
+import arc.mock.*;
import arc.struct.Array;
import arc.struct.*;
import arc.files.*;
diff --git a/core/src/mindustry/mod/ModLoadingSound.java b/core/src/mindustry/mod/ModLoadingSound.java
index 558b29ce28..7bae2f5a6b 100644
--- a/core/src/mindustry/mod/ModLoadingSound.java
+++ b/core/src/mindustry/mod/ModLoadingSound.java
@@ -1,8 +1,8 @@
package mindustry.mod;
import arc.audio.*;
-import arc.audio.mock.*;
import arc.math.geom.*;
+import arc.mock.*;
import arc.util.ArcAnnotate.*;
public class ModLoadingSound implements Sound{
diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java
index 8ddd3c3300..66dc8d6fa7 100644
--- a/core/src/mindustry/mod/Mods.java
+++ b/core/src/mindustry/mod/Mods.java
@@ -118,7 +118,7 @@ public class Mods implements Loadable{
private void packSprites(Array sprites, LoadedMod mod, boolean prefix){
for(Fi file : sprites){
try(InputStream stream = file.read()){
- byte[] bytes = Streams.copyStreamToByteArray(stream, Math.max((int)file.length(), 512));
+ byte[] bytes = Streams.copyBytes(stream, Math.max((int)file.length(), 512));
Pixmap pixmap = new Pixmap(bytes, 0, bytes.length);
packer.add(getPage(file), (prefix ? mod.name + "-" : "") + file.nameWithoutExtension(), new PixmapRegion(pixmap));
pixmap.dispose();
@@ -317,7 +317,7 @@ public class Mods implements Loadable{
return result;
}
- private LoadedMod locateMod(String name){
+ public LoadedMod locateMod(String name){
return mods.find(mod -> mod.enabled() && mod.name.equals(name));
}
@@ -460,22 +460,25 @@ public class Mods implements Loadable{
eachEnabled(mod -> {
if(mod.root.child("scripts").exists()){
content.setCurrentMod(mod);
- mod.scripts = mod.root.child("scripts").findAll(f -> f.extension().equals("js"));
- Log.debug("[{0}] Found {1} scripts.", mod.meta.name, mod.scripts.size);
-
- for(Fi file : mod.scripts){
+ //if there's only one script file, use it (for backwards compatibility); if there isn't, use "main.js"
+ Array allScripts = mod.root.child("scripts").findAll(f -> f.extEquals("js"));
+ Fi main = allScripts.size == 1 ? allScripts.first() : mod.root.child("scripts").child("main.js");
+ if(main.exists() && !main.isDirectory()){
try{
if(scripts == null){
scripts = platform.createScripts();
}
- scripts.run(mod, file);
+ scripts.run(mod, main);
}catch(Throwable e){
Core.app.post(() -> {
- Log.err("Error loading script {0} for mod {1}.", file.name(), mod.meta.name);
+ Log.err("Error loading main script {0} for mod {1}.", main.name(), mod.meta.name);
e.printStackTrace();
});
- break;
}
+ }else{
+ Core.app.post(() -> {
+ Log.err("No main.js found for mod {0}.", mod.meta.name);
+ });
}
}
});
@@ -611,7 +614,7 @@ public class Mods implements Loadable{
Fi metaf = zip.child("mod.json").exists() ? zip.child("mod.json") : zip.child("mod.hjson").exists() ? zip.child("mod.hjson") : zip.child("plugin.json");
if(!metaf.exists()){
- Log.warn("Mod {0} doesn't have a 'mod.json'/'plugin.json'/'mod.js' file, skipping.", sourceFile);
+ Log.warn("Mod {0} doesn't have a 'mod.json'/'mod.hjson'/'plugin.json' file, skipping.", sourceFile);
throw new IllegalArgumentException("No mod.json found.");
}
diff --git a/core/src/mindustry/mod/Scripts.java b/core/src/mindustry/mod/Scripts.java
index 6d70437dbb..1e1e5cb325 100644
--- a/core/src/mindustry/mod/Scripts.java
+++ b/core/src/mindustry/mod/Scripts.java
@@ -8,16 +8,22 @@ import arc.util.Log.*;
import mindustry.*;
import mindustry.mod.Mods.*;
import org.mozilla.javascript.*;
+import org.mozilla.javascript.commonjs.module.*;
+import org.mozilla.javascript.commonjs.module.provider.*;
+
+import java.io.*;
+import java.net.*;
+import java.util.regex.*;
public class Scripts implements Disposable{
private final Array blacklist = Array.with("net", "files", "reflect", "javax", "rhino", "file", "channels", "jdk",
"runtime", "util.os", "rmi", "security", "org.", "sun.", "beans", "sql", "http", "exec", "compiler", "process", "system",
- ".awt", "socket", "classloader", "oracle");
+ ".awt", "socket", "classloader", "oracle", "invoke");
private final Array whitelist = Array.with("mindustry.net");
private final Context context;
- private final String wrapper;
private Scriptable scope;
private boolean errored;
+ private LoadedMod currentMod = null;
public Scripts(){
Time.mark();
@@ -25,9 +31,12 @@ public class Scripts implements Disposable{
context = Vars.platform.getScriptContext();
context.setClassShutter(type -> !blacklist.contains(type.toLowerCase()::contains) || whitelist.contains(type.toLowerCase()::contains));
context.getWrapFactory().setJavaPrimitiveWrap(false);
-
+
scope = new ImporterTopLevel(context);
- wrapper = Core.files.internal("scripts/wrapper.js").readString();
+
+ new RequireBuilder()
+ .setModuleScriptProvider(new SoftCachingModuleScriptProvider(new ScriptModuleProvider()))
+ .setSandboxed(true).createRequire(context, scope).install(scope);
if(!run(Core.files.internal("scripts/global.js").readString(), "global.js")){
errored = true;
@@ -68,14 +77,23 @@ public class Scripts implements Disposable{
}
public void run(LoadedMod mod, Fi file){
- run(wrapper.replace("$SCRIPT_NAME$", mod.name + "/" + file.nameWithoutExtension()).replace("$CODE$", file.readString()).replace("$MOD_NAME$", mod.name), file.name());
+ currentMod = mod;
+ run(file.readString(), file.name());
+ currentMod = null;
}
private boolean run(String script, String file){
try{
+ if(currentMod != null){
+ //inject script info into file (TODO maybe rhino handles this?)
+ context.evaluateString(scope, "modName = \"" + currentMod.name + "\"\nscriptName = \"" + file + "\"", "initscript.js", 1, null);
+ }
context.evaluateString(scope, script, file, 1, null);
return true;
}catch(Throwable t){
+ if(currentMod != null){
+ file = currentMod.name + "/" + file;
+ }
log(LogLevel.err, file, "" + getError(t));
return false;
}
@@ -85,4 +103,40 @@ public class Scripts implements Disposable{
public void dispose(){
Context.exit();
}
+
+ private class ScriptModuleProvider extends UrlModuleSourceProvider{
+ private Pattern directory = Pattern.compile("^(.+?)/(.+)");
+
+ public ScriptModuleProvider(){
+ super(null, null);
+ }
+
+ @Override
+ public ModuleSource loadSource(String moduleId, Scriptable paths, Object validator) throws IOException, URISyntaxException{
+ if(currentMod == null) return null;
+ return loadSource(moduleId, currentMod.root.child("scripts"), validator);
+ }
+
+ private ModuleSource loadSource(String moduleId, Fi root, Object validator) throws URISyntaxException{
+ Matcher matched = directory.matcher(moduleId);
+ if(matched.find()){
+ LoadedMod required = Vars.mods.locateMod(matched.group(1));
+ String script = matched.group(2);
+ if(required == null || root.equals(required.root.child("scripts"))){ // Mod not found, or already using a mod
+ Fi dir = root.child(matched.group(1));
+ if(!dir.exists()) return null; // Mod and folder not found
+ return loadSource(script, dir, validator);
+ }
+
+ currentMod = required;
+ return loadSource(script, required.root.child("scripts"), validator);
+ }
+
+ Fi module = root.child(moduleId + ".js");
+ if(!module.exists() || module.isDirectory()) return null;
+ return new ModuleSource(
+ new InputStreamReader(new ByteArrayInputStream((module.readString()).getBytes())),
+ null, new URI(moduleId), root.file().toURI(), validator);
+ }
+ }
}
diff --git a/core/src/mindustry/net/Administration.java b/core/src/mindustry/net/Administration.java
index ca5a30f5c4..1c452d81da 100644
--- a/core/src/mindustry/net/Administration.java
+++ b/core/src/mindustry/net/Administration.java
@@ -41,7 +41,6 @@ public class Administration{
if(player.getInfo().messageInfractions >= Config.messageSpamKick.num() && Config.messageSpamKick.num() != 0){
player.con.kick("You have been kicked for spamming.", 1000 * 60 * 2);
}
- player.getInfo().lastSentMessage = message;
return null;
}else{
player.getInfo().messageInfractions = 0;
@@ -191,7 +190,7 @@ public class Administration{
}
}
- bannedIPs.removeValue(ip, false);
+ bannedIPs.remove(ip, false);
if(found){
save();
@@ -231,7 +230,7 @@ public class Administration{
}
/**
- * Returns list of all players with admin status
+ * Returns list of all players which are banned
*/
public Array getBanned(){
Array result = new Array<>();
@@ -420,7 +419,7 @@ public class Administration{
crashReport("Whether to send crash reports.", false, "crashreport"),
logging("Whether to log everything to files.", true),
strict("Whether strict mode is on - corrects positions and prevents duplicate UUIDs.", true),
- antiSpam("Whether spammers are automatically kicked and rate-limited.", true),
+ antiSpam("Whether spammers are automatically kicked and rate-limited.", headless),
messageRateLimit("Message rate limit in seconds. 0 to disable.", 0),
messageSpamKick("How many times a player must send a message before the cooldown to get kicked. 0 to disable.", 3),
socketInput("Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)),
@@ -569,6 +568,7 @@ public class Administration{
player = null;
type = null;
tile = null;
+ block = null;
}
}
diff --git a/core/src/mindustry/net/ArcNetProvider.java b/core/src/mindustry/net/ArcNetProvider.java
index ccfdafb94f..1ed6713e93 100644
--- a/core/src/mindustry/net/ArcNetProvider.java
+++ b/core/src/mindustry/net/ArcNetProvider.java
@@ -418,4 +418,4 @@ public class ArcNetProvider implements NetProvider{
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/net/Interpolator.java b/core/src/mindustry/net/Interpolator.java
index b2a544dbd6..42cf9df137 100644
--- a/core/src/mindustry/net/Interpolator.java
+++ b/core/src/mindustry/net/Interpolator.java
@@ -65,4 +65,4 @@ public class Interpolator{
}
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/net/Packets.java b/core/src/mindustry/net/Packets.java
index 98a3bad857..e24d18db6f 100644
--- a/core/src/mindustry/net/Packets.java
+++ b/core/src/mindustry/net/Packets.java
@@ -181,7 +181,7 @@ public class Packets{
usid = TypeIO.readString(buffer);
mobile = buffer.get() == 1;
color = buffer.getInt();
- byte[] idbytes = new byte[8];
+ byte[] idbytes = new byte[16];
buffer.get(idbytes);
uuid = new String(Base64Coder.encode(idbytes));
int totalMods = buffer.get();
diff --git a/core/src/mindustry/type/StatusEffect.java b/core/src/mindustry/type/StatusEffect.java
index 1ade3af60f..ef86ae6176 100644
--- a/core/src/mindustry/type/StatusEffect.java
+++ b/core/src/mindustry/type/StatusEffect.java
@@ -10,7 +10,7 @@ import mindustry.ctype.ContentType;
import mindustry.entities.*;
import mindustry.entities.Effects.*;
import mindustry.entities.type.*;
-import mindustry.entities.units.Statuses.*;
+import mindustry.entities.units.*;
public class StatusEffect extends MappableContent{
/** Damage dealt by the unit with the effect. */
diff --git a/core/src/mindustry/ui/ItemsDisplay.java b/core/src/mindustry/ui/ItemsDisplay.java
index 995918895f..a597bd256c 100644
--- a/core/src/mindustry/ui/ItemsDisplay.java
+++ b/core/src/mindustry/ui/ItemsDisplay.java
@@ -44,7 +44,7 @@ public class ItemsDisplay extends Table{
t.setText(state.is(State.menu) ? "$launcheditems" : "$launchinfo");
t.setChecked(col.isCollapsed());
((Image)t.getChildren().get(1)).setDrawable(col.isCollapsed() ? Icon.upOpen : Icon.downOpen);
- }).padBottom(4).left().fillX().margin(12f);
+ }).padBottom(4).left().fillX().margin(12f).minWidth(200f);
c.row();
c.add(col);
});
diff --git a/core/src/mindustry/ui/Links.java b/core/src/mindustry/ui/Links.java
index 7047c6d4d7..09f26e78f5 100644
--- a/core/src/mindustry/ui/Links.java
+++ b/core/src/mindustry/ui/Links.java
@@ -16,7 +16,7 @@ public class Links{
new LinkEntry("changelog", "https://github.com/Anuken/Mindustry/releases", Icon.list, Pal.accent.cpy()),
new LinkEntry("trello", "https://trello.com/b/aE2tcUwF", Icon.trello, Color.valueOf("026aa7")),
new LinkEntry("wiki", "https://mindustrygame.github.io/wiki/", Icon.book, Color.valueOf("0f142f")),
- new LinkEntry("feathub", "https://feathub.com/Anuken/Mindustry/", Icon.add, Color.valueOf("ebebeb")),
+ new LinkEntry("feathub", "https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose/", Icon.add, Color.valueOf("ebebeb")),
new LinkEntry("reddit", "https://www.reddit.com/r/Mindustry/", Icon.redditAlien, Color.valueOf("ee593b")),
new LinkEntry("itch.io", "https://anuke.itch.io/mindustry", Icon.itchio, Color.valueOf("fa5c5c")),
new LinkEntry("google-play", "https://play.google.com/store/apps/details?id=io.anuke.mindustry", Icon.googleplay, Color.valueOf("689f38")),
diff --git a/core/src/mindustry/ui/dialogs/CustomGameDialog.java b/core/src/mindustry/ui/dialogs/CustomGameDialog.java
index 03ec05961e..9b3107820a 100644
--- a/core/src/mindustry/ui/dialogs/CustomGameDialog.java
+++ b/core/src/mindustry/ui/dialogs/CustomGameDialog.java
@@ -73,7 +73,6 @@ public class CustomGameDialog extends FloatingDialog{
image.row();
image.add(img).size(images);
-
BorderImage border = new BorderImage(map.safeTexture(), 3f);
border.setScaling(Scaling.fit);
image.replaceImage(border);
@@ -91,4 +90,4 @@ public class CustomGameDialog extends FloatingDialog{
cont.add(pane).uniformX();
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java
index c65efdd919..42ae294123 100644
--- a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java
+++ b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java
@@ -172,6 +172,7 @@ public class CustomRulesDialog extends FloatingDialog{
number("$rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200));
title("$rules.title.experimental");
+ number("$rules.solarpowermultiplier", f -> rules.solarPowerMultiplier = f, () -> rules.solarPowerMultiplier);
check("$rules.lighting", b -> rules.lighting = b, () -> rules.lighting);
main.addButton(b -> {
diff --git a/core/src/mindustry/ui/dialogs/GameOverDialog.java b/core/src/mindustry/ui/dialogs/GameOverDialog.java
index bb9b4fcc8d..f5a9605a82 100644
--- a/core/src/mindustry/ui/dialogs/GameOverDialog.java
+++ b/core/src/mindustry/ui/dialogs/GameOverDialog.java
@@ -62,6 +62,10 @@ public class GameOverDialog extends FloatingDialog{
t.row();
t.add(Core.bundle.format("stat.deconstructed", state.stats.buildingsDeconstructed));
t.row();
+ if(control.saves.getCurrent() != null){
+ t.add(Core.bundle.format("stat.playtime", control.saves.getCurrent().getPlayTime()));
+ t.row();
+ }
if(world.isZone() && !state.stats.itemsDelivered.isEmpty()){
t.add("$stat.delivered");
t.row();
diff --git a/core/src/mindustry/ui/dialogs/JoinDialog.java b/core/src/mindustry/ui/dialogs/JoinDialog.java
index 429b27f5fb..2e62161957 100644
--- a/core/src/mindustry/ui/dialogs/JoinDialog.java
+++ b/core/src/mindustry/ui/dialogs/JoinDialog.java
@@ -13,6 +13,7 @@ import arc.util.serialization.*;
import mindustry.*;
import mindustry.core.*;
import mindustry.gen.*;
+import mindustry.graphics.*;
import mindustry.net.*;
import mindustry.net.Packets.*;
import mindustry.ui.*;
@@ -25,6 +26,7 @@ public class JoinDialog extends FloatingDialog{
Server renaming;
Table local = new Table();
Table remote = new Table();
+ Table global = new Table();
Table hosts = new Table();
int totalHosts;
@@ -80,15 +82,11 @@ public class JoinDialog extends FloatingDialog{
}
});
- keyDown(KeyCode.F5, () -> {
- refreshLocal();
- refreshRemote();
- });
+ keyDown(KeyCode.F5, this::refreshAll);
shown(() -> {
setup();
- refreshLocal();
- refreshRemote();
+ refreshAll();
if(!steam){
Core.app.post(() -> Core.settings.getBoolOnce("joininfo", () -> ui.showInfo("$join.info")));
@@ -97,11 +95,16 @@ public class JoinDialog extends FloatingDialog{
onResize(() -> {
setup();
- refreshLocal();
- refreshRemote();
+ refreshAll();
});
}
+ void refreshAll(){
+ refreshLocal();
+ refreshRemote();
+ refreshGlobal();
+ }
+
void setupRemote(){
remote.clear();
@@ -128,21 +131,12 @@ public class JoinDialog extends FloatingDialog{
inner.add(button.getLabel()).growX();
inner.addImageButton(Icon.upOpen, Styles.emptyi, () -> {
- int index = servers.indexOf(server);
- if(index > 0){
- servers.remove(index);
- servers.insert(0, server);
+ moveRemote(server, -1);
- saveServers();
- setupRemote();
- for(Server other : servers){
- if(other.lastHost != null){
- setupServer(other, other.lastHost);
- }else{
- refreshServer(other);
- }
- }
- }
+ }).margin(3f).padTop(6f).top().right();
+
+ inner.addImageButton(Icon.downOpen, Styles.emptyi, () -> {
+ moveRemote(server, +1);
}).margin(3f).padTop(6f).top().right();
@@ -157,7 +151,7 @@ public class JoinDialog extends FloatingDialog{
inner.addImageButton(Icon.trash, Styles.emptyi, () -> {
ui.showConfirm("$confirm", "$server.delete", () -> {
- servers.removeValue(server, true);
+ servers.remove(server, true);
saveServers();
setupRemote();
refreshRemote();
@@ -172,6 +166,26 @@ public class JoinDialog extends FloatingDialog{
}
}
+ void moveRemote(Server server, int sign){
+ int index = servers.indexOf(server);
+
+ if(index + sign < 0) return;
+ if(index + sign > servers.size - 1) return;
+
+ servers.remove(index);
+ servers.insert(index + sign, server);
+
+ saveServers();
+ setupRemote();
+ for(Server other : servers){
+ if(other.lastHost != null){
+ setupServer(other, other.lastHost);
+ }else{
+ refreshServer(other);
+ }
+ }
+ }
+
void refreshRemote(){
for(Server server : servers){
refreshServer(server);
@@ -228,20 +242,22 @@ public class JoinDialog extends FloatingDialog{
}
void setup(){
+ local.clear();
+ remote.clear();
+ global.clear();
float w = targetWidth();
hosts.clear();
- hosts.add(remote).growX();
- hosts.row();
- hosts.add(local).width(w);
+ section("$servers.local", local);
+ section("$servers.remote", remote);
+ section("$servers.global", global);
ScrollPane pane = new ScrollPane(hosts);
pane.setFadeScrollBars(false);
pane.setScrollingDisabled(true, false);
setupRemote();
- refreshRemote();
cont.clear();
cont.table(t -> {
@@ -289,6 +305,24 @@ public class JoinDialog extends FloatingDialog{
});
}
+ void section(String label, Table servers){
+ Collapser coll = new Collapser(servers, Core.settings.getBool("collapsed-" + label, false));
+ coll.setDuration(0.1f);
+
+ hosts.table(name -> {
+ name.add(label).pad(10).growX().left().color(Pal.accent);
+ name.addImageButton(Icon.downOpen, Styles.emptyi, () -> {
+ coll.toggle(false);
+ Core.settings.putSave("collapsed-" + label, coll.isCollapsed());
+ }).update(i -> i.getStyle().imageUp = (!coll.isCollapsed() ? Icon.upOpen : Icon.downOpen)).size(40f).right().padRight(10f);
+ }).growX();
+ hosts.row();
+ hosts.addImage().growX().pad(5).padLeft(10).padRight(10).height(3).color(Pal.accent);
+ hosts.row();
+ hosts.add(coll).width(targetWidth());
+ hosts.row();
+ }
+
void refreshLocal(){
totalHosts = 0;
@@ -296,12 +330,17 @@ public class JoinDialog extends FloatingDialog{
local.background(null);
local.table(Tex.button, t -> t.label(() -> "[accent]" + Core.bundle.get("hosts.discovering.any") + Strings.animated(Time.time(), 4, 10f, ".")).pad(10f)).growX();
net.discoverServers(this::addLocalHost, this::finishLocalHosts);
+ }
+
+ void refreshGlobal(){
+ global.clear();
+ global.background(null);
for(String host : defaultServers){
String resaddress = host.contains(":") ? host.split(":")[0] : host;
int resport = host.contains(":") ? Strings.parseInt(host.split(":")[1]) : port;
net.pingHost(resaddress, resport, res -> {
res.port = resport;
- addLocalHost(res);
+ addGlobalHost(res);
}, e -> {});
}
}
@@ -334,6 +373,18 @@ public class JoinDialog extends FloatingDialog{
buildServer(host, button);
}
+ void addGlobalHost(Host host){
+ global.background(null);
+ float w = targetWidth();
+
+ global.row();
+
+ TextButton button = global.addButton("", Styles.cleart, () -> safeConnect(host.address, host.port, host.version))
+ .width(w).pad(5f).get();
+ button.clearChildren();
+ buildServer(host, button);
+ }
+
public void connect(String ip, int port){
if(player.name.trim().isEmpty()){
ui.showInfo("$noname");
@@ -428,4 +479,4 @@ public class JoinDialog extends FloatingDialog{
public Server(){
}
}
-}
+}
\ No newline at end of file
diff --git a/core/src/mindustry/ui/dialogs/ModsDialog.java b/core/src/mindustry/ui/dialogs/ModsDialog.java
index 8320c9eab9..00d6e2351f 100644
--- a/core/src/mindustry/ui/dialogs/ModsDialog.java
+++ b/core/src/mindustry/ui/dialogs/ModsDialog.java
@@ -40,7 +40,7 @@ public class ModsDialog extends FloatingDialog{
}else{
try{
Fi file = tmpDirectory.child(text.replace("/", "") + ".zip");
- Streams.copyStream(result.getResultAsStream(), file.write(false));
+ Streams.copy(result.getResultAsStream(), file.write(false));
mods.importMod(file);
file.delete();
Core.app.post(() -> {
@@ -87,7 +87,7 @@ public class ModsDialog extends FloatingDialog{
void modError(Throwable error){
ui.loadfrag.hide();
- if(Strings.getCauses(error).contains(t -> t.getMessage() != null && (t.getMessage().contains("SSL") || t.getMessage().contains("protocol")))){
+ if(Strings.getCauses(error).contains(t -> t.getMessage() != null && (t.getMessage().contains("trust anchor") || t.getMessage().contains("SSL") || t.getMessage().contains("protocol")))){
ui.showErrorMessage("$feature.unsupported");
}else{
ui.showException(error);
diff --git a/core/src/mindustry/ui/dialogs/SchematicsDialog.java b/core/src/mindustry/ui/dialogs/SchematicsDialog.java
index 86408921b9..5a67dacd58 100644
--- a/core/src/mindustry/ui/dialogs/SchematicsDialog.java
+++ b/core/src/mindustry/ui/dialogs/SchematicsDialog.java
@@ -1,7 +1,7 @@
package mindustry.ui.dialogs;
import arc.*;
-import arc.struct.*;
+import arc.files.*;
import arc.graphics.*;
import arc.graphics.Texture.*;
import arc.graphics.g2d.*;
@@ -10,6 +10,7 @@ import arc.scene.ui.*;
import arc.scene.ui.ImageButton.*;
import arc.scene.ui.TextButton.*;
import arc.scene.ui.layout.*;
+import arc.struct.*;
import arc.util.*;
import mindustry.core.GameState.*;
import mindustry.game.*;
@@ -89,6 +90,13 @@ public class SchematicsDialog extends FloatingDialog{
buttons.addImageButton(Icon.pencil, style, () -> {
ui.showTextInput("$schematic.rename", "$name", s.name(), res -> {
+ Schematic replacement = schematics.all().find(other -> other.name().equals(res) && other != s);
+ if(replacement != null){
+ //renaming to an existing schematic is not allowed, as it is not clear how the tags would be merged, and which one should be removed
+ ui.showErrorMessage("$schematic.exists");
+ return;
+ }
+
s.tags.put("name", res);
s.save();
rebuildPane[0].run();
@@ -214,14 +222,27 @@ public class SchematicsDialog extends FloatingDialog{
Core.app.setClipboardText(schematics.writeBase64(s));
}).marginLeft(12f);
t.row();
- t.addImageTextButton("$schematic.exportfile", Icon.export, style, () -> platform.showFileChooser(false, schematicExtension, file -> {
- dialog.hide();
- try{
- Schematics.write(s, file);
- }catch(Exception e){
- ui.showException(e);
+ t.addImageTextButton("$schematic.exportfile", Icon.export, style, () -> {
+ if(!ios){
+ platform.showFileChooser(false, schematicExtension, file -> {
+ dialog.hide();
+ try{
+ Schematics.write(s, file);
+ }catch(Throwable e){
+ ui.showException(e);
+ }
+ });
+ }else{
+ dialog.hide();
+ try{
+ Fi file = Core.files.local(s.name() + "." + schematicExtension);
+ Schematics.write(s, file);
+ platform.shareFile(file);
+ }catch(Throwable e){
+ ui.showException(e);
+ }
}
- })).marginLeft(12f);
+ }).marginLeft(12f);
});
});
diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java
index 05c1a08b11..af4f6db807 100644
--- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java
+++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java
@@ -144,7 +144,7 @@ public class SettingsMenuDialog extends SettingsDialog{
}
})));
- if(!ios){
+ if(!mobile){
t.row();
t.addImageTextButton("$data.openfolder", Icon.folder, style, () -> Core.app.openFolder(Core.settings.getDataDirectory().absolutePath()));
}
@@ -330,7 +330,12 @@ public class SettingsMenuDialog extends SettingsDialog{
if(Shaders.shield != null){
graphics.checkPref("animatedshields", !mobile);
}
- graphics.checkPref("bloom", !mobile, val -> renderer.toggleBloom(val));
+ if(!ios){
+ graphics.checkPref("bloom", !mobile, val -> renderer.toggleBloom(val));
+ }else{
+ Core.settings.put("bloom", false);
+ }
+
graphics.checkPref("pixelate", false, val -> {
if(val){
Events.fire(Trigger.enablePixelation);
diff --git a/core/src/mindustry/ui/fragments/ChatFragment.java b/core/src/mindustry/ui/fragments/ChatFragment.java
index a6cd4354cf..f1cd0d1327 100644
--- a/core/src/mindustry/ui/fragments/ChatFragment.java
+++ b/core/src/mindustry/ui/fragments/ChatFragment.java
@@ -231,6 +231,8 @@ public class ChatFragment extends Table{
fadetime += 1f;
fadetime = Math.min(fadetime, messagesShown) + 1f;
+
+ if(scrollPos > 0) scrollPos++;
}
private static class ChatMessage{
diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java
index 3109179e1b..09f309dd5d 100644
--- a/core/src/mindustry/ui/fragments/HudFragment.java
+++ b/core/src/mindustry/ui/fragments/HudFragment.java
@@ -43,6 +43,9 @@ public class HudFragment extends Fragment{
private boolean shown = true;
private float dsize = 47.2f;
+ private String hudText = "";
+ private boolean showHudText;
+
private long lastToast;
public void build(Group parent){
@@ -341,6 +344,19 @@ public class HudFragment extends Fragment{
t.add("$saveload").style(Styles.outlineLabel);
});
+ parent.fill(p -> {
+ p.top().table(Styles.black3, t -> t.margin(4).label(() -> hudText)
+ .style(Styles.outlineLabel)).padTop(10).visible(p.color.a >= 0.001f);
+ p.update(() -> {
+ p.color.a = Mathf.lerpDelta(p.color.a, Mathf.num(showHudText), 0.2f);
+ if(state.is(State.menu)){
+ p.color.a = 0f;
+ showHudText = false;
+ }
+ });
+ p.touchable(Touchable.disabled);
+ });
+
blockfrag.build(parent);
}
@@ -368,6 +384,15 @@ public class HudFragment extends Fragment{
}
}
+ public void setHudText(String text){
+ showHudText = true;
+ hudText = text;
+ }
+
+ public void toggleHudText(boolean shown){
+ showHudText = shown;
+ }
+
private void scheduleToast(Runnable run){
long duration = (int)(3.5 * 1000);
long since = Time.timeSinceMillis(lastToast);
diff --git a/core/src/mindustry/ui/layout/BranchTreeLayout.java b/core/src/mindustry/ui/layout/BranchTreeLayout.java
index 15dbe4ae6f..0e24685705 100644
--- a/core/src/mindustry/ui/layout/BranchTreeLayout.java
+++ b/core/src/mindustry/ui/layout/BranchTreeLayout.java
@@ -288,4 +288,4 @@ public class BranchTreeLayout implements TreeLayout{
public enum TreeAlignment{
center, towardsRoot, awayFromRoot
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/world/BlockStorage.java b/core/src/mindustry/world/BlockStorage.java
index 20c1ca8f98..338ec4526f 100644
--- a/core/src/mindustry/world/BlockStorage.java
+++ b/core/src/mindustry/world/BlockStorage.java
@@ -274,8 +274,7 @@ public abstract class BlockStorage extends UnlockableContent{
/** Try offloading an item to a nearby container in its facing direction. Returns true if success. */
public boolean offloadDir(Tile tile, Item item){
- Tile other = tile.getNearby(tile.rotation());
- if(other != null) other = other.link();
+ Tile other = tile.front();
if(other != null && other.getTeam() == tile.getTeam() && other.block().acceptItem(item, other, tile)){
other.block().handleItem(item, other, tile);
return true;
diff --git a/core/src/mindustry/world/blocks/Autotiler.java b/core/src/mindustry/world/blocks/Autotiler.java
index 4c25ca170d..75cb41419a 100644
--- a/core/src/mindustry/world/blocks/Autotiler.java
+++ b/core/src/mindustry/world/blocks/Autotiler.java
@@ -84,7 +84,7 @@ public interface Autotiler{
default boolean blends(Tile tile, int rotation, int direction){
Tile other = tile.getNearby(Mathf.mod(rotation - direction, 4));
if(other != null) other = other.link();
- return other != null && blends(tile, rotation, other.x, other.y, other.rotation(), other.block());
+ return other != null && other.getTeam() == tile.getTeam() && blends(tile, rotation, other.x, other.y, other.rotation(), other.block());
}
default boolean blendsArmored(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){
diff --git a/core/src/mindustry/world/blocks/Floor.java b/core/src/mindustry/world/blocks/Floor.java
index c9d7759bc9..ba4f5db8e5 100644
--- a/core/src/mindustry/world/blocks/Floor.java
+++ b/core/src/mindustry/world/blocks/Floor.java
@@ -221,4 +221,4 @@ public class Floor extends Block{
return block.edges()[x][2 - y];
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/world/blocks/ItemSelection.java b/core/src/mindustry/world/blocks/ItemSelection.java
index 8fe4994d5b..cf4f5a0580 100644
--- a/core/src/mindustry/world/blocks/ItemSelection.java
+++ b/core/src/mindustry/world/blocks/ItemSelection.java
@@ -1,31 +1,29 @@
package mindustry.world.blocks;
-import arc.struct.*;
import arc.func.*;
import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
+import arc.struct.*;
+import mindustry.ctype.*;
import mindustry.gen.*;
-import mindustry.type.*;
import mindustry.ui.*;
-import mindustry.ui.Cicon;
import static mindustry.Vars.*;
public class ItemSelection{
+ private static float scrollPos = 0f;
- public static void buildItemTable(Table table, Prov- holder, Cons
- consumer){
-
- Array
- items = content.items();
+ public static void buildTable(Table table, Array items, Prov holder, Cons consumer){
ButtonGroup group = new ButtonGroup<>();
group.setMinCheckCount(0);
Table cont = new Table();
- cont.defaults().size(38);
+ cont.defaults().size(40);
int i = 0;
- for(Item item : items){
+ for(T item : items){
if(!data.isUnlocked(item) && world.isZone()) continue;
ImageButton button = cont.addImageButton(Tex.whiteui, Styles.clearToggleTransi, 24, () -> control.input.frag.config.hideConfig()).group(group).get();
@@ -38,6 +36,22 @@ public class ItemSelection{
}
}
- table.add(cont);
+ //add extra blank spaces so it looks nice
+ if(i % 4 != 0){
+ int remaining = 4 - (i % 4);
+ for(int j = 0; j < remaining; j++){
+ cont.addImage(Styles.black6);
+ }
+ }
+
+ ScrollPane pane = new ScrollPane(cont, Styles.smallPane);
+ pane.setScrollingDisabled(true, false);
+ pane.setScrollYForce(scrollPos);
+ pane.update(() -> {
+ scrollPos = pane.getScrollY();
+ });
+
+ pane.setOverscroll(false, false);
+ table.add(pane).maxHeight(Scl.scl(40 * 5));
}
}
diff --git a/core/src/mindustry/world/blocks/distribution/Conveyor.java b/core/src/mindustry/world/blocks/distribution/Conveyor.java
index ea81cdbb48..56a4d7f2ad 100644
--- a/core/src/mindustry/world/blocks/distribution/Conveyor.java
+++ b/core/src/mindustry/world/blocks/distribution/Conveyor.java
@@ -1,11 +1,12 @@
package mindustry.world.blocks.distribution;
import arc.*;
-import arc.struct.*;
import arc.func.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
+import arc.struct.*;
+import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.entities.traits.BuilderTrait.*;
@@ -24,10 +25,8 @@ import static mindustry.Vars.*;
public class Conveyor extends Block implements Autotiler{
private static final float itemSpace = 0.4f;
- private static final float minmove = 1f / (Short.MAX_VALUE - 2);
- private static ItemPos drawpos = new ItemPos();
- private static ItemPos pos1 = new ItemPos();
- private static ItemPos pos2 = new ItemPos();
+ private static final int capacity = 4;
+
private final Vec2 tr1 = new Vec2();
private final Vec2 tr2 = new Vec2();
private TextureRegion[][] regions = new TextureRegion[7][4];
@@ -51,12 +50,6 @@ public class Conveyor extends Block implements Autotiler{
unloadable = false;
}
- private static int compareItems(long a, long b){
- pos1.set(a, ItemPos.packShorts);
- pos2.set(b, ItemPos.packShorts);
- return Float.compare(pos1.y, pos2.y);
- }
-
@Override
public void setStats(){
super.setStats();
@@ -100,6 +93,12 @@ public class Conveyor extends Block implements Autotiler{
entity.blendbits = bits[0];
entity.blendsclx = bits[1];
entity.blendscly = bits[2];
+
+ if(tile.front() != null && tile.front().entity != null){
+ entity.next = tile.front().entity;
+ entity.nextc = entity.next instanceof ConveyorEntity && entity.next.getTeam() == tile.getTeam() ? (ConveyorEntity)entity.next : null;
+ entity.aligned = entity.nextc != null && tile.rotation() == entity.next.tile.rotation();
+ }
}
@Override
@@ -124,27 +123,17 @@ public class Conveyor extends Block implements Autotiler{
@Override
public void drawLayer(Tile tile){
- ConveyorEntity entity = tile.ent();
-
+ ConveyorEntity e = tile.ent();
byte rotation = tile.rotation();
- try{
+ for(int i = 0; i < e.len; i++){
+ Item item = e.ids[i];
+ tr1.trns(rotation * 90, tilesize, 0);
+ tr2.trns(rotation * 90, -tilesize / 2f, e.xs[i] * tilesize / 2f);
- for(int i = 0; i < entity.convey.size; i++){
- ItemPos pos = drawpos.set(entity.convey.get(i), ItemPos.drawShorts);
-
- if(pos.item == null) continue;
-
- tr1.trns(rotation * 90, tilesize, 0);
- tr2.trns(rotation * 90, -tilesize / 2f, pos.x * tilesize / 2f);
-
- Draw.rect(pos.item.icon(Cicon.medium),
- (tile.x * tilesize + tr1.x * pos.y + tr2.x),
- (tile.y * tilesize + tr1.y * pos.y + tr2.y), itemSize, itemSize);
- }
-
- }catch(IndexOutOfBoundsException e){
- Log.err(e);
+ Draw.rect(item.icon(Cicon.medium),
+ (tile.x * tilesize + tr1.x * e.ys[i] + tr2.x),
+ (tile.y * tilesize + tr1.y * e.ys[i] + tr2.y), itemSize, itemSize);
}
}
@@ -173,79 +162,56 @@ public class Conveyor extends Block implements Autotiler{
if(Math.abs(tile.worldx() - unit.x) < 1f) centerx = 0f;
}
- if(entity.convey.size * itemSpace < 0.9f){
+ if(entity.len * itemSpace < 0.9f){
unit.applyImpulse((tx * speed + centerx) * entity.delta(), (ty * speed + centery) * entity.delta());
}
}
@Override
public void update(Tile tile){
- ConveyorEntity entity = tile.ent();
- entity.minitem = 1f;
- Tile next = tile.getNearby(tile.rotation());
- if(next != null) next = next.link();
+ ConveyorEntity e = tile.ent();
+ e.minitem = 1f;
+ e.mid = 0;
- float nextMax = next != null && next.block() instanceof Conveyor && next.block().acceptItem(null, next, tile) ? 1f - Math.max(itemSpace - next.ent().minitem, 0) : 1f;
- int minremove = Integer.MAX_VALUE;
+ //skip updates if possible
+ if(e.len == 0){
+ e.clogHeat = 0f;
+ e.sleep();
+ return;
+ }
- for(int i = entity.convey.size - 1; i >= 0; i--){
- long value = entity.convey.get(i);
- ItemPos pos = pos1.set(value, ItemPos.updateShorts);
+ float nextMax = e.aligned ? 1f - Math.max(itemSpace - e.nextc.minitem, 0) : 1f;
- //..this should never happen, but in case it does, remove it and stop here
- if(pos.item == null){
- entity.convey.removeValue(value);
- break;
- }
+ for(int i = e.len - 1; i >= 0; i--){
+ float nextpos = (i == e.len - 1 ? 100f : e.ys[i + 1]) - itemSpace;
+ float maxmove = Mathf.clamp(nextpos - e.ys[i], 0, speed * e.delta());
- float nextpos = (i == entity.convey.size - 1 ? 100f : pos2.set(entity.convey.get(i + 1), ItemPos.updateShorts).y) - itemSpace;
- float maxmove = Math.min(nextpos - pos.y, speed * entity.delta());
+ e.ys[i] += maxmove;
- if(maxmove > minmove){
- pos.y += maxmove;
- if(Mathf.equal(pos.x, 0, 0.1f)){
- pos.x = 0f;
+ if(e.ys[i] > nextMax) e.ys[i] = nextMax;
+ if(e.ys[i] > 0.5 && i > 0) e.mid = i - 1;
+ e.xs[i] = Mathf.approachDelta(e.xs[i], 0, speed*2);
+
+ if(e.ys[i] >= 1f && offloadDir(tile, e.ids[i])){
+ //align X position if passing forwards
+ if(e.aligned){
+ e.nextc.xs[e.nextc.lastInserted] = e.xs[i];
}
- pos.x = Mathf.lerpDelta(pos.x, 0, 0.1f);
- }
-
- pos.y = Mathf.clamp(pos.y, 0, nextMax);
-
- if(pos.y >= 0.9999f && offloadDir(tile, pos.item)){
- if(next != null && next.block() instanceof Conveyor){
- ConveyorEntity othere = next.ent();
-
- ItemPos ni = pos2.set(othere.convey.get(othere.lastInserted), ItemPos.updateShorts);
-
- if(next.rotation() == tile.rotation()){
- ni.x = pos.x;
- }
- othere.convey.set(othere.lastInserted, ni.pack());
- }
- minremove = Math.min(i, minremove);
- tile.entity.items.remove(pos.item, 1);
- }else{
- value = pos.pack();
-
- if(pos.y < entity.minitem)
- entity.minitem = pos.y;
- entity.convey.set(i, value);
+ //remove last item
+ e.items.remove(e.ids[i], e.len - i);
+ e.len = Math.min(i, e.len);
+ }else if(e.ys[i] < e.minitem){
+ e.minitem = e.ys[i];
}
}
- if(entity.minitem < itemSpace){
- entity.clogHeat = Mathf.lerpDelta(entity.clogHeat, 1f, 0.02f);
+ if(e.minitem < itemSpace + (e.blendbits == 1 ? 0.3f : 0f)){
+ e.clogHeat = Mathf.lerpDelta(e.clogHeat, 1f, 0.02f);
}else{
- entity.clogHeat = Mathf.lerpDelta(entity.clogHeat, 0f, 1f);
+ e.clogHeat = 0f;
}
- if(entity.items.total() == 0){
- entity.sleep();
- }else{
- entity.noSleep();
- }
-
- if(minremove != Integer.MAX_VALUE) entity.convey.truncate(minremove);
+ e.noSleep();
}
@Override
@@ -265,22 +231,22 @@ public class Conveyor extends Block implements Autotiler{
@Override
public int removeStack(Tile tile, Item item, int amount){
- ConveyorEntity entity = tile.ent();
- entity.noSleep();
+ ConveyorEntity e = tile.ent();
+ e.noSleep();
int removed = 0;
for(int j = 0; j < amount; j++){
- for(int i = 0; i < entity.convey.size; i++){
- long val = entity.convey.get(i);
- ItemPos pos = pos1.set(val, ItemPos.drawShorts);
- if(pos.item == item){
- entity.convey.removeValue(val);
- entity.items.remove(item, 1);
- removed++;
+ for(int i = 0; i < e.len; i++){
+ if(e.ids[i] == item){
+ e.remove(i);
+ removed ++;
break;
}
}
}
+
+ e.items.remove(item, removed);
+
return removed;
}
@@ -297,58 +263,67 @@ public class Conveyor extends Block implements Autotiler{
@Override
public void handleStack(Item item, int amount, Tile tile, Unit source){
- ConveyorEntity entity = tile.ent();
+ ConveyorEntity e = tile.ent();
+ amount = Math.min(amount, itemCapacity - e.len);
for(int i = amount - 1; i >= 0; i--){
- long result = ItemPos.packItem(item, 0f, i * itemSpace);
- entity.convey.insert(0, result);
- entity.items.add(item, 1);
+ e.add(0);
+ e.xs[0] = 0;
+ e.ys[0] = i * itemSpace;
+ e.ids[0] = item;
+ e.items.add(item, 1);
}
- entity.noSleep();
+ e.noSleep();
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
+ ConveyorEntity e = tile.ent();
+ if(e.len >= capacity) return false;
int direction = source == null ? 0 : Math.abs(source.relativeTo(tile.x, tile.y) - tile.rotation());
- float minitem = tile.ent().minitem;
- return (((direction == 0) && minitem > itemSpace) ||
- ((direction % 2 == 1) && minitem > 0.52f)) && (source == null || !(source.block().rotate && (source.rotation() + 2) % 4 == tile.rotation()));
+ return (((direction == 0) && e.minitem >= itemSpace) || ((direction % 2 == 1) && e.minitem > 0.7f)) && (source == null || !(source.block().rotate && (source.rotation() + 2) % 4 == tile.rotation()));
}
@Override
public void handleItem(Item item, Tile tile, Tile source){
- byte rotation = tile.rotation();
+ ConveyorEntity e = tile.ent();
+ if(e.len >= capacity) return;
- int ch = Math.abs(source.relativeTo(tile.x, tile.y) - rotation);
- int ang = ((source.relativeTo(tile.x, tile.y) - rotation));
+ byte r = tile.rotation();
+ int ang = ((source.relativeTo(tile.x, tile.y) - r));
+ float x = (ang == -1 || ang == 3) ? 1 : (ang == 1 || ang == -3) ? -1 : 0;
- float pos = ch == 0 ? 0 : ch % 2 == 1 ? 0.5f : 1f;
- float y = (ang == -1 || ang == 3) ? 1 : (ang == 1 || ang == -3) ? -1 : 0;
+ e.noSleep();
+ e.items.add(item, 1);
- ConveyorEntity entity = tile.ent();
- entity.noSleep();
- long result = ItemPos.packItem(item, y * 0.9f, pos);
-
- tile.entity.items.add(item, 1);
-
- for(int i = 0; i < entity.convey.size; i++){
- if(compareItems(result, entity.convey.get(i)) < 0){
- entity.convey.insert(i, result);
- entity.lastInserted = (byte)i;
- return;
- }
+ if(Math.abs(source.relativeTo(tile.x, tile.y) - r) == 0){ //idx = 0
+ e.add(0);
+ e.xs[0] = x;
+ e.ys[0] = 0;
+ e.ids[0] = item;
+ }else{ //idx = mid
+ e.add(e.mid);
+ e.xs[e.mid] = x;
+ e.ys[e.mid] = 0.5f;
+ e.ids[e.mid] = item;
}
-
- //this item must be greater than anything there...
- entity.convey.add(result);
- entity.lastInserted = (byte)(entity.convey.size - 1);
}
public static class ConveyorEntity extends TileEntity{
+ //parallel array data
+ Item[] ids = new Item[capacity];
+ float[] xs = new float[capacity];
+ float[] ys = new float[capacity];
+ //amount of items, always < capacity
+ int len = 0;
+ //next entity
+ @Nullable TileEntity next;
+ @Nullable ConveyorEntity nextc;
+ //whether the next conveyor's rotation == tile rotation
+ boolean aligned;
- LongArray convey = new LongArray();
- byte lastInserted;
+ int lastInserted, mid;
float minitem = 1;
int blendbits;
@@ -356,96 +331,53 @@ public class Conveyor extends Block implements Autotiler{
float clogHeat = 0f;
+ final void add(int o){
+ for(int i = Math.max(o + 1, len); i > o; i--){
+ ids[i] = ids[i - 1];
+ xs[i] = xs[i - 1];
+ ys[i] = ys[i - 1];
+ }
+
+ len++;
+ }
+
+ final void remove(int o){
+ for(int i = o; i < len - 1; i++){
+ ids[i] = ids[i + 1];
+ xs[i] = xs[i + 1];
+ ys[i] = ys[i + 1];
+ }
+
+ len--;
+ }
+
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
- stream.writeInt(convey.size);
+ stream.writeInt(len);
- for(int i = 0; i < convey.size; i++){
- stream.writeInt(ItemPos.toInt(convey.get(i)));
+ for(int i = 0; i < len; i++){
+ stream.writeInt(Pack.intBytes((byte)ids[i].id, (byte)(xs[i] * 127), (byte)(ys[i] * 255 - 128), (byte)0));
}
}
@Override
public void read(DataInput stream, byte revision) throws IOException{
super.read(stream, revision);
- convey.clear();
int amount = stream.readInt();
- convey.ensureCapacity(Math.min(amount, 10));
+ len = Math.min(amount, capacity);
for(int i = 0; i < amount; i++){
- convey.add(ItemPos.toLong(stream.readInt()));
+ int val = stream.readInt();
+ byte id = (byte)(val >> 24);
+ float x = (float)((byte)(val >> 16)) / 127f;
+ float y = ((float)((byte)(val >> 8)) + 128f) / 255f;
+ if(i < capacity){
+ ids[i] = content.item(id);
+ xs[i] = x;
+ ys[i] = y;
+ }
}
}
}
-
- //Container class. Do not instantiate.
- static class ItemPos{
- private static short[] writeShort = new short[4];
- private static byte[] writeByte = new byte[4];
-
- private static short[] packShorts = new short[4];
- private static short[] drawShorts = new short[4];
- private static short[] updateShorts = new short[4];
-
- Item item;
- float x, y;
-
- private ItemPos(){
- }
-
- static long packItem(Item item, float x, float y){
- short[] shorts = packShorts;
- shorts[0] = (short)item.id;
- shorts[1] = (short)(x * Short.MAX_VALUE);
- shorts[2] = (short)((y - 1f) * Short.MAX_VALUE);
- return Pack.longShorts(shorts);
- }
-
- static int toInt(long value){
- short[] values = Pack.shorts(value, writeShort);
-
- short itemid = values[0];
- float x = values[1] / (float)Short.MAX_VALUE;
- float y = ((float)values[2]) / Short.MAX_VALUE + 1f;
-
- byte[] bytes = writeByte;
- bytes[0] = (byte)itemid;
- bytes[1] = (byte)(x * 127);
- bytes[2] = (byte)(y * 255 - 128);
-
- return Pack.intBytes(bytes);
- }
-
- static long toLong(int value){
- byte[] values = Pack.bytes(value, writeByte);
-
- short itemid = content.item(values[0]).id;
- float x = values[1] / 127f;
- float y = ((int)values[2] + 128) / 255f;
-
- short[] shorts = writeShort;
- shorts[0] = itemid;
- shorts[1] = (short)(x * Short.MAX_VALUE);
- shorts[2] = (short)((y - 1f) * Short.MAX_VALUE);
- return Pack.longShorts(shorts);
- }
-
- ItemPos set(long lvalue, short[] values){
- Pack.shorts(lvalue, values);
-
- if(values[0] >= content.items().size || values[0] < 0)
- item = null;
- else
- item = content.items().get(values[0]);
-
- x = values[1] / (float)Short.MAX_VALUE;
- y = ((float)values[2]) / Short.MAX_VALUE + 1f;
- return this;
- }
-
- long pack(){
- return packItem(item, x, y);
- }
- }
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/world/blocks/distribution/Junction.java b/core/src/mindustry/world/blocks/distribution/Junction.java
index 99c22057e1..b1c2dd2b7d 100644
--- a/core/src/mindustry/world/blocks/distribution/Junction.java
+++ b/core/src/mindustry/world/blocks/distribution/Junction.java
@@ -24,7 +24,6 @@ public class Junction extends Block{
super(name);
update = true;
solid = true;
- instantTransfer = true;
group = BlockGroup.transportation;
unloadable = false;
entityType = JunctionEntity::new;
diff --git a/core/src/mindustry/world/blocks/distribution/OverflowGate.java b/core/src/mindustry/world/blocks/distribution/OverflowGate.java
index c3314577f3..3236010b2a 100644
--- a/core/src/mindustry/world/blocks/distribution/OverflowGate.java
+++ b/core/src/mindustry/world/blocks/distribution/OverflowGate.java
@@ -2,13 +2,15 @@ package mindustry.world.blocks.distribution;
import arc.math.Mathf;
import arc.util.Time;
-import mindustry.entities.type.TileEntity;
+import mindustry.entities.type.*;
import mindustry.type.Item;
import mindustry.world.*;
import mindustry.world.meta.BlockGroup;
import java.io.*;
+import static mindustry.Vars.world;
+
public class OverflowGate extends Block{
public float speed = 1f;
public boolean invert = false;
@@ -28,6 +30,11 @@ public class OverflowGate extends Block{
return true;
}
+ @Override
+ public int acceptStack(Item item, int amount, Tile tile, Unit source){
+ return 0;
+ }
+
@Override
public int removeStack(Tile tile, Item item, int amount){
OverflowGateEntity entity = tile.ent();
@@ -47,6 +54,11 @@ public class OverflowGate extends Block{
}
if(entity.lastItem != null){
+ if(entity.lastInput == null){
+ entity.lastItem = null;
+ return;
+ }
+
entity.time += 1f / speed * Time.delta();
Tile target = getTileTarget(tile, entity.lastItem, entity.lastInput, false);
@@ -120,19 +132,24 @@ public class OverflowGate extends Block{
@Override
public byte version(){
- return 2;
+ return 3;
}
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
+ stream.writeInt(lastInput == null ? Pos.invalid : lastInput.pos());
}
@Override
public void read(DataInput stream, byte revision) throws IOException{
super.read(stream, revision);
+
if(revision == 1){
new DirectionalItemBuffer(25, 50f).read(stream);
+ }else if(revision == 3){
+ lastInput = world.tile(stream.readInt());
+ lastItem = items.first();
}
}
}
diff --git a/core/src/mindustry/world/blocks/distribution/Sorter.java b/core/src/mindustry/world/blocks/distribution/Sorter.java
index 22e196791e..192c27a7db 100644
--- a/core/src/mindustry/world/blocks/distribution/Sorter.java
+++ b/core/src/mindustry/world/blocks/distribution/Sorter.java
@@ -14,7 +14,7 @@ import mindustry.world.meta.*;
import java.io.*;
-import static mindustry.Vars.content;
+import static mindustry.Vars.*;
public class Sorter extends Block{
private static Item lastItem;
@@ -46,6 +46,9 @@ public class Sorter extends Block{
@Override
public void configured(Tile tile, Player player, int value){
tile.ent().sortItem = content.item(value);
+ if(!headless){
+ renderer.minimap.update(tile);
+ }
}
@Override
@@ -133,7 +136,7 @@ public class Sorter extends Block{
@Override
public void buildConfiguration(Tile tile, Table table){
SorterEntity entity = tile.ent();
- ItemSelection.buildItemTable(table, () -> entity.sortItem, item -> {
+ ItemSelection.buildTable(table, content.items(), () -> entity.sortItem, item -> {
lastItem = item;
tile.configure(item == null ? -1 : item.id);
});
diff --git a/core/src/mindustry/world/blocks/liquid/LiquidExtendingBridge.java b/core/src/mindustry/world/blocks/liquid/LiquidExtendingBridge.java
index 8b7840e9ad..ceb3e0c58f 100644
--- a/core/src/mindustry/world/blocks/liquid/LiquidExtendingBridge.java
+++ b/core/src/mindustry/world/blocks/liquid/LiquidExtendingBridge.java
@@ -39,7 +39,6 @@ public class LiquidExtendingBridge extends ExtendingItemBridge{
}
if(entity.uptime >= 0.5f){
-
if(tryMoveLiquid(tile, other, false, entity.liquids.current()) > 0.1f){
entity.cycleSpeed = Mathf.lerpDelta(entity.cycleSpeed, 4f, 0.05f);
}else{
diff --git a/core/src/mindustry/world/blocks/power/Battery.java b/core/src/mindustry/world/blocks/power/Battery.java
index 2783984747..38fc08ed5e 100644
--- a/core/src/mindustry/world/blocks/power/Battery.java
+++ b/core/src/mindustry/world/blocks/power/Battery.java
@@ -1,10 +1,29 @@
package mindustry.world.blocks.power;
+import arc.graphics.*;
+import arc.graphics.g2d.*;
+import mindustry.world.*;
+
+import static mindustry.Vars.tilesize;
+
public class Battery extends PowerDistributor{
+ public int topRegion = reg("-top");
+
+ public Color emptyLightColor = Color.valueOf("f8c266");
+ public Color fullLightColor = Color.valueOf("fb9567");
public Battery(String name){
super(name);
outputsPower = true;
consumesPower = true;
}
+
+ @Override
+ public void draw(Tile tile){
+ Draw.color(emptyLightColor, fullLightColor, tile.entity.power.status);
+ Fill.square(tile.drawx(), tile.drawy(), tilesize * size / 2f - 1);
+ Draw.color();
+
+ Draw.rect(reg(topRegion), tile.drawx(), tile.drawy());
+ }
}
diff --git a/core/src/mindustry/world/blocks/power/SolarGenerator.java b/core/src/mindustry/world/blocks/power/SolarGenerator.java
index 75a23c9d6f..cd7efdb5ed 100644
--- a/core/src/mindustry/world/blocks/power/SolarGenerator.java
+++ b/core/src/mindustry/world/blocks/power/SolarGenerator.java
@@ -17,7 +17,7 @@ public class SolarGenerator extends PowerGenerator{
@Override
public void update(Tile tile){
- tile.ent().productionEfficiency = state.rules.lighting ? 1f - state.rules.ambientLight.a : 1f;
+ tile.ent().productionEfficiency = state.rules.solarPowerMultiplier < 0 ? (state.rules.lighting ? 1f - state.rules.ambientLight.a : 1f) : state.rules.solarPowerMultiplier;
}
@Override
diff --git a/core/src/mindustry/world/blocks/production/GenericCrafter.java b/core/src/mindustry/world/blocks/production/GenericCrafter.java
index 3dd94822be..56333434a7 100644
--- a/core/src/mindustry/world/blocks/production/GenericCrafter.java
+++ b/core/src/mindustry/world/blocks/production/GenericCrafter.java
@@ -139,7 +139,7 @@ public class GenericCrafter extends Block{
if(outputItem != null && tile.entity.items.get(outputItem.item) >= itemCapacity){
return false;
}
- return outputLiquid == null || !(tile.entity.liquids.get(outputLiquid.liquid) >= liquidCapacity);
+ return outputLiquid == null || !(tile.entity.liquids.get(outputLiquid.liquid) >= liquidCapacity - 0.001f);
}
@Override
diff --git a/core/src/mindustry/world/blocks/production/Separator.java b/core/src/mindustry/world/blocks/production/Separator.java
index 7361b8f4bb..26d0ae8f46 100644
--- a/core/src/mindustry/world/blocks/production/Separator.java
+++ b/core/src/mindustry/world/blocks/production/Separator.java
@@ -107,6 +107,7 @@ public class Separator extends Block{
entity.cons.trigger();
if(item != null && entity.items.get(item) < itemCapacity){
+ useContent(tile, item);
offloadNear(tile, item);
}
}
diff --git a/core/src/mindustry/world/blocks/production/SolidPump.java b/core/src/mindustry/world/blocks/production/SolidPump.java
index 13db9f2e2c..d6db8ea3aa 100644
--- a/core/src/mindustry/world/blocks/production/SolidPump.java
+++ b/core/src/mindustry/world/blocks/production/SolidPump.java
@@ -111,6 +111,7 @@ public class SolidPump extends Pump{
tile.entity.liquids.add(result, maxPump);
entity.lastPump = maxPump;
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f);
+ if(tile.entity.timer.get(timerContentCheck, 10)) useContent(tile, result);
if(Mathf.chance(entity.delta() * updateEffectChance))
Effects.effect(updateEffect, entity.x + Mathf.range(size * 2f), entity.y + Mathf.range(size * 2f));
}else{
diff --git a/core/src/mindustry/world/blocks/sandbox/ItemSource.java b/core/src/mindustry/world/blocks/sandbox/ItemSource.java
index 2ff49b70d8..bf7a4a5c45 100644
--- a/core/src/mindustry/world/blocks/sandbox/ItemSource.java
+++ b/core/src/mindustry/world/blocks/sandbox/ItemSource.java
@@ -81,7 +81,7 @@ public class ItemSource extends Block{
@Override
public void buildConfiguration(Tile tile, Table table){
ItemSourceEntity entity = tile.ent();
- ItemSelection.buildItemTable(table, () -> entity.outputItem, item -> {
+ ItemSelection.buildTable(table, content.items(), () -> entity.outputItem, item -> {
lastItem = item;
tile.configure(item == null ? -1 : item.id);
});
diff --git a/core/src/mindustry/world/blocks/sandbox/LiquidSource.java b/core/src/mindustry/world/blocks/sandbox/LiquidSource.java
index d1ff0be0dd..a30367fc81 100644
--- a/core/src/mindustry/world/blocks/sandbox/LiquidSource.java
+++ b/core/src/mindustry/world/blocks/sandbox/LiquidSource.java
@@ -1,24 +1,19 @@
package mindustry.world.blocks.sandbox;
import arc.*;
-import arc.struct.*;
import arc.graphics.g2d.*;
-import arc.scene.style.*;
-import arc.scene.ui.*;
import arc.scene.ui.layout.*;
-import arc.util.*;
import arc.util.ArcAnnotate.*;
+import arc.util.*;
import mindustry.entities.traits.BuilderTrait.*;
import mindustry.entities.type.*;
-import mindustry.gen.*;
import mindustry.type.*;
-import mindustry.ui.*;
-import mindustry.ui.Cicon;
import mindustry.world.*;
+import mindustry.world.blocks.*;
import java.io.*;
-import static mindustry.Vars.*;
+import static mindustry.Vars.content;
public class LiquidSource extends Block{
public static Liquid lastLiquid;
@@ -82,29 +77,10 @@ public class LiquidSource extends Block{
public void buildConfiguration(Tile tile, Table table){
LiquidSourceEntity entity = tile.ent();
- Array items = content.liquids();
-
- ButtonGroup group = new ButtonGroup<>();
- group.setMinCheckCount(0);
- Table cont = new Table();
-
- for(int i = 0; i < items.size; i++){
- final int f = i;
- ImageButton button = cont.addImageButton(Tex.clear, Styles.clearToggleTransi, 24, () -> control.input.frag.config.hideConfig()).size(38).group(group).get();
- button.changed(() -> {
- tile.configure(button.isChecked() ? items.get(f).id : -1);
- control.input.frag.config.hideConfig();
- lastLiquid = items.get(f);
- });
- button.getStyle().imageUp = new TextureRegionDrawable(items.get(i).icon(Cicon.medium));
- button.setChecked(entity.source == items.get(i));
-
- if(i % 4 == 3){
- cont.row();
- }
- }
-
- table.add(cont);
+ ItemSelection.buildTable(table, content.liquids(), () -> entity.source, liquid -> {
+ lastLiquid = liquid;
+ tile.configure(liquid == null ? -1 : liquid.id);
+ });
}
@Override
diff --git a/core/src/mindustry/world/blocks/storage/Unloader.java b/core/src/mindustry/world/blocks/storage/Unloader.java
index b63e7a04bb..bd571efe0a 100644
--- a/core/src/mindustry/world/blocks/storage/Unloader.java
+++ b/core/src/mindustry/world/blocks/storage/Unloader.java
@@ -123,7 +123,7 @@ public class Unloader extends Block{
@Override
public void buildConfiguration(Tile tile, Table table){
UnloaderEntity entity = tile.ent();
- ItemSelection.buildItemTable(table, () -> entity.sortItem, item -> {
+ ItemSelection.buildTable(table, content.items(), () -> entity.sortItem, item -> {
lastItem = item;
tile.configure(item == null ? -1 : item.id);
});
diff --git a/core/src/mindustry/world/meta/values/FloorValue.java b/core/src/mindustry/world/meta/values/FloorValue.java
index 7993a60eb0..81b612164b 100644
--- a/core/src/mindustry/world/meta/values/FloorValue.java
+++ b/core/src/mindustry/world/meta/values/FloorValue.java
@@ -18,4 +18,4 @@ public class FloorValue implements StatValue{
table.add(new Image(floor.icon(Cicon.small))).padRight(3);
table.add(floor.localizedName).padRight(3);
}
-}
\ No newline at end of file
+}
diff --git a/core/src/mindustry/world/modules/ItemModule.java b/core/src/mindustry/world/modules/ItemModule.java
index 8fc338dcbd..b08a419d0b 100644
--- a/core/src/mindustry/world/modules/ItemModule.java
+++ b/core/src/mindustry/world/modules/ItemModule.java
@@ -1,10 +1,9 @@
package mindustry.world.modules;
-import mindustry.type.Item;
-import mindustry.type.ItemStack;
+import mindustry.type.*;
import java.io.*;
-import java.util.Arrays;
+import java.util.*;
import static mindustry.Vars.content;
@@ -12,6 +11,9 @@ public class ItemModule extends BlockModule{
private int[] items = new int[content.items().size];
private int total;
+ // Make the take() loop persistent so it does not return the same item twice in a row unless there is nothing else to return.
+ protected int takeRotation;
+
public void forEach(ItemConsumer cons){
for(int i = 0; i < items.length; i++){
if(items[i] > 0){
@@ -66,17 +68,29 @@ public class ItemModule extends BlockModule{
return total;
}
- public Item take(){
+ public Item first(){
for(int i = 0; i < items.length; i++){
if(items[i] > 0){
- items[i]--;
- total--;
return content.item(i);
}
}
return null;
}
+ public Item take(){
+ for(int i = 0; i < items.length; i++){
+ int index = (i + takeRotation);
+ if(index >= items.length) index -= items.length;
+ if(items[index] > 0){
+ items[index] --;
+ total --;
+ takeRotation = index + 1;
+ return content.item(index);
+ }
+ }
+ return null;
+ }
+
public int get(Item item){
return items[item.id];
}
diff --git a/desktop/src/mindustry/desktop/DesktopLauncher.java b/desktop/src/mindustry/desktop/DesktopLauncher.java
index b09894e521..6c54691e0f 100644
--- a/desktop/src/mindustry/desktop/DesktopLauncher.java
+++ b/desktop/src/mindustry/desktop/DesktopLauncher.java
@@ -3,6 +3,7 @@ package mindustry.desktop;
import arc.*;
import arc.Files.*;
import arc.backend.sdl.*;
+import arc.backend.sdl.jni.*;
import arc.files.*;
import arc.func.*;
import arc.math.*;
@@ -11,7 +12,6 @@ import arc.util.*;
import arc.util.serialization.*;
import club.minnced.discord.rpc.*;
import com.codedisaster.steamworks.*;
-import io.anuke.arc.backends.sdl.jni.*;
import mindustry.*;
import mindustry.core.GameState.*;
import mindustry.core.*;
@@ -180,14 +180,16 @@ public class DesktopLauncher extends ClientLauncher{
static void handleCrash(Throwable e){
Cons dialog = Runnable::run;
boolean badGPU = false;
+ String total = Strings.getCauses(e).toString();
- if(e.getMessage() != null && (e.getMessage().contains("Couldn't create window") || e.getMessage().contains("OpenGL 2.0 or higher") || e.getMessage().toLowerCase().contains("pixel format"))){
+ if(total.contains("Couldn't create window") ||
+ total.contains("OpenGL 2.0 or higher") || total.toLowerCase().contains("pixel format") || total.contains("GLEW")){
dialog.get(() -> message(
e.getMessage().contains("Couldn't create window") ? "A graphics initialization error has occured! Try to update your graphics drivers:\n" + e.getMessage() :
- "Your graphics card does not support OpenGL 2.0!\n" +
- "Try to update your graphics drivers.\n\n" +
- "(If that doesn't work, your computer just doesn't support Mindustry.)"));
+ "Your graphics card does not support OpenGL 2.0 with the framebuffer_object extension!\n" +
+ "Try to update your graphics drivers. If this doesn't work, your computer may not support Mindustry.\n\n" +
+ "Full message: " + e.getMessage()));
badGPU = true;
}
@@ -243,36 +245,65 @@ public class DesktopLauncher extends ClientLauncher{
@Override
public void updateRPC(){
- if(!useDiscord) return;
+ //if we're using neither discord nor steam, do no work
+ if(!useDiscord && !steam) return;
- DiscordRichPresence presence = new DiscordRichPresence();
+ //common elements they each share
+ boolean inGame = !state.is(State.menu);
+ String gameMapWithWave = "Unknown Map";
+ String gameMode = "";
+ String gamePlayersSuffix = "";
+ String uiState = "";
- if(!state.is(State.menu)){
- String map = world.getMap() == null ? "Unknown Map" : world.isZone() ? world.getZone().localizedName : Strings.capitalize(world.getMap().name());
- String mode = state.rules.pvp ? "PvP" : state.rules.attackMode ? "Attack" : "Survival";
- String players = net.active() && playerGroup.size() > 1 ? " | " + playerGroup.size() + " Players" : "";
-
- presence.state = mode + players;
-
- if(!state.rules.waves){
- presence.details = map;
- }else{
- presence.details = map + " | Wave " + state.wave;
- presence.largeImageText = "Wave " + state.wave;
+ if(inGame){
+ if(world.getMap() != null){
+ gameMapWithWave = world.isZone() ? world.getZone().localizedName : Strings.capitalize(world.getMap().name());
+ }
+ if(state.rules.waves){
+ gameMapWithWave += " | Wave " + state.wave;
+ }
+ gameMode = state.rules.pvp ? "PvP" : state.rules.attackMode ? "Attack" : "Survival";
+ if(net.active() && playerGroup.size() > 1){
+ gamePlayersSuffix = " | " + playerGroup.size() + " Players";
}
}else{
if(ui.editor != null && ui.editor.isShown()){
- presence.state = "In Editor";
+ uiState = "In Editor";
}else if(ui.deploy != null && ui.deploy.isShown()){
- presence.state = "In Launch Selection";
+ uiState = "In Launch Selection";
}else{
- presence.state = "In Menu";
+ uiState = "In Menu";
}
}
- presence.largeImageKey = "logo";
+ if(useDiscord){
+ DiscordRichPresence presence = new DiscordRichPresence();
- DiscordRPC.INSTANCE.Discord_UpdatePresence(presence);
+ if(inGame){
+ presence.state = gameMode + gamePlayersSuffix;
+ presence.details = gameMapWithWave;
+ if(state.rules.waves){
+ presence.largeImageText = "Wave " + state.wave;
+ }
+ }else{
+ presence.state = uiState;
+ }
+
+ presence.largeImageKey = "logo";
+
+ DiscordRPC.INSTANCE.Discord_UpdatePresence(presence);
+ }
+
+ if(steam){
+ //Steam mostly just expects us to give it a nice string, but it apparently expects "steam_display" to always be a loc token, so I've uploaded this one which just passes through 'steam_status' raw.
+ SVars.net.friends.setRichPresence("steam_display", "#steam_status_raw");
+
+ if(inGame){
+ SVars.net.friends.setRichPresence("steam_status", gameMapWithWave);
+ }else{
+ SVars.net.friends.setRichPresence("steam_status", uiState);
+ }
+ }
}
@Override
@@ -290,7 +321,7 @@ public class DesktopLauncher extends ClientLauncher{
try{
Enumeration e = NetworkInterface.getNetworkInterfaces();
NetworkInterface out;
- for(out = e.nextElement(); (out.getHardwareAddress() == null || !validAddress(out.getHardwareAddress())) && e.hasMoreElements(); out = e.nextElement());
+ for(out = e.nextElement(); (out.getHardwareAddress() == null || out.isVirtual() || !validAddress(out.getHardwareAddress())) && e.hasMoreElements(); out = e.nextElement());
byte[] bytes = out.getHardwareAddress();
byte[] result = new byte[8];
diff --git a/desktop/src/mindustry/desktop/steam/SNet.java b/desktop/src/mindustry/desktop/steam/SNet.java
index 218b02ca42..3e2ec342e7 100644
--- a/desktop/src/mindustry/desktop/steam/SNet.java
+++ b/desktop/src/mindustry/desktop/steam/SNet.java
@@ -1,16 +1,16 @@
package mindustry.desktop.steam;
import arc.*;
+import arc.func.*;
+import arc.struct.*;
+import arc.util.*;
+import arc.util.pooling.*;
import com.codedisaster.steamworks.*;
import com.codedisaster.steamworks.SteamFriends.*;
import com.codedisaster.steamworks.SteamMatchmaking.*;
import com.codedisaster.steamworks.SteamNetworking.*;
-import arc.struct.*;
-import arc.func.*;
-import arc.util.*;
-import arc.util.pooling.*;
import mindustry.core.GameState.*;
-import mindustry.core.Version;
+import mindustry.core.*;
import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.net.ArcNetProvider.*;
@@ -240,12 +240,9 @@ public class SNet implements SteamNetworkingCallback, SteamMatchmakingCallback,
return;
}
- if(net.active()){
- net.disconnect();
- net.closeServer();
- logic.reset();
- state.set(State.menu);
- }
+ logic.reset();
+ net.reset();
+ state.set(State.menu);
currentLobby = steamIDLobby;
currentServer = smat.getLobbyOwner(steamIDLobby);
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/103.1.txt b/fastlane/metadata/android/cs-CZ/changelogs/103.1.txt
new file mode 100644
index 0000000000..ea267c1c96
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/103.1.txt
@@ -0,0 +1,9 @@
+- Přidány nové ikony s hladkým škálováním
+- Přidána černá díra na kapaliny (přispěno uživatelem @GioIacca9)
+- Přidáno nastavení průsvitnosti přemostění přepravníku (přispěno uživatelem @Quezler)
+- Přidána brána s podtokem (protikus k bráně s přetečením)
+- Přidány emotikony do kanálu zpráv, pro většinu bloků a předmětů
+- Přidán nový strom technologií, s lepší podporou pro modifikace
+- Přidán soubor s logem hry, uložený v adresáři s daty
+- Přidán nový oddělovač pro sprity a animace (ocení modéři)
+- Přidán seznam synergetických dlaždic do statistik některých bloků (viz například vrt na vodu)
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/103.2.txt b/fastlane/metadata/android/cs-CZ/changelogs/103.2.txt
new file mode 100644
index 0000000000..ea267c1c96
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/103.2.txt
@@ -0,0 +1,9 @@
+- Přidány nové ikony s hladkým škálováním
+- Přidána černá díra na kapaliny (přispěno uživatelem @GioIacca9)
+- Přidáno nastavení průsvitnosti přemostění přepravníku (přispěno uživatelem @Quezler)
+- Přidána brána s podtokem (protikus k bráně s přetečením)
+- Přidány emotikony do kanálu zpráv, pro většinu bloků a předmětů
+- Přidán nový strom technologií, s lepší podporou pro modifikace
+- Přidán soubor s logem hry, uložený v adresáři s daty
+- Přidán nový oddělovač pro sprity a animace (ocení modéři)
+- Přidán seznam synergetických dlaždic do statistik některých bloků (viz například vrt na vodu)
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/103.3.txt b/fastlane/metadata/android/cs-CZ/changelogs/103.3.txt
new file mode 100644
index 0000000000..ea267c1c96
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/103.3.txt
@@ -0,0 +1,9 @@
+- Přidány nové ikony s hladkým škálováním
+- Přidána černá díra na kapaliny (přispěno uživatelem @GioIacca9)
+- Přidáno nastavení průsvitnosti přemostění přepravníku (přispěno uživatelem @Quezler)
+- Přidána brána s podtokem (protikus k bráně s přetečením)
+- Přidány emotikony do kanálu zpráv, pro většinu bloků a předmětů
+- Přidán nový strom technologií, s lepší podporou pro modifikace
+- Přidán soubor s logem hry, uložený v adresáři s daty
+- Přidán nový oddělovač pro sprity a animace (ocení modéři)
+- Přidán seznam synergetických dlaždic do statistik některých bloků (viz například vrt na vodu)
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/103.txt b/fastlane/metadata/android/cs-CZ/changelogs/103.txt
new file mode 100644
index 0000000000..ea267c1c96
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/103.txt
@@ -0,0 +1,9 @@
+- Přidány nové ikony s hladkým škálováním
+- Přidána černá díra na kapaliny (přispěno uživatelem @GioIacca9)
+- Přidáno nastavení průsvitnosti přemostění přepravníku (přispěno uživatelem @Quezler)
+- Přidána brána s podtokem (protikus k bráně s přetečením)
+- Přidány emotikony do kanálu zpráv, pro většinu bloků a předmětů
+- Přidán nový strom technologií, s lepší podporou pro modifikace
+- Přidán soubor s logem hry, uložený v adresáři s daty
+- Přidán nový oddělovač pro sprity a animace (ocení modéři)
+- Přidán seznam synergetických dlaždic do statistik některých bloků (viz například vrt na vodu)
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/29591.txt b/fastlane/metadata/android/cs-CZ/changelogs/29591.txt
new file mode 100644
index 0000000000..ea267c1c96
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/29591.txt
@@ -0,0 +1,9 @@
+- Přidány nové ikony s hladkým škálováním
+- Přidána černá díra na kapaliny (přispěno uživatelem @GioIacca9)
+- Přidáno nastavení průsvitnosti přemostění přepravníku (přispěno uživatelem @Quezler)
+- Přidána brána s podtokem (protikus k bráně s přetečením)
+- Přidány emotikony do kanálu zpráv, pro většinu bloků a předmětů
+- Přidán nový strom technologií, s lepší podporou pro modifikace
+- Přidán soubor s logem hry, uložený v adresáři s daty
+- Přidán nový oddělovač pro sprity a animace (ocení modéři)
+- Přidán seznam synergetických dlaždic do statistik některých bloků (viz například vrt na vodu)
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/29594.txt b/fastlane/metadata/android/cs-CZ/changelogs/29594.txt
new file mode 100644
index 0000000000..ea267c1c96
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/29594.txt
@@ -0,0 +1,9 @@
+- Přidány nové ikony s hladkým škálováním
+- Přidána černá díra na kapaliny (přispěno uživatelem @GioIacca9)
+- Přidáno nastavení průsvitnosti přemostění přepravníku (přispěno uživatelem @Quezler)
+- Přidána brána s podtokem (protikus k bráně s přetečením)
+- Přidány emotikony do kanálu zpráv, pro většinu bloků a předmětů
+- Přidán nový strom technologií, s lepší podporou pro modifikace
+- Přidán soubor s logem hry, uložený v adresáři s daty
+- Přidán nový oddělovač pro sprity a animace (ocení modéři)
+- Přidán seznam synergetických dlaždic do statistik některých bloků (viz například vrt na vodu)
diff --git a/fastlane/metadata/android/cs-CZ/changelogs/29597.txt b/fastlane/metadata/android/cs-CZ/changelogs/29597.txt
new file mode 100644
index 0000000000..ea267c1c96
--- /dev/null
+++ b/fastlane/metadata/android/cs-CZ/changelogs/29597.txt
@@ -0,0 +1,9 @@
+- Přidány nové ikony s hladkým škálováním
+- Přidána černá díra na kapaliny (přispěno uživatelem @GioIacca9)
+- Přidáno nastavení průsvitnosti přemostění přepravníku (přispěno uživatelem @Quezler)
+- Přidána brána s podtokem (protikus k bráně s přetečením)
+- Přidány emotikony do kanálu zpráv, pro většinu bloků a předmětů
+- Přidán nový strom technologií, s lepší podporou pro modifikace
+- Přidán soubor s logem hry, uložený v adresáři s daty
+- Přidán nový oddělovač pro sprity a animace (ocení modéři)
+- Přidán seznam synergetických dlaždic do statistik některých bloků (viz například vrt na vodu)
diff --git a/fastlane/metadata/android/cs-CZ/title.txt b/fastlane/metadata/android/cs-CZ/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/cs-CZ/title.txt
+++ b/fastlane/metadata/android/cs-CZ/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/en-US/changelogs/103.3.txt b/fastlane/metadata/android/en-US/changelogs/103.3.txt
new file mode 100644
index 0000000000..fa41c882ac
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/103.3.txt
@@ -0,0 +1,9 @@
+- Added new icons w/ smooth scaling
+- Added liquid void (Contributed by @GioIacca9)
+- Added bridge opacity slider (Contributed by @Quezler)
+- Added "underflow" gate (opposite of overflow gate)
+- Added "emojis" for most blocks and items into the font
+- Added new tech tree layout w/ better mod support
+- Added game log file, stored in data folder
+- Added new separator sprite/animation
+- Added list of affinity tiles to stats of certain blocks
diff --git a/fastlane/metadata/android/en-US/changelogs/104.1.txt b/fastlane/metadata/android/en-US/changelogs/104.1.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/104.1.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/104.2.txt b/fastlane/metadata/android/en-US/changelogs/104.2.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/104.2.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/104.3.txt b/fastlane/metadata/android/en-US/changelogs/104.3.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/104.3.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/104.4.txt b/fastlane/metadata/android/en-US/changelogs/104.4.txt
new file mode 100644
index 0000000000..4790ed135d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/104.4.txt
@@ -0,0 +1,4 @@
+- Updated feature suggestion link
+- Updated translations
+- Fixed modified player name persisting after server is exited
+- Added solar power multiplier rule
diff --git a/fastlane/metadata/android/en-US/changelogs/104.txt b/fastlane/metadata/android/en-US/changelogs/104.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/104.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/12203.txt b/fastlane/metadata/android/en-US/changelogs/12203.txt
index 942424c5ea..8cefa14d20 100644
--- a/fastlane/metadata/android/en-US/changelogs/12203.txt
+++ b/fastlane/metadata/android/en-US/changelogs/12203.txt
@@ -1,3 +1,3 @@
- Fixed incorrect attribution in credits; music was made by "A Drop A Day"
- Removed unneeded 3.5 upgrade dialog that sometimes showed up in main menu
-- Added information dialog regarding v4 beta
\ No newline at end of file
+- Added information dialog regarding v4 beta
diff --git a/fastlane/metadata/android/en-US/changelogs/12205.txt b/fastlane/metadata/android/en-US/changelogs/12205.txt
index 70bcab4bfc..d2fbea0415 100644
--- a/fastlane/metadata/android/en-US/changelogs/12205.txt
+++ b/fastlane/metadata/android/en-US/changelogs/12205.txt
@@ -1,4 +1,4 @@
- Fixed incorrect attribution in credits; music was made by "A Drop A Day"
- Removed unneeded 3.5 upgrade dialog that sometimes showed up in main menu
- Added information dialog regarding v4 beta
-- Fixed Discord link
\ No newline at end of file
+- Fixed Discord link
diff --git a/fastlane/metadata/android/en-US/changelogs/26593.txt b/fastlane/metadata/android/en-US/changelogs/26593.txt
index 610563a652..7e281e161b 100644
--- a/fastlane/metadata/android/en-US/changelogs/26593.txt
+++ b/fastlane/metadata/android/en-US/changelogs/26593.txt
@@ -1 +1 @@
-sound.
\ No newline at end of file
+sound.
diff --git a/fastlane/metadata/android/en-US/changelogs/27434.txt b/fastlane/metadata/android/en-US/changelogs/27434.txt
index 5240303123..0228878dee 100644
--- a/fastlane/metadata/android/en-US/changelogs/27434.txt
+++ b/fastlane/metadata/android/en-US/changelogs/27434.txt
@@ -1 +1 @@
-everything
\ No newline at end of file
+everything
diff --git a/fastlane/metadata/android/en-US/changelogs/27476.txt b/fastlane/metadata/android/en-US/changelogs/27476.txt
index ec779fbdba..f7a9042cc4 100644
--- a/fastlane/metadata/android/en-US/changelogs/27476.txt
+++ b/fastlane/metadata/android/en-US/changelogs/27476.txt
@@ -1 +1 @@
-Fixed some minor sound/multiplayer issues
\ No newline at end of file
+Fixed some minor sound/multiplayer issues
diff --git a/fastlane/metadata/android/en-US/changelogs/27689.txt b/fastlane/metadata/android/en-US/changelogs/27689.txt
index 92e8a8c47e..f849f7a245 100644
--- a/fastlane/metadata/android/en-US/changelogs/27689.txt
+++ b/fastlane/metadata/android/en-US/changelogs/27689.txt
@@ -1 +1 @@
-Bugfixes
\ No newline at end of file
+Bugfixes
diff --git a/fastlane/metadata/android/en-US/changelogs/27770.txt b/fastlane/metadata/android/en-US/changelogs/27770.txt
index 04d19b551f..06f22342a3 100644
--- a/fastlane/metadata/android/en-US/changelogs/27770.txt
+++ b/fastlane/metadata/android/en-US/changelogs/27770.txt
@@ -1 +1 @@
-Added ambient sounds for machines.
\ No newline at end of file
+Added ambient sounds for machines.
diff --git a/fastlane/metadata/android/en-US/changelogs/27997.txt b/fastlane/metadata/android/en-US/changelogs/27997.txt
index 2b2809c72d..9b26aa3618 100644
--- a/fastlane/metadata/android/en-US/changelogs/27997.txt
+++ b/fastlane/metadata/android/en-US/changelogs/27997.txt
@@ -1,2 +1,2 @@
The first release of version 4.0; an update that has been in the works for over a year.
-Extensive changes, including new gamemodes, customizable rules, a new editor, new graphics, new enemies, unit production, new progression, a campaign, and more. See the in-game changelog link for specific details of what has been added and removed over the past year.
\ No newline at end of file
+Extensive changes, including new gamemodes, customizable rules, a new editor, new graphics, new enemies, unit production, new progression, a campaign, and more. See the in-game changelog link for specific details of what has been added and removed over the past year.
diff --git a/fastlane/metadata/android/en-US/changelogs/28409.txt b/fastlane/metadata/android/en-US/changelogs/28409.txt
index 670c65cc45..72fac509ef 100644
--- a/fastlane/metadata/android/en-US/changelogs/28409.txt
+++ b/fastlane/metadata/android/en-US/changelogs/28409.txt
@@ -1,2 +1,2 @@
Fixed multiplayer not syncing positions and shots correctly on certain networks or situations.
-Various other bugfixes.
\ No newline at end of file
+Various other bugfixes.
diff --git a/fastlane/metadata/android/en-US/changelogs/29597.txt b/fastlane/metadata/android/en-US/changelogs/29597.txt
new file mode 100644
index 0000000000..fa41c882ac
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/29597.txt
@@ -0,0 +1,9 @@
+- Added new icons w/ smooth scaling
+- Added liquid void (Contributed by @GioIacca9)
+- Added bridge opacity slider (Contributed by @Quezler)
+- Added "underflow" gate (opposite of overflow gate)
+- Added "emojis" for most blocks and items into the font
+- Added new tech tree layout w/ better mod support
+- Added game log file, stored in data folder
+- Added new separator sprite/animation
+- Added list of affinity tiles to stats of certain blocks
diff --git a/fastlane/metadata/android/en-US/changelogs/29603.txt b/fastlane/metadata/android/en-US/changelogs/29603.txt
new file mode 100644
index 0000000000..a268ddebde
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/29603.txt
@@ -0,0 +1,9 @@
+- Fixed incorrect line numbers in script errors
+- Fixed black screen caused by bloom on some devices
+- Fixed some music issues with sound playing at 0% [Android]
+- Fixed client IDs resetting
+- Fixed data resetting between specific versions [Android]
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Optimized and cleaned up conveyors
+- Tweaked power block palette
diff --git a/fastlane/metadata/android/en-US/changelogs/29607.txt b/fastlane/metadata/android/en-US/changelogs/29607.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/29607.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/29609.txt b/fastlane/metadata/android/en-US/changelogs/29609.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/29609.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/29612.txt b/fastlane/metadata/android/en-US/changelogs/29612.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/29612.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/29616.txt b/fastlane/metadata/android/en-US/changelogs/29616.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/29616.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/29618.txt b/fastlane/metadata/android/en-US/changelogs/29618.txt
new file mode 100644
index 0000000000..6f3e27fb4d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/29618.txt
@@ -0,0 +1,6 @@
+- Optimized and cleaned up conveyors
+- Made unloaders take items from blocks in equal amounts
+- Added server categories (Partially contributed by @Quezler)
+- Added require() function for scripts (Contributed by @DeltaNedas)
+- Added color gradient for battery status (Contributed by @Arkanic (eventually))
+- Changed script loading: For mods with more than one script file, the main file must be named "main.js" and must require() other files to run them
diff --git a/fastlane/metadata/android/en-US/changelogs/29620.txt b/fastlane/metadata/android/en-US/changelogs/29620.txt
new file mode 100644
index 0000000000..4790ed135d
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/29620.txt
@@ -0,0 +1,4 @@
+- Updated feature suggestion link
+- Updated translations
+- Fixed modified player name persisting after server is exited
+- Added solar power multiplier rule
diff --git a/fastlane/metadata/android/en-US/changelogs/721.txt b/fastlane/metadata/android/en-US/changelogs/721.txt
index 8361896cef..4e72aaf3d6 100644
--- a/fastlane/metadata/android/en-US/changelogs/721.txt
+++ b/fastlane/metadata/android/en-US/changelogs/721.txt
@@ -2,4 +2,4 @@
- Fixed wave timer being extremely slow at high TPS with multithreading enabled
- Fixed scrolling on player menu causing the whole screen to pan
- Updated Polish translation
-- Updated Russian translation with new text (Thanks to @Prosta4okua)
\ No newline at end of file
+- Updated Russian translation with new text (Thanks to @Prosta4okua)
diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt
index b9958053f1..d95c57b64d 100644
--- a/fastlane/metadata/android/en-US/full_description.txt
+++ b/fastlane/metadata/android/en-US/full_description.txt
@@ -11,4 +11,4 @@ Feature include:
- Cross-platform multiplayer via local networks or dedicated servers
- Custom game rules: Change block costs, enemy stats, starting items, wave timing and more
- A powerful editor, with tools to randomly generate ores, terrain, decoration and apply symmetry to maps
-- Customizable map wave layouts
\ No newline at end of file
+- Customizable map wave layouts
diff --git a/fastlane/metadata/android/en-US/short_description.txt b/fastlane/metadata/android/en-US/short_description.txt
index b6b5a942cd..ac569c2687 100644
--- a/fastlane/metadata/android/en-US/short_description.txt
+++ b/fastlane/metadata/android/en-US/short_description.txt
@@ -1 +1 @@
-A factory-based sandbox tower defense game.
\ No newline at end of file
+A factory-based sandbox tower defense game.
diff --git a/fastlane/metadata/android/en-US/summary.txt b/fastlane/metadata/android/en-US/summary.txt
index b6b5a942cd..ac569c2687 100644
--- a/fastlane/metadata/android/en-US/summary.txt
+++ b/fastlane/metadata/android/en-US/summary.txt
@@ -1 +1 @@
-A factory-based sandbox tower defense game.
\ No newline at end of file
+A factory-based sandbox tower defense game.
diff --git a/fastlane/metadata/android/en-US/title.txt b/fastlane/metadata/android/en-US/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/en-US/title.txt
+++ b/fastlane/metadata/android/en-US/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/fr-FR/title.txt b/fastlane/metadata/android/fr-FR/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/fr-FR/title.txt
+++ b/fastlane/metadata/android/fr-FR/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/fr-FR/video.txt b/fastlane/metadata/android/fr-FR/video.txt
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/fastlane/metadata/android/it-IT/changelogs/102.2.txt b/fastlane/metadata/android/it-IT/changelogs/102.2.txt
index db62ea93f9..718fa43d87 100644
--- a/fastlane/metadata/android/it-IT/changelogs/102.2.txt
+++ b/fastlane/metadata/android/it-IT/changelogs/102.2.txt
@@ -2,4 +2,4 @@
- Aggiunta regola della salute dei blocchi
- Aggiunte ulteriori squadre interne per modalità di gioco alternative
- Aggiunte funzionalità per migliorare il server modding
-- Cambiamenti interni: il package ora è "mindustry" invece di "io.anuke.mindustry" (i plugin non funzioneranno)
\ No newline at end of file
+- Cambiamenti interni: il package ora è "mindustry" invece di "io.anuke.mindustry" (i plugin non funzioneranno)
diff --git a/fastlane/metadata/android/it-IT/full_description.txt b/fastlane/metadata/android/it-IT/full_description.txt
index 1cf2ed8e9c..071cef4ed0 100644
--- a/fastlane/metadata/android/it-IT/full_description.txt
+++ b/fastlane/metadata/android/it-IT/full_description.txt
@@ -11,4 +11,4 @@ Funzionalità:
- Modalità multigiocatore cross-platform attraverso la rete locale o i server dedicati
- Regole di gioco personalizzate: modifica i costi dei blocchi, le statistiche nemiche, gli oggetti di partenza, i tempi tra le ondate e molto altro
- Un editor potente, con strumenti che permettono di generare i minerali in modo casuale, il terreno, le decorazioni e applicare simmetria alle mappe
-- Ondate delle mappe completamente personalizzabili
\ No newline at end of file
+- Ondate delle mappe completamente personalizzabili
diff --git a/fastlane/metadata/android/it-IT/short_description.txt b/fastlane/metadata/android/it-IT/short_description.txt
index 92c1aa2e38..335469c50a 100644
--- a/fastlane/metadata/android/it-IT/short_description.txt
+++ b/fastlane/metadata/android/it-IT/short_description.txt
@@ -1 +1 @@
-Un gioco sandbox basato sulla produzione e sulla difesa.
\ No newline at end of file
+Un gioco sandbox basato sulla produzione e sulla difesa.
diff --git a/fastlane/metadata/android/it-IT/summary.txt b/fastlane/metadata/android/it-IT/summary.txt
index 92c1aa2e38..335469c50a 100644
--- a/fastlane/metadata/android/it-IT/summary.txt
+++ b/fastlane/metadata/android/it-IT/summary.txt
@@ -1 +1 @@
-Un gioco sandbox basato sulla produzione e sulla difesa.
\ No newline at end of file
+Un gioco sandbox basato sulla produzione e sulla difesa.
diff --git a/fastlane/metadata/android/it-IT/title.txt b/fastlane/metadata/android/it-IT/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/it-IT/title.txt
+++ b/fastlane/metadata/android/it-IT/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/it-IT/video.txt b/fastlane/metadata/android/it-IT/video.txt
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/fastlane/metadata/android/ja-JP/full_description.txt b/fastlane/metadata/android/ja-JP/full_description.txt
index d76eb7a53c..8b0405139b 100644
--- a/fastlane/metadata/android/ja-JP/full_description.txt
+++ b/fastlane/metadata/android/ja-JP/full_description.txt
@@ -11,4 +11,4 @@ Mindustryの特徴:
- ローカルネットワークや専用サーバーで、クロスプラットフォームのマルチプレイが可能
- カスタマイズ可能なゲーム: ブロックのコスト変更、敵のステータス、初期アイテム、ウェーブのタイミング など…
- 強力なエディター、鉱石をランダムに生成したり、地形、デコレーション、左右対称なマップも制作可能
-- マップごとにウェーブの構成もカスタマイズ可能
\ No newline at end of file
+- マップごとにウェーブの構成もカスタマイズ可能
diff --git a/fastlane/metadata/android/ja-JP/short_description.txt b/fastlane/metadata/android/ja-JP/short_description.txt
index a7430009eb..093c9d2745 100644
--- a/fastlane/metadata/android/ja-JP/short_description.txt
+++ b/fastlane/metadata/android/ja-JP/short_description.txt
@@ -1 +1 @@
-工場ベースのサンドボックスタワーディフェンスゲーム。
\ No newline at end of file
+工場ベースのサンドボックスタワーディフェンスゲーム。
diff --git a/fastlane/metadata/android/ja-JP/title.txt b/fastlane/metadata/android/ja-JP/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/ja-JP/title.txt
+++ b/fastlane/metadata/android/ja-JP/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/pl-PL/short_description.txt b/fastlane/metadata/android/pl-PL/short_description.txt
index 6628c453f0..520c848bcf 100644
--- a/fastlane/metadata/android/pl-PL/short_description.txt
+++ b/fastlane/metadata/android/pl-PL/short_description.txt
@@ -1 +1 @@
-Bazująca na przetrwaniu i zarządzaniu fabryką sandboxowa gra typu obrony wieży.
\ No newline at end of file
+Bazująca na przetrwaniu i zarządzaniu fabryką sandboxowa gra typu obrony wieży.
diff --git a/fastlane/metadata/android/pl-PL/summary.txt b/fastlane/metadata/android/pl-PL/summary.txt
index 6628c453f0..520c848bcf 100644
--- a/fastlane/metadata/android/pl-PL/summary.txt
+++ b/fastlane/metadata/android/pl-PL/summary.txt
@@ -1 +1 @@
-Bazująca na przetrwaniu i zarządzaniu fabryką sandboxowa gra typu obrony wieży.
\ No newline at end of file
+Bazująca na przetrwaniu i zarządzaniu fabryką sandboxowa gra typu obrony wieży.
diff --git a/fastlane/metadata/android/pl-PL/title.txt b/fastlane/metadata/android/pl-PL/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/pl-PL/title.txt
+++ b/fastlane/metadata/android/pl-PL/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/pt-PT/changelogs/102.txt b/fastlane/metadata/android/pt-PT/changelogs/102.txt
index 784f53ea20..f1fb5da19e 100644
--- a/fastlane/metadata/android/pt-PT/changelogs/102.txt
+++ b/fastlane/metadata/android/pt-PT/changelogs/102.txt
@@ -2,4 +2,4 @@
- Foi adicionada regra de integridade do bloco
- Adicionado mais equipes internas para modos de jogo alternativos
- Adicionado recursos para melhorar a modificação do servidor
--Grande mudança interna: o pacote agora é "mindustry" em vez de "io.anuke.mindustry" (irá quebrar os plugins)
\ No newline at end of file
+-Grande mudança interna: o pacote agora é "mindustry" em vez de "io.anuke.mindustry" (irá quebrar os plugins)
diff --git a/fastlane/metadata/android/pt-PT/full_description.txt b/fastlane/metadata/android/pt-PT/full_description.txt
index 07cf8233b3..78d122abe5 100644
--- a/fastlane/metadata/android/pt-PT/full_description.txt
+++ b/fastlane/metadata/android/pt-PT/full_description.txt
@@ -11,4 +11,4 @@ Recursos incluidos:
- Multijogador multiplataforma através de redes locais ou servidores dedicados
- Regras personalizadas do jogo: altere os custos de bloqueio, estatísticas do inimigo, itens iniciais, tempo das ondas e muito mais
- Um editor poderoso, com ferramentas para gerar aleatoriamente minérios, terrenos, decoração e aplicar simetria a mapas
-- Layouts de rondas de mapa personalizáveis
\ No newline at end of file
+- Layouts de rondas de mapa personalizáveis
diff --git a/fastlane/metadata/android/pt-PT/summary.txt b/fastlane/metadata/android/pt-PT/summary.txt
index 24b4edd83d..6ecbaf697a 100644
--- a/fastlane/metadata/android/pt-PT/summary.txt
+++ b/fastlane/metadata/android/pt-PT/summary.txt
@@ -1 +1 @@
-Um jogo de defesa de torre de sandbox baseado em construção.
\ No newline at end of file
+Um jogo de defesa de torre de sandbox baseado em construção.
diff --git a/fastlane/metadata/android/pt-PT/title.txt b/fastlane/metadata/android/pt-PT/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/pt-PT/title.txt
+++ b/fastlane/metadata/android/pt-PT/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/ru-RU/changelogs/104.4.txt b/fastlane/metadata/android/ru-RU/changelogs/104.4.txt
new file mode 100644
index 0000000000..49c499f087
--- /dev/null
+++ b/fastlane/metadata/android/ru-RU/changelogs/104.4.txt
@@ -0,0 +1,4 @@
+- Обновлена ссылка для предложений новых возможностей
+- Обновлены переводы
+- Исправлена ошибка, из-за которой имя игрока, изменённое сервером, оставалось после выхода
+- Добавлено правило множителя солнечной энергии
diff --git a/fastlane/metadata/android/ru-RU/full_description.txt b/fastlane/metadata/android/ru-RU/full_description.txt
index 03219413cc..c69aa89b6f 100644
--- a/fastlane/metadata/android/ru-RU/full_description.txt
+++ b/fastlane/metadata/android/ru-RU/full_description.txt
@@ -1,15 +1,14 @@
-Создавайте сложные логистические сети для переноса боеприпасов для турелей, добывайте ресурсы для строительства, и защищайте их от различных волн врагов.
-Играйте с друзьями в кроссплатформенные кооперативные игры, или бросьте им вызов в командных PvP-матчах.
+Создавайте сложные логистические сети для переноса боеприпасов для турелей, добывайте ресурсы для строительства и защищайте их от различных волн врагов. Играйте с друзьями в кроссплатформенные многопользовательские кооперативные игры или бросьте им вызов в командных PvP-матчах.
Особенности игры:
- 24 встроенные карты
-- Кампания, с полноценным технологическим древом и прогрессией карт
+- Кампания с полноценным технологическим древом и прогрессией карт
- 4 вида мощных боссов
- Системы для транспорта электроэнергии, жидкостей и ресурсов
-- 19 видов дронов, кораблей и наземных боевых единиц
+- 19 видов дронов, мехов и кораблей
- Более 120 блоков в техногическом древе
- Более 75 видов натуральных блоков
-- Кроссплатформенный мультиплеер с поддержкой как и локальных сетей, так и серверов
+- Кроссплатформенная многопользовательская игра с поддержкой как и локальных сетей, так и серверов
- Пользовательские настройки игры — изменяйте цену блоков, силу врагов, количество стартовых ресурсов, интервал между волнами и т.д.
-- Редактор карт с бесчисленными возможностями, инструментами для случайной генерации руд, рельефа, декораций, а также для симметрии карт
-- Настраиваемые раскладки волн для карт
\ No newline at end of file
+- Мощный редактор карт с инструментами для случайной генерации руд, рельефа, декораций, а также для симметрии карт
+- Настраиваемые раскладки волн для карт
diff --git a/fastlane/metadata/android/ru-RU/title.txt b/fastlane/metadata/android/ru-RU/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/ru-RU/title.txt
+++ b/fastlane/metadata/android/ru-RU/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/uk/changelogs/104.4 b/fastlane/metadata/android/uk/changelogs/104.4
new file mode 100644
index 0000000000..9a5813fab6
--- /dev/null
+++ b/fastlane/metadata/android/uk/changelogs/104.4
@@ -0,0 +1,4 @@
+- Оновлено посилання, яке веде на сторінку, де можна запропонувати нову можливість
+- Оновлено переклади
+- Виправлено помилка, через яку ім'я гравця, змінене серером, залишається
+- Додано користувацьке правило — множник сонячної енергії
diff --git a/fastlane/metadata/android/uk/full_description.txt b/fastlane/metadata/android/uk/full_description.txt
index d1f27119ef..6d0515099a 100644
--- a/fastlane/metadata/android/uk/full_description.txt
+++ b/fastlane/metadata/android/uk/full_description.txt
@@ -1,5 +1,4 @@
-Створюйте складні логістичні мережі для перенесення боєприпасів у башточки, видобувайте ресурси для будівництва, і захищайте своє ядро від різних хвиль ворогів.
-Грайте з друзями на різних платформах у кооперативні ігри, або киньте їм виклик в командних PvP-матчах.
+Створюйте складні системи логістики для перенесення боєприпасів у башточки, видобувайте ресурси для будівництва, і захищайте своє ядро від різних хвиль ворогів. Грайте з друзями на різних платформах у кооперативні ігри, або киньте їм виклик в командних PvP-матчах.
Особливості гри:
- 24 вбудовані мапи
@@ -9,7 +8,7 @@
- 19 видів дронів, кораблів та наземних бойових одиниць
- Понад 120 блоків у дереві технологій
- Понад 75 видів блоків навколишнього середовища
-- Багатоплатформовий мультиплеєр з підтримкою як і локальних мереж, так і серверів
+- Багатоплатформова багатокористувацька гра з підтримкою як і локальних мереж, так і серверів
- Користувацькі налаштування гри — ціну блоків, сила ворогів, кількість початкових ресурсів, інтервал між хвилями, тощо
-- Редактор мап з незкінченними можливостями, інструментами для випадкової генерації руд, рельєфу, декорацій, а також для симетрії мап
+- Редактор мап з нескінченними можливостями, інструментами для випадкової генерації руд, рельєфу, декорацій, а також для симетрії мап
- Розкладки хвиль для мап можна налаштувати
diff --git a/fastlane/metadata/android/uk/title.txt b/fastlane/metadata/android/uk/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/uk/title.txt
+++ b/fastlane/metadata/android/uk/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/android/zh-TW/title.txt b/fastlane/metadata/android/zh-TW/title.txt
index 2beb939017..e2842b447a 100644
--- a/fastlane/metadata/android/zh-TW/title.txt
+++ b/fastlane/metadata/android/zh-TW/title.txt
@@ -1 +1 @@
-Mindustry
\ No newline at end of file
+Mindustry
diff --git a/fastlane/metadata/steam/russian/short-description.txt b/fastlane/metadata/steam/russian/short-description.txt
index 4bd0ace80c..995a8f2cf0 100644
--- a/fastlane/metadata/steam/russian/short-description.txt
+++ b/fastlane/metadata/steam/russian/short-description.txt
@@ -1 +1 @@
-Безграничная игра в жанре башенная защита с упором на управлении ресурсами.
+Безграничная игра в жанре «башенная защита» с упором на управление ресурсами.
diff --git a/fastlane/metadata/steam/ukrainian/achievements.vdf b/fastlane/metadata/steam/ukrainian/achievements.vdf
index 65b55d7f73..6049e105ff 100644
--- a/fastlane/metadata/steam/ukrainian/achievements.vdf
+++ b/fastlane/metadata/steam/ukrainian/achievements.vdf
@@ -3,28 +3,28 @@
"Language" "ukrainian"
"Tokens"
{
- "NEW_ACHIEVEMENT_20_0_NAME" "Перевірено"
+ "NEW_ACHIEVEMENT_20_0_NAME" "Перевірений"
"NEW_ACHIEVEMENT_20_0_DESC" "Завершіть навчання."
"NEW_ACHIEVEMENT_20_1_NAME" "Забіяка"
- "NEW_ACHIEVEMENT_20_1_DESC" "Знищьте 1000 ворожих одиниць."
+ "NEW_ACHIEVEMENT_20_1_DESC" "Знищте 1 000 ворожих одиниць."
"NEW_ACHIEVEMENT_20_2_NAME" "Чистка"
- "NEW_ACHIEVEMENT_20_2_DESC" "Знишьте 100 000 ворожих одиниць."
+ "NEW_ACHIEVEMENT_20_2_DESC" "Знищте 100 000 ворожих одиниць."
"NEW_ACHIEVEMENT_20_3_NAME" "Атмосферне перевезення"
"NEW_ACHIEVEMENT_20_3_DESC" "Запустіть 10 000 предметів загалом."
- "NEW_ACHIEVEMENT_20_5_NAME" "Нескінченні поставки"
- "NEW_ACHIEVEMENT_20_5_DESC" "Запустіть 1000 000 предметів загалом."
+ "NEW_ACHIEVEMENT_20_5_NAME" "Нескінченне постачання"
+ "NEW_ACHIEVEMENT_20_5_DESC" "Запустіть 1 000 000 предметів загалом."
"NEW_ACHIEVEMENT_20_6_NAME" "Завойовник"
"NEW_ACHIEVEMENT_20_6_DESC" "Виграйте 10 матчів у режимі атаки."
"NEW_ACHIEVEMENT_20_7_NAME" "Чемпіон"
"NEW_ACHIEVEMENT_20_7_DESC" "Виграйте 10 матчів у режимі PvP."
"NEW_ACHIEVEMENT_20_8_NAME" "Бліц"
- "NEW_ACHIEVEMENT_20_8_DESC" "Знищіть вороже ядро в зоні атаки за 5 хвиль або менше."
+ "NEW_ACHIEVEMENT_20_8_DESC" "Знищте вороже ядро в зоні атаки за 5 хвиль або менше."
"NEW_ACHIEVEMENT_20_9_NAME" "Ядерний дощ"
"NEW_ACHIEVEMENT_20_9_DESC" "Запустіть своє ядро в зону 30 разів."
"NEW_ACHIEVEMENT_20_10_NAME" "Наполегливий"
- "NEW_ACHIEVEMENT_20_10_DESC" "Виживіть 100 хвиль."
+ "NEW_ACHIEVEMENT_20_10_DESC" "Протримайтесь 100 хвиль."
"NEW_ACHIEVEMENT_20_11_NAME" "Неперевершений"
- "NEW_ACHIEVEMENT_20_11_DESC" "Виживіть 500 хвиль."
+ "NEW_ACHIEVEMENT_20_11_DESC" "Протримайтесь 500 хвиль."
"NEW_ACHIEVEMENT_20_12_NAME" "Дослідник"
"NEW_ACHIEVEMENT_20_12_DESC" "Дослідіть все."
"NEW_ACHIEVEMENT_20_13_NAME" "Перевертень"
@@ -32,23 +32,23 @@
"NEW_ACHIEVEMENT_20_14_NAME" "Перевантаження"
"NEW_ACHIEVEMENT_20_14_DESC" "Нанесіть шкоду мокрому ворогу електрикою."
"NEW_ACHIEVEMENT_20_15_NAME" "Рикошет"
- "NEW_ACHIEVEMENT_20_15_DESC" "Знищіть ворога його же власною відбитою кулею."
+ "NEW_ACHIEVEMENT_20_15_DESC" "Знищте ворога його же власною відбитою кулею."
"NEW_ACHIEVEMENT_20_17_NAME" "ВЕЛИЧЕЗНА ПОМИЛКА"
"NEW_ACHIEVEMENT_20_17_DESC" "Дослідіть маршрутизатора."
"NEW_ACHIEVEMENT_20_18_NAME" "Створення"
"NEW_ACHIEVEMENT_20_18_DESC" "Побудуйте 10 000 блоків."
"NEW_ACHIEVEMENT_20_19_NAME" "Зрівняти з землею"
- "NEW_ACHIEVEMENT_20_19_DESC" "Знищьте 1000 ворожих блоків."
+ "NEW_ACHIEVEMENT_20_19_DESC" "Знищте 1 000 ворожих блоків."
"NEW_ACHIEVEMENT_20_20_NAME" "Ефектна катастрофа"
- "NEW_ACHIEVEMENT_20_20_DESC" "Торієвий реактор повинен перегрітися й вибихнути."
+ "NEW_ACHIEVEMENT_20_20_DESC" "Торієвий реактор повинен перегрітися й вибухнути."
"NEW_ACHIEVEMENT_20_21_NAME" "Картограф"
"NEW_ACHIEVEMENT_20_21_DESC" "Зробіть 10 мап."
"NEW_ACHIEVEMENT_20_22_NAME" "Веб-переглядач"
- "NEW_ACHIEVEMENT_20_22_DESC" "Завантажте карту з Майстерні."
+ "NEW_ACHIEVEMENT_20_22_DESC" "Завантажте мапу з Майстерні."
"NEW_ACHIEVEMENT_20_23_NAME" "Творець"
- "NEW_ACHIEVEMENT_20_23_DESC" "Опублікуйте карту в Майстерні."
+ "NEW_ACHIEVEMENT_20_23_DESC" "Опублікуйте мапу в Майстерні."
"NEW_ACHIEVEMENT_20_24_NAME" "Убивця"
- "NEW_ACHIEVEMENT_20_24_DESC" "Переможіть боса."
+ "NEW_ACHIEVEMENT_20_24_DESC" "Здолайте боса."
"NEW_ACHIEVEMENT_20_25_NAME" "Дослідник"
"NEW_ACHIEVEMENT_20_25_DESC" "Розблокуйте всі зони в кампанії."
"NEW_ACHIEVEMENT_20_26_NAME" "Завершувач"
@@ -58,7 +58,7 @@
"NEW_ACHIEVEMENT_20_31_NAME" "Матеріал I"
"NEW_ACHIEVEMENT_20_31_DESC" "Розблокуйте титан."
"NEW_ACHIEVEMENT_21_0_NAME" "Камікадзе"
- "NEW_ACHIEVEMENT_21_0_DESC" "Заповніть свого меха вибуховими матеріалами і помріть, створивши вибух."
+ "NEW_ACHIEVEMENT_21_0_DESC" "Заповніть свого меха вибуховими матеріалами й помріть, створивши вибух."
"NEW_ACHIEVEMENT_21_1_NAME" "Це починається"
"NEW_ACHIEVEMENT_21_1_DESC" "Побудуйте завод мехів «Кинджал»."
"NEW_ACHIEVEMENT_21_2_NAME" "Прямий наступ"
@@ -92,7 +92,7 @@
"NEW_ACHIEVEMENT_21_16_NAME" "Єресь"
"NEW_ACHIEVEMENT_21_16_DESC" "Побудуйте два маршрутизатори поруч."
"NEW_ACHIEVEMENT_21_17_NAME" "Одинокий захисник"
- "NEW_ACHIEVEMENT_21_17_DESC" "Виживіть 10 хвиль в першій-ліпшій зоні без будування будування будь-яких блоків."
+ "NEW_ACHIEVEMENT_21_17_DESC" "Протримайтесь 10 хвиль в першій-ліпшій зоні без будування будування будь-яких блоків."
"NEW_ACHIEVEMENT_21_18_NAME" "Спалити"
"NEW_ACHIEVEMENT_21_18_DESC" "Використайте піротит в будь-якій башті."
"NEW_ACHIEVEMENT_21_19_NAME" "Ефективність"
@@ -100,8 +100,8 @@
"NEW_ACHIEVEMENT_21_20_NAME" "Класичний режим"
"NEW_ACHIEVEMENT_21_20_DESC" "Увімкніть пікселізацію."
"NEW_ACHIEVEMENT_21_21_NAME" "Науковець"
- "NEW_ACHIEVEMENT_21_21_DESC" "Відкрийти Wiki з гри."
- "NEW_ACHIEVEMENT_21_22_NAME" "Впевнений початок"
+ "NEW_ACHIEVEMENT_21_21_DESC" "Відкрийти Вікі з гри."
+ "NEW_ACHIEVEMENT_21_22_NAME" "Упевнений початок"
"NEW_ACHIEVEMENT_21_22_DESC" "Вивантажіть в зону 10 000 або більше предметів."
"NEW_ACHIEVEMENT_21_23_NAME" "Запалювання"
"NEW_ACHIEVEMENT_21_23_DESC" "Підвищте потужність імпульсного реактора."
diff --git a/fastlane/metadata/steam/ukrainian/description.txt b/fastlane/metadata/steam/ukrainian/description.txt
index e53e92394b..0b0f6ca04c 100644
--- a/fastlane/metadata/steam/ukrainian/description.txt
+++ b/fastlane/metadata/steam/ukrainian/description.txt
@@ -1,4 +1,4 @@
-Створюйте складні логістичні мережі для перенесення боєприпасів у башти, видобувайте ресурси для будівництва, і захищайте своє ядро від різних хвиль ворогів. Грайте з друзями на різних платформах у кооперативні ігри, або киньте їм виклик у командних PvP-матчах.
+Створюйте складні системи логістики для перенесення боєприпасів у башти, видобувайте ресурси для будівництва, і захищайте своє ядро від різних хвиль ворогів. Грайте з друзями на різних платформах у кооперативні ігри, або киньте їм виклик у командних PvP-матчах.
[img]{STEAM_APP_IMAGE}/extras/ezgif-4-0e70c282f775.gif[/img]
@@ -28,9 +28,10 @@
[h2][h2]Ігрові режими[/h2][/h2]
-[*] [b]Виживання[/b]: створіть башти для захисту від ворогів в ігровому процесі, заснованому на захисті башт. Виживайте якомога довше, за бажанням можна запустит своє ядро, щоб використовувати зібрані ресурси для досліджень. Підготуйте свою базу для періодичних атак повітряних босів.
+[list]
+[*] [b]Виживання[/b]: створіть башти для захисту від ворогів в ігровому процесі, заснованому на захисті башт. Виживайте якомога довше, за бажанням можна запустити своє ядро, щоб використовувати зібрані ресурси для досліджень. Підготуйте свою базу для періодичних атак повітряних босів.
[*] [b]Атака[/b]: будуйте заводи бойових одиниць для знищення ворожих ядер, одночасно захищаючи свою базу від хвиль ворожих одиниць.
-[*] [b]PvP[/b]: змагайтеся з іншими гравцями, щоб знищити ядра один одного. Створюйте бойові одиниці або нападайте на інші бази безпосередньо зі своїми мехами. Створіть різноманітні типи підтримувальиних та наступальних підрозділів, щоб допоможуть вам у досягненні цілей.
+[*] [b]PvP[/b]: змагайтеся з іншими гравцями, щоб знищити ядра один одного. Створюйте бойові одиниці або нападайте на інші бази безпосередньо зі своїми мехами. Створіть різноманітні типи підрозділів підтримки та наступу, що допоможуть вам у досягненні цілей.
[*] [b]Пісочниця[/b]: грайте з нескінченними ресурсами без загрози з боку ворога. Використовуйте специфічні для пісочниці елементи та блоки рідких джерел для тестування конструкцій та появи ворогів за запитом.
[/list]
@@ -49,7 +50,7 @@
[list]
[*] Створюйте ландшафт за допомогою візуального редактора.
[*] Змінюйте і переглядайте споруди так само як в грі.
-[*] Регульовані режими інструментів — змініть функціонування кожного інструменту.
+[*] Регульовані режими інструментів — змініть функціювання кожного інструменту.
[*] Потужна система генерації мап, що має безліч різних типів фільтрів для процедурного маніпулювання місцевістю.
[*] Застосуйте на своїх мапах шум, спотворення, згладжування, ерозію, симетрію, генерацію руди та випадкову генерацію місцевості.
[*] Відкрита гра в жанрі «захист башт» з акцентом на керуванні ресурсами.
diff --git a/gradle.properties b/gradle.properties
index 1aea212e80..1aeb38a829 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,3 @@
org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m
-archash=b54ec5513f66ad97f191b46da10f6c61046a301a
+archash=b8f094ba9916ba5a0ce5b4c9dc0fc3d2d0cf89fd
diff --git a/ios/data/Assets.xcassets/AppIcon.appiconset/Contents.json b/ios/data/Assets.xcassets/AppIcon.appiconset/Contents.json
index 2210d686de..8d0bc9cd26 100644
--- a/ios/data/Assets.xcassets/AppIcon.appiconset/Contents.json
+++ b/ios/data/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -113,4 +113,4 @@
"version": 1,
"author": "xcode"
}
-}
\ No newline at end of file
+}
diff --git a/ios/data/Assets.xcassets/Contents.json b/ios/data/Assets.xcassets/Contents.json
index 121dee67a6..97a8662ebd 100644
--- a/ios/data/Assets.xcassets/Contents.json
+++ b/ios/data/Assets.xcassets/Contents.json
@@ -3,4 +3,4 @@
"version": 1,
"author": "xcode"
}
-}
\ No newline at end of file
+}
diff --git a/ios/src/mindustry/ios/IOSLauncher.java b/ios/src/mindustry/ios/IOSLauncher.java
index 5e0102db79..e4da9160d0 100644
--- a/ios/src/mindustry/ios/IOSLauncher.java
+++ b/ios/src/mindustry/ios/IOSLauncher.java
@@ -1,12 +1,13 @@
package mindustry.ios;
import arc.*;
-import com.badlogic.gdx.backends.iosrobovm.*;
+import arc.Input.*;
import arc.files.*;
import arc.func.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import arc.util.io.*;
+import com.badlogic.gdx.backends.iosrobovm.*;
import mindustry.*;
import mindustry.game.EventType.*;
import mindustry.game.Saves.*;
@@ -40,13 +41,32 @@ public class IOSLauncher extends IOSApplication.Delegate{
@Override
public void showFileChooser(boolean open, String extension, Cons cons){
- UIDocumentBrowserViewController cont = new UIDocumentBrowserViewController((NSArray)null);
+ if(!open){ //when exporting, just share it.
+ //ask for export name
+ Core.input.getTextInput(new TextInput(){{
+ title = Core.bundle.get("filename");
+ accepted = name -> {
+ try{
+ //write result
+ Fi result = tmpDirectory.child(name + "." + extension);
+ cons.get(result);
+ //import the document
+ shareFile(result);
+ }catch(Throwable t){
+ ui.showException(t);
+ }
+ };
+ }});
+ return;
+ }
+
+ UIDocumentBrowserViewController cont = new UIDocumentBrowserViewController((NSArray)null);
NSArray arr = new NSArray<>(new UIBarButtonItem(Core.bundle.get("cancel"), UIBarButtonItemStyle.Plain,
uiBarButtonItem -> cont.dismissViewController(true, () -> {})));
- cont.setAllowsDocumentCreation(!open);
+ cont.setAllowsDocumentCreation(false);
cont.setAdditionalLeadingNavigationBarButtonItems(arr);
class ChooserDelegate extends NSObject implements UIDocumentBrowserViewControllerDelegate{
@@ -97,7 +117,6 @@ public class IOSLauncher extends IOSApplication.Delegate{
@Override
public void failedToImportDocument(UIDocumentBrowserViewController controller, NSURL documentURL, NSError error){
-
}
@Override
diff --git a/server/server_template/run_server.bat b/server/server_template/run_server.bat
index 26d919a77b..8084a7149f 100644
--- a/server/server_template/run_server.bat
+++ b/server/server_template/run_server.bat
@@ -1 +1 @@
-java -jar server.jar
\ No newline at end of file
+java -jar server.jar
diff --git a/server/server_template/run_server.sh b/server/server_template/run_server.sh
index 26d919a77b..8084a7149f 100755
--- a/server/server_template/run_server.sh
+++ b/server/server_template/run_server.sh
@@ -1 +1 @@
-java -jar server.jar
\ No newline at end of file
+java -jar server.jar
diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java
index 0ee88e19e2..6ecc42fb05 100644
--- a/server/src/mindustry/server/ServerControl.java
+++ b/server/src/mindustry/server/ServerControl.java
@@ -653,52 +653,56 @@ public class ServerControl implements ApplicationListener{
});
handler.register("unban", "", "Completely unban a person by IP or ID.", arg -> {
- if(arg[0].contains(".")){
- if(netServer.admins.unbanPlayerIP(arg[0])){
- info("Unbanned player by IP: {0}.", arg[0]);
- }else{
- err("That IP is not banned!");
- }
+ if(netServer.admins.unbanPlayerIP(arg[0]) || netServer.admins.unbanPlayerID(arg[0])){
+ info("Unbanned player.", arg[0]);
}else{
- if(netServer.admins.unbanPlayerID(arg[0])){
- info("Unbanned player by ID: {0}.", arg[0]);
- }else{
- err("That ID is not banned!");
- }
+ err("That IP/ID is not banned!");
+ }
+ });
+
+ handler.register("pardon", "", "Pardons a votekicked player by ID and allows them to join again.", arg -> {
+ PlayerInfo info = netServer.admins.getInfoOptional(arg[0]);
+
+ if(info != null){
+ info.lastKicked = 0;
+ info("Pardoned player: {0}", info.lastName);
+ }else{
+ err("That ID can't be found.");
}
});
- handler.register("admin", "", "Make an online user admin", arg -> {
+ handler.register("admin", " ", "Make an online user admin", arg -> {
if(!state.is(State.playing)){
err("Open the server first.");
return;
}
- Player target = playerGroup.find(p -> p.name.equals(arg[0]));
-
- if(target != null){
- netServer.admins.adminPlayer(target.uuid, target.usid);
- target.isAdmin = true;
- info("Admin-ed player: {0}", arg[0]);
- }else{
- info("Nobody with that name could be found.");
- }
- });
-
- handler.register("unadmin", "", "Removes admin status from an online player", arg -> {
- if(!state.is(State.playing)){
- err("Open the server first.");
+ if(!(arg[0].equals("add") || arg[0].equals("remove"))){
+ err("Second parameter must be either 'add' or 'remove'.");
return;
}
- Player target = playerGroup.find(p -> p.name.equals(arg[0]));
+ boolean add = arg[0].equals("add");
+
+ PlayerInfo target;
+ Player playert = playerGroup.find(p -> p.name.equals(arg[1]));
+ if(playert != null){
+ target = playert.getInfo();
+ }else{
+ target = netServer.admins.getInfoOptional(arg[1]);
+ playert = playerGroup.find(p -> p.getInfo() == target);
+ }
if(target != null){
- netServer.admins.unAdminPlayer(target.uuid);
- target.isAdmin = false;
- info("Un-admin-ed player: {0}", arg[0]);
+ if(add){
+ netServer.admins.adminPlayer(target.id, target.adminUsid);
+ }else{
+ netServer.admins.unAdminPlayer(target.id);
+ }
+ if(playert != null) playert.isAdmin = add;
+ info("Changed admin status of player: &ly{0}", target.lastName);
}else{
- info("Nobody with that name could be found.");
+ err("Nobody with that name or ID could be found. If adding an admin by name, make sure they're online; otherwise, use their UUID.");
}
});
@@ -715,6 +719,18 @@ public class ServerControl implements ApplicationListener{
}
});
+ handler.register("players", "List all players currently in game.", arg -> {
+ if(playerGroup.size() == 0){
+ info("No players are currently in the server.");
+ }else{
+ info("&lyPlayers: {0}", playerGroup.size());
+ for(Player user : playerGroup){
+ PlayerInfo userInfo = user.getInfo();
+ info(" &lm {0} / ID: '{1}' / IP: '{2}' / Admin: '{3}'", userInfo.lastName, userInfo.id, userInfo.lastIP, userInfo.admin);
+ }
+ }
+ });
+
handler.register("runwave", "Trigger the next wave.", arg -> {
if(!state.is(State.playing)){
err("Not hosting. Host a game first.");
diff --git a/server/src/mindustry/server/ServerLauncher.java b/server/src/mindustry/server/ServerLauncher.java
index 8b668ae669..c7d2ea9b3f 100644
--- a/server/src/mindustry/server/ServerLauncher.java
+++ b/server/src/mindustry/server/ServerLauncher.java
@@ -82,4 +82,4 @@ public class ServerLauncher implements ApplicationListener{
Events.fire(new ServerLoadEvent());
}
-}
\ No newline at end of file
+}
diff --git a/servers.json b/servers.json
index 81ea4bf021..56c8a52456 100644
--- a/servers.json
+++ b/servers.json
@@ -1,5 +1,41 @@
[
{
"address": "mindustry.us.to"
+ },
+ {
+ "address": "mindustry.indielm.com:1101"
+ },
+ {
+ "address": "mindustry.indielm.com"
+ },
+ {
+ "address": "mindustry.nydus.app:1337"
+ },
+ {
+ "address": "mindustry.ecansol.com:6597"
+ },
+ {
+ "address": "mindustry.ecansol.com:6499"
+ },
+ {
+ "address": "mindustry.ru"
+ },
+ {
+ "address": "mindustry.ru:7000"
+ },
+ {
+ "address": "mindustry.io"
+ },
+ {
+ "address": "mindustry.io:1000"
+ },
+ {
+ "address": "mindustry.io:2000"
+ },
+ {
+ "address": "mindustry.io:3000"
+ },
+ {
+ "address": "aamindustry.play.ai"
}
]
diff --git a/servers_be.json b/servers_be.json
index be72d9d6da..e99eefaaad 100644
--- a/servers_be.json
+++ b/servers_be.json
@@ -2,4 +2,4 @@
{
"address": "mindustry.us.to:6568"
}
-]
\ No newline at end of file
+]
diff --git a/tests/build.gradle b/tests/build.gradle
index 93595748d0..14b7dccb41 100644
--- a/tests/build.gradle
+++ b/tests/build.gradle
@@ -8,4 +8,4 @@ sourceSets{
srcDir "src/test/resources"
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/src/test/java/ApplicationTests.java b/tests/src/test/java/ApplicationTests.java
index 722bf246d0..b37ca45b06 100644
--- a/tests/src/test/java/ApplicationTests.java
+++ b/tests/src/test/java/ApplicationTests.java
@@ -1,25 +1,23 @@
-import arc.ApplicationCore;
-import arc.Core;
-import arc.backend.headless.HeadlessApplication;
+import arc.*;
+import arc.backend.headless.*;
+import arc.math.geom.*;
import arc.struct.*;
-import arc.math.geom.Point2;
-import arc.util.Log;
-import arc.util.Time;
-import mindustry.Vars;
+import arc.util.*;
+import mindustry.*;
import mindustry.content.*;
-import mindustry.core.GameState.State;
import mindustry.core.*;
-import mindustry.entities.traits.BuilderTrait.BuildRequest;
-import mindustry.entities.type.BaseUnit;
+import mindustry.core.GameState.*;
+import mindustry.ctype.*;
+import mindustry.entities.traits.BuilderTrait.*;
+import mindustry.entities.type.*;
import mindustry.entities.type.base.*;
-import mindustry.game.Team;
-import mindustry.io.SaveIO;
-import mindustry.maps.Map;
-import mindustry.net.*;
-import mindustry.ctype.ContentType;
-import mindustry.type.Item;
+import mindustry.game.*;
+import mindustry.io.*;
+import mindustry.maps.*;
+import mindustry.net.Net;
+import mindustry.type.*;
import mindustry.world.*;
-import mindustry.world.blocks.BlockPart;
+import mindustry.world.blocks.*;
import org.junit.jupiter.api.*;
import static mindustry.Vars.*;
@@ -216,6 +214,59 @@ public class ApplicationTests{
assertTrue(state.teams.playerCores().size > 0);
}
+ @Test
+ void conveyorCrash(){
+ world.loadMap(testMap);
+ state.set(State.playing);
+
+ world.tile(0, 0).setBlock(Blocks.conveyor);
+ world.tile(0, 0).rotation(0);
+ Blocks.conveyor.acceptStack(Items.copper, 1000, world.tile(0, 0), null);
+ }
+
+ @Test
+ void conveyorBench(){
+ int[] items = {0};
+
+ world.loadMap(testMap);
+ state.set(State.playing);
+ int length = 128;
+ world.tile(0, 0).setBlock(Blocks.itemSource);
+ world.tile(0, 0).configureAny(Items.copper.id);
+
+ Array entities = Array.with(world.tile(0, 0).entity);
+
+ for(int i = 0; i < length; i++){
+ world.tile(i + 1, 0).setBlock(Blocks.conveyor);
+ world.tile(i + 1, 0).rotation(0);
+ entities.add(world.tile(i + 1, 0).entity);
+ }
+
+ world.tile(length + 1, 0).setBlock(new Block("___"){
+ @Override
+ public void handleItem(Item item, Tile tile, Tile source){
+ items[0] ++;
+ }
+
+ @Override
+ public boolean acceptItem(Item item, Tile tile, Tile source){
+ return true;
+ }
+ });
+
+ //warmup
+ for(int i = 0; i < 100000; i++){
+ entities.each(TileEntity::update);
+ }
+
+ Time.mark();
+ for(int i = 0; i < 200000; i++){
+ entities.each(TileEntity::update);
+ }
+ Log.info(Time.elapsed() + "ms to process " + items[0] + " items");
+ assertTrue(items[0] > 0);
+ }
+
@Test
void load77Save(){
resetWorld();
@@ -406,4 +457,4 @@ public class ApplicationTests{
tile.block().handleStack(item, 1, tile, unit);
assertEquals(capacity, tile.entity.items.get(item));
}
-}
\ No newline at end of file
+}
diff --git a/tools/build.gradle b/tools/build.gradle
index 959121fcda..e527ef1e10 100644
--- a/tools/build.gradle
+++ b/tools/build.gradle
@@ -34,7 +34,7 @@ def transformColors = { List
> list ->
}
-transformColors([["6e7080", "989aa4", "b0bac0"], ["bc5452", "ea8878", "feb380"], ["dea158", "f8c266", "ffe18f"], ["feb380", "ea8878", "bc5452"]])
+transformColors([["6e7080", "989aa4", "b0bac0"], ["bc5452", "ea8878", "feb380"], ["de9458", "f8c266", "ffe18f"], ["feb380", "ea8878", "bc5452"]])
def antialias = { File file ->
if(!doAntialias || file.lastModified() <= 1000) return
diff --git a/tools/run-newline b/tools/run-newline
new file mode 100755
index 0000000000..e9e4394169
--- /dev/null
+++ b/tools/run-newline
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+# add newlines to all files
+git ls-files -z | while IFS= read -rd '' f; do tail -c1 < "$f" | read -r _ || echo >> "$f"; done
+
+# undo the changes in these
+git checkout -- "*.png"
+git checkout -- "*.msav"
+git checkout -- "*.jar"
+git checkout -- "*.ogg"
+git checkout -- "*.ico"
+git checkout -- "*.icns"
+git checkout -- "*.ttf"
+git checkout -- "*.glsl"
diff --git a/tools/src/mindustry/tools/FontGenerator.java b/tools/src/mindustry/tools/FontGenerator.java
index 32bbc121cf..f38766f37d 100644
--- a/tools/src/mindustry/tools/FontGenerator.java
+++ b/tools/src/mindustry/tools/FontGenerator.java
@@ -31,7 +31,7 @@ public class FontGenerator{
String session = folder.child("session").readString();
net.httpGet("http://fontello.com/" + session + "/get", result -> {
try{
- Streams.copyStream(result.getResultAsStream(), folder.child("font.zip").write());
+ Streams.copy(result.getResultAsStream(), folder.child("font.zip").write());
}catch(IOException e){
throw new RuntimeException(e);
}
@@ -46,7 +46,8 @@ public class FontGenerator{
Log.info("Merge...");
- OS.exec("fontforge", "-script", "core/assets-raw/fontgen/merge.pe");
+ //don't merge since it breaks the font
+ //OS.exec("fontforge", "-script", "core/assets-raw/fontgen/merge.pe");
Log.info("Done.");
}
diff --git a/tools/src/mindustry/tools/ScriptStubGenerator.java b/tools/src/mindustry/tools/ScriptStubGenerator.java
index 58dfd27b28..ddbd357e60 100644
--- a/tools/src/mindustry/tools/ScriptStubGenerator.java
+++ b/tools/src/mindustry/tools/ScriptStubGenerator.java
@@ -30,13 +30,6 @@ public class ScriptStubGenerator{
DataInputStream.class, DataOutputStream.class, Integer.class, Float.class, Double.class, Long.class, Boolean.class, Short.class, Byte.class, Character.class);
Array nopackage = Array.with("java.lang", "java");
- String fileTemplate = "package mindustry.mod;\n" +
- "\nimport arc.struct.*;\n" +
- "//obviously autogenerated, do not touch\n" +
- "public class ClassAccess{\n" +
- "\tpublic static final ObjectSet allowedClassNames = ObjectSet.with($ALLOWED_CLASS_NAMES$);\n" +
- "}";
-
List classLoadersList = new LinkedList<>();
classLoadersList.add(ClasspathHelper.contextClassLoader());
classLoadersList.add(ClasspathHelper.staticClassLoader());
@@ -73,8 +66,5 @@ public class ScriptStubGenerator{
//Log.info(result);
new Fi("core/assets/scripts/global.js").writeString(result.toString());
- new Fi("core/src/mindustry/mod/ClassAccess.java").writeString(fileTemplate
- .replace("$ALLOWED_CLASSES$", classes.toString(", ", type -> type.getName() + ".class"))
- .replace("$ALLOWED_CLASS_NAMES$", classes.toString(", ", type -> "\"" + type.getName() + "\"")));
}
}