New tile edge algorithms / Struct value type generation

This commit is contained in:
Anuken
2019-01-28 22:11:23 -05:00
parent 5a3ec8f407
commit 54bade668e
19 changed files with 7275 additions and 7459 deletions

View File

@@ -22,7 +22,7 @@ public class Annotations{
} }
/**Marks a field of a struct. Optional.*/ /**Marks a field of a struct. Optional.*/
@Target(ElementType.TYPE) @Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE) @Retention(RetentionPolicy.SOURCE)
public @interface StructField{ public @interface StructField{
/**Size of a struct field in bits. Not valid on booleans or floating point numbers.*/ /**Size of a struct field in bits. Not valid on booleans or floating point numbers.*/

View File

@@ -9,14 +9,17 @@ import io.anuke.annotations.Annotations.StructField;
import javax.annotation.processing.*; import javax.annotation.processing.*;
import javax.lang.model.SourceVersion; import javax.lang.model.SourceVersion;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement; import javax.lang.model.element.VariableElement;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.util.ElementFilter; import javax.lang.model.util.ElementFilter;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
import java.util.Collections; import java.util.List;
import java.util.Set; import java.util.Set;
/**Generates ""value types"" classes that are packed into integer primitives of the most aproppriate size.
* It would be nice if Java didn't make crazy hacks like this necessary.*/
@SupportedSourceVersion(SourceVersion.RELEASE_8) @SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({ @SupportedAnnotationTypes({
"io.anuke.annotations.Annotations.Struct" "io.anuke.annotations.Annotations.Struct"
@@ -44,31 +47,114 @@ public class StructAnnotationProcessor extends AbstractProcessor{
Set<TypeElement> elements = ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Struct.class)); Set<TypeElement> elements = ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Struct.class));
for(TypeElement elem : elements){ for(TypeElement elem : elements){
TypeName type = TypeName.get(elem.asType());
if(!type.toString().endsWith("Struct")){ if(!elem.getSimpleName().toString().endsWith("Struct")){
Utils.messager.printMessage(Kind.ERROR, "All classes annotated with @Struct must have their class names end in 'Struct'.", elem); Utils.messager.printMessage(Kind.ERROR, "All classes annotated with @Struct must have their class names end in 'Struct'.", elem);
continue; continue;
} }
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(packageName + "." + elem.getSimpleName().toString()); String structName = elem.getSimpleName().toString().substring(0, elem.getSimpleName().toString().length() - "Struct".length());
String structParam = structName.toLowerCase();
int offset = 0; TypeSpec.Builder classBuilder = TypeSpec.classBuilder(structName)
.addModifiers(Modifier.FINAL, Modifier.PUBLIC);
for(VariableElement var : ElementFilter.fieldsIn(Collections.singletonList(elem))){ try{
if(!var.asType().getKind().isPrimitive()){ List<VariableElement> variables = ElementFilter.fieldsIn(elem.getEnclosedElements());
Utils.messager.printMessage(Kind.ERROR, "All struct fields must be primitives.", var); int structSize = variables.stream().mapToInt(StructAnnotationProcessor::varSize).sum();
int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64);
if(variables.size() == 0){
Utils.messager.printMessage(Kind.ERROR, "making a struct with no fields is utterly pointles.", elem);
continue; continue;
} }
StructField an = var.getAnnotation(StructField.class); //obtain type which will be stored
int size = an == null ? typeSize(var.asType().getKind()) : an.value(); Class<?> structType = typeForSize(structSize);
MethodSpec.Builder getter = MethodSpec.methodBuilder(var.getSimpleName().toString()); //[constructor] get(fields...) : structType
MethodSpec.Builder setter = MethodSpec.methodBuilder(var.getSimpleName().toString()); MethodSpec.Builder constructor = MethodSpec.methodBuilder("get") //TODO 'get'..?
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
.returns(structType);
StringBuilder cons = new StringBuilder();
StringBuilder doc = new StringBuilder();
doc.append("Bits used: ").append(structSize).append(" / ").append(structTotalSize).append("\n");
int offset = 0;
for(VariableElement var : variables){
int size = varSize(var);
TypeName varType = TypeName.get(var.asType());
String varName = var.getSimpleName().toString();
//add val param to constructor
constructor.addParameter(varType, varName);
//[get] field(structType) : fieldType
MethodSpec.Builder getter = MethodSpec.methodBuilder(var.getSimpleName().toString())
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
.returns(varType)
.addParameter(structType, structParam);
//[set] field(structType, fieldType) : structType
MethodSpec.Builder setter = MethodSpec.methodBuilder(var.getSimpleName().toString())
.addModifiers(Modifier.STATIC, Modifier.PUBLIC)
.returns(structType)
.addParameter(structType, structParam).addParameter(varType, "value");
//[getter]
if(varType == TypeName.BOOLEAN){
//bools: single bit, is simplified
getter.addStatement("return ($L & (1L << $L)) != 0", structParam, offset);
}else if(varType == TypeName.FLOAT){
//floats: need conversion
getter.addStatement("return Float.intBitsToFloat((int)(($L >> $L) & $L))", structParam, offset, bitString(size, structTotalSize));
}else{
//bytes, shorts, chars, ints
getter.addStatement("return ($T)(($L >> $L) & $L)", varType, structParam, offset, bitString(size, structTotalSize));
} }
//[setter] + [constructor building]
if(varType == TypeName.BOOLEAN){
cons.append(" | (").append(varName).append(" ? ").append("1L << ").append(offset).append("L : 0)");
//bools: single bit, needs special case to clear things
setter.beginControlFlow("if(value)");
setter.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset);
setter.nextControlFlow("else");
setter.addStatement("return ($T)(($L & ~(1L << $LL)) | (1L << $LL))", structType, structParam, offset, offset);
setter.endControlFlow();
}else if(varType == TypeName.FLOAT){
cons.append(" | (").append("(").append(structType).append(")").append("Float.floatToIntBits(").append(varName).append(") << ").append(offset).append("L)");
//floats: need conversion
setter.addStatement("return ($T)(($L & $L) | (($T)Float.floatToIntBits(value) << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset);
}else{
cons.append(" | (").append("(").append(structType).append(")").append(varName).append(" << ").append(offset).append("L)");
//bytes, shorts, chars, ints
setter.addStatement("return ($T)(($L & $L) | (($T)value << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset);
}
doc.append("<br> ").append(varName).append(" [").append(offset).append("..").append(size + offset).append("]\n");
//add finished methods
classBuilder.addMethod(getter.build());
classBuilder.addMethod(setter.build());
offset += size;
}
classBuilder.addJavadoc(doc.toString());
//add constructor final statement + add to class and build
constructor.addStatement("return ($T)($L)", structType, cons.toString().substring(3));
classBuilder.addMethod(constructor.build());
JavaFile.builder(packageName, classBuilder.build()).build().writeTo(Utils.filer); JavaFile.builder(packageName, classBuilder.build()).build().writeTo(Utils.filer);
}catch(IllegalArgumentException e){
e.printStackTrace();
Utils.messager.printMessage(Kind.ERROR, e.getMessage(), elem);
}
} }
return true; return true;
@@ -78,8 +164,53 @@ public class StructAnnotationProcessor extends AbstractProcessor{
} }
} }
static String bitString(int offset, int size, int totalSize){
StringBuilder builder = new StringBuilder();
for(int i = 0; i < offset; i++) builder.append('0');
for(int i = 0; i < size; i++) builder.append('1');
for(int i = 0; i < totalSize - size - offset; i++) builder.append('0');
return "0b" + builder.reverse().toString() + "L";
}
static String bitString(int size, int totalSize){
StringBuilder builder = new StringBuilder();
for(int i = 0; i < size; i++) builder.append('1');
for(int i = 0; i < totalSize - size; i++) builder.append('0');
return "0b" + builder.reverse().toString() + "L";
}
static int varSize(VariableElement var) throws IllegalArgumentException{
if(!var.asType().getKind().isPrimitive()){
throw new IllegalArgumentException("All struct fields must be primitives: " + var);
}
StructField an = var.getAnnotation(StructField.class);
if(var.asType().getKind() == TypeKind.BOOLEAN && an != null && an.value() != 1){
throw new IllegalArgumentException("Booleans can only be one bit long... why would you do this?");
}
if(var.asType().getKind() == TypeKind.FLOAT && an != null && an.value() != 32){
throw new IllegalArgumentException("Float size can't be changed. Very sad.");
}
return an == null ? typeSize(var.asType().getKind()) : an.value();
}
static Class<?> typeForSize(int size) throws IllegalArgumentException{
if(size <= 8){
return byte.class;
}else if(size <= 16){
return short.class;
}else if(size <= 32){
return int.class;
}else if(size <= 64){
return long.class;
}
throw new IllegalArgumentException("Too many fields, must fit in 64 bits. Curent size: " + size);
}
/**returns a type's element size in bits.*/ /**returns a type's element size in bits.*/
static int typeSize(TypeKind kind){ static int typeSize(TypeKind kind) throws IllegalArgumentException{
switch(kind){ switch(kind){
case BOOLEAN: case BOOLEAN:
return 1; return 1;
@@ -91,11 +222,8 @@ public class StructAnnotationProcessor extends AbstractProcessor{
case CHAR: case CHAR:
case INT: case INT:
return 32; return 32;
case LONG:
case DOUBLE:
return 64;
default: default:
throw new IllegalArgumentException("Invalid type kind: " + kind); throw new IllegalArgumentException("Invalid type kind: " + kind + ". Note that doubles and longs are not supported.");
} }
} }
} }

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1013 KiB

