From b4b6b9fa441b4c693082f49f5feec7f14fe1d729 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 24 May 2024 21:46:15 -0400 Subject: [PATCH] World processor list in editor --- .../annotations/impl/AssetsProcess.java | 5 + core/assets/bundles/bundle.properties | 7 + core/src/mindustry/Vars.java | 7 + core/src/mindustry/ai/types/LogicAI.java | 10 -- .../mindustry/ctype/UnlockableContent.java | 5 + .../src/mindustry/editor/MapEditorDialog.java | 7 +- core/src/mindustry/editor/MapInfoDialog.java | 20 ++- .../mindustry/editor/MapProcessorsDialog.java | 155 ++++++++++++++++++ core/src/mindustry/logic/LCanvas.java | 6 + core/src/mindustry/logic/LogicDialog.java | 23 ++- core/src/mindustry/ui/Fonts.java | 8 +- core/src/mindustry/ui/Styles.java | 10 ++ .../mindustry/ui/dialogs/DatabaseDialog.java | 4 +- .../ui/dialogs/IconSelectDialog.java | 74 +++++++++ .../mindustry/ui/dialogs/PausedDialog.java | 15 +- .../mindustry/ui/dialogs/PlanetDialog.java | 10 +- .../ui/dialogs/SchematicsDialog.java | 3 +- .../world/blocks/logic/LogicBlock.java | 69 +++++++- gradle.properties | 2 +- 19 files changed, 395 insertions(+), 45 deletions(-) create mode 100644 core/src/mindustry/editor/MapProcessorsDialog.java create mode 100644 core/src/mindustry/ui/dialogs/IconSelectDialog.java diff --git a/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java b/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java index 5ca28c0370..64b12ca33d 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java +++ b/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java @@ -57,6 +57,9 @@ public class AssetsProcess extends BaseProcessor{ ichtype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectIntMap.class, String.class), "codes", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectIntMap<>()").build()); + ichtype.addField(FieldSpec.builder(ParameterizedTypeName.get(IntMap.class, String.class), + "codeToName", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new IntMap<>()").build()); + ObjectSet used = new ObjectSet<>(); for(Jval val : icons.get("glyphs").asArray()){ @@ -67,7 +70,9 @@ public class AssetsProcess extends BaseProcessor{ int code = val.getInt("code", 0); iconcAll.append((char)code); ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).addJavadoc(String.format("\\u%04x", code)).initializer("'" + ((char)code) + "'").build()); + ichinit.addStatement("codes.put($S, $L)", name, code); + ichinit.addStatement("codeToName.put($L, $S)", code, name); ictype.addField(TextureRegionDrawable.class, name + "Small", Modifier.PUBLIC, Modifier.STATIC); icload.addStatement(name + "Small = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.def, (char)" + code + ")"); diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 642330901d..eea3c1046d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -445,6 +445,11 @@ editor.rules = Rules editor.generation = Generation editor.objectives = Objectives editor.locales = Locale Bundles +editor.worldprocessors = World Processors +editor.worldprocessors.editname = Edit Name +editor.worldprocessors.none = [lightgray]No world processor blocks found!\nAdd one in the map editor, or use the \ue813 Add button below. +editor.worldprocessors.nospace = No free space to place a world processor!\nDid you fill the map with structures? Why would you do this? +editor.worldprocessors.delete.confirm = Are you sure you want to delete this world processor?\n\nIf it is surrounded by walls, it will be replaced by an environmental wall. editor.ingame = Edit In-Game editor.playtest = Playtest editor.publish.workshop = Publish On Workshop @@ -501,7 +506,9 @@ editor.default = [lightgray] details = Details... edit = Edit variables = Vars +logic.clear.confirm = Are you sure you want to clear all code from this processor? logic.globals = Built-in Variables + editor.name = Name: editor.spawn = Spawn Unit editor.removeunit = Remove Unit diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 552f0ac54f..3538a39ce6 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -137,6 +137,13 @@ public class Vars implements Loadable{ Color.valueOf("4b5ef1"), Color.valueOf("2cabfe"), }; + /** Icons available to the user for customization in certain dialogs. */ + public static final String[] accessibleIcons = { + "effect", "power", "logic", "units", "liquid", "production", "defense", "turret", "distribution", "crafting", + "settings", "cancel", "zoom", "ok", "star", "home", "pencil", "up", "down", "left", "right", + "hammer", "warning", "tree", "admin", "map", "modePvp", "terrain", + "modeSurvival", "commandRally", "commandAttack", + }; /** maximum TCP packet size */ public static final int maxTcpSize = 900; /** default server port */ diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index 9486ebea42..f6be1965b3 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -40,8 +40,6 @@ public class LogicAI extends AIController{ public PosTeam posTarget = PosTeam.create(); private ObjectSet radars = new ObjectSet<>(); - private float lastMoveX, lastMoveY; - private int lastPathId = 0; // LogicAI state should not be reset after reading. @Override @@ -51,14 +49,6 @@ public class LogicAI extends AIController{ @Override public void updateMovement(){ - if(control == LUnitControl.pathfind){ - if(!Mathf.equal(moveX, lastMoveX, 0.1f) || !Mathf.equal(moveY, lastMoveY, 0.1f)){ - lastPathId ++; - lastMoveX = moveX; - lastMoveY = moveY; - } - } - if(targetTimer > 0f){ targetTimer -= Time.delta; }else{ diff --git a/core/src/mindustry/ctype/UnlockableContent.java b/core/src/mindustry/ctype/UnlockableContent.java index 434be84c4f..2692c28a94 100644 --- a/core/src/mindustry/ctype/UnlockableContent.java +++ b/core/src/mindustry/ctype/UnlockableContent.java @@ -147,6 +147,11 @@ public abstract class UnlockableContent extends MappableContent{ return Fonts.getUnicodeStr(name); } + public int emojiChar(){ + return Fonts.getUnicode(name); + } + + public boolean hasEmoji(){ return Fonts.hasUnicodeStr(name); } diff --git a/core/src/mindustry/editor/MapEditorDialog.java b/core/src/mindustry/editor/MapEditorDialog.java index 56ce52d8e1..a4c2de9dc3 100644 --- a/core/src/mindustry/editor/MapEditorDialog.java +++ b/core/src/mindustry/editor/MapEditorDialog.java @@ -24,7 +24,6 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.io.*; import mindustry.maps.*; -import mindustry.type.*; import mindustry.ui.*; import mindustry.ui.dialogs.*; import mindustry.world.*; @@ -212,11 +211,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ margin(0); update(() -> { - if(Core.scene.getKeyboardFocus() instanceof Dialog && Core.scene.getKeyboardFocus() != this){ - return; - } - - if(Core.scene != null && Core.scene.getKeyboardFocus() == this){ + if(hasKeyboard()){ doInput(); } }); diff --git a/core/src/mindustry/editor/MapInfoDialog.java b/core/src/mindustry/editor/MapInfoDialog.java index 86a217e82e..ebc1f0b300 100644 --- a/core/src/mindustry/editor/MapInfoDialog.java +++ b/core/src/mindustry/editor/MapInfoDialog.java @@ -14,16 +14,15 @@ import mindustry.ui.dialogs.*; import static mindustry.Vars.*; public class MapInfoDialog extends BaseDialog{ - private final WaveInfoDialog waveInfo; - private final MapGenerateDialog generate; - private final CustomRulesDialog ruleInfo = new CustomRulesDialog(); - private final MapObjectivesDialog objectives = new MapObjectivesDialog(); - private final MapLocalesDialog locales = new MapLocalesDialog(); + private WaveInfoDialog waveInfo = new WaveInfoDialog(); + private MapGenerateDialog generate = new MapGenerateDialog(false); + private CustomRulesDialog ruleInfo = new CustomRulesDialog(); + private MapObjectivesDialog objectives = new MapObjectivesDialog(); + private MapLocalesDialog locales = new MapLocalesDialog(); + private MapProcessorsDialog processors = new MapProcessorsDialog(); public MapInfoDialog(){ super("@editor.mapinfo"); - this.waveInfo = new WaveInfoDialog(); - this.generate = new MapGenerateDialog(false); addCloseButton(); @@ -108,7 +107,12 @@ public class MapInfoDialog extends BaseDialog{ ui.showException(e); } hide(); - }).marginLeft(10f).width(0f).colspan(2).center().growX(); + }).marginLeft(10f); + + r.button("@editor.worldprocessors", Icon.logic, style, () -> { + hide(); + processors.show(); + }).marginLeft(10f); }).colspan(2).center(); name.change(); diff --git a/core/src/mindustry/editor/MapProcessorsDialog.java b/core/src/mindustry/editor/MapProcessorsDialog.java new file mode 100644 index 0000000000..593ef429bb --- /dev/null +++ b/core/src/mindustry/editor/MapProcessorsDialog.java @@ -0,0 +1,155 @@ +package mindustry.editor; + +import arc.scene.style.*; +import arc.scene.ui.*; +import arc.scene.ui.layout.*; +import arc.struct.*; +import arc.util.*; +import mindustry.*; +import mindustry.content.*; +import mindustry.game.*; +import mindustry.gen.*; +import mindustry.ui.*; +import mindustry.ui.dialogs.*; +import mindustry.world.*; +import mindustry.world.blocks.environment.*; +import mindustry.world.blocks.logic.*; +import mindustry.world.blocks.logic.LogicBlock.*; + +import static mindustry.Vars.*; + +public class MapProcessorsDialog extends BaseDialog{ + private IconSelectDialog iconSelect = new IconSelectDialog(); + private TextField search; + private Seq processors = new Seq<>(); + private Table list; + + public MapProcessorsDialog(){ + super("@editor.worldprocessors"); + + shown(this::setup); + + addCloseButton(); + buttons.button("@add", Icon.add, () -> { + boolean foundAny = false; + + outer: + for(int y = 0; y < Vars.world.height(); y++){ + for(int x = 0; x < Vars.world.width(); x++){ + Tile tile = Vars.world.rawTile(x, y); + if(!tile.synthetic()){ + foundAny = true; + tile.setNet(Blocks.worldProcessor, Team.sharded, 0); + if(ui.editor.isShown()){ + Vars.editor.renderer.updatePoint(x, y); + } + break outer; + } + } + } + + if(!foundAny){ + ui.showErrorMessage("@editor.worldprocessors.nospace"); + }else{ + setup(); + } + }).size(210f, 64f); + + cont.top(); + getCell(cont).grow(); + + cont.table(s -> { + s.image(Icon.zoom).padRight(8); + search = s.field(null, text -> rebuild()).growX().get(); + search.setMessageText("@players.search"); + }).width(440f).fillX().padBottom(4).row(); + + cont.pane(t -> { + list = t; + }); + } + + private void rebuild(){ + list.clearChildren(); + + if(processors.isEmpty()){ + list.add("@editor.worldprocessors.none"); + }else{ + Table t = list; + var text = search.getText().toLowerCase(); + + t.defaults().pad(4f); + float h = 50f; + for(var build : processors){ + if(build instanceof LogicBuild log && (text.isEmpty() || (log.tag != null && log.tag.toLowerCase().contains(text)))){ + + t.button(log.iconTag == 0 ? Styles.none : new TextureRegionDrawable(Fonts.getLargeIcon(Fonts.unicodeToName(log.iconTag))), Styles.graySquarei, iconMed, () -> { + iconSelect.show(ic -> { + log.iconTag = (char)ic; + rebuild(); + }); + }).size(h); + + t.button((log.tag == null ? "\n" : "[accent]" + log.tag + "\n") + "[lightgray][[" + log.tile.x + ", " + log.tile.y + "]", Styles.grayt, () -> { + //TODO: bug: if you edit name inside of the edit dialog, it won't show up in the list properly + log.showEditDialog(); + }).size(Vars.mobile ? 390f : 450f, h).margin(10f).with(b -> { + b.getLabel().setAlignment(Align.left, Align.left); + }); + + t.button(Icon.pencil, Styles.graySquarei, Vars.iconMed, () -> { + ui.showTextInput("", "@editor.name", LogicBlock.maxNameLength, log.tag == null ? "" : log.tag, tag -> { + //bypass configuration and set it directly in case privileged checks mess things up + log.tag = tag; + setup(); + }); + }).size(h); + + if(Vars.state.isGame() && state.isEditor()){ + t.button(Icon.eyeSmall, Styles.graySquarei, Vars.iconMed, () -> { + hide(); + control.input.config.showConfig(build); + control.input.panCamera(Tmp.v1.set(build)); + }).size(h); + } + + t.button(Icon.trash, Styles.graySquarei, iconMed, () -> { + ui.showConfirm("@editor.worldprocessors.delete.confirm", () -> { + boolean surrounded = true; + for(int i = 0; i < 4; i++){ + Tile other = build.tile.nearby(i); + if(other != null && !(other.block().privileged || other.block().isStatic())){ + surrounded = false; + break; + } + } + if(surrounded){ + build.tile.setNet(build.tile.floor().wall instanceof StaticWall ? build.tile.floor().wall : Blocks.stoneWall); + }else{ + build.tile.setNet(Blocks.air); + } + processors.remove(build); + rebuild(); + }); + }).size(h); + + t.row(); + } + } + } + } + + private void setup(){ + + processors.clear(); + + //scan the entire world for processor (Groups.build can be empty, indexer is probably inaccurate) + Vars.world.tiles.eachTile(t -> { + if(t.isCenter() && t.block() == Blocks.worldProcessor){ + processors.add(t.build); + } + }); + + rebuild(); + } +} diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index d8c28c0464..04871c02fb 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -164,6 +164,12 @@ public class LCanvas extends Table{ this.statements.layout(); } + public void clearStatements(){ + jumps.clear(); + statements.clearChildren(); + statements.layout(); + } + StatementElem checkHovered(){ Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true); if(e != null){ diff --git a/core/src/mindustry/logic/LogicDialog.java b/core/src/mindustry/logic/LogicDialog.java index e49928e572..d35d040870 100644 --- a/core/src/mindustry/logic/LogicDialog.java +++ b/core/src/mindustry/logic/LogicDialog.java @@ -17,6 +17,7 @@ import mindustry.logic.LExecutor.*; import mindustry.logic.LStatements.*; import mindustry.ui.*; import mindustry.ui.dialogs.*; +import mindustry.world.blocks.logic.*; import static mindustry.Vars.*; import static mindustry.logic.LCanvas.*; @@ -92,11 +93,29 @@ public class LogicDialog extends BaseDialog{ TextButtonStyle style = Styles.flatt; t.defaults().size(280f, 60f).left(); + if(privileged && executor != null && executor.build != null){// && !ui.editor.isShown() + t.button("@editor.worldprocessors.editname", Icon.edit, style, () -> { + ui.showTextInput("", "@editor.name", LogicBlock.maxNameLength, executor.build.tag == null ? "" : executor.build.tag, tag -> { + if(privileged && executor != null && executor.build != null){ + executor.build.configure(tag); + //just in case of privilege shenanigans... + executor.build.tag = tag; + } + }); + dialog.hide(); + }).marginLeft(12f).row(); + } + + t.button("@clear", Icon.cancel, style, () -> { + ui.showConfirm("@logic.clear.confirm", () -> canvas.clearStatements()); + dialog.hide(); + }).marginLeft(12f).row(); + t.button("@schematic.copy", Icon.copy, style, () -> { dialog.hide(); Core.app.setClipboardText(canvas.save()); - }).marginLeft(12f); - t.row(); + }).marginLeft(12f).row(); + t.button("@schematic.copy.import", Icon.download, style, () -> { dialog.hide(); try{ diff --git a/core/src/mindustry/ui/Fonts.java b/core/src/mindustry/ui/Fonts.java index 6dd4ff1c24..29945aa9d2 100644 --- a/core/src/mindustry/ui/Fonts.java +++ b/core/src/mindustry/ui/Fonts.java @@ -30,6 +30,7 @@ public class Fonts{ private static final String mainFont = "fonts/font.woff"; private static final ObjectSet unscaled = ObjectSet.with("iconLarge"); private static ObjectIntMap unicodeIcons = new ObjectIntMap<>(); + private static IntMap unicodeToName = new IntMap<>(); private static ObjectMap stringIcons = new ObjectMap<>(); private static ObjectMap largeIcons = new ObjectMap<>(); private static TextureRegion[] iconTable; @@ -95,12 +96,16 @@ public class Fonts{ }})).loaded = f -> Fonts.logic = f; } + public static @Nullable String unicodeToName(int unicode){ + return unicodeToName.get(unicode, () -> Iconc.codeToName.get(unicode)); + } + public static TextureRegion getLargeIcon(String name){ return largeIcons.get(name, () -> { var region = new TextureRegion(); int code = Iconc.codes.get(name, '\uF8D4'); var glyph = iconLarge.getData().getGlyph((char)code); - if(glyph == null) return Core.atlas.find("error"); + if(glyph == null) return Core.atlas.find(name); region.set(iconLarge.getRegion().texture); region.set(glyph.u, glyph.v2, glyph.u2, glyph.v); return region; @@ -127,6 +132,7 @@ public class Fonts{ unicodeIcons.put(nametex[0], ch); stringIcons.put(nametex[0], ((char)ch) + ""); + unicodeToName.put(ch, texture); Vec2 out = Scaling.fit.apply(region.width, region.height, size, size); diff --git a/core/src/mindustry/ui/Styles.java b/core/src/mindustry/ui/Styles.java index 416aeb3c2b..b25c2d70c2 100644 --- a/core/src/mindustry/ui/Styles.java +++ b/core/src/mindustry/ui/Styles.java @@ -73,6 +73,8 @@ public class Styles{ geni, /** Gray, toggleable, no background. */ grayi, + /** Gray square background, standard behavior. Equivalent to grayt. */ + graySquarei, /** Flat, square, black background. */ flati, /** Square border. */ @@ -288,6 +290,14 @@ public class Styles{ imageUpColor = Color.lightGray; imageDownColor = Color.white; }}; + graySquarei = new ImageButtonStyle(){{ + imageUpColor = Color.white; + imageDownColor = Color.lightGray; + + over = flatOver; + down = flatOver; + up = grayPanel; + }}; flati = new ImageButtonStyle(){{ down = flatOver; up = black; diff --git a/core/src/mindustry/ui/dialogs/DatabaseDialog.java b/core/src/mindustry/ui/dialogs/DatabaseDialog.java index 9ef878c280..9fdefa9e1c 100644 --- a/core/src/mindustry/ui/dialogs/DatabaseDialog.java +++ b/core/src/mindustry/ui/dialogs/DatabaseDialog.java @@ -45,7 +45,7 @@ public class DatabaseDialog extends BaseDialog{ void rebuild(){ all.clear(); - var text = search.getText(); + var text = search.getText().toLowerCase(); Seq[] allContent = Vars.content.getContentMap(); @@ -54,7 +54,7 @@ public class DatabaseDialog extends BaseDialog{ Seq array = allContent[j] .select(c -> c instanceof UnlockableContent u && !u.isHidden() && - (text.isEmpty() || u.localizedName.toLowerCase().contains(text.toLowerCase()))).as(); + (text.isEmpty() || u.localizedName.toLowerCase().contains(text))).as(); if(array.size == 0) continue; all.add("@content." + type.name() + ".name").growX().left().color(Pal.accent); diff --git a/core/src/mindustry/ui/dialogs/IconSelectDialog.java b/core/src/mindustry/ui/dialogs/IconSelectDialog.java new file mode 100644 index 0000000000..153d25761d --- /dev/null +++ b/core/src/mindustry/ui/dialogs/IconSelectDialog.java @@ -0,0 +1,74 @@ +package mindustry.ui.dialogs; + +import arc.*; +import arc.func.*; +import arc.scene.style.*; +import arc.scene.ui.*; +import arc.scene.ui.layout.*; +import arc.util.*; +import mindustry.ctype.*; +import mindustry.gen.*; +import mindustry.graphics.*; +import mindustry.ui.*; + +import static mindustry.Vars.*; + +public class IconSelectDialog extends Dialog{ + private Intc consumer = i -> Log.info("you have mere seconds"); + + public IconSelectDialog(){ + closeOnBack(); + setFillParent(true); + + cont.pane(t -> { + resized(true, () -> { + t.clearChildren(); + t.marginRight(19f); + t.defaults().size(48f); + + t.button(Icon.none, Styles.flati, () -> { + hide(); + consumer.get(0); + }); + + int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f)); + + int i = 1; + for(var key : accessibleIcons){ + var value = Icon.icons.get(key); + + t.button(value, Styles.flati, () -> { + hide(); + consumer.get(Iconc.codes.get(key)); + }); + + if(++i % cols == 0) t.row(); + } + + for(ContentType ctype : defaultContentIcons){ + t.row(); + t.image().colspan(cols).growX().width(Float.NEGATIVE_INFINITY).height(3f).color(Pal.accent); + t.row(); + + i = 0; + for(UnlockableContent u : content.getBy(ctype).as()){ + if(!u.isHidden() && u.unlocked()){ + t.button(new TextureRegionDrawable(u.uiIcon), Styles.flati, iconMed, () -> { + hide(); + consumer.get(u.emojiChar()); + }); + + if(++i % cols == 0) t.row(); + } + } + } + }); + }); + buttons.button("@back", Icon.left, this::hide).size(210f, 64f); + } + + public void show(Intc listener){ + consumer = listener; + super.show(); + } +} diff --git a/core/src/mindustry/ui/dialogs/PausedDialog.java b/core/src/mindustry/ui/dialogs/PausedDialog.java index 129e64d658..b854aa1eb9 100644 --- a/core/src/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/mindustry/ui/dialogs/PausedDialog.java @@ -1,11 +1,13 @@ package mindustry.ui.dialogs; import arc.*; +import mindustry.editor.*; import mindustry.gen.*; import static mindustry.Vars.*; public class PausedDialog extends BaseDialog{ + private MapProcessorsDialog processors = new MapProcessorsDialog(); private SaveDialog save = new SaveDialog(); private LoadDialog load = new LoadDialog(); private boolean wasClient = false; @@ -49,13 +51,22 @@ public class PausedDialog extends BaseDialog{ cont.row(); - cont.button("@hostserver", Icon.host, () -> { + //the button runs out of space when the editor button is added, so use the mobile text + cont.button(state.isEditor() ? "@hostserver.mobile" : "@hostserver", Icon.host, () -> { if(net.server() && steam){ platform.inviteFriends(); }else{ ui.host.show(); } - }).disabled(b -> !((steam && net.server()) || !net.active())).colspan(2).width(dw * 2 + 10f).update(e -> e.setText(net.server() && steam ? "@invitefriends" : "@hostserver")); + }).disabled(b -> !((steam && net.server()) || !net.active())).colspan(state.isEditor() ? 1 : 2).width(state.isEditor() ? dw : dw * 2 + 10f) + .update(e -> e.setText(net.server() && steam ? "@invitefriends" : state.isEditor() ? "@hostserver.mobile" : "@hostserver")); + + if(state.isEditor()){ + cont.button("@editor.worldprocessors", Icon.logic, () -> { + hide(); + processors.show(); + }); + } cont.row(); diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 0c43dbff42..c8f4fb44c2 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -43,13 +43,6 @@ import static mindustry.graphics.g3d.PlanetRenderer.*; import static mindustry.ui.dialogs.PlanetDialog.Mode.*; public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ - static final String[] defaultIcons = { - "effect", "power", "logic", "units", "liquid", "production", "defense", "turret", "distribution", "crafting", - "settings", "cancel", "zoom", "ok", "star", "home", "pencil", "up", "down", "left", "right", - "hammer", "warning", "tree", "admin", "map", "modePvp", "terrain", - "modeSurvival", "commandRally", "commandAttack", - }; - //if true, enables launching anywhere for testing public static boolean debugSelect = false; public static float sectorShowDuration = 60f * 2.4f; @@ -1054,6 +1047,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ Icon.icons.get(sector.info.icon + "Small"); title.button(icon == null ? Icon.noneSmall : icon, Styles.clearNonei, iconSmall, () -> { + //TODO use IconSelectDialog new Dialog(""){{ closeOnBack(); setFillParent(true); @@ -1080,7 +1074,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f)); int i = 1; - for(var key : defaultIcons){ + for(var key : accessibleIcons){ var value = Icon.icons.get(key); t.button(value, Styles.squareTogglei, () -> { diff --git a/core/src/mindustry/ui/dialogs/SchematicsDialog.java b/core/src/mindustry/ui/dialogs/SchematicsDialog.java index ff444b8991..3907f10dbe 100644 --- a/core/src/mindustry/ui/dialogs/SchematicsDialog.java +++ b/core/src/mindustry/ui/dialogs/SchematicsDialog.java @@ -398,6 +398,7 @@ public class SchematicsDialog extends BaseDialog{ closeOnBack(); setFillParent(true); + //TODO: use IconSelectDialog cont.pane(t -> { resized(true, () -> { t.clearChildren(); @@ -407,7 +408,7 @@ public class SchematicsDialog extends BaseDialog{ int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f)); int i = 0; - for(String icon : PlanetDialog.defaultIcons){ + for(String icon : accessibleIcons){ String out = (char)Iconc.codes.get(icon) + ""; if(tags.contains(out)) continue; diff --git a/core/src/mindustry/world/blocks/logic/LogicBlock.java b/core/src/mindustry/world/blocks/logic/LogicBlock.java index 3b5abe456e..764126991b 100644 --- a/core/src/mindustry/world/blocks/logic/LogicBlock.java +++ b/core/src/mindustry/world/blocks/logic/LogicBlock.java @@ -3,6 +3,8 @@ package mindustry.world.blocks.logic; import arc.Graphics.*; import arc.Graphics.Cursor.*; import arc.func.*; +import arc.graphics.*; +import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; import arc.scene.ui.layout.*; @@ -10,6 +12,7 @@ import arc.struct.Bits; import arc.struct.*; import arc.util.*; import arc.util.io.*; +import arc.util.pooling.*; import mindustry.ai.types.*; import mindustry.core.*; import mindustry.gen.*; @@ -31,6 +34,7 @@ import static mindustry.Vars.*; public class LogicBlock extends Block{ private static final int maxByteLen = 1024 * 100; + public static final int maxNameLength = 32; public int maxInstructionScale = 5; public int instructionsPerTick = 1; @@ -56,6 +60,14 @@ public class LogicBlock extends Block{ build.readCompressed(data, true); }); + config(String.class,(LogicBuild build, String data) -> { + if(!accessible() || !privileged) return; + + if(data != null && data.length() < maxNameLength){ + build.tag = data; + } + }); + config(Integer.class, (LogicBuild entity, Integer pos) -> { if(!accessible()) return; @@ -235,6 +247,9 @@ public class LogicBlock extends Block{ public boolean checkedDuplicates = false; //dynamic only for privileged processors public int ipt = instructionsPerTick; + /** Display name, for convenience. This is currently only available for world processors. */ + public @Nullable String tag; + public char iconTag; /** Block of code to run after load. */ public @Nullable Runnable loadBlock; @@ -563,9 +578,45 @@ public class LogicBlock extends Block{ @Override public void drawSelect(){ + if(!accessible()) return; + Groups.unit.each(u -> u.controller() instanceof LogicAI ai && ai.controller == this, unit -> { Drawf.square(unit.x, unit.y, unit.hitSize, unit.rotation + 45); }); + + //draw tag over processor (world processor only) + if(!(renderer.pixelate || !privileged || tag == null || tag.isEmpty())){ + Font font = Fonts.outline; + GlyphLayout l = Pools.obtain(GlyphLayout.class, GlyphLayout::new); + boolean ints = font.usesIntegerPositions(); + font.getData().setScale(1 / 4f / Scl.scl(1f)); + font.setUseIntegerPositions(false); + + l.setText(font, tag, Color.white, 90f, Align.left, true); + float offset = 1f; + + //Draw.color(0f, 0f, 0f, 0.1f); + //Fill.rect(x, y + tilesize/2f - l.height/2f - offset, l.width + offset*2f, l.height + offset*2f); + Draw.color(); + font.setColor(1f, 1f, 1f, 0.5f); + font.draw(tag, x - l.width/2f, y + tilesize + 2f - offset, 90f, Align.left, true); + font.setUseIntegerPositions(ints); + + font.getData().setScale(1f); + + Pools.free(l); + } + + if(iconTag != 0){ + TextureRegion icon = Fonts.getLargeIcon(Fonts.unicodeToName(iconTag)); + if(icon.found()){ + Draw.alpha(0.5f); + + Draw.rect(icon, x, y, tilesize, tilesize / icon.ratio()); + + Draw.color(); + } + } } public boolean validLink(Building other){ @@ -579,9 +630,11 @@ public class LogicBlock extends Block{ @Override public void buildConfiguration(Table table){ - table.button(Icon.pencil, Styles.cleari, () -> { - ui.logic.show(code, executor, privileged, code -> configure(compress(code, relativeConnections()))); - }).size(40); + table.button(Icon.pencil, Styles.cleari, this::showEditDialog).size(40); + } + + public void showEditDialog(){ + ui.logic.show(code, executor, privileged, code -> configure(compress(code, relativeConnections()))); } @Override @@ -601,7 +654,7 @@ public class LogicBlock extends Block{ @Override public byte version(){ - return 2; + return 3; } @Override @@ -638,6 +691,9 @@ public class LogicBlock extends Block{ if(privileged){ write.s(Mathf.clamp(ipt, 1, maxInstructionsPerTick)); } + + TypeIO.writeString(write, tag); + write.s(iconTag); } @Override @@ -694,6 +750,11 @@ public class LogicBlock extends Block{ ipt = Mathf.clamp(read.s(), 1, maxInstructionsPerTick); } + if(revision >= 3){ + tag = TypeIO.readString(read); + iconTag = (char)read.us(); + } + } } } diff --git a/gradle.properties b/gradle.properties index 8226af606b..7dfd79eecb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,4 +25,4 @@ org.gradle.caching=true #used for slow jitpack builds; TODO see if this actually works org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.connectionTimeout=100000 -archash=5a944efcc0 +archash=f0d4fdbf89