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:
@@ -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.
|
||||
for(var obj : MapObjectives.allObjectiveTypes) setInterpreter(obj.get().getClass(), defaultInterpreter());
|
||||
for(var mark : MapObjectives.allMarkerTypes) setInterpreter(mark.get().getClass(), defaultInterpreter());
|
||||
|
||||
@@ -10,6 +10,9 @@ import arc.math.geom.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
import arc.util.serialization.Json.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
@@ -21,8 +24,12 @@ import mindustry.io.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.logic.CanvasBlock.*;
|
||||
import mindustry.world.blocks.logic.LogicDisplay.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.lang.annotation.*;
|
||||
import java.nio.*;
|
||||
import java.util.*;
|
||||
|
||||
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. */
|
||||
public static abstract class ObjectiveMarker{
|
||||
public static abstract class ObjectiveMarker implements JsonSerializable{
|
||||
/** Internal use only! Do not access. */
|
||||
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 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. */
|
||||
public String typeName(){
|
||||
@@ -727,6 +734,17 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
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. */
|
||||
@@ -1127,20 +1145,20 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
/** Displays a texture with specified name. */
|
||||
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 String textureName = "";
|
||||
public final TextureHolder texture = new TextureHolder();
|
||||
public Color color = Color.white.cpy();
|
||||
|
||||
private transient TextureRegion fetchedRegion;
|
||||
|
||||
public TextureMarker(String textureName, float x, float y, float width, float height){
|
||||
this.textureName = textureName;
|
||||
public TextureMarker(Object texture, float x, float y, float width, float height){
|
||||
this.texture.value = texture;
|
||||
this.pos.set(x, y);
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
public TextureMarker(String textureName, float x, float y){
|
||||
this.textureName = textureName;
|
||||
public TextureMarker(Object texture, float x, float y){
|
||||
this.texture.value = texture;
|
||||
this.pos.set(x, y);
|
||||
}
|
||||
|
||||
@@ -1167,9 +1185,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
@Override
|
||||
public void draw(float scaleFactor){
|
||||
if(textureName.isEmpty()) return;
|
||||
|
||||
if(fetchedRegion == null) setTexture(textureName);
|
||||
if(fetchedRegion == null) setTexture(texture.value);
|
||||
prepareTexture(this, texture.value);
|
||||
|
||||
// Zero width/height scales marker to original texture's size
|
||||
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
|
||||
public void setTexture(String textureName){
|
||||
this.textureName = textureName;
|
||||
public void setTexture(Object texture){
|
||||
this.texture.value = texture;
|
||||
|
||||
if(headless) return;
|
||||
if(fetchedRegion == null) fetchedRegion = new TextureRegion();
|
||||
lookupRegion(textureName, fetchedRegion);
|
||||
lookupRegion(texture, fetchedRegion);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class QuadMarker extends ObjectiveMarker{
|
||||
public String textureName = "white";
|
||||
public final TextureHolder texture = new TextureHolder();
|
||||
public @Vertices float[] vertices = new float[24];
|
||||
private boolean mapRegion = true;
|
||||
|
||||
@@ -1207,7 +1223,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
@Override
|
||||
public void draw(float scaleFactor){
|
||||
if(fetchedRegion == null) setTexture(textureName);
|
||||
if(fetchedRegion == null) setTexture(texture.value);
|
||||
prepareTexture(this, texture.value);
|
||||
|
||||
Draw.z(drawLayer);
|
||||
Draw.vert(fetchedRegion.texture, vertices, 0, vertices.length);
|
||||
@@ -1243,8 +1260,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTexture(String textureName){
|
||||
this.textureName = textureName;
|
||||
public void setTexture(Object texture){
|
||||
this.texture.value = texture;
|
||||
if(headless) return;
|
||||
|
||||
boolean firstUpdate = fetchedRegion == null;
|
||||
@@ -1252,7 +1269,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
if(firstUpdate) fetchedRegion = new TextureRegion();
|
||||
Tmp.tr1.set(fetchedRegion);
|
||||
|
||||
lookupRegion(textureName, fetchedRegion);
|
||||
lookupRegion(texture, fetchedRegion);
|
||||
|
||||
if(firstUpdate){
|
||||
if(mapRegion){
|
||||
@@ -1290,7 +1307,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
if(headless) return;
|
||||
|
||||
if(i >= 0 && i < 4){
|
||||
if(fetchedRegion == null) setTexture(textureName);
|
||||
if(fetchedRegion == null) setTexture(texture);
|
||||
|
||||
if(!Double.isNaN(u)){
|
||||
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){
|
||||
TextureRegion region = Core.atlas.find(name);
|
||||
if(region.found()){
|
||||
out.set(region);
|
||||
}else{
|
||||
if(Core.assets.isLoaded(name, Texture.class)){
|
||||
private static void lookupRegion(Object texture, TextureRegion out){
|
||||
if(texture instanceof String name){
|
||||
TextureRegion region = Core.atlas.find(name);
|
||||
if(region.found()){
|
||||
out.set(region);
|
||||
}else if(Core.assets.isLoaded(name, Texture.class)){
|
||||
out.set(Core.assets.get(name, Texture.class));
|
||||
}else{
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2067,7 +2067,7 @@ public class LExecutor{
|
||||
marker.setTexture(exec.textBuffer.toString());
|
||||
exec.textBuffer.setLength(0);
|
||||
}else{
|
||||
marker.setTexture(PrintI.toString(p2.obj()));
|
||||
marker.setTexture(p2.obj());
|
||||
}
|
||||
}else{
|
||||
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)
|
||||
public static void updateMarkerTexture(int id, String textureName){
|
||||
public static void updateMarkerTexture(int id, Object texture){
|
||||
var marker = state.markers.get(id);
|
||||
if(marker != null){
|
||||
marker.setTexture(textureName);
|
||||
marker.setTexture(texture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -149,8 +149,7 @@ public class CanvasBlock extends Block{
|
||||
public @Nullable Texture texture;
|
||||
public byte[] data = new byte[Mathf.ceil(canvasSize * canvasSize * bitsPerPixel / 8f)];
|
||||
public int blending;
|
||||
|
||||
protected boolean updated = false;
|
||||
public boolean updated = false;
|
||||
|
||||
public void setPixel(int pos, int index){
|
||||
if(pos < canvasSize * canvasSize && pos >= 0 && index >= 0 && index < palette.length){
|
||||
|
||||
@@ -89,15 +89,7 @@ public class LogicDisplay extends Block{
|
||||
//don't even bother processing anything when displays are off.
|
||||
if(!Vars.renderer.drawDisplays) return;
|
||||
|
||||
Draw.draw(Draw.z(), () -> {
|
||||
if(buffer == null){
|
||||
buffer = new FrameBuffer(displaySize, displaySize);
|
||||
//clear the buffer - some OSs leave garbage in it
|
||||
buffer.begin(Pal.darkerMetal);
|
||||
buffer.end();
|
||||
}
|
||||
});
|
||||
|
||||
Draw.draw(Draw.z(), this::ensureBuffer);
|
||||
processCommands();
|
||||
|
||||
Draw.blend(Blending.disabled);
|
||||
@@ -129,11 +121,20 @@ public class LogicDisplay extends Block{
|
||||
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(){
|
||||
//don't bother processing commands if displays are off
|
||||
if(!commands.isEmpty() && buffer != null){
|
||||
Draw.draw(Draw.z(), () -> {
|
||||
if(buffer == null) return;
|
||||
if(buffer == null || commands.isEmpty()) return;
|
||||
|
||||
Tmp.m1.set(Draw.proj());
|
||||
Tmp.m2.set(Draw.trans());
|
||||
|
||||
Reference in New Issue
Block a user