Merge branch 'master' into pr-readwrite

This commit is contained in:
Anuken
2025-02-09 00:38:18 -05:00
committed by GitHub
432 changed files with 20155 additions and 10789 deletions

View File

@@ -1,6 +1,7 @@
package mindustry.logic;
import arc.*;
import arc.audio.*;
import arc.files.*;
import arc.graphics.*;
import arc.math.*;
@@ -8,6 +9,7 @@ import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.game.*;
import mindustry.type.*;
import mindustry.world.*;
@@ -20,12 +22,13 @@ 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();
//non-constants that depend on state
private static LVar varTime, varTick, varSecond, varMinute, varWave, varWaveTime, varMapW, varMapH, varServer, varClient, varClientLocale, varClientUnit, varClientName, varClientTeam, varClientMobile;
private static LVar varTime, varTick, varSecond, varMinute, varWave, varWaveTime, varMapW, varMapH, varWait, varServer, varClient, varClientLocale, varClientUnit, varClientName, varClientTeam, varClientMobile;
private ObjectMap<String, LVar> vars = new ObjectMap<>();
private Seq<VarEntry> varEntries = new Seq<>();
@@ -33,6 +36,8 @@ public class GlobalVars{
private UnlockableContent[][] logicIdToContent;
private int[][] contentIdToLogicId;
public static final Seq<String> soundNames = new Seq<>();
public void init(){
putEntryOnly("sectionProcessor");
@@ -69,6 +74,7 @@ public class GlobalVars{
varMapW = putEntry("@mapw", 0);
varMapH = putEntry("@maph", 0);
varWait = putEntry("@wait", null);
putEntryOnly("sectionNetwork");
@@ -87,6 +93,17 @@ public class GlobalVars{
put("@ctrlPlayer", ctrlPlayer);
put("@ctrlCommand", ctrlCommand);
//sounds
if(Core.assets != null){
for(Sound sound : Core.assets.getAll(Sound.class, new Seq<>(Sound.class))){
if(sound != Sounds.none && sound != Sounds.swish && sound.file != null){
String name = sound.file.nameWithoutExtension();
soundNames.add(name);
put("@sfx-" + name, Sounds.getSoundId(sound));
}
}
}
//store base content
for(Team team : Team.baseTeams){
@@ -116,7 +133,9 @@ public class GlobalVars{
}
for(UnitType type : Vars.content.units()){
put("@" + type.name, type);
if(!type.internal){
put("@" + type.name, type);
}
}
for(Weather weather : Vars.content.weathers()){
@@ -137,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];
@@ -185,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();
@@ -194,12 +213,21 @@ public class GlobalVars{
}
}
public LVar waitVar(){
return varWait;
}
public Seq<VarEntry> getEntries(){
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;
}

View File

@@ -55,6 +55,8 @@ public enum LAccess{
name,
payloadCount,
payloadType,
totalPayload,
payloadCapacity,
id,
//values with parameters are considered controllable

View File

@@ -10,7 +10,6 @@ import mindustry.logic.LExecutor.*;
/** "Compiles" a sequence of statements into instructions. */
public class LAssembler{
public static ObjectMap<String, Func<String[], LStatement>> customParsers = new ObjectMap<>();
public static final int maxTokenLength = 36;
private static final int invalidNum = Integer.MIN_VALUE;
@@ -22,7 +21,7 @@ public class LAssembler{
public LAssembler(){
//instruction counter
putVar("@counter");
putVar("@counter").isobj = false;
//currently controlled unit
putConst("@unit", null);
//reference to self
@@ -34,8 +33,9 @@ public class LAssembler{
Seq<LStatement> st = read(data, privileged);
asm.instructions = st.map(l -> l.build(asm)).retainAll(l -> l != null).toArray(LInstruction.class);
asm.privileged = privileged;
asm.instructions = st.map(l -> l.build(asm)).retainAll(l -> l != null).toArray(LInstruction.class);
return asm;
}
@@ -59,7 +59,7 @@ public class LAssembler{
/** @return a variable by name.
* This may be a constant variable referring to a number or object. */
public LVar var(String symbol){
LVar constVar = Vars.logicVars.get(symbol);
LVar constVar = Vars.logicVars.get(symbol, privileged);
if(constVar != null) return constVar;
symbol = symbol.trim();
@@ -121,7 +121,9 @@ public class LAssembler{
if(vars.containsKey(name)){
return vars.get(name);
}else{
//variables are null objects by default
LVar var = new LVar(name);
var.isobj = true;
vars.put(name, var);
return var;
}

View File

@@ -116,14 +116,7 @@ public class LCanvas extends Table{
jumps.cullable = false;
}).grow().get();
pane.setFlickScroll(false);
pane.setScrollYForce(s);
pane.updateVisualScroll();
//load old scroll percent
Core.app.post(() -> {
pane.setScrollYForce(s);
pane.updateVisualScroll();
});
if(toLoad != null){
load(toLoad);
@@ -171,7 +164,7 @@ public class LCanvas extends Table{
}
StatementElem checkHovered(){
Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true);
Element e = Core.scene.getHoverElement();
if(e != null){
while(e != null && !(e instanceof StatementElem)){
e = e.parent;
@@ -257,7 +250,7 @@ public class LCanvas extends Table{
}
}
invalidateHierarchy();
if(parent != null) parent.invalidateHierarchy();
if(parent != null && parent instanceof Table){
setCullingArea(parent.getCullingArea());

View File

@@ -1,6 +1,7 @@
package mindustry.logic;
import arc.*;
import arc.audio.*;
import arc.graphics.*;
import arc.math.*;
import arc.math.geom.*;
@@ -41,13 +42,12 @@ public class LExecutor{
maxDisplayBuffer = 1024,
maxTextBuffer = 400;
public LInstruction[] instructions = {};
/** Non-constant variables used for network sync */
public LVar[] vars = {};
public LVar counter, unit, thisv, ipt;
public int[] binds;
public boolean yield;
@@ -569,7 +569,7 @@ public class LExecutor{
int address = position.numi();
Building from = target.building();
if(from instanceof MemoryBuild mem && (exec.privileged || from.team == exec.team)){
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) && position.isobj && position.objval instanceof String name){
LVar fromVar = logic.executor.optionalVar(name);
@@ -693,7 +693,7 @@ public class LExecutor{
LogicAI ai = null;
if(base instanceof Ranged r && (exec.privileged || r.team() == exec.team) &&
(base instanceof Building || (ai = UnitControlI.checkLogicAI(exec, base)) != null)){ //must be a building or a controllable unit
((base instanceof Building b && (!b.block.privileged || exec.privileged)) || (ai = UnitControlI.checkLogicAI(exec, base)) != null)){ //must be a building or a controllable unit
float range = r.range();
Healthc targeted;
@@ -997,8 +997,8 @@ public class LExecutor{
exec.textBuffer.append(strValue);
}else{
//display integer version when possible
if(Math.abs(value.numval - (long)value.numval) < 0.00001){
exec.textBuffer.append((long)value.numval);
if(Math.abs(value.numval - Math.round(value.numval)) < 0.00001){
exec.textBuffer.append(Math.round(value.numval));
}else{
exec.textBuffer.append(value.numval);
}
@@ -1009,7 +1009,6 @@ public class LExecutor{
return
obj == null ? "null" :
obj instanceof String s ? s :
obj == Blocks.stoneWall ? "solid" : //special alias
obj instanceof MappableContent content ? content.name :
obj instanceof Content ? "[content]" :
obj instanceof Building build ? build.block.name :
@@ -1020,6 +1019,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;
@@ -1059,8 +1081,8 @@ public class LExecutor{
exec.textBuffer.replace(placeholderIndex, placeholderIndex + 3, strValue);
}else{
//display integer version when possible
if(Math.abs(value.numval - (long)value.numval) < 0.00001){
exec.textBuffer.replace(placeholderIndex, placeholderIndex + 3, (long)value.numval + "");
if(Math.abs(value.numval - Math.round(value.numval)) < 0.00001){
exec.textBuffer.replace(placeholderIndex, placeholderIndex + 3, Math.round(value.numval) + "");
}else{
exec.textBuffer.replace(placeholderIndex, placeholderIndex + 3, value.numval + "");
}
@@ -1081,7 +1103,7 @@ public class LExecutor{
@Override
public void run(LExecutor exec){
if(target.building() instanceof MessageBuild d && (d.team == exec.team || exec.privileged)){
if(target.building() instanceof MessageBuild d && (exec.privileged || (d.team == exec.team && !d.block.privileged))){
d.message.setLength(0);
d.message.append(exec.textBuffer, 0, Math.min(exec.textBuffer.length(), maxTextBuffer));
@@ -1273,7 +1295,8 @@ public class LExecutor{
result.setobj(units == null || i < 0 || i >= units.size ? null : units.get(i));
}
}
case player -> result.setobj(i < 0 || i >= data.players.size ? null : data.players.get(i).unit());
case player -> result.setobj(i < 0 || i >= data.players.size ? null :
data.players.get(i).unit() instanceof BlockUnitc block ? block.tile() : data.players.get(i).unit());
case core -> result.setobj(i < 0 || i >= data.cores.size ? null : data.cores.get(i));
case build -> {
Block block = extra.obj() instanceof Block b ? b : null;
@@ -1326,7 +1349,7 @@ public class LExecutor{
@Override
public void run(LExecutor exec){
Tile tile = world.tile(x.numi(), y.numi());
Tile tile = world.tile(Mathf.round(x.numf()), Mathf.round(y.numf()));
if(tile == null){
dest.setobj(null);
}else{
@@ -1380,7 +1403,7 @@ public class LExecutor{
if(t == null) t = Team.derelict;
if(tile.block() != b || tile.team() != t){
tile.setBlock(b, t, Mathf.clamp(rotation.numi(), 0, 3));
tile.setNet(b, t, Mathf.clamp(rotation.numi(), 0, 3));
}
}
}
@@ -1411,7 +1434,7 @@ public class LExecutor{
Team t = team.team();
if(type.obj() instanceof UnitType type && !type.hidden && t != null && Units.canCreate(t, type)){
if(type.obj() instanceof UnitType type && !type.internal && !type.hidden && t != null && 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());
@@ -1520,6 +1543,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)){
@@ -1546,7 +1570,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();
@@ -1554,6 +1578,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);
@@ -1631,7 +1656,7 @@ public class LExecutor{
public void run(LExecutor exec){
//set default to success
outSuccess.setnum(1);
if(headless && type != MessageType.mission) {
if(headless && type != MessageType.mission){
exec.textBuffer.setLength(0);
return;
}
@@ -1641,8 +1666,14 @@ public class LExecutor{
type == MessageType.notify && ui.hudfrag.hasToast() ||
type == MessageType.toast && ui.hasAnnouncement()
){
//set outSuccess=false to let user retry.
outSuccess.setnum(0);
//backwards compatibility; if it is @wait, block execution
if(outSuccess == logicVars.waitVar()){
exec.counter.numval--;
exec.yield = true;
}else{
//set outSuccess=false to let user retry.
outSuccess.setnum(0);
}
return;
}
@@ -1727,7 +1758,7 @@ public class LExecutor{
public static void logicExplosion(Team team, float x, float y, float radius, float damage, boolean air, boolean ground, boolean pierce, boolean effect){
if(damage < 0f) return;
Damage.damage(team, x, y, radius, damage, pierce, air, ground);
Damage.damage(team, x, y, radius, damage, pierce, air, ground, true, null);
if(effect){
if(pierce){
Fx.spawnShockwave.at(x, y, World.conv(radius));
@@ -1942,6 +1973,37 @@ public class LExecutor{
}
}
public static class PlaySoundI implements LInstruction{
public boolean positional;
public LVar id, volume, pitch, pan, x, y, limit;
public PlaySoundI(){
}
public PlaySoundI(boolean positional, LVar id, LVar volume, LVar pitch, LVar pan, LVar x, LVar y, LVar limit){
this.positional = positional;
this.id = id;
this.volume = volume;
this.pitch = pitch;
this.pan = pan;
this.x = x;
this.y = y;
this.limit = limit;
}
@Override
public void run(LExecutor exec){
Sound sound = Sounds.getSound(id.numi());
if(sound == null || sound == Sounds.swish) sound = Sounds.none; //no.
if(positional){
sound.at(World.unconv(x.numf()), World.unconv(y.numf()), pitch.numf(), Math.min(volume.numf(), 2f), limit.bool());
}else{
sound.play(Math.min(volume.numf() * (Core.settings.getInt("sfxvol") / 100f), 2f), pitch.numf(), pan.numf(), false, limit.bool());
}
}
}
public static class SetMarkerI implements LInstruction{
public LMarkerControl type = LMarkerControl.pos;
public LVar id, p1, p2, p3;

View File

@@ -103,7 +103,7 @@ public abstract class LStatement{
protected Cell<TextField> field(Table table, String value, Cons<String> setter){
return table.field(value, Styles.nodeField, s -> setter.get(sanitize(s)))
.size(144f, 40f).pad(2f).color(table.color).maxTextLength(LAssembler.maxTokenLength);
.size(144f, 40f).pad(2f).color(table.color);
}
protected Cell<TextField> fields(Table table, String desc, String value, Cons<String> setter){

View File

@@ -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);
@@ -1324,15 +1388,11 @@ public class LStatements{
@Override
public void build(Table table){
rebuild(table);
}
void rebuild(Table table){
table.clearChildren();
table.button(clear ? "clear" : "apply", Styles.logict, () -> {
clear = !clear;
rebuild(table);
build(table);
}).size(80f, 40f).pad(4f).color(table.color);
if(statusNames == null){
@@ -1343,11 +1403,10 @@ public class LStatements{
b.label(() -> effect).grow().wrap().labelAlign(Align.center).center();
b.clicked(() -> showSelect(b, statusNames, effect, o -> {
effect = o;
build(table);
}, 2, c -> c.size(120f, 38f)));
}, Styles.logict, () -> {}).size(120f, 40f).pad(4f).color(table.color);
//TODO effect select
table.add(clear ? " from " : " to ");
row(table);
@@ -1498,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
@@ -1552,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";
}
@@ -1594,7 +1653,7 @@ public class LStatements{
@RegisterStatement("message")
public static class FlushMessageStatement extends LStatement{
public MessageType type = MessageType.announce;
public String duration = "3", outSuccess = "success";
public String duration = "3", outSuccess = "@wait";
@Override
public void build(Table table){
@@ -1616,9 +1675,11 @@ public class LStatements{
case announce, toast -> {
table.add(" for ");
fields(table, duration, str -> duration = str);
table.add(" secs ");
table.add(" sec ");
}
}
row(table);
table.add(" success ");
fields(table, outSuccess, str -> outSuccess = str);
}
@@ -2122,6 +2183,74 @@ public class LStatements{
}
}
@RegisterStatement("playsound")
public static class PlaySoundStatement extends LStatement{
public boolean positional;
public String id = "@sfx-pew", volume = "1", pitch = "1", pan = "0", x = "@thisx", y = "@thisy", limit = "true";
@Override
public void build(Table table){
rebuild(table);
}
void rebuild(Table table){
table.clearChildren();
table.button(positional ? "positional" : "global", Styles.logict, () -> {
positional = !positional;
rebuild(table);
}).size(160f, 40f).pad(4f).color(table.color);
row(table);
field(table, id, str -> id = str).padRight(0f).get();
table.button(b -> {
b.image(Icon.pencilSmall);
String soundName = id.startsWith("@sfx-") ? id.substring(5) : id;
b.clicked(() -> showSelect(b, GlobalVars.soundNames.toArray(String.class), soundName, t -> {
id = "@sfx-" + t;
rebuild(table);
}, 2, cell -> cell.size(160, 50)));
}, Styles.logict, () -> {}).size(40).color(table.color).left().padLeft(-1);
row(table);
fieldst(table, "volume", volume, str -> volume = str);
fieldst(table, "pitch", pitch, str -> pitch = str);
table.row();
if(positional){
fieldst(table, "x", x, str -> x = str);
fieldst(table, "y", y, str -> y = str);
}else{
fieldst(table, "pan", pan, str -> pan = str);
}
table.row();
fieldst(table, "limit", limit, str -> limit = str);
}
@Override
public boolean privileged(){
return true;
}
@Override
public LInstruction build(LAssembler builder){
return new PlaySoundI(positional, builder.var(id), builder.var(volume), builder.var(pitch), builder.var(pan), builder.var(x), builder.var(y), builder.var(limit));
}
@Override
public LCategory category(){
return LCategory.world;
}
}
@RegisterStatement("setmarker")
public static class SetMarkerStatement extends LStatement{
public LMarkerControl type = LMarkerControl.pos;

View File

@@ -3,6 +3,7 @@ package mindustry.logic;
import arc.*;
import arc.func.*;
import arc.graphics.*;
import arc.input.*;
import arc.scene.actions.*;
import arc.scene.ui.*;
import arc.scene.ui.TextButton.*;
@@ -19,6 +20,8 @@ import mindustry.ui.*;
import mindustry.ui.dialogs.*;
import mindustry.world.blocks.logic.*;
import java.util.*;
import static mindustry.Vars.*;
import static mindustry.logic.LCanvas.*;
@@ -28,6 +31,7 @@ public class LogicDialog extends BaseDialog{
boolean privileged;
@Nullable LExecutor executor;
GlobalVarsDialog globalsDialog = new GlobalVarsDialog();
boolean wasRows, wasPortrait;
public LogicDialog(){
super("logic");
@@ -40,10 +44,25 @@ public class LogicDialog extends BaseDialog{
addCloseListener();
shown(this::setup);
shown(() -> {
wasRows = LCanvas.useRows();
wasPortrait = Core.graphics.isPortrait();
});
hidden(() -> consumer.get(canvas.save()));
onResize(() -> {
setup();
canvas.rebuild();
if(wasRows != LCanvas.useRows() || wasPortrait != Core.graphics.isPortrait()){
setup();
canvas.rebuild();
wasPortrait = Core.graphics.isPortrait();
wasRows = LCanvas.useRows();
}
});
//show add instruction on shift+enter
keyDown(KeyCode.enter, () -> {
if(Core.input.shift()){
showAddDialog();
}
});
add(canvas).grow().name("canvas");
@@ -136,13 +155,13 @@ public class LogicDialog extends BaseDialog{
buttons.button("@variables", Icon.menu, () -> {
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);
}
});
@@ -169,7 +188,7 @@ public class LogicDialog extends BaseDialog{
Label label = out.add("").style(Styles.outlineLabel).padLeft(4).padRight(4).width(140f).wrap().get();
label.update(() -> {
if(counter[0] < 0 || (counter[0] += Time.delta) >= period){
String text = s.isobj ? PrintI.toString(s.objval) : Math.abs(s.numval - (long)s.numval) < 0.00001 ? (long)s.numval + "" : s.numval + "";
String text = s.isobj ? PrintI.toString(s.objval) : Math.abs(s.numval - Math.round(s.numval)) < 0.00001 ? Math.round(s.numval) + "" : s.numval + "";
if(!label.textEquals(text)){
label.setText(text);
if(counter[0] >= 0f){
@@ -201,16 +220,60 @@ 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").disabled(b -> executor == null || executor.vars.length == 0 || state.isMenu());
buttons.button("@add", Icon.add, () -> {
BaseDialog dialog = new BaseDialog("@add");
dialog.cont.table(table -> {
table.background(Tex.button);
table.pane(t -> {
showAddDialog();
}).disabled(t -> canvas.statements.getChildren().size >= LExecutor.maxInstructions);
}
public void showAddDialog(){
BaseDialog dialog = new BaseDialog("@add");
dialog.cont.table(table -> {
String[] searchText = {""};
Prov[] matched = {null};
Runnable[] rebuild = {() -> {}};
table.background(Tex.button);
table.table(s -> {
s.image(Icon.zoom).padRight(8);
var search = s.field(null, text -> {
searchText[0] = text;
rebuild[0].run();
}).growX().get();
search.setMessageText("@players.search");
//auto add first match on enter key
if(!mobile){
//don't focus on mobile (it may cause issues with a popup keyboard)
Core.app.post(search::requestKeyboard);
search.keyDown(KeyCode.enter, () -> {
if(!searchText[0].isEmpty() && matched[0] != null){
canvas.add((LStatement)matched[0].get());
dialog.hide();
}
});
}
}).growX().padBottom(4).row();
table.pane(t -> {
rebuild[0] = () -> {
t.clear();
var text = searchText[0].toLowerCase();
matched[0] = null;
for(Prov<LStatement> prov : LogicIO.allStatements){
LStatement example = prov.get();
if(example instanceof InvalidStatement || example.hidden() || (example.privileged() && !privileged) || (example.nonPrivileged() && privileged)) continue;
if(example instanceof InvalidStatement || example.hidden() || (example.privileged() && !privileged) || (example.nonPrivileged() && privileged) || (!text.isEmpty() && !example.name().toLowerCase(Locale.ROOT).contains(text))) continue;
if(matched[0] == null){
matched[0] = prov;
}
LCategory category = example.category();
Table cat = t.find(category.name);
@@ -242,11 +305,13 @@ public class LogicDialog extends BaseDialog{
if(cat.getChildren().size % 3 == 0) cat.row();
}
}).grow();
}).fill().maxHeight(Core.graphics.getHeight() * 0.8f);
dialog.addCloseButton();
dialog.show();
}).disabled(t -> canvas.statements.getChildren().size >= LExecutor.maxInstructions);
};
rebuild[0].run();
}).grow();
}).fill().maxHeight(Core.graphics.getHeight() * 0.8f);
dialog.addCloseButton();
dialog.show();
}
public void show(String code, LExecutor executor, boolean privileged, Cons<String> modified){

View File

@@ -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,