Allow markers to use content/displays as textures (#11142)

* Allow markers to use content/displays as textures

* Don't process empty commands

* Allow canvases to be used as marker textures

* Proper Json serialization

---------

Co-authored-by: Anuken <arnukren@gmail.com>
This commit is contained in:
Redstonneur1256
2025-12-28 03:37:03 +01:00
committed by GitHub
parent 7d3e3155f7
commit 59c3b18a6f
5 changed files with 118 additions and 41 deletions

View File

@@ -290,6 +290,11 @@ public class MapObjectivesDialog extends BaseDialog{
}, () -> {}); }, () -> {});
}); });
setInterpreter(TextureHolder.class, (cont, name, type, field, remover, indexer, get, set) -> {
name(cont, name, remover, indexer);
cont.field(String.valueOf(get.get().value), s -> get.get().value = s).growX();
});
// Types that use the default interpreter. It would be nice if all types could use it, but I don't know how to reliably prevent classes like [? extends Content] from using it. // Types that use the default interpreter. It would be nice if all types could use it, but I don't know how to reliably prevent classes like [? extends Content] from using it.
for(var obj : MapObjectives.allObjectiveTypes) setInterpreter(obj.get().getClass(), defaultInterpreter()); for(var obj : MapObjectives.allObjectiveTypes) setInterpreter(obj.get().getClass(), defaultInterpreter());
for(var mark : MapObjectives.allMarkerTypes) setInterpreter(mark.get().getClass(), defaultInterpreter()); for(var mark : MapObjectives.allMarkerTypes) setInterpreter(mark.get().getClass(), defaultInterpreter());

View File

