diff --git a/core/src/mindustry/core/World.java b/core/src/mindustry/core/World.java index c6b4c885be..c5bfcd6d14 100644 --- a/core/src/mindustry/core/World.java +++ b/core/src/mindustry/core/World.java @@ -32,18 +32,18 @@ public class World{ 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. */ - public int tileChanges = -1; + public int tileChanges = 1, floorChanges = 1; private boolean generating, invalidMap; private ObjectMap customMapLoaders = new ObjectMap<>(); public World(){ - Events.on(TileChangeEvent.class, e -> { - tileChanges ++; - }); + Events.on(TileChangeEvent.class, e -> tileChanges ++); + Events.on(TileFloorChangeEvent.class, e -> floorChanges ++); Events.on(WorldLoadEvent.class, e -> { tileChanges = -1; + floorChanges = -1; //make each building check if it can update in the given map area for(var build : Groups.build){ diff --git a/core/src/mindustry/editor/EditorTile.java b/core/src/mindustry/editor/EditorTile.java index c68ecf9001..1fe749b991 100644 --- a/core/src/mindustry/editor/EditorTile.java +++ b/core/src/mindustry/editor/EditorTile.java @@ -36,6 +36,8 @@ public class EditorTile extends Tile{ op(DrawOperation.opFloor, floor.id); + world.floorChanges ++; + this.floor = type; type.floorChanged(this); } @@ -85,6 +87,10 @@ public class EditorTile extends Tile{ if(build != null){ build.wasVisible = true; } + + world.tileChanges ++; + + type.blockChanged(this); } @Override @@ -112,6 +118,10 @@ public class EditorTile extends Tile{ if(this.overlay == overlay) return; op(DrawOperation.opOverlay, this.overlay.id); super.setOverlay(overlay); + + world.floorChanges ++; + + ((Floor)overlay).floorChanged(this); } @Override diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index 63e14636cb..83881dd00a 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -938,6 +938,8 @@ public class LExecutor{ exec.graphicsBuffer.add(DisplayCmd.get(LogicDisplay.commandPrint, packSign(curX + xOffset), packSign(curY + yOffset), next, 0, 0, 0)); } curX += advance; + + if(exec.graphicsBuffer.size >= maxGraphicsBuffer) break; } exec.textBuffer.setLength(0); @@ -1010,7 +1012,7 @@ public class LExecutor{ if(value.isobj){ String strValue = toString(value.objval); - exec.textBuffer.append(strValue); + exec.textBuffer.append(strValue, 0, Math.min(strValue.length(), maxTextBuffer - exec.textBuffer.length())); }else{ //display integer version when possible 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))){ d.message.setLength(0); d.message.append(exec.textBuffer, 0, Math.min(exec.textBuffer.length(), maxTextBuffer)); - } exec.textBuffer.setLength(0); diff --git a/core/src/mindustry/world/Tiles.java b/core/src/mindustry/world/Tiles.java index b81603da00..77b8e3f726 100644 --- a/core/src/mindustry/world/Tiles.java +++ b/core/src/mindustry/world/Tiles.java @@ -16,6 +16,8 @@ public class Tiles implements Iterable{ final Puddle[] puddles; final Fire[] fires; + @Nullable long[] tmpFloorState, tmpBlockState; + public Tiles(int width, int height){ this.array = new Tile[width * height]; this.width = width; @@ -24,6 +26,24 @@ public class Tiles implements Iterable{ 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){ return puddles[pos]; } diff --git a/core/src/mindustry/world/blocks/environment/TiledFloor.java b/core/src/mindustry/world/blocks/environment/TiledFloor.java new file mode 100644 index 0000000000..85ff8603c1 --- /dev/null +++ b/core/src/mindustry/world/blocks/environment/TiledFloor.java @@ -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; + } +} diff --git a/core/src/mindustry/world/blocks/environment/TiledWall.java b/core/src/mindustry/world/blocks/environment/TiledWall.java new file mode 100644 index 0000000000..1286add73c --- /dev/null +++ b/core/src/mindustry/world/blocks/environment/TiledWall.java @@ -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()); + } +} diff --git a/core/src/mindustry/world/blocks/logic/MessageBlock.java b/core/src/mindustry/world/blocks/logic/MessageBlock.java index 3ccdade0d5..20546b1a35 100644 --- a/core/src/mindustry/world/blocks/logic/MessageBlock.java +++ b/core/src/mindustry/world/blocks/logic/MessageBlock.java @@ -24,7 +24,7 @@ import static mindustry.Vars.*; public class MessageBlock extends Block{ //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 MessageBlock(String name){