TiledFloor and TiledWall implementation classes

This commit is contained in:
Anuken
2025-12-30 19:19:23 -05:00
parent 4d57e9fbdb
commit 8db5ec0247
7 changed files with 287 additions and 7 deletions

View File

@@ -32,18 +32,18 @@ public class World{
public Tiles tiles = new Tiles(0, 0); public Tiles tiles = new Tiles(0, 0);
/** The number of times tiles have changed in this session. Used for blocks that need to poll world state, but not frequently. */ /** The number of times tiles have changed in this session. Used for blocks that need to poll world state, but not frequently. */
public int tileChanges = -1; public int tileChanges = 1, floorChanges = 1;
private boolean generating, invalidMap; private boolean generating, invalidMap;
private ObjectMap<Map, Runnable> customMapLoaders = new ObjectMap<>(); private ObjectMap<Map, Runnable> customMapLoaders = new ObjectMap<>();
public World(){ public World(){
Events.on(TileChangeEvent.class, e -> { Events.on(TileChangeEvent.class, e -> tileChanges ++);
tileChanges ++; Events.on(TileFloorChangeEvent.class, e -> floorChanges ++);
});
Events.on(WorldLoadEvent.class, e -> { Events.on(WorldLoadEvent.class, e -> {
tileChanges = -1; tileChanges = -1;
floorChanges = -1;
//make each building check if it can update in the given map area //make each building check if it can update in the given map area
for(var build : Groups.build){ for(var build : Groups.build){

View File

@@ -36,6 +36,8 @@ public class EditorTile extends Tile{
op(DrawOperation.opFloor, floor.id); op(DrawOperation.opFloor, floor.id);
world.floorChanges ++;
this.floor = type; this.floor = type;
type.floorChanged(this); type.floorChanged(this);
} }
@@ -85,6 +87,10 @@ public class EditorTile extends Tile{
if(build != null){ if(build != null){
build.wasVisible = true; build.wasVisible = true;
} }
world.tileChanges ++;
type.blockChanged(this);
} }
@Override @Override
@@ -112,6 +118,10 @@ public class EditorTile extends Tile{
if(this.overlay == overlay) return; if(this.overlay == overlay) return;
op(DrawOperation.opOverlay, this.overlay.id); op(DrawOperation.opOverlay, this.overlay.id);
super.setOverlay(overlay); super.setOverlay(overlay);
world.floorChanges ++;
((Floor)overlay).floorChanged(this);
} }
@Override @Override

View File

@@ -938,6 +938,8 @@ public class LExecutor{
exec.graphicsBuffer.add(DisplayCmd.get(LogicDisplay.commandPrint, packSign(curX + xOffset), packSign(curY + yOffset), next, 0, 0, 0)); exec.graphicsBuffer.add(DisplayCmd.get(LogicDisplay.commandPrint, packSign(curX + xOffset), packSign(curY + yOffset), next, 0, 0, 0));
} }
curX += advance; curX += advance;
if(exec.graphicsBuffer.size >= maxGraphicsBuffer) break;
} }
exec.textBuffer.setLength(0); exec.textBuffer.setLength(0);
@@ -1010,7 +1012,7 @@ public class LExecutor{
if(value.isobj){ if(value.isobj){
String strValue = toString(value.objval); String strValue = toString(value.objval);
exec.textBuffer.append(strValue); exec.textBuffer.append(strValue, 0, Math.min(strValue.length(), maxTextBuffer - exec.textBuffer.length()));
}else{ }else{
//display integer version when possible //display integer version when possible
if(Math.abs(value.numval - Math.round(value.numval)) < 0.00001){ if(Math.abs(value.numval - Math.round(value.numval)) < 0.00001){
@@ -1122,7 +1124,6 @@ public class LExecutor{
if(target.building() instanceof MessageBuild d && d.isValid() && (exec.privileged || (d.team == exec.team && !d.block.privileged))){ if(target.building() instanceof MessageBuild d && d.isValid() && (exec.privileged || (d.team == exec.team && !d.block.privileged))){
d.message.setLength(0); d.message.setLength(0);
d.message.append(exec.textBuffer, 0, Math.min(exec.textBuffer.length(), maxTextBuffer)); d.message.append(exec.textBuffer, 0, Math.min(exec.textBuffer.length(), maxTextBuffer));
} }
exec.textBuffer.setLength(0); exec.textBuffer.setLength(0);

View File

@@ -16,6 +16,8 @@ public class Tiles implements Iterable<Tile>{
final Puddle[] puddles; final Puddle[] puddles;
final Fire[] fires; final Fire[] fires;
@Nullable long[] tmpFloorState, tmpBlockState;
public Tiles(int width, int height){ public Tiles(int width, int height){
this.array = new Tile[width * height]; this.array = new Tile[width * height];
this.width = width; this.width = width;
@@ -24,6 +26,24 @@ public class Tiles implements Iterable<Tile>{
this.fires = new Fire[width * height]; this.fires = new Fire[width * height];
} }
public long getTmpFloorState(int pos){
return tmpFloorState == null ? 0 : tmpFloorState[pos];
}
public void setTmpFloorState(int pos, long value){
if(tmpFloorState == null || tmpFloorState.length != array.length) tmpFloorState = new long[array.length];
tmpFloorState[pos] = value;
}
public long getTmpBlockState(int pos){
return tmpBlockState == null ? 0 : tmpBlockState[pos];
}
public void setTmpBlockState(int pos, long value){
if(tmpBlockState == null || tmpBlockState.length != array.length) tmpBlockState = new long[array.length];
tmpBlockState[pos] = value;
}
public Puddle getPuddle(int pos){ public Puddle getPuddle(int pos){
return puddles[pos]; return puddles[pos];
} }

View File

@@ -0,0 +1,131 @@
package mindustry.world.blocks.environment;
import arc.*;
import arc.graphics.g2d.*;
import arc.math.*;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
import mindustry.world.*;
import static mindustry.Vars.*;
public class TiledFloor extends Floor{
public TextureRegion[][][][] sizedRegions;
public int maxSize = 3;
public TiledFloor(String name){
super(name);
}
public TiledFloor(String name, int variants, int maxSize){
super(name);
this.variants = variants;
this.maxSize = maxSize;
}
@Override
public void load(){
super.load();
sizedRegions = new TextureRegion[maxSize + 1][variants][][];
for(int size = 1; size <= maxSize; size++){
for(int v = 0; v < variants; v++){
TextureRegion base = Core.atlas.find(name + "-" + size + "-" + v);
int actualSize = size;
if(!base.found()) base = Core.atlas.find(name + "-" + size);
if(!base.found()){
actualSize = 1; //the 1x1 sprite should not be split like a larger region
base = region;
}
sizedRegions[size][v] = base.split(base.width / actualSize, base.height / actualSize);
}
}
}
long state(Tile tile){
return world.tiles.getTmpFloorState(tile.array());
}
@Override
public void floorChanged(Tile tile){
if(TiledState.changes(state(tile)) != world.floorChanges && !world.isGenerating()){
scan(tile);
}
}
void scan(Tile tile){
//max size possible
int size = maxSize;
int changes = world.floorChanges;
boolean isOverlay = tile.floor() != this;
//scan to the top right for the biggest size possible
for(int cx = 0; cx < size; cx++){
for(int cy = 0; cy < size; cy++){
Tile other = tile.nearby(cx, cy);
if(other == null || ((isOverlay ? other.overlay() : other.floor()) != this) || TiledState.changes(state(other)) == changes){
int max = Math.max(cx, cy);
size = Math.min(max, size);
size = Math.min(max, size);
}
}
}
//assign roots based on max value
for(int cx = 0; cx < size; cx++){
for(int cy = 0; cy < size; cy++){
Tile other = tile.nearby(cx, cy);
if(other != null){
long otherState = state(other);
if(!headless && TiledState.changes(otherState) != 0){
Tile otherRoot = other.nearby(-TiledState.x(otherState), -TiledState.y(otherState));
if(otherRoot != null){
Core.app.post(() -> renderer.blocks.floor.recacheTile(otherRoot));
}
}
//mark as updated
world.tiles.setTmpFloorState(other.array(), TiledState.get(cx, cy, size, changes));
if(!headless && otherState != 0){
Core.app.post(() -> renderer.blocks.floor.recacheTile(other));
}
}
}
}
}
@Override
public void drawMain(Tile tile){
long state = state(tile);
//stale state, start scanning.
if(TiledState.changes(state) != world.floorChanges){
scan(tile);
//state has most likely updated
state = state(tile);
}
int size = Mathf.clamp(TiledState.size(state), 1, maxSize);
int cx = TiledState.x(state), cy = TiledState.y(state);
int variant = variant(tile.x - cx, tile.y - cy, variants);
TextureRegion[][] regions = sizedRegions[size][variant];
Draw.rect(regions[Mathf.clamp(cx, 0, regions.length - 1)][Mathf.clamp(size - 1 - cy, 0, regions[0].length - 1)], tile.worldx(), tile.worldy());
}
@Struct
class TiledStateStruct{
@StructField(4)
int x;
@StructField(4)
int y;
@StructField(8)
int size;
int changes;
}
}

View File

@@ -0,0 +1,118 @@
package mindustry.world.blocks.environment;
import arc.*;
import arc.graphics.g2d.*;
import arc.math.*;
import mindustry.gen.*;
import mindustry.world.*;
import static mindustry.Vars.*;
public class TiledWall extends StaticWall{
public TextureRegion[][][][] sizedRegions;
public int maxSize = 3;
public TiledWall(String name){
super(name);
}
public TiledWall(String name, int variants, int maxSize){
super(name);
this.variants = variants;
this.maxSize = maxSize;
}
@Override
public void load(){
super.load();
sizedRegions = new TextureRegion[maxSize + 1][variants][][];
for(int size = 1; size <= maxSize; size++){
for(int v = 0; v < variants; v++){
TextureRegion base = Core.atlas.find(name + "-" + size + "-" + v);
int actualSize = size;
if(!base.found()) base = Core.atlas.find(name + "-" + size);
if(!base.found()){
actualSize = 1; //the 1x1 sprite should not be split like a larger region
base = region;
}
sizedRegions[size][v] = base.split(base.width / actualSize, base.height / actualSize);
}
}
}
long state(Tile tile){
return world.tiles.getTmpBlockState(tile.array());
}
@Override
public void blockChanged(Tile tile){
super.blockChanged(tile);
if(TiledState.changes(state(tile)) != world.tileChanges && !world.isGenerating()){
scan(tile);
}
}
void scan(Tile tile){
//max size possible
int size = maxSize;
int changes = world.tileChanges;
//scan to the top right for the biggest size possible
for(int cx = 0; cx < size; cx++){
for(int cy = 0; cy < size; cy++){
Tile other = tile.nearby(cx, cy);
if(other == null || other.block() != this || TiledState.changes(state(other)) == changes){
int max = Math.max(cx, cy);
size = Math.min(max, size);
size = Math.min(max, size);
}
}
}
//assign roots based on max value
for(int cx = 0; cx < size; cx++){
for(int cy = 0; cy < size; cy++){
Tile other = tile.nearby(cx, cy);
if(other != null){
long otherState = state(other);
if(!headless && TiledState.changes(otherState) != 0){
Tile otherRoot = other.nearby(-TiledState.x(otherState), -TiledState.y(otherState));
if(otherRoot != null){
Core.app.post(() -> renderer.blocks.floor.recacheTile(otherRoot));
}
}
//mark as updated
world.tiles.setTmpBlockState(other.array(), TiledState.get(cx, cy, size, changes));
if(!headless && otherState != 0){
Core.app.post(() -> renderer.blocks.floor.recacheTile(other));
}
}
}
}
}
@Override
public void drawBase(Tile tile){
long state = state(tile);
//stale state, start scanning.
if(TiledState.changes(state) != world.tileChanges){
scan(tile);
//state has most likely updated
state = state(tile);
}
int size = Mathf.clamp(TiledState.size(state), 1, maxSize);
int cx = TiledState.x(state), cy = TiledState.y(state);
int variant = variant(tile.x - cx, tile.y - cy, variants);
TextureRegion[][] regions = sizedRegions[size][variant];
Draw.rect(regions[Mathf.clamp(cx, 0, regions.length - 1)][Mathf.clamp(size - 1 - cy, 0, regions[0].length - 1)], tile.worldx(), tile.worldy());
}
}

View File

@@ -24,7 +24,7 @@ import static mindustry.Vars.*;
public class MessageBlock extends Block{ public class MessageBlock extends Block{
//don't change this too much unless you want to run into issues with packet sizes //don't change this too much unless you want to run into issues with packet sizes
public int maxTextLength = 220; public int maxTextLength = 300;
public int maxNewlines = 24; public int maxNewlines = 24;
public MessageBlock(String name){ public MessageBlock(String name){