After

Width:  |  Height:  |  Size: 1013 KiB

View File

@@ -1,7 +1,9 @@
package io.anuke.mindustry.content; package io.anuke.mindustry.content;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw; import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.mindustry.game.ContentList; import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.graphics.CacheLayer; import io.anuke.mindustry.graphics.CacheLayer;
import io.anuke.mindustry.type.Category; import io.anuke.mindustry.type.Category;
@@ -32,8 +34,9 @@ public class Blocks implements ContentList{
public static Block public static Block
//environment //environment
air, part, spawn, space, metalfloor, deepwater, water, tar, stone, craters, charr, blackstone, dirt, sand, ice, snow, iceSnow, air, part, spawn, deepwater, water, tar, stone, craters, charr, sand, ice, snow,
grass, holostone, holostoneSnow, shrub, rock, icerock, blackrock, rocks, icerocks, cliffs, pine, whiteTree, sporeCluster, grass, holostone, rocks, icerocks, cliffs, pine, whiteTree, whiteTreeDead, sporeCluster,
iceSnow,
//crafting //crafting
siliconSmelter, graphitePress, plastaniumCompressor, multiPress, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer, siliconSmelter, graphitePress, plastaniumCompressor, multiPress, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer,
@@ -83,6 +86,12 @@ public class Blocks implements ContentList{
public void draw(Tile tile){} public void draw(Tile tile){}
public void load(){} public void load(){}
public void init(){} public void init(){}
public TextureRegion[] variantRegions(){
if(variantRegions == null){
variantRegions = new TextureRegion[]{Core.atlas.find("clear")};
}
return variantRegions;
}
}; };
part = new BlockPart(); part = new BlockPart();
@@ -97,18 +106,6 @@ public class Blocks implements ContentList{
new BuildBlock("build" + i); new BuildBlock("build" + i);
} }
space = new Floor("space"){{
placeableOn = false;
variants = 0;
cacheLayer = CacheLayer.space;
solid = true;
minimapColor = Color.valueOf("000001");
}};
metalfloor = new Floor("metalfloor"){{
variants = 6;
}};
deepwater = new Floor("deepwater"){{ deepwater = new Floor("deepwater"){{
liquidColor = Color.valueOf("546bb3"); liquidColor = Color.valueOf("546bb3");
speedMultiplier = 0.2f; speedMultiplier = 0.2f;
@@ -161,16 +158,6 @@ public class Blocks implements ContentList{
minimapColor = Color.valueOf("323232"); minimapColor = Color.valueOf("323232");
}}; }};
blackstone = new Floor("blackstone"){{
minimapColor = Color.valueOf("252525");
playerUnmineable = true;
hasOres = true;
}};
dirt = new Floor("dirt"){{
minimapColor = Color.valueOf("6e501e");
}};
sand = new Floor("sand"){{ sand = new Floor("sand"){{
itemDrop = Items.sand; itemDrop = Items.sand;
minimapColor = Color.valueOf("988a67"); minimapColor = Color.valueOf("988a67");
@@ -184,13 +171,12 @@ public class Blocks implements ContentList{
minimapColor = Color.valueOf("b8eef8"); minimapColor = Color.valueOf("b8eef8");
}}; }};
snow = new Floor("snow"){{ holostone = new Floor("holostone"){{
minimapColor = Color.valueOf("c2d1d2"); hasOres = true;
}}; }};
iceSnow = new Floor("ice-snow"){{ snow = new Floor("snow"){{
minimapColor = Color.valueOf("c2d1d2"); minimapColor = Color.valueOf("c2d1d2");
variants = 3;
}}; }};
grass = new Floor("grass"){{ grass = new Floor("grass"){{
@@ -198,8 +184,6 @@ public class Blocks implements ContentList{
minimapColor = Color.valueOf("549d5b"); minimapColor = Color.valueOf("549d5b");
}}; }};
shrub = new Rock("shrub");
cliffs = new StaticWall("cliffs"){{ cliffs = new StaticWall("cliffs"){{
variants = 1; variants = 1;
fillsTile = false; fillsTile = false;
@@ -218,19 +202,18 @@ public class Blocks implements ContentList{
variants = 0; variants = 0;
}}; }};
whiteTree = new TreeBlock("white-tree-dead"){{ whiteTreeDead = new TreeBlock("white-tree-dead"){{
}};
whiteTree = new TreeBlock("white-tree"){{
}}; }};
sporeCluster = new Rock("spore-cluster"){{ sporeCluster = new Rock("spore-cluster"){{
variants = 3; variants = 3;
}}; }};
holostone = new Floor("holostone"){{ iceSnow = new Floor("iceSnow"){{
hasOres = true; variants = 3;
}};
holostoneSnow = new Floor("holostone-snow"){{
}}; }};
//endregion //endregion

View File

@@ -167,7 +167,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
editor.beginEdit(data, meta.tags, false); editor.beginEdit(data, meta.tags, false);
view.clearStack(); view.clearStack();
}catch(Exception e){ }catch(Exception e){
ui.showError(Core.bundle.format("editor.errormapload", Strings.parseException(e, false))); ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false)));
Log.err(e); Log.err(e);
} }
})); }));

