Logic progress
This commit is contained in:
@@ -81,10 +81,10 @@ public class Blocks implements ContentList{
|
||||
repairPoint, resupplyPoint,
|
||||
|
||||
//campaign
|
||||
launchPad, launchPadLarge, dataProcessor,
|
||||
launchPad, launchPadLarge,
|
||||
|
||||
//misc experimental
|
||||
blockForge, blockLoader, blockUnloader;
|
||||
logicProcessor, blockForge, blockLoader, blockUnloader;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
@@ -1883,16 +1883,16 @@ public class Blocks implements ContentList{
|
||||
consumes.power(6f);
|
||||
}};
|
||||
|
||||
dataProcessor = new ResearchBlock("data-processor"){{
|
||||
//requirements(Category.effect, BuildVisibility.campaignOnly, with(Items.copper, 200, Items.lead, 100));
|
||||
//endregion campaign
|
||||
//region experimental
|
||||
|
||||
logicProcessor = new ResearchBlock("logic-processor"){{
|
||||
requirements(Category.effect, BuildVisibility.debugOnly, with(Items.copper, 200, Items.lead, 100));
|
||||
|
||||
size = 3;
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
|
||||
//endregion campaign
|
||||
//region experimental
|
||||
|
||||
blockForge = new BlockForge("block-forge"){{
|
||||
requirements(Category.production, BuildVisibility.debugOnly, with(Items.thorium, 100));
|
||||
hasPower = true;
|
||||
|
||||
@@ -24,6 +24,7 @@ import mindustry.editor.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.ui.fragments.*;
|
||||
@@ -67,6 +68,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
public SchematicsDialog schematics;
|
||||
public ModsDialog mods;
|
||||
public ColorPicker picker;
|
||||
public LogicDialog logic;
|
||||
|
||||
public Cursor drillCursor, unloadCursor;
|
||||
|
||||
@@ -179,6 +181,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
research = new ResearchDialog();
|
||||
mods = new ModsDialog();
|
||||
schematics = new SchematicsDialog();
|
||||
logic = new LogicDialog();
|
||||
|
||||
Group group = Core.scene.root;
|
||||
|
||||
|
||||
@@ -278,7 +278,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
units.sort(u -> u.dst2(player.unit()));
|
||||
units.truncate(player.unit().type().commandLimit);
|
||||
|
||||
if(units.any()) pattern.spacing = units.max(u -> u.hitSize).hitSize * 2.4f;
|
||||
if(units.any()) pattern.spacing = units.max(u -> u.hitSize).hitSize * 2f;
|
||||
|
||||
commander.command(formation, units);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,11 @@ public enum BinaryOp{
|
||||
mul("*", (a, b) -> a * b),
|
||||
div("/", (a, b) -> a / b),
|
||||
mod("%", (a, b) -> a % b),
|
||||
equal("=", (a, b) -> Math.abs(a - b) < 0.000001 ? 1 : 0),
|
||||
lessThan("<", (a, b) -> a < b ? 1 : 0),
|
||||
lessThanEq("<=", (a, b) -> a <= b ? 1 : 0),
|
||||
greaterThan(">", (a, b) -> a > b ? 1 : 0),
|
||||
greaterThanEq(">=", (a, b) -> a >= b ? 1 : 0),
|
||||
pow("^", Math::pow),
|
||||
shl(">>", (a, b) -> (int)a >> (int)b),
|
||||
shr("<<", (a, b) -> (int)a << (int)b),
|
||||
@@ -13,18 +18,16 @@ public enum BinaryOp{
|
||||
and("and", (a, b) -> (int)a & (int)b),
|
||||
xor("xor", (a, b) -> (int)a ^ (int)b);
|
||||
|
||||
final OpLambda function;
|
||||
final String symbol;
|
||||
public static final BinaryOp[] all = values();
|
||||
|
||||
public final OpLambda function;
|
||||
public final String symbol;
|
||||
|
||||
BinaryOp(String symbol, OpLambda function){
|
||||
this.symbol = symbol;
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public double get(double a, double b){
|
||||
return function.get(a, b);
|
||||
}
|
||||
|
||||
interface OpLambda{
|
||||
double get(double a, double b);
|
||||
}
|
||||
|
||||
@@ -14,23 +14,33 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.LogicNode.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
public class LogicCanvas extends WidgetGroup{
|
||||
private static final Color backgroundCol = Color.black, gridCol = Pal.accent.cpy().mul(0.2f);
|
||||
private static final Color outCol = Pal.place, inCol = Pal.remove;
|
||||
private static final Color backgroundCol = Pal.darkMetal.cpy().mul(0.1f), gridCol = Pal.darkMetal.cpy().mul(0.5f);
|
||||
|
||||
private Element selected;
|
||||
private Element entered;
|
||||
private Seq<LogicNode> nodes = new Seq<>();
|
||||
|
||||
{
|
||||
for(int i = 0; i < 3; i++){
|
||||
LogicElement e = new LogicElement();
|
||||
e.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f);
|
||||
addChild(e);
|
||||
e.pack();
|
||||
}
|
||||
add(new BinaryOpNode());
|
||||
add(new BinaryOpNode());
|
||||
add(new BinaryOpNode());
|
||||
add(new NumberNode());
|
||||
add(new NumberNode());
|
||||
add(new NumberNode());
|
||||
add(new ConditionNode());
|
||||
add(new ConditionNode());
|
||||
add(new SignalNode());
|
||||
}
|
||||
|
||||
private void add(LogicNode node){
|
||||
LogicElement e = new LogicElement(node);
|
||||
e.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f);
|
||||
addChild(e);
|
||||
e.pack();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,14 +74,14 @@ public class LogicCanvas extends WidgetGroup{
|
||||
if(e instanceof LogicElement){
|
||||
LogicElement l = (LogicElement)e;
|
||||
|
||||
for(NodeField field : l.fields){
|
||||
for(SlotTable field : l.slots){
|
||||
field.drawConnection();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(selected != null){
|
||||
NodeField field = (NodeField)selected.userObject;
|
||||
SlotTable field = (SlotTable)selected.userObject;
|
||||
Vec2 dest = selected.localToStageCoordinates(Tmp.v1.set(selected.getWidth()/2f, selected.getHeight()/2f));
|
||||
Vec2 mouse = Core.input.mouse();
|
||||
drawCurve(dest.x, dest.y, mouse.x, mouse.y, field.color);
|
||||
@@ -80,10 +90,13 @@ public class LogicCanvas extends WidgetGroup{
|
||||
|
||||
void drawCurve(float x, float y, float x2, float y2, Color color){
|
||||
Lines.stroke(4f, color);
|
||||
|
||||
float dist = Math.abs(x - x2)/2f;
|
||||
|
||||
Lines.curve(
|
||||
x, y,
|
||||
x2, y,
|
||||
x, y2,
|
||||
x + dist, y,
|
||||
x2 - dist, y2,
|
||||
x2, y2,
|
||||
Math.max(3, (int)(Mathf.dst(x, y, x2, y2) / 5))
|
||||
);
|
||||
@@ -92,12 +105,22 @@ public class LogicCanvas extends WidgetGroup{
|
||||
}
|
||||
|
||||
class LogicElement extends Table{
|
||||
LogicNode node;
|
||||
NodeField[] fields = {new NodeField(true, "input 1"), new NodeField(true, "input 2"), new NodeField(false, "output 1"), new NodeField(false, "output 2")};
|
||||
final LogicNode node;
|
||||
final SlotTable[] slots;
|
||||
|
||||
LogicElement(LogicNode node){
|
||||
this.node = node;
|
||||
nodes.add(node);
|
||||
|
||||
NodeSlot[] nslots = node.slots();
|
||||
|
||||
this.slots = new SlotTable[nslots.length];
|
||||
for(int i = 0; i < nslots.length; i++){
|
||||
this.slots[i] = new SlotTable(nslots[i]);
|
||||
}
|
||||
|
||||
LogicElement(){
|
||||
background(Tex.whitePane);
|
||||
setColor(Pal.accent.cpy().mul(0.9f).shiftSaturation(-0.3f));
|
||||
setColor(node.category().color);
|
||||
margin(0f);
|
||||
|
||||
table(Tex.whiteui, t -> {
|
||||
@@ -108,11 +131,12 @@ public class LogicCanvas extends WidgetGroup{
|
||||
t.margin(8f);
|
||||
t.touchable = Touchable.enabled;
|
||||
|
||||
t.add("Node").style(Styles.outlineLabel).color(color);
|
||||
t.add(node.name()).style(Styles.outlineLabel).color(color).padRight(8);
|
||||
t.add().growX();
|
||||
t.button(Icon.cancel, Styles.onlyi, () -> {
|
||||
//TODO disconnect things
|
||||
remove();
|
||||
nodes.remove(node);
|
||||
});
|
||||
t.addListener(new InputListener(){
|
||||
float lastx, lasty;
|
||||
@@ -139,10 +163,14 @@ public class LogicCanvas extends WidgetGroup{
|
||||
|
||||
row();
|
||||
|
||||
node.build(this);
|
||||
|
||||
row();
|
||||
|
||||
defaults().height(30);
|
||||
|
||||
for(NodeField field : fields){
|
||||
add(field).align(field.input ? Align.left : Align.right);
|
||||
for(SlotTable field : slots){
|
||||
add(field).align(field.slot.input ? Align.left : Align.right);
|
||||
row();
|
||||
}
|
||||
|
||||
@@ -162,19 +190,20 @@ public class LogicCanvas extends WidgetGroup{
|
||||
}
|
||||
}
|
||||
|
||||
class NodeField extends Table{
|
||||
boolean input;
|
||||
class SlotTable extends Table{
|
||||
final NodeSlot slot;
|
||||
|
||||
ImageButton button;
|
||||
Element connection;
|
||||
|
||||
NodeField(boolean input, String name){
|
||||
this.input = input;
|
||||
//TODO color should depend on data type
|
||||
setColor(outCol);
|
||||
SlotTable(NodeSlot slot){
|
||||
this.slot = slot;
|
||||
|
||||
setColor(slot.type.color);
|
||||
|
||||
float marg = 24f;
|
||||
|
||||
if(input){
|
||||
if(slot.input){
|
||||
addIcon();
|
||||
left();
|
||||
marginRight(marg);
|
||||
@@ -183,9 +212,9 @@ public class LogicCanvas extends WidgetGroup{
|
||||
marginLeft(marg);
|
||||
}
|
||||
|
||||
add(name).padLeft(5).padRight(5).style(Styles.outlineLabel).color(color);
|
||||
add(slot.name).padLeft(5).padRight(5).style(Styles.outlineLabel).color(color);
|
||||
|
||||
if(!input){
|
||||
if(!slot.input){
|
||||
addIcon();
|
||||
}
|
||||
}
|
||||
@@ -211,7 +240,7 @@ public class LogicCanvas extends WidgetGroup{
|
||||
|
||||
float pad = s/2f - 3f;
|
||||
|
||||
if(input){
|
||||
if(slot.input){
|
||||
c.padLeft(-pad);
|
||||
}else{
|
||||
c.padRight(-pad);
|
||||
@@ -236,9 +265,10 @@ public class LogicCanvas extends WidgetGroup{
|
||||
localToStageCoordinates(Tmp.v1.set(x, y));
|
||||
Element element = entered;
|
||||
|
||||
if(element != null && element.userObject instanceof NodeField){
|
||||
NodeField field = (NodeField)element.userObject;
|
||||
if(field != NodeField.this && field.input != input){
|
||||
if(element != null && element.userObject instanceof SlotTable){
|
||||
SlotTable field = (SlotTable)element.userObject;
|
||||
//make sure inputs are matched to outputs, and that slot types match
|
||||
if(field != SlotTable.this && field.slot.input != slot.input && field.slot.type == slot.type){
|
||||
connection = element;
|
||||
//field.connection = button;
|
||||
}
|
||||
|
||||
@@ -1,29 +1,193 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
/** Base class for a type of logic node. */
|
||||
public abstract class LogicNode{
|
||||
|
||||
/** Runs the calculation(s) and sets the output value. */
|
||||
public void run(){}
|
||||
|
||||
public NodeInput[] inputs(){
|
||||
return new NodeInput[0];
|
||||
public NodeSlot[] slots(){
|
||||
return (NodeSlot[])LogicSlotMap.map.get(getClass());
|
||||
}
|
||||
|
||||
public static class BinaryOpNode{
|
||||
public abstract NodeCategory category();
|
||||
|
||||
public void build(Table table){}
|
||||
|
||||
public String name(){
|
||||
return getClass().getSimpleName().replace("Node", "");
|
||||
}
|
||||
|
||||
public static class BinaryOpNode extends LogicNode{
|
||||
public BinaryOp op = BinaryOp.add;
|
||||
@NodeSlotDef
|
||||
public double a, b;
|
||||
@NodeSlotDef
|
||||
public SetNum result = val -> {};
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
TextButton[] button = {null};
|
||||
button[0] = table.button(op.symbol, Styles.cleart, () -> {
|
||||
op = BinaryOp.all[(op.ordinal() + 1) % BinaryOp.all.length];
|
||||
button[0].setText(op.symbol);
|
||||
}).size(100f, 40f).pad(2f).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
result.set(op.function.get(a, b));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeCategory category(){
|
||||
return NodeCategory.operations;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NodeInput<T>{
|
||||
public boolean num;
|
||||
public String name;
|
||||
public SetNum setNum;
|
||||
public Cons<T> setObject;
|
||||
public static class ConditionNode extends LogicNode{
|
||||
@NodeSlotDef(input = true)
|
||||
public Runnable input = this::run;
|
||||
@NodeSlotDef
|
||||
public double condition;
|
||||
@NodeSlotDef
|
||||
public Runnable yes, no;
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
if(condition > 0){
|
||||
yes.run();
|
||||
}else{
|
||||
no.run();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeCategory category(){
|
||||
return NodeCategory.controlFlow;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NumberNode extends LogicNode{
|
||||
@NodeSlotDef
|
||||
public SetNum value;
|
||||
public double var;
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
table.field(var + "", Styles.nodeField, str -> var = Strings.parseDouble(str, var))
|
||||
.valid(Strings::canParsePositiveFloat)
|
||||
.size(100f, 40f).pad(2f).color(table.color)
|
||||
.update(f -> f.setColor(f.isValid() ? table.color : Color.white));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
value.set(var);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeCategory category(){
|
||||
return NodeCategory.controlFlow;
|
||||
}
|
||||
}
|
||||
|
||||
public static class SignalNode extends LogicNode{
|
||||
@NodeSlotDef
|
||||
public Runnable run;
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
run.run();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NodeCategory category(){
|
||||
return NodeCategory.controlFlow;
|
||||
}
|
||||
}
|
||||
|
||||
/** A field for a node, either an input or output. */
|
||||
public static class NodeSlot{
|
||||
/** The slot's display name. */
|
||||
public final String name;
|
||||
/** If true this field accepts values. */
|
||||
public final boolean input;
|
||||
/** The type of data accepted or returned by this slot. */
|
||||
public final DataType type;
|
||||
|
||||
public final NumOutput<?> numOutput;
|
||||
public final ObjOutput<?, ?> objOutput;
|
||||
|
||||
public NodeSlot(String name, boolean input, DataType type, NumOutput<?> numOutput, ObjOutput<?, ?> objOutput){
|
||||
this.name = name;
|
||||
this.input = input;
|
||||
this.type = type;
|
||||
this.numOutput = numOutput;
|
||||
this.objOutput = objOutput;
|
||||
}
|
||||
}
|
||||
|
||||
static{
|
||||
new NodeSlot("a", true, DataType.number, (BinaryOpNode node, double val) -> node.a = val, null);
|
||||
}
|
||||
|
||||
public interface NumOutput<N>{
|
||||
void set(N node, double val);
|
||||
}
|
||||
|
||||
public interface ObjOutput<N, T>{
|
||||
void set(N node, T val);
|
||||
}
|
||||
|
||||
public interface SetNum{
|
||||
void set(double val);
|
||||
}
|
||||
|
||||
public interface SetObj<T>{
|
||||
void set(T val);
|
||||
}
|
||||
|
||||
public enum NodeCategory{
|
||||
controlFlow(Pal.accentBack),
|
||||
operations(Pal.place.cpy().shiftSaturation(-0.4f).mul(0.7f));
|
||||
|
||||
public final Color color;
|
||||
|
||||
NodeCategory(Color color){
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
/** The types of data a node field can be. */
|
||||
public enum DataType{
|
||||
/** A double. Used for integer calculations as well. */
|
||||
number(Pal.place),
|
||||
/** Any type of content, e.g. item. */
|
||||
content(Color.cyan),
|
||||
/** A building of a tile. */
|
||||
building(Pal.items),
|
||||
/** A unit on the map. */
|
||||
unit(Pal.health),
|
||||
/** Control flow (void)*/
|
||||
control(Color.white),
|
||||
/** Java string */
|
||||
string(Color.royal);
|
||||
|
||||
public final Color color;
|
||||
|
||||
DataType(Color color){
|
||||
this.color = color;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public class Styles{
|
||||
public static KeybindDialogStyle defaultKeybindDialog;
|
||||
public static SliderStyle defaultSlider, vSlider;
|
||||
public static LabelStyle defaultLabel, outlineLabel, techLabel;
|
||||
public static TextFieldStyle defaultField, areaField;
|
||||
public static TextFieldStyle defaultField, nodeField, areaField;
|
||||
public static CheckBoxStyle defaultCheck;
|
||||
public static DialogStyle defaultDialog, fullDialog;
|
||||
|
||||
@@ -306,6 +306,20 @@ public class Styles{
|
||||
messageFont = Fonts.def;
|
||||
messageFontColor = Color.gray;
|
||||
}};
|
||||
|
||||
nodeField = new TextFieldStyle(){{
|
||||
font = Fonts.chat;
|
||||
fontColor = Color.white;
|
||||
disabledFontColor = Color.gray;
|
||||
disabledBackground = underlineDisabled;
|
||||
selection = Tex.selection;
|
||||
background = underlineWhite;
|
||||
invalidBackground = underlineRed;
|
||||
cursor = Tex.cursor;
|
||||
messageFont = Fonts.def;
|
||||
messageFontColor = Color.gray;
|
||||
}};
|
||||
|
||||
areaField = new TextFieldStyle(){{
|
||||
font = Fonts.chat;
|
||||
fontColor = Color.white;
|
||||
|
||||
28
core/src/mindustry/world/blocks/logic/LogicProcessor.java
Normal file
28
core/src/mindustry/world/blocks/logic/LogicProcessor.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package mindustry.world.blocks.logic;
|
||||
|
||||
import mindustry.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class LogicProcessor extends Block{
|
||||
|
||||
public LogicProcessor(String name){
|
||||
super(name);
|
||||
update = true;
|
||||
configurable = true;
|
||||
}
|
||||
|
||||
public class LogicEntity extends Building{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configTapped(){
|
||||
Vars.ui.logic.show();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user