From fba935c52787ba5f37f9ac769f6f064d404245c2 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 18 Jul 2025 18:14:32 -0400 Subject: [PATCH] Char tile picker support --- .../mindustry/ui/fragments/HudFragment.java | 28 ++++++------ .../ui/fragments/PlacementFragment.java | 5 ++- core/src/mindustry/world/Block.java | 20 ++++++++- core/src/mindustry/world/Build.java | 4 +- .../blocks/environment/CharacterOverlay.java | 43 ++++++++++++++++++- .../blocks/environment/ColoredFloor.java | 34 ++++++++++++++- .../world/blocks/environment/ColoredWall.java | 13 +++++- 7 files changed, 123 insertions(+), 24 deletions(-) diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index 704fb279d7..969a4e7d9f 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -72,6 +72,9 @@ public class HudFragment{ } }); + Table[] configTable = {null}; + Block[] lastBlock = {null}; + cont.table(search -> { search.image(Icon.zoom).padRight(8); search.field("", text -> rebuildBlockSelection(blockSelection, text)).growX() @@ -79,23 +82,18 @@ public class HudFragment{ }).growX().pad(-2).padLeft(6f); cont.row(); cont.collapser(t -> { - t.button(b -> { - b.margin(4f); - b.left(); - b.table(Tex.pane, in -> { - in.image(Tex.whiteui).update(i -> { - if(control.input.block != null && control.input.block.lastConfig instanceof Integer col){ - i.color.set(col | 0xff); - } - }).grow(); - }).margin(4).size(50f).padRight(10); - b.add("@color"); - }, Styles.cleart, () -> ui.picker.show(control.input.block != null && control.input.block.lastConfig instanceof Integer col ? new Color(col | 0xff) : new Color(Color.white), false, col -> { + configTable[0] = t; + }, () -> control.input.block != null && control.input.block.editorConfigurable).with(c -> c.setEnforceMinSize(true)).update(col -> { + + if(lastBlock[0] != control.input.block){ + configTable[0].clear(); if(control.input.block != null){ - control.input.block.lastConfig = col.rgba8888(); + control.input.block.buildEditorConfig(configTable[0]); + col.invalidateHierarchy(); } - })).left().width(250f).pad(3f).row(); - }, () -> control.input.block != null && control.input.block.showColorEdit).with(c -> c.setEnforceMinSize(true)).growX().row(); + lastBlock[0] = control.input.block; + } + }).growX().row(); cont.add(pane).expandY().top().left(); rebuildBlockSelection(blockSelection, ""); diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index 2b1a0ef25c..b6b4ad7846 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -153,8 +153,8 @@ public class PlacementFragment{ tile.floor() != Blocks.air ? tile.floor() : null; } - if(tryBlock != null && tryBlock.showColorEdit && tryConfig == null){ - tryConfig = tile.extraData; + if(tryBlock != null && build == null && tryConfig == null){ + tryConfig = tryBlock.getConfig(tile); } if(tryBlock != null && ((tryBlock.isVisible() && unlocked(tryBlock)) || state.rules.editor)){ @@ -163,6 +163,7 @@ public class PlacementFragment{ if(tryBlock.isVisible()){ currentCategory = input.block.category; } + tryBlock.onPicked(tile); return true; } } diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index dbc4a6945d..33ada2e966 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -80,8 +80,8 @@ public class Block extends UnlockableContent implements Senseable{ public boolean displayFlow = true; /** whether this block is visible in the editor */ public boolean inEditor = true; - /** if true, a color picker will be shown for the lastConfig field in the in-game-editor, and will be assigned as an integer. */ - public boolean showColorEdit; + /** if true, {@link #buildEditorConfig(Table)} will be called for configuring this block in the editor. */ + public boolean editorConfigurable; /** the last configuration value applied to this block. */ public @Nullable Object lastConfig; /** whether to save the last config and apply it to newly placed blocks */ @@ -701,6 +701,10 @@ public class Block extends UnlockableContent implements Senseable{ return liquidFilter[liq.id]; } + public boolean canReplace(Tile tile, Block other){ + return canReplace(other); + } + public boolean canReplace(Block other){ if(other.alwaysReplace) return true; if(other.privileged) return false; @@ -947,6 +951,18 @@ public class Block extends UnlockableContent implements Senseable{ return (envEnabled & env) != 0 && (envDisabled & env) == 0 && (envRequired == 0 || (envRequired & env) == envRequired); } + /** Called to set up configuration UI in the editor. {@link #editorConfigurable} must be true. + * Config value should be assigned to lastConfig.*/ + public void buildEditorConfig(Table table){} + + /** Called when the block is picked (middle click). Clientside only! */ + public void onPicked(Tile tile){} + + /** @return the config value returned when this block is picked on a certain tile. This is only called for non-buildings. */ + public Object getConfig(Tile tile){ + return null; + } + /** Called when this block is set on the specified tile. */ public void blockChanged(Tile tile){} diff --git a/core/src/mindustry/world/Build.java b/core/src/mindustry/world/Build.java index 6948e6910e..402684dde6 100644 --- a/core/src/mindustry/world/Build.java +++ b/core/src/mindustry/world/Build.java @@ -216,7 +216,7 @@ public class Build{ //floors have different checks if(type.isFloor()){ - return type.isOverlay() ? tile.overlay() != type : tile.floor() != type; + return type.isOverlay() ? type.canReplace(tile, tile.overlay()) : type.canReplace(tile, tile.floor()); } //campaign darkness check @@ -247,7 +247,7 @@ public class Build{ !check.floor().placeableOn && !type.ignoreBuildDarkness || //solid floor //when you have a payload, you cannot place blocks on things, even if normal placement rules allow it. this is a hack that assumes checkVisible = true means it's coming from a payload (!checkVisible && checkCoreRadius && !check.block().alwaysReplace) || //replacing a block that should be replaced (e.g. payload placement) - !(((type.canReplace(check.block()) || (check.build != null && check.build.canBeReplaced(type)) || (type == check.block && team != Team.derelict && state.rules.derelictRepair && check.team() == Team.derelict)) || //can replace type OR can replace derelict block of same type + !(((type.canReplace(check, check.block()) || (check.build != null && check.build.canBeReplaced(type)) || (type == check.block && team != Team.derelict && state.rules.derelictRepair && check.team() == Team.derelict)) || //can replace type OR can replace derelict block of same type (check.build instanceof ConstructBuild build && build.current == type && check.centerX() == tile.x && check.centerY() == tile.y)) && //same type in construction type.bounds(tile.x, tile.y, Tmp.r1).grow(0.01f).contains(check.block.bounds(check.centerX(), check.centerY(), Tmp.r2))) || //no replacement (type.requiresWater && check.floor().liquidDrop != Liquids.water) //requires water but none found diff --git a/core/src/mindustry/world/blocks/environment/CharacterOverlay.java b/core/src/mindustry/world/blocks/environment/CharacterOverlay.java index c57bd0de36..d95afed719 100644 --- a/core/src/mindustry/world/blocks/environment/CharacterOverlay.java +++ b/core/src/mindustry/world/blocks/environment/CharacterOverlay.java @@ -2,13 +2,16 @@ package mindustry.world.blocks.environment; import arc.graphics.*; import arc.graphics.g2d.*; +import arc.scene.ui.layout.*; import arc.util.*; +import mindustry.*; import mindustry.annotations.Annotations.*; +import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.world.*; public class CharacterOverlay extends OverlayFloor{ - /** This is a reduced character set! It is not ASCII! */ + /** This is a special reduced character set that fits in 6 bits! It is not ASCII! */ public static final String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\"!?.,;:()[]{}<>|/@\\^-%+=#_&~"; public @Load(value = "character-overlay#", length = 64) TextureRegion[] letterRegions; @@ -20,6 +23,8 @@ public class CharacterOverlay extends OverlayFloor{ variants = 0; rotate = true; drawArrow = false; + saveConfig = true; + editorConfigurable = true; } @Override @@ -30,6 +35,42 @@ public class CharacterOverlay extends OverlayFloor{ Draw.color(); } + @Override + public Object getConfig(Tile tile){ + return (int)tile.overlayData; + } + + @Override + public void drawPlanRegion(BuildPlan plan, Eachable list){ + byte data = 0; + + if(plan.config instanceof Integer i){ + data = i.byteValue(); + } + + int letterChar = CharOverlayData.character(data); + + TextureRegion reg = letterRegions[letterChar]; + Draw.tint(color); + Draw.rect(reg, plan.drawx(), plan.drawy(), plan.rotation * 90); + Draw.tint(Color.white); + } + + @Override + public void onPicked(Tile tile){ + Vars.control.input.rotation = CharOverlayData.rotation(tile.overlayData); + } + + @Override + public void buildEditorConfig(Table table){ + char value = chars.charAt(lastConfig instanceof Integer i ? CharOverlayData.character(i.byteValue()) : 0); + table.field(value + "", val -> { + if(val.length() == 1){ + lastConfig = (int)charToData(val.charAt(0)); + } + }).valid(t -> t.length() == 1 && chars.indexOf(Character.toUpperCase(t.charAt(0))) != -1).maxTextLength(1); + } + @Override public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){ byte data = 0; diff --git a/core/src/mindustry/world/blocks/environment/ColoredFloor.java b/core/src/mindustry/world/blocks/environment/ColoredFloor.java index 0b8dff8bba..0a9ca1fe27 100644 --- a/core/src/mindustry/world/blocks/environment/ColoredFloor.java +++ b/core/src/mindustry/world/blocks/environment/ColoredFloor.java @@ -3,13 +3,17 @@ package mindustry.world.blocks.environment; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.geom.*; +import arc.scene.ui.layout.*; import arc.util.*; import mindustry.*; import mindustry.entities.units.*; import mindustry.gen.*; +import mindustry.ui.*; import mindustry.world.*; import mindustry.world.blocks.*; +import static mindustry.Vars.*; + public class ColoredFloor extends Floor{ /** If the alpha value of the color is set to this value, different colors are ignored and no border is drawn. */ public static final int flagIgnoreDifferentColor = 1; @@ -24,7 +28,7 @@ public class ColoredFloor extends Floor{ public ColoredFloor(String name){ super(name); saveData = true; - showColorEdit = true; + editorConfigurable = true; saveConfig = true; } @@ -34,6 +38,34 @@ public class ColoredFloor extends Floor{ lastConfig = defaultColorRgba = defaultColor.rgba(); } + @Override + public void buildEditorConfig(Table table){ + showColorEdit(table, this); + } + + public static void showColorEdit(Table t, Block block){ + t.button(b -> { + b.margin(4f); + b.left(); + b.table(Tex.pane, in -> { + in.image(Tex.whiteui).update(i -> { + if(block.lastConfig instanceof Integer col){ + i.color.set(col | 0xff); + } + }).grow(); + }).margin(4).size(50f).padRight(10); + b.add("@color"); + }, Styles.cleart, () -> + ui.picker.show( + block.lastConfig instanceof Integer col ? new Color(col | 0xff) : new Color(Color.white), false, + col -> block.lastConfig = col.rgba8888())).left().width(250f).pad(3f).row(); + } + + @Override + public Object getConfig(Tile tile){ + return tile.extraData; + } + @Override public void drawBase(Tile tile){ //make sure to mask out the alpha channel - it's generally undesirable, and leads to invisible blocks when the data is not initialized diff --git a/core/src/mindustry/world/blocks/environment/ColoredWall.java b/core/src/mindustry/world/blocks/environment/ColoredWall.java index 268daa4306..4f6fcc23e3 100644 --- a/core/src/mindustry/world/blocks/environment/ColoredWall.java +++ b/core/src/mindustry/world/blocks/environment/ColoredWall.java @@ -2,6 +2,7 @@ package mindustry.world.blocks.environment; import arc.graphics.*; import arc.graphics.g2d.*; +import arc.scene.ui.layout.*; import arc.util.*; import mindustry.entities.units.*; import mindustry.gen.*; @@ -19,7 +20,7 @@ public class ColoredWall extends StaticWall{ public ColoredWall(String name){ super(name); saveData = true; - showColorEdit = true; + editorConfigurable = true; saveConfig = true; } @@ -29,6 +30,16 @@ public class ColoredWall extends StaticWall{ lastConfig = defaultColorRgba = defaultColor.rgba(); } + @Override + public Object getConfig(Tile tile){ + return tile.extraData; + } + + @Override + public void buildEditorConfig(Table table){ + ColoredFloor.showColorEdit(table, this); + } + @Override public void drawBase(Tile tile){ //make sure to mask out the alpha channel - it's generally undesirable, and leads to invisible blocks when thtoe data is not initialized