From bac6f7cfa97eb3e826ea66c388a2710f327d2658 Mon Sep 17 00:00:00 2001 From: BlackDeluxeCat <65377021+BlackDeluxeCat@users.noreply.github.com> Date: Thu, 25 Apr 2024 22:23:13 +0800 Subject: [PATCH] Add logic filter (#7251) * logic filter * bundles * available in map editor * @this = null * generating only * ensure not using net --------- Co-authored-by: Anuken --- core/assets/bundles/bundle.properties | 3 + core/src/mindustry/logic/LExecutor.java | 4 +- core/src/mindustry/maps/Maps.java | 2 +- .../mindustry/maps/filters/LogicFilter.java | 82 +++++++++++++++++++ 4 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 core/src/mindustry/maps/filters/LogicFilter.java diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index cbf4f022d1..f7aeab2413 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -590,6 +590,7 @@ filter.clear = Clear filter.option.ignore = Ignore filter.scatter = Scatter filter.terrain = Terrain +filter.logic = Logic filter.option.scale = Scale filter.option.chance = Chance @@ -613,6 +614,8 @@ filter.option.floor2 = Secondary Floor filter.option.threshold2 = Secondary Threshold filter.option.radius = Radius filter.option.percentile = Percentile +filter.option.code = Code +filter.option.loop = Loop locales.info = Here, you can add locale bundles for specific languages to your map. In locale bundles, each property has a name and a value. These properties can be used by world processors and objectives using their names. They support text formatting (replacing placeholders with actual values).\n\n[cyan]Example property:\n[]name: [accent]timer[]\nvalue: [accent]Example timer, time left: {0}[]\n\n[cyan]Usage:\n[]Set it as objective's text: [accent]@timer\n\n[]Print it in a world processor:\n[accent]localeprint "timer"\nformat time\n[gray](where time is a separately calculated variable) locales.deletelocale = Are you sure you want to delete this locale bundle? diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index 91cb4575e2..c852d63981 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -60,7 +60,7 @@ public class LExecutor{ public @Nullable LogicBuild build; public IntSet linkIds = new IntSet(); public Team team = Team.derelict; - public boolean privileged = false; + public boolean privileged = false, isFilter = false; //yes, this is a minor memory leak, but it's probably not significant enough to matter protected static IntFloatMap unitTimeouts = new IntFloatMap(); @@ -1479,7 +1479,7 @@ public class LExecutor{ if(t == null) t = Team.derelict; if(tile.block() != b || tile.team() != t){ - tile.setNet(b, t, Mathf.clamp(exec.numi(rotation), 0, 3)); + tile.setBlock(b, t, Mathf.clamp(exec.numi(rotation), 0, 3)); } } } diff --git a/core/src/mindustry/maps/Maps.java b/core/src/mindustry/maps/Maps.java index 01bbde4b20..101a7447d7 100644 --- a/core/src/mindustry/maps/Maps.java +++ b/core/src/mindustry/maps/Maps.java @@ -33,7 +33,7 @@ public class Maps{ NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new, RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new, BlendFilter::new, MirrorFilter::new, ClearFilter::new, CoreSpawnFilter::new, - EnemySpawnFilter::new, SpawnPathFilter::new + EnemySpawnFilter::new, SpawnPathFilter::new, LogicFilter::new }; /** List of all built-in maps. Filenames only. */ diff --git a/core/src/mindustry/maps/filters/LogicFilter.java b/core/src/mindustry/maps/filters/LogicFilter.java new file mode 100644 index 0000000000..fc5f6129f0 --- /dev/null +++ b/core/src/mindustry/maps/filters/LogicFilter.java @@ -0,0 +1,82 @@ +package mindustry.maps.filters; + +import arc.scene.ui.layout.*; +import mindustry.gen.*; +import mindustry.logic.*; +import mindustry.maps.filters.FilterOption.*; +import mindustry.world.*; + +import static mindustry.Vars.*; + +public class LogicFilter extends GenerateFilter{ + /** max available execution for logic filter */ + public static int maxInstructionsExecution = 500 * 500 * 25; + public String code; + public boolean loop; + LExecutor executor; + + @Override + public FilterOption[] options(){ + return new FilterOption[]{ + new FilterOption(){ + final String name; + { + name = "code"; + } + @Override + public void build(Table table){ + table.button(b -> b.image(Icon.pencil).size(iconSmall), () -> { + ui.logic.show(code, null, true, code -> LogicFilter.this.code = code); + }).pad(4).margin(12f); + + table.add("@filter.option." + name); + } + }, + new ToggleOption("loop", () -> loop, f -> loop = f) + }; + } + + @Override + public void apply(Tiles tiles, GenerateInput in){ + executor = new LExecutor(); + executor.privileged = true; + executor.isFilter = true; + configure(code); + + //limited run + for(int i = 1; i < maxInstructionsExecution; i++){ + if(!loop && (executor.counter.numval >= executor.instructions.length || executor.counter.numval < 0)) break; + executor.runOnce(); + } + } + + @Override + public char icon(){ + return Iconc.blockMicroProcessor; + } + + @Override + public boolean isPost(){ + return true; + } + + void configure(String code){ + try{ + //create assembler to store extra variables + LAssembler asm = LAssembler.assemble(code, true); + + asm.putConst("@mapw", world.width()); + asm.putConst("@maph", world.height()); + asm.putConst("@links", executor.links.length); + asm.putConst("@ipt", 1); + + asm.putConst("@thisx", 0); + asm.putConst("@thisy", 0); + + executor.load(asm); + }catch(Exception e){ + //handle malformed code and replace it with nothing + executor.load(LAssembler.assemble(code = "", true)); + } + } +}