@@ -10,6 +10,9 @@ import arc.math.geom.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.io.*;
import arc.util.serialization.*;
import arc.util.serialization.Json.*;
import mindustry.*; import mindustry.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.core.*; import mindustry.core.*;
@@ -21,8 +24,12 @@ import mindustry.io.*;
import mindustry.logic.*; import mindustry.logic.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.logic.CanvasBlock.*;
import mindustry.world.blocks.logic.LogicDisplay.*;
import java.io.*;
import java.lang.annotation.*; import java.lang.annotation.*;
import java.nio.*;
import java.util.*; import java.util.*;
import static java.lang.annotation.ElementType.*; import static java.lang.annotation.ElementType.*;
@@ -665,7 +672,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
} }
/** Marker used for drawing various content to indicate something along with an objective. Mostly used as UI overlay. */ /** Marker used for drawing various content to indicate something along with an objective. Mostly used as UI overlay. */
public static abstract class ObjectiveMarker{ public static abstract class ObjectiveMarker implements JsonSerializable{
/** Internal use only! Do not access. */ /** Internal use only! Do not access. */
public transient int arrayIndex; public transient int arrayIndex;
@@ -694,7 +701,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
public void setText(String text, boolean fetch){} public void setText(String text, boolean fetch){}
public void setTexture(String textureName){} public void setTexture(Object texture){}
/** @return The localized type-name of this objective, defaulting to the class simple name without the "Marker" prefix. */ /** @return The localized type-name of this objective, defaulting to the class simple name without the "Marker" prefix. */
public String typeName(){ public String typeName(){
@@ -727,6 +734,17 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
return UI.formatIcons(text); return UI.formatIcons(text);
} }
} }
@Override
public void write(Json json){
json.writeFields(this);
}
@Override
public void read(Json json, JsonValue jsonData){
json.readFields(this, jsonData);
if(jsonData.has("textureName")) setTexture(jsonData.getString("textureName"));
}
} }
/** A marker that has a position in the world in world coordinates. */ /** A marker that has a position in the world in world coordinates. */
@@ -1127,20 +1145,20 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
/** Displays a texture with specified name. */ /** Displays a texture with specified name. */
public static class TextureMarker extends PosMarker{ public static class TextureMarker extends PosMarker{
public float rotation = 0f, width = 0f, height = 0f; // Zero width/height scales marker to original texture's size public float rotation = 0f, width = 0f, height = 0f; // Zero width/height scales marker to original texture's size
public String textureName = ""; public final TextureHolder texture = new TextureHolder();
public Color color = Color.white.cpy(); public Color color = Color.white.cpy();
private transient TextureRegion fetchedRegion; private transient TextureRegion fetchedRegion;
public TextureMarker(String textureName, float x, float y, float width, float height){ public TextureMarker(Object texture, float x, float y, float width, float height){
this.textureName = textureName; this.texture.value = texture;
this.pos.set(x, y); this.pos.set(x, y);
this.width = width; this.width = width;
this.height = height; this.height = height;
} }
public TextureMarker(String textureName, float x, float y){ public TextureMarker(Object texture, float x, float y){
this.textureName = textureName; this.texture.value = texture;
this.pos.set(x, y); this.pos.set(x, y);
} }
@@ -1167,9 +1185,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
@Override @Override
public void draw(float scaleFactor){ public void draw(float scaleFactor){
if(textureName.isEmpty()) return; if(fetchedRegion == null) setTexture(texture.value);
prepareTexture(this, texture.value);
if(fetchedRegion == null) setTexture(textureName);
// Zero width/height scales marker to original texture's size // Zero width/height scales marker to original texture's size
if(Mathf.equal(width, 0f)) width = fetchedRegion.width * fetchedRegion.scl() * Draw.xscl; if(Mathf.equal(width, 0f)) width = fetchedRegion.width * fetchedRegion.scl() * Draw.xscl;
@@ -1181,18 +1198,17 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
} }
@Override @Override
public void setTexture(String textureName){ public void setTexture(Object texture){
this.textureName = textureName; this.texture.value = texture;
if(headless) return; if(headless) return;
if(fetchedRegion == null) fetchedRegion = new TextureRegion(); if(fetchedRegion == null) fetchedRegion = new TextureRegion();
lookupRegion(textureName, fetchedRegion); lookupRegion(texture, fetchedRegion);
} }
} }
public static class QuadMarker extends ObjectiveMarker{ public static class QuadMarker extends ObjectiveMarker{
public String textureName = "white"; public final TextureHolder texture = new TextureHolder();
public @Vertices float[] vertices = new float[24]; public @Vertices float[] vertices = new float[24];
private boolean mapRegion = true; private boolean mapRegion = true;
@@ -1207,7 +1223,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
@Override @Override
public void draw(float scaleFactor){ public void draw(float scaleFactor){
if(fetchedRegion == null) setTexture(textureName); if(fetchedRegion == null) setTexture(texture.value);
prepareTexture(this, texture.value);
Draw.z(drawLayer); Draw.z(drawLayer);
Draw.vert(fetchedRegion.texture, vertices, 0, vertices.length); Draw.vert(fetchedRegion.texture, vertices, 0, vertices.length);
@@ -1243,8 +1260,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
} }
@Override @Override
public void setTexture(String textureName){ public void setTexture(Object texture){
this.textureName = textureName; this.texture.value = texture;
if(headless) return; if(headless) return;
boolean firstUpdate = fetchedRegion == null; boolean firstUpdate = fetchedRegion == null;
@@ -1252,7 +1269,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
if(firstUpdate) fetchedRegion = new TextureRegion(); if(firstUpdate) fetchedRegion = new TextureRegion();
Tmp.tr1.set(fetchedRegion); Tmp.tr1.set(fetchedRegion);
lookupRegion(textureName, fetchedRegion); lookupRegion(texture, fetchedRegion);
if(firstUpdate){ if(firstUpdate){
if(mapRegion){ if(mapRegion){
@@ -1290,7 +1307,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
if(headless) return; if(headless) return;
if(i >= 0 && i < 4){ if(i >= 0 && i < 4){
if(fetchedRegion == null) setTexture(textureName); if(fetchedRegion == null) setTexture(texture);
if(!Double.isNaN(u)){ if(!Double.isNaN(u)){
boolean clampU = fetchedRegion.texture.getUWrap() != TextureWrap.mirroredRepeat && fetchedRegion.texture.getUWrap() != TextureWrap.repeat; boolean clampU = fetchedRegion.texture.getUWrap() != TextureWrap.mirroredRepeat && fetchedRegion.texture.getUWrap() != TextureWrap.repeat;
@@ -1304,16 +1321,71 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
} }
} }
private static void lookupRegion(String name, TextureRegion out){ private static void lookupRegion(Object texture, TextureRegion out){
TextureRegion region = Core.atlas.find(name); if(texture instanceof String name){
if(region.found()){ TextureRegion region = Core.atlas.find(name);
out.set(region); if(region.found()){
}else{ out.set(region);
if(Core.assets.isLoaded(name, Texture.class)){ }else if(Core.assets.isLoaded(name, Texture.class)){
out.set(Core.assets.get(name, Texture.class)); out.set(Core.assets.get(name, Texture.class));
}else{ }else{
out.set(Core.atlas.find("error")); out.set(Core.atlas.find("error"));
} }
}else if(texture instanceof UnlockableContent u){
out.set(u.fullIcon);
}else if(texture instanceof LogicDisplayBuild d){
d.ensureBuffer();
out.set(d.buffer.getTexture());
}else if(texture instanceof CanvasBuild c){
if(c.texture == null) c.updateTexture();
out.set(c.texture);
}else{
out.set(Core.atlas.find("error"));
}
}
private static void prepareTexture(ObjectiveMarker marker, Object texture){
if(texture instanceof LogicDisplayBuild d){
if(d.buffer == null || d.buffer.isDisposed()){
marker.setTexture("error");
}else{
d.processCommands();
}
}else if(texture instanceof CanvasBuild c){
if(c.texture == null || c.texture.isDisposed()){
marker.setTexture("error");
}else if(c.updated){
c.updated = false;
c.updateTexture();
}
}
}
public static class TextureHolder implements JsonSerializable{
public Object value = "white";
@Override
public void write(Json json){
if(value instanceof String s){
json.writeValue("string", s);
}else if(value instanceof UnlockableContent c){
json.writeValue("content", c.name);
}else if(value instanceof Building b){
json.writeValue("building", b.pos());
}
}
@Override
public void read(Json json, JsonValue jsonData){
if(jsonData.has("string")){
value = jsonData.get("string").asString();
}else if(jsonData.has("content")){
value = content.byName(jsonData.get("content").asString());
}else if(jsonData.has("building")){
value = world.build(jsonData.get("building").asInt());
}else{
value = "white";
}
} }
} }

View File

@@ -2067,7 +2067,7 @@ public class LExecutor{
marker.setTexture(exec.textBuffer.toString()); marker.setTexture(exec.textBuffer.toString());
exec.textBuffer.setLength(0); exec.textBuffer.setLength(0);
}else{ }else{
marker.setTexture(PrintI.toString(p2.obj())); marker.setTexture(p2.obj());
} }
}else{ }else{
marker.control(type, p1.numOrNan(), p2.numOrNan(), p3.numOrNan()); marker.control(type, p1.numOrNan(), p2.numOrNan(), p3.numOrNan());
@@ -2138,10 +2138,10 @@ public class LExecutor{
} }
@Remote(called = Loc.server, variants = Variant.both, unreliable = true) @Remote(called = Loc.server, variants = Variant.both, unreliable = true)
public static void updateMarkerTexture(int id, String textureName){ public static void updateMarkerTexture(int id, Object texture){
var marker = state.markers.get(id); var marker = state.markers.get(id);
if(marker != null){ if(marker != null){
marker.setTexture(textureName); marker.setTexture(texture);
} }
} }

View File

@@ -149,8 +149,7 @@ public class CanvasBlock extends Block{
public @Nullable Texture texture; public @Nullable Texture texture;
public byte[] data = new byte[Mathf.ceil(canvasSize * canvasSize * bitsPerPixel / 8f)]; public byte[] data = new byte[Mathf.ceil(canvasSize * canvasSize * bitsPerPixel / 8f)];
public int blending; public int blending;
public boolean updated = false;
protected boolean updated = false;
public void setPixel(int pos, int index){ public void setPixel(int pos, int index){
if(pos < canvasSize * canvasSize && pos >= 0 && index >= 0 && index < palette.length){ if(pos < canvasSize * canvasSize && pos >= 0 && index >= 0 && index < palette.length){

View File

@@ -89,15 +89,7 @@ public class LogicDisplay extends Block{
//don't even bother processing anything when displays are off. //don't even bother processing anything when displays are off.
if(!Vars.renderer.drawDisplays) return; if(!Vars.renderer.drawDisplays) return;
Draw.draw(Draw.z(), () -> { Draw.draw(Draw.z(), this::ensureBuffer);
if(buffer == null){
buffer = new FrameBuffer(displaySize, displaySize);
//clear the buffer - some OSs leave garbage in it
buffer.begin(Pal.darkerMetal);
buffer.end();
}
});
processCommands(); processCommands();
Draw.blend(Blending.disabled); Draw.blend(Blending.disabled);
@@ -129,11 +121,20 @@ public class LogicDisplay extends Block{
operations++; operations++;
} }
public void ensureBuffer() {
if(buffer == null){
buffer = new FrameBuffer(displaySize, displaySize);
//clear the buffer - some OSs leave garbage in it
buffer.begin(Pal.darkerMetal);
buffer.end();
}
}
public void processCommands(){ public void processCommands(){
//don't bother processing commands if displays are off //don't bother processing commands if displays are off
if(!commands.isEmpty() && buffer != null){ if(!commands.isEmpty() && buffer != null){
Draw.draw(Draw.z(), () -> { Draw.draw(Draw.z(), () -> {
if(buffer == null) return; if(buffer == null || commands.isEmpty()) return;
Tmp.m1.set(Draw.proj()); Tmp.m1.set(Draw.proj());
Tmp.m2.set(Draw.trans()); Tmp.m2.set(Draw.trans());