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.
|
// 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());
|
||||||
|
|||||||
@@ -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";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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){
|
||||||
|
|||||||
@@ -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());
|
||||||
|
|||||||
Reference in New Issue
Block a user