View File

@@ -201,7 +201,7 @@ public class FloorRenderer{
int chunksx = Mathf.ceil((float) (world.width()) / chunksize), int chunksx = Mathf.ceil((float) (world.width()) / chunksize),
chunksy = Mathf.ceil((float) (world.height()) / chunksize) ; chunksy = Mathf.ceil((float) (world.height()) / chunksize) ;
cache = new Chunk[chunksx][chunksy]; cache = new Chunk[chunksx][chunksy];
SpriteCache sprites = new SpriteCache(world.width() * world.height() * 3, (world.width() / chunksize) * (world.height() / chunksize) * 2, false); SpriteCache sprites = new SpriteCache(world.width() * world.height() * 5, (world.width() / chunksize) * (world.height() / chunksize) * 2, false);
cbatch = new CacheBatch(sprites); cbatch = new CacheBatch(sprites);
Time.mark(); Time.mark();

View File

@@ -13,6 +13,7 @@ import io.anuke.mindustry.maps.MapTileData.TileDataMarker;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.OreBlock; import io.anuke.mindustry.world.blocks.OreBlock;
import io.anuke.mindustry.world.blocks.StaticWall;
import io.anuke.mindustry.world.blocks.storage.CoreBlock; import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -89,7 +90,8 @@ public class MapGenerator extends Generator{
int newX = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x, y) * distortion + x), 0, data.width()-1); int newX = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x, y) * distortion + x), 0, data.width()-1);
int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, data.height()-1); int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, data.height()-1);
if(tiles[newX][newY].block() != Blocks.spawn && !tile.block().synthetic()&& !tiles[newX][newY].block().synthetic()){ if(tile.block() instanceof StaticWall
&& tiles[newX][newY].block() instanceof StaticWall){
tile.setBlock(tiles[newX][newY].block()); tile.setBlock(tiles[newX][newY].block());
} }

