diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index c9f5ae6d06..a0aea04348 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -2326,7 +2326,7 @@ lst.setprop = Sets a property of a unit or building. lst.effect = Create a particle effect. lst.sync = Sync a variable across the network.\nLimited to 20 times a second per variable. lst.makemarker = Create a new logic marker in the world.\nAn ID to identify this marker must be provided.\nMarkers currently limited to 20,000 per world. -lst.setmarker = Set a property for a marker.\nThe ID used must be the same as in the Make Marker instruction. +lst.setmarker = Set a property for a marker.\nThe ID used must be the same as in the Make Marker instruction.\n[accent]null []values are ignored. lst.localeprint = Add map locale property value to the text buffer.\nTo set map locale bundles in map editor, check [accent]Map Info > Locale Bundles[].\nIf client is a mobile device, tries to print a property ending in ".mobile" first. logic.nounitbuild = [red]Unit building logic is not allowed here. @@ -2480,3 +2480,8 @@ lenum.build = Build a structure. lenum.getblock = Fetch a building, floor and type at coordinates.\nUnit must be in range of position.\nSolid non-buildings will have the type [accent]@solid[]. lenum.within = Check if unit is near a position. lenum.boost = Start/stop boosting. + +lenum.flushtext = Flush print buffer's content to marker, if applicable.\nIf fetch is set to true, tries to fetch properties from map locale bundle or game's bundle. +lenum.texture = Texture name straight from game's texture atlas (using kebab-case naming style).\nIf printFlush is set to true, consumes text buffer content as text argument. +lenum.texturesize = Size of texture in tiles. Zero value scales marker width to original texture's size. +lenum.autoscale = Whether to scale marker corresponding to player's zoom level. diff --git a/core/src/mindustry/core/GameState.java b/core/src/mindustry/core/GameState.java index 5d9ee3515e..7c89abe05d 100644 --- a/core/src/mindustry/core/GameState.java +++ b/core/src/mindustry/core/GameState.java @@ -1,11 +1,9 @@ package mindustry.core; import arc.*; -import arc.struct.*; import arc.util.*; import mindustry.game.EventType.*; import mindustry.game.*; -import mindustry.game.MapObjectives.*; import mindustry.gen.*; import mindustry.maps.*; import mindustry.type.*; @@ -35,7 +33,7 @@ public class GameState{ /** Statistics for this save/game. Displayed after game over. */ public GameStats stats = new GameStats(); /** Markers not linked to objectives. Controlled by world processors. */ - public IntMap markers = new IntMap<>(); + public MapMarkers markers = new MapMarkers(); /** Locale-specific string bundles of current map */ public MapLocales mapLocales = new MapLocales(); /** Global attributes of the environment, calculated by weather. */ diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index 7769d3be76..13b1ead08f 100644 --- a/core/src/mindustry/core/Renderer.java +++ b/core/src/mindustry/core/Renderer.java @@ -372,6 +372,23 @@ public class Renderer implements ApplicationListener{ }); } + //draw objective markers + state.rules.objectives.eachRunning(obj -> { + for(var marker : obj.markers){ + if(!marker.minimap){ + marker.drawWorld(); + } + } + }); + + for(var marker : state.markers){ + if(!marker.isHidden() && !marker.minimap){ + marker.drawWorld(); + } + } + + Draw.reset(); + Draw.draw(Layer.overlayUI, overlays::drawTop); if(state.rules.fog) Draw.draw(Layer.fogOfWar, fog::drawFog); Draw.draw(Layer.space, this::drawLanding); diff --git a/core/src/mindustry/editor/MapLocalesDialog.java b/core/src/mindustry/editor/MapLocalesDialog.java index 0a8098fb29..0ac5a53d6d 100644 --- a/core/src/mindustry/editor/MapLocalesDialog.java +++ b/core/src/mindustry/editor/MapLocalesDialog.java @@ -5,6 +5,8 @@ import arc.func.*; import arc.graphics.*; import arc.scene.style.*; import arc.scene.ui.*; +import arc.scene.ui.Button.*; +import arc.scene.ui.TextButton.*; import arc.scene.ui.layout.*; import arc.scene.utils.*; import arc.struct.*; @@ -23,6 +25,15 @@ import static mindustry.Vars.*; public class MapLocalesDialog extends BaseDialog{ /** Width of UI property card. */ private static final float cardWidth = 400f; + /** Style for filter options buttons */ + private static final TextButtonStyle filterStyle = new TextButtonStyle(){{ + up = down = checked = over = Tex.whitePane; + font = Fonts.outline; + fontColor = Color.lightGray; + overFontColor = Pal.accent; + disabledFontColor = Color.gray; + disabled = Styles.black; + }}; /** Icons for use in map locales dialog. */ private static final ContentType[] contentIcons = {ContentType.item, ContentType.block, ContentType.liquid, ContentType.status, ContentType.unit}; @@ -168,7 +179,7 @@ public class MapLocalesDialog extends BaseDialog{ String name = loc.toString(); if(locales.containsKey(name)){ - p.button(loc.getDisplayName(), Styles.flatTogglet, () -> { + p.button(loc.getDisplayName(Core.bundle.getLocale()), Styles.flatTogglet, () -> { if(name.equals(selectedLocale)) return; selectedLocale = name; @@ -195,8 +206,8 @@ public class MapLocalesDialog extends BaseDialog{ main.image().color(Pal.gray).height(3f).growX().expandY().top().row(); main.pane(p -> { - int cols = (Core.graphics.getWidth() - 380) / ((int)cardWidth + 10); - if(props.size == 0 || cols == 0){ + int cols = Math.max(1, (Core.graphics.getWidth() - 630) / ((int)cardWidth + 10)); + if(props.size == 0){ main.add("@empty").center().row(); return; } @@ -343,7 +354,7 @@ public class MapLocalesDialog extends BaseDialog{ String name = loc.toString(); if(!locales.containsKey(name)){ - t.button(loc.getDisplayName(), Styles.flatTogglet, () -> { + t.button(loc.getDisplayName(Core.bundle.getLocale()), Styles.flatTogglet, () -> { if(name.equals(selectedLocale)) return; locales.put(name, new StringMap()); @@ -524,12 +535,12 @@ public class MapLocalesDialog extends BaseDialog{ if(status == PropertyStatus.same && !showSame) continue; if(status != PropertyStatus.missing){ - var comparsionString = (searchByValue ? locales.get(name).get(key).toLowerCase() : loc.getDisplayName().toLowerCase()); + var comparsionString = (searchByValue ? locales.get(name).get(key).toLowerCase() : loc.getDisplayName(Core.bundle.getLocale()).toLowerCase()); if(!searchString.isEmpty() && !comparsionString.contains(searchString.toLowerCase())) continue; } colTables[i].table(Tex.whitePane, t -> { - t.add(loc.getDisplayName()).left().color(Pal.accent).row(); + t.add(loc.getDisplayName(Core.bundle.getLocale())).left().color(Pal.accent).row(); t.image().color(Pal.accent).fillX().row(); if(status == PropertyStatus.missing){ @@ -576,26 +587,24 @@ public class MapLocalesDialog extends BaseDialog{ }).padTop(5f); }).row(); - dialog.cont.table(Tex.whitePane, t -> - t.button("@locales.showcorrect", Icon.ok, Styles.nonet, () -> showCorrect = !showCorrect).update(b -> { + dialog.cont.button("@locales.showcorrect", Icon.ok, filterStyle, () -> showCorrect = !showCorrect).update(b -> { ((Image)b.getChildren().get(1)).setDrawable(showCorrect ? Icon.ok : Icon.cancel); b.setChecked(showCorrect); - }).grow().pad(15f)).size(450f, 100f).color(Pal.gray).padTop(50f); + }).size(450f, 100f).color(Pal.gray).padTop(65f); dialog.cont.row(); - dialog.cont.table(Tex.whitePane, t -> - t.button("@locales.showmissing", Icon.ok, Styles.nonet, () -> showMissing = !showMissing).update(b -> { - ((Image)b.getChildren().get(1)).setDrawable(showMissing ? Icon.ok : Icon.cancel); - b.setChecked(showMissing); - }).grow().pad(15f)).size(450f, 100f).color(Pal.accent).padTop(50f); + dialog.cont.button("@locales.showmissing", Icon.ok, filterStyle, () -> showMissing = !showMissing).update(b -> { + ((Image)b.getChildren().get(1)).setDrawable(showMissing ? Icon.ok : Icon.cancel); + b.setChecked(showMissing); + }).size(450f, 100f).color(Pal.accent).padTop(65f); dialog.cont.row(); - dialog.cont.table(Tex.whitePane, t -> - t.button("@locales.showsame", Icon.ok, Styles.nonet, () -> showSame = !showSame).update(b -> { - ((Image)b.getChildren().get(1)).setDrawable(showSame ? Icon.ok : Icon.cancel); - b.setChecked(showSame); - }).grow().pad(15f)).size(450f, 100f).color(Pal.techBlue).padTop(50f); + + dialog.cont.button("@locales.showsame", Icon.ok, filterStyle, () -> showSame = !showSame).update(b -> { + ((Image)b.getChildren().get(1)).setDrawable(showSame ? Icon.ok : Icon.cancel); + b.setChecked(showSame); + }).size(450f, 100f).color(Pal.techBlue).padTop(65f); dialog.buttons.button("@back", Icon.left, () -> { hidden.run(); diff --git a/core/src/mindustry/game/MapMarkers.java b/core/src/mindustry/game/MapMarkers.java new file mode 100644 index 0000000000..3afb6895ee --- /dev/null +++ b/core/src/mindustry/game/MapMarkers.java @@ -0,0 +1,72 @@ +package mindustry.game; + +import arc.struct.*; +import arc.util.*; +import mindustry.game.MapObjectives.*; +import mindustry.io.*; + +import java.io.*; +import java.util.*; + +public class MapMarkers implements Iterable{ + /** Maps marker unique ID to marker. */ + private IntMap map = new IntMap<>(); + /** Sequential list of markers. This allows for faster iteration than a map. */ + private Seq all = new Seq<>(false); + + public void add(int id, ObjectiveMarker marker){ + if(marker == null) return; + + var prev = map.put(id, marker); + if(prev != null){ + all.set(prev.arrayIndex, marker); + }else{ + all.add(marker); + marker.arrayIndex = all.size - 1; + } + } + + public void remove(int id){ + var prev = map.remove(id); + if(prev != null){ + if(all.size > prev.arrayIndex + 1){ //there needs to be something above the index to replace it with + all.remove(prev.arrayIndex); + //update its index + all.get(prev.arrayIndex).arrayIndex = prev.arrayIndex; + }else{ + //no sense updating the index of the replaced element when it was not replaced + all.remove(prev.arrayIndex); + } + } + } + + public @Nullable ObjectiveMarker get(int id){ + return map.get(id); + } + + public boolean has(int id){ + return get(id) != null; + } + + public int size(){ + return all.size; + } + + public void write(DataOutput stream) throws IOException{ + JsonIO.writeBytes(map, ObjectiveMarker.class, (DataOutputStream)stream); + } + + public void read(DataInput stream) throws IOException{ + all.clear(); + map = JsonIO.readBytes(IntMap.class, ObjectiveMarker.class, (DataInputStream)stream); + for(var entry : map.entries()){ + all.add(entry.value); + entry.value.arrayIndex = all.size - 1; + } + } + + @Override + public Iterator iterator(){ + return all.iterator(); + } +} diff --git a/core/src/mindustry/game/MapObjectives.java b/core/src/mindustry/game/MapObjectives.java index 264375ae1d..879ba2ad94 100644 --- a/core/src/mindustry/game/MapObjectives.java +++ b/core/src/mindustry/game/MapObjectives.java @@ -64,7 +64,8 @@ public class MapObjectives implements Iterable, Eachable, Eachable