From 0049a0004ecd01a0a4f9085df6ee2f130da3fb62 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 29 Sep 2019 19:54:52 -0400 Subject: [PATCH] Block rotation / Mod loading improvements --- core/assets/bundles/bundle.properties | 1 + .../mindustry/graphics/OverlayRenderer.java | 12 +++- .../src/io/anuke/mindustry/input/Binding.java | 1 + .../anuke/mindustry/input/DesktopInput.java | 6 +- .../anuke/mindustry/input/InputHandler.java | 24 ++++++- .../io/anuke/mindustry/mod/ContentParser.java | 64 +++++++++++++------ core/src/io/anuke/mindustry/mod/Mod.java | 4 +- core/src/io/anuke/mindustry/mod/Mods.java | 6 ++ core/src/io/anuke/mindustry/type/Item.java | 4 ++ core/src/io/anuke/mindustry/type/Liquid.java | 5 ++ core/src/io/anuke/mindustry/type/Mech.java | 4 ++ .../mindustry/ui/dialogs/FileChooser.java | 4 +- .../mindustry/ui/fragments/MenuFragment.java | 3 +- .../anuke/mindustry/world/BlockStorage.java | 2 +- 14 files changed, 109 insertions(+), 31 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 37a25e9c41..df7f7dc469 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -627,6 +627,7 @@ keybind.chat.name = Chat keybind.player_list.name = Player list keybind.console.name = Console keybind.rotate.name = Rotate +keybind.rotateplaced.name = Rotate Placed (Hold) keybind.toggle_menus.name = Toggle menus keybind.chat_history_prev.name = Chat history prev keybind.chat_history_next.name = Chat history next diff --git a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java index 97b74f881f..b9c366512e 100644 --- a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java +++ b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java @@ -2,8 +2,7 @@ package io.anuke.mindustry.graphics; import io.anuke.arc.Core; import io.anuke.arc.graphics.Color; -import io.anuke.arc.graphics.g2d.Draw; -import io.anuke.arc.graphics.g2d.Lines; +import io.anuke.arc.graphics.g2d.*; import io.anuke.arc.math.Mathf; import io.anuke.arc.math.geom.Rectangle; import io.anuke.arc.math.geom.Vector2; @@ -14,7 +13,7 @@ import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.type.Player; import io.anuke.mindustry.game.Team; -import io.anuke.mindustry.input.InputHandler; +import io.anuke.mindustry.input.*; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.Tile; @@ -112,6 +111,13 @@ public class OverlayRenderer{ if(tile != null && tile.block() != Blocks.air && tile.getTeam() == player.getTeam()){ tile.block().drawSelect(tile); + + if(Core.input.keyDown(Binding.rotateplaced) && tile.block().rotate){ + control.input.drawArrow(tile.block(), tile.x, tile.y, tile.rotation(), true); + Draw.color(Pal.accent, 0.3f + Mathf.absin(4f, 0.2f)); + Fill.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize/2f); + Draw.color(); + } } } diff --git a/core/src/io/anuke/mindustry/input/Binding.java b/core/src/io/anuke/mindustry/input/Binding.java index 1eb5da1875..8f599a872f 100644 --- a/core/src/io/anuke/mindustry/input/Binding.java +++ b/core/src/io/anuke/mindustry/input/Binding.java @@ -13,6 +13,7 @@ public enum Binding implements KeyBind{ deselect(KeyCode.MOUSE_RIGHT), break_block(KeyCode.MOUSE_RIGHT), rotate(new Axis(KeyCode.SCROLL)), + rotateplaced(KeyCode.R), diagonal_placement(KeyCode.CONTROL_LEFT), pick(KeyCode.MOUSE_MIDDLE), dash(KeyCode.SHIFT_LEFT), diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index acef76ac47..3bd752086e 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -11,9 +11,9 @@ import io.anuke.arc.util.*; import io.anuke.mindustry.content.*; import io.anuke.mindustry.core.GameState.*; import io.anuke.mindustry.game.EventType.*; +import io.anuke.mindustry.gen.*; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.input.PlaceUtils.*; -import io.anuke.mindustry.net.Net; import io.anuke.mindustry.world.*; import static io.anuke.arc.Core.scene; @@ -189,6 +189,10 @@ public class DesktopInput extends InputHandler{ if(canTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y)){ cursorType = ui.unloadCursor; } + + if(!isPlacing() && Math.abs(Core.input.axisTap(Binding.rotate)) > 0 && Core.input.keyDown(Binding.rotateplaced) && cursor.block().rotate){ + Call.rotateBlock(player, cursor, Core.input.axisTap(Binding.rotate) > 0); + } } if(!Core.scene.hasMouse()){ diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index caa89ffc56..0cae4577e3 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -57,6 +57,20 @@ public abstract class InputHandler implements InputProcessor{ player.clearItem(); } + @Remote(targets = Loc.both, called = Loc.server, forward = true, unreliable = true) + public static void rotateBlock(Player player, Tile tile, boolean direction){ + if(net.server() && !Units.canInteract(player, tile)){ + throw new ValidateException(player, "Player cannot drop an item."); + } + + tile.rotation(Mathf.mod(tile.rotation() + Mathf.sign(direction), 4)); + + if(tile.entity != null){ + tile.entity.updateProximity(); + tile.entity.noSleep(); + } + } + @Remote(targets = Loc.both, forward = true, called = Loc.server) public static void transferInventory(Player player, Tile tile){ if(player == null || player.timer == null || !player.timer.get(Player.timerTransfer, 40)) return; @@ -352,15 +366,19 @@ public abstract class InputHandler implements InputProcessor{ player.addBuildRequest(new BuildRequest(tile.x, tile.y)); } - void drawArrow(Block block, int x, int y, int rotation){ - Draw.color(!validPlace(x, y, block, rotation) ? Pal.removeBack : Pal.accentBack); + public void drawArrow(Block block, int x, int y, int rotation){ + drawArrow(block, x, y, rotation, validPlace(x, y, block, rotation)); + } + + public void drawArrow(Block block, int x, int y, int rotation, boolean valid){ + Draw.color(!valid ? Pal.removeBack : Pal.accentBack); Draw.rect(Core.atlas.find("place-arrow"), x * tilesize + block.offset(), y * tilesize + block.offset() - 1, Core.atlas.find("place-arrow").getWidth() * Draw.scl, Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90); - Draw.color(!validPlace(x, y, block, rotation) ? Pal.remove : Pal.accent); + Draw.color(!valid ? Pal.remove : Pal.accent); Draw.rect(Core.atlas.find("place-arrow"), x * tilesize + block.offset(), y * tilesize + block.offset(), diff --git a/core/src/io/anuke/mindustry/mod/ContentParser.java b/core/src/io/anuke/mindustry/mod/ContentParser.java index dd8adfe111..8beb9cd900 100644 --- a/core/src/io/anuke/mindustry/mod/ContentParser.java +++ b/core/src/io/anuke/mindustry/mod/ContentParser.java @@ -1,14 +1,15 @@ package io.anuke.mindustry.mod; import io.anuke.arc.collection.*; -import io.anuke.arc.graphics.*; -import io.anuke.arc.util.*; +import io.anuke.arc.function.*; import io.anuke.arc.util.ArcAnnotate.*; +import io.anuke.arc.util.*; import io.anuke.arc.util.reflect.*; import io.anuke.arc.util.serialization.*; import io.anuke.arc.util.serialization.Json.*; import io.anuke.mindustry.*; import io.anuke.mindustry.content.*; +import io.anuke.mindustry.entities.Effects.*; import io.anuke.mindustry.entities.bullet.*; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.game.*; @@ -19,24 +20,24 @@ import io.anuke.mindustry.world.*; public class ContentParser{ private static final boolean ignoreUnknownFields = true; private ObjectMap, ContentType> contentTypes = new ObjectMap<>(); + private ObjectMap, FieldParser> classParsers = new ObjectMap, FieldParser>(){{ + put(BulletType.class, (type, data) -> field(Bullets.class, data)); + put(Effect.class, (type, data) -> field(Fx.class, data)); + }}; private Json parser = new Json(){ public T readValue(Class type, Class elementType, JsonValue jsonData){ - try{ - if(type == BulletType.class){ - BulletType b = (BulletType)Bullets.class.getField(jsonData.asString()).get(null); - if(b == null) throw new IllegalArgumentException("Bullet type not found: " + jsonData.asString()); - return (T)b; + if(type != null){ + if(classParsers.containsKey(type)){ + return (T)classParsers.get(type).parse(type, jsonData); } - if(type != null && Content.class.isAssignableFrom(type)){ + if(Content.class.isAssignableFrom(type)){ return (T)Vars.content.getByName(contentTypes.getThrow(type, () -> new IllegalArgumentException("No content type for class: " + type.getSimpleName())), jsonData.asString()); } - - return super.readValue(type, elementType, jsonData); - }catch(Exception e){ - throw new RuntimeException(e); } + + return super.readValue(type, elementType, jsonData); } }; @@ -55,11 +56,6 @@ public class ContentParser{ return block; }, - ContentType.item, (TypeParser)(mod, name, value) -> { - Item item = new Item(mod + "-" + name, new Color(Color.black)); - readFields(item, value); - return item; - }, ContentType.unit, (TypeParser)(mod, name, value) -> { String clas = value.getString("type"); Class type = resolve("io.anuke.mindustry.entities.type.base." + clas); @@ -75,9 +71,20 @@ public class ContentParser{ readFields(unit, value); return unit; - } + }, + ContentType.item, parser(Item::new), + ContentType.liquid, parser(Liquid::new), + ContentType.mech, parser(Mech::new) ); + private TypeParser parser(Function constructor){ + return (mod, name, value) -> { + T item = constructor.get(mod + "-" + name); + readFields(item, value); + return item; + }; + } + private void init(){ for(ContentType type : ContentType.all){ Array arr = Vars.content.getBy(type); @@ -115,6 +122,21 @@ public class ContentParser{ return c; } + private Object field(Class type, JsonValue value){ + return field(type, value.asString()); + } + + /** Gets a field from a static class by name, throwing a descriptive exception if not found. */ + private Object field(Class type, String name){ + try{ + Object b = type.getField(name).get(null); + if(b == null) throw new IllegalArgumentException(type.getSimpleName() + ": not found: '" + name + "'"); + return b; + }catch(Exception e){ + throw new RuntimeException(e); + } + } + /** Checks all @NonNull fields in this object, recursively. * Throws an exception if any are null.*/ private void checkNulls(Object object){ @@ -186,7 +208,11 @@ public class ContentParser{ throw new IllegalArgumentException("Type not found: " + potentials[0]); } - public interface TypeParser{ + private interface FieldParser{ + Object parse(Class type, JsonValue value); + } + + private interface TypeParser{ T parse(String mod, String name, JsonValue value) throws Exception; } diff --git a/core/src/io/anuke/mindustry/mod/Mod.java b/core/src/io/anuke/mindustry/mod/Mod.java index c25d4df1c2..5ee0f699c4 100644 --- a/core/src/io/anuke/mindustry/mod/Mod.java +++ b/core/src/io/anuke/mindustry/mod/Mod.java @@ -16,7 +16,9 @@ public class Mod{ } /** Create any content needed here. */ - public void loadContent(){} + public void loadContent(){ + + } /** Register any commands to be used on the server side, e.g. from the console. */ public void registerServerCommands(CommandHandler handler){ diff --git a/core/src/io/anuke/mindustry/mod/Mods.java b/core/src/io/anuke/mindustry/mod/Mods.java index 258c9307bf..e784333376 100644 --- a/core/src/io/anuke/mindustry/mod/Mods.java +++ b/core/src/io/anuke/mindustry/mod/Mods.java @@ -102,6 +102,7 @@ public class Mods implements Loadable{ public void loadSync(){ if(packer == null) return; + //get textures packed if(totalSprites > 0){ TextureFilter filter = Core.settings.getBool("linear") ? TextureFilter.Linear : TextureFilter.Nearest; packer.getPages().each(page -> page.updateTexture(filter, filter, false)); @@ -140,6 +141,9 @@ public class Mods implements Loadable{ } } + //sort mods to make sure servers handle them properly. + loaded.sort(Structs.comparing(m -> m.name)); + buildFiles(); } @@ -280,5 +284,7 @@ public class Mods implements Loadable{ public static class ModMeta{ public String name, author, description, version, main; public String[] dependencies = {}; //TODO implement + /** Hidden mods are only server-side or client-side, and do not support adding new content. */ + public boolean hidden; } } diff --git a/core/src/io/anuke/mindustry/type/Item.java b/core/src/io/anuke/mindustry/type/Item.java index 19ca42e7b4..16c8d3b3af 100644 --- a/core/src/io/anuke/mindustry/type/Item.java +++ b/core/src/io/anuke/mindustry/type/Item.java @@ -39,6 +39,10 @@ public class Item extends UnlockableContent implements Comparable{ this.description = Core.bundle.getOrNull("item." + this.name + ".description"); } + public Item(String name){ + this(name, new Color(Color.black)); + } + @Override public void load(){ regions = new TextureRegion[Icon.values().length]; diff --git a/core/src/io/anuke/mindustry/type/Liquid.java b/core/src/io/anuke/mindustry/type/Liquid.java index bb3a0f363a..f34ed71148 100644 --- a/core/src/io/anuke/mindustry/type/Liquid.java +++ b/core/src/io/anuke/mindustry/type/Liquid.java @@ -34,6 +34,11 @@ public class Liquid extends UnlockableContent{ this.description = Core.bundle.getOrNull("liquid." + name + ".description"); } + /** For modding only.*/ + public Liquid(String name){ + this(name, new Color(Color.black)); + } + public boolean canExtinguish(){ return flammability < 0.1f && temperature <= 0.5f; } diff --git a/core/src/io/anuke/mindustry/type/Mech.java b/core/src/io/anuke/mindustry/type/Mech.java index 1acea9eb77..bfc6abf056 100644 --- a/core/src/io/anuke/mindustry/type/Mech.java +++ b/core/src/io/anuke/mindustry/type/Mech.java @@ -41,6 +41,10 @@ public class Mech extends UnlockableContent{ this.description = Core.bundle.get("mech." + name + ".description"); } + public Mech(String name){ + this(name, false); + } + public String localizedName(){ return Core.bundle.get("mech." + name + ".name"); } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/FileChooser.java b/core/src/io/anuke/mindustry/ui/dialogs/FileChooser.java index b7faca0abe..75b260594b 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/FileChooser.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/FileChooser.java @@ -216,7 +216,9 @@ public class FileChooser extends FloatingDialog{ String filename = file.name(); - TextButton button = new TextButton(shorten(filename), Styles.clearTogglet); + TextButton button = new TextButton(filename, Styles.clearTogglet); + button.getLabel().setWrap(false); + button.getLabel().setEllipsis(true); group.add(button); button.clicked(() -> { diff --git a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java index be5d611eae..2f8aae6308 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java @@ -161,8 +161,7 @@ public class MenuFragment extends Fragment{ new Buttoni("$loadgame", Icon.loadSmall, ui.load::show), new Buttoni("$tutorial", Icon.infoSmall, control::playTutorial) ), - new Buttoni("$editor", Icon.editorSmall, ui.maps::show), - steam ? new Buttoni("$workshop", Icon.saveSmall, platform::openWorkshop) : null, + new Buttoni("$editor", Icon.editorSmall, ui.maps::show), steam ? new Buttoni("$workshop", Icon.saveSmall, platform::openWorkshop) : null, new Buttoni(Core.bundle.get("mods") + "\n" + Core.bundle.get("mods.alpha"), Icon.wikiSmall, ui.mods::show), new Buttoni("$settings", Icon.toolsSmall, ui.settings::show), new Buttoni("$about.button", Icon.infoSmall, ui.about::show), diff --git a/core/src/io/anuke/mindustry/world/BlockStorage.java b/core/src/io/anuke/mindustry/world/BlockStorage.java index 920dba6c3e..a4aab0894f 100644 --- a/core/src/io/anuke/mindustry/world/BlockStorage.java +++ b/core/src/io/anuke/mindustry/world/BlockStorage.java @@ -3,7 +3,7 @@ package io.anuke.mindustry.world; import io.anuke.arc.collection.Array; import io.anuke.arc.math.Mathf; import io.anuke.arc.math.geom.Vector2; -import io.anuke.arc.util.Time; +import io.anuke.arc.util.*; import io.anuke.mindustry.Vars; import io.anuke.mindustry.content.Fx; import io.anuke.mindustry.entities.Effects;