diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 17683467c9..880f697a25 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -18,6 +18,7 @@ import mindustry.world.blocks.environment.*; import mindustry.world.blocks.experimental.*; import mindustry.world.blocks.legacy.*; import mindustry.world.blocks.liquid.*; +import mindustry.world.blocks.logic.*; import mindustry.world.blocks.power.*; import mindustry.world.blocks.production.*; import mindustry.world.blocks.sandbox.*; @@ -1886,7 +1887,7 @@ public class Blocks implements ContentList{ //endregion campaign //region experimental - logicProcessor = new ResearchBlock("logic-processor"){{ + logicProcessor = new LogicProcessor("logic-processor"){{ requirements(Category.effect, BuildVisibility.debugOnly, with(Items.copper, 200, Items.lead, 100)); size = 3; diff --git a/core/src/mindustry/logic/LAssembler.java b/core/src/mindustry/logic/LAssembler.java index 7fd12026d9..471cf8770e 100644 --- a/core/src/mindustry/logic/LAssembler.java +++ b/core/src/mindustry/logic/LAssembler.java @@ -1,7 +1,6 @@ package mindustry.logic; import arc.struct.*; -import arc.util.ArcAnnotate.*; import mindustry.io.*; import mindustry.logic.LCanvas.*; import mindustry.logic.LExecutor.*; @@ -13,8 +12,6 @@ public class LAssembler{ ObjectMap vars = new ObjectMap<>(); /** All instructions to be executed. */ LInstruction[] instructions; - /** Statement UI elements being processed. */ - @Nullable Seq elements; public LAssembler(){ //add default constants @@ -26,35 +23,42 @@ public class LAssembler{ public static LAssembler assemble(Seq seq){ LAssembler out = new LAssembler(); - out.elements = seq; + seq.each(s -> s.st.saveUI()); out.instructions = seq.map(s -> s.st.build(out)).toArray(LInstruction.class); return out; } + public static String toJson(Seq seq){ + seq.each(s -> s.st.saveUI()); + LStatement[] statements = seq.map(s -> s.st).toArray(LStatement.class); + + return JsonIO.write(statements); + } + //TODO this is awful and confusing public static LAssembler fromJson(String json){ LAssembler asm = new LAssembler(); LStatement[] statements = JsonIO.read(LStatement[].class, json); for(LStatement s : statements){ - s.afterLoad(asm); + s.setupUI(); } asm.instructions = Seq.with(statements).map(l -> l.build(asm)).toArray(LInstruction.class); return asm; } - public String toJson(){ - Seq states = elements.map(s -> s.st); - states.each(s -> s.beforeSave(this)); - return JsonIO.write(states.toArray(LStatement.class)); - } - /** @return a variable ID by name. * This may be a constant variable referring to a number or object. */ public int var(String symbol){ symbol = symbol.trim(); + + //string case + if(symbol.startsWith("\"") && symbol.endsWith("\"")){ + return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1)).id; + } + try{ double value = Double.parseDouble(symbol); //this creates a hidden const variable with the specified value @@ -66,7 +70,7 @@ public class LAssembler{ } /** Adds a constant value by name. */ - private BVar putConst(String name, Object value){ + BVar putConst(String name, Object value){ BVar var = putVar(name); var.constant = true; var.value = value; @@ -74,7 +78,7 @@ public class LAssembler{ } /** Registers a variable name mapping. */ - private BVar putVar(String name){ + BVar putVar(String name){ if(vars.containsKey(name)){ return vars.get(name); }else{ diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index ce1c4d73ba..3824dc8cc8 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -35,11 +35,8 @@ public class LCanvas extends Table{ pane(statements).grow().get().setClip(false); + add(new PrintStatement()); add(new AssignStatement()); - add(new FetchBuildStatement()); - add(new JumpStatement()); - add(new ToggleStatement()); - add(new OpStatement()); } private void drawGrid(){ @@ -73,7 +70,7 @@ public class LCanvas extends Table{ } String save(){ - return LAssembler.assemble(statements.getChildren().as()).toJson(); + return LAssembler.toJson(statements.getChildren().as()); } void load(String json){ @@ -83,11 +80,8 @@ public class LCanvas extends Table{ add(st); } - LAssembler asm = new LAssembler(); - asm.elements = this.statements.getChildren().as(); - for(LStatement st : statements){ - st.afterLoad(asm); + st.setupUI(); } this.statements.layout(); @@ -197,6 +191,7 @@ public class LCanvas extends Table{ public StatementElem(LStatement st){ this.st = st; + st.elem = this; background(Tex.whitePane); setColor(st.category().color); diff --git a/core/src/mindustry/logic/LDialog.java b/core/src/mindustry/logic/LDialog.java index d16007e355..41aa30586b 100644 --- a/core/src/mindustry/logic/LDialog.java +++ b/core/src/mindustry/logic/LDialog.java @@ -1,10 +1,13 @@ package mindustry.logic; +import arc.func.*; import arc.scene.ui.layout.*; +import arc.util.*; import mindustry.ui.dialogs.*; public class LDialog extends BaseDialog{ - mindustry.logic.LCanvas canvas; + LCanvas canvas; + Cons consumer = s -> Log.info(s); public LDialog(){ super("logic"); @@ -17,5 +20,16 @@ public class LDialog extends BaseDialog{ t.bottom(); t.add(buttons); })).grow(); + + hidden(() -> { + consumer.get(canvas.save()); + }); + } + + public void show(String code, Cons consumer){ + canvas.load(code); + this.consumer = consumer; + + show(); } } diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index af9fc7352d..0f571a9629 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -1,6 +1,7 @@ package mindustry.logic; import arc.util.ArcAnnotate.*; +import arc.util.*; import mindustry.*; import mindustry.gen.*; @@ -11,6 +12,10 @@ public class LExecutor{ //values of all the variables Var[] vars; + public boolean initialized(){ + return instructions != null && vars != null && instructions.length > 0; + } + /** Runs all the instructions at once. Debugging only. */ public void runAll(){ counter = 0; @@ -20,7 +25,9 @@ public class LExecutor{ } } - public void load(LAssembler builder){ + public void load(Object context, LAssembler builder){ + builder.putConst("this", context); + vars = new Var[builder.vars.size]; instructions = builder.instructions; counter = 0; @@ -58,6 +65,11 @@ public class LExecutor{ return v.isobj ? 0 : v.numval; } + String str(int index){ + Var v = vars[index]; + return v.isobj ? String.valueOf(v.objval) : String.valueOf(v.numval); + } + int numi(int index){ return (int)num(index); } @@ -101,6 +113,8 @@ public class LExecutor{ this.value = value; } + ToggleI(){} + @Override public void run(LExecutor exec){ Building b = exec.building(target); @@ -125,6 +139,8 @@ public class LExecutor{ this.to = to; } + AssignI(){} + @Override public void run(LExecutor exec){ Var v = exec.vars[to]; @@ -144,7 +160,7 @@ public class LExecutor{ } public static class BinaryOpI implements LInstruction{ - public mindustry.logic.BinaryOp op; + public BinaryOp op; public int a, b, dest; public BinaryOpI(BinaryOp op, int a, int b, int dest){ @@ -154,6 +170,8 @@ public class LExecutor{ this.dest = dest; } + BinaryOpI(){} + @Override public void run(LExecutor exec){ exec.setnum(dest, op.function.get(exec.num(a), exec.num(b))); @@ -168,6 +186,21 @@ public class LExecutor{ } } + public static class PrintI implements LInstruction{ + public int value; + + public PrintI(int value){ + this.value = value; + } + + PrintI(){} + + @Override + public void run(LExecutor exec){ + Log.info(exec.str(value)); + } + } + public static class JumpI implements LInstruction{ public int cond, to; @@ -176,6 +209,8 @@ public class LExecutor{ this.to = to; } + JumpI(){} + @Override public void run(LExecutor exec){ if(to != -1 && exec.bool(cond)){ @@ -194,6 +229,8 @@ public class LExecutor{ this.y = y; } + FetchBuildI(){} + @Override public void run(LExecutor exec){ exec.setobj(dest, Vars.world.build(exec.numi(x), exec.numi(y))); diff --git a/core/src/mindustry/logic/LStatement.java b/core/src/mindustry/logic/LStatement.java index c4295b473e..32ebb7806f 100644 --- a/core/src/mindustry/logic/LStatement.java +++ b/core/src/mindustry/logic/LStatement.java @@ -1,19 +1,23 @@ package mindustry.logic; import arc.scene.ui.layout.*; +import arc.util.ArcAnnotate.*; +import mindustry.logic.LCanvas.*; +import mindustry.logic.LExecutor.*; /** A statement is an intermediate representation of an instruction, to be used in UI. */ public abstract class LStatement{ + public transient @Nullable StatementElem elem; public abstract void build(Table table); public abstract LCategory category(); - public abstract LExecutor.LInstruction build(LAssembler builder); + public abstract LInstruction build(LAssembler builder); - public void afterLoad(LAssembler assembler){ + public void setupUI(){ } - public void beforeSave(LAssembler assembler){ + public void saveUI(){ } diff --git a/core/src/mindustry/logic/LStatements.java b/core/src/mindustry/logic/LStatements.java index 4d8f3507d2..8d484145f8 100644 --- a/core/src/mindustry/logic/LStatements.java +++ b/core/src/mindustry/logic/LStatements.java @@ -115,8 +115,29 @@ public class LStatements{ } } + public static class PrintStatement extends LStatement{ + public String value = "\"frog\""; + + @Override + public void build(Table table){ + table.field(value, Styles.nodeField, str -> value = str) + .size(100f, 40f).pad(2f).color(table.color); + } + + @Override + public LInstruction build(LAssembler builder){ + return new PrintI(builder.var(value)); + } + + @Override + public LCategory category(){ + return LCategory.control; + } + } + public static class JumpStatement extends LStatement{ public transient StatementElem dest; + public int destIndex; public String condition = "true"; @@ -131,20 +152,21 @@ public class LStatements{ //elements need separate conversion logic @Override - public void afterLoad(LAssembler assembler){ - if(assembler.elements != null){ - dest = assembler.elements.get(destIndex); + public void setupUI(){ + if(elem != null){ + dest = (StatementElem)elem.parent.getChildren().get(destIndex); } } @Override - public void beforeSave(LAssembler assembler){ - destIndex = dest == null ? -1 : dest.parent.getChildren().indexOf(dest); + public void saveUI(){ + if(elem != null){ + destIndex = dest == null ? -1 : dest.parent.getChildren().indexOf(dest); + } } @Override public LInstruction build(LAssembler builder){ - beforeSave(builder); return new JumpI(builder.var(condition),destIndex); } diff --git a/core/src/mindustry/world/blocks/logic/LogicProcessor.java b/core/src/mindustry/world/blocks/logic/LogicProcessor.java index e6d180d83c..f12a100fdc 100644 --- a/core/src/mindustry/world/blocks/logic/LogicProcessor.java +++ b/core/src/mindustry/world/blocks/logic/LogicProcessor.java @@ -1,7 +1,9 @@ package mindustry.world.blocks.logic; +import arc.util.io.*; import mindustry.*; import mindustry.gen.*; +import mindustry.logic.*; import mindustry.world.*; public class LogicProcessor extends Block{ @@ -10,19 +12,46 @@ public class LogicProcessor extends Block{ super(name); update = true; configurable = true; + + config(String.class, (LogicEntity entity, String code) -> { + if(code != null){ + entity.code = code; + entity.executor.load(entity, LAssembler.fromJson(code)); + } + }); } public class LogicEntity extends Building{ + /** logic "source code" as list of json statements */ + String code = "[]"; + LExecutor executor = new LExecutor(); @Override public void updateTile(){ - + if(executor.initialized()){ + executor.runAll(); + } } @Override public boolean configTapped(){ - Vars.ui.logic.show(); + Vars.ui.logic.show(code, this::configure); return false; } + + @Override + public void write(Writes write){ + super.write(write); + + write.str(code); + } + + @Override + public void read(Reads read, byte revision){ + super.read(read, revision); + + code = read.str(); + executor.load(this, LAssembler.fromJson(code)); + } } }