View File

@@ -115,7 +115,7 @@ public class Block extends BlockStorage{
protected Array<Tile> tempTiles = new Array<>(); protected Array<Tile> tempTiles = new Array<>();
protected TextureRegion[] icons = new TextureRegion[Icon.values().length]; protected TextureRegion[] icons = new TextureRegion[Icon.values().length];
protected TextureRegion[] generatedIcons; protected TextureRegion[] generatedIcons;
protected TextureRegion[] variants; protected TextureRegion[] variantRegions;
protected TextureRegion region; protected TextureRegion region;
public Block(String name){ public Block(String name){
@@ -503,10 +503,10 @@ public class Block extends BlockStorage{
} }
public TextureRegion[] variantRegions(){ public TextureRegion[] variantRegions(){
if(variants == null){ if(variantRegions == null){
variants = new TextureRegion[]{icon(Icon.full)}; variantRegions = new TextureRegion[]{icon(Icon.full)};
} }
return variants; return variantRegions;
} }
public boolean hasEntity(){ public boolean hasEntity(){

View File

@@ -20,7 +20,8 @@ public class LegacyColorMapper implements ContentList{
public void load(){ public void load(){
defaultValue = new LegacyBlock(Blocks.stone, Blocks.air); defaultValue = new LegacyBlock(Blocks.stone, Blocks.air);
map("ff0000", Blocks.dirt, 0); //TODO remap colors later
// map("ff0000", Blocks.dirt, 0);
map("00ff00", Blocks.stone, 0); map("00ff00", Blocks.stone, 0);
map("323232", Blocks.stone, 0); map("323232", Blocks.stone, 0);
map("646464", Blocks.stone, 1); map("646464", Blocks.stone, 1);
@@ -28,15 +29,15 @@ public class LegacyColorMapper implements ContentList{
map("5ab464", Blocks.grass, 1); map("5ab464", Blocks.grass, 1);
map("506eb4", Blocks.water, 0); map("506eb4", Blocks.water, 0);
map("465a96", Blocks.deepwater, 0); map("465a96", Blocks.deepwater, 0);
map("252525", Blocks.blackstone, 0); //map("252525", Blocks.blackstone, 0);
map("575757", Blocks.blackstone, 1); //map("575757", Blocks.blackstone, 1);
map("988a67", Blocks.sand, 0); map("988a67", Blocks.sand, 0);
map("e5d8bb", Blocks.sand, 1); map("e5d8bb", Blocks.sand, 1);
map("c2d1d2", Blocks.snow, 0); map("c2d1d2", Blocks.snow, 0);
map("c4e3e7", Blocks.ice, 0); map("c4e3e7", Blocks.ice, 0);
map("f7feff", Blocks.snow, 1); map("f7feff", Blocks.snow, 1);
map("6e501e", Blocks.dirt, 0); //map("6e501e", Blocks.dirt, 0);
map("ed5334", Blocks.blackstone, 0); //map("ed5334", Blocks.blackstone, 0);
map("292929", Blocks.tar, 0); map("292929", Blocks.tar, 0);
map("c3a490", OreBlock.get(Blocks.stone, Items.copper), 0); map("c3a490", OreBlock.get(Blocks.stone, Items.copper), 0);
map("161616", OreBlock.get(Blocks.stone, Items.coal), 0); map("161616", OreBlock.get(Blocks.stone, Items.coal), 0);

View File

@@ -6,6 +6,8 @@ import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw; import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.TextureRegion; import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Point2;
import io.anuke.mindustry.content.Fx; import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.content.StatusEffects; import io.anuke.mindustry.content.StatusEffects;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
@@ -51,8 +53,9 @@ public class Floor extends Block{
public float heat = 0f; public float heat = 0f;
/** if true, this block cannot be mined by players. useful for annoying things like sand. */ /** if true, this block cannot be mined by players. useful for annoying things like sand. */
public boolean playerUnmineable = false; public boolean playerUnmineable = false;
protected TextureRegion[] regions;
protected TextureRegion[][] edges; protected TextureRegion[][] edges;
protected byte eq = 0;
public Floor(String name){ public Floor(String name){
super(name); super(name);
@@ -65,24 +68,26 @@ public class Floor extends Block{
//load variant regions for drawing //load variant regions for drawing
if(variants > 0){ if(variants > 0){
regions = new TextureRegion[variants]; variantRegions = new TextureRegion[variants];
for(int i = 0; i < variants; i++){ for(int i = 0; i < variants; i++){
regions[i] = Core.atlas.find(name + (i + 1)); variantRegions[i] = Core.atlas.find(name + (i + 1));
} }
}else{ }else{
regions = new TextureRegion[1]; variantRegions = new TextureRegion[1];
regions[0] = Core.atlas.find(name); variantRegions[0] = Core.atlas.find(name);
} }
int size = (int)(tilesize / Draw.scl); int size = (int)(tilesize / Draw.scl);
if(Core.atlas.has(name + "-edge")){
edges = Core.atlas.find(name + "-edge").split(size, size); edges = Core.atlas.find(name + "-edge").split(size, size);
region = regions[0]; }
region = variantRegions[0];
} }
@Override @Override
public TextureRegion[] variantRegions(){ public TextureRegion[] generateIcons(){
return regions; return new TextureRegion[]{Core.atlas.find(Core.atlas.has(name) ? name : name + "1")};
} }
@Override @Override
@@ -98,12 +103,57 @@ public class Floor extends Block{
public void draw(Tile tile){ public void draw(Tile tile){
Mathf.random.setSeed(tile.pos()); Mathf.random.setSeed(tile.pos());
Draw.rect(regions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, regions.length - 1))], tile.worldx(), tile.worldy()); Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
drawEdges(tile);
} }
@Override protected void drawEdges(Tile tile){
public TextureRegion[] generateIcons(){ eq = 0;
return new TextureRegion[]{Core.atlas.find(Core.atlas.has(name) ? name : name + "1")};
Floor floor = tile.floor();
for(int i = 0; i < 8; i++){
Point2 point = Geometry.d8[i];
Tile other = tile.getNearby(point);
if(other != null && other.floor().id < floor.id && other.floor().edges != null){
eq |= (1 << i);
}
}
for(int i = 0; i < 8; i++){
if(eq(i)){
Point2 point = Geometry.d8[i];
Tile other = tile.getNearby(point);
TextureRegion region = edge(other.floor(), type(i), 2-(point.x + 1), 2-(point.y + 1));
Draw.rect(region, tile.worldx(), tile.worldy());
}
}
}
int type(int i){
if(!eq(i - 1) && !eq(i + 1)){
//case 0: touching
return 0;
}else if(eq(i - 1) && eq(i - 2) && eq(i + 1) && eq(i + 2)){
//case 2: surrounded
return 2;
}else if(eq(i - 1) && eq(i + 1)){
//case 1: flat
return 1;
}else{
//case 0 is rounded, so it's the safest choice, should work for most possibilities
return 0;
}
}
boolean eq(int i){
return (eq & (1 << Mathf.mod(i, 8))) != 0;
}
TextureRegion edge(Floor block, int type, int x, int y){
return block.edges[x + type*3][2-y];
} }
} }

View File

@@ -31,7 +31,7 @@ public class OreBlock extends Floor{
@Override @Override
public void draw(Tile tile){ public void draw(Tile tile){
Draw.rect(regions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, regions.length - 1))], tile.worldx(), tile.worldy()); Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
} }
public static Block get(Block floor, Item item){ public static Block get(Block floor, Item item){

View File

@@ -9,6 +9,7 @@ import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Mech; import io.anuke.mindustry.type.Mech;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon; import io.anuke.mindustry.world.Block.Icon;
import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.OreBlock; import io.anuke.mindustry.world.blocks.OreBlock;
import static io.anuke.mindustry.Vars.content; import static io.anuke.mindustry.Vars.content;
@@ -143,6 +144,31 @@ public class Generators {
} }
} }
}); });
ImagePacker.generate("edges", () -> {
for(Block block : content.blocks()){
if(!(block instanceof Floor)) continue;
Floor floor = (Floor)block;
if(ImagePacker.has(floor.name + "-edge")){
continue;
}
try{
Image image = ImagePacker.get(floor.generateIcons()[0]);
Image edge = ImagePacker.get("edge-stencil");
for(int x = 0; x < edge.width(); x++){
for(int y = 0; y < edge.height(); y++){
edge.draw(x, y, edge.getColor(x, y).mul(image.getColor(x % image.width(), y % image.height())));
}
}
edge.save(floor.name + "-edge");
}catch(Exception ignored){}
}
});
} }
} }

View File

@@ -111,6 +111,10 @@ public class ImagePacker{
return get(Core.atlas.find(name)); return get(Core.atlas.find(name));
} }
static boolean has(String name){
return Core.atlas.has(name);
}
static Image get(TextureRegion region){ static Image get(TextureRegion region){
GenRegion.validate(region); GenRegion.validate(region);