diff --git a/core/assets/shaders/hit.fragment b/core/assets/shaders/fullmix.fragment similarity index 74% rename from core/assets/shaders/hit.fragment rename to core/assets/shaders/fullmix.fragment index 756f47ef52..00ff3997a2 100644 --- a/core/assets/shaders/hit.fragment +++ b/core/assets/shaders/fullmix.fragment @@ -4,6 +4,7 @@ precision mediump int; #endif uniform sampler2D u_texture; +uniform vec4 u_color; varying vec4 v_color; varying vec2 v_texCoord; @@ -12,7 +13,7 @@ void main() { vec4 c = texture2D(u_texture, v_texCoord.xy); - c = mix(c, vec4(1.0, 1.0, 1.0, c.a), v_color.a); + c = mix(c, vec4(v_color.r, v_color.g, v_color.b, c.a), v_color.a); gl_FragColor = c * vec4(v_color.rgb, 1.0); } diff --git a/core/assets/shaders/mix.fragment b/core/assets/shaders/mix.fragment new file mode 100644 index 0000000000..4a9ea9c64b --- /dev/null +++ b/core/assets/shaders/mix.fragment @@ -0,0 +1,19 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D u_texture; +uniform vec4 u_color; + +varying vec4 v_color; +varying vec2 v_texCoord; + +void main() { + + vec4 c = texture2D(u_texture, v_texCoord.xy); + + c = mix(c, vec4(u_color.r, u_color.g, u_color.b, c.a), v_color.a); + + gl_FragColor = c * vec4(v_color.rgb, 1.0); +} diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index ec718622f4..59b1806e74 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -161,14 +161,15 @@ public class Vars{ unitGroups[team.ordinal()] = Entities.addGroup(BaseUnit.class).enableMapping(); } - mobile = (Gdx.app.getType() == ApplicationType.Android) || - Gdx.app.getType() == ApplicationType.iOS || testMobile; + mobile = Gdx.app.getType() == ApplicationType.Android || Gdx.app.getType() == ApplicationType.iOS || testMobile; ios = Gdx.app.getType() == ApplicationType.iOS; android = Gdx.app.getType() == ApplicationType.Android; gwt = Gdx.app.getType() == ApplicationType.WebGL; - customMapDirectory = OS.getAppDataDirectory("Mindustry").child("maps/"); - saveDirectory = OS.getAppDataDirectory("Mindustry").child("saves/"); + if(!gwt) { + customMapDirectory = OS.getAppDataDirectory("Mindustry").child("maps/"); + saveDirectory = OS.getAppDataDirectory("Mindustry").child("saves/"); + } fontScale = Math.max(Unit.dp.scl(1f)/2f, 0.5f); baseCameraScale = Math.round(Unit.dp.scl(4)); diff --git a/core/src/io/anuke/mindustry/content/fx/Fx.java b/core/src/io/anuke/mindustry/content/fx/Fx.java index 907950bb05..072cfff290 100644 --- a/core/src/io/anuke/mindustry/content/fx/Fx.java +++ b/core/src/io/anuke/mindustry/content/fx/Fx.java @@ -12,7 +12,7 @@ import io.anuke.ucore.util.Angles; import static io.anuke.mindustry.Vars.tilesize; public class Fx implements ContentList { - public static Effect none, placeBlock, breakBlock, smoke, spawn; + public static Effect none, placeBlock, breakBlock, smoke, spawn, tapBlock; @Override public void load() { @@ -23,19 +23,24 @@ public class Fx implements ContentList { placeBlock = new Effect(16, e -> { Draw.color(Palette.accent); Lines.stroke(3f - e.fin() * 2f); - Lines.square(e.x, e.y, tilesize / 2f * (float) (e.data) + e.fin() * 3f); + Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f); + Draw.reset(); + }); + + tapBlock = new Effect(12, e -> { + Draw.color(Palette.accent); + Lines.stroke(3f - e.fin() * 2f); + Lines.circle(e.x, e.y, 4f + (tilesize/1.5f * e.rotation) * e.fin()); Draw.reset(); }); breakBlock = new Effect(12, e -> { - float data = (float) (e.data); - Draw.color(Palette.remove); Lines.stroke(3f - e.fin() * 2f); - Lines.square(e.x, e.y, tilesize / 2f * data + e.fin() * 3f); + Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f); - Angles.randLenVectors(e.id, 3 + (int) (data * 3), data * 2f + (tilesize * data) * e.finpow(), (x, y) -> { - Fill.square(e.x + x, e.y + y, 1f + e.fout() * (3f + data)); + Angles.randLenVectors(e.id, 3 + (int) (e.rotation * 3), e.rotation * 2f + (tilesize * e.rotation) * e.finpow(), (x, y) -> { + Fill.square(e.x + x, e.y + y, 1f + e.fout() * (3f + e.rotation)); }); Draw.reset(); }); diff --git a/core/src/io/anuke/mindustry/core/ContentLoader.java b/core/src/io/anuke/mindustry/core/ContentLoader.java index f5cd495f8f..208fc83b35 100644 --- a/core/src/io/anuke/mindustry/core/ContentLoader.java +++ b/core/src/io/anuke/mindustry/core/ContentLoader.java @@ -25,12 +25,6 @@ public class ContentLoader { //liquids new Liquids(), - //ammotypes - new AmmoTypes(), - - //mechs - new Mechs(), - //bullets new ArtilleryBullets(), new FlakBullets(), @@ -39,6 +33,12 @@ public class ContentLoader { new StandardBullets(), new TurretBullets(), + //ammotypes + new AmmoTypes(), + + //mechs + new Mechs(), + //units new UnitTypes(), diff --git a/core/src/io/anuke/mindustry/core/Renderer.java b/core/src/io/anuke/mindustry/core/Renderer.java index 26dae2481e..d6d16587df 100644 --- a/core/src/io/anuke/mindustry/core/Renderer.java +++ b/core/src/io/anuke/mindustry/core/Renderer.java @@ -225,6 +225,8 @@ public class Renderer extends RendererModule{ blocks.skipLayer(Layer.turret); blocks.drawBlocks(Layer.laser); + overlays.drawBottom(); + Entities.drawWith(playerGroup, p -> true, Player::drawBuildRequests); drawAllTeams(true); @@ -233,7 +235,7 @@ public class Renderer extends RendererModule{ Entities.draw(airItemGroup); Entities.draw(effectGroup); - overlays.draw(); + overlays.drawTop(); if(pixelate) Graphics.flushSurface(); @@ -257,7 +259,7 @@ public class Renderer extends RendererModule{ Shaders.outline.color.set(team.color); Graphics.beginShaders(Shaders.outline); - Graphics.shader(Shaders.hit, false); + Graphics.shader(Shaders.mix, false); Entities.draw(unitGroups[team.ordinal()], u -> u.isFlying() == flying); Entities.draw(playerGroup, p -> p.isFlying() == flying && p.team == team); Graphics.shader(); diff --git a/core/src/io/anuke/mindustry/entities/BlockBuilder.java b/core/src/io/anuke/mindustry/entities/BlockBuilder.java index dc3ed3e184..8dc043c718 100644 --- a/core/src/io/anuke/mindustry/entities/BlockBuilder.java +++ b/core/src/io/anuke/mindustry/entities/BlockBuilder.java @@ -30,6 +30,20 @@ public interface BlockBuilder { /**Returns the queue for storing build requests.*/ Queue getPlaceQueue(); + /**If a place request matching this signature is present, it is removed. + * Otherwise, a new place request is added to the queue.*/ + default void replaceBuilding(int x, int y, int rotation, Recipe recipe){ + for(BuildRequest request : getPlaceQueue()){ + if(request.x == x && request.y == y){ + clearBuilding(); + addBuildRequest(request); + return; + } + } + + addBuildRequest(new BuildRequest(x, y, rotation, recipe)); + } + /**Clears the placement queue.*/ default void clearBuilding(){ getPlaceQueue().clear(); diff --git a/core/src/io/anuke/mindustry/entities/Player.java b/core/src/io/anuke/mindustry/entities/Player.java index 36454470e1..11058af381 100644 --- a/core/src/io/anuke/mindustry/entities/Player.java +++ b/core/src/io/anuke/mindustry/entities/Player.java @@ -245,9 +245,12 @@ public class Player extends Unit implements BlockBuilder { Draw.tscl(fontScale); } + /**Draw all current build requests. Does not draw the beam effect, only the positions.*/ public void drawBuildRequests(){ for (BuildRequest request : getPlaceQueue()) { + if(request.remove){ + //draw removal request Draw.color(Palette.remove); Draw.alpha(0.4f); Lines.stroke(1f); @@ -270,11 +273,17 @@ public class Player extends Unit implements BlockBuilder { Lines.poly(tile.drawx(), tile.drawy(), 4, tile.block().size * tilesize /2f * (1f-progress) + Mathf.absin(Timers.time(), 3f, 1f)); }else{ + //draw place request Draw.color(Palette.accent); Lines.stroke((1f-request.progress)); + Lines.poly(request.x * tilesize + request.recipe.result.offset(), request.y * tilesize + request.recipe.result.offset(), 4, request.recipe.result.size * tilesize /2f + Mathf.absin(Timers.time(), 3f, 1f)); + + Lines.poly(request.x * tilesize + request.recipe.result.offset(), + request.y * tilesize + request.recipe.result.offset(), + 4, request.recipe.result.size * tilesize /2f + 2f, 45 + 15); } } diff --git a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java index 430b57a59c..39ace9e4df 100644 --- a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java +++ b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java @@ -15,17 +15,27 @@ import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Inputs; import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Timers; -import io.anuke.ucore.graphics.CapStyle; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Fill; -import io.anuke.ucore.graphics.Lines; +import io.anuke.ucore.graphics.*; import io.anuke.ucore.util.Mathf; import static io.anuke.mindustry.Vars.*; public class OverlayRenderer { - public void draw(){ + public void drawBottom(){ + for(Player player : players) { + InputHandler input = control.input(player.playerIndex); + + Shaders.outline.color.set(Palette.accent); + Graphics.beginShaders(Shaders.outline); + + input.drawBottom(); + + Graphics.endShaders(); + } + } + + public void drawTop(){ for(Player player : players) { @@ -37,7 +47,7 @@ public class OverlayRenderer { tile.block().drawConfigure(tile); } - input.draw(); + input.drawTop(); Draw.reset(); diff --git a/core/src/io/anuke/mindustry/graphics/Palette.java b/core/src/io/anuke/mindustry/graphics/Palette.java index 2f80016914..476ddc2fbb 100644 --- a/core/src/io/anuke/mindustry/graphics/Palette.java +++ b/core/src/io/anuke/mindustry/graphics/Palette.java @@ -43,7 +43,7 @@ public class Palette { public static final Color place = Color.valueOf("6335f8"); public static final Color remove = Color.valueOf("e55454"); public static final Color placeRotate = Color.ORANGE; - public static final Color breakInvalid = Color.SCARLET; + public static final Color breakInvalid = Color.valueOf("d44b3d"); public static final Color range = Color.valueOf("f4ba6e"); public static final Color power = Color.valueOf("fbd367"); } diff --git a/core/src/io/anuke/mindustry/graphics/Shaders.java b/core/src/io/anuke/mindustry/graphics/Shaders.java index c71c64b1c9..ca56b6be10 100644 --- a/core/src/io/anuke/mindustry/graphics/Shaders.java +++ b/core/src/io/anuke/mindustry/graphics/Shaders.java @@ -22,7 +22,8 @@ public class Shaders{ public static SurfaceShader oil; public static Space space; public static UnitBuild build; - public static Shader hit; + public static MixShader mix; + public static Shader fullMix; public static void init(){ outline = new Outline(); @@ -34,7 +35,22 @@ public class Shaders{ oil = new SurfaceShader("oil"); space = new Space(); build = new UnitBuild(); - hit = new Shader("hit", "default"); + mix = new MixShader(); + fullMix = new Shader("fullmix", "default"); + } + + public static class MixShader extends Shader{ + public Color color = new Color(Color.WHITE); + + public MixShader(){ + super("mix", "default"); + } + + @Override + public void apply() { + super.apply(); + shader.setUniformf("u_color", color); + } } public static class Space extends SurfaceShader{ diff --git a/core/src/io/anuke/mindustry/input/AndroidInput.java b/core/src/io/anuke/mindustry/input/AndroidInput.java index 7ee9e674a0..3bd0111b57 100644 --- a/core/src/io/anuke/mindustry/input/AndroidInput.java +++ b/core/src/io/anuke/mindustry/input/AndroidInput.java @@ -1,17 +1,27 @@ package io.anuke.mindustry.input; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.input.GestureDetector; import com.badlogic.gdx.input.GestureDetector.GestureListener; import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; +import io.anuke.mindustry.content.fx.Fx; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.graphics.Palette; +import io.anuke.mindustry.graphics.Shaders; +import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult; +import io.anuke.mindustry.input.PlaceUtils.NormalizeResult; import io.anuke.mindustry.type.Recipe; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.types.BuildBlock; +import io.anuke.mindustry.world.blocks.types.BuildBlock.BuildEntity; import io.anuke.ucore.core.Core; +import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Inputs; import io.anuke.ucore.graphics.Draw; @@ -27,20 +37,37 @@ import static io.anuke.mindustry.Vars.*; public class AndroidInput extends InputHandler implements GestureListener{ private static Rectangle r1 = new Rectangle(), r2 = new Rectangle(); + /**Maximum line length.*/ + private static final int maxLength = 100; + /**Maximum speed the player can pan.*/ + private static final float maxPanSpeed = 1.3f; + /**Distance to edge of screen to start panning.*/ + private final float edgePan = Unit.dp.scl(60f); + //gesture data private Vector2 pinch1 = new Vector2(-1, -1), pinch2 = pinch1.cpy(); private Vector2 vector = new Vector2(); private float initzoom = -1; private boolean zoomed = false; + /**Position where the player started dragging a line.*/ + private int lineStartX, lineStartY; + + /**Animation scale for line.*/ + private float lineScale; + /**List of currently selected tiles to place.*/ private Array placement = new Array<>(); + /**Place requests to be removed.*/ + private Array removals = new Array<>(); /**Whether or not the player is currently shifting all placed tiles.*/ private boolean selecting; + /**Whether the player is currently in line-place mode.*/ + private boolean lineMode; public AndroidInput(Player player){ super(player); - Inputs.addProcessor(new GestureDetector(20, 0.5f, 2, 0.15f, this)); + Inputs.addProcessor(new GestureDetector(20, 0.5f, 0.4f, 0.15f, this)); } /**Returns whether this tile is in the list of requests, or at least colliding with one.*/ @@ -88,6 +115,32 @@ public class AndroidInput extends InputHandler implements GestureListener{ return null; } + Tile tileAt(float x, float y){ + Vector2 vec = Graphics.world(x, y); + return world.tileWorld(vec.x, vec.y); + } + + void removeRequest(PlaceRequest request){ + placement.removeValue(request, true); + removals.add(request); + } + + void drawRequest(PlaceRequest request){ + Tile tile = request.tile(); + + float offset = request.recipe.result.offset(); + TextureRegion[] regions = request.recipe.result.getBlockIcon(); + + Draw.alpha(Mathf.clamp((1f - request.scale) / 0.5f)); + Draw.tint(Color.WHITE, Palette.breakInvalid, request.redness); + + for(TextureRegion region : regions){ + Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset, + region.getRegionWidth() * request.scale, region.getRegionHeight() * request.scale, + request.recipe.result.rotate ? request.rotation * 90 : 0); + } + } + @Override public void buildUI(Group group) { @@ -97,10 +150,7 @@ public class AndroidInput extends InputHandler implements GestureListener{ defaults().size(60f); //Add a cancel button, which clears the selection. - new imagebutton("icon-cancel", 14*2f, () -> { - placement.clear(); - selecting = false; - }); + new imagebutton("icon-cancel", 14*2f, () -> recipe = null); //Add an accept button, which places everything. new imagebutton("icon-check", 14*2f, () -> { @@ -114,22 +164,83 @@ public class AndroidInput extends InputHandler implements GestureListener{ } } + removals.addAll(placement); placement.clear(); selecting = false; }); - }}.visible(this::isPlacing).end(); + }}.visible(() -> isPlacing() && placement.size > 0).end(); } @Override - public void draw(){ - Draw.color(Palette.accent); + public void drawBottom(){ - //Draw all placement requests as squares + Shaders.mix.color.set(Palette.accent); + Graphics.shader(Shaders.mix); + + //draw removals + for(PlaceRequest request : removals){ + Tile tile = request.tile(); + + if(tile == null) continue; + + request.scale = Mathf.lerpDelta(request.scale, 0f, 0.2f); + request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f); + + drawRequest(request); + } + + //draw normals for(PlaceRequest request : placement){ Tile tile = request.tile(); + if(tile == null) continue; - Lines.square(tile.worldx() + request.recipe.result.offset(), tile.worldy() + request.recipe.result.offset(), - request.recipe.result.size * tilesize/2f); + + if(validPlace(tile.x, tile.y, request.recipe.result)){ + request.scale = Mathf.lerpDelta(request.scale, 1f, 0.2f); + request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f); + }else{ + request.scale = Mathf.lerpDelta(request.scale, 0.5f, 0.1f); + request.redness = Mathf.lerpDelta(request.redness, 1f, 0.2f); + } + + drawRequest(request); + } + + Graphics.shader(); + + Draw.color(Palette.accent); + + //Draw lines + if(lineMode){ + Tile tile = tileAt(control.gdxInput().getX(), control.gdxInput().getY()); + + if(tile != null){ + NormalizeDrawResult dresult = PlaceUtils.normalizeDrawArea(recipe.result, lineStartX, lineStartY, tile.x, tile.y, true, maxLength, lineScale); + + Lines.rect(dresult.x, dresult.y, dresult.x2 - dresult.x, dresult.y2 - dresult.y); + + NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, maxLength); + + //go through each cell and draw the block to place if valid + for(int i = 0; i <= result.getLength(); i += recipe.result.size){ + int x = lineStartX + i * Mathf.sign(tile.x - lineStartX) * Mathf.bool(result.isX()); + int y = lineStartY + i * Mathf.sign(tile.y - lineStartY) * Mathf.bool(!result.isX()); + + if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result)){ + Draw.color(); + + TextureRegion[] regions = recipe.result.getBlockIcon(); + + for(TextureRegion region : regions){ + Draw.rect(region, x *tilesize + recipe.result.offset(), y * tilesize + recipe.result.offset(), + region.getRegionWidth() * lineScale, region.getRegionHeight() * lineScale, recipe.result.rotate ? rotation * 90 : 0); + } + }else{ + Draw.color(Palette.breakInvalid); + Lines.square(x*tilesize + recipe.result.offset(), y*tilesize + recipe.result.offset(), recipe.result.size * tilesize/2f); + } + } + } } Draw.color(); @@ -140,13 +251,13 @@ public class AndroidInput extends InputHandler implements GestureListener{ if(state.is(State.menu)) return false; //get tile on cursor - Tile cursor = world.tile(Mathf.scl2(Graphics.mouseWorld().x, tilesize), Mathf.scl2(Graphics.mouseWorld().y, tilesize)); + Tile cursor = tileAt(screenX, screenY); //ignore off-screen taps if(cursor == null || ui.hasMouse(screenX, screenY)) return false; //only begin selecting if the tapped block is a request - selecting = hasRequest(cursor); + selecting = hasRequest(cursor) && isPlacing(); Tile linked = cursor.target(); @@ -157,6 +268,13 @@ public class AndroidInput extends InputHandler implements GestureListener{ frag.config.hideConfig(); } + //when tapping a build block, select it + if(linked.block() instanceof BuildBlock){ + BuildEntity entity = linked.entity(); + + player.replaceBuilding(linked.x, linked.y, linked.getRotation(), entity.recipe); + } + //call tapped() event //TODO net event for block tapped linked.block().tapped(linked, player); @@ -164,36 +282,73 @@ public class AndroidInput extends InputHandler implements GestureListener{ return false; } + @Override + public boolean touchUp(int screenX, int screenY, int pointer, int button){ + + //place down a line if in line mode + if(lineMode) { + Tile tile = tileAt(screenX, screenY); + + if(tile == null) return false; + + //normalize area + NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tile.x, tile.y, rotation, true, 100); + + rotation = result.rotation; + + //place blocks on line + for(int i = 0; i <= result.getLength(); i += recipe.result.size){ + int x = lineStartX + i * Mathf.sign(tile.x - lineStartX) * Mathf.bool(result.isX()); + int y = lineStartY + i * Mathf.sign(tile.y - lineStartY) * Mathf.bool(!result.isX()); + + if(!checkOverlapPlacement(x, y, recipe.result) && validPlace(x, y, recipe.result)){ + PlaceRequest request = new PlaceRequest(x * tilesize, y * tilesize, recipe, result.rotation); + request.scale = 1f; + placement.add(request); + } + } + + lineMode = false; + } + return false; + } + @Override public boolean longPress(float x, float y) { if(state.is(State.menu)) return false; //get tile on cursor - Tile cursor = world.tile(Mathf.scl2(Graphics.mouseWorld().x, tilesize), Mathf.scl2(Graphics.mouseWorld().y, tilesize)); + Tile cursor = tileAt(x, y); //ignore off-screen taps if(cursor == null || ui.hasMouse(x, y)) return false; //remove request if it's there - if(hasRequest(cursor)){ - placement.removeValue(getRequest(cursor), true); - } + //long pressing enables line mode otherwise + lineStartX = cursor.x; + lineStartY = cursor.y; + lineMode = true; + + Effects.effect(Fx.tapBlock, cursor.worldx() + recipe.result.offset(), cursor.worldy() + recipe.result.offset(), recipe.result.size); return false; } @Override public boolean tap(float x, float y, int count, int button) { - if(state.is(State.menu)) return false; + if(state.is(State.menu) || lineMode) return false; //get tile on cursor - Tile cursor = world.tile(Mathf.scl2(Graphics.mouseWorld().x, tilesize), Mathf.scl2(Graphics.mouseWorld().y, tilesize)); + Tile cursor = tileAt(x, y); //ignore off-screen taps if(cursor == null || ui.hasMouse(x, y)) return false; - //add to placement queue if it's a valid place position - if(isPlacing() && validPlace(cursor.x, cursor.y, recipe.result) && !checkOverlapPlacement(cursor.x, cursor.y, recipe.result)){ + //remove if request present + if(hasRequest(cursor)) { + removeRequest(getRequest(cursor)); + }else if(isPlacing() && validPlace(cursor.x, cursor.y, recipe.result) && !checkOverlapPlacement(cursor.x, cursor.y, recipe.result)){ + //add to placement queue if it's a valid place position placement.add(new PlaceRequest(cursor.worldx(), cursor.worldy(), recipe, rotation)); } @@ -203,14 +358,68 @@ public class AndroidInput extends InputHandler implements GestureListener{ @Override public void update(){ + //reset state when not placing if(!isPlacing()){ selecting = false; + lineMode = false; + removals.addAll(placement); placement.clear(); } + + if(lineMode){ + lineScale = Mathf.lerpDelta(lineScale, 1f, 0.1f); + + //When in line mode, pan when near screen edges automatically + if(Gdx.input.isTouched(0) && lineMode){ + float screenX = Graphics.mouse().x, screenY = Graphics.mouse().y; + + float panX = 0, panY = 0; + + if(screenX <= edgePan){ + panX = -(edgePan - screenX); + } + + if(screenX >= Gdx.graphics.getWidth() - edgePan){ + panX = (screenX - Gdx.graphics.getWidth()) + edgePan; + } + + if(screenY <= edgePan){ + panY = -(edgePan - screenY); + } + + if(screenY >= Gdx.graphics.getHeight() - edgePan){ + panY = (screenY - Gdx.graphics.getHeight()) + edgePan; + } + + vector.set(panX, panY).scl((Core.camera.viewportWidth * Core.camera.zoom) / Gdx.graphics.getWidth()); + vector.limit(maxPanSpeed); + + player.x += vector.x; + player.y += vector.y; + player.targetAngle = vector.angle(); + } + }else{ + lineScale = 0f; + } + + //remove place requests that have disappeared + for(int i = removals.size - 1; i >= 0; i --){ + PlaceRequest request = removals.get(i); + + if(request.scale <= 0.0001f){ + removals.removeIndex(i); + i --; + } + } } @Override public boolean pan(float x, float y, float deltaX, float deltaY){ + //can't pan in line mode with one finger! + if(lineMode && !Gdx.input.isTouched(1)){ + return false; + } + float dx = deltaX * Core.camera.zoom / Core.cameraScale, dy = deltaY * Core.camera.zoom / Core.cameraScale; if(selecting){ @@ -282,6 +491,10 @@ public class AndroidInput extends InputHandler implements GestureListener{ Recipe recipe; int rotation; + //animation variables + float scale; + float redness; + PlaceRequest(float x, float y, Recipe recipe, int rotation) { this.x = x; this.y = y; diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index c346d12b4b..43046a9caf 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -1,6 +1,5 @@ package io.anuke.mindustry.input; -import com.badlogic.gdx.Gdx; import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.math.Vector2; import io.anuke.mindustry.entities.BlockBuilder.BuildRequest; @@ -25,11 +24,9 @@ import io.anuke.ucore.util.Translator; import static io.anuke.mindustry.Vars.*; public abstract class InputHandler extends InputAdapter{ - float playerSelectRange = Unit.dp.scl(60f); + float playerSelectRange = Unit.dp.scl(80f); Translator stackTrns = new Translator(); - private float mx, my; - public final Player player; public final OverlayFragment frag = new OverlayFragment(this); @@ -50,11 +47,11 @@ public abstract class InputHandler extends InputAdapter{ } public float getMouseX(){ - return mx; + return control.gdxInput().getX(); } public float getMouseY(){ - return my; + return control.gdxInput().getY(); } public void resetCursor(){ @@ -65,16 +62,19 @@ public abstract class InputHandler extends InputAdapter{ return false; } - public void updateController(){ - mx = Gdx.input.getX(); - my = Gdx.input.getY(); - } - public void buildUI(Group group){ } - public void draw(){ + public void updateController(){ + + } + + public void drawBottom(){ + + } + + public void drawTop(){ } diff --git a/core/src/io/anuke/mindustry/input/PlaceMode.java b/core/src/io/anuke/mindustry/input/PlaceMode.java index 02c669707a..5d3d7b7ac5 100644 --- a/core/src/io/anuke/mindustry/input/PlaceMode.java +++ b/core/src/io/anuke/mindustry/input/PlaceMode.java @@ -158,12 +158,12 @@ public enum PlaceMode{ void process(int tilex, int tiley, int endx, int endy){ /* - if(Math.abs(endx - tilex) > maxlen){ - endx = Mathf.sign(endx - tilex) * maxlen + tilex; + if(Math.abs(x2 - tilex) > maxlen){ + x2 = Mathf.sign(x2 - tilex) * maxlen + tilex; } - if(Math.abs(endy - tiley) > maxlen){ - endy = Mathf.sign(endy - tiley) * maxlen + tiley; + if(Math.abs(y2 - tiley) > maxlen){ + y2 = Mathf.sign(y2 - tiley) * maxlen + tiley; }*/ if(endx < tilex){ @@ -289,18 +289,18 @@ public enum PlaceMode{ //todo hold shift to snap /* - if(Math.abs(tilex - endx) > Math.abs(tiley - endy)){ - endy = tiley; + if(Math.abs(tilex - x2) > Math.abs(tiley - y2)){ + y2 = tiley; }else{ - endx = tilex; + x2 = tilex; } - if(Math.abs(endx - tilex) > maxlen){ - endx = Mathf.sign(endx - tilex) * maxlen + tilex; + if(Math.abs(x2 - tilex) > maxlen){ + x2 = Mathf.sign(x2 - tilex) * maxlen + tilex; } - if(Math.abs(endy - tiley) > maxlen){ - endy = Mathf.sign(endy - tiley) * maxlen + tiley; + if(Math.abs(y2 - tiley) > maxlen){ + y2 = Mathf.sign(y2 - tiley) * maxlen + tiley; }*/ int dx = endx - tilex, dy = endy - tiley; diff --git a/core/src/io/anuke/mindustry/input/PlaceUtils.java b/core/src/io/anuke/mindustry/input/PlaceUtils.java new file mode 100644 index 0000000000..2d514f9506 --- /dev/null +++ b/core/src/io/anuke/mindustry/input/PlaceUtils.java @@ -0,0 +1,139 @@ +package io.anuke.mindustry.input; + +import io.anuke.mindustry.world.Block; +import io.anuke.ucore.util.Mathf; + +import static io.anuke.mindustry.Vars.tilesize; + +public class PlaceUtils { + private static final NormalizeResult result = new NormalizeResult(); + private static final NormalizeDrawResult drawResult = new NormalizeDrawResult(); + + /**Normalizes a placement area and returns the result, ready to be used for drawing a rectangle. + * Returned x2 and y2 will always be greater than x and y. + * + * @param block block that will be drawn + * @param startx starting X coordinate + * @param starty starting Y coordinate + * @param endx ending X coordinate + * @param endy ending Y coordinate + * @param snap whether to snap to a line + * @param maxLength maximum length of area + */ + public static NormalizeDrawResult normalizeDrawArea(Block block, int startx, int starty, int endx, int endy, boolean snap, int maxLength, float scaling){ + normalizeArea(startx, starty, endx, endy, 0, snap, maxLength); + + float offset = block.offset(); + + drawResult.x = result.x * tilesize; + drawResult.y = result.y * tilesize; + drawResult.x2 = result.x2 * tilesize; + drawResult.y2 = result.y2 * tilesize; + + drawResult.x -= block.size * scaling * tilesize/2; + drawResult.x2 += block.size * scaling * tilesize/2; + + + drawResult.y -= block.size * scaling * tilesize/2; + drawResult.y2 += block.size * scaling * tilesize/2; + + drawResult.x += offset; + drawResult.y += offset; + drawResult.x2 += offset; + drawResult.y2 += offset; + + return drawResult; + } + + /**Normalizes a placement area and returns the result. + * Returned x2 and y2 will always be greater than x and y. + * + * @param tilex starting X coordinate + * @param tiley starting Y coordinate + * @param endx ending X coordinate + * @param endy ending Y coordinate + * @param snap whether to snap to a line + * @param rotation placement rotation + * @param maxLength maximum length of area + */ + public static NormalizeResult normalizeArea(int tilex, int tiley, int endx, int endy, int rotation, boolean snap, int maxLength){ + + if(snap) { + if (Math.abs(tilex - endx) > Math.abs(tiley - endy)) { + endy = tiley; + } else { + endx = tilex; + } + + if (Math.abs(endx - tilex) > maxLength) { + endx = Mathf.sign(endx - tilex) * maxLength + tilex; + } + + if (Math.abs(endy - tiley) > maxLength) { + endy = Mathf.sign(endy - tiley) * maxLength + tiley; + } + } + + int dx = endx - tilex, dy = endy - tiley; + + if(Math.abs(dx) > Math.abs(dy)){ + if(dx >= 0){ + rotation = 0; + }else{ + rotation = 2; + } + }else if(Math.abs(dx) < Math.abs(dy)){ + if(dy >= 0){ + rotation = 1; + }else{ + rotation = 3; + } + } + + if(endx < tilex){ + int t = endx; + endx = tilex; + tilex = t; + } + if(endy < tiley){ + int t = endy; + endy = tiley; + tiley = t; + } + + result.x2 = endx; + result.y2 = endy; + result.x = tilex; + result.y = tiley; + result.rotation = rotation; + + return result; + } + + static class NormalizeDrawResult { + float x, y, x2, y2; + } + + static class NormalizeResult{ + int x, y, x2, y2, rotation; + + boolean isX(){ + return Math.abs(x2 - x) > Math.abs(y2 - y); + } + + /**Returns length of greater edge of the selection.*/ + int getLength(){ + return Math.max(x2 - x, y2 - y); + } + + /**Returns the X position of a specific index along this area as a line.*/ + int getScaledX(int i){ + return x + (x2 - x > y2 - y ? i : 0); + } + + /**Returns the Y position of a specific index along this area as a line.*/ + int getScaledY(int i){ + return y + (x2 - x > y2 - y ? 0 : i); + } + } +} diff --git a/core/src/io/anuke/mindustry/world/Build.java b/core/src/io/anuke/mindustry/world/Build.java index 5ac2f32ded..1c52fc13cc 100644 --- a/core/src/io/anuke/mindustry/world/Build.java +++ b/core/src/io/anuke/mindustry/world/Build.java @@ -29,7 +29,7 @@ public class Build { //todo add break results to core inventory if(sound) Effects.sound("break", x * tilesize, y * tilesize); - if(effect) Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), 0f, (float)block.size); + if(effect) Effects.effect(Fx.breakBlock, tile.drawx(), tile.drawy(), block.size); if(!tile.block().isMultiblock() && !tile.isLinked()){ tile.setBlock(Blocks.air); diff --git a/core/src/io/anuke/mindustry/world/blocks/types/BuildBlock.java b/core/src/io/anuke/mindustry/world/blocks/types/BuildBlock.java index 573e73d4ea..9b93a66849 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/BuildBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/BuildBlock.java @@ -107,7 +107,7 @@ public class BuildBlock extends Block { Team team = tile.getTeam(); tile.setBlock(entity.recipe.result); tile.setTeam(team); - Effects.effect(Fx.placeBlock, tile.drawx(), tile.drawy(), 0f, (float)size); + Effects.effect(Fx.placeBlock, tile.drawx(), tile.drawy(), size); }else if(entity.progress < 0f){ entity.damage(entity.health + 1); } diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java b/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java index 3571c8bceb..d25c590a4d 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java @@ -10,6 +10,7 @@ import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; import io.anuke.mindustry.net.Net; import io.anuke.ucore.core.Settings; +import io.anuke.ucore.util.OS; import io.anuke.ucore.util.Strings; import java.net.NetworkInterface; @@ -24,7 +25,7 @@ import java.util.Random; import static io.anuke.mindustry.Vars.*; public class DesktopPlatform extends Platform { - final static boolean useDiscord = false;//OS.getPropertyNotNull("sun.arch.data.model").equals("64"); + final static boolean useDiscord = !OS.is64Bit; final static String applicationId = "398246104468291591"; final static DateFormat format = SimpleDateFormat.getDateTimeInstance(); String[] args; @@ -32,6 +33,8 @@ public class DesktopPlatform extends Platform { public DesktopPlatform(String[] args){ this.args = args; + + if(useDiscord) { DiscordEventHandlers handlers = new DiscordEventHandlers(); DiscordRPC.INSTANCE.Discord_Initialize(applicationId, handlers, true, "");