Merge branch 'master' of https://github.com/Anuken/Mindustry into tileable-logic-displays
# Conflicts: # core/assets/icons/icons.properties # core/assets/logicids.dat
This commit is contained in:
@@ -22,7 +22,8 @@ import static mindustry.Vars.*;
|
||||
/** Stores global logic variables for logic processors. */
|
||||
public class GlobalVars{
|
||||
public static final int ctrlProcessor = 1, ctrlPlayer = 2, ctrlCommand = 3;
|
||||
public static final ContentType[] lookableContent = {ContentType.block, ContentType.unit, ContentType.item, ContentType.liquid};
|
||||
public static final ContentType[] lookableContent = {ContentType.block, ContentType.unit, ContentType.item, ContentType.liquid, ContentType.team};
|
||||
public static final ContentType[] writableLookableContent = {ContentType.block, ContentType.unit, ContentType.item, ContentType.liquid};
|
||||
/** Global random state. */
|
||||
public static final Rand rand = new Rand();
|
||||
|
||||
@@ -34,9 +35,9 @@ public class GlobalVars{
|
||||
private ObjectSet<String> privilegedNames = new ObjectSet<>();
|
||||
private UnlockableContent[][] logicIdToContent;
|
||||
private int[][] contentIdToLogicId;
|
||||
|
||||
|
||||
public static final Seq<String> soundNames = new Seq<>();
|
||||
|
||||
|
||||
public void init(){
|
||||
putEntryOnly("sectionProcessor");
|
||||
|
||||
@@ -73,7 +74,7 @@ public class GlobalVars{
|
||||
|
||||
varMapW = putEntry("@mapw", 0);
|
||||
varMapH = putEntry("@maph", 0);
|
||||
varWait = putEntry("@wait", null);
|
||||
varWait = put("@wait", null, true, true);
|
||||
|
||||
putEntryOnly("sectionNetwork");
|
||||
|
||||
@@ -91,7 +92,7 @@ public class GlobalVars{
|
||||
put("@ctrlProcessor", ctrlProcessor);
|
||||
put("@ctrlPlayer", ctrlPlayer);
|
||||
put("@ctrlCommand", ctrlCommand);
|
||||
|
||||
|
||||
//sounds
|
||||
if(Core.assets != null){
|
||||
for(Sound sound : Core.assets.getAll(Sound.class, new Seq<>(Sound.class))){
|
||||
@@ -155,7 +156,7 @@ public class GlobalVars{
|
||||
if(ids.exists()){
|
||||
//read logic ID mapping data (generated in ImagePacker)
|
||||
try(DataInputStream in = new DataInputStream(ids.readByteStream())){
|
||||
for(ContentType ctype : lookableContent){
|
||||
for(ContentType ctype : writableLookableContent){
|
||||
short amount = in.readShort();
|
||||
logicIdToContent[ctype.ordinal()] = new UnlockableContent[amount];
|
||||
contentIdToLogicId[ctype.ordinal()] = new int[Vars.content.getBy(ctype).size];
|
||||
@@ -203,7 +204,7 @@ public class GlobalVars{
|
||||
varClient.numval = net.client() ? 1 : 0;
|
||||
|
||||
//client
|
||||
if(!net.server() && player != null){
|
||||
if(player != null){
|
||||
varClientLocale.objval = player.locale();
|
||||
varClientUnit.objval = player.unit();
|
||||
varClientName.objval = player.name();
|
||||
@@ -220,8 +221,13 @@ public class GlobalVars{
|
||||
return varEntries;
|
||||
}
|
||||
|
||||
/** @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){
|
||||
/** @return a piece of content based on its logic ID. This is not equivalent to content ID. In the case of teams, the return value may not be Content. */
|
||||
public @Nullable Object lookupContent(ContentType type, int id){
|
||||
//teams are a special case; they are not technically content, but can be looked up
|
||||
if(type == ContentType.team){
|
||||
return id >= 0 && id < 256 ? Team.all[id] : null;
|
||||
}
|
||||
|
||||
var arr = logicIdToContent[type.ordinal()];
|
||||
return arr != null && id >= 0 && id < arr.length ? arr[id] : null;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ public enum LAccess{
|
||||
ammo,
|
||||
ammoCapacity,
|
||||
currentAmmoType,
|
||||
memoryCapacity,
|
||||
health,
|
||||
maxHealth,
|
||||
heat,
|
||||
@@ -55,6 +56,8 @@ public enum LAccess{
|
||||
name,
|
||||
payloadCount,
|
||||
payloadType,
|
||||
totalPayload,
|
||||
payloadCapacity,
|
||||
id,
|
||||
|
||||
//values with parameters are considered controllable
|
||||
|
||||
@@ -11,7 +11,8 @@ import mindustry.logic.LExecutor.*;
|
||||
public class LAssembler{
|
||||
public static ObjectMap<String, Func<String[], LStatement>> customParsers = new ObjectMap<>();
|
||||
|
||||
private static final int invalidNum = Integer.MIN_VALUE;
|
||||
private static final int invalidNumNegative = Integer.MIN_VALUE;
|
||||
private static final int invalidNumPositive = Integer.MAX_VALUE;
|
||||
|
||||
private boolean privileged;
|
||||
/** Maps names to variable. */
|
||||
@@ -34,7 +35,7 @@ public class LAssembler{
|
||||
Seq<LStatement> st = read(data, privileged);
|
||||
|
||||
asm.privileged = privileged;
|
||||
|
||||
|
||||
asm.instructions = st.map(l -> l.build(asm)).retainAll(l -> l != null).toArray(LInstruction.class);
|
||||
return asm;
|
||||
}
|
||||
@@ -72,9 +73,11 @@ public class LAssembler{
|
||||
//remove spaces for non-strings
|
||||
symbol = symbol.replace(' ', '_');
|
||||
|
||||
double value = parseDouble(symbol);
|
||||
//use a positive invalid number if number might be negative, else use a negative invalid number
|
||||
int usedInvalidNum = symbol.length() > 0 && symbol.charAt(0) == '-' ? invalidNumPositive : invalidNumNegative;
|
||||
double value = parseDouble(symbol, usedInvalidNum);
|
||||
|
||||
if(value == invalidNum){
|
||||
if(value == usedInvalidNum){
|
||||
return putVar(symbol);
|
||||
}else{
|
||||
//this creates a hidden const variable with the specified value
|
||||
@@ -82,10 +85,14 @@ public class LAssembler{
|
||||
}
|
||||
}
|
||||
|
||||
double parseDouble(String symbol){
|
||||
double parseDouble(String symbol, int invalidNum){
|
||||
//parse hex/binary syntax
|
||||
if(symbol.startsWith("0b")) return Strings.parseLong(symbol, 2, 2, symbol.length(), invalidNum);
|
||||
if(symbol.startsWith("+0b")) return Strings.parseLong(symbol, 2, 3, symbol.length(), invalidNum);
|
||||
if(symbol.startsWith("-0b")) return -Strings.parseLong(symbol, 2, 3, symbol.length(), invalidNum);//FIXME: breaks with Long.MIN_VALUE
|
||||
if(symbol.startsWith("0x")) return Strings.parseLong(symbol, 16, 2, symbol.length(), invalidNum);
|
||||
if(symbol.startsWith("+0x")) return Strings.parseLong(symbol, 16, 3, symbol.length(), invalidNum);
|
||||
if(symbol.startsWith("-0x")) return -Strings.parseLong(symbol, 16, 3, symbol.length(), invalidNum);//FIXME: breaks with Long.MIN_VALUE
|
||||
if(symbol.startsWith("%") && (symbol.length() == 7 || symbol.length() == 9)) return parseColor(symbol);
|
||||
|
||||
return Strings.parseDouble(symbol, invalidNum);
|
||||
|
||||
@@ -20,18 +20,21 @@ import mindustry.logic.LStatements.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
public class LCanvas extends Table{
|
||||
public static final int maxJumpsDrawn = 100;
|
||||
private static final Seq<JumpCurve> tmpOccupiers1 = new Seq<>();
|
||||
private static final Seq<JumpCurve> tmpOccupiers2 = new Seq<>();
|
||||
private static final Bits tmpBits1 = new Bits();
|
||||
private static final Bits tmpBits2 = new Bits();
|
||||
private static final int invalidJump = Integer.MAX_VALUE; // terrible hack
|
||||
//ew static variables
|
||||
static LCanvas canvas;
|
||||
private static final boolean dynamicJumpHeights = true;
|
||||
|
||||
public DragLayout statements;
|
||||
public ScrollPane pane;
|
||||
public Group jumps;
|
||||
|
||||
StatementElem dragging;
|
||||
StatementElem hovered;
|
||||
float targetWidth;
|
||||
int jumpCount = 0;
|
||||
boolean privileged;
|
||||
Seq<Tooltip> tooltips = new Seq<>();
|
||||
|
||||
@@ -106,14 +109,15 @@ public class LCanvas extends Table{
|
||||
clear();
|
||||
|
||||
statements = new DragLayout();
|
||||
jumps = new WidgetGroup();
|
||||
|
||||
pane = pane(t -> {
|
||||
t.center();
|
||||
t.add(statements).pad(2f).center().width(targetWidth);
|
||||
t.addChild(jumps);
|
||||
t.addChild(statements.jumps);
|
||||
|
||||
jumps.cullable = false;
|
||||
statements.jumps.touchable = Touchable.disabled;
|
||||
statements.jumps.update(() -> statements.jumps.setCullingArea(t.getCullingArea()));
|
||||
statements.jumps.cullable = false;
|
||||
}).grow().get();
|
||||
pane.setFlickScroll(false);
|
||||
pane.setScrollYForce(s);
|
||||
@@ -123,12 +127,6 @@ public class LCanvas extends Table{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
jumpCount = 0;
|
||||
super.draw();
|
||||
}
|
||||
|
||||
public void add(LStatement statement){
|
||||
statements.addChild(new StatementElem(statement));
|
||||
}
|
||||
@@ -141,7 +139,7 @@ public class LCanvas extends Table{
|
||||
}
|
||||
|
||||
public void load(String asm){
|
||||
jumps.clear();
|
||||
statements.jumps.clear();
|
||||
|
||||
Seq<LStatement> statements = LAssembler.read(asm, privileged);
|
||||
statements.truncate(LExecutor.maxInstructions);
|
||||
@@ -154,13 +152,12 @@ public class LCanvas extends Table{
|
||||
st.setupUI();
|
||||
}
|
||||
|
||||
this.statements.layout();
|
||||
this.statements.updateJumpHeights = true;
|
||||
}
|
||||
|
||||
public void clearStatements(){
|
||||
jumps.clear();
|
||||
statements.jumps.clear();
|
||||
statements.clearChildren();
|
||||
statements.layout();
|
||||
}
|
||||
|
||||
StatementElem checkHovered(){
|
||||
@@ -195,6 +192,11 @@ public class LCanvas extends Table{
|
||||
Seq<Element> seq = new Seq<>();
|
||||
int insertPosition = 0;
|
||||
boolean invalidated;
|
||||
public Group jumps = new WidgetGroup();
|
||||
private Seq<JumpCurve> processedJumps = new Seq<>();
|
||||
private IntMap<JumpCurve> reprBefore = new IntMap<>();
|
||||
private IntMap<JumpCurve> reprAfter = new IntMap<>();
|
||||
public boolean updateJumpHeights = true;
|
||||
|
||||
{
|
||||
setTransform(true);
|
||||
@@ -206,10 +208,12 @@ public class LCanvas extends Table{
|
||||
float cy = 0;
|
||||
seq.clear();
|
||||
|
||||
float totalHeight = getChildren().sumf(e -> e.getHeight() + space);
|
||||
|
||||
height = prefHeight = totalHeight;
|
||||
width = prefWidth = Scl.scl(targetWidth);
|
||||
float totalHeight = getChildren().sumf(e -> e.getPrefHeight() + space);
|
||||
if(height != totalHeight || width != Scl.scl(targetWidth)){
|
||||
height = prefHeight = totalHeight;
|
||||
width = prefWidth = Scl.scl(targetWidth);
|
||||
invalidateHierarchy();
|
||||
}
|
||||
|
||||
//layout everything normally
|
||||
for(int i = 0; i < getChildren().size; i++){
|
||||
@@ -219,7 +223,7 @@ public class LCanvas extends Table{
|
||||
if(dragging == e) continue;
|
||||
|
||||
e.setSize(width, e.getPrefHeight());
|
||||
e.setPosition(0, height - cy, Align.topLeft);
|
||||
e.setPosition(0, totalHeight - cy, Align.topLeft);
|
||||
((StatementElem)e).updateAddress(i);
|
||||
|
||||
cy += e.getPrefHeight() + space;
|
||||
@@ -250,13 +254,97 @@ public class LCanvas extends Table{
|
||||
}
|
||||
}
|
||||
|
||||
if(parent != null) parent.invalidateHierarchy();
|
||||
if(dynamicJumpHeights){
|
||||
if(updateJumpHeights) setJumpHeights();
|
||||
updateJumpHeights = false;
|
||||
}
|
||||
|
||||
if(parent != null && parent instanceof Table){
|
||||
setCullingArea(parent.getCullingArea());
|
||||
}
|
||||
}
|
||||
|
||||
private void setJumpHeights(){
|
||||
SnapshotSeq<Element> jumpsChildren = jumps.getChildren();
|
||||
processedJumps.clear();
|
||||
reprBefore.clear();
|
||||
reprAfter.clear();
|
||||
jumpsChildren.each(e -> {
|
||||
if(!(e instanceof JumpCurve e2)) return;
|
||||
e2.prepareHeight();
|
||||
if(e2.jumpUIBegin == invalidJump) return;
|
||||
if(e2.flipped){
|
||||
JumpCurve prev = reprAfter.get(e2.jumpUIBegin);
|
||||
if(prev != null && prev.jumpUIEnd >= e2.jumpUIEnd) return;
|
||||
reprAfter.put(e2.jumpUIBegin, e2);
|
||||
}else{
|
||||
JumpCurve prev = reprBefore.get(e2.jumpUIEnd);
|
||||
if(prev != null && prev.jumpUIBegin <= e2.jumpUIBegin) return;
|
||||
reprBefore.put(e2.jumpUIEnd, e2);
|
||||
}
|
||||
});
|
||||
processedJumps.add(reprBefore.values().toArray());
|
||||
processedJumps.add(reprAfter.values().toArray());
|
||||
processedJumps.sort((a, b) -> a.jumpUIBegin - b.jumpUIBegin);
|
||||
|
||||
Seq<JumpCurve> occupiers = tmpOccupiers1;
|
||||
Bits occupied = tmpBits1;
|
||||
occupiers.clear();
|
||||
occupied.clear();
|
||||
for(int i = 0; i < processedJumps.size; i++){
|
||||
JumpCurve cur = processedJumps.get(i);
|
||||
occupiers.retainAll(e -> {
|
||||
if(e.jumpUIEnd > cur.jumpUIBegin) return true;
|
||||
occupied.clear(e.predHeight);
|
||||
return false;
|
||||
});
|
||||
int h = getJumpHeight(i, occupiers, occupied);
|
||||
occupiers.add(cur);
|
||||
occupied.set(h);
|
||||
}
|
||||
|
||||
occupiers.clear();
|
||||
|
||||
jumpsChildren.each(e -> {
|
||||
if(!(e instanceof JumpCurve e2)) return;
|
||||
if(e2.jumpUIBegin == invalidJump) return;
|
||||
e2.predHeight = e2.flipped ? reprAfter.get(e2.jumpUIBegin).predHeight : reprBefore.get(e2.jumpUIEnd).predHeight;
|
||||
e2.markedDone = true;
|
||||
});
|
||||
}
|
||||
|
||||
private int getJumpHeight(int index, Seq<JumpCurve> occupiers, Bits occupied){
|
||||
JumpCurve jmp = processedJumps.get(index);
|
||||
if(jmp.markedDone) return jmp.predHeight;
|
||||
|
||||
Seq<JumpCurve> tmpOccupiers = tmpOccupiers2;
|
||||
Bits tmpOccupied = tmpBits2;
|
||||
tmpOccupiers.set(occupiers);
|
||||
tmpOccupied.set(occupied);
|
||||
|
||||
int max = -1;
|
||||
for(int i = index + 1; i < processedJumps.size; i++){
|
||||
JumpCurve cur = processedJumps.get(i);
|
||||
if(cur.jumpUIEnd > jmp.jumpUIEnd) continue;
|
||||
tmpOccupiers.retainAll(e -> {
|
||||
if(e.jumpUIEnd > cur.jumpUIBegin) return true;
|
||||
tmpOccupied.clear(e.predHeight);
|
||||
return false;
|
||||
});
|
||||
int h = getJumpHeight(i, tmpOccupiers, tmpOccupied);
|
||||
tmpOccupiers.add(cur);
|
||||
tmpOccupied.set(h);
|
||||
max = Math.max(max, h);
|
||||
}
|
||||
|
||||
jmp.predHeight = occupied.nextClearBit(max + 1);
|
||||
jmp.markedDone = true;
|
||||
|
||||
tmpOccupiers.clear();
|
||||
|
||||
return jmp.predHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getPrefWidth(){
|
||||
return prefWidth;
|
||||
@@ -312,9 +400,10 @@ public class LCanvas extends Table{
|
||||
}
|
||||
|
||||
dragging = null;
|
||||
invalidateHierarchy();
|
||||
}
|
||||
|
||||
layout();
|
||||
updateJumpHeights = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +439,7 @@ public class LCanvas extends Table{
|
||||
t.button(Icon.cancel, Styles.logici, () -> {
|
||||
remove();
|
||||
dragging = null;
|
||||
statements.layout();
|
||||
statements.updateJumpHeights = true;
|
||||
}).size(24f);
|
||||
|
||||
t.addListener(new InputListener(){
|
||||
@@ -369,7 +458,8 @@ public class LCanvas extends Table{
|
||||
lasty = v.y;
|
||||
dragging = StatementElem.this;
|
||||
toFront();
|
||||
statements.layout();
|
||||
statements.updateJumpHeights = true;
|
||||
statements.invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -381,7 +471,7 @@ public class LCanvas extends Table{
|
||||
lastx = v.x;
|
||||
lasty = v.y;
|
||||
|
||||
statements.layout();
|
||||
statements.invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -423,9 +513,9 @@ public class LCanvas extends Table{
|
||||
StatementElem s = new StatementElem(copy);
|
||||
|
||||
statements.addChildAfter(StatementElem.this, s);
|
||||
statements.layout();
|
||||
copy.elem = s;
|
||||
copy.setupUI();
|
||||
statements.updateJumpHeights = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -444,19 +534,20 @@ public class LCanvas extends Table{
|
||||
|
||||
public static class JumpButton extends ImageButton{
|
||||
Color hoverColor = Pal.place;
|
||||
Color defaultColor = Color.white;
|
||||
Prov<StatementElem> to;
|
||||
boolean selecting;
|
||||
float mx, my;
|
||||
ClickListener listener;
|
||||
|
||||
public JumpCurve curve;
|
||||
public StatementElem elem;
|
||||
|
||||
public JumpButton(Prov<StatementElem> getter, Cons<StatementElem> setter){
|
||||
public JumpButton(Prov<StatementElem> getter, Cons<StatementElem> setter, StatementElem elem){
|
||||
super(Tex.logicNode, new ImageButtonStyle(){{
|
||||
imageUpColor = Color.white;
|
||||
}});
|
||||
|
||||
this.elem = elem;
|
||||
to = getter;
|
||||
addListener(listener = new ClickListener());
|
||||
|
||||
@@ -467,6 +558,7 @@ public class LCanvas extends Table{
|
||||
setter.get(null);
|
||||
mx = x;
|
||||
my = y;
|
||||
canvas.statements.updateJumpHeights = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -487,6 +579,7 @@ public class LCanvas extends Table{
|
||||
setter.get(null);
|
||||
}
|
||||
selecting = false;
|
||||
canvas.statements.updateJumpHeights = true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -495,7 +588,7 @@ public class LCanvas extends Table{
|
||||
setter.get(null);
|
||||
}
|
||||
|
||||
setColor(listener.isOver() ? hoverColor : defaultColor);
|
||||
setColor(listener.isOver() ? hoverColor : Color.white);
|
||||
getStyle().imageUpColor = this.color;
|
||||
});
|
||||
|
||||
@@ -509,22 +602,58 @@ public class LCanvas extends Table{
|
||||
if(stage == null){
|
||||
curve.remove();
|
||||
}else{
|
||||
canvas.jumps.addChild(curve);
|
||||
canvas.statements.jumps.addChild(curve);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class JumpCurve extends Element{
|
||||
public JumpButton button;
|
||||
private boolean invertedHeight;
|
||||
|
||||
// for jump prediction; see DragLayout
|
||||
public int predHeight = 0;
|
||||
public boolean markedDone = false;
|
||||
public int jumpUIBegin = 0, jumpUIEnd = 0;
|
||||
public boolean flipped = false;
|
||||
|
||||
private float uiHeight = 60f;
|
||||
|
||||
public JumpCurve(JumpButton button){
|
||||
this.button = button;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSize(float width, float height){
|
||||
if(height < 0){
|
||||
y += height;
|
||||
height = -height;
|
||||
invertedHeight = true;
|
||||
}
|
||||
super.setSize(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
|
||||
//MDTX(WayZer, 2024/8/6) Support Cull
|
||||
invertedHeight = false;
|
||||
Group desc = canvas.statements.jumps.parent;
|
||||
Vec2 t = Tmp.v1.set(button.getWidth() / 2f, button.getHeight() / 2f);
|
||||
button.localToAscendantCoordinates(desc, t);
|
||||
setPosition(t.x, t.y);
|
||||
Element hover = button.to.get() == null && button.selecting ? canvas.hovered : button.to.get();
|
||||
if(hover != null){
|
||||
t.set(hover.getWidth(), hover.getHeight() / 2f);
|
||||
hover.localToAscendantCoordinates(desc, t);
|
||||
setSize(t.x - x, t.y - y);
|
||||
}else if(button.selecting){
|
||||
setSize(button.mx, button.my);
|
||||
}else{
|
||||
setSize(0, 0);
|
||||
}
|
||||
|
||||
if(button.listener.isOver()){
|
||||
toFront();
|
||||
}
|
||||
@@ -532,71 +661,66 @@ public class LCanvas extends Table{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
canvas.jumpCount ++;
|
||||
|
||||
if(canvas.jumpCount > maxJumpsDrawn && !button.selecting && !button.listener.isOver()){
|
||||
return;
|
||||
}
|
||||
|
||||
Element hover = button.to.get() == null && button.selecting ? canvas.hovered : button.to.get();
|
||||
boolean draw = false;
|
||||
Vec2 t = Tmp.v1, r = Tmp.v2;
|
||||
if(height == 0) return;
|
||||
Vec2 t = Tmp.v1.set(width, !invertedHeight ? height : 0), r = Tmp.v2.set(0, !invertedHeight ? 0 : height);
|
||||
|
||||
Group desc = canvas.pane;
|
||||
localToAscendantCoordinates(desc, r);
|
||||
localToAscendantCoordinates(desc, t);
|
||||
|
||||
button.localToAscendantCoordinates(desc, r.set(0, 0));
|
||||
drawCurve(r.x, r.y, t.x, t.y);
|
||||
|
||||
if(hover != null){
|
||||
hover.localToAscendantCoordinates(desc, t.set(hover.getWidth(), hover.getHeight()/2f));
|
||||
|
||||
draw = true;
|
||||
}else if(button.selecting){
|
||||
t.set(r).add(button.mx, button.my);
|
||||
draw = true;
|
||||
}
|
||||
|
||||
float offset = canvas.pane.getVisualScrollY() - canvas.pane.getMaxY();
|
||||
|
||||
t.y += offset;
|
||||
r.y += offset;
|
||||
|
||||
if(draw){
|
||||
drawCurve(r.x + button.getWidth()/2f, r.y + button.getHeight()/2f, t.x, t.y);
|
||||
|
||||
float s = button.getWidth();
|
||||
Draw.color(button.color);
|
||||
Tex.logicNode.draw(t.x + s*0.75f, t.y - s/2f, -s, s);
|
||||
Draw.reset();
|
||||
}
|
||||
float s = button.getWidth();
|
||||
Draw.color(button.color, parentAlpha);
|
||||
Tex.logicNode.draw(t.x + s * 0.75f, t.y - s / 2f, -s, s);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public void drawCurve(float x, float y, float x2, float y2){
|
||||
Lines.stroke(4f, button.color);
|
||||
Lines.stroke(Scl.scl(4f), button.color);
|
||||
Draw.alpha(parentAlpha);
|
||||
|
||||
float dist = 100f;
|
||||
|
||||
//square jumps
|
||||
if(false){
|
||||
float len = Scl.scl(Mathf.randomSeed(hashCode(), 10, 50));
|
||||
|
||||
float maxX = Math.max(x, x2) + len;
|
||||
// exponential smoothing
|
||||
uiHeight = Mathf.lerp(
|
||||
Scl.scl(Core.graphics.isPortrait() ? 20f : 40f) + Scl.scl(Core.graphics.isPortrait() ? 8f : 10f) * (float) predHeight,
|
||||
uiHeight,
|
||||
dynamicJumpHeights ? Mathf.pow(0.9f, Time.delta) : 0
|
||||
);
|
||||
|
||||
//trapezoidal jumps
|
||||
float dy = (y2 == y ? 0f : y2 > y ? 1f : -1f) * uiHeight * 0.5f;
|
||||
//there's absolutely a better way to detect invalid trapezoids, but this probably isn't *that* slow and I don't care to fix it right now
|
||||
if(Intersector.intersectSegments(x, y, x + uiHeight, y + dy, x2, y2, x + uiHeight, y2 - dy, Tmp.v3)){
|
||||
Lines.beginLine();
|
||||
Lines.linePoint(x, y);
|
||||
Lines.linePoint(maxX, y);
|
||||
Lines.linePoint(maxX, y2);
|
||||
Lines.linePoint(Tmp.v3.x, Tmp.v3.y);
|
||||
Lines.linePoint(x2, y2);
|
||||
Lines.endLine();
|
||||
}else{
|
||||
Lines.beginLine();
|
||||
Lines.linePoint(x, y);
|
||||
Lines.linePoint(x + uiHeight, y + dy);
|
||||
Lines.linePoint(x + uiHeight, y2 - dy);
|
||||
Lines.linePoint(x2, y2);
|
||||
Lines.endLine();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Lines.curve(
|
||||
x, y,
|
||||
x + dist, y,
|
||||
x2 + dist, y2,
|
||||
x2, y2,
|
||||
Math.max(18, (int)(Mathf.dst(x, y, x2, y2) / 6)));
|
||||
public void prepareHeight(){
|
||||
if(this.button.to.get() == null){
|
||||
this.markedDone = true;
|
||||
this.predHeight = 0;
|
||||
this.flipped = false;
|
||||
this.jumpUIBegin = this.jumpUIEnd = invalidJump;
|
||||
}else{
|
||||
this.markedDone = false;
|
||||
int i = this.button.elem.index;
|
||||
int j = this.button.to.get().index;
|
||||
this.flipped = i >= j;
|
||||
this.jumpUIBegin = Math.min(i,j);
|
||||
this.jumpUIEnd = Math.max(i,j);
|
||||
// height will be recalculated later
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ public class LExecutor{
|
||||
|
||||
//yes, this is a minor memory leak, but it's probably not significant enough to matter
|
||||
protected static IntFloatMap unitTimeouts = new IntFloatMap();
|
||||
//lookup variable by name, lazy init.
|
||||
protected @Nullable ObjectIntMap<String> nameMap;
|
||||
|
||||
static{
|
||||
Events.on(ResetEvent.class, e -> unitTimeouts.clear());
|
||||
@@ -92,6 +94,7 @@ public class LExecutor{
|
||||
|
||||
/** Loads with a specified assembler. Resets all variables. */
|
||||
public void load(LAssembler builder){
|
||||
nameMap = null;
|
||||
vars = builder.vars.values().toSeq().retainAll(var -> !var.constant).toArray(LVar.class);
|
||||
for(int i = 0; i < vars.length; i++){
|
||||
vars[i].id = i;
|
||||
@@ -106,6 +109,17 @@ public class LExecutor{
|
||||
|
||||
//region utility
|
||||
|
||||
|
||||
public @Nullable LVar optionalVar(String name){
|
||||
if(nameMap == null){
|
||||
nameMap = new ObjectIntMap<>();
|
||||
for(int i = 0; i < vars.length; i++){
|
||||
nameMap.put(vars[i].name, i);
|
||||
}
|
||||
}
|
||||
return optionalVar(nameMap.get(name, -1));
|
||||
}
|
||||
|
||||
/** @return a Var from this processor. May be null if out of bounds. */
|
||||
public @Nullable LVar optionalVar(int index){
|
||||
return index < 0 || index >= vars.length ? null : vars[index];
|
||||
@@ -557,6 +571,15 @@ public class LExecutor{
|
||||
|
||||
if(from instanceof MemoryBuild mem && (exec.privileged || (from.team == exec.team && !mem.block.privileged))){
|
||||
output.setnum(address < 0 || address >= mem.memory.length ? 0 : mem.memory[address]);
|
||||
}else if(from instanceof LogicBuild logic && (exec.privileged || (from.team == exec.team && !from.block.privileged)) && position.isobj && position.objval instanceof String name){
|
||||
LVar fromVar = logic.executor.optionalVar(name);
|
||||
if(fromVar != null && !output.constant){
|
||||
output.objval = fromVar.objval;
|
||||
output.numval = fromVar.numval;
|
||||
output.isobj = fromVar.isobj;
|
||||
}
|
||||
}else if(target.isobj && target.objval instanceof CharSequence str){
|
||||
output.setnum(address < 0 || address >= str.length() ? Double.NaN : (int)str.charAt(address));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -580,6 +603,13 @@ public class LExecutor{
|
||||
|
||||
if(from instanceof MemoryBuild mem && (exec.privileged || (from.team == exec.team && !mem.block.privileged)) && address >= 0 && address < mem.memory.length){
|
||||
mem.memory[address] = value.num();
|
||||
}else if(from instanceof LogicBuild logic && (exec.privileged || (from.team == exec.team && !from.block.privileged)) && position.isobj && position.objval instanceof String name){
|
||||
LVar toVar = logic.executor.optionalVar(name);
|
||||
if(toVar != null && !toVar.constant){
|
||||
toVar.objval = value.objval;
|
||||
toVar.numval = value.numval;
|
||||
toVar.isobj = value.isobj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -622,6 +652,10 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if(target instanceof CharSequence seq && sense == LAccess.size){
|
||||
to.setnum(seq.length());
|
||||
return;
|
||||
}
|
||||
to.setobj(null);
|
||||
}
|
||||
}
|
||||
@@ -984,6 +1018,29 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
public static class PrintCharI implements LInstruction{
|
||||
public LVar value;
|
||||
|
||||
public PrintCharI(LVar value){
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
PrintCharI(){}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
|
||||
if(exec.textBuffer.length() >= maxTextBuffer) return;
|
||||
if(value.isobj){
|
||||
if(!(value.objval instanceof UnlockableContent cont)) return;
|
||||
exec.textBuffer.append((char)cont.emojiChar());
|
||||
return;
|
||||
}
|
||||
|
||||
exec.textBuffer.append((char)Math.floor(value.numval));
|
||||
}
|
||||
}
|
||||
|
||||
public static class FormatI implements LInstruction{
|
||||
public LVar value;
|
||||
|
||||
@@ -1376,10 +1433,10 @@ public class LExecutor{
|
||||
|
||||
Team t = team.team();
|
||||
|
||||
if(type.obj() instanceof UnitType type && !type.internal && !type.hidden && t != null && Units.canCreate(t, type)){
|
||||
if(t != null && type.obj() instanceof UnitType type && !type.internal && Units.canCreate(t, type)){
|
||||
//random offset to prevent stacking
|
||||
var unit = type.spawn(t, World.unconv(x.numf()) + Mathf.range(0.01f), World.unconv(y.numf()) + Mathf.range(0.01f));
|
||||
spawner.spawnEffect(unit, rotation.numf());
|
||||
spawner.spawnEffect(unit);
|
||||
result.setobj(unit);
|
||||
}
|
||||
}
|
||||
@@ -1485,6 +1542,7 @@ public class LExecutor{
|
||||
case dropZoneRadius -> state.rules.dropZoneRadius = value.numf() * 8f;
|
||||
case unitCap -> state.rules.unitCap = Math.max(value.numi(), 0);
|
||||
case lighting -> state.rules.lighting = value.bool();
|
||||
case canGameOver -> state.rules.canGameOver = value.bool();
|
||||
case mapArea -> {
|
||||
int x = p1.numi(), y = p2.numi(), w = p3.numi(), h = p4.numi();
|
||||
if(!checkMapArea(x, y, w, h, false)){
|
||||
@@ -1511,7 +1569,7 @@ public class LExecutor{
|
||||
state.rules.bannedUnits.remove(u);
|
||||
}
|
||||
}
|
||||
case unitHealth, unitBuildSpeed, unitCost, unitDamage, blockHealth, blockDamage, buildSpeed, rtsMinSquad, rtsMinWeight -> {
|
||||
case unitHealth, unitBuildSpeed, unitMineSpeed, unitCost, unitDamage, blockHealth, blockDamage, buildSpeed, rtsMinSquad, rtsMinWeight -> {
|
||||
Team team = p1.team();
|
||||
if(team != null){
|
||||
float num = value.numf();
|
||||
@@ -1519,6 +1577,7 @@ public class LExecutor{
|
||||
case buildSpeed -> team.rules().buildSpeedMultiplier = Mathf.clamp(num, 0.001f, 50f);
|
||||
case unitHealth -> team.rules().unitHealthMultiplier = Math.max(num, 0.001f);
|
||||
case unitBuildSpeed -> team.rules().unitBuildSpeedMultiplier = Mathf.clamp(num, 0f, 50f);
|
||||
case unitMineSpeed -> team.rules().unitMineSpeedMultiplier = Math.max(num, 0f);
|
||||
case unitCost -> team.rules().unitCostMultiplier = Math.max(num, 0f);
|
||||
case unitDamage -> team.rules().unitDamageMultiplier = Math.max(num, 0f);
|
||||
case blockHealth -> team.rules().blockHealthMultiplier = Math.max(num, 0.001f);
|
||||
@@ -1546,11 +1605,12 @@ public class LExecutor{
|
||||
}else if(full){
|
||||
//disable the rule, covers the whole map
|
||||
if(set){
|
||||
int prevX = state.rules.limitX, prevY = state.rules.limitY, prevW = state.rules.limitWidth, prevH = state.rules.limitHeight;
|
||||
state.rules.limitMapArea = false;
|
||||
if(!headless){
|
||||
renderer.updateAllDarkness();
|
||||
}
|
||||
world.checkMapArea();
|
||||
world.checkMapArea(prevX, prevY, prevW, prevH);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1559,12 +1619,20 @@ public class LExecutor{
|
||||
}
|
||||
|
||||
if(set){
|
||||
int prevX = state.rules.limitX, prevY = state.rules.limitY, prevW = state.rules.limitWidth, prevH = state.rules.limitHeight;
|
||||
if(!state.rules.limitMapArea){
|
||||
//it was never on in the first place, so the old bounds don't apply
|
||||
prevW = 0;
|
||||
prevH = 0;
|
||||
prevX = -1;
|
||||
prevY = -1;
|
||||
}
|
||||
state.rules.limitMapArea = true;
|
||||
state.rules.limitX = x;
|
||||
state.rules.limitY = y;
|
||||
state.rules.limitWidth = w;
|
||||
state.rules.limitHeight = h;
|
||||
world.checkMapArea();
|
||||
world.checkMapArea(prevX, prevY, prevW, prevH);
|
||||
|
||||
if(!headless){
|
||||
renderer.updateAllDarkness();
|
||||
@@ -1876,9 +1944,7 @@ public class LExecutor{
|
||||
for(int i = 0; i < spawned; i++){
|
||||
Tmp.v1.rnd(spread);
|
||||
|
||||
Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1);
|
||||
unit.set(spawnX + Tmp.v1.x, spawnY + Tmp.v1.y);
|
||||
Vars.spawner.spawnEffect(unit);
|
||||
spawner.spawnUnit(group, spawnX + Tmp.v1.x, spawnY + Tmp.v1.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,13 @@ public class LParser{
|
||||
}else{
|
||||
//attempt parsing using custom parser if a match is found; this is for mods
|
||||
if(LAssembler.customParsers.containsKey(tokens[0])){
|
||||
statements.add(LAssembler.customParsers.get(tokens[0]).get(tokens));
|
||||
var parsed = LAssembler.customParsers.get(tokens[0]).get(tokens);
|
||||
|
||||
if(!privileged && parsed != null && parsed.privileged()){
|
||||
statements.add(new InvalidStatement());
|
||||
}else{
|
||||
statements.add(parsed);
|
||||
}
|
||||
}else{
|
||||
//unparseable statement
|
||||
statements.add(new InvalidStatement());
|
||||
|
||||
@@ -19,6 +19,7 @@ import mindustry.logic.LExecutor.*;
|
||||
import mindustry.logic.LogicFx.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -312,6 +313,46 @@ public class LStatements{
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("printchar")
|
||||
public static class PrintCharStatement extends LStatement{
|
||||
public String value = "65";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
table.add(" char ");
|
||||
TextField field = field(table, value, str -> value = str).get();
|
||||
table.button(b -> {
|
||||
b.image(Icon.pencilSmall);
|
||||
b.clicked(() -> showSelectTable(b, (t, hide) -> {
|
||||
t.row();
|
||||
t.table(i -> {
|
||||
i.left();
|
||||
int c = 0;
|
||||
for(char j = 32; j < 127; j++){
|
||||
final int chr = j;
|
||||
i.button(String.valueOf(j), Styles.flatt, () -> {
|
||||
value = Integer.toString(chr);
|
||||
field.setText(value);
|
||||
hide.run();
|
||||
}).size(32f);
|
||||
if(++c % 8 == 0) i.row();
|
||||
}
|
||||
});
|
||||
}));
|
||||
}, Styles.logict, () -> {}).size(40f).padLeft(-2).color(table.color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new PrintCharI(builder.var(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.io;
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("format")
|
||||
public static class FormatStatement extends LStatement{
|
||||
public String value = "\"frog\"";
|
||||
@@ -574,6 +615,29 @@ public class LStatements{
|
||||
if(++c % 6 == 0) i.row();
|
||||
}
|
||||
}),
|
||||
new Table(i -> {
|
||||
i.left();
|
||||
int c = 0;
|
||||
for(UnitType item : Vars.content.units()){
|
||||
if(!item.unlockedNow() || item.hidden) continue;
|
||||
i.button(new TextureRegionDrawable(item.uiIcon), Styles.flati, iconSmall, () -> {
|
||||
stype("@" + item.name);
|
||||
hide.run();
|
||||
}).size(40f);
|
||||
|
||||
if(++c % 6 == 0) i.row();
|
||||
}
|
||||
|
||||
for(Block item : Vars.content.blocks()){
|
||||
if(!item.unlockedNow() || item.isHidden()) continue;
|
||||
i.button(new TextureRegionDrawable(item.uiIcon), Styles.flati, iconSmall, () -> {
|
||||
stype("@" + item.name);
|
||||
hide.run();
|
||||
}).size(40f);
|
||||
|
||||
if(++c % 6 == 0) i.row();
|
||||
}
|
||||
}),
|
||||
//sensors
|
||||
new Table(i -> {
|
||||
for(LAccess sensor : LAccess.senseable){
|
||||
@@ -585,7 +649,7 @@ public class LStatements{
|
||||
})
|
||||
};
|
||||
|
||||
Drawable[] icons = {Icon.box, Icon.liquid, Icon.tree};
|
||||
Drawable[] icons = {Icon.box, Icon.liquid, Icon.units, Icon.tree};
|
||||
Stack stack = new Stack(tables[selected]);
|
||||
ButtonGroup<Button> group = new ButtonGroup<>();
|
||||
|
||||
@@ -603,7 +667,7 @@ public class LStatements{
|
||||
}).height(50f).growX().checked(selected == fi).group(group);
|
||||
}
|
||||
t.row();
|
||||
t.add(stack).colspan(3).width(240f).left();
|
||||
t.add(stack).colspan(4).width(240f).left();
|
||||
}));
|
||||
}, Styles.logict, () -> {}).size(40f).padLeft(-1).color(table.color);
|
||||
|
||||
@@ -870,7 +934,7 @@ public class LStatements{
|
||||
table.table(this::rebuild);
|
||||
|
||||
table.add().growX();
|
||||
table.add(new JumpButton(() -> dest, s -> dest = s)).size(30).right().padLeft(-8);
|
||||
table.add(new JumpButton(() -> dest, s -> dest = s, this.elem)).size(30).right().padLeft(-8);
|
||||
|
||||
String name = name();
|
||||
|
||||
@@ -1493,11 +1557,11 @@ public class LStatements{
|
||||
table.add("natural ");
|
||||
fields(table, natural, str -> natural = str);
|
||||
|
||||
table.add("x ").visible(() -> natural.equals("false"));
|
||||
fields(table, x, str -> x = str).visible(() -> natural.equals("false"));
|
||||
table.add("x ").visible(() -> !natural.equals("true"));
|
||||
fields(table, x, str -> x = str).visible(() -> !natural.equals("true"));
|
||||
|
||||
table.add(" y ").visible(() -> natural.equals("false"));
|
||||
fields(table, y, str -> y = str).visible(() -> natural.equals("false"));
|
||||
table.add(" y ").visible(() -> !natural.equals("true"));
|
||||
fields(table, y, str -> y = str).visible(() -> !natural.equals("true"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1547,7 +1611,7 @@ public class LStatements{
|
||||
fields(table, "w", p3, s -> p3 = s);
|
||||
fields(table, "h", p4, s -> p4 = s);
|
||||
}
|
||||
case buildSpeed, unitHealth, unitBuildSpeed, unitCost, unitDamage, blockHealth, blockDamage, rtsMinSquad, rtsMinWeight -> {
|
||||
case buildSpeed, unitHealth, unitBuildSpeed, unitMineSpeed, unitCost, unitDamage, blockHealth, blockDamage, rtsMinSquad, rtsMinWeight -> {
|
||||
if(p1.equals("0")){
|
||||
p1 = "@sharded";
|
||||
}
|
||||
|
||||
@@ -153,20 +153,27 @@ public class LogicDialog extends BaseDialog{
|
||||
if(Core.graphics.isPortrait()) buttons.row();
|
||||
|
||||
buttons.button("@variables", Icon.menu, () -> {
|
||||
//in the editor, it should display the global variables only (the button text is different)
|
||||
if(!shouldShowVariables()){
|
||||
globalsDialog.show();
|
||||
return;
|
||||
}
|
||||
|
||||
BaseDialog dialog = new BaseDialog("@variables");
|
||||
dialog.hidden(() -> {
|
||||
if(!wasPaused && !net.active()){
|
||||
if(!wasPaused && !net.active() && !state.isMenu()){
|
||||
state.set(State.paused);
|
||||
}
|
||||
});
|
||||
|
||||
dialog.shown(() -> {
|
||||
if(!wasPaused && !net.active()){
|
||||
if(!wasPaused && !net.active() && !state.isMenu()){
|
||||
state.set(State.playing);
|
||||
}
|
||||
});
|
||||
|
||||
dialog.cont.pane(p -> {
|
||||
|
||||
p.margin(10f).marginRight(16f);
|
||||
p.table(Tex.button, t -> {
|
||||
t.defaults().fillX().height(45f);
|
||||
@@ -220,11 +227,23 @@ public class LogicDialog extends BaseDialog{
|
||||
dialog.buttons.button("@logic.globals", Icon.list, () -> globalsDialog.show()).size(210f, 64f);
|
||||
|
||||
dialog.show();
|
||||
}).name("variables").disabled(b -> executor == null || executor.vars.length == 0);
|
||||
}).name("variables").update(b -> {
|
||||
if(shouldShowVariables()){
|
||||
b.setText("@variables");
|
||||
}else{
|
||||
b.setText("@logic.globals");
|
||||
}
|
||||
});
|
||||
|
||||
buttons.button("@add", Icon.add, () -> {
|
||||
showAddDialog();
|
||||
}).disabled(t -> canvas.statements.getChildren().size >= LExecutor.maxInstructions);
|
||||
|
||||
Core.app.post(canvas::rebuild);
|
||||
}
|
||||
|
||||
public boolean shouldShowVariables(){
|
||||
return executor != null && executor.vars.length > 0 && !state.isMenu();
|
||||
}
|
||||
|
||||
public void showAddDialog(){
|
||||
|
||||
@@ -13,6 +13,7 @@ public enum LogicRule{
|
||||
unitCap,
|
||||
mapArea,
|
||||
lighting,
|
||||
canGameOver,
|
||||
ambientLight,
|
||||
solarMultiplier,
|
||||
dragMultiplier,
|
||||
@@ -23,6 +24,7 @@ public enum LogicRule{
|
||||
buildSpeed,
|
||||
unitHealth,
|
||||
unitBuildSpeed,
|
||||
unitMineSpeed,
|
||||
unitCost,
|
||||
unitDamage,
|
||||
blockHealth,
|
||||
|
||||
Reference in New Issue
Block a user