Add logic filter (#7251)

* logic filter

* bundles

* available in map editor

* @this = null

* generating only

* ensure not using net

---------

Co-authored-by: Anuken <arnukren@gmail.com>
This commit is contained in:
BlackDeluxeCat 2024-04-25 22:23:13 +08:00 committed by GitHub
parent 7fcde1e109
commit bac6f7cfa9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 88 additions and 3 deletions

View File

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

View File

@ -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));
}
}
}

View File

@ -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. */

View File

@ -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));
}
}
}