mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-01-14 21:32:39 -08:00
Implemented correct map loading, legacy format support
This commit is contained in:
parent
0fd0abc6c0
commit
20427fdffa
15 changed files with 474 additions and 180 deletions
|
|
@ -30,6 +30,8 @@ import java.util.Locale;
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Vars{
|
||||
/**IO buffer size.*/
|
||||
public static final int bufferSize = 8192;
|
||||
/**global charset*/
|
||||
public static final Charset charset = Charset.forName("UTF-8");
|
||||
/**main application name, capitalized*/
|
||||
|
|
|
|||
|
|
@ -235,7 +235,7 @@ public class World implements ApplicationListener{
|
|||
|
||||
try{
|
||||
createTiles(map.width, map.height);
|
||||
tiles = MapIO.readTiles(map, tiles);
|
||||
MapIO.readTiles(map, tiles);
|
||||
prepareTiles(tiles);
|
||||
}catch(Exception e){
|
||||
Log.err(e);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,12 @@ package io.anuke.mindustry.editor;
|
|||
|
||||
import io.anuke.annotations.Annotations.Struct;
|
||||
import io.anuke.arc.collection.LongArray;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.gen.TileOp;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
|
||||
import static io.anuke.mindustry.Vars.content;
|
||||
|
||||
public class DrawOperation{
|
||||
private LongArray array = new LongArray();
|
||||
|
|
@ -17,12 +23,28 @@ public class DrawOperation{
|
|||
public void undo(MapEditor editor){
|
||||
for(int i = 0; i < array.size; i++){
|
||||
long l = array.get(i);
|
||||
set(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.from(l));
|
||||
}
|
||||
}
|
||||
|
||||
public void redo(MapEditor editor){
|
||||
for(int i = 0; i < array.size; i++){
|
||||
long l = array.get(i);
|
||||
set(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.to(l));
|
||||
}
|
||||
}
|
||||
|
||||
void set(Tile tile, byte type, byte to){
|
||||
if(type == OpType.floor.ordinal()){
|
||||
tile.setFloor((Floor)content.block(to));
|
||||
}else if(type == OpType.block.ordinal()){
|
||||
tile.setBlock(content.block(to));
|
||||
}else if(type == OpType.rotation.ordinal()){
|
||||
tile.setRotation(to);
|
||||
}else if(type == OpType.team.ordinal()){
|
||||
tile.setTeam(Team.all[to]);
|
||||
}else if(type == OpType.ore.ordinal()){
|
||||
tile.setOre(to);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -39,6 +61,7 @@ public class DrawOperation{
|
|||
floor,
|
||||
block,
|
||||
rotation,
|
||||
team
|
||||
team,
|
||||
ore
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,10 @@ import io.anuke.mindustry.gen.TileOp;
|
|||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
import io.anuke.mindustry.world.modules.ConsumeModule;
|
||||
import io.anuke.mindustry.world.modules.ItemModule;
|
||||
import io.anuke.mindustry.world.modules.LiquidModule;
|
||||
import io.anuke.mindustry.world.modules.PowerModule;
|
||||
|
||||
import static io.anuke.mindustry.Vars.ui;
|
||||
|
||||
|
|
@ -28,7 +32,7 @@ public class EditorTile extends Tile{
|
|||
Block previous = block();
|
||||
if(previous == type) return;
|
||||
super.setBlock(type);
|
||||
op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous.id, type.id));
|
||||
op(TileOp.get(x, y, (byte)OpType.block.ordinal(), previous.id, type.id));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -36,7 +40,7 @@ public class EditorTile extends Tile{
|
|||
byte previous = getTeamID();
|
||||
if(previous == team.ordinal()) return;
|
||||
super.setTeam(team);
|
||||
op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous, (byte)team.ordinal()));
|
||||
op(TileOp.get(x, y, (byte)OpType.team.ordinal(), previous, (byte)team.ordinal()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -44,7 +48,35 @@ public class EditorTile extends Tile{
|
|||
byte previous = getRotation();
|
||||
if(previous == rotation) return;
|
||||
super.setRotation(rotation);
|
||||
op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous, rotation));
|
||||
op(TileOp.get(x, y, (byte)OpType.rotation.ordinal(), previous, rotation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOre(byte ore){
|
||||
byte previous = getRotation();
|
||||
if(previous == ore) return;
|
||||
super.setOre(ore);
|
||||
op(TileOp.get(x, y, (byte)OpType.ore.ordinal(), previous, ore));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preChanged(){
|
||||
super.setTeam(Team.none);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void changed(){
|
||||
entity = null;
|
||||
|
||||
Block block = block();
|
||||
|
||||
if(block.hasEntity()){
|
||||
entity = block.newEntity();
|
||||
entity.cons = new ConsumeModule(entity);
|
||||
if(block.hasItems) entity.items = new ItemModule();
|
||||
if(block.hasLiquids) entity.liquids = new LiquidModule();
|
||||
if(block.hasPower) entity.power = new PowerModule();
|
||||
}
|
||||
}
|
||||
|
||||
private static void op(long op){
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package io.anuke.mindustry.editor;
|
|||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Log;
|
||||
import io.anuke.arc.util.Pack;
|
||||
import io.anuke.arc.util.Structs;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package io.anuke.mindustry.io;
|
||||
|
||||
import io.anuke.arc.collection.IntIntMap;
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.collection.ObjectMap.Entry;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
|
|
@ -13,26 +14,40 @@ import io.anuke.mindustry.game.MappableContent;
|
|||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.CachedTile;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import static io.anuke.mindustry.Vars.bufferSize;
|
||||
import static io.anuke.mindustry.Vars.content;
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
|
||||
/** Reads and writes map files.*/
|
||||
public class MapIO{
|
||||
private static final int version = 1;
|
||||
public static final int version = 1;
|
||||
|
||||
private static final int[] pngHeader = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||
private static ObjectMap<String, Block> missingBlocks;
|
||||
|
||||
private static void initBlocks(){
|
||||
if(missingBlocks != null) return;
|
||||
|
||||
missingBlocks = ObjectMap.of(
|
||||
"stained-stone", Blocks.moss
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean isImage(FileHandle file){
|
||||
try(InputStream stream = file.read()){
|
||||
try(InputStream stream = file.read(32)){
|
||||
for(int i1 : pngHeader){
|
||||
if(stream.read() != i1){
|
||||
return false;
|
||||
|
|
@ -45,8 +60,20 @@ public class MapIO{
|
|||
}
|
||||
|
||||
//TODO implement
|
||||
public static Pixmap generatePreview(Map map){
|
||||
return null;
|
||||
public static Pixmap generatePreview(Map map) throws IOException{
|
||||
Pixmap pixmap = new Pixmap(map.width, map.height, Format.RGBA8888);
|
||||
CachedTile tile = new CachedTile(){
|
||||
@Override
|
||||
protected void changed(){
|
||||
pixmap.drawPixel(x, pixmap.getHeight() - 1 - y, colorFor(floor(), block(), getTeam()));
|
||||
}
|
||||
};
|
||||
readTiles(map, (x, y) -> {
|
||||
tile.x = (short)x;
|
||||
tile.y = (short)y;
|
||||
return tile;
|
||||
});
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
public static Pixmap generatePreview(Tile[][] tiles){
|
||||
|
|
@ -60,6 +87,222 @@ public class MapIO{
|
|||
return pixmap;
|
||||
}
|
||||
|
||||
public static int colorFor(Block floor, Block wall, Team team){
|
||||
if(wall.synthetic()){
|
||||
return team.intColor;
|
||||
}
|
||||
return Color.rgba8888(wall.solid ? wall.color : floor.color);
|
||||
}
|
||||
|
||||
public static void writeMap(FileHandle file, Map map, Tile[][] tiles) throws IOException{
|
||||
OutputStream output = file.write(false, bufferSize);
|
||||
|
||||
{
|
||||
DataOutputStream stream = new DataOutputStream(output);
|
||||
stream.writeInt(version);
|
||||
stream.writeInt(Version.build);
|
||||
stream.writeShort(tiles.length);
|
||||
stream.writeShort(tiles[0].length);
|
||||
stream.writeByte((byte)map.tags.size);
|
||||
|
||||
for(Entry<String, String> entry : map.tags.entries()){
|
||||
stream.writeUTF(entry.key);
|
||||
stream.writeUTF(entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
|
||||
int width = map.width, height = map.height;
|
||||
|
||||
SaveIO.getSaveWriter().writeContentHeader(stream);
|
||||
|
||||
//floor first
|
||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
||||
Tile tile = tiles[i % width][i / width];
|
||||
stream.writeByte(tile.getFloorID());
|
||||
stream.writeByte(tile.getOre());
|
||||
int consecutives = 0;
|
||||
|
||||
for(int j = i + 1; j < width * height && consecutives < 255; j++){
|
||||
Tile nextTile = tiles[j % width][j / width];
|
||||
|
||||
if(nextTile.getFloorID() != tile.getFloorID() || nextTile.block() != Blocks.air || nextTile.getOre() != tile.getOre()){
|
||||
break;
|
||||
}
|
||||
|
||||
consecutives++;
|
||||
}
|
||||
|
||||
stream.writeByte(consecutives);
|
||||
i += consecutives;
|
||||
}
|
||||
|
||||
//then blocks
|
||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
||||
Tile tile = tiles[i % width][i / width];
|
||||
stream.writeByte(tile.getBlockID());
|
||||
|
||||
if(tile.block() instanceof BlockPart){
|
||||
stream.writeByte(tile.link);
|
||||
}else if(tile.entity != null){
|
||||
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation
|
||||
stream.writeShort((short)tile.entity.health); //health
|
||||
tile.entity.writeConfig(stream);
|
||||
}else{
|
||||
//write consecutive non-entity blocks
|
||||
int consecutives = 0;
|
||||
|
||||
for(int j = i + 1; j < width * height && consecutives < 255; j++){
|
||||
Tile nextTile = tiles[j % width][j / width];
|
||||
|
||||
if(nextTile.block() != tile.block()){
|
||||
break;
|
||||
}
|
||||
|
||||
consecutives++;
|
||||
}
|
||||
|
||||
stream.writeByte(consecutives);
|
||||
i += consecutives;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map readMap(FileHandle file, boolean custom) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(file.read(1024))){
|
||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
||||
|
||||
//meta is uncompressed
|
||||
int version = stream.readInt();
|
||||
if(version == 0){
|
||||
return readLegacyMap(file, custom);
|
||||
}
|
||||
int build = stream.readInt();
|
||||
short width = stream.readShort(), height = stream.readShort();
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
String name = stream.readUTF();
|
||||
String value = stream.readUTF();
|
||||
tags.put(name, value);
|
||||
}
|
||||
|
||||
return new Map(file, width, height, tags, custom, version, build);
|
||||
}
|
||||
}
|
||||
|
||||
/**Reads tiles from a map, version-agnostic.*/
|
||||
public static void readTiles(Map map, Tile[][] tiles) throws IOException{
|
||||
readTiles(map, (x, y) -> tiles[x][y]);
|
||||
}
|
||||
|
||||
/**Reads tiles from a map, version-agnostic.*/
|
||||
public static void readTiles(Map map, TileProvider tiles) throws IOException{
|
||||
if(map.version == 0){
|
||||
readLegacyMmapTiles(map.file, tiles);
|
||||
}else if(map.version == version){
|
||||
readTiles(map.file, map.width, map.height, tiles);
|
||||
}else{
|
||||
throw new IOException("Unknown map version. What?");
|
||||
}
|
||||
}
|
||||
|
||||
/**Reads tiles from a map in the new build-65 format.*/
|
||||
private static void readTiles(FileHandle file, int width, int height, Tile[][] tiles) throws IOException{
|
||||
readTiles(file, width, height, (x, y) -> tiles[x][y]);
|
||||
}
|
||||
|
||||
/**Reads tiles from a map in the new build-65 format.*/
|
||||
private static void readTiles(FileHandle file, int width, int height, TileProvider tiles) throws IOException{
|
||||
try(BufferedInputStream input = file.read(bufferSize)){
|
||||
|
||||
//read map
|
||||
{
|
||||
DataInputStream stream = new DataInputStream(input);
|
||||
|
||||
stream.readInt(); //version
|
||||
stream.readInt(); //build
|
||||
stream.readInt(); //width + height
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
stream.readUTF();
|
||||
stream.readUTF();
|
||||
}
|
||||
}
|
||||
|
||||
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){
|
||||
|
||||
MappableContent[][] c = SaveIO.getSaveWriter().readContentHeader(stream);
|
||||
|
||||
try{
|
||||
content.setTemporaryMapper(c);
|
||||
|
||||
//read floor and create tiles first
|
||||
for(int i = 0; i < width * height; i++){
|
||||
int x = i % width, y = i / width;
|
||||
byte floorid = stream.readByte();
|
||||
byte oreid = stream.readByte();
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
Tile tile = tiles.get(x, y);
|
||||
tile.setFloor((Floor)content.block(floorid));
|
||||
tile.setOre(oreid);
|
||||
|
||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||
int newx = j % width, newy = j / width;
|
||||
Tile newTile = tiles.get(newx, newy);
|
||||
newTile.setFloor((Floor)content.block(floorid));
|
||||
newTile.setOre(oreid);
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
|
||||
//read blocks
|
||||
for(int i = 0; i < width * height; i++){
|
||||
int x = i % width, y = i / width;
|
||||
Block block = content.block(stream.readByte());
|
||||
|
||||
Tile tile = tiles.get(x, y);
|
||||
tile.setBlock(block);
|
||||
|
||||
if(block == Blocks.part){
|
||||
tile.link = stream.readByte();
|
||||
}else if(tile.entity != null){
|
||||
byte tr = stream.readByte();
|
||||
short health = stream.readShort();
|
||||
|
||||
byte team = Pack.leftByte(tr);
|
||||
byte rotation = Pack.rightByte(tr);
|
||||
|
||||
tile.setTeam(Team.all[team]);
|
||||
tile.entity.health = health;
|
||||
tile.setRotation(rotation);
|
||||
|
||||
tile.entity.readConfig(stream);
|
||||
}else{ //no entity/part, read consecutives
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||
int newx = j % width, newy = j / width;
|
||||
tiles.get(newx, newy).setBlock(block);
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
}
|
||||
|
||||
}finally{
|
||||
content.setTemporaryMapper(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region legacy IO
|
||||
|
||||
/**Reads a pixmap in the 3.5 pixmap format.*/
|
||||
public static void readLegacyPixmap(Pixmap pixmap, Tile[][] tiles){
|
||||
for(int x = 0; x < pixmap.getWidth(); x++){
|
||||
|
|
@ -96,100 +339,88 @@ public class MapIO{
|
|||
}
|
||||
}
|
||||
|
||||
//TODO implement
|
||||
/**Reads a pixmap in the old 4.0 .mmap format.*/
|
||||
private static void readLegacyMmap(FileHandle file, Tile[][] tiles) throws IOException{
|
||||
|
||||
private static void readLegacyMmapTiles(FileHandle file, Tile[][] tiles) throws IOException{
|
||||
readLegacyMmapTiles(file, (x, y) -> tiles[x][y]);
|
||||
}
|
||||
|
||||
public static int colorFor(Block floor, Block wall, Team team){
|
||||
if(wall.synthetic()){
|
||||
return team.intColor;
|
||||
}
|
||||
return Color.rgba8888(wall.solid ? wall.color : floor.color);
|
||||
}
|
||||
//TODO implement
|
||||
/**Reads a mmap in the old 4.0 .mmap format.*/
|
||||
private static void readLegacyMmapTiles(FileHandle file, TileProvider tiles) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(file.read(bufferSize))){
|
||||
stream.readInt(); //version
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
public static void writeMap(FileHandle file, Map map, Tile[][] tiles) throws IOException{
|
||||
OutputStream output = file.write(false);
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(output)){
|
||||
stream.writeInt(version);
|
||||
stream.writeInt(Version.build);
|
||||
stream.writeShort(tiles.length);
|
||||
stream.writeShort(tiles[0].length);
|
||||
stream.writeByte((byte)map.tags.size);
|
||||
|
||||
for(Entry<String, String> entry : map.tags.entries()){
|
||||
stream.writeUTF(entry.key);
|
||||
stream.writeUTF(entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
|
||||
|
||||
SaveIO.getSaveWriter().writeContentHeader(stream);
|
||||
|
||||
//floor first
|
||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
||||
Tile tile = world.tile(i % world.width(), i / world.width());
|
||||
stream.writeByte(tile.getFloorID());
|
||||
stream.writeByte(tile.getOre());
|
||||
int consecutives = 0;
|
||||
|
||||
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
|
||||
Tile nextTile = world.tile(j % world.width(), j / world.width());
|
||||
|
||||
if(nextTile.getFloorID() != tile.getFloorID() || nextTile.block() != Blocks.air || nextTile.getOre() != tile.getOre()){
|
||||
break;
|
||||
}
|
||||
|
||||
consecutives++;
|
||||
}
|
||||
|
||||
stream.writeByte(consecutives);
|
||||
i += consecutives;
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
stream.readUTF(); //key
|
||||
stream.readUTF(); //val
|
||||
}
|
||||
|
||||
//then blocks
|
||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
||||
Tile tile = world.tile(i % world.width(), i / world.width());
|
||||
stream.writeByte(tile.getBlockID());
|
||||
initBlocks();
|
||||
|
||||
if(tile.block() instanceof BlockPart){
|
||||
stream.writeByte(tile.link);
|
||||
}else if(tile.entity != null){
|
||||
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation
|
||||
stream.writeShort((short)tile.entity.health); //health
|
||||
tile.entity.writeConfig(stream);
|
||||
}else{
|
||||
//write consecutive non-entity blocks
|
||||
int consecutives = 0;
|
||||
//block id -> real id map
|
||||
IntIntMap map = new IntIntMap();
|
||||
IntIntMap oreMap = new IntIntMap();
|
||||
|
||||
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
|
||||
Tile nextTile = world.tile(j % world.width(), j / world.width());
|
||||
|
||||
if(nextTile.block() != tile.block()){
|
||||
break;
|
||||
short blocks = stream.readShort();
|
||||
for(int i = 0; i < blocks; i++){
|
||||
short id = stream.readShort();
|
||||
String name = stream.readUTF();
|
||||
Block block = content.getByName(ContentType.block, name);
|
||||
if(block == null){
|
||||
//substitute for replacement in missingBlocks if possible
|
||||
if(missingBlocks.containsKey(name)){
|
||||
block = missingBlocks.get(name);
|
||||
}else if(name.startsWith("ore-")){ //an ore floor combination
|
||||
String[] split = name.split("-");
|
||||
String itemName = split[1], floorName = split[2];
|
||||
Item item = content.getByName(ContentType.item, itemName);
|
||||
Block floor = content.getByName(ContentType.block, floorName);
|
||||
if(item != null && floor != null){
|
||||
oreMap.put(id, item.id);
|
||||
block = floor;
|
||||
}else{
|
||||
block = Blocks.air;
|
||||
}
|
||||
|
||||
consecutives++;
|
||||
}else{
|
||||
block = Blocks.air;
|
||||
}
|
||||
|
||||
stream.writeByte(consecutives);
|
||||
i += consecutives;
|
||||
}
|
||||
map.put(id, block.id);
|
||||
}
|
||||
short width = stream.readShort(), height = stream.readShort();
|
||||
|
||||
for(int y = 0; y < height; y++){
|
||||
for(int x = 0; x < width; x++){
|
||||
Tile tile = tiles.get(x, y);
|
||||
byte floorb = stream.readByte();
|
||||
byte blockb = stream.readByte();
|
||||
byte rotTeamb = stream.readByte();
|
||||
byte linkb = stream.readByte();
|
||||
stream.readByte(); //unused stuff
|
||||
|
||||
tile.setFloor((Floor)content.block(map.get(floorb, 0)));
|
||||
tile.setBlock(content.block(map.get(blockb, 0)));
|
||||
tile.setRotation(Pack.leftByte(rotTeamb));
|
||||
if(tile.block() == Blocks.part){
|
||||
tile.setLinkByte(linkb);
|
||||
}
|
||||
|
||||
if(oreMap.containsKey(floorb)){
|
||||
tile.setOre((byte)oreMap.get(floorb, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Map readMap(FileHandle file, boolean custom) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(file.read())){
|
||||
private static Map readLegacyMap(FileHandle file, boolean custom) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(file.read(bufferSize))){
|
||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
||||
|
||||
//meta is uncompressed
|
||||
int version = stream.readInt();
|
||||
int build = stream.readInt();
|
||||
short width = stream.readShort(), height = stream.readShort();
|
||||
if(version != 0) throw new IOException("Attempted to read non-legacy map in legacy method!");
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
|
|
@ -198,83 +429,21 @@ public class MapIO{
|
|||
tags.put(name, value);
|
||||
}
|
||||
|
||||
return new Map(file, width, height, tags, custom);
|
||||
}
|
||||
}
|
||||
|
||||
public static Tile[][] readTiles(Map map, Tile[][] tiles) throws IOException{
|
||||
return readTiles(map.file, map.width, map.height, tiles);
|
||||
}
|
||||
|
||||
public static Tile[][] readTiles(FileHandle file, int width, int height, Tile[][] tiles) throws IOException{
|
||||
readMap(file, false);
|
||||
|
||||
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(file.read()))){
|
||||
|
||||
MappableContent[][] c = SaveIO.getSaveWriter().readContentHeader(stream);
|
||||
|
||||
try{
|
||||
content.setTemporaryMapper(c);
|
||||
|
||||
//read floor and create tiles first
|
||||
for(int i = 0; i < width * height; i++){
|
||||
int x = i % width, y = i / width;
|
||||
byte floorid = stream.readByte();
|
||||
byte oreid = stream.readByte();
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
tiles[x][y] = new Tile(x, y, floorid, (byte)0);
|
||||
tiles[x][y].setOre(oreid);
|
||||
|
||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||
int newx = j % width, newy = j / width;
|
||||
Tile newTile = new Tile(newx, newy, floorid, (byte)0);
|
||||
newTile.setOre(oreid);
|
||||
tiles[newx][newy] = newTile;
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
|
||||
//read blocks
|
||||
for(int i = 0; i < width * height; i++){
|
||||
int x = i % width, y = i / width;
|
||||
Block block = content.block(stream.readByte());
|
||||
|
||||
Tile tile = tiles[x][y];
|
||||
tile.setBlock(block);
|
||||
|
||||
if(block == Blocks.part){
|
||||
tile.link = stream.readByte();
|
||||
}else if(tile.entity != null){
|
||||
byte tr = stream.readByte();
|
||||
short health = stream.readShort();
|
||||
|
||||
byte team = Pack.leftByte(tr);
|
||||
byte rotation = Pack.rightByte(tr);
|
||||
|
||||
tile.setTeam(Team.all[team]);
|
||||
tile.entity.health = health;
|
||||
tile.setRotation(rotation);
|
||||
|
||||
tile.entity.readConfig(stream);
|
||||
}else{ //no entity/part, read consecutives
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||
int newx = j % width, newy = j / width;
|
||||
tiles[newx][newy].setBlock(block);
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
}
|
||||
|
||||
return tiles;
|
||||
|
||||
}finally{
|
||||
content.setTemporaryMapper(null);
|
||||
short blocks = stream.readShort();
|
||||
for(int i = 0; i < blocks; i++){
|
||||
stream.readShort();
|
||||
stream.readUTF();
|
||||
}
|
||||
short width = stream.readShort(), height = stream.readShort();
|
||||
|
||||
//note that build 64 is the default build of all maps <65; while this can be inaccurate it's better than nothing
|
||||
return new Map(file, width, height, tags, custom, 0, 64);
|
||||
}
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
interface TileProvider{
|
||||
Tile get(int x, int y);
|
||||
}
|
||||
}
|
||||
|
|
@ -48,7 +48,7 @@ public class SaveIO{
|
|||
}
|
||||
|
||||
public static DataInputStream getSlotStream(int slot){
|
||||
return new DataInputStream(new InflaterInputStream(fileFor(slot).read()));
|
||||
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize)));
|
||||
}
|
||||
|
||||
public static boolean isSaveValid(int slot){
|
||||
|
|
@ -60,7 +60,7 @@ public class SaveIO{
|
|||
}
|
||||
|
||||
public static boolean isSaveValid(FileHandle file){
|
||||
return isSaveValid(new DataInputStream(new InflaterInputStream(file.read())));
|
||||
return isSaveValid(new DataInputStream(new InflaterInputStream(file.read(bufferSize))));
|
||||
}
|
||||
|
||||
public static boolean isSaveValid(DataInputStream stream){
|
||||
|
|
@ -95,7 +95,7 @@ public class SaveIO{
|
|||
}
|
||||
|
||||
public static void write(FileHandle file){
|
||||
write(new DeflaterOutputStream(file.write(false)){
|
||||
write(new DeflaterOutputStream(file.write(false, bufferSize)){
|
||||
byte[] tmp = {0};
|
||||
|
||||
public void write(int var1) throws IOException {
|
||||
|
|
@ -120,12 +120,12 @@ public class SaveIO{
|
|||
public static void load(FileHandle file) throws SaveException{
|
||||
try{
|
||||
//try and load; if any exception at all occurs
|
||||
load(new InflaterInputStream(file.read()));
|
||||
load(new InflaterInputStream(file.read(bufferSize)));
|
||||
}catch(SaveException e){
|
||||
e.printStackTrace();
|
||||
FileHandle backup = file.sibling(file.name() + "-backup." + file.extension());
|
||||
if(backup.exists()){
|
||||
load(new InflaterInputStream(backup.read()));
|
||||
load(new InflaterInputStream(backup.read(bufferSize)));
|
||||
}else{
|
||||
throw new SaveException(e.getCause());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import io.anuke.arc.Core;
|
|||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.graphics.Texture;
|
||||
import io.anuke.mindustry.io.MapIO;
|
||||
|
||||
public class Map{
|
||||
/** Whether this is a custom map.*/
|
||||
|
|
@ -12,17 +13,31 @@ public class Map{
|
|||
public final ObjectMap<String, String> tags;
|
||||
/** Base file of this map.*/
|
||||
public final FileHandle file;
|
||||
/**Map width/height, shorts.*/
|
||||
/** Format version.*/
|
||||
public final int version;
|
||||
/** Map width/height, shorts.*/
|
||||
public int width, height;
|
||||
/** Preview texture.*/
|
||||
public Texture texture;
|
||||
/** Build that this map was created in. -1 = unknown or custom build.*/
|
||||
public int build;
|
||||
|
||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom){
|
||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom, int version, int build){
|
||||
this.custom = custom;
|
||||
this.tags = tags;
|
||||
this.file = file;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.version = version;
|
||||
this.build = build;
|
||||
}
|
||||
|
||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom, int version){
|
||||
this(file, width, height, tags, custom, version, -1);
|
||||
}
|
||||
|
||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom){
|
||||
this(file, width, height, tags, custom, MapIO.version);
|
||||
}
|
||||
|
||||
public String fileName(){
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ public class Maps implements Disposable{
|
|||
}
|
||||
|
||||
/** Import a map, then save it. This updates all values and stored data necessary. */
|
||||
public void importMap(FileHandle file, Map map){
|
||||
public void importMap(FileHandle file, Map map) throws IOException{
|
||||
file.copyTo(customMapDirectory.child(file.name()));
|
||||
if(!headless){
|
||||
map.texture = new Texture(MapIO.generatePreview(map));
|
||||
|
|
|
|||
|
|
@ -39,8 +39,13 @@ public class MapsDialog extends FloatingDialog{
|
|||
ui.showError(Core.bundle.format("editor.import.exists", name));
|
||||
}else if(conflict != null){
|
||||
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
|
||||
world.maps.importMap(file, map);
|
||||
setup();
|
||||
try{
|
||||
world.maps.importMap(file, map);
|
||||
setup();
|
||||
}catch(Exception e){
|
||||
ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false)));
|
||||
Log.err(e);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
world.maps.importMap(file, map);
|
||||
|
|
|
|||
46
core/src/io/anuke/mindustry/world/CachedTile.java
Normal file
46
core/src/io/anuke/mindustry/world/CachedTile.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package io.anuke.mindustry.world;
|
||||
|
||||
import io.anuke.arc.collection.IntMap;
|
||||
import io.anuke.mindustry.entities.type.TileEntity;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.world.modules.ConsumeModule;
|
||||
import io.anuke.mindustry.world.modules.ItemModule;
|
||||
import io.anuke.mindustry.world.modules.LiquidModule;
|
||||
import io.anuke.mindustry.world.modules.PowerModule;
|
||||
|
||||
/**A tile which does not trigger change events and whose entity types are cached.
|
||||
* Prevents garbage when loading previews.*/
|
||||
public class CachedTile extends Tile{
|
||||
private static IntMap<TileEntity> entities = new IntMap<>();
|
||||
|
||||
public CachedTile(){
|
||||
super(0, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void preChanged(){
|
||||
super.setTeam(Team.none);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void changed(){
|
||||
entity = null;
|
||||
|
||||
Block block = block();
|
||||
|
||||
if(block.hasEntity()){
|
||||
//cache all entity types so only one is ever created per block type. do not add it.
|
||||
if(!entities.containsKey(block.id)){
|
||||
TileEntity n = block.newEntity();
|
||||
n.cons = new ConsumeModule(entity);
|
||||
if(block.hasItems) n.items = new ItemModule();
|
||||
if(block.hasLiquids) n.liquids = new LiquidModule();
|
||||
if(block.hasPower) n.power = new PowerModule();
|
||||
entities.put(block.id, n);
|
||||
}
|
||||
|
||||
entity = entities.get(block.id);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -361,7 +361,7 @@ public class Tile implements Position, TargetTrait{
|
|||
}
|
||||
}
|
||||
|
||||
private void preChanged(){
|
||||
protected void preChanged(){
|
||||
block().removed(this);
|
||||
if(entity != null){
|
||||
entity.removeFromProximity();
|
||||
|
|
@ -369,7 +369,7 @@ public class Tile implements Position, TargetTrait{
|
|||
team = 0;
|
||||
}
|
||||
|
||||
private void changed(){
|
||||
protected void changed(){
|
||||
if(entity != null){
|
||||
entity.remove();
|
||||
entity = null;
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public class IOSLauncher extends IOSApplication.Delegate {
|
|||
ui.editor.show();
|
||||
}
|
||||
|
||||
ui.editor.beginEditMap(file.read());
|
||||
ui.editor.beginEditMap(file);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -144,11 +144,11 @@ public class ServerControl implements ApplicationListener{
|
|||
|
||||
Call.onInfoMessage((state.rules.pvp
|
||||
? "[YELLOW]The " + event.winner.name() + " team is victorious![]" : "[SCARLET]Game over![]")
|
||||
+ "\nNext selected map:[accent] "+map.name+"[]"
|
||||
+ (map.meta.author() != null ? " by[accent] " + map.meta.author() + "[]" : "") + "."+
|
||||
+ "\nNext selected map:[accent] "+map.name()+"[]"
|
||||
+ (map.author() != null ? " by[accent] " + map.author() + "[]" : "") + "."+
|
||||
"\nNew game begins in " + roundExtraTime + " seconds.");
|
||||
|
||||
info("Selected next map to be {0}.", map.name);
|
||||
info("Selected next map to be {0}.", map.name());
|
||||
|
||||
Map fmap = map;
|
||||
|
||||
|
|
@ -199,7 +199,7 @@ public class ServerControl implements ApplicationListener{
|
|||
|
||||
if(lastTask != null) lastTask.cancel();
|
||||
|
||||
Map result = world.maps.all().find(map -> map.name.equalsIgnoreCase(arg[0]));
|
||||
Map result = world.maps.all().find(map -> map.name().equalsIgnoreCase(arg[0]));
|
||||
|
||||
if(result == null){
|
||||
err("No map with name &y'{0}'&lr found.", arg[0]);
|
||||
|
|
@ -252,7 +252,7 @@ public class ServerControl implements ApplicationListener{
|
|||
if(!world.maps.all().isEmpty()){
|
||||
info("Maps:");
|
||||
for(Map map : world.maps.all()){
|
||||
info(" &ly{0}: &lb&fi{1} / {2}x{3}", map.name, map.custom ? "Custom" : "Default", map.meta.width, map.meta.height);
|
||||
info(" &ly{0}: &lb&fi{1} / {2}x{3}", map.name(), map.custom ? "Custom" : "Default", map.width, map.height);
|
||||
}
|
||||
}else{
|
||||
info("No maps found.");
|
||||
|
|
@ -265,7 +265,7 @@ public class ServerControl implements ApplicationListener{
|
|||
info("Status: &rserver closed");
|
||||
}else{
|
||||
info("Status:");
|
||||
info(" &lyPlaying on map &fi{0}&fb &lb/&ly Wave {1}", Strings.capitalize(world.getMap().name), state.wave);
|
||||
info(" &lyPlaying on map &fi{0}&fb &lb/&ly Wave {1}", Strings.capitalize(world.getMap().name()), state.wave);
|
||||
|
||||
if(state.rules.waves){
|
||||
info("&ly {0} enemies.", unitGroups[Team.red.ordinal()].size());
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ def antialias = {File file ->
|
|||
|
||||
ImageIO.write(out, "png", file)
|
||||
}
|
||||
|
||||
task swapColors(){
|
||||
doLast{
|
||||
if (project.hasProperty("colors")) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue