Functional world processors
This commit is contained in:
BIN
core/assets-raw/sprites/blocks/logic/world-processor.png
Normal file
BIN
core/assets-raw/sprites/blocks/logic/world-processor.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 586 B |
@@ -530,3 +530,4 @@
|
|||||||
63173=beam-link|block-beam-link-ui
|
63173=beam-link|block-beam-link-ui
|
||||||
63172=drone-center|block-drone-center-ui
|
63172=drone-center|block-drone-center-ui
|
||||||
63171=effect-drone|unit-effect-drone-ui
|
63171=effect-drone|unit-effect-drone-ui
|
||||||
|
63170=world-processor|block-world-processor-ui
|
||||||
|
|||||||
Binary file not shown.
@@ -149,6 +149,7 @@ public class Blocks{
|
|||||||
|
|
||||||
//logic
|
//logic
|
||||||
message, switchBlock, microProcessor, logicProcessor, hyperProcessor, largeLogicDisplay, logicDisplay, memoryCell, memoryBank,
|
message, switchBlock, microProcessor, logicProcessor, hyperProcessor, largeLogicDisplay, logicDisplay, memoryCell, memoryBank,
|
||||||
|
worldProcessor,
|
||||||
|
|
||||||
//campaign
|
//campaign
|
||||||
//TODO launch pad on erekir, 5x5, uses nuclear(?) fuel
|
//TODO launch pad on erekir, 5x5, uses nuclear(?) fuel
|
||||||
@@ -3778,6 +3779,16 @@ public class Blocks{
|
|||||||
size = 6;
|
size = 6;
|
||||||
}};
|
}};
|
||||||
|
|
||||||
|
worldProcessor = new LogicBlock("world-processor"){{
|
||||||
|
//currently incomplete, debugOnly for now
|
||||||
|
requirements(Category.logic, BuildVisibility.debugOnly, with());
|
||||||
|
|
||||||
|
instructionsPerTick = 2;
|
||||||
|
forceDark = true;
|
||||||
|
privileged = true;
|
||||||
|
size = 1;
|
||||||
|
}};
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -402,6 +402,8 @@ public class Logic implements ApplicationListener{
|
|||||||
}
|
}
|
||||||
Time.update();
|
Time.update();
|
||||||
|
|
||||||
|
constants.update();
|
||||||
|
|
||||||
//weather is serverside
|
//weather is serverside
|
||||||
if(!net.client() && !state.isEditor()){
|
if(!net.client() && !state.isEditor()){
|
||||||
updateWeather();
|
updateWeather();
|
||||||
|
|||||||
@@ -530,7 +530,7 @@ public class World{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Tile tile = world.tile(x, y);
|
Tile tile = world.tile(x, y);
|
||||||
if(tile != null && tile.block().solid && tile.block().fillsTile && !tile.block().synthetic()){
|
if(tile != null && tile.isDarkened()){
|
||||||
dark = Math.max(dark, tile.data);
|
dark = Math.max(dark, tile.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,6 +119,7 @@ public class Pal{
|
|||||||
logicOperations = Color.valueOf("877bad"),
|
logicOperations = Color.valueOf("877bad"),
|
||||||
logicIo = Color.valueOf("a08a8a"),
|
logicIo = Color.valueOf("a08a8a"),
|
||||||
logicUnits = Color.valueOf("c7b59d"),
|
logicUnits = Color.valueOf("c7b59d"),
|
||||||
|
logicWorld = Color.valueOf("6b84d4"),
|
||||||
|
|
||||||
berylShot = Color.valueOf("b1dd7e"),
|
berylShot = Color.valueOf("b1dd7e"),
|
||||||
tungstenShot = Color.valueOf("768a9a"),
|
tungstenShot = Color.valueOf("768a9a"),
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ import mindustry.world.*;
|
|||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
/** Stores global constants for logic processors. */
|
/** Stores global constants for logic processors. */
|
||||||
public class GlobalConstants{
|
public class GlobalConstants{
|
||||||
public static final int ctrlProcessor = 1, ctrlPlayer = 2, ctrlFormation = 3;
|
public static final int ctrlProcessor = 1, ctrlPlayer = 2, ctrlFormation = 3;
|
||||||
@@ -22,6 +24,9 @@ public class GlobalConstants{
|
|||||||
/** Global random state. */
|
/** Global random state. */
|
||||||
public static final Rand rand = new Rand();
|
public static final Rand rand = new Rand();
|
||||||
|
|
||||||
|
//non-constants that depend on state
|
||||||
|
private static int varTime, varTick, varWave, varWaveTime;
|
||||||
|
|
||||||
private ObjectIntMap<String> namesToIds = new ObjectIntMap<>();
|
private ObjectIntMap<String> namesToIds = new ObjectIntMap<>();
|
||||||
private Seq<Var> vars = new Seq<>(Var.class);
|
private Seq<Var> vars = new Seq<>(Var.class);
|
||||||
private UnlockableContent[][] logicIdToContent;
|
private UnlockableContent[][] logicIdToContent;
|
||||||
@@ -34,6 +39,12 @@ public class GlobalConstants{
|
|||||||
put("true", 1);
|
put("true", 1);
|
||||||
put("null", null);
|
put("null", null);
|
||||||
|
|
||||||
|
//time
|
||||||
|
varTime = put("@time", 0);
|
||||||
|
varTick = put("@tick", 0);
|
||||||
|
varWave = put("@waveNumber", 0);
|
||||||
|
varWaveTime = put("@waveTime", 0);
|
||||||
|
|
||||||
//special enums
|
//special enums
|
||||||
|
|
||||||
put("@ctrlProcessor", ctrlProcessor);
|
put("@ctrlProcessor", ctrlProcessor);
|
||||||
@@ -51,9 +62,7 @@ public class GlobalConstants{
|
|||||||
}
|
}
|
||||||
|
|
||||||
for(Block block : Vars.content.blocks()){
|
for(Block block : Vars.content.blocks()){
|
||||||
if(block.synthetic()){
|
put("@" + block.name, block);
|
||||||
put("@" + block.name, block);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//used as a special value for any environmental solid block
|
//used as a special value for any environmental solid block
|
||||||
@@ -105,6 +114,18 @@ public class GlobalConstants{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Updates global time and other state variables. */
|
||||||
|
public void update(){
|
||||||
|
//set up time; note that @time is now only updated once every invocation and directly based off of @tick.
|
||||||
|
//having time be based off of user system time was a very bad idea.
|
||||||
|
vars.items[varTime].numval = state.tick / 60.0 * 1000.0;
|
||||||
|
vars.items[varTick].numval = state.tick;
|
||||||
|
|
||||||
|
//wave state
|
||||||
|
vars.items[varWave].numval = state.wave;
|
||||||
|
vars.items[varWaveTime].numval = state.wavetime / 60f;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return a piece of content based on its logic ID. This is not equivalent to content ID. */
|
/** @return a piece of content based on its logic ID. This is not equivalent to content ID. */
|
||||||
public @Nullable Content lookupContent(ContentType type, int id){
|
public @Nullable Content lookupContent(ContentType type, int id){
|
||||||
var arr = logicIdToContent[type.ordinal()];
|
var arr = logicIdToContent[type.ordinal()];
|
||||||
@@ -127,8 +148,13 @@ public class GlobalConstants{
|
|||||||
return vars.items[id];
|
return vars.items[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Sets a global variable by an ID returned from put(). */
|
||||||
|
public void set(int id, double value){
|
||||||
|
get(id).numval = value;
|
||||||
|
}
|
||||||
|
|
||||||
/** Adds a constant value by name. */
|
/** Adds a constant value by name. */
|
||||||
public Var put(String name, Object value){
|
public int put(String name, Object value){
|
||||||
Var var = new Var(name);
|
Var var = new Var(name);
|
||||||
var.constant = true;
|
var.constant = true;
|
||||||
if(value instanceof Number num){
|
if(value instanceof Number num){
|
||||||
@@ -141,6 +167,6 @@ public class GlobalConstants{
|
|||||||
int index = vars.size;
|
int index = vars.size;
|
||||||
namesToIds.put(name, index);
|
namesToIds.put(name, index);
|
||||||
vars.add(var);
|
vars.add(var);
|
||||||
return var;
|
return index;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,20 +22,28 @@ public class LAssembler{
|
|||||||
public LAssembler(){
|
public LAssembler(){
|
||||||
//instruction counter
|
//instruction counter
|
||||||
putVar("@counter").value = 0;
|
putVar("@counter").value = 0;
|
||||||
//timestamp
|
|
||||||
putConst("@time", 0);
|
|
||||||
//currently controlled unit
|
//currently controlled unit
|
||||||
putConst("@unit", null);
|
putConst("@unit", null);
|
||||||
//reference to self
|
//reference to self
|
||||||
putConst("@this", null);
|
putConst("@this", null);
|
||||||
//global tick
|
|
||||||
putConst("@tick", 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @deprecated use the one with the privileged parameter */
|
||||||
|
@Deprecated
|
||||||
public static LAssembler assemble(String data){
|
public static LAssembler assemble(String data){
|
||||||
|
return assemble(data, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @deprecated use the one with the privileged parameter */
|
||||||
|
@Deprecated
|
||||||
|
public static Seq<LStatement> read(String text){
|
||||||
|
return read(text, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LAssembler assemble(String data, boolean privileged){
|
||||||
LAssembler asm = new LAssembler();
|
LAssembler asm = new LAssembler();
|
||||||
|
|
||||||
Seq<LStatement> st = read(data);
|
Seq<LStatement> st = read(data, privileged);
|
||||||
|
|
||||||
asm.instructions = st.map(l -> l.build(asm)).filter(l -> l != null).toArray(LInstruction.class);
|
asm.instructions = st.map(l -> l.build(asm)).filter(l -> l != null).toArray(LInstruction.class);
|
||||||
return asm;
|
return asm;
|
||||||
@@ -51,8 +59,11 @@ public class LAssembler{
|
|||||||
return out.toString();
|
return out.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Seq<LStatement> read(String data){
|
/** Parses a sequence of statements from a string. */
|
||||||
return LParser.parse(data);
|
public static Seq<LStatement> read(String text, boolean privileged){
|
||||||
|
//don't waste time parsing null/empty text
|
||||||
|
if(text == null || text.isEmpty()) return new Seq<>();
|
||||||
|
return new LParser(text, privileged).parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return a variable ID by name.
|
/** @return a variable ID by name.
|
||||||
|
|||||||
@@ -27,10 +27,12 @@ public class LCanvas extends Table{
|
|||||||
public DragLayout statements;
|
public DragLayout statements;
|
||||||
public ScrollPane pane;
|
public ScrollPane pane;
|
||||||
public Group jumps;
|
public Group jumps;
|
||||||
|
|
||||||
StatementElem dragging;
|
StatementElem dragging;
|
||||||
StatementElem hovered;
|
StatementElem hovered;
|
||||||
float targetWidth;
|
float targetWidth;
|
||||||
int jumpCount = 0;
|
int jumpCount = 0;
|
||||||
|
boolean privileged;
|
||||||
Seq<Tooltip> tooltips = new Seq<>();
|
Seq<Tooltip> tooltips = new Seq<>();
|
||||||
|
|
||||||
public LCanvas(){
|
public LCanvas(){
|
||||||
@@ -146,7 +148,7 @@ public class LCanvas extends Table{
|
|||||||
public void load(String asm){
|
public void load(String asm){
|
||||||
jumps.clear();
|
jumps.clear();
|
||||||
|
|
||||||
Seq<LStatement> statements = LAssembler.read(asm);
|
Seq<LStatement> statements = LAssembler.read(asm, privileged);
|
||||||
statements.truncate(LExecutor.maxInstructions);
|
statements.truncate(LExecutor.maxInstructions);
|
||||||
this.statements.clearChildren();
|
this.statements.clearChildren();
|
||||||
for(LStatement st : statements){
|
for(LStatement st : statements){
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ import mindustry.game.Teams.*;
|
|||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
import mindustry.world.blocks.environment.*;
|
||||||
import mindustry.world.blocks.logic.*;
|
import mindustry.world.blocks.logic.*;
|
||||||
import mindustry.world.blocks.logic.LogicDisplay.*;
|
import mindustry.world.blocks.logic.LogicDisplay.*;
|
||||||
import mindustry.world.blocks.logic.MemoryBlock.*;
|
import mindustry.world.blocks.logic.MemoryBlock.*;
|
||||||
@@ -31,10 +32,8 @@ public class LExecutor{
|
|||||||
//special variables
|
//special variables
|
||||||
public static final int
|
public static final int
|
||||||
varCounter = 0,
|
varCounter = 0,
|
||||||
varTime = 1,
|
varUnit = 1,
|
||||||
varUnit = 2,
|
varThis = 2;
|
||||||
varThis = 3,
|
|
||||||
varTick = 4;
|
|
||||||
|
|
||||||
public static final int
|
public static final int
|
||||||
maxGraphicsBuffer = 256,
|
maxGraphicsBuffer = 256,
|
||||||
@@ -43,6 +42,7 @@ public class LExecutor{
|
|||||||
|
|
||||||
public LInstruction[] instructions = {};
|
public LInstruction[] instructions = {};
|
||||||
public Var[] vars = {};
|
public Var[] vars = {};
|
||||||
|
public Var counter;
|
||||||
public int[] binds;
|
public int[] binds;
|
||||||
|
|
||||||
public LongSeq graphicsBuffer = new LongSeq();
|
public LongSeq graphicsBuffer = new LongSeq();
|
||||||
@@ -50,32 +50,24 @@ public class LExecutor{
|
|||||||
public Building[] links = {};
|
public Building[] links = {};
|
||||||
public IntSet linkIds = new IntSet();
|
public IntSet linkIds = new IntSet();
|
||||||
public Team team = Team.derelict;
|
public Team team = Team.derelict;
|
||||||
|
public boolean privileged = false;
|
||||||
|
|
||||||
public boolean initialized(){
|
public boolean initialized(){
|
||||||
return instructions != null && vars != null && instructions.length > 0;
|
return instructions.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runs a single instruction. */
|
/** Runs a single instruction. */
|
||||||
public void runOnce(){
|
public void runOnce(){
|
||||||
//set up time; note that @time is now only updated once every invocation and directly based off of @tick.
|
|
||||||
//having time be based off of user system time was a very bad idea.
|
|
||||||
vars[varTime].numval = state.tick / 60.0 * 1000.0;
|
|
||||||
vars[varTick].numval = state.tick;
|
|
||||||
|
|
||||||
//reset to start
|
//reset to start
|
||||||
if(vars[varCounter].numval >= instructions.length || vars[varCounter].numval < 0){
|
if(counter.numval >= instructions.length || counter.numval < 0){
|
||||||
vars[varCounter].numval = 0;
|
counter.numval = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vars[varCounter].numval < instructions.length){
|
if(counter.numval < instructions.length){
|
||||||
instructions[(int)(vars[varCounter].numval++)].run(this);
|
instructions[(int)(counter.numval++)].run(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void load(String data){
|
|
||||||
load(LAssembler.assemble(data));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Loads with a specified assembler. Resets all variables. */
|
/** Loads with a specified assembler. Resets all variables. */
|
||||||
public void load(LAssembler builder){
|
public void load(LAssembler builder){
|
||||||
vars = new Var[builder.vars.size];
|
vars = new Var[builder.vars.size];
|
||||||
@@ -95,6 +87,8 @@ public class LExecutor{
|
|||||||
dest.objval = var.value;
|
dest.objval = var.value;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
counter = vars[varCounter];
|
||||||
}
|
}
|
||||||
|
|
||||||
//region utility
|
//region utility
|
||||||
@@ -560,7 +554,7 @@ public class LExecutor{
|
|||||||
@Override
|
@Override
|
||||||
public void run(LExecutor exec){
|
public void run(LExecutor exec){
|
||||||
Object obj = exec.obj(target);
|
Object obj = exec.obj(target);
|
||||||
if(obj instanceof Building b && b.team == exec.team && exec.linkIds.contains(b.id)){
|
if(obj instanceof Building b && (exec.privileged || (b.team == exec.team && exec.linkIds.contains(b.id)))){
|
||||||
if(type.isObj && exec.var(p1).isobj){
|
if(type.isObj && exec.var(p1).isobj){
|
||||||
b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4));
|
b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4));
|
||||||
}else{
|
}else{
|
||||||
@@ -921,7 +915,7 @@ public class LExecutor{
|
|||||||
//graphics on headless servers are useless.
|
//graphics on headless servers are useless.
|
||||||
if(Vars.headless) return;
|
if(Vars.headless) return;
|
||||||
|
|
||||||
if(exec.building(target) instanceof LogicDisplayBuild d && d.team == exec.team){
|
if(exec.building(target) instanceof LogicDisplayBuild d && (d.team == exec.team || exec.privileged)){
|
||||||
if(d.commands.size + exec.graphicsBuffer.size < maxDisplayBuffer){
|
if(d.commands.size + exec.graphicsBuffer.size < maxDisplayBuffer){
|
||||||
for(int i = 0; i < exec.graphicsBuffer.size; i++){
|
for(int i = 0; i < exec.graphicsBuffer.size; i++){
|
||||||
d.commands.addLast(exec.graphicsBuffer.items[i]);
|
d.commands.addLast(exec.graphicsBuffer.items[i]);
|
||||||
@@ -1067,6 +1061,7 @@ public class LExecutor{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO lookup color instruction and inverse lookup
|
||||||
public static class LookupI implements LInstruction{
|
public static class LookupI implements LInstruction{
|
||||||
public int dest;
|
public int dest;
|
||||||
public int from;
|
public int from;
|
||||||
@@ -1087,6 +1082,79 @@ public class LExecutor{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region privileged / world instructions
|
||||||
|
|
||||||
|
public static class GetBlockI implements LInstruction{
|
||||||
|
public int x, y;
|
||||||
|
public int dest;
|
||||||
|
public TileLayer layer = TileLayer.block;
|
||||||
|
|
||||||
|
public GetBlockI(int x, int y, int dest, TileLayer layer){
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.dest = dest;
|
||||||
|
this.layer = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GetBlockI(){
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(LExecutor exec){
|
||||||
|
Tile tile = world.tile(exec.numi(x), exec.numi(y));
|
||||||
|
if(tile == null){
|
||||||
|
exec.setobj(dest, null);
|
||||||
|
}else{
|
||||||
|
exec.setobj(dest, switch(layer){
|
||||||
|
case floor -> tile.floor();
|
||||||
|
case ore -> tile.overlay();
|
||||||
|
case block -> tile.block();
|
||||||
|
case building -> tile.build;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SetBlockI implements LInstruction{
|
||||||
|
public int x, y;
|
||||||
|
public int block;
|
||||||
|
public int team, rotation;
|
||||||
|
public TileLayer layer = TileLayer.block;
|
||||||
|
|
||||||
|
public SetBlockI(int x, int y, int block, int team, int rotation, TileLayer layer){
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
this.block = block;
|
||||||
|
this.team = team;
|
||||||
|
this.rotation = rotation;
|
||||||
|
this.layer = layer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SetBlockI(){
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(LExecutor exec){
|
||||||
|
Tile tile = world.tile(exec.numi(x), exec.numi(y));
|
||||||
|
if(tile != null && exec.obj(block) instanceof Block b){
|
||||||
|
//TODO this can be quite laggy...
|
||||||
|
switch(layer){
|
||||||
|
case ore -> {
|
||||||
|
if(b instanceof OverlayFloor o) tile.setOverlay(o);
|
||||||
|
}
|
||||||
|
case floor -> {
|
||||||
|
if(b instanceof Floor f) tile.setFloor(f);
|
||||||
|
}
|
||||||
|
case block -> {
|
||||||
|
Team t = exec.obj(team) instanceof Team steam ? steam : Team.derelict;
|
||||||
|
tile.setBlock(b, t, Mathf.clamp(exec.numi(rotation), 0, 3));
|
||||||
|
}
|
||||||
|
//building case not allowed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,18 +19,13 @@ public class LParser{
|
|||||||
Seq<LStatement> statements = new Seq<>();
|
Seq<LStatement> statements = new Seq<>();
|
||||||
char[] chars;
|
char[] chars;
|
||||||
int pos, line, tok;
|
int pos, line, tok;
|
||||||
|
boolean privileged;
|
||||||
|
|
||||||
LParser(String text){
|
LParser(String text, boolean privileged){
|
||||||
|
this.privileged = privileged;
|
||||||
this.chars = text.toCharArray();
|
this.chars = text.toCharArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parses a sequence of statements from a string. */
|
|
||||||
public static Seq<LStatement> parse(String text){
|
|
||||||
//don't waste time parsing null/empty text
|
|
||||||
if(text == null || text.isEmpty()) return new Seq<>();
|
|
||||||
return new LParser(text).parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
void comment(){
|
void comment(){
|
||||||
//read until \n or eof
|
//read until \n or eof
|
||||||
while(pos < chars.length && chars[pos++] != '\n');
|
while(pos < chars.length && chars[pos++] != '\n');
|
||||||
@@ -142,6 +137,11 @@ public class LParser{
|
|||||||
st = new InvalidStatement();
|
st = new InvalidStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//discard misplaced privileged instructions
|
||||||
|
if(!privileged && st != null && st.privileged()){
|
||||||
|
st = new InvalidStatement();
|
||||||
|
}
|
||||||
|
|
||||||
//store jumps that use labels
|
//store jumps that use labels
|
||||||
if(st instanceof JumpStatement jump && wasJump){
|
if(st instanceof JumpStatement jump && wasJump){
|
||||||
jumps.add(new JumpIndex(jump, jumpLoc));
|
jumps.add(new JumpIndex(jump, jumpLoc));
|
||||||
|
|||||||
@@ -30,7 +30,8 @@ public abstract class LStatement{
|
|||||||
public LStatement copy(){
|
public LStatement copy(){
|
||||||
StringBuilder build = new StringBuilder();
|
StringBuilder build = new StringBuilder();
|
||||||
write(build);
|
write(build);
|
||||||
Seq<LStatement> read = LAssembler.read(build.toString());
|
//assume privileged when copying, because there's no way privileged instructions can appear here anyway, and the instructions get validated on load anyway
|
||||||
|
Seq<LStatement> read = LAssembler.read(build.toString(), true);
|
||||||
return read.size == 0 ? null : read.first();
|
return read.size == 0 ? null : read.first();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,6 +39,16 @@ public abstract class LStatement{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Privileged instructions are only allowed in world processors. */
|
||||||
|
public boolean privileged(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** If true, this statement is considered useless with privileged processors and is not allowed in them. */
|
||||||
|
public boolean nonPrivileged(){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//protected methods are only for internal UI layout utilities
|
//protected methods are only for internal UI layout utilities
|
||||||
|
|
||||||
protected void param(Cell<Label> label){
|
protected void param(Cell<Label> label){
|
||||||
|
|||||||
@@ -1050,4 +1050,108 @@ public class LStatements{
|
|||||||
return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound), builder.var(outBuild));
|
return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound), builder.var(outBuild));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RegisterStatement("getblock")
|
||||||
|
public static class GetBlockStatement extends LStatement{
|
||||||
|
public TileLayer layer = TileLayer.block;
|
||||||
|
public String result = "result", x = "0", y = "0";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void build(Table table){
|
||||||
|
fields(table, result, str -> result = str);
|
||||||
|
|
||||||
|
table.add(" = get ");
|
||||||
|
|
||||||
|
row(table);
|
||||||
|
|
||||||
|
table.button(b -> {
|
||||||
|
b.label(() -> layer.name());
|
||||||
|
b.clicked(() -> showSelect(b, TileLayer.all, layer, o -> layer = o));
|
||||||
|
}, Styles.logict, () -> {}).size(64f, 40f).pad(4f).color(table.color);
|
||||||
|
|
||||||
|
table.add(" at ");
|
||||||
|
|
||||||
|
fields(table, x, str -> x = str);
|
||||||
|
table.add(", ");
|
||||||
|
fields(table, y, str -> y = str);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean privileged(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color color(){
|
||||||
|
return Pal.logicWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LInstruction build(LAssembler builder){
|
||||||
|
return new GetBlockI(builder.var(x), builder.var(y), builder.var(result), layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@RegisterStatement("setblock")
|
||||||
|
public static class SetBlockStatement extends LStatement{
|
||||||
|
public TileLayer layer = TileLayer.block;
|
||||||
|
public String block = "@air", x = "0", y = "0", team = "@derelict", rotation = "0";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void build(Table table){
|
||||||
|
rebuild(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rebuild(Table table){
|
||||||
|
table.clearChildren();
|
||||||
|
table.add("set");
|
||||||
|
|
||||||
|
table.button(b -> {
|
||||||
|
b.label(() -> layer.name());
|
||||||
|
b.clicked(() -> showSelect(b, TileLayer.settable, layer, o -> {
|
||||||
|
layer = o;
|
||||||
|
rebuild(table);
|
||||||
|
}));
|
||||||
|
}, Styles.logict, () -> {}).size(64f, 40f).pad(4f).color(table.color);
|
||||||
|
|
||||||
|
row(table);
|
||||||
|
|
||||||
|
table.add(" at ");
|
||||||
|
|
||||||
|
fields(table, x, str -> x = str);
|
||||||
|
table.add(", ");
|
||||||
|
fields(table, y, str -> y = str);
|
||||||
|
|
||||||
|
row(table);
|
||||||
|
|
||||||
|
table.add(" to ");
|
||||||
|
|
||||||
|
fields(table, block, str -> block = str);
|
||||||
|
|
||||||
|
if(layer == TileLayer.block){
|
||||||
|
row(table);
|
||||||
|
|
||||||
|
table.add("team ");
|
||||||
|
fields(table, team, str -> team = str);
|
||||||
|
|
||||||
|
table.add(" rotation ");
|
||||||
|
fields(table, rotation, str -> rotation = str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean privileged(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Color color(){
|
||||||
|
return Pal.logicWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LInstruction build(LAssembler builder){
|
||||||
|
return new SetBlockI(builder.var(x), builder.var(y), builder.var(block), builder.var(team), builder.var(rotation), layer);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import static mindustry.logic.LCanvas.*;
|
|||||||
public class LogicDialog extends BaseDialog{
|
public class LogicDialog extends BaseDialog{
|
||||||
public LCanvas canvas;
|
public LCanvas canvas;
|
||||||
Cons<String> consumer = s -> {};
|
Cons<String> consumer = s -> {};
|
||||||
|
boolean privileged;
|
||||||
@Nullable LExecutor executor;
|
@Nullable LExecutor executor;
|
||||||
|
|
||||||
public LogicDialog(){
|
public LogicDialog(){
|
||||||
@@ -156,7 +157,7 @@ public class LogicDialog extends BaseDialog{
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
for(Prov<LStatement> prov : LogicIO.allStatements){
|
for(Prov<LStatement> prov : LogicIO.allStatements){
|
||||||
LStatement example = prov.get();
|
LStatement example = prov.get();
|
||||||
if(example instanceof InvalidStatement || example.hidden()) continue;
|
if(example instanceof InvalidStatement || example.hidden() || (example.privileged() && !privileged) || (example.nonPrivileged() && privileged)) continue;
|
||||||
|
|
||||||
TextButtonStyle style = new TextButtonStyle(Styles.cleart);
|
TextButtonStyle style = new TextButtonStyle(Styles.cleart);
|
||||||
style.fontColor = example.color();
|
style.fontColor = example.color();
|
||||||
@@ -184,10 +185,12 @@ public class LogicDialog extends BaseDialog{
|
|||||||
onResize(() -> canvas.rebuild());
|
onResize(() -> canvas.rebuild());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void show(String code, LExecutor executor, Cons<String> modified){
|
public void show(String code, LExecutor executor, boolean privileged, Cons<String> modified){
|
||||||
this.executor = executor;
|
this.executor = executor;
|
||||||
|
this.privileged = privileged;
|
||||||
canvas.statements.clearChildren();
|
canvas.statements.clearChildren();
|
||||||
canvas.rebuild();
|
canvas.rebuild();
|
||||||
|
canvas.privileged = privileged;
|
||||||
try{
|
try{
|
||||||
canvas.load(code);
|
canvas.load(code);
|
||||||
}catch(Throwable t){
|
}catch(Throwable t){
|
||||||
|
|||||||
10
core/src/mindustry/logic/TileLayer.java
Normal file
10
core/src/mindustry/logic/TileLayer.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package mindustry.logic;
|
||||||
|
|
||||||
|
public enum TileLayer{
|
||||||
|
floor,
|
||||||
|
ore,
|
||||||
|
block,
|
||||||
|
building;
|
||||||
|
|
||||||
|
public static final TileLayer[] all = values(), settable = {floor, ore, block};
|
||||||
|
}
|
||||||
@@ -162,6 +162,8 @@ public class Block extends UnlockableContent{
|
|||||||
public CacheLayer cacheLayer = CacheLayer.normal;
|
public CacheLayer cacheLayer = CacheLayer.normal;
|
||||||
/** Special flag; if false, floor will be drawn under this block even if it is cached. */
|
/** Special flag; if false, floor will be drawn under this block even if it is cached. */
|
||||||
public boolean fillsTile = true;
|
public boolean fillsTile = true;
|
||||||
|
/** If true, this block can be covered by darkness / fog even if synthetic. */
|
||||||
|
public boolean forceDark = false;
|
||||||
/** whether this block can be replaced in all cases */
|
/** whether this block can be replaced in all cases */
|
||||||
public boolean alwaysReplace = false;
|
public boolean alwaysReplace = false;
|
||||||
/** if false, this block can never be replaced. */
|
/** if false, this block can never be replaced. */
|
||||||
|
|||||||
@@ -166,7 +166,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDarkened(){
|
public boolean isDarkened(){
|
||||||
return block.solid && !block.synthetic() && block.fillsTile;
|
return block.solid && ((!block.synthetic() && block.fillsTile) || block.forceDark);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Floor floor(){
|
public Floor floor(){
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public interface Autotiler{
|
|||||||
BuildPlan[] directionals = AutotilerHolder.directionals;
|
BuildPlan[] directionals = AutotilerHolder.directionals;
|
||||||
|
|
||||||
Arrays.fill(directionals, null);
|
Arrays.fill(directionals, null);
|
||||||
|
//TODO this is O(n^2), very slow, should use quadtree or intmap or something instead
|
||||||
list.each(other -> {
|
list.each(other -> {
|
||||||
if(other.breaking || other == req) return;
|
if(other.breaking || other == req) return;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package mindustry.world.blocks.logic;
|
package mindustry.world.blocks.logic;
|
||||||
|
|
||||||
|
import arc.Graphics.*;
|
||||||
|
import arc.Graphics.Cursor.*;
|
||||||
import arc.func.*;
|
import arc.func.*;
|
||||||
import arc.math.geom.*;
|
import arc.math.geom.*;
|
||||||
import arc.scene.ui.layout.*;
|
import arc.scene.ui.layout.*;
|
||||||
@@ -32,6 +34,7 @@ public class LogicBlock extends Block{
|
|||||||
public int maxInstructionScale = 5;
|
public int maxInstructionScale = 5;
|
||||||
public int instructionsPerTick = 1;
|
public int instructionsPerTick = 1;
|
||||||
public float range = 8 * 10;
|
public float range = 8 * 10;
|
||||||
|
public boolean privileged;
|
||||||
|
|
||||||
public LogicBlock(String name){
|
public LogicBlock(String name){
|
||||||
super(name);
|
super(name);
|
||||||
@@ -44,9 +47,15 @@ public class LogicBlock extends Block{
|
|||||||
//universal, no real requirements
|
//universal, no real requirements
|
||||||
envEnabled = Env.any;
|
envEnabled = Env.any;
|
||||||
|
|
||||||
config(byte[].class, (LogicBuild build, byte[] data) -> build.readCompressed(data, true));
|
config(byte[].class, (LogicBuild build, byte[] data) -> {
|
||||||
|
if(!accessible()) return;
|
||||||
|
|
||||||
|
build.readCompressed(data, true);
|
||||||
|
});
|
||||||
|
|
||||||
config(Integer.class, (LogicBuild entity, Integer pos) -> {
|
config(Integer.class, (LogicBuild entity, Integer pos) -> {
|
||||||
|
if(!accessible()) return;
|
||||||
|
|
||||||
//if there is no valid link in the first place, nobody cares
|
//if there is no valid link in the first place, nobody cares
|
||||||
if(!entity.validLink(world.build(pos))) return;
|
if(!entity.validLink(world.build(pos))) return;
|
||||||
var lbuild = world.build(pos);
|
var lbuild = world.build(pos);
|
||||||
@@ -71,6 +80,15 @@ public class LogicBlock extends Block{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean accessible(){
|
||||||
|
return !privileged || state.rules.editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBreak(Tile tile){
|
||||||
|
return accessible();
|
||||||
|
}
|
||||||
|
|
||||||
public static String getLinkName(Block block){
|
public static String getLinkName(Block block){
|
||||||
String name = block.name;
|
String name = block.name;
|
||||||
if(name.contains("-")){
|
if(name.contains("-")){
|
||||||
@@ -124,12 +142,16 @@ public class LogicBlock extends Block{
|
|||||||
public void setStats(){
|
public void setStats(){
|
||||||
super.setStats();
|
super.setStats();
|
||||||
|
|
||||||
stats.add(Stat.linkRange, range / 8, StatUnit.blocks);
|
if(!privileged){
|
||||||
stats.add(Stat.instructions, instructionsPerTick * 60, StatUnit.perSecond);
|
stats.add(Stat.linkRange, range / 8, StatUnit.blocks);
|
||||||
|
stats.add(Stat.instructions, instructionsPerTick * 60, StatUnit.perSecond);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||||
|
if(privileged) return;
|
||||||
|
|
||||||
Drawf.circles(x*tilesize + offset, y*tilesize + offset, range);
|
Drawf.circles(x*tilesize + offset, y*tilesize + offset, range);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,6 +225,10 @@ public class LogicBlock extends Block{
|
|||||||
/** Block of code to run after load. */
|
/** Block of code to run after load. */
|
||||||
public @Nullable Runnable loadBlock;
|
public @Nullable Runnable loadBlock;
|
||||||
|
|
||||||
|
{
|
||||||
|
executor.privileged = privileged;
|
||||||
|
}
|
||||||
|
|
||||||
public void readCompressed(byte[] data, boolean relative){
|
public void readCompressed(byte[] data, boolean relative){
|
||||||
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(data)))){
|
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(data)))){
|
||||||
int version = stream.read();
|
int version = stream.read();
|
||||||
@@ -292,7 +318,7 @@ public class LogicBlock extends Block{
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
//create assembler to store extra variables
|
//create assembler to store extra variables
|
||||||
LAssembler asm = LAssembler.assemble(str);
|
LAssembler asm = LAssembler.assemble(str, privileged);
|
||||||
|
|
||||||
//store connections
|
//store connections
|
||||||
for(LogicLink link : links){
|
for(LogicLink link : links){
|
||||||
@@ -344,11 +370,29 @@ public class LogicBlock extends Block{
|
|||||||
executor.load(asm);
|
executor.load(asm);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
//handle malformed code and replace it with nothing
|
//handle malformed code and replace it with nothing
|
||||||
executor.load(code = "");
|
executor.load(LAssembler.assemble(code = "", privileged));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//editor-only processors cannot be damaged or destroyed
|
||||||
|
@Override
|
||||||
|
public boolean collide(Bullet other){
|
||||||
|
return !privileged;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void damage(float damage){
|
||||||
|
if(!privileged){
|
||||||
|
super.damage(damage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cursor getCursor(){
|
||||||
|
return !accessible() ? SystemCursor.arrow : super.getCursor();
|
||||||
|
}
|
||||||
|
|
||||||
//logic blocks cause write problems when picked up
|
//logic blocks cause write problems when picked up
|
||||||
@Override
|
@Override
|
||||||
public boolean canPickup(){
|
public boolean canPickup(){
|
||||||
@@ -426,15 +470,13 @@ public class LogicBlock extends Block{
|
|||||||
updateCode(code, true, null);
|
updateCode(code, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(enabled){
|
if(enabled && executor.initialized()){
|
||||||
accumulator += edelta() * instructionsPerTick * (consValid() ? 1 : 0);
|
accumulator += edelta() * instructionsPerTick * (consValid() ? 1 : 0);
|
||||||
|
|
||||||
if(accumulator > maxInstructionScale * instructionsPerTick) accumulator = maxInstructionScale * instructionsPerTick;
|
if(accumulator > maxInstructionScale * instructionsPerTick) accumulator = maxInstructionScale * instructionsPerTick;
|
||||||
|
|
||||||
for(int i = 0; i < (int)accumulator; i++){
|
for(int i = 0; i < (int)accumulator; i++){
|
||||||
if(executor.initialized()){
|
executor.runOnce();
|
||||||
executor.runOnce();
|
|
||||||
}
|
|
||||||
accumulator --;
|
accumulator --;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -460,7 +502,9 @@ public class LogicBlock extends Block{
|
|||||||
public void drawConfigure(){
|
public void drawConfigure(){
|
||||||
super.drawConfigure();
|
super.drawConfigure();
|
||||||
|
|
||||||
Drawf.circles(x, y, range);
|
if(!privileged){
|
||||||
|
Drawf.circles(x, y, range);
|
||||||
|
}
|
||||||
|
|
||||||
for(LogicLink l : links){
|
for(LogicLink l : links){
|
||||||
Building build = world.build(l.x, l.y);
|
Building build = world.build(l.x, l.y);
|
||||||
@@ -486,19 +530,25 @@ public class LogicBlock extends Block{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean validLink(Building other){
|
public boolean validLink(Building other){
|
||||||
return other != null && other.isValid() && other.team == team && other.within(this, range + other.block.size*tilesize/2f) && !(other instanceof ConstructBuild);
|
return other != null && other.isValid() && (privileged || (other.team == team && other.within(this, range + other.block.size*tilesize/2f))) && !(other instanceof ConstructBuild);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void buildConfiguration(Table table){
|
public void buildConfiguration(Table table){
|
||||||
|
if(!accessible()){
|
||||||
|
//go away
|
||||||
|
deselect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
table.button(Icon.pencil, Styles.clearTransi, () -> {
|
table.button(Icon.pencil, Styles.clearTransi, () -> {
|
||||||
ui.logic.show(code, executor, code -> configure(compress(code, relativeConnections())));
|
ui.logic.show(code, executor, privileged, code -> configure(compress(code, relativeConnections())));
|
||||||
}).size(40);
|
}).size(40);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onConfigureTileTapped(Building other){
|
public boolean onConfigureTileTapped(Building other){
|
||||||
if(this == other){
|
if(this == other || !accessible()){
|
||||||
deselect();
|
deselect();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,4 +24,4 @@ android.useAndroidX=true
|
|||||||
#used for slow jitpack builds; TODO see if this actually works
|
#used for slow jitpack builds; TODO see if this actually works
|
||||||
org.gradle.internal.http.socketTimeout=100000
|
org.gradle.internal.http.socketTimeout=100000
|
||||||
org.gradle.internal.http.connectionTimeout=100000
|
org.gradle.internal.http.connectionTimeout=100000
|
||||||
archash=3ad7f0345b
|
archash=15cbd2710c
|
||||||
|
|||||||
Reference in New Issue
Block a user