Merge branch 'crux-floor' of https://github.com/Anuken/MindustryPrivate
This commit is contained in:
@@ -11,6 +11,7 @@ import mindustry.entities.effect.*;
|
||||
import mindustry.entities.part.DrawPart.*;
|
||||
import mindustry.entities.part.*;
|
||||
import mindustry.entities.pattern.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
@@ -57,8 +58,18 @@ public class Blocks{
|
||||
//boulders
|
||||
shaleBoulder, sandBoulder, daciteBoulder, boulder, snowBoulder, basaltBoulder, carbonBoulder, ferricBoulder, beryllicBoulder, yellowStoneBoulder,
|
||||
arkyicBoulder, crystalCluster, vibrantCrystalCluster, crystalBlocks, crystalOrbs, crystallineBoulder, redIceBoulder, rhyoliteBoulder, redStoneBoulder,
|
||||
|
||||
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor4, metalFloor5, basalt, magmarock, hotrock, snowWall, saltWall,
|
||||
//old metal floors
|
||||
darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6, darkMetal,
|
||||
//new metal floors
|
||||
metalTiles1, metalTiles2, metalTiles3, metalTiles4, metalTiles5, metalTiles6, metalTiles7, metalTiles8, metalTiles9, metalTiles10, metalTiles11, metalTiles12,
|
||||
|
||||
//colored
|
||||
coloredFloor, coloredWall,
|
||||
characterOverlayGray,
|
||||
characterOverlayWhite,
|
||||
|
||||
pebbles, tendrils,
|
||||
|
||||
//ores
|
||||
@@ -314,6 +325,7 @@ public class Blocks{
|
||||
solid = true;
|
||||
variants = 0;
|
||||
canShadow = false;
|
||||
drawEdgeOut = false;
|
||||
}};
|
||||
|
||||
empty = new EmptyFloor("empty");
|
||||
@@ -808,6 +820,107 @@ public class Blocks{
|
||||
|
||||
darkMetal = new StaticWall("dark-metal");
|
||||
|
||||
metalTiles1 = new Floor("metal-tiles-1"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
}};
|
||||
|
||||
metalTiles2 = new Floor("metal-tiles-2"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
}};
|
||||
|
||||
metalTiles3 = new Floor("metal-tiles-3"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
}};
|
||||
|
||||
metalTiles4 = new Floor("metal-tiles-4"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
}};
|
||||
|
||||
metalTiles5 = new Floor("metal-tiles-5"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
}};
|
||||
|
||||
metalTiles6 = new Floor("metal-tiles-6"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
|
||||
emitLight = true;
|
||||
lightRadius = 30f;
|
||||
lightColor = Team.crux.color.cpy().a(0.3f);
|
||||
}};
|
||||
|
||||
metalTiles7 = new Floor("metal-tiles-7"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
autotileMidVariants = 9;
|
||||
}};
|
||||
|
||||
metalTiles8 = new Floor("metal-tiles-8"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
autotileMidVariants = 2;
|
||||
}};
|
||||
|
||||
metalTiles9 = new Floor("metal-tiles-9"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
}};
|
||||
|
||||
metalTiles10 = new Floor("metal-tiles-10"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
}};
|
||||
|
||||
metalTiles11 = new Floor("metal-tiles-11"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
autotileVariants = 3;
|
||||
}};
|
||||
|
||||
metalTiles12 = new Floor("metal-tiles-12"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
autotileVariants = 4;
|
||||
emitLight = true;
|
||||
lightRadius = 30f;
|
||||
lightColor = Team.crux.color.cpy().a(0.3f);
|
||||
}};
|
||||
|
||||
coloredFloor = new ColoredFloor("colored-floor"){{
|
||||
autotile = true;
|
||||
drawEdgeOut = false;
|
||||
drawEdgeIn = false;
|
||||
}};
|
||||
|
||||
coloredWall = new ColoredWall("colored-wall"){{
|
||||
autotile = true;
|
||||
}};
|
||||
|
||||
characterOverlayGray = new CharacterOverlay("character-overlay"){{
|
||||
color = Pal.metalGrayDark;
|
||||
}};
|
||||
|
||||
characterOverlayWhite = new CharacterOverlay("character-overlay-white"){{
|
||||
color = Color.white;
|
||||
}};
|
||||
|
||||
Seq.with(metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor4, metalFloor5, darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6)
|
||||
.each(b -> b.asFloor().wall = darkMetal);
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ public class Renderer implements ApplicationListener{
|
||||
public @Nullable Bloom bloom;
|
||||
public @Nullable FrameBuffer backgroundBuffer;
|
||||
public FrameBuffer effectBuffer = new FrameBuffer();
|
||||
public boolean animateShields, drawWeather = true, drawStatus, enableEffects, drawDisplays = true, drawLight = true, pixelate = false;
|
||||
public boolean animateShields, animateWater, drawWeather = true, drawStatus, enableEffects, drawDisplays = true, drawLight = true, pixelate = false;
|
||||
public float weatherAlpha;
|
||||
/** minZoom = zooming out, maxZoom = zooming in, used by cutscenes */
|
||||
public float minZoom = 1.5f, maxZoom = 6f;
|
||||
@@ -167,6 +167,7 @@ public class Renderer implements ApplicationListener{
|
||||
laserOpacity = settings.getInt("lasersopacity") / 100f;
|
||||
bridgeOpacity = settings.getInt("bridgeopacity") / 100f;
|
||||
animateShields = settings.getBool("animatedshields");
|
||||
animateWater = settings.getBool("animatewater");
|
||||
drawStatus = settings.getBool("blockstatus");
|
||||
enableEffects = settings.getBool("effects");
|
||||
drawDisplays = !settings.getBool("hidedisplays");
|
||||
@@ -310,7 +311,7 @@ public class Renderer implements ApplicationListener{
|
||||
graphics.clear(clearColor);
|
||||
Draw.reset();
|
||||
|
||||
if(settings.getBool("animatedwater") || animateShields){
|
||||
if(animateWater || animateShields){
|
||||
effectBuffer.resize(graphics.getWidth(), graphics.getHeight());
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,13 @@ import mindustry.world.blocks.environment.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class DrawOperation{
|
||||
static final byte
|
||||
opFloor = 0,
|
||||
opBlock = 1,
|
||||
opRotation = 2,
|
||||
opTeam = 3,
|
||||
opOverlay = 4;
|
||||
|
||||
private LongSeq array = new LongSeq();
|
||||
|
||||
public boolean isEmpty(){
|
||||
@@ -39,45 +46,58 @@ public class DrawOperation{
|
||||
}
|
||||
|
||||
short getTile(Tile tile, byte type){
|
||||
if(type == OpType.floor.ordinal()){
|
||||
return tile.floorID();
|
||||
}else if(type == OpType.block.ordinal()){
|
||||
return tile.blockID();
|
||||
}else if(type == OpType.rotation.ordinal()){
|
||||
return tile.build == null ? 0 : (byte)tile.build.rotation;
|
||||
}else if(type == OpType.team.ordinal()){
|
||||
return (byte)tile.getTeamID();
|
||||
}else if(type == OpType.overlay.ordinal()){
|
||||
return tile.overlayID();
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid type.");
|
||||
return switch(type){
|
||||
case opFloor -> tile.floorID();
|
||||
case opOverlay -> tile.overlayID();
|
||||
case opBlock -> tile.blockID();
|
||||
case opRotation -> tile.build == null ? 0 : (byte)tile.build.rotation;
|
||||
case opTeam -> (byte)tile.getTeamID();
|
||||
default -> throw new IllegalArgumentException("Invalid type.");
|
||||
};
|
||||
}
|
||||
|
||||
void setTile(Tile tile, byte type, short to){
|
||||
if(type == opBlock || type == opTeam || type == opRotation){
|
||||
tile.getLinkedTiles(t -> {
|
||||
editor.renderer.updateBlock(t);
|
||||
editor.renderer.updateStatic(t.x, t.y);
|
||||
});
|
||||
}else{
|
||||
editor.renderer.updateStatic(tile.x, tile.y);
|
||||
}
|
||||
|
||||
editor.load(() -> {
|
||||
if(type == OpType.floor.ordinal()){
|
||||
if(content.block(to) instanceof Floor floor){
|
||||
tile.setFloor(floor);
|
||||
switch(type){
|
||||
case opFloor -> {
|
||||
if(content.block(to) instanceof Floor floor){
|
||||
tile.setFloor(floor);
|
||||
}
|
||||
}
|
||||
}else if(type == OpType.block.ordinal()){
|
||||
tile.getLinkedTiles(t -> editor.renderer.updatePoint(t.x, t.y));
|
||||
|
||||
Block block = content.block(to);
|
||||
tile.setBlock(block, tile.team(), tile.build == null ? 0 : tile.build.rotation);
|
||||
if(tile.build != null){
|
||||
tile.build.enabled = true;
|
||||
case opOverlay -> {
|
||||
if(content.block(to) instanceof Floor floor){
|
||||
tile.setOverlay(floor);
|
||||
}
|
||||
}
|
||||
|
||||
tile.getLinkedTiles(t -> editor.renderer.updatePoint(t.x, t.y));
|
||||
}else if(type == OpType.rotation.ordinal()){
|
||||
if(tile.build != null) tile.build.rotation = to;
|
||||
}else if(type == OpType.team.ordinal()){
|
||||
tile.setTeam(Team.get(to));
|
||||
}else if(type == OpType.overlay.ordinal()){
|
||||
tile.setOverlayID(to);
|
||||
case opBlock -> {
|
||||
Block block = content.block(to);
|
||||
tile.setBlock(block, tile.team(), tile.build == null ? 0 : tile.build.rotation);
|
||||
if(tile.build != null){
|
||||
tile.build.enabled = true;
|
||||
}
|
||||
}
|
||||
case opRotation -> {
|
||||
if(tile.build != null) tile.build.rotation = to;
|
||||
}
|
||||
case opTeam -> tile.setTeam(Team.get(to));
|
||||
}
|
||||
});
|
||||
editor.renderer.updatePoint(tile.x, tile.y);
|
||||
|
||||
if(type == opBlock || type == opTeam || type == opRotation){
|
||||
tile.getLinkedTiles(t -> {
|
||||
editor.renderer.updateBlock(t);
|
||||
editor.renderer.updateStatic(t.x, t.y);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Struct
|
||||
@@ -87,12 +107,4 @@ public class DrawOperation{
|
||||
byte type;
|
||||
short value;
|
||||
}
|
||||
|
||||
public enum OpType{
|
||||
floor,
|
||||
block,
|
||||
rotation,
|
||||
team,
|
||||
overlay
|
||||
}
|
||||
}
|
||||
|
||||
171
core/src/mindustry/editor/EditorSpriteCache.java
Normal file
171
core/src/mindustry/editor/EditorSpriteCache.java
Normal file
@@ -0,0 +1,171 @@
|
||||
package mindustry.editor;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
|
||||
public class EditorSpriteCache implements Disposable{
|
||||
//xy + color + uv
|
||||
static final int vertexSize = 2 + 1 + 2;
|
||||
|
||||
private @Nullable Mesh mesh;
|
||||
private final Seq<Texture> textures = new Seq<>(8);
|
||||
private final IntSeq counts = new IntSeq(8);
|
||||
|
||||
private float[] tmpVertices;
|
||||
|
||||
/** Index in tmpVertices of current vertex data. */
|
||||
private int index;
|
||||
|
||||
/** @param tmpVertices Temporary buffer to hold vertices while building up sprites. Should be large enough to hold all sprite data this cache will contain. */
|
||||
public EditorSpriteCache(float[] tmpVertices){
|
||||
this.tmpVertices = tmpVertices;
|
||||
}
|
||||
|
||||
/** @return whether anything was added to the cache. */
|
||||
public boolean isEmpty(){
|
||||
return index == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds this cache into a mesh that can be used for rendering. Use after calling {@link #draw(TextureRegion, float, float, float, float, float, float, float, float)}.
|
||||
* Until this method is called, no mesh is created.
|
||||
*
|
||||
* @param indices The shared index data in standard quad format, as seen in SpriteBatch.
|
||||
* Since this data is static, it should be the same across all caches, and be large enough to accommodate all sprites.
|
||||
* */
|
||||
public void build(IndexData indices){
|
||||
if(mesh != null) mesh.dispose();
|
||||
|
||||
mesh = new Mesh(true, index / vertexSize, 0,
|
||||
VertexAttribute.position,
|
||||
VertexAttribute.color,
|
||||
VertexAttribute.texCoords
|
||||
);
|
||||
mesh.indices = indices;
|
||||
mesh.setVertices(tmpVertices, 0, index);
|
||||
}
|
||||
|
||||
/** Adds the specified region to the cache. */
|
||||
public void draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float rotation, float colorPacked){
|
||||
if(mesh != null) throw new IllegalStateException("This cache is already built. Call #clear() before drawing new sprites.");
|
||||
|
||||
// bottom left and top right corner points relative to origin
|
||||
final float worldOriginX = x + originX;
|
||||
final float worldOriginY = y + originY;
|
||||
float fx = -originX;
|
||||
float fy = -originY;
|
||||
float fx2 = width - originX;
|
||||
float fy2 = height - originY;
|
||||
|
||||
float x1, y1, x2, y2, x3, y3, x4, y4;
|
||||
|
||||
// rotate
|
||||
if(rotation != 0){
|
||||
final float cos = Mathf.cosDeg(rotation);
|
||||
final float sin = Mathf.sinDeg(rotation);
|
||||
|
||||
x1 = cos * fx - sin * fy;
|
||||
y1 = sin * fx + cos * fy;
|
||||
|
||||
x2 = cos * fx - sin * fy2;
|
||||
y2 = sin * fx + cos * fy2;
|
||||
|
||||
x3 = cos * fx2 - sin * fy2;
|
||||
y3 = sin * fx2 + cos * fy2;
|
||||
|
||||
x4 = x1 + (x3 - x2);
|
||||
y4 = y3 - (y2 - y1);
|
||||
}else{
|
||||
x1 = fx;
|
||||
y1 = fy;
|
||||
|
||||
x2 = fx;
|
||||
y2 = fy2;
|
||||
|
||||
x3 = fx2;
|
||||
y3 = fy2;
|
||||
|
||||
x4 = fx2;
|
||||
y4 = fy;
|
||||
}
|
||||
|
||||
x1 += worldOriginX;
|
||||
y1 += worldOriginY;
|
||||
x2 += worldOriginX;
|
||||
y2 += worldOriginY;
|
||||
x3 += worldOriginX;
|
||||
y3 += worldOriginY;
|
||||
x4 += worldOriginX;
|
||||
y4 += worldOriginY;
|
||||
|
||||
final float u = region.u;
|
||||
final float v = region.v2;
|
||||
final float u2 = region.u2;
|
||||
final float v2 = region.v;
|
||||
|
||||
int idx = index;
|
||||
float[] verts = tmpVertices;
|
||||
Texture texture = region.texture;
|
||||
|
||||
verts[idx + 0] = x1;
|
||||
verts[idx + 1] = y1;
|
||||
verts[idx + 2] = colorPacked;
|
||||
verts[idx + 3] = u;
|
||||
verts[idx + 4] = v;
|
||||
|
||||
verts[idx + 5] = x2;
|
||||
verts[idx + 6] = y2;
|
||||
verts[idx + 7] = colorPacked;
|
||||
verts[idx + 8] = u;
|
||||
verts[idx + 9] = v2;
|
||||
|
||||
verts[idx + 10] = x3;
|
||||
verts[idx + 11] = y3;
|
||||
verts[idx + 12] = colorPacked;
|
||||
verts[idx + 13] = u2;
|
||||
verts[idx + 14] = v2;
|
||||
|
||||
verts[idx + 15] = x4;
|
||||
verts[idx + 16] = y4;
|
||||
verts[idx + 17] = colorPacked;
|
||||
verts[idx + 18] = u2;
|
||||
verts[idx + 19] = v;
|
||||
|
||||
int lastIndex = textures.size - 1;
|
||||
if(lastIndex < 0 || textures.get(lastIndex) != texture){
|
||||
textures.add(texture);
|
||||
counts.add(6);
|
||||
}else{
|
||||
counts.incr(lastIndex, 6);
|
||||
}
|
||||
|
||||
index += vertexSize * 4;
|
||||
}
|
||||
|
||||
/** Renders the cached mesh. The shader must already have the correct view matrix set as a uniform. */
|
||||
public void render(Shader shader){
|
||||
if(mesh == null) throw new IllegalStateException("Cache is empty, call build() first.");
|
||||
|
||||
int offset = 0;
|
||||
|
||||
for(int i = 0; i < textures.size; i++){
|
||||
int count = counts.items[i];
|
||||
textures.get(i).bind();
|
||||
|
||||
mesh.render(shader, Gl.triangles, offset, count);
|
||||
offset += count;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
if(mesh != null){
|
||||
mesh.dispose();
|
||||
mesh = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,9 @@ package mindustry.editor;
|
||||
|
||||
import arc.func.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.editor.DrawOperation.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.modules.*;
|
||||
@@ -27,17 +27,17 @@ public class EditorTile extends Tile{
|
||||
if(type instanceof OverlayFloor){
|
||||
//don't place on liquids
|
||||
if(floor.hasSurface() || !type.needsSurface){
|
||||
setOverlayID(type.id);
|
||||
setOverlay(type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(floor == type && overlayID() == 0) return;
|
||||
if(overlayID() != 0) op(OpType.overlay, overlayID());
|
||||
if(floor != type) op(OpType.floor, floor.id);
|
||||
if(floor == type) return;
|
||||
|
||||
op(DrawOperation.opFloor, floor.id);
|
||||
|
||||
this.floor = type;
|
||||
this.overlay = (Floor)Blocks.air;
|
||||
type.floorChanged(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,30 +47,40 @@ public class EditorTile extends Tile{
|
||||
|
||||
@Override
|
||||
public void setBlock(Block type, Team team, int rotation, Prov<Building> entityprov){
|
||||
Block prev = this.block;
|
||||
Tile prevCenter = (build == null ? this : build.tile);
|
||||
|
||||
if(skip()){
|
||||
super.setBlock(type, team, rotation, entityprov);
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.block == type && (build == null || build.rotation == rotation)){
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isCenter()){
|
||||
EditorTile cen = (EditorTile)build.tile;
|
||||
cen.op(OpType.rotation, (byte)build.rotation);
|
||||
cen.op(OpType.team, (byte)build.team.id);
|
||||
cen.op(OpType.block, block.id);
|
||||
update();
|
||||
cen.op(DrawOperation.opRotation, (byte)build.rotation);
|
||||
cen.op(DrawOperation.opTeam, (byte)build.team.id);
|
||||
cen.op(DrawOperation.opBlock, block.id);
|
||||
updateStatic();
|
||||
}else{
|
||||
if(build != null) op(OpType.rotation, (byte)build.rotation);
|
||||
if(build != null) op(OpType.team, (byte)build.team.id);
|
||||
op(OpType.block, block.id);
|
||||
|
||||
if(build != null) op(DrawOperation.opRotation, (byte)build.rotation);
|
||||
if(build != null) op(DrawOperation.opTeam, (byte)build.team.id);
|
||||
op(DrawOperation.opBlock, block.id);
|
||||
}
|
||||
|
||||
super.setBlock(type, team, rotation, entityprov);
|
||||
|
||||
if(requiresBlockUpdate(type) || requiresBlockUpdate(prev)){
|
||||
if(prev.size > 1){
|
||||
prevCenter.getLinkedTilesAs(prev, tile -> editor.renderer.updateBlock(tile));
|
||||
}
|
||||
getLinkedTiles(tile -> editor.renderer.updateBlock(tile));
|
||||
}else{
|
||||
renderer.blocks.updateShadowTile(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,11 +90,11 @@ public class EditorTile extends Tile{
|
||||
return;
|
||||
}
|
||||
|
||||
if(getTeamID() == team.id) return;
|
||||
op(OpType.team, (byte)getTeamID());
|
||||
if(getTeamID() == team.id || !synthetic()) return;
|
||||
op(DrawOperation.opTeam, (byte)getTeamID());
|
||||
super.setTeam(team);
|
||||
|
||||
getLinkedTiles(t -> editor.renderer.updatePoint(t.x, t.y));
|
||||
getLinkedTiles(t -> editor.renderer.updateBlock(t.x, t.y));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -95,8 +105,8 @@ public class EditorTile extends Tile{
|
||||
}
|
||||
|
||||
if(!floor.hasSurface() && overlay.asFloor().needsSurface && (overlay instanceof OreBlock || !floor.supportsOverlay)) return;
|
||||
if(overlay() == overlay) return;
|
||||
op(OpType.overlay, this.overlay.id);
|
||||
if(this.overlay == overlay) return;
|
||||
op(DrawOperation.opOverlay, this.overlay.id);
|
||||
super.setOverlay(overlay);
|
||||
}
|
||||
|
||||
@@ -105,7 +115,7 @@ public class EditorTile extends Tile{
|
||||
if(skip()){
|
||||
super.fireChanged();
|
||||
}else{
|
||||
update();
|
||||
updateStatic();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +124,7 @@ public class EditorTile extends Tile{
|
||||
if(skip()){
|
||||
super.firePreChanged();
|
||||
}else{
|
||||
update();
|
||||
updateStatic();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,15 +164,24 @@ public class EditorTile extends Tile{
|
||||
}
|
||||
}
|
||||
|
||||
private void update(){
|
||||
editor.renderer.updatePoint(x, y);
|
||||
@Override
|
||||
public boolean isDarkened(){
|
||||
return skip() && super.isDarkened();
|
||||
}
|
||||
|
||||
private boolean requiresBlockUpdate(Block block){
|
||||
return block != Blocks.air && block.cacheLayer == CacheLayer.normal;
|
||||
}
|
||||
|
||||
private void updateStatic(){
|
||||
editor.renderer.updateStatic(x, y);
|
||||
}
|
||||
|
||||
private boolean skip(){
|
||||
return state.isGame() || editor.isLoading() || world.isGenerating();
|
||||
}
|
||||
|
||||
private void op(OpType type, short value){
|
||||
editor.addTileOp(TileOp.get(x, y, (byte)type.ordinal(), value));
|
||||
private void op(int type, short value){
|
||||
editor.addTileOp(TileOp.get(x, y, (byte)type, value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ public enum EditorTool{
|
||||
Block dest = tile.floor();
|
||||
if(dest == editor.drawBlock) return;
|
||||
tester = t -> t.floor() == dest;
|
||||
setter = t -> t.setFloorUnder(editor.drawBlock.asFloor());
|
||||
setter = t -> t.setFloor(editor.drawBlock.asFloor());
|
||||
}else{
|
||||
Block dest = tile.block();
|
||||
if(dest == editor.drawBlock) return;
|
||||
|
||||
@@ -7,7 +7,6 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.editor.DrawOperation.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -149,6 +148,7 @@ public class MapEditor{
|
||||
y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1);
|
||||
if(!hasOverlap(x, y)){
|
||||
tile(x, y).setBlock(drawBlock, drawTeam, rotation);
|
||||
addTileOp(TileOp.get((short)x, (short)y, DrawOperation.opTeam, (byte)drawTeam.id));
|
||||
}
|
||||
}else{
|
||||
boolean isFloor = drawBlock.isFloor() && drawBlock != Blocks.air;
|
||||
@@ -166,10 +166,14 @@ public class MapEditor{
|
||||
}
|
||||
}else if(!(tile.block().isMultiblock() && !drawBlock.isMultiblock())){
|
||||
if(drawBlock.rotate && tile.build != null && tile.build.rotation != rotation){
|
||||
addTileOp(TileOp.get(tile.x, tile.y, (byte)OpType.rotation.ordinal(), (byte)rotation));
|
||||
addTileOp(TileOp.get(tile.x, tile.y, DrawOperation.opRotation, (byte)rotation));
|
||||
}
|
||||
|
||||
tile.setBlock(drawBlock, drawTeam, rotation);
|
||||
|
||||
if(drawBlock.synthetic()){
|
||||
addTileOp(TileOp.get(tile.x, tile.y, DrawOperation.opTeam, (byte)drawTeam.id));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -375,7 +379,7 @@ public class MapEditor{
|
||||
if(currentOp == null) currentOp = new DrawOperation();
|
||||
currentOp.addOperation(data);
|
||||
|
||||
renderer.updatePoint(TileOp.x(data), TileOp.y(data));
|
||||
renderer.updateStatic(TileOp.x(data), TileOp.y(data));
|
||||
}
|
||||
|
||||
class Context implements WorldContext{
|
||||
|
||||
@@ -29,6 +29,7 @@ import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -303,7 +304,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
state.rules = (lastSavedRules == null ? new Rules() : lastSavedRules);
|
||||
lastSavedRules = null;
|
||||
saved = false;
|
||||
editor.renderer.updateAll();
|
||||
editor.renderer.recache();
|
||||
}
|
||||
|
||||
private void editInGame(){
|
||||
@@ -323,9 +324,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
"width", editor.width(),
|
||||
"height", editor.height()
|
||||
));
|
||||
state.set(State.playing);
|
||||
world.endMapLoad();
|
||||
player.set(world.width() * tilesize/2f, world.height() * tilesize/2f);
|
||||
Core.camera.position.set(player);
|
||||
player.clearUnit();
|
||||
|
||||
for(var unit : Groups.unit){
|
||||
@@ -338,14 +338,17 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
Groups.weather.clear();
|
||||
logic.play();
|
||||
|
||||
if(player.team().core() == null){
|
||||
player.set(world.width() * tilesize/2f, world.height() * tilesize/2f);
|
||||
var unit = (state.rules.hasEnv(Env.scorching) ? UnitTypes.evoke : UnitTypes.alpha).spawn(player.team(), player.x, player.y);
|
||||
unit.spawnedByCore = true;
|
||||
player.unit(unit);
|
||||
}
|
||||
Point2 center = view.project(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f);
|
||||
|
||||
player.checkSpawn();
|
||||
CoreBuild best = player.bestCore();
|
||||
|
||||
player.set(center.x * tilesize, center.y * tilesize);
|
||||
var unit = (best != null ? ((CoreBlock)best.block).unitType : (state.rules.hasEnv(Env.scorching) ? UnitTypes.evoke : UnitTypes.alpha)).spawn(editor.drawTeam, player.x, player.y);
|
||||
unit.spawnedByCore = true;
|
||||
player.unit(unit);
|
||||
player.set(unit);
|
||||
|
||||
Core.camera.position.set(unit.x, unit.y);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -748,7 +751,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
//ctrl keys (undo, redo, save)
|
||||
if(Core.input.ctrl()){
|
||||
if(Core.input.keyTap(KeyCode.z)){
|
||||
editor.undo();
|
||||
if(Core.input.shift()){
|
||||
editor.redo();
|
||||
}else{
|
||||
editor.undo();
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(KeyCode.y)){
|
||||
@@ -827,7 +834,9 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
if(i == 0) editor.drawBlock = block;
|
||||
|
||||
if(++i % 6 == 0){
|
||||
int cols = mobile ? 4 : 6;
|
||||
|
||||
if(++i % cols == 0){
|
||||
blockSelection.row();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
//reset undo stack as generation... messes things up
|
||||
editor.renderer.updateAll();
|
||||
editor.renderer.recache();
|
||||
editor.clearOp();
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ public class MapProcessorsDialog extends BaseDialog{
|
||||
foundAny = true;
|
||||
tile.setNet(Blocks.worldProcessor, Team.sharded, 0);
|
||||
if(ui.editor.isShown()){
|
||||
Vars.editor.renderer.updatePoint(x, y);
|
||||
Vars.editor.renderer.updateStatic(x, y);
|
||||
}
|
||||
break outer;
|
||||
}
|
||||
|
||||
@@ -3,186 +3,220 @@ package mindustry.editor;
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MapRenderer implements Disposable{
|
||||
private static final int chunkSize = 64;
|
||||
private IndexedRenderer[][] chunks;
|
||||
private IntSet updates = new IntSet();
|
||||
private IntSet delayedUpdates = new IntSet();
|
||||
private TextureRegion clearEditor;
|
||||
private static final int chunkSize = 60;
|
||||
private static final Seq<Tile> tmpTiles = new Seq<>();
|
||||
|
||||
private EditorSpriteCache[][] chunks;
|
||||
private IntSet recacheChunks = new IntSet();
|
||||
private int width, height;
|
||||
|
||||
private Shader shader;
|
||||
|
||||
public void resize(int width, int height){
|
||||
updates.clear();
|
||||
delayedUpdates.clear();
|
||||
if(chunks != null){
|
||||
for(int x = 0; x < chunks.length; x++){
|
||||
for(int y = 0; y < chunks[0].length; y++){
|
||||
chunks[x][y].dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
dispose();
|
||||
|
||||
chunks = new IndexedRenderer[(int)Math.ceil((float)width / chunkSize)][(int)Math.ceil((float)height / chunkSize)];
|
||||
recacheChunks.clear();
|
||||
chunks = new EditorSpriteCache[(int)Math.ceil((float)width / chunkSize)][(int)Math.ceil((float)height / chunkSize)];
|
||||
|
||||
for(int x = 0; x < chunks.length; x++){
|
||||
for(int y = 0; y < chunks[0].length; y++){
|
||||
chunks[x][y] = new IndexedRenderer(chunkSize * chunkSize * 2);
|
||||
}
|
||||
}
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
updateAll();
|
||||
|
||||
recache();
|
||||
}
|
||||
|
||||
public void draw(float tx, float ty, float tw, float th){
|
||||
Draw.flush();
|
||||
clearEditor = Core.atlas.find("clear-editor");
|
||||
if(shader == null){
|
||||
shader = new Shader(
|
||||
"""
|
||||
attribute vec4 a_position;
|
||||
attribute vec4 a_color;
|
||||
attribute vec2 a_texCoord0;
|
||||
uniform mat4 u_projTrans;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_texCoords;
|
||||
void main(){
|
||||
v_color = a_color;
|
||||
v_color.a = v_color.a * (255.0/254.0);
|
||||
v_texCoords = a_texCoord0;
|
||||
gl_Position = u_projTrans * a_position;
|
||||
}
|
||||
""",
|
||||
|
||||
updates.each(i -> render(i % width, i / width));
|
||||
updates.clear();
|
||||
|
||||
updates.addAll(delayedUpdates);
|
||||
delayedUpdates.clear();
|
||||
|
||||
//????
|
||||
if(chunks == null){
|
||||
return;
|
||||
"""
|
||||
varying lowp vec4 v_color;
|
||||
varying vec2 v_texCoords;
|
||||
uniform sampler2D u_texture;
|
||||
|
||||
void main(){
|
||||
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
|
||||
}
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
var texture = Core.atlas.find("clear-editor").texture;
|
||||
Draw.flush();
|
||||
|
||||
//don't process terrain updates every frame (helps with lag on low end devices)
|
||||
boolean doUpdate = Core.graphics.getFrameId() % 2 == 0;
|
||||
|
||||
if(doUpdate) renderer.blocks.floor.checkChanges();
|
||||
|
||||
boolean prev = renderer.animateWater;
|
||||
renderer.animateWater = false;
|
||||
|
||||
Tmp.v3.set(Core.camera.position);
|
||||
Core.camera.position.set(world.width()/2f * tilesize, world.height()/2f * tilesize);
|
||||
Core.camera.width = 999999f;
|
||||
Core.camera.height = 999999f;
|
||||
Core.camera.mat.set(Draw.proj()).mul(Tmp.m3.setToTranslation(tx, ty).scale(tw / (width * tilesize), th / (height * tilesize)).translate(4f, 4f));
|
||||
renderer.blocks.floor.drawFloor();
|
||||
|
||||
Tmp.m2.set(Draw.proj());
|
||||
|
||||
//scissors are always enabled because this is drawn clipped in UI, make sure they don't interfere with drawing shadow events
|
||||
Gl.disable(Gl.scissorTest);
|
||||
|
||||
if(doUpdate) renderer.blocks.processShadows();
|
||||
|
||||
Gl.enable(Gl.scissorTest);
|
||||
|
||||
Draw.proj(Core.camera.mat);
|
||||
|
||||
Draw.shader(Shaders.darkness);
|
||||
Draw.rect(Draw.wrap(renderer.blocks.getShadowBuffer().getTexture()), world.width() * tilesize/2f - tilesize/2f, world.height() * tilesize/2f - tilesize/2f, world.width() * tilesize, -world.height() * tilesize);
|
||||
Draw.shader();
|
||||
|
||||
Draw.proj(Tmp.m2);
|
||||
|
||||
renderer.blocks.floor.beginDraw();
|
||||
renderer.blocks.floor.drawLayer(CacheLayer.walls);
|
||||
renderer.animateWater = prev;
|
||||
|
||||
if(chunks == null) return;
|
||||
|
||||
if(doUpdate){
|
||||
recacheChunks.each(i -> recacheChunk(Point2.x(i), Point2.y(i)));
|
||||
recacheChunks.clear();
|
||||
}
|
||||
|
||||
shader.bind();
|
||||
shader.setUniformMatrix4("u_projTrans", Core.camera.mat);
|
||||
|
||||
for(int x = 0; x < chunks.length; x++){
|
||||
for(int y = 0; y < chunks[0].length; y++){
|
||||
IndexedRenderer mesh = chunks[x][y];
|
||||
EditorSpriteCache mesh = chunks[x][y];
|
||||
|
||||
if(mesh == null){
|
||||
continue;
|
||||
if(mesh == null) continue;
|
||||
|
||||
mesh.render(shader);
|
||||
}
|
||||
}
|
||||
|
||||
Core.camera.position.set(Tmp.v3);
|
||||
}
|
||||
|
||||
void updateStatic(int x, int y){
|
||||
renderer.blocks.floor.recacheTile(x, y);
|
||||
if(x > 0) renderer.blocks.floor.recacheTile(x - 1, y);
|
||||
if(y > 0) renderer.blocks.floor.recacheTile(x, y - 1);
|
||||
if(x < world.width() - 1) renderer.blocks.floor.recacheTile(x + 1, y);
|
||||
if(y < world.height() - 1) renderer.blocks.floor.recacheTile(x, y + 1);
|
||||
}
|
||||
|
||||
void updateBlock(Tile tile){
|
||||
updateBlock(tile.x, tile.y);
|
||||
renderer.blocks.updateShadowTile(tile);
|
||||
}
|
||||
|
||||
void updateBlock(int x, int y){
|
||||
recacheChunks.add(Point2.pack(x / chunkSize, y / chunkSize));
|
||||
}
|
||||
|
||||
void recache(){
|
||||
renderer.blocks.floor.clearTiles();
|
||||
renderer.blocks.reload();
|
||||
|
||||
for(int x = 0; x < chunks.length; x++){
|
||||
for(int y = 0; y < chunks[0].length; y++){
|
||||
recacheChunk(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void recacheChunk(int cx, int cy){
|
||||
if(chunks[cx][cy] != null){
|
||||
chunks[cx][cy].dispose();
|
||||
chunks[cx][cy] = null;
|
||||
}
|
||||
|
||||
EditorSpriteCache cache = new EditorSpriteCache(renderer.blocks.floor.getVertexBuffer());
|
||||
|
||||
TextureRegion teamRegion = Core.atlas.find("block-border");
|
||||
|
||||
tmpTiles.clear();
|
||||
|
||||
for(int x = cx * chunkSize; x < (cx + 1) * chunkSize; x++){
|
||||
for(int y = cy * chunkSize; y < (cy + 1) * chunkSize; y++){
|
||||
Tile tile = world.tile(x, y);
|
||||
|
||||
if(tile != null && tile.block() != Blocks.air && tile.block().cacheLayer == CacheLayer.normal && tile.isCenter()){
|
||||
tmpTiles.add(tile);
|
||||
}
|
||||
|
||||
mesh.getTransformMatrix().setToTranslation(tx, ty).scale(tw / (width * tilesize), th / (height * tilesize));
|
||||
mesh.setProjectionMatrix(Draw.proj());
|
||||
|
||||
mesh.render(texture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updatePoint(int x, int y){
|
||||
updates.add(x + y * width);
|
||||
}
|
||||
tmpTiles.sort(Structs.comparingBool(b -> !b.block().synthetic()));
|
||||
|
||||
public void updateAll(){
|
||||
clearEditor = Core.atlas.find("clear-editor");
|
||||
for(int x = 0; x < width; x++){
|
||||
for(int y = 0; y < height; y++){
|
||||
render(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
for(Tile tile : tmpTiles){
|
||||
int x = tile.x, y = tile.y;
|
||||
Block block = tile.block();
|
||||
|
||||
private TextureRegion getIcon(Block wall, int index){
|
||||
return !wall.editorIcon().found() ?
|
||||
clearEditor : wall.variants > 0 ?
|
||||
wall.editorVariantRegions()[Mathf.randomSeed(index, 0, wall.editorVariantRegions().length - 1)] :
|
||||
wall.editorIcon();
|
||||
}
|
||||
TextureRegion region = block.fullIcon;
|
||||
|
||||
private void render(int wx, int wy){
|
||||
int x = wx / chunkSize, y = wy / chunkSize;
|
||||
if(x >= chunks.length || y >= chunks[0].length) return;
|
||||
IndexedRenderer mesh = chunks[x][y];
|
||||
Tile tile = editor.tiles().getn(wx, wy);
|
||||
float width = region.width * region.scl(), height = region.height * region.scl();
|
||||
|
||||
Team team = tile.team();
|
||||
Floor floor = tile.floor();
|
||||
Floor overlay = tile.overlay();
|
||||
Block wall = tile.block();
|
||||
|
||||
TextureRegion region;
|
||||
|
||||
int idxWall = (wx % chunkSize) + (wy % chunkSize) * chunkSize;
|
||||
int idxDecal = (wx % chunkSize) + (wy % chunkSize) * chunkSize + chunkSize * chunkSize;
|
||||
boolean center = tile.isCenter();
|
||||
boolean useSyntheticWall = wall.synthetic() || overlay.wallOre;
|
||||
|
||||
//draw synthetic wall or floor OR standard wall if wall ore
|
||||
if(wall != Blocks.air && useSyntheticWall){
|
||||
region = !center ? clearEditor : getIcon(wall, idxWall);
|
||||
|
||||
float width = region.width * region.scl(), height = region.height * region.scl(), ox = wall.offset + (tilesize - width) / 2f, oy = wall.offset + (tilesize - height) / 2f;
|
||||
|
||||
//force fit to tile
|
||||
if(overlay.wallOre && !wall.synthetic()){
|
||||
width = height = tilesize;
|
||||
ox = oy = 0f;
|
||||
}
|
||||
|
||||
mesh.draw(idxWall, region,
|
||||
wx * tilesize + ox,
|
||||
wy * tilesize + oy,
|
||||
cache.draw(block.fullIcon,
|
||||
x * tilesize + block.offset - width / 2f,
|
||||
y * tilesize + block.offset - height / 2f,
|
||||
width/2f, height/2f,
|
||||
width, height,
|
||||
tile.build == null || !wall.rotate ? 0 : tile.build.rotdeg());
|
||||
}else{
|
||||
region = floor.editorVariantRegions()[Mathf.randomSeed(idxWall, 0, floor.editorVariantRegions().length - 1)];
|
||||
tile.build == null || !block.rotate ? 0 : tile.build.rotdeg(),
|
||||
Color.whiteFloatBits);
|
||||
|
||||
mesh.draw(idxWall, region, wx * tilesize, wy * tilesize, 8, 8);
|
||||
if(tile.build != null){
|
||||
cache.draw(teamRegion,
|
||||
x * tilesize + block.offset - width / 2f,
|
||||
y * tilesize + block.offset - height / 2f,
|
||||
0f, 0f,
|
||||
teamRegion.width * teamRegion.scl(), teamRegion.height * teamRegion.scl(),
|
||||
0f,
|
||||
tile.build.team.color.toFloatBits());
|
||||
}
|
||||
}
|
||||
|
||||
float offsetX = -((wall.size + 1) / 3) * tilesize, offsetY = -((wall.size + 1) / 3) * tilesize;
|
||||
tmpTiles.clear();
|
||||
|
||||
//draw non-synthetic wall or ore
|
||||
if((wall.update || wall.destructible) && center){
|
||||
mesh.setColor(team.color);
|
||||
region = Core.atlas.find("block-border-editor");
|
||||
if(wall.size == 2){
|
||||
offsetX += tilesize;
|
||||
offsetY += tilesize;
|
||||
}
|
||||
}else if(!useSyntheticWall && wall != Blocks.air && center){
|
||||
region = getIcon(wall, idxWall);
|
||||
|
||||
if(wall == Blocks.cliff){
|
||||
mesh.setColor(Tmp.c1.set(floor.mapColor).mul(1.6f));
|
||||
region = ((Cliff)Blocks.cliff).editorCliffs[tile.data & 0xff];
|
||||
}
|
||||
|
||||
offsetX = tilesize / 2f - region.width * region.scl() / 2f;
|
||||
offsetY = tilesize / 2f - region.height * region.scl() / 2f;
|
||||
}else if((wall == Blocks.air || overlay.wallOre) && !overlay.isAir()){
|
||||
if(floor.isLiquid){
|
||||
mesh.setColor(Tmp.c1.set(1f, 1f, 1f, floor.overlayAlpha));
|
||||
}
|
||||
region = overlay.editorVariantRegions()[Mathf.randomSeed(idxWall, 0, tile.overlay().editorVariantRegions().length - 1)];
|
||||
}else{
|
||||
region = clearEditor;
|
||||
if(!cache.isEmpty()){
|
||||
cache.build(renderer.blocks.floor.getIndexData());
|
||||
chunks[cx][cy] = cache;
|
||||
}
|
||||
|
||||
float width = region.width * region.scl(), height = region.height * region.scl();
|
||||
if(!wall.synthetic() && wall != Blocks.air && !wall.isMultiblock()){
|
||||
offsetX = offsetY = 0f;
|
||||
width = height = tilesize;
|
||||
}
|
||||
|
||||
mesh.draw(idxDecal, region, wx * tilesize + offsetX, wy * tilesize + offsetY, width, height);
|
||||
mesh.setColor(Color.white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
if(chunks == null){
|
||||
return;
|
||||
}
|
||||
if(chunks == null) return;
|
||||
|
||||
for(int x = 0; x < chunks.length; x++){
|
||||
for(int y = 0; y < chunks[0].length; y++){
|
||||
if(chunks[x][y] != null){
|
||||
|
||||
@@ -11,6 +11,7 @@ import arc.scene.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.ui.*;
|
||||
@@ -18,7 +19,7 @@ import mindustry.ui.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MapView extends Element implements GestureListener{
|
||||
EditorTool tool = EditorTool.pencil;
|
||||
EditorTool tool = Vars.mobile ? EditorTool.zoom : EditorTool.pencil;
|
||||
private float offsetx, offsety;
|
||||
private float zoom = 1f;
|
||||
private boolean grid = false;
|
||||
@@ -204,7 +205,7 @@ public class MapView extends Element implements GestureListener{
|
||||
zoom = Mathf.clamp(zoom, 0.2f, 20f);
|
||||
}
|
||||
|
||||
Point2 project(float x, float y){
|
||||
public Point2 project(float x, float y){
|
||||
float ratio = 1f / ((float)editor.width() / editor.height());
|
||||
float size = Math.min(width, height);
|
||||
float sclwidth = size * zoom;
|
||||
|
||||
@@ -145,7 +145,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
!Structs.contains(current.block.requirements, i -> !core.items.has(i.item, Math.min(Mathf.round(i.amount * state.rules.buildCostMultiplier), 1)));
|
||||
|
||||
if(hasAll){
|
||||
Call.beginPlace(self(), current.block, team, current.x, current.y, current.rotation);
|
||||
Call.beginPlace(self(), current.block, team, current.x, current.y, current.rotation, current.block.instantBuild ? current.config : null);
|
||||
|
||||
if(!net.client() && current.block.instantBuild){
|
||||
if(plans.size > 0){
|
||||
|
||||
@@ -64,48 +64,7 @@ public class BlockRenderer{
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
blockTree = new BlockQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
|
||||
blockLightTree = new BlockLightQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
|
||||
floorTree = new FloorQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
|
||||
|
||||
shadowEvents.clear();
|
||||
updateFloors.clear();
|
||||
lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated
|
||||
hadMapLimit = state.rules.limitMapArea;
|
||||
|
||||
shadows.getTexture().setFilter(TextureFilter.linear, TextureFilter.linear);
|
||||
shadows.resize(world.width(), world.height());
|
||||
shadows.begin();
|
||||
Core.graphics.clear(Color.white);
|
||||
Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight());
|
||||
|
||||
Draw.color(blendShadowColor);
|
||||
|
||||
for(Tile tile : world.tiles){
|
||||
recordIndex(tile);
|
||||
|
||||
if(tile.floor().updateRender(tile)){
|
||||
updateFloors.add(new UpdateRenderState(tile, tile.floor()));
|
||||
}
|
||||
|
||||
if(tile.overlay().updateRender(tile)){
|
||||
updateFloors.add(new UpdateRenderState(tile, tile.overlay()));
|
||||
}
|
||||
|
||||
if(tile.build != null && (tile.team() == player.team() || !state.rules.fog || (tile.build.visibleFlags & (1L << player.team().id)) != 0)){
|
||||
tile.build.wasVisible = true;
|
||||
}
|
||||
|
||||
if(tile.block().displayShadow(tile) && (tile.build == null || tile.build.wasVisible)){
|
||||
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.flush();
|
||||
Draw.color();
|
||||
shadows.end();
|
||||
|
||||
updateDarkness();
|
||||
reload();
|
||||
});
|
||||
|
||||
//sometimes darkness gets disabled.
|
||||
@@ -150,6 +109,51 @@ public class BlockRenderer{
|
||||
});
|
||||
}
|
||||
|
||||
public void reload(){
|
||||
blockTree = new BlockQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
|
||||
blockLightTree = new BlockLightQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
|
||||
floorTree = new FloorQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
|
||||
|
||||
shadowEvents.clear();
|
||||
updateFloors.clear();
|
||||
lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated
|
||||
hadMapLimit = state.rules.limitMapArea;
|
||||
|
||||
shadows.getTexture().setFilter(TextureFilter.linear, TextureFilter.linear);
|
||||
shadows.resize(world.width(), world.height());
|
||||
shadows.begin();
|
||||
Core.graphics.clear(Color.white);
|
||||
Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight());
|
||||
|
||||
Draw.color(blendShadowColor);
|
||||
|
||||
for(Tile tile : world.tiles){
|
||||
recordIndex(tile);
|
||||
|
||||
if(tile.floor().updateRender(tile)){
|
||||
updateFloors.add(new UpdateRenderState(tile, tile.floor()));
|
||||
}
|
||||
|
||||
if(tile.overlay().updateRender(tile)){
|
||||
updateFloors.add(new UpdateRenderState(tile, tile.overlay()));
|
||||
}
|
||||
|
||||
if(tile.build != null && (tile.team() == player.team() || !state.rules.fog || (tile.build.visibleFlags & (1L << player.team().id)) != 0)){
|
||||
tile.build.wasVisible = true;
|
||||
}
|
||||
|
||||
if(tile.block().displayShadow(tile) && (tile.build == null || tile.build.wasVisible)){
|
||||
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.flush();
|
||||
Draw.color();
|
||||
shadows.end();
|
||||
|
||||
updateDarkness();
|
||||
}
|
||||
|
||||
public void updateDarkness(){
|
||||
darkEvents.clear();
|
||||
dark.getTexture().setFilter(TextureFilter.linear);
|
||||
@@ -197,6 +201,10 @@ public class BlockRenderer{
|
||||
}
|
||||
}
|
||||
|
||||
public FrameBuffer getShadowBuffer(){
|
||||
return shadows;
|
||||
}
|
||||
|
||||
public void removeFloorIndex(Tile tile){
|
||||
if(indexFloor(tile)) floorTree.remove(tile);
|
||||
}
|
||||
@@ -294,7 +302,7 @@ public class BlockRenderer{
|
||||
}
|
||||
}
|
||||
|
||||
public void drawShadows(){
|
||||
public void processShadows(){
|
||||
if(!shadowEvents.isEmpty()){
|
||||
Draw.flush();
|
||||
|
||||
@@ -315,6 +323,10 @@ public class BlockRenderer{
|
||||
|
||||
Draw.proj(camera);
|
||||
}
|
||||
}
|
||||
|
||||
public void drawShadows(){
|
||||
processShadows();
|
||||
|
||||
float ww = world.width() * tilesize, wh = world.height() * tilesize;
|
||||
float x = camera.position.x + tilesize / 2f, y = camera.position.y + tilesize / 2f;
|
||||
@@ -511,6 +523,10 @@ public class BlockRenderer{
|
||||
}
|
||||
}
|
||||
|
||||
public void updateShadowTile(Tile tile){
|
||||
shadowEvents.add(tile);
|
||||
}
|
||||
|
||||
static class BlockQuadtree extends QuadTree<Tile>{
|
||||
|
||||
public BlockQuadtree(Rect bounds){
|
||||
|
||||
@@ -97,20 +97,20 @@ public class CacheLayer{
|
||||
|
||||
@Override
|
||||
public void begin(){
|
||||
if(!Core.settings.getBool("animatedwater")) return;
|
||||
if(!renderer.animateWater) return;
|
||||
|
||||
renderer.effectBuffer.begin();
|
||||
Core.graphics.clear(Color.clear);
|
||||
renderer.blocks.floor.beginc();
|
||||
renderer.blocks.floor.beginDraw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end(){
|
||||
if(!Core.settings.getBool("animatedwater")) return;
|
||||
if(!renderer.animateWater) return;
|
||||
|
||||
renderer.effectBuffer.end();
|
||||
renderer.effectBuffer.blit(shader);
|
||||
renderer.blocks.floor.beginc();
|
||||
renderer.blocks.floor.beginDraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,10 +111,21 @@ public class FloorRenderer{
|
||||
Events.on(WorldLoadEvent.class, event -> clearTiles());
|
||||
}
|
||||
|
||||
public IndexData getIndexData(){
|
||||
return indexData;
|
||||
}
|
||||
|
||||
public float[] getVertexBuffer(){
|
||||
return vertices;
|
||||
}
|
||||
|
||||
/** Queues up a cache change for a tile. Only runs in render loop. */
|
||||
public void recacheTile(Tile tile){
|
||||
//recaching all layers may not be necessary
|
||||
recacheSet.add(Point2.pack(tile.x / chunksize, tile.y / chunksize));
|
||||
recacheTile(tile.x, tile.y);
|
||||
}
|
||||
|
||||
public void recacheTile(int x, int y){
|
||||
recacheSet.add(Point2.pack(x / chunksize, y / chunksize));
|
||||
}
|
||||
|
||||
public void drawFloor(){
|
||||
@@ -127,10 +138,10 @@ public class FloorRenderer{
|
||||
float pad = tilesize/2f;
|
||||
|
||||
int
|
||||
minx = (int)((camera.position.x - camera.width/2f - pad) / chunkunits),
|
||||
miny = (int)((camera.position.y - camera.height/2f - pad) / chunkunits),
|
||||
maxx = Mathf.ceil((camera.position.x + camera.width/2f + pad) / chunkunits),
|
||||
maxy = Mathf.ceil((camera.position.y + camera.height/2f + pad) / chunkunits);
|
||||
minx = Math.max((int)((camera.position.x - camera.width/2f - pad) / chunkunits), 0),
|
||||
miny = Math.max((int)((camera.position.y - camera.height/2f - pad) / chunkunits), 0),
|
||||
maxx = Math.min(Mathf.ceil((camera.position.x + camera.width/2f + pad) / chunkunits), cache.length),
|
||||
maxy = Math.min(Mathf.ceil((camera.position.y + camera.height/2f + pad) / chunkunits), cache[0].length);
|
||||
|
||||
int layers = CacheLayer.all.length;
|
||||
|
||||
@@ -176,14 +187,6 @@ public class FloorRenderer{
|
||||
underwaterDraw.clear();
|
||||
}
|
||||
|
||||
public void beginc(){
|
||||
shader.bind();
|
||||
shader.setUniformMatrix4("u_projectionViewMatrix", Core.camera.mat);
|
||||
|
||||
//only ever use the base environment texture
|
||||
texture.bind(0);
|
||||
}
|
||||
|
||||
public void checkChanges(){
|
||||
if(recacheSet.size > 0){
|
||||
//recache one chunk at a time
|
||||
@@ -208,7 +211,11 @@ public class FloorRenderer{
|
||||
|
||||
Draw.flush();
|
||||
|
||||
beginc();
|
||||
shader.bind();
|
||||
shader.setUniformMatrix4("u_projectionViewMatrix", Core.camera.mat);
|
||||
|
||||
//only ever use the base environment texture
|
||||
texture.bind(0);
|
||||
|
||||
Gl.enable(Gl.blend);
|
||||
}
|
||||
@@ -221,10 +228,10 @@ public class FloorRenderer{
|
||||
Camera camera = Core.camera;
|
||||
|
||||
int
|
||||
minx = (int)((camera.position.x - camera.width/2f - pad) / chunkunits),
|
||||
miny = (int)((camera.position.y - camera.height/2f - pad) / chunkunits),
|
||||
maxx = Mathf.ceil((camera.position.x + camera.width/2f + pad) / chunkunits),
|
||||
maxy = Mathf.ceil((camera.position.y + camera.height/2f + pad) / chunkunits);
|
||||
minx = Math.max((int)((camera.position.x - camera.width/2f - pad) / chunkunits), 0),
|
||||
miny = Math.max((int)((camera.position.y - camera.height/2f - pad) / chunkunits), 0),
|
||||
maxx = Math.min(Mathf.ceil((camera.position.x + camera.width/2f + pad) / chunkunits), cache.length),
|
||||
maxy = Math.min(Mathf.ceil((camera.position.y + camera.height/2f + pad) / chunkunits), cache[0].length);
|
||||
|
||||
layer.begin();
|
||||
|
||||
@@ -336,7 +343,7 @@ public class FloorRenderer{
|
||||
(cx+1) * tilesize * chunksize + tilesize/2f, (cy+1) * tilesize * chunksize + tilesize/2f);
|
||||
|
||||
mesh.setVertices(vertices, 0, vidx);
|
||||
//all vertices are shared
|
||||
//all indices are shared and identical
|
||||
mesh.indices = indexData;
|
||||
|
||||
return mesh;
|
||||
@@ -514,7 +521,14 @@ public class FloorRenderer{
|
||||
|
||||
@Override
|
||||
protected void draw(Texture texture, float[] spriteVertices, int offset, int count){
|
||||
throw new IllegalArgumentException("cache vertices unsupported");
|
||||
if(spriteVertices.length != spriteSize){
|
||||
throw new IllegalArgumentException("cached vertices must be in non-mixcolor format (20 per sprite, 5 per vertex)");
|
||||
}
|
||||
|
||||
float[] verts = vertices;
|
||||
int idx = vidx;
|
||||
System.arraycopy(spriteVertices, offset, verts, idx, spriteSize);
|
||||
vidx += spriteSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
package mindustry.graphics;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
|
||||
public class IndexedRenderer implements Disposable{
|
||||
private static final int vsize = 5;
|
||||
|
||||
private final static Shader program = new Shader(
|
||||
"""
|
||||
attribute vec4 a_position;
|
||||
attribute vec4 a_color;
|
||||
attribute vec2 a_texCoord0;
|
||||
uniform mat4 u_projTrans;
|
||||
varying vec4 v_color;
|
||||
varying vec2 v_texCoords;
|
||||
void main(){
|
||||
v_color = a_color;
|
||||
v_color.a = v_color.a * (255.0/254.0);
|
||||
v_texCoords = a_texCoord0;
|
||||
gl_Position = u_projTrans * a_position;
|
||||
}
|
||||
""",
|
||||
|
||||
"""
|
||||
varying lowp vec4 v_color;
|
||||
varying vec2 v_texCoords;
|
||||
uniform sampler2D u_texture;
|
||||
void main(){
|
||||
gl_FragColor = v_color * texture2D(u_texture, v_texCoords);
|
||||
}
|
||||
"""
|
||||
);
|
||||
private static final float[] tmpVerts = new float[vsize * 6];
|
||||
|
||||
private Mesh mesh;
|
||||
|
||||
private Mat projMatrix = new Mat();
|
||||
private Mat transMatrix = new Mat();
|
||||
private Mat combined = new Mat();
|
||||
private float color = Color.white.toFloatBits();
|
||||
|
||||
public IndexedRenderer(int sprites){
|
||||
resize(sprites);
|
||||
}
|
||||
|
||||
public void render(Texture texture){
|
||||
Gl.enable(Gl.blend);
|
||||
|
||||
updateMatrix();
|
||||
|
||||
program.bind();
|
||||
texture.bind();
|
||||
|
||||
program.setUniformMatrix4("u_projTrans", combined);
|
||||
|
||||
mesh.render(program, Gl.triangles, 0, mesh.getMaxVertices());
|
||||
}
|
||||
|
||||
public void setColor(Color color){
|
||||
this.color = color.toFloatBits();
|
||||
}
|
||||
|
||||
public void draw(int index, TextureRegion region, float x, float y, float w, float h){
|
||||
float fx2 = x + w;
|
||||
float fy2 = y + h;
|
||||
float u = region.u;
|
||||
float v = region.v2;
|
||||
float u2 = region.u2;
|
||||
float v2 = region.v;
|
||||
|
||||
float[] vertices = tmpVerts;
|
||||
float color = this.color;
|
||||
|
||||
int idx = 0;
|
||||
vertices[idx++] = x;
|
||||
vertices[idx++] = y;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u;
|
||||
vertices[idx++] = v;
|
||||
|
||||
vertices[idx++] = x;
|
||||
vertices[idx++] = fy2;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u;
|
||||
vertices[idx++] = v2;
|
||||
|
||||
vertices[idx++] = fx2;
|
||||
vertices[idx++] = fy2;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u2;
|
||||
vertices[idx++] = v2;
|
||||
|
||||
//tri2
|
||||
vertices[idx++] = fx2;
|
||||
vertices[idx++] = fy2;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u2;
|
||||
vertices[idx++] = v2;
|
||||
|
||||
vertices[idx++] = fx2;
|
||||
vertices[idx++] = y;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u2;
|
||||
vertices[idx++] = v;
|
||||
|
||||
vertices[idx++] = x;
|
||||
vertices[idx++] = y;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u;
|
||||
vertices[idx++] = v;
|
||||
|
||||
mesh.updateVertices(index * vsize * 6, vertices);
|
||||
}
|
||||
|
||||
public void draw(int index, TextureRegion region, float x, float y, float w, float h, float rotation){
|
||||
float u = region.u;
|
||||
float v = region.v2;
|
||||
float u2 = region.u2;
|
||||
float v2 = region.v;
|
||||
|
||||
float originX = w / 2, originY = h / 2;
|
||||
|
||||
float cos = Mathf.cosDeg(rotation);
|
||||
float sin = Mathf.sinDeg(rotation);
|
||||
|
||||
float fx = -originX;
|
||||
float fy = -originY;
|
||||
float fx2 = w - originX;
|
||||
float fy2 = h - originY;
|
||||
|
||||
float worldOriginX = x + originX;
|
||||
float worldOriginY = y + originY;
|
||||
|
||||
float x1 = cos * fx - sin * fy + worldOriginX;
|
||||
float y1 = sin * fx + cos * fy + worldOriginY;
|
||||
float x2 = cos * fx - sin * fy2 + worldOriginX;
|
||||
float y2 = sin * fx + cos * fy2 + worldOriginY;
|
||||
float x3 = cos * fx2 - sin * fy2 + worldOriginX;
|
||||
float y3 = sin * fx2 + cos * fy2 + worldOriginY;
|
||||
float x4 = x1 + (x3 - x2);
|
||||
float y4 = y3 - (y2 - y1);
|
||||
|
||||
float[] vertices = tmpVerts;
|
||||
float color = this.color;
|
||||
|
||||
int idx = 0;
|
||||
vertices[idx++] = x1;
|
||||
vertices[idx++] = y1;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u;
|
||||
vertices[idx++] = v;
|
||||
|
||||
vertices[idx++] = x2;
|
||||
vertices[idx++] = y2;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u;
|
||||
vertices[idx++] = v2;
|
||||
|
||||
vertices[idx++] = x3;
|
||||
vertices[idx++] = y3;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u2;
|
||||
vertices[idx++] = v2;
|
||||
|
||||
//tri2
|
||||
vertices[idx++] = x3;
|
||||
vertices[idx++] = y3;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u2;
|
||||
vertices[idx++] = v2;
|
||||
|
||||
vertices[idx++] = x4;
|
||||
vertices[idx++] = y4;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u2;
|
||||
vertices[idx++] = v;
|
||||
|
||||
vertices[idx++] = x1;
|
||||
vertices[idx++] = y1;
|
||||
vertices[idx++] = color;
|
||||
vertices[idx++] = u;
|
||||
vertices[idx++] = v;
|
||||
|
||||
mesh.updateVertices(index * vsize * 6, vertices);
|
||||
}
|
||||
|
||||
public Mat getTransformMatrix(){
|
||||
return transMatrix;
|
||||
}
|
||||
|
||||
public void setProjectionMatrix(Mat matrix){
|
||||
projMatrix = matrix;
|
||||
}
|
||||
|
||||
public void resize(int sprites){
|
||||
if(mesh != null) mesh.dispose();
|
||||
|
||||
mesh = new Mesh(true, 6 * sprites, 0,
|
||||
VertexAttribute.position,
|
||||
VertexAttribute.color,
|
||||
VertexAttribute.texCoords);
|
||||
|
||||
//TODO why is this the only way to get it working properly? it should not need an array
|
||||
mesh.setVertices(new float[6 * sprites * vsize]);
|
||||
}
|
||||
|
||||
private void updateMatrix(){
|
||||
combined.set(projMatrix).mul(transMatrix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
mesh.dispose();
|
||||
}
|
||||
}
|
||||
@@ -360,6 +360,7 @@ public class MinimapRenderer{
|
||||
if(tile == null) return 0;
|
||||
Block real = realBlock(tile);
|
||||
int bc = real.minimapColor(tile);
|
||||
if(bc == 0 && tile.block() == Blocks.air && tile.overlay() == Blocks.air) bc = tile.floor().minimapColor(tile);
|
||||
|
||||
Color color = Tmp.c1.set(bc == 0 ? MapIO.colorFor(real, tile.floor(), tile.overlay(), tile.team()) : bc);
|
||||
color.mul(1f - Mathf.clamp(world.getDarkness(tile.x, tile.y) / 4f));
|
||||
|
||||
@@ -118,8 +118,7 @@ public class MultiPacker implements Disposable{
|
||||
|
||||
environment(4096),
|
||||
ui(4096),
|
||||
rubble(4096, 2048),
|
||||
editor(4096, 2048);
|
||||
rubble(4096, 2048);
|
||||
|
||||
public static final PageType[] all = values();
|
||||
|
||||
|
||||
@@ -1627,6 +1627,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
/** Draws a placement icon for a specific block. */
|
||||
protected void drawPlan(int x, int y, Block block, int rotation){
|
||||
bplan.set(x, y, rotation, block);
|
||||
if(block.saveConfig){
|
||||
bplan.config = block.lastConfig;
|
||||
}
|
||||
bplan.animScale = 1f;
|
||||
block.drawPlan(bplan, allPlans(), validPlace(x, y, block, rotation));
|
||||
}
|
||||
|
||||
@@ -141,6 +141,23 @@ public class MapIO{
|
||||
}
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReadTileData(){
|
||||
//colored floor/wall tile data will affect the map preview
|
||||
|
||||
if(!tile.block().synthetic() && tile.block() != Blocks.air){
|
||||
int color = tile.block().minimapColor(tile);
|
||||
if(color != 0){
|
||||
walls.set(tile.x, walls.height - 1 - tile.y, color);
|
||||
}
|
||||
}else if(tile.overlay() == Blocks.air && tile.block() == Blocks.air){
|
||||
int color = tile.floor().minimapColor(tile);
|
||||
if(color != 0){
|
||||
floors.set(tile.x, floors.height - 1 - tile.y, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
floors.draw(walls, true);
|
||||
@@ -156,7 +173,14 @@ public class MapIO{
|
||||
for(int x = 0; x < pixmap.width; x++){
|
||||
for(int y = 0; y < pixmap.height; y++){
|
||||
Tile tile = tiles.getn(x, y);
|
||||
pixmap.set(x, pixmap.height - 1 - y, colorFor(tile.block(), tile.floor(), tile.overlay(), tile.team()));
|
||||
int color = 0;
|
||||
if(!tile.block().synthetic() && tile.block() != Blocks.air){
|
||||
color = tile.block().minimapColor(tile);
|
||||
}else if(tile.overlay() == Blocks.air && tile.block() == Blocks.air){
|
||||
color = tile.floor().minimapColor(tile);
|
||||
}
|
||||
if(color == 0) color = colorFor(tile.block(), tile.floor(), tile.overlay(), tile.team());
|
||||
pixmap.set(x, pixmap.height - 1 - y, color);
|
||||
}
|
||||
}
|
||||
return pixmap;
|
||||
@@ -202,7 +226,7 @@ public class MapIO{
|
||||
for(Tile tile : tiles){
|
||||
//default to stone floor
|
||||
if(tile.floor() == Blocks.air){
|
||||
tile.setFloorUnder((Floor)Blocks.stone);
|
||||
tile.setFloor((Floor)Blocks.stone);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class SaveIO{
|
||||
/** Save format header. */
|
||||
public static final byte[] header = {'M', 'S', 'A', 'V'};
|
||||
public static final IntMap<SaveVersion> versions = new IntMap<>();
|
||||
public static final Seq<SaveVersion> versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8());
|
||||
public static final Seq<SaveVersion> versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8(), new Save9());
|
||||
|
||||
static{
|
||||
for(SaveVersion version : versionArray){
|
||||
|
||||
@@ -209,7 +209,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
|
||||
//floor + overlay
|
||||
for(int i = 0; i < world.width() * world.height(); i++){
|
||||
Tile tile = world.rawTile(i % world.width(), i / world.width());
|
||||
Tile tile = world.tiles.geti(i);
|
||||
stream.writeShort(tile.floorID());
|
||||
stream.writeShort(tile.overlayID());
|
||||
int consecutives = 0;
|
||||
@@ -230,16 +230,26 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
|
||||
//blocks
|
||||
for(int i = 0; i < world.width() * world.height(); i++){
|
||||
Tile tile = world.rawTile(i % world.width(), i / world.width());
|
||||
Tile tile = world.tiles.geti(i);
|
||||
stream.writeShort(tile.blockID());
|
||||
|
||||
boolean savedata = tile.floor().saveData || tile.overlay().saveData || tile.block().saveData;
|
||||
boolean savedata = tile.shouldSaveData();
|
||||
|
||||
byte packed = (byte)((tile.build != null ? 1 : 0) | (savedata ? 2 : 0));
|
||||
//in the old version, the second bit was set to indicate presence of data, but that approach was flawed - it didn't allow buildings + data on the same tile
|
||||
//so now the third bit is used instead
|
||||
byte packed = (byte)((tile.build != null ? 1 : 0) | (savedata ? 4 : 0));
|
||||
|
||||
//make note of whether there was an entity/rotation here
|
||||
//make note of whether there was an entity or custom tile data here
|
||||
stream.writeByte(packed);
|
||||
|
||||
if(savedata){
|
||||
//the new 'extra data' format writes 7 bytes of data instead of 1
|
||||
stream.writeByte(tile.data);
|
||||
stream.writeByte(tile.floorData);
|
||||
stream.writeByte(tile.overlayData);
|
||||
stream.writeInt(tile.extraData);
|
||||
}
|
||||
|
||||
//only write the entity for multiblocks once - in the center
|
||||
if(tile.build != null){
|
||||
if(tile.isCenter()){
|
||||
@@ -251,16 +261,14 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
}else{
|
||||
stream.writeBoolean(false);
|
||||
}
|
||||
}else if(savedata){
|
||||
stream.writeByte(tile.data);
|
||||
}else{
|
||||
}else if(!savedata){ //don't write consecutive blocks when there is custom data
|
||||
//write consecutive non-entity blocks
|
||||
int consecutives = 0;
|
||||
|
||||
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
|
||||
Tile nextTile = world.rawTile(j % world.width(), j / world.width());
|
||||
|
||||
if(nextTile.blockID() != tile.blockID()){
|
||||
if(nextTile.blockID() != tile.blockID() || savedata != nextTile.shouldSaveData()){
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -310,7 +318,19 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
boolean isCenter = true;
|
||||
byte packedCheck = stream.readByte();
|
||||
boolean hadEntity = (packedCheck & 1) != 0;
|
||||
boolean hadData = (packedCheck & 2) != 0;
|
||||
//old data format (bit 2): 1 byte only if no building is present
|
||||
//new data format (bit 3): 7 bytes (3x block-specific bytes + 1x 4-byte extra data int)
|
||||
boolean hadDataOld = (packedCheck & 2) != 0, hadDataNew = (packedCheck & 4) != 0;
|
||||
|
||||
byte data = 0, floorData = 0, overlayData = 0;
|
||||
int extraData = 0;
|
||||
|
||||
if(hadDataNew){
|
||||
data = stream.readByte();
|
||||
floorData = stream.readByte();
|
||||
overlayData = stream.readByte();
|
||||
extraData = stream.readInt();
|
||||
}
|
||||
|
||||
if(hadEntity){
|
||||
isCenter = stream.readBoolean();
|
||||
@@ -321,6 +341,15 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
tile.setBlock(block);
|
||||
}
|
||||
|
||||
//must be assigned after setBlock, because that can reset data
|
||||
if(hadDataNew){
|
||||
tile.data = data;
|
||||
tile.floorData = floorData;
|
||||
tile.overlayData = overlayData;
|
||||
tile.extraData = extraData;
|
||||
context.onReadTileData();
|
||||
}
|
||||
|
||||
if(hadEntity){
|
||||
if(isCenter){ //only read entity for center blocks
|
||||
if(block.hasBuilding()){
|
||||
@@ -339,9 +368,12 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
|
||||
context.onReadBuilding();
|
||||
}
|
||||
}else if(hadData){
|
||||
tile.setBlock(block);
|
||||
tile.data = stream.readByte();
|
||||
}else if(hadDataOld || hadDataNew){ //never read consecutive blocks if there's any kind of data
|
||||
if(hadDataOld){
|
||||
tile.setBlock(block);
|
||||
//the old data format was only read in the case where there is no building, and only contained a single byte
|
||||
tile.data = stream.readByte();
|
||||
}
|
||||
}else{
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package mindustry.io.versions;
|
||||
|
||||
import mindustry.io.*;
|
||||
|
||||
/** Adds support for the marker binary data region. The code is unchanged here, because it was easier to add a >= 8 check in the SaveVersion class itself. */
|
||||
public class Save8 extends SaveVersion{
|
||||
|
||||
public Save8(){
|
||||
|
||||
11
core/src/mindustry/io/versions/Save9.java
Normal file
11
core/src/mindustry/io/versions/Save9.java
Normal file
@@ -0,0 +1,11 @@
|
||||
package mindustry.io.versions;
|
||||
|
||||
import mindustry.io.*;
|
||||
|
||||
/** Adds support for the new 7-byte custom tile data. This can read Save8 data, but Save8 doesn't know how to handle this version's output, thus the version change. */
|
||||
public class Save9 extends SaveVersion{
|
||||
|
||||
public Save9(){
|
||||
super(9);
|
||||
}
|
||||
}
|
||||
@@ -273,7 +273,6 @@ public class Mods implements Loadable{
|
||||
ObjectMap<Texture, PageType> pageTypes = ObjectMap.of(
|
||||
Core.atlas.find("white").texture, PageType.main,
|
||||
Core.atlas.find("stone1").texture, PageType.environment,
|
||||
Core.atlas.find("clear-editor").texture, PageType.editor,
|
||||
Core.atlas.find("whiteui").texture, PageType.ui,
|
||||
Core.atlas.find("rubble-1-0").texture, PageType.rubble
|
||||
);
|
||||
@@ -405,7 +404,6 @@ public class Mods implements Loadable{
|
||||
String path = file.path();
|
||||
return
|
||||
path.contains("sprites/blocks/environment") || path.contains("sprites-override/blocks/environment") ? PageType.environment :
|
||||
path.contains("sprites/editor") || path.contains("sprites-override/editor") ? PageType.editor :
|
||||
path.contains("sprites/rubble") || path.contains("sprites-override/rubble") ? PageType.rubble :
|
||||
path.contains("sprites/ui") || path.contains("sprites-override/ui") ? PageType.ui :
|
||||
PageType.main;
|
||||
|
||||
@@ -72,12 +72,28 @@ public class HudFragment{
|
||||
}
|
||||
});
|
||||
|
||||
Table[] configTable = {null};
|
||||
Block[] lastBlock = {null};
|
||||
|
||||
cont.table(search -> {
|
||||
search.image(Icon.zoom).padRight(8);
|
||||
search.field("", text -> rebuildBlockSelection(blockSelection, text)).growX()
|
||||
.name("editor/search").maxTextLength(maxNameLength).get().setMessageText("@players.search");
|
||||
}).growX().pad(-2).padLeft(6f);
|
||||
cont.row();
|
||||
cont.collapser(t -> {
|
||||
configTable[0] = t;
|
||||
}, () -> control.input.block != null && control.input.block.editorConfigurable).with(c -> c.setEnforceMinSize(true)).update(col -> {
|
||||
|
||||
if(lastBlock[0] != control.input.block){
|
||||
configTable[0].clear();
|
||||
if(control.input.block != null){
|
||||
control.input.block.buildEditorConfig(configTable[0]);
|
||||
col.invalidateHierarchy();
|
||||
}
|
||||
lastBlock[0] = control.input.block;
|
||||
}
|
||||
}).growX().row();
|
||||
cont.add(pane).expandY().top().left();
|
||||
|
||||
rebuildBlockSelection(blockSelection, "");
|
||||
|
||||
@@ -126,41 +126,44 @@ public class PlacementFragment{
|
||||
}
|
||||
|
||||
boolean updatePick(InputHandler input){
|
||||
if(Core.input.keyTap(Binding.pick) && player.isBuilder() && !Core.scene.hasDialog()){ //mouse eyedropper select
|
||||
var build = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
Tile tile = world.tileWorld(Core.input.mouseWorldX(), Core.input.mouseWorldY());
|
||||
if(tile != null && Core.input.keyTap(Binding.pick) && player.isBuilder() && !Core.scene.hasDialog()){ //mouse eyedropper select
|
||||
var build = tile.build;
|
||||
|
||||
//can't middle click buildings in fog
|
||||
if(build != null && build.inFogTo(player.team())){
|
||||
build = null;
|
||||
}
|
||||
|
||||
Block tryRecipe = build == null ? null : build instanceof ConstructBuild c ? c.current : build.block;
|
||||
Block tryBlock = build == null ? null : build instanceof ConstructBuild c ? c.current : build.block;
|
||||
Object tryConfig = build == null || !build.block.copyConfig ? null : build.config();
|
||||
|
||||
for(BuildPlan req : player.unit().plans()){
|
||||
if(!req.breaking && req.block.bounds(req.x, req.y, Tmp.r1).contains(Core.input.mouseWorld())){
|
||||
tryRecipe = req.block;
|
||||
tryBlock = req.block;
|
||||
tryConfig = req.config;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(tryRecipe == null && state.rules.editor){
|
||||
var tile = world.tileWorld(Core.input.mouseWorldX(), Core.input.mouseWorldY());
|
||||
if(tile != null){
|
||||
tryRecipe =
|
||||
if(tryBlock == null && state.rules.editor){
|
||||
tryBlock =
|
||||
tile.block() != Blocks.air ? tile.block() :
|
||||
tile.overlay() != Blocks.air ? tile.overlay() :
|
||||
tile.floor() != Blocks.air ? tile.floor() : null;
|
||||
}
|
||||
}
|
||||
|
||||
if(tryRecipe != null && ((tryRecipe.isVisible() && unlocked(tryRecipe)) || state.rules.editor)){
|
||||
input.block = tryRecipe;
|
||||
tryRecipe.lastConfig = tryConfig;
|
||||
if(tryRecipe.isVisible()){
|
||||
if(tryBlock != null && build == null && tryConfig == null){
|
||||
tryConfig = tryBlock.getConfig(tile);
|
||||
}
|
||||
|
||||
if(tryBlock != null && ((tryBlock.isVisible() && unlocked(tryBlock)) || state.rules.editor)){
|
||||
input.block = tryBlock;
|
||||
tryBlock.lastConfig = tryConfig;
|
||||
if(tryBlock.isVisible()){
|
||||
currentCategory = input.block.category;
|
||||
}
|
||||
tryBlock.onPicked(tile);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public boolean displayFlow = true;
|
||||
/** whether this block is visible in the editor */
|
||||
public boolean inEditor = true;
|
||||
/** if true, {@link #buildEditorConfig(Table)} will be called for configuring this block in the editor. */
|
||||
public boolean editorConfigurable;
|
||||
/** the last configuration value applied to this block. */
|
||||
public @Nullable Object lastConfig;
|
||||
/** whether to save the last config and apply it to newly placed blocks */
|
||||
@@ -380,11 +382,10 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
protected Seq<Consume> consumeBuilder = new Seq<>();
|
||||
|
||||
protected TextureRegion[] generatedIcons;
|
||||
protected TextureRegion[] editorVariantRegions;
|
||||
|
||||
/** Regions indexes from icons() that are rotated. If either of these is not -1, other regions won't be rotated in ConstructBlocks. */
|
||||
public int regionRotated1 = -1, regionRotated2 = -1;
|
||||
public TextureRegion region, editorIcon;
|
||||
public TextureRegion region;
|
||||
public @Load("@-shadow") TextureRegion customShadowRegion;
|
||||
public @Load("@-team") TextureRegion teamRegion;
|
||||
public TextureRegion[] teamRegions, variantRegions, variantShadowRegions;
|
||||
@@ -566,6 +567,11 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
return forceDark;
|
||||
}
|
||||
|
||||
/** If true, the 'map edge' darkness will be applied to this block. */
|
||||
public boolean isDarkened(Tile tile){
|
||||
return solid && ((!synthetic() && fillsTile) || checkForceDark(tile));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
@@ -857,24 +863,6 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
}
|
||||
}
|
||||
|
||||
/** Never use outside of the editor! */
|
||||
public TextureRegion editorIcon(){
|
||||
return editorIcon == null ? (editorIcon = Core.atlas.find(name + "-icon-editor")) : editorIcon;
|
||||
}
|
||||
|
||||
/** Never use outside of the editor! */
|
||||
public TextureRegion[] editorVariantRegions(){
|
||||
if(editorVariantRegions == null){
|
||||
variantRegions();
|
||||
editorVariantRegions = new TextureRegion[variantRegions.length];
|
||||
for(int i = 0; i < variantRegions.length; i++){
|
||||
AtlasRegion region = (AtlasRegion)variantRegions[i];
|
||||
editorVariantRegions[i] = Core.atlas.find("editor-" + region.name);
|
||||
}
|
||||
}
|
||||
return editorVariantRegions;
|
||||
}
|
||||
|
||||
/** @return special icons to outline and save with an -outline variant. Vanilla only. */
|
||||
public TextureRegion[] makeIconRegions(){
|
||||
return new TextureRegion[0];
|
||||
@@ -940,6 +928,21 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
return (envEnabled & env) != 0 && (envDisabled & env) == 0 && (envRequired == 0 || (envRequired & env) == envRequired);
|
||||
}
|
||||
|
||||
/** Called to set up configuration UI in the editor. {@link #editorConfigurable} must be true.
|
||||
* Config value should be assigned to lastConfig.*/
|
||||
public void buildEditorConfig(Table table){}
|
||||
|
||||
/** Called when the block is picked (middle click). Clientside only! */
|
||||
public void onPicked(Tile tile){}
|
||||
|
||||
/** @return the config value returned when this block is picked on a certain tile. This is only called for non-buildings. */
|
||||
public Object getConfig(Tile tile){
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Called when this block is set on the specified tile. */
|
||||
public void blockChanged(Tile tile){}
|
||||
|
||||
/** Called when building of this block begins. */
|
||||
public void placeBegan(Tile tile, Block previous){
|
||||
|
||||
@@ -951,7 +954,7 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
}
|
||||
|
||||
/** Called when building of this block ends. */
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder){
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){
|
||||
|
||||
}
|
||||
|
||||
@@ -1366,13 +1369,6 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
mapColor.set(image.get(image.width/2, image.height/2));
|
||||
}
|
||||
|
||||
if(variants > 0){
|
||||
for(int i = 0; i < variants; i++){
|
||||
String rname = name + (i + 1);
|
||||
packer.add(PageType.editor, "editor-" + rname, Core.atlas.getPixmap(rname));
|
||||
}
|
||||
}
|
||||
|
||||
Seq<Pixmap> toDispose = new Seq<>();
|
||||
|
||||
//generate paletted team regions
|
||||
@@ -1437,8 +1433,6 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
}
|
||||
}
|
||||
|
||||
PixmapRegion editorBase;
|
||||
|
||||
if(gen.length > 1){
|
||||
Pixmap base = Core.atlas.getPixmap(gen[0]).crop();
|
||||
for(int i = 1; i < gen.length; i++){
|
||||
@@ -1450,15 +1444,11 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
}
|
||||
packer.add(PageType.main, "block-" + name + "-full", base);
|
||||
|
||||
editorBase = new PixmapRegion(base);
|
||||
toDispose.add(base);
|
||||
}else{
|
||||
if(gen[0] != null) packer.add(PageType.main, "block-" + name + "-full", Core.atlas.getPixmap(gen[0]));
|
||||
editorBase = gen[0] == null ? Core.atlas.getPixmap(fullIcon) : Core.atlas.getPixmap(gen[0]);
|
||||
}
|
||||
|
||||
packer.add(PageType.editor, name + "-icon-editor", editorBase);
|
||||
|
||||
toDispose.each(Pixmap::dispose);
|
||||
}
|
||||
|
||||
|
||||
@@ -66,9 +66,9 @@ public class Build{
|
||||
Events.fire(new BlockBuildBeginEvent(tile, team, unit, true));
|
||||
}
|
||||
|
||||
/** Places a ConstructBlock at this location. */
|
||||
/** Places a ConstructBlock at this location. To preserve bandwidth, a config is only passed in the case of instant-place blocks. */
|
||||
@Remote(called = Loc.server)
|
||||
public static void beginPlace(@Nullable Unit unit, Block result, Team team, int x, int y, int rotation){
|
||||
public static void beginPlace(@Nullable Unit unit, Block result, Team team, int x, int y, int rotation, @Nullable Object placeConfig){
|
||||
if(!validPlace(result, team, x, y, rotation)){
|
||||
return;
|
||||
}
|
||||
@@ -127,7 +127,7 @@ public class Build{
|
||||
if(result.instantBuild){
|
||||
Events.fire(new BlockBuildBeginEvent(tile, team, unit, false));
|
||||
result.placeBegan(tile, tile.block, unit);
|
||||
ConstructBlock.constructFinish(tile, result, unit, (byte)rotation, team, null);
|
||||
ConstructBlock.constructFinish(tile, result, unit, (byte)rotation, team, placeConfig);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,8 +26,14 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
private static final TileFloorChangeEvent floorChange = new TileFloorChangeEvent();
|
||||
private static final ObjectSet<Building> tileSet = new ObjectSet<>();
|
||||
|
||||
/** Extra data for very specific blocks. */
|
||||
public byte data;
|
||||
/**
|
||||
* Extra data for specific blocks. Only saved if Block#saveData is true.
|
||||
* It is generally recommended that blocks only access data in their own category unless necessary - for example, a floor should not read/write overlay data.
|
||||
* However, one byte may sometimes not be enough to hold enough data, in which case "overlapping" data storage is necessary.
|
||||
* */
|
||||
public byte data, floorData, overlayData;
|
||||
/** Even more data for blocks. Use with caution; any floor/block can access this value. Due to 8-byte alignment of Java objects, this extra 4-byte field can be added with no additional cost.*/
|
||||
public int extraData;
|
||||
/** Tile entity, usually null. */
|
||||
public @Nullable Building build;
|
||||
public short x, y;
|
||||
@@ -166,7 +172,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
}
|
||||
|
||||
public boolean isDarkened(){
|
||||
return block.solid && ((!block.synthetic() && block.fillsTile) || block.checkForceDark(this));
|
||||
return block.isDarkened(this);
|
||||
}
|
||||
|
||||
public Floor floor(){
|
||||
@@ -274,6 +280,8 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
|
||||
changed();
|
||||
changing = false;
|
||||
|
||||
block.blockChanged(this);
|
||||
}
|
||||
|
||||
public void setBlock(Block type, Team team){
|
||||
@@ -284,11 +292,11 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
setBlock(type, Team.derelict, 0);
|
||||
}
|
||||
|
||||
/** This resets the overlay! */
|
||||
public void setFloor(Floor type){
|
||||
if(this.floor == type) return;
|
||||
|
||||
var prev = this.floor;
|
||||
this.floor = type;
|
||||
this.overlay = (Floor)Blocks.air;
|
||||
|
||||
if(!headless && !world.isGenerating() && !isEditorTile()){
|
||||
renderer.blocks.removeFloorIndex(this);
|
||||
@@ -302,24 +310,19 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
pathfinder.updateTile(this);
|
||||
}
|
||||
|
||||
if(!world.isGenerating() && prev != type){
|
||||
if(!world.isGenerating()){
|
||||
Events.fire(floorChange.set(this, prev, type));
|
||||
}
|
||||
|
||||
if(this.floor != prev){
|
||||
this.floor.floorChanged(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEditorTile(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Sets the floor, preserving overlay.*/
|
||||
public void setFloorUnder(Floor floor){
|
||||
Block overlay = this.overlay;
|
||||
setFloor(floor);
|
||||
if(this.overlay != overlay){
|
||||
setOverlay(overlay);
|
||||
}
|
||||
}
|
||||
|
||||
/** Sets the block to air. */
|
||||
public void setAir(){
|
||||
setBlock(Blocks.air);
|
||||
@@ -402,11 +405,9 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
return floor.id;
|
||||
}
|
||||
|
||||
public void setOverlayID(short ore){
|
||||
setOverlay(content.block(ore));
|
||||
}
|
||||
|
||||
public void setOverlay(Block block){
|
||||
if(this.overlay == block) return;
|
||||
|
||||
this.overlay = (Floor)block;
|
||||
|
||||
recache();
|
||||
@@ -421,7 +422,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
}
|
||||
|
||||
public void clearOverlay(){
|
||||
setOverlayID((short)0);
|
||||
setOverlay(Blocks.air);
|
||||
}
|
||||
|
||||
public boolean passable(){
|
||||
@@ -558,6 +559,10 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
null : null;
|
||||
}
|
||||
|
||||
public boolean shouldSaveData(){
|
||||
return floor.saveData || overlay.saveData || block.saveData;
|
||||
}
|
||||
|
||||
public int staticDarkness(){
|
||||
return block.solid && block.fillsTile && !block.synthetic() ? data : 0;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ public interface WorldContext{
|
||||
/** Called when a building is finished reading. */
|
||||
default void onReadBuilding(){}
|
||||
|
||||
/** Called when data finishes reading for a tile. */
|
||||
default void onReadTileData(){}
|
||||
|
||||
default @Nullable Sector getSector(){
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public class ConstructBlock extends Block{
|
||||
if(block instanceof OverlayFloor overlay){
|
||||
tile.setOverlay(overlay);
|
||||
}else if(block instanceof Floor floor){
|
||||
tile.setFloorUnder(floor);
|
||||
tile.setFloor(floor);
|
||||
}else{
|
||||
tile.setBlock(block, team, rotation);
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class ConstructBlock extends Block{
|
||||
if(shouldPlay()) block.placeSound.at(tile, block.placePitchChange ? calcPitch(true) : 1f);
|
||||
}
|
||||
|
||||
block.placeEnded(tile, builder);
|
||||
block.placeEnded(tile, builder, rotation, config);
|
||||
|
||||
Events.fire(new BlockBuildEndEvent(tile, builder, team, false, config));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package mindustry.world.blocks;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
|
||||
public class TileBitmask{
|
||||
/** Autotile bitmasks for 8-directional sprites (see <a href="https://github.com/GglLfr/tile-gen">tile-gen</a>)*/
|
||||
public static final int[] values = {
|
||||
@@ -20,4 +23,23 @@ public class TileBitmask{
|
||||
3, 0, 3, 0, 15, 42, 15, 12, 3, 0, 3, 0, 15, 42, 15, 12,
|
||||
2, 1, 2, 1, 9, 45, 9, 19, 2, 1, 2, 1, 14, 18, 14, 13,
|
||||
};
|
||||
|
||||
public static TextureRegion[] load(String name){
|
||||
var regions = new TextureRegion[47];
|
||||
for(int i = 0; i < 47; i++){
|
||||
regions[i] = Core.atlas.find(name + "-" + i);
|
||||
}
|
||||
return regions;
|
||||
}
|
||||
|
||||
public static TextureRegion[][] loadVariants(String name, int variants){
|
||||
var regions = new TextureRegion[variants][47];
|
||||
for(int v = 0; v < variants; v++){
|
||||
for(int i = 0; i < 47; i++){
|
||||
regions[v][i] = Core.atlas.find(name + "-" + (v+1) + "-" + i);
|
||||
}
|
||||
}
|
||||
|
||||
return regions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class CharacterOverlay extends OverlayFloor{
|
||||
/** This is a special reduced character set that fits in 6 bits! It is not ASCII! */
|
||||
public static final String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\"!?.,;:()[]{}<>|/@\\^-%+=#_&~";
|
||||
|
||||
public @Load(value = "character-overlay#", length = 64) TextureRegion[] letterRegions;
|
||||
public Color color = Color.white;
|
||||
|
||||
public CharacterOverlay(String name){
|
||||
super(name);
|
||||
saveData = true;
|
||||
variants = 0;
|
||||
rotate = true;
|
||||
drawArrow = false;
|
||||
saveConfig = true;
|
||||
editorConfigurable = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
Draw.color(color);
|
||||
int letterChar = CharOverlayData.character(tile.overlayData);
|
||||
Draw.rect(letterRegions[letterChar], tile.worldx(), tile.worldy(), CharOverlayData.rotation(tile.overlayData) * 90f);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig(Tile tile){
|
||||
return (int)tile.overlayData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlanRegion(BuildPlan plan, Eachable<BuildPlan> list){
|
||||
byte data = 0;
|
||||
|
||||
if(plan.config instanceof Integer i){
|
||||
data = i.byteValue();
|
||||
}
|
||||
|
||||
int letterChar = CharOverlayData.character(data);
|
||||
|
||||
TextureRegion reg = letterRegions[letterChar];
|
||||
Draw.tint(color);
|
||||
Draw.rect(reg, plan.drawx(), plan.drawy(), plan.rotation * 90);
|
||||
Draw.tint(Color.white);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPicked(Tile tile){
|
||||
Vars.control.input.rotation = CharOverlayData.rotation(tile.overlayData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEditorConfig(Table table){
|
||||
char value = chars.charAt(lastConfig instanceof Integer i ? CharOverlayData.character(i.byteValue()) : 0);
|
||||
table.field(value + "", val -> {
|
||||
if(val.length() == 1){
|
||||
lastConfig = (int)charToData(val.charAt(0));
|
||||
}
|
||||
}).valid(t -> t.length() == 1 && chars.indexOf(Character.toUpperCase(t.charAt(0))) != -1).maxTextLength(1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){
|
||||
byte data = 0;
|
||||
if(config instanceof Integer i){
|
||||
data = i.byteValue();
|
||||
}
|
||||
tile.overlayData = CharOverlayData.get(data, (byte)rotation);
|
||||
}
|
||||
|
||||
public static byte charToData(char c){
|
||||
int index = chars.indexOf(Character.toUpperCase(c));
|
||||
return index == -1 ? 0 : (byte)index;
|
||||
}
|
||||
|
||||
@Struct
|
||||
class CharOverlayDataStruct{
|
||||
@StructField(6)
|
||||
byte character;
|
||||
@StructField(2)
|
||||
byte rotation;
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import mindustry.world.*;
|
||||
public class Cliff extends Block{
|
||||
public float size = 11f;
|
||||
public @Load(value = "cliffmask#", length = 256) TextureRegion[] cliffs;
|
||||
public @Load(value = "editor-cliffmask#", length = 256) TextureRegion[] editorCliffs;
|
||||
|
||||
public Cliff(String name){
|
||||
super(name);
|
||||
|
||||
199
core/src/mindustry/world/blocks/environment/ColoredFloor.java
Normal file
199
core/src/mindustry/world/blocks/environment/ColoredFloor.java
Normal file
@@ -0,0 +1,199 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ColoredFloor extends Floor{
|
||||
/** If the alpha value of the color is set to this value, different colors are ignored and no border is drawn. */
|
||||
public static final int flagIgnoreDifferentColor = 1;
|
||||
/** If the alpha value of the color is set to this value, colors are interpolated across corners. This is essentially linear filtering for the whole "image". */
|
||||
public static final int flagSmoothBlend = 2;
|
||||
|
||||
private static final float[] verts = new float[20];
|
||||
|
||||
public Color defaultColor = Color.white;
|
||||
protected int defaultColorRgba;
|
||||
|
||||
public ColoredFloor(String name){
|
||||
super(name);
|
||||
saveData = true;
|
||||
editorConfigurable = true;
|
||||
saveConfig = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
lastConfig = defaultColorRgba = defaultColor.rgba();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEditorConfig(Table table){
|
||||
showColorEdit(table, this);
|
||||
}
|
||||
|
||||
public static void showColorEdit(Table t, Block block){
|
||||
t.button(b -> {
|
||||
b.margin(4f);
|
||||
b.left();
|
||||
b.table(Tex.pane, in -> {
|
||||
in.image(Tex.whiteui).update(i -> {
|
||||
if(block.lastConfig instanceof Integer col){
|
||||
i.color.set(col | 0xff);
|
||||
}
|
||||
}).grow();
|
||||
}).margin(4).size(50f).padRight(10);
|
||||
b.add("@color");
|
||||
}, Styles.cleart, () ->
|
||||
ui.picker.show(
|
||||
block.lastConfig instanceof Integer col ? new Color(col | 0xff) : new Color(Color.white), false,
|
||||
col -> block.lastConfig = col.rgba8888())).left().width(250f).pad(3f).row();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig(Tile tile){
|
||||
return tile.extraData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
//make sure to mask out the alpha channel - it's generally undesirable, and leads to invisible blocks when the data is not initialized
|
||||
Draw.color(tile.extraData | 0xff);
|
||||
if((tile.extraData & 0xff) == flagSmoothBlend && autotile){
|
||||
//Only autotiling is supported right now for the sake of simplicity
|
||||
int bits = 0;
|
||||
|
||||
for(int i = 0; i < 8; i++){
|
||||
Tile other = tile.nearby(Geometry.d8[i]);
|
||||
//force flagIgnoreDifferentColor by bypassing checkAutotileSame
|
||||
if(other != null && other.floor().blendGroup == blendGroup){
|
||||
bits |= (1 << i);
|
||||
}
|
||||
}
|
||||
var region = autotileRegions[TileBitmask.values[bits]];
|
||||
float s = Vars.tilesize/2f;
|
||||
float x = tile.worldx(), y = tile.worldy();
|
||||
|
||||
verts[0] = x - s;
|
||||
verts[1] = y - s;
|
||||
verts[2] = sample(this, tile.x, tile.y, tile.x - 1, tile.y - 1, tile.x, tile.y - 1, tile.x - 1, tile.y);
|
||||
verts[3] = region.u;
|
||||
verts[4] = region.v2;
|
||||
|
||||
verts[5] = x + s;
|
||||
verts[6] = y - s;
|
||||
verts[7] = sample(this, tile.x, tile.y, tile.x, tile.y - 1, tile.x + 1, tile.y - 1, tile.x + 1, tile.y);
|
||||
verts[8] = region.u2;
|
||||
verts[9] = region.v2;
|
||||
|
||||
verts[10] = x + s;
|
||||
verts[11] = y + s;
|
||||
verts[12] = sample(this, tile.x, tile.y, tile.x + 1, tile.y + 1, tile.x, tile.y + 1, tile.x + 1, tile.y);
|
||||
verts[13] = region.u2;
|
||||
verts[14] = region.v;
|
||||
|
||||
verts[15] = x - s;
|
||||
verts[16] = y + s;
|
||||
verts[17] = sample(this, tile.x, tile.y, tile.x - 1, tile.y + 1, tile.x, tile.y + 1, tile.x - 1, tile.y);
|
||||
verts[18] = region.u;
|
||||
verts[19] = region.v;
|
||||
|
||||
Draw.vert(region.texture, verts, 0, verts.length);
|
||||
}else{
|
||||
super.drawBase(tile);
|
||||
}
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
static float sample(Block target, int tx1, int ty1, int tx2, int ty2, int tx3, int ty3, int tx4, int ty4){
|
||||
int total = 0;
|
||||
float r = 0f, g = 0f, b = 0f;
|
||||
Tile t1 = Vars.world.tile(tx1, ty1);
|
||||
Tile t2 = Vars.world.tile(tx2, ty2);
|
||||
Tile t3 = Vars.world.tile(tx3, ty3);
|
||||
Tile t4 = Vars.world.tile(tx4, ty4);
|
||||
|
||||
//manually unrolled loops, hooray
|
||||
if(t1 != null && t1.floor() == target){
|
||||
total ++;
|
||||
r += Color.ri(t1.extraData);
|
||||
g += Color.gi(t1.extraData);
|
||||
b += Color.bi(t1.extraData);
|
||||
}
|
||||
|
||||
if(t2 != null && t2.floor() == target){
|
||||
total ++;
|
||||
r += Color.ri(t2.extraData);
|
||||
g += Color.gi(t2.extraData);
|
||||
b += Color.bi(t2.extraData);
|
||||
}
|
||||
|
||||
if(t3 != null && t3.floor() == target){
|
||||
total ++;
|
||||
r += Color.ri(t3.extraData);
|
||||
g += Color.gi(t3.extraData);
|
||||
b += Color.bi(t3.extraData);
|
||||
}
|
||||
|
||||
if(t4 != null && t4.floor() == target){
|
||||
total ++;
|
||||
r += Color.ri(t4.extraData);
|
||||
g += Color.gi(t4.extraData);
|
||||
b += Color.bi(t4.extraData);
|
||||
}
|
||||
|
||||
return Color.toFloatBits((int)(r/total), (int)(g/total), (int)(b/total), 255);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOverlay(Tile tile){
|
||||
//make sure color doesn't carry over
|
||||
Draw.color();
|
||||
super.drawOverlay(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void floorChanged(Tile tile){
|
||||
//reset to white
|
||||
if(tile.extraData == 0){
|
||||
tile.extraData = defaultColorRgba;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){
|
||||
//config is assumed to be an integer RGBA color
|
||||
if(config instanceof Integer i){
|
||||
tile.extraData = i;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlanRegion(BuildPlan plan, Eachable<BuildPlan> list){
|
||||
if(plan.config instanceof Integer i){
|
||||
Draw.tint(Tmp.c1.set(i | 0xff));
|
||||
}
|
||||
drawDefaultPlanRegion(plan, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAutotileSame(Tile tile, @Nullable Tile other){
|
||||
return other != null && other.floor().blendGroup == blendGroup && ((tile.extraData & 0xff) == flagIgnoreDifferentColor || tile.extraData == other.extraData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minimapColor(Tile tile){
|
||||
return tile.extraData | 0xff;
|
||||
}
|
||||
}
|
||||
89
core/src/mindustry/world/blocks/environment/ColoredWall.java
Normal file
89
core/src/mindustry/world/blocks/environment/ColoredWall.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class ColoredWall extends StaticWall{
|
||||
/** If the alpha value of the color is set to this value, different colors are ignored and no border is drawn. */
|
||||
public static final int flagIgnoreDifferentColor = 1;
|
||||
/** If the alpha value of the color is set to this value, the wall will have darkness applied, as other walls do. */
|
||||
public static final int flagApplyDarkness = 2;
|
||||
|
||||
public Color defaultColor = Color.white;
|
||||
protected int defaultColorRgba;
|
||||
|
||||
public ColoredWall(String name){
|
||||
super(name);
|
||||
saveData = true;
|
||||
editorConfigurable = true;
|
||||
saveConfig = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
lastConfig = defaultColorRgba = defaultColor.rgba();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getConfig(Tile tile){
|
||||
return tile.extraData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildEditorConfig(Table table){
|
||||
ColoredFloor.showColorEdit(table, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
//make sure to mask out the alpha channel - it's generally undesirable, and leads to invisible blocks when thtoe data is not initialized
|
||||
Draw.color(tile.extraData | 0xff);
|
||||
super.drawBase(tile);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blockChanged(Tile tile){
|
||||
//reset to white on first placement
|
||||
if(tile.extraData == 0){
|
||||
tile.extraData = defaultColorRgba;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){
|
||||
//config is assumed to be an integer RGBA color
|
||||
if(config instanceof Integer i){
|
||||
tile.extraData = i;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlanRegion(BuildPlan plan, Eachable<BuildPlan> list){
|
||||
if(plan.config instanceof Integer i){
|
||||
Draw.tint(Tmp.c1.set(i | 0xff));
|
||||
}
|
||||
drawDefaultPlanRegion(plan, list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkAutotileSame(Tile tile, @Nullable Tile other){
|
||||
return other != null && other.block() == this && ((tile.extraData == flagIgnoreDifferentColor) || tile.extraData == other.extraData);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDarkened(Tile tile){
|
||||
return (tile.extraData == flagApplyDarkness);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minimapColor(Tile tile){
|
||||
return tile.extraData | 0xff;
|
||||
}
|
||||
}
|
||||
@@ -80,9 +80,18 @@ public class Floor extends Block{
|
||||
public int tilingVariants = 0;
|
||||
/** If true, this floor uses autotiling; variants are not supported. See https://github.com/GglLfr/tile-gen*/
|
||||
public boolean autotile = false;
|
||||
/** If >1, the middle region of the autotile has random variants. */
|
||||
public int autotileMidVariants = 1;
|
||||
/** Variants of the main autotile sprite. */
|
||||
public int autotileVariants = 1;
|
||||
/** If true (default), this floor will draw edges of other floors on itself. */
|
||||
public boolean drawEdgeIn = true;
|
||||
/** If true (default), this floor will draw its edges onto other floors. */
|
||||
public boolean drawEdgeOut = true;
|
||||
|
||||
protected TextureRegion[][][] tilingRegions;
|
||||
protected TextureRegion[] autotileRegions;
|
||||
protected TextureRegion[] autotileRegions, autotileMidRegions;
|
||||
protected TextureRegion[][] autotileVariantRegions;
|
||||
protected int tilingSize;
|
||||
protected TextureRegion[][] edges;
|
||||
protected Seq<Floor> blenders = new Seq<>();
|
||||
@@ -141,9 +150,15 @@ public class Floor extends Block{
|
||||
}
|
||||
|
||||
if(autotile){
|
||||
autotileRegions = new TextureRegion[47];
|
||||
for(int i = 0; i < 47; i++){
|
||||
autotileRegions[i] = Core.atlas.find(name + "-" + i);
|
||||
autotileRegions = TileBitmask.load(name);
|
||||
if(autotileVariants > 1){
|
||||
autotileVariantRegions = TileBitmask.loadVariants(name, autotileVariants);
|
||||
}
|
||||
if(autotileMidVariants > 1){
|
||||
autotileMidRegions = new TextureRegion[autotileMidVariants];
|
||||
for(int i = 0; i < autotileMidVariants; i++){
|
||||
autotileMidRegions[i] = Core.atlas.find(i == 0 ? name + "-13" : name + "-mid-" + (i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +210,6 @@ public class Floor extends Block{
|
||||
@Override
|
||||
public void createIcons(MultiPacker packer){
|
||||
super.createIcons(packer);
|
||||
packer.add(PageType.editor, "editor-" + name, Core.atlas.getPixmap(fullIcon));
|
||||
|
||||
if(blendGroup != this){
|
||||
return;
|
||||
@@ -226,25 +240,40 @@ public class Floor extends Block{
|
||||
}else if(autotile){
|
||||
int bits = 0;
|
||||
|
||||
TextureRegion[] regions = autotileVariants > 1 ? autotileVariantRegions[variant(tile.x, tile.y, autotileVariantRegions.length)] : autotileRegions;
|
||||
|
||||
for(int i = 0; i < 8; i++){
|
||||
Tile other = tile.nearby(Geometry.d8[i]);
|
||||
if(other != null && other.floor().blendGroup == blendGroup){
|
||||
if(checkAutotileSame(tile, other)){
|
||||
bits |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.rect(autotileRegions[TileBitmask.values[bits]], tile.worldx(), tile.worldy());
|
||||
int bit = TileBitmask.values[bits];
|
||||
TextureRegion region = bit == 13 && autotileMidVariants > 1 ? autotileMidRegions[variant(tile.x, tile.y, autotileMidRegions.length)] : regions[bit];
|
||||
|
||||
Draw.rect(region, tile.worldx(), tile.worldy());
|
||||
}else{
|
||||
Draw.rect(variantRegions[variant(tile.x, tile.y)], tile.worldx(), tile.worldy());
|
||||
}
|
||||
|
||||
Draw.alpha(1f);
|
||||
drawEdges(tile);
|
||||
if(drawEdgeIn){
|
||||
drawEdges(tile);
|
||||
}
|
||||
drawOverlay(tile);
|
||||
}
|
||||
|
||||
public boolean checkAutotileSame(Tile tile, @Nullable Tile other){
|
||||
return other != null && other.floor().blendGroup == blendGroup;
|
||||
}
|
||||
|
||||
public int variant(int x, int y){
|
||||
return Mathf.randomSeed(Point2.pack(x, y), 0, Math.max(0, variantRegions.length - 1));
|
||||
return variant(x, y, variantRegions.length);
|
||||
}
|
||||
|
||||
public int variant(int x, int y, int max){
|
||||
return Mathf.randomSeed(Point2.pack(x, y), 0, Math.max(0, max - 1));
|
||||
}
|
||||
|
||||
public void drawOverlay(Tile tile){
|
||||
@@ -265,6 +294,9 @@ public class Floor extends Block{
|
||||
return new TextureRegion[]{Core.atlas.find(Core.atlas.has(name) ? name : name + "1")};
|
||||
}
|
||||
|
||||
/** Called when this floor is set on the specified tile. */
|
||||
public void floorChanged(Tile tile){}
|
||||
|
||||
/** @return whether to index this floor by flag */
|
||||
public boolean shouldIndex(Tile tile){
|
||||
return true;
|
||||
@@ -298,7 +330,7 @@ public class Floor extends Block{
|
||||
Point2 point = Geometry.d8[i];
|
||||
Tile other = tile.nearby(point);
|
||||
//special case: empty is, well, empty, so never draw emptiness on top, as that would just be an incorrect black texture
|
||||
if(other != null && other.floor().cacheLayer == layer && other.floor().edges(tile.x, tile.y) != null && other.floor() != Blocks.empty){
|
||||
if(other != null && other.floor().drawEdgeOut && other.floor().cacheLayer == layer && other.floor().edges(tile.x, tile.y) != null){
|
||||
if(!blended.getAndSet(other.floor().id)){
|
||||
blenders.add(other.floor());
|
||||
dirs[i] = other.floorID();
|
||||
@@ -319,7 +351,7 @@ public class Floor extends Block{
|
||||
Point2 point = Geometry.d8[i];
|
||||
Tile other = tile.nearby(point);
|
||||
|
||||
if(other != null && doEdge(tile, other, other.floor()) && other.floor().cacheLayer == realCache && other.floor().edges(tile.x, tile.y) != null && other.floor() != Blocks.empty){
|
||||
if(other != null && other.floor().drawEdgeOut && doEdge(tile, other, other.floor()) && other.floor().cacheLayer == realCache && other.floor().edges(tile.x, tile.y) != null){
|
||||
if(!blended.getAndSet(other.floor().id)){
|
||||
blenders.add(other.floor());
|
||||
}
|
||||
@@ -345,19 +377,6 @@ public class Floor extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
//'new' style of edges with shadows instead of colors, not used currently
|
||||
protected void drawEdgesFlat(Tile tile, boolean sameLayer){
|
||||
for(int i = 0; i < 4; i++){
|
||||
Tile other = tile.nearby(i);
|
||||
if(other != null && doEdge(tile, other, other.floor())){
|
||||
Color color = other.floor().mapColor;
|
||||
Draw.color(color.r, color.g, color.b, 1f);
|
||||
Draw.rect(edgeRegion, tile.worldx(), tile.worldy(), i*90);
|
||||
}
|
||||
}
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
public int realBlendId(Tile tile){
|
||||
if(tile.floor().isLiquid && !tile.overlay().isAir() && !(tile.overlay() instanceof OreBlock)){
|
||||
return -((tile.overlay().blendId) | (tile.floor().blendId << 15));
|
||||
|
||||
@@ -63,10 +63,8 @@ public class OreBlock extends OverlayFloor{
|
||||
}
|
||||
|
||||
packer.add(PageType.environment, name + (i + 1), image);
|
||||
packer.add(PageType.editor, "editor-" + name + (i + 1), image);
|
||||
|
||||
if(i == 0){
|
||||
packer.add(PageType.editor, "editor-block-" + name + "-full", image);
|
||||
packer.add(PageType.main, "block-" + name + "-full", image);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public class RemoveOre extends OverlayFloor{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder){
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, Object config){
|
||||
tile.setOverlay(Blocks.air);
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public class RemoveWall extends Block{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder){
|
||||
public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, Object config){
|
||||
tile.setBlock(Blocks.air);
|
||||
if(tile.overlay().wallOre){
|
||||
tile.setOverlay(Blocks.air);
|
||||
|
||||
@@ -47,9 +47,8 @@ public class ShallowLiquid extends Floor{
|
||||
}
|
||||
}
|
||||
|
||||
String baseName = this.name + "" + (++index);
|
||||
String baseName = this.name + (++index);
|
||||
packer.add(PageType.environment, baseName, res);
|
||||
packer.add(PageType.editor, "editor-" + baseName, res);
|
||||
|
||||
res.dispose();
|
||||
}
|
||||
|
||||
@@ -4,16 +4,22 @@ import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class StaticWall extends Prop{
|
||||
public @Load("@-large") TextureRegion large;
|
||||
public TextureRegion[][] split;
|
||||
/** If true, this wall uses autotiling; variants are not supported. See https://github.com/GglLfr/tile-gen*/
|
||||
public boolean autotile;
|
||||
|
||||
protected TextureRegion[] autotileRegions;
|
||||
|
||||
public StaticWall(String name){
|
||||
super(name);
|
||||
@@ -30,15 +36,28 @@ public class StaticWall extends Prop{
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
int rx = tile.x / 2 * 2;
|
||||
int ry = tile.y / 2 * 2;
|
||||
if(autotile){
|
||||
int bits = 0;
|
||||
|
||||
if(Core.atlas.isFound(large) && eq(rx, ry) && Mathf.randomSeed(Point2.pack(rx, ry)) < 0.5 && split.length >= 2 && split[0].length >= 2){
|
||||
Draw.rect(split[tile.x % 2][1 - tile.y % 2], tile.worldx(), tile.worldy());
|
||||
}else if(variants > 0){
|
||||
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
|
||||
for(int i = 0; i < 8; i++){
|
||||
Tile other = tile.nearby(Geometry.d8[i]);
|
||||
if(checkAutotileSame(tile, other)){
|
||||
bits |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.rect(autotileRegions[TileBitmask.values[bits]], tile.worldx(), tile.worldy());
|
||||
}else{
|
||||
Draw.rect(region, tile.worldx(), tile.worldy());
|
||||
int rx = tile.x / 2 * 2;
|
||||
int ry = tile.y / 2 * 2;
|
||||
|
||||
if(Core.atlas.isFound(large) && eq(rx, ry) && Mathf.randomSeed(Point2.pack(rx, ry)) < 0.5 && split.length >= 2 && split[0].length >= 2){
|
||||
Draw.rect(split[tile.x % 2][1 - tile.y % 2], tile.worldx(), tile.worldy());
|
||||
}else if(variants > 0){
|
||||
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
|
||||
}else{
|
||||
Draw.rect(region, tile.worldx(), tile.worldy());
|
||||
}
|
||||
}
|
||||
|
||||
//draw ore on top
|
||||
@@ -47,6 +66,10 @@ public class StaticWall extends Prop{
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkAutotileSame(Tile tile, @Nullable Tile other){
|
||||
return other != null && other.block() == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
@@ -59,6 +82,10 @@ public class StaticWall extends Prop{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(autotile){
|
||||
autotileRegions = TileBitmask.load(name);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -11,15 +11,15 @@ public class BuildVisibility{
|
||||
shown = new BuildVisibility(() -> true),
|
||||
debugOnly = new BuildVisibility(() -> false),
|
||||
editorOnly = new BuildVisibility(() -> Vars.state.rules.editor),
|
||||
coreZoneOnly = new BuildVisibility(() -> Vars.indexer.isBlockPresent(Blocks.coreZone)),
|
||||
coreZoneOnly = new BuildVisibility(() -> Vars.indexer.isBlockPresent(Blocks.coreZone) || !Vars.state.isGame()),
|
||||
worldProcessorOnly = new BuildVisibility(() -> Vars.state.rules.editor || Vars.state.rules.allowEditWorldProcessors),
|
||||
sandboxOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.infiniteResources),
|
||||
campaignOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.isCampaign()),
|
||||
campaignOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.isCampaign() || !Vars.state.isGame()),
|
||||
legacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || Vars.state.isCampaign() && Vars.state.getPlanet().campaignRules.legacyLaunchPads) && Blocks.advancedLaunchPad != null && Blocks.advancedLaunchPad.unlocked()),
|
||||
notLegacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || Vars.state.rules.infiniteResources || Vars.state.isCampaign() && !Vars.state.getPlanet().campaignRules.legacyLaunchPads)),
|
||||
lightingOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.lighting || Vars.state.isCampaign()),
|
||||
notLegacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || !Vars.state.isGame() || Vars.state.rules.infiniteResources || Vars.state.isCampaign() && !Vars.state.getPlanet().campaignRules.legacyLaunchPads)),
|
||||
lightingOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.lighting || Vars.state.isCampaign() || !Vars.state.isGame()),
|
||||
ammoOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.unitAmmo),
|
||||
fogOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.fog || Vars.state.rules.editor);
|
||||
fogOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.fog || Vars.state.rules.editor || !Vars.state.isGame());
|
||||
|
||||
private final Boolp visible;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user