diff --git a/core/assets-raw/sprites/blocks/turrets/salvo.png b/core/assets-raw/sprites/blocks/turrets/salvo.png index f43f7bbd47..c15a9d47cd 100644 Binary files a/core/assets-raw/sprites/blocks/turrets/salvo.png and b/core/assets-raw/sprites/blocks/turrets/salvo.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 74c88600fc..b106eca042 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -254,6 +254,20 @@ editor.mapname = Map Name: editor.overwrite = [accent]Warning!\nThis overwrites an existing map. editor.overwrite.confirm = [scarlet]Warning![] A map with this name already exists. Are you sure you want to overwrite it? editor.selectmap = Select a map to load: + +toolmode.replace = Replace +toolmode.replace.description = Draws only on solid blocks. +toolmode.replaceall = Replace All +toolmode.replaceall.description = Replace all blocks in map. +toolmode.straight = Straight +toolmode.straight.description = Draws only straight lines. +toolmode.square = Square +toolmode.square.description = Square brush. +toolmode.eraseores = Erase Ores +toolmode.eraseores.description = Erase only ores. +toolmode.fillteams = Fill Teams +toolmode.fillteams.description = Fill teams instead of blocks. + filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. filter.distort = Distort filter.noise = Noise @@ -276,6 +290,7 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile + width = Width: height = Height: menu = Menu diff --git a/core/assets/maps/groundZero.msav b/core/assets/maps/groundZero.msav index ee0593027f..cafa3e5e86 100644 Binary files a/core/assets/maps/groundZero.msav and b/core/assets/maps/groundZero.msav differ diff --git a/core/assets/maps/saltFlats.msav b/core/assets/maps/saltFlats.msav index 687a9f628b..d3adf86160 100644 Binary files a/core/assets/maps/saltFlats.msav and b/core/assets/maps/saltFlats.msav differ diff --git a/core/src/io/anuke/mindustry/editor/EditorTool.java b/core/src/io/anuke/mindustry/editor/EditorTool.java index 7eb6ffa334..b81661cb3d 100644 --- a/core/src/io/anuke/mindustry/editor/EditorTool.java +++ b/core/src/io/anuke/mindustry/editor/EditorTool.java @@ -1,13 +1,14 @@ package io.anuke.mindustry.editor; -import io.anuke.arc.Core; import io.anuke.arc.collection.IntArray; -import io.anuke.arc.function.IntPositionConsumer; -import io.anuke.arc.input.KeyCode; -import io.anuke.arc.util.*; +import io.anuke.arc.function.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Bresenham2; +import io.anuke.arc.util.Structs; +import io.anuke.mindustry.Vars; import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.game.Team; import io.anuke.mindustry.world.*; -import io.anuke.mindustry.world.blocks.*; public enum EditorTool{ zoom, @@ -16,12 +17,34 @@ public enum EditorTool{ if(!Structs.inBounds(x, y, editor.width(), editor.height())) return; Tile tile = editor.tile(x, y).link(); - editor.drawBlock = tile.block() == Blocks.air ? tile.overlay() == Blocks.air ? tile.floor() : tile.overlay() : tile.block(); } }, - line, - pencil{ + line("replace", "straight"){ + + @Override + public void touchedLine(MapEditor editor, int x1, int y1, int x2, int y2){ + //straight + if(mode == 1){ + if(Math.abs(x2 - x1) > Math.abs(y2 - y1)){ + y2 = y1; + }else{ + x2 = x1; + } + } + + Bresenham2.line(x1, y1, x2, y2, (x, y) -> { + if(mode == 0){ + //replace + editor.drawBlocksReplace(x, y); + }else{ + //normal + editor.drawBlocks(x, y); + } + }); + } + }, + pencil("replace", "square"){ { edit = true; draggable = true; @@ -29,10 +52,20 @@ public enum EditorTool{ @Override public void touched(MapEditor editor, int x, int y){ - editor.draw(x, y, isPaint()); + if(mode == -1){ + //normal mode + editor.drawBlocks(x, y); + }else if(mode == 0){ + //replace mode + editor.drawBlocksReplace(x, y); + }else if(mode == 1){ + //square mode + editor.drawBlocks(x, y, true, tile -> true); + } + } }, - eraser{ + eraser("eraseores"){ { edit = true; draggable = true; @@ -40,19 +73,25 @@ public enum EditorTool{ @Override public void touched(MapEditor editor, int x, int y){ - editor.draw(x, y, isPaint(), Blocks.air); + editor.drawCircle(x, y, tile -> { + if(mode == -1){ + //erase block + Vars.world.removeBlock(tile); + }else if(mode == 0){ + //erase ore + tile.clearOverlay(); + } + }); } }, - fill{ + fill("replaceall", "fillteams"){ { edit = true; } IntArray stack = new IntArray(); - Block dest; - boolean isFloor; - MapEditor data; + @Override public void touched(MapEditor editor, int x, int y){ if(!Structs.inBounds(x, y, editor.width(), editor.height())) return; Tile tile = editor.tile(x, y); @@ -63,74 +102,57 @@ public enum EditorTool{ return; } - data = editor; - isFloor = editor.drawBlock instanceof Floor; + //mode 0 or 1, fill everything with the floor/tile or replace it + if(mode == 0 || mode == -1){ + Predicate tester; + Consumer setter; - Block floor = tile.floor(); - Block block = tile.block(); - boolean synth = editor.drawBlock.synthetic(); - - Block draw = editor.drawBlock; - dest = draw instanceof OverlayFloor ? tile.overlay() : isFloor ? floor : block; - - if(dest == draw || block instanceof BlockPart || block.isMultiblock()){ - return; - } - - boolean alt = isAlt(); - - int width = editor.width(); - int height = editor.height(); - - IntPositionConsumer writer = (px, py) -> { - Tile write = editor.tile(px, py); - - if(isFloor){ - if(alt && !(draw instanceof OverlayFloor)){ - Block ore = write.overlay(); - write.setFloor((Floor)draw); - write.setOverlay(ore); - }else{ - write.setFloor((Floor)draw); - } + if(editor.drawBlock.isOverlay()){ + Block dest = tile.overlay(); + tester = t -> t.overlay() == dest; + setter = t -> t.setOverlay(editor.drawBlock); + }else if(editor.drawBlock.isFloor()){ + Block dest = tile.floor(); + tester = t -> t.floor() == dest; + setter = t -> t.setFloorUnder(editor.drawBlock.asFloor()); }else{ - write.setBlock(draw); + Block dest = tile.block(); + tester = t -> t.block() == dest; + setter = t -> t.setBlock(editor.drawBlock); } - if(synth){ - write.setTeam(editor.drawTeam); - } + //replace only when the mode is 0 using the specified functions + fill(editor, x, y, mode == 0, tester, setter); + }else if(mode == 1){ //mode 1 is team fill - if(draw.rotate){ - write.rotation((byte)editor.rotation); - } - }; + //only fill synthetic blocks, it's meaningless otherwise + if(tile.link().synthetic()){ - if(isAlt()){ - //fill all of the same type regardless of borders + Team dest = tile.getTeam(); + fill(editor, x, y, false, t -> t.getTeam() == dest, t -> t.setTeam(editor.drawTeam)); + } + } + } + + void fill(MapEditor editor, int x, int y, boolean replace, Predicate tester, Consumer filler){ + int width = editor.width(), height = editor.height(); + + if(replace){ + //just do it on everything for(int cx = 0; cx < width; cx++){ for(int cy = 0; cy < height; cy++){ - if(eq(cx, cy)){ - writer.accept(cx, cy); - } - } - } - }else if(isAlt2()){ - //fill all teams. - for(int cx = 0; cx < width; cx++){ - for(int cy = 0; cy < height; cy++){ - Tile write = editor.tile(cx, cy); - if(write.block().synthetic()){ - write.setTeam(editor.drawTeam); + Tile tile = editor.tile(cx, cy); + if(tester.test(tile)){ + filler.accept(tile); } } } + }else{ - //normal fill + //perform flood fill int x1; stack.clear(); - stack.add(Pos.get(x, y)); while(stack.size > 0){ @@ -139,23 +161,23 @@ public enum EditorTool{ y = Pos.y(popped); x1 = x; - while(x1 >= 0 && eq(x1, y)) x1--; + while(x1 >= 0 && tester.test(editor.tile(x1, y))) x1--; x1++; boolean spanAbove = false, spanBelow = false; - while(x1 < width && eq(x1, y)){ - writer.accept(x1, y); + while(x1 < width && tester.test(editor.tile(x1, y))){ + filler.accept(editor.tile(x1, y)); - if(!spanAbove && y > 0 && eq(x1, y - 1)){ + if(!spanAbove && y > 0 && tester.test(editor.tile(x1, y - 1))){ stack.add(Pos.get(x1, y - 1)); spanAbove = true; - }else if(spanAbove && !eq(x1, y - 1)){ + }else if(spanAbove && !tester.test(editor.tile(x1, y - 1))){ spanAbove = false; } - if(!spanBelow && y < height - 1 && eq(x1, y + 1)){ + if(!spanBelow && y < height - 1 && tester.test(editor.tile(x1, y + 1))){ stack.add(Pos.get(x1, y + 1)); spanBelow = true; - }else if(spanBelow && y < height - 1 && !eq(x1, y + 1)){ + }else if(spanBelow && y < height - 1 && !tester.test(editor.tile(x1, y + 1))){ spanBelow = false; } x1++; @@ -163,14 +185,10 @@ public enum EditorTool{ } } } - - boolean eq(int px, int py){ - Tile tile = data.tile(px, py); - - return (data.drawBlock instanceof OverlayFloor ? tile.overlay() : isFloor ? tile.floor() : tile.block()) == dest && !(data.drawBlock instanceof OverlayFloor && tile.floor().isLiquid); - } }, - spray{ + spray("replace"){ + final double chance = 0.012; + { edit = true; draggable = true; @@ -178,25 +196,40 @@ public enum EditorTool{ @Override public void touched(MapEditor editor, int x, int y){ - editor.draw(x, y, isPaint(), editor.drawBlock, 0.012); + + //floor spray + if(editor.drawBlock.isFloor()){ + editor.drawCircle(x, y, tile -> { + if(Mathf.chance(chance)){ + tile.setFloor(editor.drawBlock.asFloor()); + } + }); + }else if(mode == 0){ //replace-only mode, doesn't affect air + editor.drawBlocks(x, y, tile -> Mathf.chance(chance) && tile.block() != Blocks.air); + }else{ + editor.drawBlocks(x, y, tile -> Mathf.chance(chance)); + } } }; - boolean edit, draggable; + /** All the internal alternate placement modes of this tool. */ + public final String[] altModes; + /** The current alternate placement mode. -1 is the standard mode, no changes.*/ + public int mode = -1; + /** Whether this tool causes canvas changes when touched.*/ + public boolean edit; + /** Whether this tool should be dragged across the canvas when the mouse moves.*/ + public boolean draggable; - public static boolean isPaint(){ - return Core.input.keyDown(KeyCode.CONTROL_LEFT); + EditorTool(){ + this(new String[]{}); } - public static boolean isAlt(){ - return Core.input.keyDown(KeyCode.TAB); + EditorTool(String... altModes){ + this.altModes = altModes; } - public static boolean isAlt2(){ - return Core.input.keyDown(KeyCode.GRAVE); - } + public void touched(MapEditor editor, int x, int y){} - public void touched(MapEditor editor, int x, int y){ - - } + public void touchedLine(MapEditor editor, int x1, int y1, int x2, int y2){} } diff --git a/core/src/io/anuke/mindustry/editor/MapEditor.java b/core/src/io/anuke/mindustry/editor/MapEditor.java index 13824d0d8a..dd1cecd4d3 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditor.java +++ b/core/src/io/anuke/mindustry/editor/MapEditor.java @@ -2,6 +2,8 @@ package io.anuke.mindustry.editor; import io.anuke.arc.collection.StringMap; import io.anuke.arc.files.FileHandle; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.function.Predicate; import io.anuke.arc.graphics.Pixmap; import io.anuke.arc.math.Mathf; import io.anuke.arc.util.Structs; @@ -132,15 +134,111 @@ public class MapEditor{ return world.height(); } - public void draw(int x, int y, boolean paint){ - draw(x, y, paint, drawBlock); + public void drawBlocksReplace(int x, int y){ + drawBlocks(x, y, tile -> tile.block() != Blocks.air || drawBlock.isFloor()); } - public void draw(int x, int y, boolean paint, Block drawBlock){ - draw(x, y, paint, drawBlock, 1.0); + public void drawBlocks(int x, int y){ + drawBlocks(x, y, false, tile -> true); } - public void draw(int x, int y, boolean paint, Block drawBlock, double chance){ + public void drawBlocks(int x, int y, Predicate tester){ + drawBlocks(x, y, false, tester); + } + + public void drawBlocks(int x, int y, boolean square, Predicate tester){ + if(drawBlock.isMultiblock()){ + x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1); + y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1); + + int offsetx = -(drawBlock.size - 1) / 2; + int offsety = -(drawBlock.size - 1) / 2; + + for(int dx = 0; dx < drawBlock.size; dx++){ + for(int dy = 0; dy < drawBlock.size; dy++){ + int worldx = dx + offsetx + x; + int worldy = dy + offsety + y; + + if(Structs.inBounds(worldx, worldy, width(), height())){ + Tile tile = tile(worldx, worldy); + + Block block = tile.block(); + + //bail out if there's anything blocking the way + if(block.isMultiblock() || block instanceof BlockPart){ + return; + } + + renderer.updatePoint(worldx, worldy); + } + } + } + + world.setBlock(tile(x, y), drawBlock, drawTeam); + }else{ + + boolean isFloor = drawBlock.isFloor() && drawBlock != Blocks.air; + + Consumer drawer = tile -> { + if(!tester.test(tile)) return; + + //remove linked tiles blocking the way + if(!isFloor && (tile.isLinked() || tile.block().isMultiblock())){ + world.removeBlock(tile.link()); + } + + if(isFloor){ + tile.setFloor(drawBlock.asFloor()); + }else{ + tile.setBlock(drawBlock); + if(drawBlock.synthetic()){ + tile.setTeam(drawTeam); + } + if(drawBlock.rotate){ + tile.rotation((byte)rotation); + } + } + }; + + if(square){ + drawSquare(x, y, drawer); + }else{ + drawCircle(x, y, drawer); + } + } + } + + public void drawCircle(int x, int y, Consumer drawer){ + for(int rx = -brushSize; rx <= brushSize; rx++){ + for(int ry = -brushSize; ry <= brushSize; ry++){ + if(Mathf.dst2(rx, ry) <= (brushSize - 0.5f) * (brushSize - 0.5f)){ + int wx = x + rx, wy = y + ry; + + if(wx < 0 || wy < 0 || wx >= width() || wy >= height()){ + continue; + } + + drawer.accept(tile(wx, wy)); + } + } + } + } + + public void drawSquare(int x, int y, Consumer drawer){ + for(int rx = -brushSize; rx <= brushSize; rx++){ + for(int ry = -brushSize; ry <= brushSize; ry++){ + int wx = x + rx, wy = y + ry; + + if(wx < 0 || wy < 0 || wx >= width() || wy >= height()){ + continue; + } + + drawer.accept(tile(wx, wy)); + } + } + } + + public void draw_DEPRECATED(int x, int y, boolean paint, Block drawBlock, double chance){ boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air; Tile[][] tiles = world.getTiles(); diff --git a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java index 89c76b5126..ff7404bcca 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java @@ -12,8 +12,10 @@ import io.anuke.arc.input.KeyCode; import io.anuke.arc.math.Mathf; import io.anuke.arc.math.geom.Vector2; import io.anuke.arc.scene.actions.Actions; +import io.anuke.arc.scene.event.Touchable; import io.anuke.arc.scene.style.TextureRegionDrawable; import io.anuke.arc.scene.ui.*; +import io.anuke.arc.scene.ui.TextButton.TextButtonStyle; import io.anuke.arc.scene.ui.layout.Table; import io.anuke.arc.scene.ui.layout.Unit; import io.anuke.arc.util.*; @@ -21,6 +23,7 @@ import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.game.*; +import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.io.JsonIO; import io.anuke.mindustry.io.MapIO; import io.anuke.mindustry.maps.Map; @@ -380,15 +383,77 @@ public class MapEditorDialog extends Dialog implements Disposable{ ButtonGroup group = new ButtonGroup<>(); Consumer addTool = tool -> { + Table[] lastTable = {null}; + ImageButton button = new ImageButton("icon-" + tool.name(), "clear-toggle"); - button.clicked(() -> view.setTool(tool)); + button.clicked(() -> { + view.setTool(tool); + if(lastTable[0] != null){ + lastTable[0].remove(); + } + }); button.resizeImage(16 * 2f); button.update(() -> button.setChecked(view.getTool() == tool)); group.add(button); - if(tool == EditorTool.pencil) - button.setChecked(true); - tools.add(button); + if(tool.altModes.length > 0){ + button.clicked(l -> { + if(!mobile){ + //desktop: rightclick + l.setButton(KeyCode.MOUSE_RIGHT); + } + }, e -> { + //need to double tap + if(mobile && e.getTapCount() < 2){ + return; + } + + if(lastTable[0] != null){ + lastTable[0].remove(); + } + + Table table = new Table("dialogDim"); + table.defaults().size(280f, 70f); + + for(int i = 0; i < tool.altModes.length; i++){ + int mode = i; + String name = tool.altModes[i]; + + table.addButton(b -> { + b.left(); + b.marginLeft(6); + b.setStyle(Core.scene.skin.get("clear-toggle", TextButtonStyle.class)); + b.add(Core.bundle.get("toolmode." + name)).left(); + b.row(); + b.add(Core.bundle.get("toolmode." + name + ".description")).color(Color.LIGHT_GRAY).left(); + }, () -> { + tool.mode = (tool.mode == mode ? -1 : mode); + table.remove(); + }).update(b -> b.setChecked(tool.mode == mode)); + table.row(); + } + + table.update(() -> { + Vector2 v = button.localToStageCoordinates(Tmp.v1.setZero()); + table.setPosition(v.x, v.y, Align.topLeft); + }); + + table.pack(); + table.act(Core.graphics.getDeltaTime()); + + Core.scene.add(table); + lastTable[0] = table; + }); + } + + + Label mode = new Label(""); + mode.setColor(Pal.remove); + mode.update(() -> mode.setText(tool.mode == -1 ? "" : "M" + (tool.mode + 1) + " ")); + mode.setAlignment(Align.bottomRight, Align.bottomRight); + mode.touchable(Touchable.disabled); + + tools.stack(button, mode); }; tools.defaults().size(size, size); @@ -479,14 +544,26 @@ public class MapEditorDialog extends Dialog implements Disposable{ } private void doInput(){ - //tool select - for(int i = 0; i < EditorTool.values().length; i++){ - if(Core.input.keyTap(KeyCode.valueOf("NUM_" + (i + 1)))){ - view.setTool(EditorTool.values()[i]); - break; + + if(Core.input.ctrl()){ + //alt mode select + //TODO these keycode are unusable, tweak later + for(int i = 0; i < view.getTool().altModes.length + 1; i++){ + if(Core.input.keyTap(KeyCode.valueOf("NUM_" + (i + 1)))){ + view.getTool().mode = i - 1; + } + } + }else{ + //tool select + for(int i = 0; i < EditorTool.values().length; i++){ + if(Core.input.keyTap(KeyCode.valueOf("NUM_" + (i + 1)))){ + view.setTool(EditorTool.values()[i]); + break; + } } } + if(Core.input.keyTap(KeyCode.ESCAPE)){ if(!menu.isShown()){ menu.show(); diff --git a/core/src/io/anuke/mindustry/editor/MapInfoDialog.java b/core/src/io/anuke/mindustry/editor/MapInfoDialog.java index b13abef7ea..1bfe9fad4c 100644 --- a/core/src/io/anuke/mindustry/editor/MapInfoDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapInfoDialog.java @@ -29,47 +29,49 @@ public class MapInfoDialog extends FloatingDialog{ cont.clear(); ObjectMap tags = editor.getTags(); + + cont.pane(t -> { + t.add("$editor.name").padRight(8).left(); + t.defaults().padTop(15); - cont.add("$editor.name").padRight(8).left(); + TextField name = t.addField(tags.get("name", ""), text -> { + tags.put("name", text); + }).size(400, 55f).get(); + name.setMessageText("$unknown"); - cont.defaults().padTop(15); + t.row(); + t.add("$editor.description").padRight(8).left(); - TextField name = cont.addField(tags.get("name", ""), text -> { - tags.put("name", text); - }).size(400, 55f).get(); - name.setMessageText("$unknown"); + TextArea description = t.addArea(tags.get("description", ""), "textarea", text -> { + tags.put("description", text); + }).size(400f, 140f).get(); - cont.row(); - cont.add("$editor.description").padRight(8).left(); + t.row(); + t.add("$editor.author").padRight(8).left(); - TextArea description = cont.addArea(tags.get("description", ""), "textarea", text -> { - tags.put("description", text); - }).size(400f, 140f).get(); + TextField author = t.addField(tags.get("author", Core.settings.getString("mapAuthor", "")), text -> { + tags.put("author", text); + Core.settings.put("mapAuthor", text); + Core.settings.save(); + }).size(400, 55f).get(); + author.setMessageText("$unknown"); - cont.row(); - cont.add("$editor.author").padRight(8).left(); + t.row(); + t.add("$editor.rules").padRight(8).left(); + t.addButton("$edit", () -> ruleInfo.show(Vars.state.rules, () -> Vars.state.rules = new Rules())).left().width(200f); - TextField author = cont.addField(tags.get("author", Core.settings.getString("mapAuthor", "")), text -> { - tags.put("author", text); - Core.settings.put("mapAuthor", text); - Core.settings.save(); - }).size(400, 55f).get(); - author.setMessageText("$unknown"); + t.row(); + t.add("$editor.waves").padRight(8).left(); + t.addButton("$edit", waveInfo::show).left().width(200f); - cont.row(); - cont.add("$editor.rules").padRight(8).left(); - cont.addButton("$edit", () -> ruleInfo.show(Vars.state.rules, () -> Vars.state.rules = new Rules())).left().width(200f); + name.change(); + description.change(); + author.change(); - cont.row(); - cont.add("$editor.waves").padRight(8).left(); - cont.addButton("$edit", waveInfo::show).left().width(200f); - - name.change(); - description.change(); - author.change(); - - Platform.instance.addDialog(name, 50); - Platform.instance.addDialog(author, 50); - Platform.instance.addDialog(description, 1000); + Platform.instance.addDialog(name, 50); + Platform.instance.addDialog(author, 50); + Platform.instance.addDialog(description, 1000); + t.margin(16f); + }); } } diff --git a/core/src/io/anuke/mindustry/editor/MapView.java b/core/src/io/anuke/mindustry/editor/MapView.java index a332dd3adc..79e141fe34 100644 --- a/core/src/io/anuke/mindustry/editor/MapView.java +++ b/core/src/io/anuke/mindustry/editor/MapView.java @@ -1,7 +1,6 @@ package io.anuke.mindustry.editor; import io.anuke.arc.Core; -import io.anuke.arc.collection.Array; import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.input.GestureDetector; @@ -24,7 +23,6 @@ import static io.anuke.mindustry.Vars.ui; public class MapView extends Element implements GestureListener{ private MapEditor editor; private EditorTool tool = EditorTool.pencil; - private Bresenham2 br = new Bresenham2(); private float offsetx, offsety; private float zoom = 1f; private boolean grid = false; @@ -107,19 +105,8 @@ public class MapView extends Element implements GestureListener{ Point2 p = project(x, y); if(tool == EditorTool.line){ - if(Core.input.keyDown(KeyCode.TAB)){ - if(Math.abs(p.x - firstTouch.x) > Math.abs(p.y - firstTouch.y)){ - p.y = firstTouch.y; - }else{ - p.x = firstTouch.x; - } - } - ui.editor.resetSaved(); - Array points = br.line(startx, starty, p.x, p.y); - for(Point2 point : points){ - editor.draw(point.x, point.y, EditorTool.isPaint()); - } + tool.touchedLine(editor, startx, starty, p.x, p.y); } editor.flushOp(); @@ -133,7 +120,6 @@ public class MapView extends Element implements GestureListener{ @Override public void touchDragged(InputEvent event, float x, float y, int pointer){ - mousex = x; mousey = y; @@ -141,13 +127,10 @@ public class MapView extends Element implements GestureListener{ if(drawing && tool.draggable && !(p.x == lastx && p.y == lasty)){ ui.editor.resetSaved(); - Array points = br.line(lastx, lasty, p.x, p.y); - for(Point2 point : points){ - tool.touched(editor, point.x, point.y); - } + Bresenham2.line(lastx, lasty, p.x, p.y, (cx, cy) -> tool.touched(editor, cx, cy)); } - if(tool == EditorTool.line && Core.input.keyDown(KeyCode.TAB)){ + if(tool == EditorTool.line && tool.mode == 1){ if(Math.abs(p.x - firstTouch.x) > Math.abs(p.y - firstTouch.y)){ lastx = p.x; lasty = firstTouch.y; @@ -296,7 +279,13 @@ public class MapView extends Element implements GestureListener{ if((tool.edit || (tool == EditorTool.line && !drawing)) && (!mobile || drawing)){ Point2 p = project(mousex, mousey); Vector2 v = unproject(p.x, p.y).add(x, y); - Lines.poly(brushPolygons[index], v.x, v.y, scaling); + + //pencil square outline + if(tool == EditorTool.pencil && tool.mode == 1){ + Lines.square(v.x + scaling/2f, v.y + scaling/2f, scaling * (editor.brushSize + 0.5f)); + }else{ + Lines.poly(brushPolygons[index], v.x, v.y, scaling); + } } }else{ if((tool.edit || tool == EditorTool.line) && (!mobile || drawing)){ diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index eea9a3b82a..c80552996f 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -27,17 +27,8 @@ public class Net{ private static ObjectMap, BiConsumer> serverListeners = new ObjectMap<>(); private static ClientProvider clientProvider; private static ServerProvider serverProvider; - private static IntMap streams = new IntMap<>(); - public static boolean hasClient(){ - return clientProvider != null; - } - - public static boolean hasServer(){ - return serverProvider != null; - } - /** Display a network error. Call on the graphics thread. */ public static void showError(Throwable e){ @@ -50,12 +41,13 @@ public class Net{ String error = t.getMessage() == null ? "" : t.getMessage().toLowerCase(); String type = t.getClass().toString().toLowerCase(); + boolean isError = false; if(e instanceof BufferUnderflowException || e instanceof BufferOverflowException){ error = Core.bundle.get("error.io"); }else if(error.equals("mismatch")){ error = Core.bundle.get("error.mismatch"); - }else if(error.contains("port out of range") || error.contains("invalid argument") || (error.contains("invalid") && error.contains("address"))){ + }else if(error.contains("port out of range") || error.contains("invalid argument") || (error.contains("invalid") && error.contains("address")) || Strings.parseException(e, true).contains("address associated")){ error = Core.bundle.get("error.invalidaddress"); }else if(error.contains("connection refused") || error.contains("route to host") || type.contains("unknownhost")){ error = Core.bundle.get("error.unreachable"); @@ -65,9 +57,14 @@ public class Net{ error = Core.bundle.get("error.alreadyconnected"); }else if(!error.isEmpty()){ error = Core.bundle.get("error.any") + "\n" + Strings.parseException(e, true); + isError = true; } - ui.showText("", Core.bundle.format("connectfail", error)); + if(isError){ + ui.showError(Core.bundle.format("connectfail", error)); + }else{ + ui.showText("", Core.bundle.format("connectfail", error)); + } ui.loadfrag.hide(); if(Net.client()){ diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 1c44841832..a274a08810 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -27,6 +27,8 @@ import io.anuke.mindustry.input.InputHandler.PlaceDraw; import io.anuke.mindustry.type.*; import io.anuke.mindustry.ui.Bar; import io.anuke.mindustry.ui.ContentDisplay; +import io.anuke.mindustry.world.blocks.Floor; +import io.anuke.mindustry.world.blocks.OverlayFloor; import io.anuke.mindustry.world.consumers.*; import io.anuke.mindustry.world.meta.*; @@ -658,6 +660,18 @@ public class Block extends BlockStorage{ return buildVisibility.get() && !isHidden(); } + public boolean isFloor(){ + return this instanceof Floor; + } + + public boolean isOverlay(){ + return this instanceof OverlayFloor; + } + + public Floor asFloor(){ + return (Floor)this; + } + @Override public boolean isHidden(){ return !buildVisibility.get(); diff --git a/core/src/io/anuke/mindustry/world/Tile.java b/core/src/io/anuke/mindustry/world/Tile.java index cac7d27a7f..618d3cfc97 100644 --- a/core/src/io/anuke/mindustry/world/Tile.java +++ b/core/src/io/anuke/mindustry/world/Tile.java @@ -9,8 +9,7 @@ import io.anuke.mindustry.entities.traits.TargetTrait; import io.anuke.mindustry.entities.type.TileEntity; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.type.Item; -import io.anuke.mindustry.world.blocks.BlockPart; -import io.anuke.mindustry.world.blocks.Floor; +import io.anuke.mindustry.world.blocks.*; import io.anuke.mindustry.world.modules.*; import static io.anuke.mindustry.Vars.*; @@ -161,6 +160,13 @@ public class Tile implements Position, TargetTrait{ this.overlay = 0; } + /** Sets the floor, preserving overlay.*/ + public void setFloorUnder(Floor floor){ + Block overlay = overlay(); + setFloor(floor); + setOverlay(overlay); + } + public byte rotation(){ return rotation; } @@ -190,7 +196,7 @@ public class Tile implements Position, TargetTrait{ } public void clearOverlay(){ - this.overlay = 0; + setOverlayID((short)0); } public boolean passable(){