Merge branch 'master' into pr-readwrite
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -55,6 +55,8 @@ public enum LAccess{
|
||||
name,
|
||||
payloadCount,
|
||||
payloadType,
|
||||
totalPayload,
|
||||
payloadCapacity,
|
||||
id,
|
||||
|
||||
//values with parameters are considered controllable
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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