From 9f3d7be7b4c1277b6a942e6f64f36bf5ff243ef5 Mon Sep 17 00:00:00 2001 From: Anuken Date: Mon, 25 Sep 2017 18:53:04 -0400 Subject: [PATCH] Major improvements to Android touch controls, many bugfixes --- build.gradle | 2 +- core/assets-raw/ui/icon-cursor.png | Bin 0 -> 186 bytes core/assets-raw/ui/icon-rotate-arrow.png | Bin 0 -> 177 bytes core/assets-raw/ui/icon-touch.png | Bin 0 -> 188 bytes core/assets/ui/uiskin.atlas | 77 ++++--- core/assets/ui/uiskin.png | Bin 12967 -> 13100 bytes core/src/io/anuke/mindustry/Control.java | 6 +- core/src/io/anuke/mindustry/EffectLoader.java | 15 ++ core/src/io/anuke/mindustry/Renderer.java | 211 +++++++++--------- core/src/io/anuke/mindustry/UI.java | 91 +++++++- core/src/io/anuke/mindustry/Vars.java | 2 +- .../mindustry/entities/enemies/Enemy.java | 2 +- .../mindustry/entities/enemies/FastEnemy.java | 2 +- .../mindustry/entities/enemies/TankEnemy.java | 18 +- .../anuke/mindustry/input/AndroidInput.java | 17 +- .../anuke/mindustry/input/GestureHandler.java | 25 ++- .../io/anuke/mindustry/input/PlaceMode.java | 5 + .../io/anuke/mindustry/resource/Weapon.java | 4 + .../src/io/anuke/mindustry/ui/LoadDialog.java | 44 ++-- .../src/io/anuke/mindustry/ui/SaveDialog.java | 13 +- core/src/io/anuke/mindustry/world/Block.java | 44 +--- .../anuke/mindustry/world/blocks/Blocks.java | 18 +- .../mindustry/world/blocks/types/Conduit.java | 2 + .../mindustry/world/blocks/types/Floor.java | 57 +++++ desktop/mindustry-saves/0.mins | Bin 1237 -> 1140 bytes desktop/mindustry-saves/1.mins | Bin 1241 -> 1357 bytes .../mindustry/desktop/DesktopLauncher.java | 2 +- 27 files changed, 425 insertions(+), 232 deletions(-) create mode 100644 core/assets-raw/ui/icon-cursor.png create mode 100644 core/assets-raw/ui/icon-rotate-arrow.png create mode 100644 core/assets-raw/ui/icon-touch.png create mode 100644 core/src/io/anuke/mindustry/input/PlaceMode.java create mode 100644 core/src/io/anuke/mindustry/world/blocks/types/Floor.java diff --git a/build.gradle b/build.gradle index 069758fa8e..356a58ccfa 100644 --- a/build.gradle +++ b/build.gradle @@ -79,7 +79,7 @@ project(":core") { apply plugin: "java" dependencies { - compile 'com.github.anuken:ucore:be4ae33e40' + compile 'com.github.anuken:ucore:f77bdb38e4' compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx-ai:1.8.1" } diff --git a/core/assets-raw/ui/icon-cursor.png b/core/assets-raw/ui/icon-cursor.png new file mode 100644 index 0000000000000000000000000000000000000000..7f8d2c80d8f4b787d0014a68a47248a6b9c63061 GIT binary patch literal 186 zcmeAS@N?(olHy`uVBq!ia0vp^AT}2V8<6ZZI=>f4aTa()7Bet#3xhBt!>l*3^|y@cN@<6c&xy1Z}{2J+@LtNPKBP1 z1N?bj3=^K;tq@b(s9atYHN~=3r|FrJ_T_}xb7lt>bbjT@URGq9!f{Tq`1h>ktD^MGH+KL4 literal 0 HcmV?d00001 diff --git a/core/assets-raw/ui/icon-rotate-arrow.png b/core/assets-raw/ui/icon-rotate-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..c2ea8f2703df11de463c115d5b08a0111218741f GIT binary patch literal 177 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 { + Draw.thickness(2f - e.ifract()*2f); + Draw.color(Hue.mix(Color.WHITE, Color.LIGHT_GRAY, e.ifract())); + Draw.spikes(e.x, e.y, 1f + e.ifract() * 4f, 1, 5); + Draw.reset(); + }); + + Effects.create("mortarshoot", 9, e -> { + Draw.thickness(1.3f - e.ifract()); + Draw.color(Hue.mix(Color.WHITE, Color.ORANGE, e.ifract())); + Draw.spikes(e.x, e.y, e.ifract() * 4f, 2, 6); + Draw.circle(e.x, e.y, e.ifract() * 5f + 1f); + Draw.reset(); + }); Effects.create("explosion", 15, e -> { Draw.thickness(2f); diff --git a/core/src/io/anuke/mindustry/Renderer.java b/core/src/io/anuke/mindustry/Renderer.java index 4e5cefc658..6933d9ad13 100644 --- a/core/src/io/anuke/mindustry/Renderer.java +++ b/core/src/io/anuke/mindustry/Renderer.java @@ -13,6 +13,7 @@ import com.badlogic.gdx.math.Vector2; import io.anuke.mindustry.GameState.State; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.input.AndroidInput; +import io.anuke.mindustry.input.PlaceMode; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.World; import io.anuke.mindustry.world.blocks.Blocks; @@ -26,145 +27,142 @@ import io.anuke.ucore.graphics.Caches; import io.anuke.ucore.modules.RendererModule; import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.utils.Cursors; -import io.anuke.ucore.util.GridMap; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Tmp; public class Renderer extends RendererModule{ int targetscale = baseCameraScale; int chunksize = 32; - GridMap caches = new GridMap<>(); - - public Renderer(){ + Cache[][] floorCache; + + public Renderer() { Core.cameraScale = baseCameraScale; pixelate(); - + Draw.addSurface("shadow", Core.cameraScale); Shaders.create(); } - + @Override public void update(){ if(Core.cameraScale != targetscale){ - float targetzoom = (float)Core.cameraScale / targetscale; - camera.zoom = Mathf.lerp(camera.zoom, targetzoom, 0.2f*Timers.delta()); - + float targetzoom = (float) Core.cameraScale / targetscale; + camera.zoom = Mathf.lerp(camera.zoom, targetzoom, 0.2f * Timers.delta()); + if(Mathf.in(camera.zoom, targetzoom, 0.005f)){ camera.zoom = 1f; Core.cameraScale = targetscale; camera.viewportWidth = Gdx.graphics.getWidth() / Core.cameraScale; camera.viewportHeight = Gdx.graphics.getHeight() / Core.cameraScale; - - AndroidInput.mousex = Gdx.graphics.getWidth()/2; - AndroidInput.mousey = Gdx.graphics.getHeight()/2; + + AndroidInput.mousex = Gdx.graphics.getWidth() / 2; + AndroidInput.mousey = Gdx.graphics.getHeight() / 2; } } - + if(GameState.is(State.menu)){ clearScreen(); }else{ - + if(World.core.block() == ProductionBlocks.core){ smoothCamera(player.x, player.y, android ? 0.3f : 0.14f); }else{ smoothCamera(World.core.worldx(), World.core.worldy(), 0.4f); } - + float prex = camera.position.x, prey = camera.position.y; - + updateShake(0.75f); float prevx = camera.position.x, prevy = camera.position.y; clampCamera(-tilesize / 2f, -tilesize / 2f, World.pixsize - tilesize / 2f, World.pixsize - tilesize / 2f); - + float deltax = camera.position.x - prex, deltay = camera.position.y - prey; - + if(android){ - player.x += camera.position.x-prevx; - player.y += camera.position.y-prevy; + player.x += camera.position.x - prevx; + player.y += camera.position.y - prevy; } - + float lastx = camera.position.x, lasty = camera.position.y; - + if(android){ - camera.position.set((int)camera.position.x, (int)camera.position.y, 0); - - if(Gdx.graphics.getHeight()/Core.cameraScale % 2 == 1){ + camera.position.set((int) camera.position.x, (int) camera.position.y, 0); + + if(Gdx.graphics.getHeight() / Core.cameraScale % 2 == 1){ camera.position.add(0, -0.5f, 0); } } - + drawDefault(); - + camera.position.set(lastx - deltax, lasty - deltay, 0); - + if(Vars.debug){ record(); } } } - + @Override public void draw(){ renderTiles(); Entities.draw(); renderPixelOverlay(); } - + @Override public void resize(int width, int height){ super.resize(width, height); - - AndroidInput.mousex = Gdx.graphics.getWidth()/2; - AndroidInput.mousey = Gdx.graphics.getHeight()/2; + + AndroidInput.mousex = Gdx.graphics.getWidth() / 2; + AndroidInput.mousey = Gdx.graphics.getHeight() / 2; camera.position.set(player.x, player.y, 0); } - + void renderTiles(){ - int chunksx = World.width()/chunksize, chunksy = World.height()/chunksize; - + int chunksx = World.width() / chunksize, chunksy = World.height() / chunksize; + //render the entire map - if(caches.size() == 0){ - for(int cx = 0; cx < chunksx; cx ++){ - for(int cy = 0; cy < chunksy; cy ++){ - Caches.begin(1600); - - for(int tilex = cx*chunksize; tilex < (cx+1)*chunksize; tilex++){ - for(int tiley = cy*chunksize; tiley < (cy+1)*chunksize; tiley++){ - World.tile(tilex, tiley).floor().drawCache(World.tile(tilex, tiley)); - } - } - caches.put(cx, cy, Caches.end()); - } - } + if(floorCache == null || floorCache.length != chunksx || floorCache[0].length != chunksy){ + floorCache = new Cache[chunksx][chunksy]; } - + OrthographicCamera camera = Core.camera; - + Draw.end(); - - int crangex = (int)(camera.viewportWidth/(chunksize*tilesize))+1; - int crangey = (int)(camera.viewportHeight/(chunksize*tilesize))+1; - + + int crangex = (int) (camera.viewportWidth / (chunksize * tilesize)) + 1; + int crangey = (int) (camera.viewportHeight / (chunksize * tilesize)) + 1; + for(int x = -crangex; x <= crangex; x++){ for(int y = -crangey; y <= crangey; y++){ - int worldx = Mathf.scl(camera.position.x, chunksize*tilesize) + x; - int worldy = Mathf.scl(camera.position.y, chunksize*tilesize) + y; - - if(caches.containsKey(worldx, worldy)) - caches.get(worldx, worldy).render(); + int worldx = Mathf.scl(camera.position.x, chunksize * tilesize) + x; + int worldy = Mathf.scl(camera.position.y, chunksize * tilesize) + y; + + if(!Mathf.inBounds(worldx, worldy, floorCache)) + continue; + + if(floorCache[worldx][worldy] == null){ + renderCache(worldx, worldy); + } + + floorCache[worldx][worldy].render(); } } - + Draw.begin(); - + Draw.reset(); - int rangex = (int) (camera.viewportWidth * camera.zoom / tilesize / 2)+2; - int rangey = (int) (camera.viewportHeight * camera.zoom / tilesize / 2)+2; - + int rangex = (int) (camera.viewportWidth * camera.zoom / tilesize / 2) + 2; + int rangey = (int) (camera.viewportHeight * camera.zoom / tilesize / 2) + 2; + boolean noshadows = Settings.getBool("noshadows"); - - for(int l = (noshadows ? 1 : 0); l < 4; l++){ + + //0 = shadows + //1 = normal blocks + //2 = over blocks + for(int l = (noshadows ? 1 : 0); l < 3; l++){ if(l == 0){ Draw.surface("shadow"); } @@ -174,14 +172,15 @@ public class Renderer extends RendererModule{ int worldx = Mathf.scl(camera.position.x, tilesize) + x; int worldy = Mathf.scl(camera.position.y, tilesize) + y; - if( World.tile(worldx, worldy) != null){ + if(World.tile(worldx, worldy) != null){ Tile tile = World.tile(worldx, worldy); if(l == 0){ - if(tile.block() != Blocks.air) + if(tile.block() != Blocks.air){ Draw.rect(tile.block().shadow, worldx * tilesize, worldy * tilesize); + } }else if(l == 1){ tile.block().draw(tile); - }else{ + }else if(l == 2){ tile.block().drawOver(tile); } } @@ -195,23 +194,34 @@ public class Renderer extends RendererModule{ } } } - - public void clearTiles(){ - for(Cache cache : caches.values()) - cache.dispose(); + + void renderCache(int cx, int cy){ + Caches.begin(1600); + + for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize; tilex++){ + for(int tiley = cy * chunksize; tiley < (cy + 1) * chunksize; tiley++){ + Tile tile = World.tile(tilex, tiley); + tile.floor().drawCache(tile); + + } + } + floorCache[cx][cy] = Caches.end(); - caches.clear(); } - + + public void clearTiles(){ + floorCache = null; + } + void renderPixelOverlay(){ - - if(player.recipe != null && Inventory.hasItems(player.recipe.requirements) && (!ui.hasMouse() || android)){ + + if(player.recipe != null && Inventory.hasItems(player.recipe.requirements) && (!ui.hasMouse() || android) && AndroidInput.mode == PlaceMode.cursor){ float x = 0; float y = 0; - + int tilex = 0; int tiley = 0; - + if(android){ Vector2 vec = Graphics.world(AndroidInput.mousex, AndroidInput.mousey); x = Mathf.round2(vec.x, tilesize); @@ -230,7 +240,7 @@ public class Renderer extends RendererModule{ Draw.color(valid ? Color.PURPLE : Color.SCARLET); Draw.thickness(2f); Draw.square(x, y, tilesize / 2 + MathUtils.sin(Timers.time() / 6f) + 1); - + player.recipe.result.drawPlace(tilex, tiley, valid); if(player.recipe.result.rotate){ @@ -238,7 +248,7 @@ public class Renderer extends RendererModule{ Tmp.v1.set(7, 0).rotate(player.rotation * 90); Draw.line(x, y, x + Tmp.v1.x, y + Tmp.v1.y); } - + Draw.thickness(1f); Draw.color("scarlet"); for(Tile spawn : World.spawnpoints){ @@ -249,7 +259,7 @@ public class Renderer extends RendererModule{ Cursors.setHand(); else Cursors.restoreCursor(); - + Draw.reset(); } @@ -262,13 +272,14 @@ public class Renderer extends RendererModule{ Draw.reset(); } } - + if(android && player.breaktime > 0){ - Tile tile = AndroidInput.selected(); + Vector2 vec = Graphics.world(Gdx.input.getX(0), Gdx.input.getY(0)); + Tile tile = World.tile(Mathf.scl2(vec.x, tilesize), Mathf.scl2(vec.y, tilesize)); if(tile.breakable() && tile.block() != ProductionBlocks.core){ float fract = player.breaktime / tile.block().breaktime; Draw.color(Color.YELLOW, Color.SCARLET, fract); - Draw.circle(tile.worldx(), tile.worldy(), 4 + (1f-fract)*26); + Draw.circle(tile.worldx(), tile.worldy(), 4 + (1f - fract) * 26); Draw.reset(); } } @@ -278,8 +289,8 @@ public class Renderer extends RendererModule{ if(tile != null && tile.block() != Blocks.air){ if(tile.entity != null) - drawHealth(tile.entity.x, tile.entity.y, tile.entity.health, tile.entity.maxhealth); - + drawHealth(tile.entity.x, tile.entity.y, tile.entity.health, tile.entity.maxhealth); + tile.block().drawPixelOverlay(tile); } } @@ -292,20 +303,20 @@ public class Renderer extends RendererModule{ } } } - + void drawHealth(float x, float y, float health, float maxhealth){ - drawBar(Color.RED, x, y, health/maxhealth); + drawBar(Color.RED, x, y, health / maxhealth); } - + public void drawBar(Color color, float x, float y, float fraction){ float len = 3; float offset = 7; - - float w = (int)(len * 2 * fraction) + 0.5f; - + + float w = (int) (len * 2 * fraction) + 0.5f; + x -= 0.5f; y += 0.5f; - + Draw.thickness(3f); Draw.color(Color.SLATE); Draw.line(x - len + 1, y - offset, x + len + 1.5f, y - offset); @@ -317,20 +328,20 @@ public class Renderer extends RendererModule{ Draw.line(x - len + 1, y - offset, x - len + w, y - offset); Draw.reset(); } - + public void setCameraScale(int amount){ targetscale = amount; clampScale(); Draw.getSurface("pixel").setScale(targetscale); Draw.getSurface("shadow").setScale(targetscale); } - + public void scaleCamera(int amount){ setCameraScale(targetscale + amount); } - + public void clampScale(){ - targetscale = Mathf.clamp(targetscale, Math.round(Unit.dp.inPixels(3)), Math.round(Unit.dp.inPixels((5)))); + targetscale = Mathf.clamp(targetscale, Math.round(Unit.dp.inPixels(2)), Math.round(Unit.dp.inPixels((5)))); } - + } diff --git a/core/src/io/anuke/mindustry/UI.java b/core/src/io/anuke/mindustry/UI.java index 03ed3321a2..142e10fe6b 100644 --- a/core/src/io/anuke/mindustry/UI.java +++ b/core/src/io/anuke/mindustry/UI.java @@ -16,6 +16,7 @@ import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.GameState.State; import io.anuke.mindustry.input.AndroidInput; +import io.anuke.mindustry.input.PlaceMode; import io.anuke.mindustry.resource.*; import io.anuke.mindustry.ui.*; import io.anuke.ucore.core.Core; @@ -25,6 +26,7 @@ import io.anuke.ucore.function.VisibilityProvider; import io.anuke.ucore.modules.SceneModule; import io.anuke.ucore.scene.actions.Actions; import io.anuke.ucore.scene.builders.*; +import io.anuke.ucore.scene.event.Touchable; import io.anuke.ucore.scene.ui.*; import io.anuke.ucore.scene.ui.Window.WindowStyle; import io.anuke.ucore.scene.ui.layout.*; @@ -129,12 +131,16 @@ public class UI extends SceneModule{ prefs.checkPref("noshadows", "Disable shadows", false); prefs.hidden(()->{ - GameState.set(State.playing); + if(!GameState.is(State.menu)){ + GameState.set(State.playing); + } }); prefs.shown(()->{ - GameState.set(State.paused); - menu.hide(); + if(!GameState.is(State.menu)){ + GameState.set(State.paused); + menu.hide(); + } }); keys = new KeybindDialog(); @@ -214,7 +220,7 @@ public class UI extends SceneModule{ player.recipe = null; } }); - add(button).fill().height(54).padTop(-10).units(Unit.dp); + add(button).fill().height(54).padRight(-0.1f).padTop(-10).units(Unit.dp); button.getImageCell().size(40).padBottom(4).units(Unit.dp); group.add(button); @@ -244,7 +250,7 @@ public class UI extends SceneModule{ //image.setDisabled(!has); image.setChecked(player.recipe == r); //image.setTouchable(has ? Touchable.enabled : Touchable.disabled); - image.getImage().setColor(has ? Color.WHITE : Color.GRAY); + image.getImage().setColor(has ? Color.WHITE : Color.DARK_GRAY); }); if(i % rows == rows-1) @@ -306,7 +312,6 @@ public class UI extends SceneModule{ }); new imagebutton("icon-pause", isize, ()->{ - //TODO pause GameState.set(GameState.is(State.paused) ? State.playing : State.paused); }){{ get().update(()->{ @@ -333,7 +338,7 @@ public class UI extends SceneModule{ atop(); new table("pane"){{ - new label("[orange]< paused >").scale(0.75f).pad(6); + new label("[orange]< paused >").scale(Unit.dp.inPixels(0.75f)).pad(6).units(Unit.dp); }}.end(); }}.end(); @@ -419,12 +424,14 @@ public class UI extends SceneModule{ }}.end(); } + //respawn background table new table("white"){{ respawntable = get(); respawntable.setColor(Color.CLEAR); }}.end(); + //respawn table new table(){{ new table("pane"){{ @@ -435,7 +442,55 @@ public class UI extends SceneModule{ }}.end(); }}.end(); + if(android){ + //placement table + new table(){{ + visible(()->player.recipe != null); + abottom(); + aleft(); + + + new table("pane"){{ + new label(()->"Placement Mode: [orange]" + AndroidInput.mode.name()).pad(4).units(Unit.dp); + row(); + + aleft(); + + new table(){{ + aleft(); + ButtonGroup group = new ButtonGroup<>(); + + defaults().size(58, 62).pad(6).units(Unit.dp); + + for(PlaceMode mode : PlaceMode.values()){ + new imagebutton("icon-" + mode.name(), "toggle", Unit.dp.inPixels(10*3), ()->{ + AndroidInput.mode = mode; + }){{ + group.add(get()); + }}; + } + + new imagebutton("icon-cancel", Unit.dp.inPixels(14*3), ()->{ + player.recipe = null; + }).visible(()->player.recipe != null && AndroidInput.mode == PlaceMode.touch); + + new imagebutton("icon-rotate-arrow", Unit.dp.inPixels(14*3), ()->{ + player.rotation ++; + player.rotation %= 4; + }).update(i->{ + i.getImage().setOrigin(Align.center); + i.getImage().setRotation(player.rotation*90); + }).visible(()->player.recipe != null && AndroidInput.mode == PlaceMode.touch + && player.recipe.result.rotate); + + }}.left().end(); + }}.end(); + }}.end(); + + } + loadingtable = new table("loadDim"){{ + get().setTouchable(Touchable.enabled); new table("button"){{ new label("[orange]Loading..."){{ get().setName("namelabel"); @@ -449,20 +504,22 @@ public class UI extends SceneModule{ tools.addIButton("icon-cancel", Unit.dp.inPixels(42), ()->{ player.recipe = null; }); + tools.addIButton("icon-rotate", Unit.dp.inPixels(42), ()->{ - player.rotation++; - + player.rotation ++; player.rotation %= 4; }); + tools.addIButton("icon-check", Unit.dp.inPixels(42), ()->{ AndroidInput.place(); }); scene.add(tools); - tools.setVisible(()->{ - return !GameState.is(State.menu) && android && player.recipe != null && Inventory.hasItems(player.recipe.requirements); - }); + tools.setVisible(()-> + !GameState.is(State.menu) && android && player.recipe != null && Inventory.hasItems(player.recipe.requirements) && + AndroidInput.mode == PlaceMode.cursor + ); tools.update(()->{ tools.setPosition(AndroidInput.mousex, Gdx.graphics.getHeight()-AndroidInput.mousey-15*Core.cameraScale, Align.top); @@ -578,6 +635,16 @@ public class UI extends SceneModule{ }); } + public void showError(String text){ + new Dialog("[crimson]An error has occured", "dialog"){{ + content().pad(Unit.dp.inPixels(15)); + content().add(text); + getButtonTable().addButton("OK", ()->{ + hide(); + }).size(90, 50).pad(4).units(Unit.dp); + }}.show(); + } + public void showLoading(){ showLoading("[orange]Loading.."); } diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index 7fad0ae946..7da5cbb7f1 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -14,7 +14,7 @@ public class Vars{ //respawn time in frames public static final float respawnduration = 60*4; //time between waves in frames - public static final float wavespace = 35*60*(android ? 2 : 1); + public static final float wavespace = 25*60*(android ? 1 : 1); //waves can last no longer than 6 minutes, otherwise the next one spawns public static final float maxwavespace = 60*60*6; //how far away from spawn points the player can't place blocks diff --git a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java index d338075034..133b58ea97 100644 --- a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java +++ b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java @@ -47,7 +47,7 @@ public class Enemy extends DestructibleEntity{ hitsize = 5; - maxhealth = 30; + maxhealth = 50; heal(); } diff --git a/core/src/io/anuke/mindustry/entities/enemies/FastEnemy.java b/core/src/io/anuke/mindustry/entities/enemies/FastEnemy.java index 276a842fc6..1922a421d7 100644 --- a/core/src/io/anuke/mindustry/entities/enemies/FastEnemy.java +++ b/core/src/io/anuke/mindustry/entities/enemies/FastEnemy.java @@ -8,7 +8,7 @@ public class FastEnemy extends Enemy{ speed = 0.7f; reload = 30; - maxhealth = 20; + maxhealth = 30; heal(); } diff --git a/core/src/io/anuke/mindustry/entities/enemies/TankEnemy.java b/core/src/io/anuke/mindustry/entities/enemies/TankEnemy.java index a995e350f9..52a95bbb59 100644 --- a/core/src/io/anuke/mindustry/entities/enemies/TankEnemy.java +++ b/core/src/io/anuke/mindustry/entities/enemies/TankEnemy.java @@ -1,6 +1,9 @@ package io.anuke.mindustry.entities.enemies; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.BulletType; +import io.anuke.ucore.util.Angles; public class TankEnemy extends Enemy{ @@ -9,8 +12,19 @@ public class TankEnemy extends Enemy{ maxhealth = 400; speed = 0.2f; - reload = 140f; - bullet = BulletType.iron; + reload = 90f; + bullet = BulletType.small; + } + + void shoot(){ + vector.set(length, 0).rotate(direction.angle()); + + Angles.shotgun(3, 4f, direction.angle(), f->{ + Bullet out = new Bullet(bullet, this, x+vector.x, y+vector.y, f).add(); + out.damage = (int)(damage*Vars.multiplier); + }); + + } } diff --git a/core/src/io/anuke/mindustry/input/AndroidInput.java b/core/src/io/anuke/mindustry/input/AndroidInput.java index 15e5fce27a..78bfadbb40 100644 --- a/core/src/io/anuke/mindustry/input/AndroidInput.java +++ b/core/src/io/anuke/mindustry/input/AndroidInput.java @@ -18,6 +18,8 @@ import io.anuke.ucore.util.Mathf; public class AndroidInput extends InputAdapter{ public static float mousex, mousey; + public static PlaceMode mode = PlaceMode.cursor; + public static boolean brokeBlock = false; private static float lmousex, lmousey; private static float warmup; private static float warmupDelay = 20; @@ -30,6 +32,12 @@ public class AndroidInput extends InputAdapter{ return false; } + @Override + public boolean touchUp (int screenX, int screenY, int pointer, int button) { + brokeBlock = false; + return false; + } + @Override public boolean touchDown (int screenX, int screenY, int pointer, int button) { ui.hideTooltip(); @@ -49,7 +57,9 @@ public class AndroidInput extends InputAdapter{ public static void breakBlock(){ Tile tile = selected(); player.breaktime += Timers.delta(); + if(player.breaktime >= tile.block().breaktime){ + brokeBlock = true; if(tile.block().drops != null){ Inventory.addItem(tile.block().drops.item, tile.block().drops.amount); } @@ -92,9 +102,11 @@ public class AndroidInput extends InputAdapter{ public static void doInput(){ if(Gdx.input.isTouched(0) && Mathf.near2d(lmousex, lmousey, Gdx.input.getX(0), Gdx.input.getY(0), 50) - && !ui.hasMouse() && player.recipe == null){ + && !ui.hasMouse() /*&& (player.recipe == null || mode == PlaceMode.touch)*/){ warmup += Timers.delta(); + float lx = mousex, ly = mousey; + mousex = Gdx.input.getX(0); mousey = Gdx.input.getY(0); @@ -110,6 +122,9 @@ public class AndroidInput extends InputAdapter{ player.breaktime = 0; } } + + mousex = lx; + mousey = ly; }else{ warmup = 0; lmousex = Gdx.input.getX(0); diff --git a/core/src/io/anuke/mindustry/input/GestureHandler.java b/core/src/io/anuke/mindustry/input/GestureHandler.java index 22e2af2dd7..2dca30f2cb 100644 --- a/core/src/io/anuke/mindustry/input/GestureHandler.java +++ b/core/src/io/anuke/mindustry/input/GestureHandler.java @@ -6,13 +6,9 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.input.GestureDetector.GestureAdapter; import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.GameState; -import io.anuke.mindustry.GameState.State; import io.anuke.mindustry.Inventory; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.World; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.core.*; +import io.anuke.mindustry.Vars; +import io.anuke.ucore.core.Core; import io.anuke.ucore.scene.ui.layout.Unit; public class GestureHandler extends GestureAdapter{ @@ -23,6 +19,7 @@ public class GestureHandler extends GestureAdapter{ @Override public boolean longPress(float x, float y){ + /* Tile tile = World.cursorTile(); player.breaktime += Timers.delta(); if(!GameState.is(State.menu) && player.breaktime >= tile.block().breaktime){ @@ -31,16 +28,28 @@ public class GestureHandler extends GestureAdapter{ tile.setBlock(Blocks.air); player.breaktime = 0f; Sounds.play("break"); + }*/ + return false; + } + + @Override + public boolean tap (float x, float y, int count, int button) { + if(AndroidInput.mode == PlaceMode.touch && !ui.hasMouse() && player.recipe != null && + Inventory.hasItems(player.recipe.requirements) && !Vars.ui.hasMouse() && !AndroidInput.brokeBlock){ + AndroidInput.mousex = x; + AndroidInput.mousey = y; + AndroidInput.place(); + return true; } return false; } @Override public boolean pan(float x, float y, float deltaX, float deltaY){ - if(player.recipe == null || !Inventory.hasItems(player.recipe.requirements)){ + if(player.recipe == null || !Inventory.hasItems(player.recipe.requirements) || AndroidInput.mode == PlaceMode.touch){ player.x -= deltaX*Core.camera.zoom/Core.cameraScale; player.y += deltaY*Core.camera.zoom/Core.cameraScale; - }else{ + }else if(AndroidInput.mode == PlaceMode.cursor){ AndroidInput.mousex += deltaX; AndroidInput.mousey += deltaY; } diff --git a/core/src/io/anuke/mindustry/input/PlaceMode.java b/core/src/io/anuke/mindustry/input/PlaceMode.java new file mode 100644 index 0000000000..b180aa3c55 --- /dev/null +++ b/core/src/io/anuke/mindustry/input/PlaceMode.java @@ -0,0 +1,5 @@ +package io.anuke.mindustry.input; + +public enum PlaceMode{ + cursor, touch; +} diff --git a/core/src/io/anuke/mindustry/resource/Weapon.java b/core/src/io/anuke/mindustry/resource/Weapon.java index 3e9cc9453e..afa490a9b0 100644 --- a/core/src/io/anuke/mindustry/resource/Weapon.java +++ b/core/src/io/anuke/mindustry/resource/Weapon.java @@ -4,6 +4,7 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; +import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.Player; @@ -72,6 +73,7 @@ public enum Weapon{ float ang = mouseAngle(p); bullet(p, p.x, p.y, ang); + Effects.effect("railshoot", p.x + vector.x, p.y+vector.y); } }, mortar(100, BulletType.shell, "Shoots a slow, but damaging shell.", stack(Item.titanium, 40), stack(Item.steel, 60)){ @@ -80,6 +82,8 @@ public enum Weapon{ public void shoot(Player p){ float ang = mouseAngle(p); bullet(p, p.x, p.y, ang); + Effects.effect("mortarshoot", p.x + vector.x, p.y+vector.y); + Effects.shake(2f, 2f, Vars.player); } }; public float reload; diff --git a/core/src/io/anuke/mindustry/ui/LoadDialog.java b/core/src/io/anuke/mindustry/ui/LoadDialog.java index 1454c9ae06..1fb0039a62 100644 --- a/core/src/io/anuke/mindustry/ui/LoadDialog.java +++ b/core/src/io/anuke/mindustry/ui/LoadDialog.java @@ -16,52 +16,58 @@ public class LoadDialog extends Dialog{ public LoadDialog() { super("Load Game"); setup(); - - shown(()->{ + + shown(() -> { setup(); }); - - getButtonTable().addButton("Back", ()->{ + + getButtonTable().addButton("Back", () -> { hide(); }).pad(2).size(180, 44).units(Unit.dp); } - + private void setup(){ content().clear(); - + content().add("Select a save slot.").padBottom(2); content().row(); - - for(int i = 0; i < Vars.saveSlots; i ++){ + + for(int i = 0; i < Vars.saveSlots; i++){ final int slot = i; - - TextButton button = new TextButton("[orange]Slot " + (i+1)); + + TextButton button = new TextButton("[orange]Slot " + (i + 1)); button.getLabelCell().top().left().growX(); button.row(); button.pad(Unit.dp.inPixels(10)); button.add("[gray]" + (!SaveIO.isSaveValid(i) ? "" : "Last Saved: " + SaveIO.getTimeString(i))).padBottom(2); - button.getLabel().setFontScale(0.75f); - button.setDisabled(!SaveIO.isSaveValid(i) ); - - button.clicked(()->{ + button.getLabel().setFontScale(Unit.dp.inPixels(0.75f)); + button.setDisabled(!SaveIO.isSaveValid(i)); + + button.clicked(() -> { if(!button.isDisabled()){ Vars.ui.showLoading(); - + Timer.schedule(new Task(){ + @Override public void run(){ - SaveIO.loadFromSlot(slot); Vars.ui.hideLoading(); hide(); + try{ + SaveIO.loadFromSlot(slot); + }catch(Exception e){ + Vars.ui.showError("[orange]Save file corrupted or invalid!"); + return; + } Vars.ui.hideMenu(); GameState.set(State.playing); } - }, 2f/60f); + }, 3f/60f); } }); - + content().add(button).size(400, 78).units(Unit.dp).pad(2); content().row(); } - + } } diff --git a/core/src/io/anuke/mindustry/ui/SaveDialog.java b/core/src/io/anuke/mindustry/ui/SaveDialog.java index 04a5c50bca..ec3df63741 100644 --- a/core/src/io/anuke/mindustry/ui/SaveDialog.java +++ b/core/src/io/anuke/mindustry/ui/SaveDialog.java @@ -40,7 +40,7 @@ public class SaveDialog extends Dialog{ button.row(); button.pad(Unit.dp.inPixels(10)); button.add((!SaveIO.isSaveValid(i) ? "[gray]" : "[LIGHT_GRAY]Last Saved: " + SaveIO.getTimeString(i))).padBottom(2); - button.getLabel().setFontScale(0.75f); + button.getLabel().setFontScale(Unit.dp.inPixels(0.75f)); button.clicked(()->{ if(SaveIO.isSaveValid(slot)){ @@ -62,16 +62,21 @@ public class SaveDialog extends Dialog{ } void save(int slot){ - Vars.ui.showLoading("[yellow]Saving..."); + Vars.ui.showLoading("[orange]Saving..."); Timer.schedule(new Task(){ @Override public void run(){ - SaveIO.saveToSlot(slot); hide(); + try{ + SaveIO.saveToSlot(slot); + }catch (Exception e){ + Vars.ui.showError("[orange]Failed to save game!"); + return; + } Vars.ui.hideLoading(); } - }, 8f/60f); + }, 5f/60f); } } diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index b0aae34428..0234347db3 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -1,7 +1,6 @@ package io.anuke.mindustry.world; import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; @@ -9,8 +8,6 @@ import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.resource.ItemStack; import io.anuke.ucore.core.Draw; -import io.anuke.ucore.graphics.Caches; -import io.anuke.ucore.util.Mathf; public class Block{ private static int lastid; @@ -157,48 +154,11 @@ public class Block{ } public void drawCache(Tile tile){ - MathUtils.random.setSeed(tile.id()); - Caches.draw(vary ? (name() + MathUtils.random(1, 3)) : name(), tile.worldx(), tile.worldy()); - - for(int dx = -1; dx <= 1; dx ++){ - for(int dy = -1; dy <= 1; dy ++){ - - if(dx == 0 && dy == 0) continue; - - Tile other = World.tile(tile.x+dx, tile.y+dy); - - if(other == null) continue; - - Block floor = other.floor(); - - if(floor.id <= this.id) continue; - - TextureRegion region = Draw.hasRegion(floor.name() + "edge") ? Draw.region(floor.name() + "edge") : - Draw.region(floor.edge + "edge"); - - int sx = -dx*8+2, sy = -dy*8+2; - int x = Mathf.clamp(sx, 0, 12); - int y = Mathf.clamp(sy, 0, 12); - int w = Mathf.clamp(sx+8, 0, 12)-x, h = Mathf.clamp(sy+8, 0, 12)-y; - - float rx = Mathf.clamp(dx*8, 0, 8-w); - float ry = Mathf.clamp(dy*8, 0, 8-h); - - temp.setTexture(region.getTexture()); - temp.setRegion(region.getRegionX()+x, region.getRegionY()+y+h, w, -h); - - Caches.draw(temp, tile.worldx()-4 + rx, tile.worldy()-4 + ry, w, h); - } - } } - + public void draw(Tile tile){ - if(tile.floor() == this){ - throw new RuntimeException("Rendering non-cached tiles is disabled."); - }else{ - Draw.rect(name(), tile.worldx(), tile.worldy(), rotate ? tile.rotation * 90 : 0); - } + Draw.rect(name(), tile.worldx(), tile.worldy(), rotate ? tile.rotation * 90 : 0); } diff --git a/core/src/io/anuke/mindustry/world/blocks/Blocks.java b/core/src/io/anuke/mindustry/world/blocks/Blocks.java index e5daab6146..7f8c3e23d9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/Blocks.java +++ b/core/src/io/anuke/mindustry/world/blocks/Blocks.java @@ -4,6 +4,7 @@ import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.resource.ItemStack; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.types.Floor; import io.anuke.mindustry.world.blocks.types.Wall; public class Blocks{ @@ -11,38 +12,39 @@ public class Blocks{ air = new Block("air"){ //no drawing here + public void drawCache(Tile tile){} public void draw(Tile tile){} }, - deepwater = new Block("deepwater"){{ + deepwater = new Floor("deepwater"){{ vary = false; solid = true; }}, - water = new Block("water"){{ + water = new Floor("water"){{ vary = false; solid = true; }}, - stone = new Block("stone"){{ + stone = new Floor("stone"){{ drops = new ItemStack(Item.stone, 1); }}, - iron = new Block("iron"){{ + iron = new Floor("iron"){{ drops = new ItemStack(Item.iron, 1); }}, - coal = new Block("coal"){{ + coal = new Floor("coal"){{ drops = new ItemStack(Item.coal, 1); }}, - titanium = new Block("titanium"){{ + titanium = new Floor("titanium"){{ drops = new ItemStack(Item.titanium, 1); }}, - dirt = new Block("dirt"), + dirt = new Floor("dirt"), - grass = new Block("grass"), + grass = new Floor("grass"), stoneblock = new Block("stoneblock"){{ solid = true; diff --git a/core/src/io/anuke/mindustry/world/blocks/types/Conduit.java b/core/src/io/anuke/mindustry/world/blocks/types/Conduit.java index f4efa850ea..2adb9ab5ba 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/Conduit.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/Conduit.java @@ -26,12 +26,14 @@ public class Conduit extends Block{ ConduitEntity entity = tile.entity(); Draw.rect(name() + "bottom", tile.worldx(), tile.worldy(), tile.rotation * 90); + if(entity.liquid != null && entity.liquidAmount > 0.01f){ Draw.color(entity.liquid.color); Draw.alpha(entity.liquidAmount / liquidCapacity); Draw.rect("conduitliquid", tile.worldx(), tile.worldy(), tile.rotation * 90); Draw.color(); } + Draw.rect(name() + "top", tile.worldx(), tile.worldy(), tile.rotation * 90); } diff --git a/core/src/io/anuke/mindustry/world/blocks/types/Floor.java b/core/src/io/anuke/mindustry/world/blocks/types/Floor.java new file mode 100644 index 0000000000..68033ccc4c --- /dev/null +++ b/core/src/io/anuke/mindustry/world/blocks/types/Floor.java @@ -0,0 +1,57 @@ +package io.anuke.mindustry.world.blocks.types; + +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.math.MathUtils; + +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.World; +import io.anuke.ucore.core.Draw; +import io.anuke.ucore.graphics.Caches; +import io.anuke.ucore.util.Mathf; + +public class Floor extends Block{ + + public Floor(String name) { + super(name); + } + + @Override + public void drawCache(Tile tile){ + MathUtils.random.setSeed(tile.id()); + + Caches.draw(vary ? (name() + MathUtils.random(1, 3)) : name(), tile.worldx(), tile.worldy()); + + for(int dx = -1; dx <= 1; dx ++){ + for(int dy = -1; dy <= 1; dy ++){ + + if(dx == 0 && dy == 0) continue; + + Tile other = World.tile(tile.x+dx, tile.y+dy); + + if(other == null) continue; + + Block floor = other.floor(); + + if(floor.id <= this.id) continue; + + TextureRegion region = Draw.hasRegion(floor.name() + "edge") ? Draw.region(floor.name() + "edge") : + Draw.region(floor.edge + "edge"); + + int sx = -dx*8+2, sy = -dy*8+2; + int x = Mathf.clamp(sx, 0, 12); + int y = Mathf.clamp(sy, 0, 12); + int w = Mathf.clamp(sx+8, 0, 12)-x, h = Mathf.clamp(sy+8, 0, 12)-y; + + float rx = Mathf.clamp(dx*8, 0, 8-w); + float ry = Mathf.clamp(dy*8, 0, 8-h); + + temp.setTexture(region.getTexture()); + temp.setRegion(region.getRegionX()+x, region.getRegionY()+y+h, w, -h); + + Caches.draw(temp, tile.worldx()-4 + rx, tile.worldy()-4 + ry, w, h); + } + } + } + +} diff --git a/desktop/mindustry-saves/0.mins b/desktop/mindustry-saves/0.mins index 4c0edb10d6e6b1949b6e5b32f0a387e0f743e32b..da7b8173529a0de6d776f5b78a9627dfbb42a8a4 100644 GIT binary patch literal 1140 zcmZXU%TE(g7{$NY5EncO&Xi(tm?BM^aSF9H(=;|lp<>h(F(fPqEL0u~*t6rmU}IuT zAi9wiF+L&>g9LQpBOwM|5ECCEaqpJz+?>0poyG0%{?3_i9!)DEEh5g;tK-83QMgfV zJF1@%8F{j;Te&X|KJr-fSQ^zud*KRpog#%EK!->zO}O~bLLZ^{h;Ye5HGt96doqkl zj|u(#fP|*2G`RtqV2$8EWi(_VAHnFrs6dFHvE!~2dW(eAhK1O|b3*b3K<5N6t$7X5 z`w6bl_%(ob{5JrN&R(U74XCMZ(s*0!v|J+$ZWBtz=$!nHHP;F5TSqC29p1b*jO}?W z*y8X^BLwE*&Ebk9he0|tF#^EN?KaGE>rJy<(`X$3U(1ny%X(2$#5eBXeMfa&aEZb$ zKrdRdP%)93iZR+LzGJ6s!QE@PF>kc##yrz#e#q1wXL&aX(LSN;0D#B7MN=`L=G=Qm zy|)>4e<1YTVbuQ7Mh5h(>L;4cF?L)F+0Qio7j`b(CFCq*#@WdnGAe&1L=%isM>K8U zXwnAs^|gOz6ifp2ch+&tPI8*y{~&}Ga`#}gocPJ8^#MQ!rhWl5iVdhq|E9?qP;+?( nMxQ-9%V_8iP3s&$I|BxEPH0Uy&nP}&x%Afp{#kHVZPCCf2bW#G literal 1237 zcmZ9KTWb?h6oq%xqM%JOnHlO#lFr05AW52u>me&?OjHtdyLXegdqpD zd79`xVd(@Rc#r|5x?7DWoX~Boi*6+&)md08+2xo;P{~#PDbH7nz{i7uG!_H-Guggc1E3v_b`e-FseAy`p9TxFQei diff --git a/desktop/mindustry-saves/1.mins b/desktop/mindustry-saves/1.mins index 3e02882e4476cb8982387615a24c42bd01c5383f..2b3704d019c127241d7ad7ea457251639042ac0a 100644 GIT binary patch literal 1357 zcmZXT$!k+l6voesZK`cLnU^Mc8QP?2OrwcYLyLk+bL+5@I&H+DR##42rB))iapA5Y zs3-_hSBhvU8pWYiP*E37SQQ0vpeX2Hpy%9tH&T+t&F_5YJKuTtkr0ua$SRaP2X7Ej zjVcvM%_Yi@-Q$w>kb4eAPZTXVYUHSugXD0PiJIA+jt!_|GC<47Qto$7rE;mk>APw; zyI=Pxzra6H>9I56sZ=htiAWtid03nl<+Fv|YGiDbtEgqEx~t=AWZsl4y`_?3soKF`m8zUM(wswP_n%$pNv6AJd!^#cZ}Zgf=<#x<6+gwWk0|5kZ@FWS zf!J9n$q*oApzE9{Ekr2;*)Tl41~TWN^j#2WC0f)Wz*8$?fWC_Y+zNII@b*9g&~^#X zsez{=Nhlo#5|>5c@nlMXJ<)D}lNR8>&=r7pF`)IDX)OnYuLD|o0KN%8z(DXO6#p#& z-qlnB1a1Q&cLex(ysH3?27ZioqtvcOt5*Z(q}M=++!tWQeqb?lNhKHa^-CF%L*G** z?a+5jiLO63QE$HxN2Kxs{3y9eD0Nd%yi>E%ZB>ab WPQ#4xXkexBfG7<0htO*Ht(AZD|A9FK&bT}63ahZLJ45TTuodW{*rhO&2)u}Bm;QzRgrI{L zJ&65)2#V;EMo?{L(J2Z@HQ!$QzR1ve z_~4|KFlOSJ1dFDNe~0A4n7<Eout*wH%>W9kfTlvtSF;8P1BR3_MTLq0&9QRm zMC*Vt0V>z?SQT#|Dn0^)j}h5VV6smEkwQ>XGYm*S2MoPnu#Cx8V9GBUXwwlf6j`qj z*$SRQH9|QPqvO8@#A0Yp?hQ$gX@voW-YXnY zCnZ43FRJN#k0^H)P!gby|A7s`xsQmPHXk(~8lSd1&R3pZNycZySvXE#ykn)!Kto~y zGRaSf$`gPK+nr;bR_9npvVWWWm${O;^p`Btq(Y0w(N2TgfRaL0p{AYD&*(TioqM~J zoqM~I=_6$)vBcj2G}Jg>VEU&R=plUpx|zI2p+*&r3SY4*R&b}WJarA=eM4vLI!xvU zOfUlz{(xx+(C+7NYWXK1t0p~*a_SZ$dlx2}gSjMz=Jf5cp?A-_jZSa}o%}vbQw%++ zJ&(vc09c2BoB+)kTwp`cf5bqmx{m>Y07Zo*HU!ZxL_N!ZUIDtXx|;f3j>wcx&>8&= SNZbQ-{{hs+P&skR#ySK0Ok>;t diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java index 1ccc698348..21e0476c8a 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java @@ -16,7 +16,7 @@ public class DesktopLauncher { Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); config.setTitle("Mindustry"); config.setMaximized(true); - //config.useVsync(false); + config.useVsync(false); config.setWindowedMode(800, 600); config.setWindowIcon("sprites/icon.png");