Merge branches 'master' and 'new-map-format' of https://github.com/Anuken/Mindustry

This commit is contained in:
Anuken
2019-03-12 13:50:48 -04:00
56 changed files with 2757 additions and 4253 deletions
@@ -178,12 +178,14 @@ public class AndroidLauncher extends AndroidApplication{
ui.showError("$save.import.invalid"); ui.showError("$save.import.invalid");
} }
}else if(map){ //open map }else if(map){ //open map
FileHandle file = Core.files.local("temp-map." + mapExtension);
file.write(inStream, false);
Core.app.post(() -> { Core.app.post(() -> {
System.out.println("Opening map."); System.out.println("Opening map.");
if(!ui.editor.isShown()){ if(!ui.editor.isShown()){
ui.editor.show(); ui.editor.show();
} }
ui.editor.beginEditMap(inStream); ui.editor.beginEditMap(file);
}); });
} }
}); });
+2 -3
View File
@@ -197,8 +197,8 @@ editor.description = Description:
editor.name = Name: editor.name = Name:
editor.teams = Teams editor.teams = Teams
editor.elevation = Elevation editor.elevation = Elevation
editor.errorimageload = Error loading file:\n[accent]{0} editor.errorload = Error loading file:\n[accent]{0}
editor.errorimagesave = Error saving file:\n[accent]{0} editor.errorsave = Error saving file:\n[accent]{0}
editor.generate = Generate editor.generate = Generate
editor.resize = Resize editor.resize = Resize
editor.loadmap = Load Map editor.loadmap = Load Map
@@ -560,7 +560,6 @@ block.white-tree-dead.name = White Tree Dead
block.white-tree.name = White Tree block.white-tree.name = White Tree
block.spore-cluster.name = Spore Cluster block.spore-cluster.name = Spore Cluster
block.stained-rocks.name = Stained Rocks block.stained-rocks.name = Stained Rocks
block.stained-stone.name = Stained Stone
block.stained-rocks-red.name = Stained Rocks Red block.stained-rocks-red.name = Stained Rocks Red
block.stained-stone-red.name = Stained Stone Red block.stained-stone-red.name = Stained Stone Red
block.stained-rocks-yellow.name = Stained Rocks Yellow block.stained-rocks-yellow.name = Stained Rocks Yellow
Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 496 B

File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 KiB

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 KiB

After

Width:  |  Height:  |  Size: 164 KiB

+2
View File
@@ -30,6 +30,8 @@ import java.util.Locale;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class Vars{ public class Vars{
/**IO buffer size.*/
public static final int bufferSize = 8192;
/**global charset*/ /**global charset*/
public static final Charset charset = Charset.forName("UTF-8"); public static final Charset charset = Charset.forName("UTF-8");
/**main application name, capitalized*/ /**main application name, capitalized*/
@@ -203,7 +203,7 @@ public class BlockIndexer{
for(int x = Math.max(0, tile.x - oreQuadrantSize / 2); x < tile.x + oreQuadrantSize / 2 && x < world.width(); x++){ for(int x = Math.max(0, tile.x - oreQuadrantSize / 2); x < tile.x + oreQuadrantSize / 2 && x < world.width(); x++){
for(int y = Math.max(0, tile.y - oreQuadrantSize / 2); y < tile.y + oreQuadrantSize / 2 && y < world.height(); y++){ for(int y = Math.max(0, tile.y - oreQuadrantSize / 2); y < tile.y + oreQuadrantSize / 2 && y < world.height(); y++){
Tile res = world.tile(x, y); Tile res = world.tile(x, y);
if(res.block() == Blocks.air && res.floor().itemDrop == item){ if(res.block() == Blocks.air && res.drop() == item){
return res; return res;
} }
} }
@@ -241,9 +241,9 @@ public class BlockIndexer{
for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){ for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){
for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){ for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){
Tile result = world.tile(x, y); Tile result = world.tile(x, y);
if( result == null || result.floor().itemDrop == null || !scanOres.contains(result.floor().itemDrop)) continue; if( result == null || result.drop() == null || !scanOres.contains(result.drop())) continue;
itemSet.add(result.floor().itemDrop); itemSet.add(result.drop());
} }
} }
@@ -320,8 +320,8 @@ public class BlockIndexer{
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
//add position of quadrant to list when an ore is found //add position of quadrant to list when an ore is found
if(tile.floor().itemDrop != null && scanOres.contains(tile.floor().itemDrop) && tile.block() == Blocks.air){ if(tile.drop() != null && scanOres.contains(tile.drop()) && tile.block() == Blocks.air){
ores.get(tile.floor().itemDrop).add(world.tile( ores.get(tile.drop()).add(world.tile(
//make sure to clamp quadrant middle position, since it might go off bounds //make sure to clamp quadrant middle position, since it might go off bounds
Mathf.clamp(qx * oreQuadrantSize + oreQuadrantSize / 2, 0, world.width() - 1), Mathf.clamp(qx * oreQuadrantSize + oreQuadrantSize / 2, 0, world.width() - 1),
Mathf.clamp(qy * oreQuadrantSize + oreQuadrantSize / 2, 0, world.height() - 1))); Mathf.clamp(qy * oreQuadrantSize + oreQuadrantSize / 2, 0, world.height() - 1)));
+16 -24
View File
@@ -7,7 +7,6 @@ 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;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
@@ -28,7 +27,8 @@ import io.anuke.mindustry.world.blocks.units.UnitFactory;
import io.anuke.mindustry.world.consumers.ConsumeItemFilter; import io.anuke.mindustry.world.consumers.ConsumeItemFilter;
import io.anuke.mindustry.world.consumers.ConsumeLiquidFilter; import io.anuke.mindustry.world.consumers.ConsumeLiquidFilter;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.world;
public class Blocks implements ContentList{ public class Blocks implements ContentList{
public static Block public static Block
@@ -39,6 +39,9 @@ public class Blocks implements ContentList{
iceSnow, sandWater, duneRocks, sandRocks, stainedRocks, moss, stainedRocksRed, stainedStoneRed, stainedRocksYellow, stainedStoneYellow, stainedBoulder, grass, salt, iceSnow, sandWater, duneRocks, sandRocks, stainedRocks, moss, stainedRocksRed, stainedStoneRed, stainedRocksYellow, stainedStoneYellow, stainedBoulder, grass, salt,
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks, metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks,
//ores
oreCopper, oreLead, oreScrap, oreCoal, oreTitanium, oreThorium,
//crafting //crafting
siliconSmelter, kiln, graphitePress, plastaniumCompressor, multiPress, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer, siliconSmelter, kiln, graphitePress, plastaniumCompressor, multiPress, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer,
melter, separator, sporePress, pulverizer, incinerator, melter, separator, sporePress, pulverizer, incinerator,
@@ -145,7 +148,7 @@ public class Blocks implements ContentList{
}}; }};
stone = new Floor("stone"){{ stone = new Floor("stone"){{
hasOres = true;
}}; }};
craters = new Floor("craters"){{ craters = new Floor("craters"){{
@@ -170,12 +173,10 @@ public class Blocks implements ContentList{
sand = new Floor("sand"){{ sand = new Floor("sand"){{
itemDrop = Items.sand; itemDrop = Items.sand;
hasOres = true;
playerUnmineable = true; playerUnmineable = true;
}}; }};
holostone = new Floor("holostone"){{ holostone = new Floor("holostone"){{
hasOres = true;
edgeStyle = "blocky"; edgeStyle = "blocky";
}}; }};
@@ -226,7 +227,6 @@ public class Blocks implements ContentList{
}}; }};
pine = new StaticWall("pine"){{ pine = new StaticWall("pine"){{
//fillsTile = false;
variants = 0; variants = 0;
}}; }};
@@ -253,7 +253,6 @@ public class Blocks implements ContentList{
}}; }};
stainedStoneRed = new Floor("stained-stone-red"){{ stainedStoneRed = new Floor("stained-stone-red"){{
hasOres = true;
variants = 1; variants = 1;
}}; }};
@@ -308,6 +307,16 @@ public class Blocks implements ContentList{
blendGroup = ignarock; blendGroup = ignarock;
}}; }};
//endregion
//region ore
oreCopper = new OreBlock(Items.copper);
oreLead = new OreBlock(Items.lead);
oreScrap = new OreBlock(Items.scrap);
oreCoal = new OreBlock(Items.coal);
oreTitanium = new OreBlock(Items.titanium);
oreThorium = new OreBlock(Items.thorium);
//endregion //endregion
//region crafting //region crafting
@@ -1418,23 +1427,6 @@ public class Blocks implements ContentList{
consumes.powerBuffered(120f); consumes.powerBuffered(120f);
}}; }};
//endregion
//region ores
//create ores for every floor and item combination necessary
for(Item item : content.items()){
if(!item.genOre) continue;
for(Block block : content.blocks()){
if(block instanceof Floor && ((Floor) block).hasOres){
new OreBlock(item, (Floor) block);
}
}
}
//special variants
new OreBlock(Items.scrap, (Floor)snow);
//endregion //endregion
} }
} }
@@ -15,7 +15,6 @@ public class Items implements ContentList{
type = ItemType.material; type = ItemType.material;
hardness = 1; hardness = 1;
cost = 0.6f; cost = 0.6f;
genOre = true;
alwaysUnlocked = true; alwaysUnlocked = true;
}}; }};
@@ -23,7 +22,6 @@ public class Items implements ContentList{
type = ItemType.material; type = ItemType.material;
hardness = 1; hardness = 1;
cost = 0.9f; cost = 0.9f;
genOre = true;
}}; }};
metaglass = new Item("metaglass", Color.valueOf("ebeef5")){{ metaglass = new Item("metaglass", Color.valueOf("ebeef5")){{
@@ -40,14 +38,12 @@ public class Items implements ContentList{
explosiveness = 0.4f; explosiveness = 0.4f;
flammability = 1f; flammability = 1f;
hardness = 2; hardness = 2;
genOre = true;
}}; }};
titanium = new Item("titanium", Color.valueOf("8da1e3")){{ titanium = new Item("titanium", Color.valueOf("8da1e3")){{
type = ItemType.material; type = ItemType.material;
hardness = 3; hardness = 3;
cost = 1.1f; cost = 1.1f;
genOre = true;
}}; }};
thorium = new Item("thorium", Color.valueOf("f9a3c7")){{ thorium = new Item("thorium", Color.valueOf("f9a3c7")){{
@@ -56,11 +52,10 @@ public class Items implements ContentList{
hardness = 4; hardness = 4;
radioactivity = 1f; radioactivity = 1f;
cost = 1.4f; cost = 1.4f;
genOre = true;
}}; }};
scrap = new Item("scrap", Color.valueOf("777777")){{ scrap = new Item("scrap", Color.valueOf("777777")){{
genOre = true;
}}; }};
silicon = new Item("silicon", Color.valueOf("53565c")){{ silicon = new Item("silicon", Color.valueOf("53565c")){{
+10 -20
View File
@@ -23,8 +23,6 @@ import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.MapIO; import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.maps.MapException; import io.anuke.mindustry.maps.MapException;
import io.anuke.mindustry.maps.MapTileData;
import io.anuke.mindustry.maps.MapTileData.TileDataMarker;
import io.anuke.mindustry.maps.Maps; import io.anuke.mindustry.maps.Maps;
import io.anuke.mindustry.maps.generators.Generator; import io.anuke.mindustry.maps.generators.Generator;
import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.ContentType;
@@ -235,12 +233,15 @@ public class World implements ApplicationListener{
beginMapLoad(); beginMapLoad();
this.currentMap = map; this.currentMap = map;
int width = map.meta.width, height = map.meta.height;
createTiles(width, height);
try{ try{
loadTileData(tiles, MapIO.readTileData(map, true)); createTiles(map.width, map.height);
for(int x = 0; x < map.width; x++){
for(int y = 0; y < map.height; y++){
tiles[x][y] = new Tile(x, y);
}
}
MapIO.readTiles(map, tiles);
prepareTiles(tiles);
}catch(Exception e){ }catch(Exception e){
Log.err(e); Log.err(e);
if(!headless){ if(!headless){
@@ -260,7 +261,7 @@ public class World implements ApplicationListener{
if(state.teams.get(players[0].getTeam()).cores.size == 0){ if(state.teams.get(players[0].getTeam()).cores.size == 0){
ui.showError("$map.nospawn"); ui.showError("$map.nospawn");
invalidMap = true; invalidMap = true;
}else if(state.rules.pvp){ //pvp maps need two cores to be valid }else if(state.rules.pvp){ //pvp maps need two cores to be valid
invalidMap = true; invalidMap = true;
for(Team team : Team.all){ for(Team team : Team.all){
if(state.teams.get(team).cores.size != 0 && team != players[0].getTeam()){ if(state.teams.get(team).cores.size != 0 && team != players[0].getTeam()){
@@ -416,18 +417,7 @@ public class World implements ApplicationListener{
} }
/**Loads raw map tile data into a Tile[][] array, setting up multiblocks, cliffs and ores. */ /**Loads raw map tile data into a Tile[][] array, setting up multiblocks, cliffs and ores. */
void loadTileData(Tile[][] tiles, MapTileData data){ void loadTileData(Tile[][] tiles){
data.position(0, 0);
TileDataMarker marker = data.newDataMarker();
for(int y = 0; y < data.height(); y++){
for(int x = 0; x < data.width(); x++){
data.read(marker);
tiles[x][y] = new Tile(x, y, marker.floor, marker.wall == Blocks.part.id ? 0 : marker.wall, marker.rotation, marker.team);
}
}
prepareTiles(tiles); prepareTiles(tiles);
} }
@@ -1,72 +1,70 @@
package io.anuke.mindustry.editor; package io.anuke.mindustry.editor;
import io.anuke.arc.collection.Array; import io.anuke.annotations.Annotations.Struct;
import io.anuke.arc.collection.IntSet; import io.anuke.arc.collection.LongArray;
import io.anuke.arc.util.Pack; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.maps.MapTileData; import io.anuke.mindustry.gen.TileOp;
import io.anuke.mindustry.maps.MapTileData.TileDataMarker; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor;
import static io.anuke.mindustry.Vars.content;
public class DrawOperation{ public class DrawOperation{
/** private LongArray array = new LongArray();
* Data to apply operation to.
*/
private MapTileData data;
/**
* List of per-tile operations that occurred.
*/
private Array<TileOperation> operations = new Array<>();
/**
* Checks for duplicate operations, useful for brushes.
*/
private IntSet checks = new IntSet();
public DrawOperation(MapTileData data){
this.data = data;
}
public boolean isEmpty(){ public boolean isEmpty(){
return operations.size == 0; return array.isEmpty();
} }
public boolean checkDuplicate(short x, short y){ public void addOperation(long op){
int i = Pack.shortInt(x, y); array.add(op);
if(checks.contains(i)) return true;
checks.add(i);
return false;
}
public void addOperation(TileOperation op){
operations.add(op);
} }
public void undo(MapEditor editor){ public void undo(MapEditor editor){
for(int i = operations.size - 1; i >= 0; i--){ for(int i = array.size - 1; i >= 0; i--){
TileOperation op = operations.get(i); long l = array.get(i);
data.position(op.x, op.y); set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.from(l));
data.write(op.from);
editor.renderer().updatePoint(op.x, op.y);
} }
} }
public void redo(MapEditor editor){ public void redo(MapEditor editor){
for(TileOperation op : operations){ for(int i = 0; i < array.size; i++){
data.position(op.x, op.y); long l = array.get(i);
data.write(op.to); set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.to(l));
editor.renderer().updatePoint(op.x, op.y);
} }
} }
public static class TileOperation{ void set(MapEditor editor, Tile tile, byte type, byte to){
public short x, y; editor.load(() -> {
public TileDataMarker from; if(type == OpType.floor.ordinal()){
public TileDataMarker to; 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.setOreByte(to);
}
});
editor.renderer().updatePoint(tile.x, tile.y);
}
public TileOperation(short x, short y, TileDataMarker from, TileDataMarker to){ @Struct
this.x = x; class TileOpStruct{
this.y = y; short x;
this.from = from; short y;
this.to = to; byte type;
} byte from;
byte to;
}
public enum OpType{
floor,
block,
rotation,
team,
ore
} }
} }
@@ -0,0 +1,105 @@
package io.anuke.mindustry.editor;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.editor.DrawOperation.OpType;
import io.anuke.mindustry.game.Team;
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.blocks.OreBlock;
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;
public class EditorTile extends Tile{
public EditorTile(int x, int y, byte floor, byte wall){
super(x, y, floor, wall);
}
@Override
public Team getTeam(){
return Team.all[getTeamID()];
}
@Override
public void setFloor(Floor type){
if(type instanceof OreBlock){
//don't place on liquids
if(!floor().isLiquid) setOreByte(type.id);
return;
}
Block previous = floor();
Block ore = oreBlock();
if(previous == type && ore == Blocks.air) return;
super.setFloor(type);
//ore may get nullified so make sure to save editrs
if(oreBlock() != ore){
op(TileOp.get(x, y, (byte)OpType.ore.ordinal(), ore.id, oreBlock().id));
}
if(previous != type){
op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous.id, type.id));
}
}
@Override
public void setBlock(Block type){
Block previous = block();
if(previous == type) return;
super.setBlock(type);
op(TileOp.get(x, y, (byte)OpType.block.ordinal(), previous.id, type.id));
}
@Override
public void setTeam(Team team){
byte previous = getTeamID();
if(previous == team.ordinal()) return;
super.setTeam(team);
op(TileOp.get(x, y, (byte)OpType.team.ordinal(), previous, (byte)team.ordinal()));
}
@Override
public void setRotation(byte rotation){
byte previous = getRotation();
if(previous == rotation) return;
super.setRotation(rotation);
op(TileOp.get(x, y, (byte)OpType.rotation.ordinal(), previous, rotation));
}
@Override
public void setOreByte(byte ore){
byte previous = getOreByte();
if(previous == ore) return;
super.setOreByte(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){
ui.editor.editor.addTileOp(op);
}
}
@@ -7,34 +7,31 @@ import io.anuke.arc.input.KeyCode;
import io.anuke.arc.util.Pack; import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Structs; import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.maps.MapTileData;
import io.anuke.mindustry.maps.MapTileData.DataPosition;
import io.anuke.mindustry.maps.MapTileData.TileDataMarker;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Pos;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BlockPart; import io.anuke.mindustry.world.blocks.BlockPart;
import io.anuke.mindustry.world.blocks.Floor; import io.anuke.mindustry.world.blocks.Floor;
import static io.anuke.mindustry.Vars.*;
public enum EditorTool{ public enum EditorTool{
pick{ pick{
public void touched(MapEditor editor, int x, int y){ public void touched(MapEditor editor, int x, int y){
if(!Structs.inBounds(x, y, editor.getMap().width(), editor.getMap().height())) return; if(!Structs.inBounds(x, y, editor.width(), editor.height())) return;
byte bf = editor.getMap().read(x, y, DataPosition.floor); Tile tile = editor.tile(x, y);
byte bw = editor.getMap().read(x, y, DataPosition.wall);
byte link = editor.getMap().read(x, y, DataPosition.link);
if(link != 0){ Block floor = tile.floor(), block = tile.block();
byte link = tile.getLinkByte();
if(block instanceof BlockPart && link != 0){
x -= (Pack.leftByte(link) - 8); x -= (Pack.leftByte(link) - 8);
y -= (Pack.rightByte(link) - 8); y -= (Pack.rightByte(link) - 8);
bf = editor.getMap().read(x, y, DataPosition.floor);
bw = editor.getMap().read(x, y, DataPosition.wall); tile = editor.tile(x, y);
block = tile.block();
} }
Block block = content.block(bw == 0 ? bf : bw); editor.drawBlock = block == Blocks.air ? floor : block;
editor.setDrawBlock(block);
ui.editor.updateSelectedBlock();
} }
}, },
pencil{ pencil{
@@ -67,7 +64,7 @@ public enum EditorTool{
@Override @Override
public void touched(MapEditor editor, int x, int y){ public void touched(MapEditor editor, int x, int y){
editor.draw(x, y, isPaint(), editor.getDrawBlock(), 0.012); editor.draw(x, y, isPaint(), editor.drawBlock, 0.012);
} }
}, },
line{ line{
@@ -81,56 +78,57 @@ public enum EditorTool{
} }
IntArray stack = new IntArray(); IntArray stack = new IntArray();
int width; Block dest;
byte be, dest; boolean isfloor;
boolean floor; MapEditor data;
MapTileData data;
public void touched(MapEditor editor, int x, int y){ public void touched(MapEditor editor, int x, int y){
if(!Structs.inBounds(x, y, editor.getMap().width(), editor.getMap().height())) return; if(!Structs.inBounds(x, y, editor.width(), editor.height())) return;
Tile tile = editor.tile(x, y);
if(editor.getDrawBlock().isMultiblock()){ if(editor.drawBlock.isMultiblock()){
//don't fill multiblocks, thanks //don't fill multiblocks, thanks
pencil.touched(editor, x, y); pencil.touched(editor, x, y);
return; return;
} }
data = editor.getMap(); data = editor;
isfloor = editor.drawBlock instanceof Floor;
floor = editor.getDrawBlock() instanceof Floor; Block floor = tile.floor();
Block block = tile.block();
boolean synth = editor.drawBlock.synthetic();
byte bf = data.read(x, y, DataPosition.floor); dest = isfloor ? floor : block;
byte bw = data.read(x, y, DataPosition.wall); Block draw = editor.drawBlock;
boolean synth = editor.getDrawBlock().synthetic();
byte brt = Pack.byteByte((byte) editor.getDrawRotation(), (byte) editor.getDrawTeam().ordinal());
dest = floor ? bf : bw; if(dest == draw || block == Blocks.part || block.isMultiblock()){
byte draw = editor.getDrawBlock().id;
if(dest == draw || content.block(bw) instanceof BlockPart || content.block(bw).isMultiblock()){
return; return;
} }
width = editor.getMap().width(); int width = editor.width();
int height = editor.getMap().height(); int height = editor.height();
IntPositionConsumer writer = (px, py) -> { IntPositionConsumer writer = (px, py) -> {
TileDataMarker prev = editor.getPrev(px, py, false); Tile write = editor.tile(px, py);
if(floor){ if(isfloor){
data.write(px, py, DataPosition.floor, draw); write.setFloor((Floor)draw);
}else{ }else{
data.write(px, py, DataPosition.wall, draw); write.setBlock(draw);
} }
if(synth){ if(synth){
data.write(px, py, DataPosition.rotationTeam, brt); write.setTeam(editor.drawTeam);
} }
editor.onWrite(px, py, prev); if(draw.rotate){
write.setRotation((byte)editor.rotation);
}
}; };
if(isAlt()){ if(isAlt()){
//fill all of the same type regardless of borders
for(int cx = 0; cx < width; cx++){ for(int cx = 0; cx < width; cx++){
for(int cy = 0; cy < height; cy++){ for(int cy = 0; cy < height; cy++){
if(eq(cx, cy)){ if(eq(cx, cy)){
@@ -139,28 +137,28 @@ public enum EditorTool{
} }
} }
}else if(isAlt2()){ }else if(isAlt2()){
//fill all teams.
for(int cx = 0; cx < width; cx++){ for(int cx = 0; cx < width; cx++){
for(int cy = 0; cy < height; cy++){ for(int cy = 0; cy < height; cy++){
byte w = data.read(cx, cy, DataPosition.wall); Tile write = editor.tile(cx, cy);
if(content.block(w).synthetic()){ if(write.block().synthetic()){
TileDataMarker prev = editor.getPrev(cx, cy, false); write.setTeam(editor.drawTeam);
data.write(cx, cy, DataPosition.rotationTeam, (byte)editor.getDrawTeam().ordinal());
editor.onWrite(cx, cy, prev);
} }
} }
} }
}else{ }else{
//normal fill
int x1; int x1;
boolean spanAbove, spanBelow; boolean spanAbove, spanBelow;
stack.clear(); stack.clear();
stack.add(asi(x, y)); stack.add(Pos.get(x, y));
while(stack.size > 0){ while(stack.size > 0){
int popped = stack.pop(); int popped = stack.pop();
x = popped % width; x = Pos.x(popped);
y = popped / width; y = Pos.y(popped);
x1 = x; x1 = x;
while(x1 >= 0 && eq(x1, y)) x1--; while(x1 >= 0 && eq(x1, y)) x1--;
@@ -170,14 +168,14 @@ public enum EditorTool{
writer.accept(x1, y); writer.accept(x1, y);
if(!spanAbove && y > 0 && eq(x1, y - 1)){ if(!spanAbove && y > 0 && eq(x1, y - 1)){
stack.add(asi(x1, y - 1)); stack.add(Pos.get(x1, y - 1));
spanAbove = true; spanAbove = true;
}else if(spanAbove && y > 0 && eq(x1, y - 1)){ }else if(spanAbove && eq(x1, y - 1)){
spanAbove = false; spanAbove = false;
} }
if(!spanBelow && y < height - 1 && eq(x1, y + 1)){ if(!spanBelow && y < height - 1 && eq(x1, y + 1)){
stack.add(asi(x1, y + 1)); stack.add(Pos.get(x1, y + 1));
spanBelow = true; spanBelow = true;
}else if(spanBelow && y < height - 1 && eq(x1, y + 1)){ }else if(spanBelow && y < height - 1 && eq(x1, y + 1)){
spanBelow = false; spanBelow = false;
@@ -189,15 +187,9 @@ public enum EditorTool{
} }
boolean eq(int px, int py){ boolean eq(int px, int py){
byte nbf = data.read(px, py, DataPosition.floor); Tile tile = data.tile(px, py);
byte nbw = data.read(px, py, DataPosition.wall);
byte nbe = data.read(px, py, DataPosition.elevation);
return (floor ? nbf : nbw) == dest && nbe == be; return (isfloor ? tile.floor() : tile.block()) == dest;
}
int asi(int x, int y){
return x + y * width;
} }
}, },
zoom; zoom;
+191 -130
View File
@@ -1,88 +1,150 @@
package io.anuke.mindustry.editor; package io.anuke.mindustry.editor;
import io.anuke.arc.collection.ObjectMap; import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Pack; import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Structs; import io.anuke.arc.util.Structs;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.editor.DrawOperation.TileOperation;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.maps.MapTileData; import io.anuke.mindustry.gen.TileOp;
import io.anuke.mindustry.maps.MapTileData.DataPosition; import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.MapTileData.TileDataMarker; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor; import io.anuke.mindustry.world.blocks.Floor;
import static io.anuke.mindustry.Vars.content; import java.io.IOException;
public class MapEditor{ public class MapEditor{
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20}; public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20};
private MapTileData map;
private ObjectMap<String, String> tags = new ObjectMap<>(); private ObjectMap<String, String> tags = new ObjectMap<>();
private MapRenderer renderer = new MapRenderer(this); private MapRenderer renderer = new MapRenderer(this);
private Tile[][] tiles;
private int brushSize = 1; private OperationStack stack = new OperationStack();
private int rotation; private DrawOperation currentOp;
private Block drawBlock = Blocks.stone; private boolean loading;
private Team drawTeam = Team.blue;
public MapTileData getMap(){ public int brushSize = 1;
return map; public int rotation;
} public Block drawBlock = Blocks.stone;
public Team drawTeam = Team.blue;
public ObjectMap<String, String> getTags(){ public ObjectMap<String, String> getTags(){
return tags; return tags;
} }
public void beginEdit(MapTileData map, ObjectMap<String, String> tags, boolean clear){ public void beginEdit(int width, int height){
this.map = map; reset();
this.brushSize = 1;
this.tags = tags;
if(clear){ loading = true;
for(int x = 0; x < map.width(); x++){ tiles = createTiles(width, height);
for(int y = 0; y < map.height(); y++){ renderer.resize(width(), height());
map.write(x, y, DataPosition.floor, Blocks.stone.id); loading = false;
}
public void beginEdit(Map map) throws IOException{
reset();
loading = true;
tiles = createTiles(map.width, map.height);
tags.putAll(map.tags);
MapIO.readTiles(map, tiles);
checkTiles();
renderer.resize(width(), height());
loading = false;
}
public void beginEdit(Tile[][] tiles){
reset();
this.tiles = tiles;
checkTiles();
renderer.resize(width(), height());
}
//adds missing blockparts
void checkTiles(){
//clear block parts first
for(int x = 0; x < width(); x ++){
for(int y = 0; y < height(); y++){
if(tiles[x][y].block() == Blocks.part){
tiles[x][y].setBlock(Blocks.air);
tiles[x][y].setLinkByte((byte)0);
} }
} }
} }
//set up missing blockparts
for(int x = 0; x < width(); x ++){
for(int y = 0; y < height(); y ++){
Block drawBlock = tiles[x][y].block();
if(drawBlock.isMultiblock()){
int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2;
for(int dx = 0; dx < drawBlock.size; dx++){
for(int dy = 0; dy < drawBlock.size; dy++){
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, width(), height()) && !(dx + offsetx == 0 && dy + offsety == 0)){
Tile tile = tiles[worldx][worldy];
tile.setBlock(Blocks.part);
tile.setLinkByte(Pack.byteByte((byte) (dx + offsetx + 8), (byte) (dy + offsety + 8)));
}
}
}
}
}
}
}
public void load(Runnable r){
loading = true;
r.run();
loading = false;
}
/**Creates a 2-D array of EditorTiles with stone as the floor block.*/
public Tile[][] createTiles(int width, int height){
tiles = new Tile[width][height];
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0);
}
}
return tiles;
}
public Map createMap(FileHandle file){
return new Map(file, width(), height(), new ObjectMap<>(tags), true);
}
private void reset(){
clearOp();
brushSize = 1;
drawBlock = Blocks.stone; drawBlock = Blocks.stone;
renderer.resize(map.width(), map.height()); tags = new ObjectMap<>();
} }
public int getDrawRotation(){ public Tile[][] tiles(){
return rotation; return tiles;
} }
public void setDrawRotation(int rotation){ public Tile tile(int x, int y){
this.rotation = rotation; return tiles[x][y];
} }
public Team getDrawTeam(){ public int width(){
return drawTeam; return tiles.length;
} }
public void setDrawTeam(Team team){ public int height(){
this.drawTeam = team; return tiles[0].length;
}
public Block getDrawBlock(){
return drawBlock;
}
public void setDrawBlock(Block block){
this.drawBlock = block;
}
public int getBrushSize(){
return brushSize;
}
public void setBrushSize(int size){
this.brushSize = size;
} }
public void draw(int x, int y, boolean paint){ public void draw(int x, int y, boolean paint){
@@ -94,15 +156,12 @@ public class MapEditor{
} }
public void draw(int x, int y, boolean paint, Block drawBlock, double chance){ public void draw(int x, int y, boolean paint, Block drawBlock, double chance){
byte writeID = drawBlock.id;
byte partID = Blocks.part.id;
byte rotationTeam = Pack.byteByte(drawBlock.rotate ? (byte)rotation : 0, drawBlock.synthetic() ? (byte)drawTeam.ordinal() : 0);
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air; boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
if(drawBlock.isMultiblock()){ if(drawBlock.isMultiblock()){
x = Mathf.clamp(x, (drawBlock.size-1)/2, map.width() - drawBlock.size/2 - 1);
y = Mathf.clamp(y, (drawBlock.size-1)/2, map.height() - drawBlock.size/2 - 1); x = Mathf.clamp(x, (drawBlock.size-1)/2, width() - drawBlock.size/2 - 1);
y = Mathf.clamp(y, (drawBlock.size-1)/2, height() - drawBlock.size/2 - 1);
int offsetx = -(drawBlock.size - 1) / 2; int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2; int offsety = -(drawBlock.size - 1) / 2;
@@ -113,68 +172,61 @@ public class MapEditor{
int worldx = dx + offsetx + x; int worldx = dx + offsetx + x;
int worldy = dy + offsety + y; int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, map.width(), map.height())){ if(Structs.inBounds(worldx, worldy, width(), height())){
TileDataMarker prev = getPrev(worldx, worldy, false); Tile tile = tiles[worldx][worldy];
if(i == 1){ if(i == 1){
map.write(worldx, worldy, DataPosition.wall, partID); tile.setBlock(Blocks.part);
map.write(worldx, worldy, DataPosition.rotationTeam, rotationTeam); tile.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
map.write(worldx, worldy, DataPosition.link, Pack.byteByte((byte) (dx + offsetx + 8), (byte) (dy + offsety + 8)));
}else{ }else{
byte link = map.read(worldx, worldy, DataPosition.link); byte link = tile.getLinkByte();
byte block = map.read(worldx, worldy, DataPosition.wall); Block block = tile.block();
if(link != 0){ if(link != 0){
removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8)); removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8));
}else if(content.block(block).isMultiblock()){ }else if(block.isMultiblock()){
removeLinked(worldx, worldy); removeLinked(worldx, worldy);
} }
} }
onWrite(worldx, worldy, prev);
} }
} }
} }
} }
TileDataMarker prev = getPrev(x, y, false); Tile tile = tiles[x][y];
tile.setBlock(drawBlock);
map.write(x, y, DataPosition.wall, writeID); tile.setTeam(drawTeam);
map.write(x, y, DataPosition.link, (byte) 0);
map.write(x, y, DataPosition.rotationTeam, rotationTeam);
onWrite(x, y, prev);
}else{ }else{
for(int rx = -brushSize; rx <= brushSize; rx++){ for(int rx = -brushSize; rx <= brushSize; rx++){
for(int ry = -brushSize; ry <= brushSize; ry++){ for(int ry = -brushSize; ry <= brushSize; ry++){
if(Mathf.dst(rx, ry) <= brushSize - 0.5f && (chance >= 0.999 || Mathf.chance(chance))){ if(Mathf.dst(rx, ry) <= brushSize - 0.5f && (chance >= 0.999 || Mathf.chance(chance))){
int wx = x + rx, wy = y + ry; int wx = x + rx, wy = y + ry;
if(wx < 0 || wy < 0 || wx >= map.width() || wy >= map.height() || (paint && !isfloor && content.block(map.read(wx, wy, DataPosition.wall)) == Blocks.air)){ if(wx < 0 || wy < 0 || wx >= width() || wy >= height() || (paint && !isfloor && tiles[wx][wy].block() == Blocks.air)){
continue; continue;
} }
TileDataMarker prev = getPrev(wx, wy, true); Tile tile = tiles[wx][wy];
if(!isfloor){ if(!isfloor){
byte link = map.read(wx, wy, DataPosition.link); byte link = tile.getLinkByte();
Log.info("Remove linkd: " + tiles[x][y]);
if(content.block(map.read(wx, wy, DataPosition.wall)).isMultiblock()){ if(tile.block().isMultiblock()){
removeLinked(wx, wy); removeLinked(wx, wy);
}else if(link != 0){ }else if(link != 0 && tiles[x][y].block() == Blocks.part){
removeLinked(wx - (Pack.leftByte(link) - 8), wy - (Pack.rightByte(link) - 8)); removeLinked(wx - (Pack.leftByte(link) - 8), wy - (Pack.rightByte(link) - 8));
} }
} }
if(isfloor){ if(isfloor){
map.write(wx, wy, DataPosition.floor, writeID); tile.setFloor((Floor)drawBlock);
}else{ }else{
map.write(wx, wy, DataPosition.wall, writeID); tile.setBlock(drawBlock);
map.write(wx, wy, DataPosition.link, (byte) 0); if(drawBlock.synthetic()){
map.write(wx, wy, DataPosition.rotationTeam, rotationTeam); tile.setTeam(drawTeam);
}
} }
onWrite(x + rx, y + ry, prev);
} }
} }
} }
@@ -182,77 +234,86 @@ public class MapEditor{
} }
private void removeLinked(int x, int y){ private void removeLinked(int x, int y){
Block block = content.block(map.read(x, y, DataPosition.wall)); Block block = tiles[x][y].block();
int offsetx = -(block.size - 1) / 2; int offsetx = -(block.size - 1) / 2;
int offsety = -(block.size - 1) / 2; int offsety = -(block.size - 1) / 2;
for(int dx = 0; dx < block.size; dx++){ for(int dx = 0; dx < block.size; dx++){
for(int dy = 0; dy < block.size; dy++){ for(int dy = 0; dy < block.size; dy++){
int worldx = x + dx + offsetx, worldy = y + dy + offsety; int worldx = x + dx + offsetx, worldy = y + dy + offsety;
if(Structs.inBounds(worldx, worldy, map.width(), map.height())){ if(Structs.inBounds(worldx, worldy, width(), height())){
TileDataMarker prev = getPrev(worldx, worldy, false); tiles[worldx][worldy].setTeam(Team.none);
tiles[worldx][worldy].setBlock(Blocks.air);
map.write(worldx, worldy, DataPosition.link, (byte) 0);
map.write(worldx, worldy, DataPosition.rotationTeam, (byte) 0);
map.write(worldx, worldy, DataPosition.wall, (byte) 0);
onWrite(worldx, worldy, prev);
} }
} }
} }
} }
boolean checkDupes(int x, int y){
return Vars.ui.editor.getView().checkForDuplicates((short) x, (short) y);
}
void onWrite(int x, int y, TileDataMarker previous){
if(previous == null){
renderer.updatePoint(x, y);
return;
}
TileDataMarker current = map.new TileDataMarker();
map.position(x, y);
map.read(current);
Vars.ui.editor.getView().addTileOp(new TileOperation((short) x, (short) y, previous, current));
renderer.updatePoint(x, y);
}
TileDataMarker getPrev(int x, int y, boolean checkDupes){
if(checkDupes && checkDupes(x, y)){
return null;
}else{
TileDataMarker marker = map.newDataMarker();
map.position(x, y);
map.read(marker);
return marker;
}
}
public MapRenderer renderer(){ public MapRenderer renderer(){
return renderer; return renderer;
} }
public void resize(int width, int height){ public void resize(int width, int height){
MapTileData previous = map; clearOp();
int offsetX = -(width - previous.width())/2, offsetY = -(height - previous.height())/2;
map = new MapTileData(width, height); Tile[][] previous = tiles;
for(int x = 0; x < map.width(); x++){ int offsetX = -(width - width())/2, offsetY = -(height - height())/2;
for(int y = 0; y < map.height(); y++){ loading = true;
tiles = new Tile[width][height];
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
int px = offsetX + x, py = offsetY + y; int px = offsetX + x, py = offsetY + y;
if(Structs.inBounds(px, py, previous.width(), previous.height())){ if(Structs.inBounds(px, py, previous.length, previous[0].length)){
map.write(x, y, DataPosition.floor, previous.read(px, py, DataPosition.floor)); tiles[x][y] = previous[px][py];
map.write(x, y, DataPosition.wall, previous.read(px, py, DataPosition.wall)); tiles[x][y].x = (short)x;
map.write(x, y, DataPosition.link, previous.read(px, py, DataPosition.link)); tiles[x][y].y = (short)y;
map.write(x, y, DataPosition.rotationTeam, previous.read(px, py, DataPosition.rotationTeam));
}else{ }else{
map.write(x, y, DataPosition.floor, Blocks.stone.id); tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0);
} }
} }
} }
renderer.resize(width, height); renderer.resize(width, height);
loading = false;
}
public void clearOp(){
stack.clear();
}
public void undo(){
if(stack.canUndo()){
stack.undo(this);
}
}
public void redo(){
if(stack.canRedo()){
stack.redo(this);
}
}
public boolean canUndo(){
return stack.canUndo();
}
public boolean canRedo(){
return stack.canRedo();
}
public void flushOp(){
if(currentOp == null || currentOp.isEmpty()) return;
stack.add(currentOp);
currentOp = null;
}
public void addTileOp(long data){
if(loading) return;
if(currentOp == null) currentOp = new DrawOperation();
currentOp.addOperation(data);
renderer.updatePoint(TileOp.x(data), TileOp.y(data));
} }
} }
@@ -2,7 +2,6 @@ package io.anuke.mindustry.editor;
import io.anuke.arc.Core; import io.anuke.arc.Core;
import io.anuke.arc.collection.Array; import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle; import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.Consumer; import io.anuke.arc.function.Consumer;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Color;
@@ -23,21 +22,17 @@ import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.io.MapIO; import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.maps.MapMeta;
import io.anuke.mindustry.maps.MapTileData;
import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.ui.dialogs.FloatingDialog;
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.Tile;
import io.anuke.mindustry.world.blocks.OreBlock; import io.anuke.mindustry.world.blocks.OreBlock;
import io.anuke.mindustry.world.blocks.storage.CoreBlock; import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import java.io.DataInputStream;
import java.io.InputStream;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class MapEditorDialog extends Dialog implements Disposable{ public class MapEditorDialog extends Dialog implements Disposable{
private MapEditor editor; public final MapEditor editor;
private MapView view; private MapView view;
private MapInfoDialog infoDialog; private MapInfoDialog infoDialog;
private MapLoadDialog loadDialog; private MapLoadDialog loadDialog;
@@ -48,8 +43,6 @@ public class MapEditorDialog extends Dialog implements Disposable{
private boolean shownWithMap = false; private boolean shownWithMap = false;
private Array<Block> blocksOut = new Array<>(); private Array<Block> blocksOut = new Array<>();
private ButtonGroup<ImageButton> blockgroup;
public MapEditorDialog(){ public MapEditorDialog(){
super("", "dialog"); super("", "dialog");
@@ -89,17 +82,12 @@ public class MapEditorDialog extends Dialog implements Disposable{
createDialog("$editor.import", createDialog("$editor.import",
"$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable) loadDialog::show, "$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable) loadDialog::show,
"$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable) () -> "$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable) () ->
Platform.instance.showFileChooser("$loadimage", "Map Files", file -> ui.loadAnd(() -> { Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> {
try{ try{
DataInputStream stream = new DataInputStream(file.read()); //TODO what if it's an image? users should be warned for their stupidity
editor.beginEdit(MapIO.readMap(file, true));
MapMeta meta = MapIO.readMapMeta(stream);
MapTileData data = MapIO.readTileData(stream, meta, false);
editor.beginEdit(data, meta.tags, false);
view.clearStack();
}catch(Exception e){ }catch(Exception e){
ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false))); ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e); Log.err(e);
} }
}), true, mapExtension), }), true, mapExtension),
@@ -108,39 +96,32 @@ public class MapEditorDialog extends Dialog implements Disposable{
Platform.instance.showFileChooser("$loadimage", "Image Files", file -> Platform.instance.showFileChooser("$loadimage", "Image Files", file ->
ui.loadAnd(() -> { ui.loadAnd(() -> {
try{ try{
MapTileData data = MapIO.readLegacyPixmap(new Pixmap(file)); Pixmap pixmap = new Pixmap(file);
Tile[][] tiles = editor.createTiles(pixmap.getWidth(), pixmap.getHeight());
editor.beginEdit(data, editor.getTags(), false); editor.load(() -> MapIO.readLegacyPixmap(pixmap, tiles));
view.clearStack(); editor.beginEdit(tiles);
}catch (Exception e){ }catch (Exception e){
ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false))); ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e); Log.err(e);
} }
}), true, "png") }), true, "png")));
));
t.addImageTextButton("$editor.export", "icon-save-map", isize, () -> createDialog("$editor.export", t.addImageTextButton("$editor.export", "icon-save-map", isize, () ->
"$editor.exportfile", "$editor.exportfile.description", "icon-file", (Runnable) () -> Platform.instance.showFileChooser("$editor.savemap", "Map Files", file -> {
Platform.instance.showFileChooser("$saveimage", "Map Files", file -> {
file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension); file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension);
FileHandle result = file; FileHandle result = file;
ui.loadAnd(() -> { ui.loadAnd(() -> {
try{ try{
if(!editor.getTags().containsKey("name")){ if(!editor.getTags().containsKey("name")){
editor.getTags().put("name", result.nameWithoutExtension()); editor.getTags().put("name", result.nameWithoutExtension());
} }
MapIO.writeMap(result.write(false), editor.getTags(), editor.getMap()); MapIO.writeMap(result, editor.createMap(result), editor.tiles());
}catch(Exception e){ }catch(Exception e){
ui.showError(Core.bundle.format("editor.errorimagesave", Strings.parseException(e, false))); ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, false)));
Log.err(e); Log.err(e);
} }
}); });
}, false, mapExtension))); }, false, mapExtension));
t.row();
t.row();
}); });
menu.cont.row(); menu.cont.row();
@@ -151,24 +132,19 @@ public class MapEditorDialog extends Dialog implements Disposable{
}).padTop(-5).size(swidth * 2f + 10, 60f); }).padTop(-5).size(swidth * 2f + 10, 60f);
resizeDialog = new MapResizeDialog(editor, (x, y) -> { resizeDialog = new MapResizeDialog(editor, (x, y) -> {
if(!(editor.getMap().width() == x && editor.getMap().height() == y)){ if(!(editor.width() == x && editor.height() == y)){
ui.loadAnd(() -> { ui.loadAnd(() -> {
editor.resize(x, y); editor.resize(x, y);
view.clearStack();
}); });
} }
}); });
loadDialog = new MapLoadDialog(map -> loadDialog = new MapLoadDialog(map ->
ui.loadAnd(() -> { ui.loadAnd(() -> {
try(DataInputStream stream = new DataInputStream(map.stream.get())){ try{
MapMeta meta = MapIO.readMapMeta(stream); editor.beginEdit(map);
MapTileData data = MapIO.readTileData(stream, meta, false);
editor.beginEdit(data, meta.tags, false);
view.clearStack();
}catch(Exception e){ }catch(Exception e){
ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false))); ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e); Log.err(e);
} }
})); }));
@@ -200,10 +176,10 @@ public class MapEditorDialog extends Dialog implements Disposable{
shown(() -> { shown(() -> {
saved = true; saved = true;
Platform.instance.beginForceLandscape(); Platform.instance.beginForceLandscape();
view.clearStack(); editor.clearOp();
Core.scene.setScrollFocus(view); Core.scene.setScrollFocus(view);
if(!shownWithMap){ if(!shownWithMap){
editor.beginEdit(new MapTileData(200, 200), new ObjectMap<>(), true); editor.beginEdit(200, 200);
} }
shownWithMap = false; shownWithMap = false;
@@ -211,6 +187,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
}); });
hidden(() -> { hidden(() -> {
editor.clearOp();
Platform.instance.updateRPC(); Platform.instance.updateRPC();
Platform.instance.endForceLandscape(); Platform.instance.endForceLandscape();
}); });
@@ -225,13 +202,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
String name = editor.getTags().get("name", ""); String name = editor.getTags().get("name", "");
if(name.isEmpty()){ if(name.isEmpty()){
ui.showError("$editor.save.noname"); infoDialog.show();
Core.app.post(() -> ui.showError("$editor.save.noname"));
}else{ }else{
Map map = world.maps.getByName(name); Map map = world.maps.all().find(m -> m.name().equals(name));
if(map != null && !map.custom){ if(map != null && !map.custom){
ui.showError("$editor.save.overwrite"); ui.showError("$editor.save.overwrite");
}else{ }else{
world.maps.saveMap(name, editor.getMap(), editor.getTags()); world.maps.saveMap(editor.getTags(), editor.tiles());
ui.showInfoFade("$editor.saved"); ui.showInfoFade("$editor.saved");
} }
} }
@@ -293,18 +271,16 @@ public class MapEditorDialog extends Dialog implements Disposable{
editor.renderer().dispose(); editor.renderer().dispose();
} }
public void beginEditMap(InputStream is){ public void beginEditMap(FileHandle file){
ui.loadAnd(() -> { ui.loadAnd(() -> {
try{ try{
Map map = MapIO.readMap(file, true);
shownWithMap = true; shownWithMap = true;
DataInputStream stream = new DataInputStream(is); editor.beginEdit(map);
MapMeta meta = MapIO.readMapMeta(stream);
editor.beginEdit(MapIO.readTileData(stream, meta, false), meta.tags, false);
is.close();
show(); show();
}catch(Exception e){ }catch(Exception e){
Log.err(e); Log.err(e);
ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false))); ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
} }
}); });
} }
@@ -317,16 +293,6 @@ public class MapEditorDialog extends Dialog implements Disposable{
saved = false; saved = false;
} }
public void updateSelectedBlock(){
Block block = editor.getDrawBlock();
for(int j = 0; j < Vars.content.blocks().size; j++){
if(block.id == j && j < blockgroup.getButtons().size){
blockgroup.getButtons().get(j).setChecked(true);
break;
}
}
}
public boolean hasPane(){ public boolean hasPane(){
return Core.scene.getScrollFocus() == pane || Core.scene.getKeyboardFocus() != this; return Core.scene.getScrollFocus() == pane || Core.scene.getKeyboardFocus() != this;
} }
@@ -370,15 +336,15 @@ public class MapEditorDialog extends Dialog implements Disposable{
tools.row(); tools.row();
ImageButton undo = tools.addImageButton("icon-undo", "clear", 16 * 2f, () -> view.undo()).get(); ImageButton undo = tools.addImageButton("icon-undo", "clear", 16 * 2f, editor::undo).get();
ImageButton redo = tools.addImageButton("icon-redo", "clear", 16 * 2f, () -> view.redo()).get(); ImageButton redo = tools.addImageButton("icon-redo", "clear", 16 * 2f, editor::redo).get();
addTool.accept(EditorTool.pick); addTool.accept(EditorTool.pick);
tools.row(); tools.row();
undo.setDisabled(() -> !view.getStack().canUndo()); undo.setDisabled(() -> !editor.canUndo());
redo.setDisabled(() -> !view.getStack().canRedo()); redo.setDisabled(() -> !editor.canRedo());
undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.GRAY : Color.WHITE)); undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.GRAY : Color.WHITE));
redo.update(() -> redo.getImage().setColor(redo.isDisabled() ? Color.GRAY : Color.WHITE)); redo.update(() -> redo.getImage().setColor(redo.isDisabled() ? Color.GRAY : Color.WHITE));
@@ -393,9 +359,9 @@ public class MapEditorDialog extends Dialog implements Disposable{
addTool.accept(EditorTool.fill); addTool.accept(EditorTool.fill);
addTool.accept(EditorTool.spray); addTool.accept(EditorTool.spray);
ImageButton rotate = tools.addImageButton("icon-arrow-16", "clear", 16 * 2f, () -> editor.setDrawRotation((editor.getDrawRotation() + 1) % 4)).get(); ImageButton rotate = tools.addImageButton("icon-arrow-16", "clear", 16 * 2f, () -> editor.rotation = (editor.rotation + 1) % 4).get();
rotate.getImage().update(() -> { rotate.getImage().update(() -> {
rotate.getImage().setRotation(editor.getDrawRotation() * 90); rotate.getImage().setRotation(editor.rotation * 90);
rotate.getImage().setOrigin(Align.center); rotate.getImage().setOrigin(Align.center);
}); });
@@ -415,8 +381,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
button.margin(4f); button.margin(4f);
button.getImageCell().grow(); button.getImageCell().grow();
button.getStyle().imageUpColor = team.color; button.getStyle().imageUpColor = team.color;
button.clicked(() -> editor.setDrawTeam(team)); button.clicked(() -> editor.drawTeam = team);
button.update(() -> button.setChecked(editor.getDrawTeam() == team)); button.update(() -> button.setChecked(editor.drawTeam == team));
teamgroup.add(button); teamgroup.add(button);
tools.add(button); tools.add(button);
@@ -429,7 +395,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
mid.table("underline", t -> { mid.table("underline", t -> {
Slider slider = new Slider(0, MapEditor.brushSizes.length - 1, 1, false); Slider slider = new Slider(0, MapEditor.brushSizes.length - 1, 1, false);
slider.moved(f -> editor.setBrushSize(MapEditor.brushSizes[(int) (float) f])); slider.moved(f -> editor.brushSize = MapEditor.brushSizes[(int) (float) f]);
t.top(); t.top();
t.add("$editor.brush"); t.add("$editor.brush");
@@ -456,22 +422,28 @@ public class MapEditorDialog extends Dialog implements Disposable{
} }
} }
if(Core.input.keyTap(KeyCode.ESCAPE)){
if(!menu.isShown()){
menu.show();
}
}
if(Core.input.keyTap(KeyCode.R)){ if(Core.input.keyTap(KeyCode.R)){
editor.setDrawRotation((editor.getDrawRotation() + 1) % 4); editor.rotation = Mathf.mod(editor.rotation + 1, 4);
} }
if(Core.input.keyTap(KeyCode.E)){ if(Core.input.keyTap(KeyCode.E)){
editor.setDrawRotation(Mathf.mod((editor.getDrawRotation() + 1), 4)); editor.rotation = Mathf.mod(editor.rotation - 1, 4);
} }
//ctrl keys (undo, redo, save) //ctrl keys (undo, redo, save)
if(UIUtils.ctrl()){ if(UIUtils.ctrl()){
if(Core.input.keyTap(KeyCode.Z)){ if(Core.input.keyTap(KeyCode.Z)){
view.undo(); editor.undo();
} }
if(Core.input.keyTap(KeyCode.Y)){ if(Core.input.keyTap(KeyCode.Y)){
view.redo(); editor.redo();
} }
if(Core.input.keyTap(KeyCode.S)){ if(Core.input.keyTap(KeyCode.S)){
@@ -498,7 +470,6 @@ public class MapEditorDialog extends Dialog implements Disposable{
pane.setFadeScrollBars(false); pane.setFadeScrollBars(false);
pane.setOverscroll(true, false); pane.setOverscroll(true, false);
ButtonGroup<ImageButton> group = new ButtonGroup<>(); ButtonGroup<ImageButton> group = new ButtonGroup<>();
blockgroup = group;
int i = 0; int i = 0;
@@ -521,9 +492,9 @@ public class MapEditorDialog extends Dialog implements Disposable{
ImageButton button = new ImageButton("white", "clear-toggle"); ImageButton button = new ImageButton("white", "clear-toggle");
button.getStyle().imageUp = new TextureRegionDrawable(region); button.getStyle().imageUp = new TextureRegionDrawable(region);
button.clicked(() -> editor.setDrawBlock(block)); button.clicked(() -> editor.drawBlock = block);
button.resizeImage(8 * 4f); button.resizeImage(8 * 4f);
button.update(() -> button.setChecked(editor.getDrawBlock() == block)); button.update(() -> button.setChecked(editor.drawBlock == block));
group.add(button); group.add(button);
content.add(button).size(50f); content.add(button).size(50f);
@@ -534,7 +505,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
group.getButtons().get(2).setChecked(true); group.getButtons().get(2).setChecked(true);
table.table("underline", extra -> extra.labelWrap(() -> editor.getDrawBlock().localizedName).width(200f).center()).growX(); table.table("underline", extra -> extra.labelWrap(() -> editor.drawBlock.localizedName).width(200f).center()).growX();
table.row(); table.row();
table.add(pane).growY().fillX(); table.add(pane).growY().fillX();
} }
@@ -9,14 +9,12 @@ 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.util.Disposable; import io.anuke.arc.util.Disposable;
import io.anuke.arc.util.Pack;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.IndexedRenderer; import io.anuke.mindustry.graphics.IndexedRenderer;
import io.anuke.mindustry.maps.MapTileData.DataPosition;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.tilesize; import static io.anuke.mindustry.Vars.tilesize;
public class MapRenderer implements Disposable{ public class MapRenderer implements Disposable{
@@ -102,27 +100,24 @@ public class MapRenderer implements Disposable{
private void render(int wx, int wy){ private void render(int wx, int wy){
int x = wx / chunksize, y = wy / chunksize; int x = wx / chunksize, y = wy / chunksize;
IndexedRenderer mesh = chunks[x][y]; IndexedRenderer mesh = chunks[x][y];
byte bf = editor.getMap().read(wx, wy, DataPosition.floor); Tile tile = editor.tiles()[wx][wy];
byte bw = editor.getMap().read(wx, wy, DataPosition.wall);
byte btr = editor.getMap().read(wx, wy, DataPosition.rotationTeam);
byte rotation = Pack.leftByte(btr);
Team team = Team.all[Pack.rightByte(btr)];
Block floor = content.block(bf); Team team = tile.getTeam();
Block wall = content.block(bw); Block floor = tile.floor();
Block wall = tile.block();
TextureRegion region; TextureRegion region;
int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize; int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize;
int idxDecal = (wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize; int idxDecal = (wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize;
if(bw != 0 && (wall.synthetic() || wall == Blocks.part)){ if(wall != Blocks.air && (wall.synthetic() || wall == Blocks.part)){
region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon(); region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
if(wall.rotate){ if(wall.rotate){
mesh.draw(idxWall, region, mesh.draw(idxWall, region,
wx * tilesize + wall.offset(), wy * tilesize + wall.offset(), wx * tilesize + wall.offset(), wy * tilesize + wall.offset(),
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, rotation * 90 - 90); region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, tile.getRotation() * 90 - 90);
}else{ }else{
mesh.draw(idxWall, region, mesh.draw(idxWall, region,
wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl)/2f, wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl)/2f,
@@ -140,10 +135,12 @@ public class MapRenderer implements Disposable{
if(wall.update || wall.destructible){ if(wall.update || wall.destructible){
mesh.setColor(team.color); mesh.setColor(team.color);
region = Core.atlas.find("block-border-editor"); region = Core.atlas.find("block-border-editor");
}else if(!wall.synthetic() && bw != 0){ }else if(!wall.synthetic() && wall != Blocks.air){
region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon(); region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
offsetX = tilesize/2f - region.getWidth()/2f * Draw.scl; offsetX = tilesize/2f - region.getWidth()/2f * Draw.scl;
offsetY = tilesize/2f - region.getHeight()/2f * Draw.scl; offsetY = tilesize/2f - region.getHeight()/2f * Draw.scl;
}else if(wall == Blocks.air && tile.oreBlock() != null){
region = tile.oreBlock().editorVariantRegions()[Mathf.randomSeed(idxWall, 0, tile.oreBlock().editorVariantRegions().length-1)];;
}else{ }else{
region = Core.atlas.find("clear-editor"); region = Core.atlas.find("clear-editor");
} }
@@ -1,22 +1,20 @@
package io.anuke.mindustry.editor; package io.anuke.mindustry.editor;
import io.anuke.arc.function.BiConsumer; import io.anuke.arc.function.IntPositionConsumer;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.arc.scene.ui.layout.Table; import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.maps.MapTileData;
import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.ui.dialogs.FloatingDialog;
public class MapResizeDialog extends FloatingDialog{ public class MapResizeDialog extends FloatingDialog{
private static final int minSize = 50, maxSize = 500, increment = 50; private static final int minSize = 50, maxSize = 500, increment = 50;
int width, height; int width, height;
public MapResizeDialog(MapEditor editor, BiConsumer<Integer, Integer> cons){ public MapResizeDialog(MapEditor editor, IntPositionConsumer cons){
super("$editor.resizemap"); super("$editor.resizemap");
shown(() -> { shown(() -> {
cont.clear(); cont.clear();
MapTileData data = editor.getMap(); width = editor.width();
width = data.width(); height = editor.height();
height = data.height();
Table table = new Table(); Table table = new Table();
@@ -24,7 +24,7 @@ public class MapSaveDialog extends FloatingDialog{
shown(() -> { shown(() -> {
cont.clear(); cont.clear();
cont.label(() -> { cont.label(() -> {
Map map = world.maps.getByName(field.getText()); Map map = world.maps.byName(field.getText());
if(map != null){ if(map != null){
if(map.custom){ if(map.custom){
return "$editor.overwrite"; return "$editor.overwrite";
@@ -69,7 +69,7 @@ public class MapSaveDialog extends FloatingDialog{
if(field.getText().isEmpty()){ if(field.getText().isEmpty()){
return true; return true;
} }
Map map = world.maps.getByName(field.getText()); Map map = world.maps.byName(field.getText());
return map != null && !map.custom; return map != null && !map.custom;
} }
} }
+18 -53
View File
@@ -18,7 +18,6 @@ import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.TextField; import io.anuke.arc.scene.ui.TextField;
import io.anuke.arc.scene.ui.layout.Unit; import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.Tmp; import io.anuke.arc.util.Tmp;
import io.anuke.mindustry.editor.DrawOperation.TileOperation;
import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.input.Binding; import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.ui.GridImage; import io.anuke.mindustry.ui.GridImage;
@@ -29,8 +28,9 @@ import static io.anuke.mindustry.Vars.ui;
public class MapView extends Element implements GestureListener{ public class MapView extends Element implements GestureListener{
private MapEditor editor; private MapEditor editor;
private EditorTool tool = EditorTool.pencil; private EditorTool tool = EditorTool.pencil;
private OperationStack stack = new OperationStack(); //private OperationStack stack = new OperationStack();
private DrawOperation op; //private DrawOperation op;
//private GridBits used;
private Bresenham2 br = new Bresenham2(); private Bresenham2 br = new Bresenham2();
private boolean updated = false; private boolean updated = false;
private float offsetx, offsety; private float offsetx, offsety;
@@ -87,8 +87,6 @@ public class MapView extends Element implements GestureListener{
mousex = x; mousex = x;
mousey = y; mousey = y;
op = new DrawOperation(editor.getMap());
updated = false; updated = false;
Point2 p = project(x, y); Point2 p = project(x, y);
@@ -135,12 +133,7 @@ public class MapView extends Element implements GestureListener{
updated = true; updated = true;
} }
if(op != null && updated){ editor.flushOp();
if(!op.isEmpty()){
stack.add(op);
}
op = null;
}
if(button == KeyCode.MOUSE_MIDDLE && lastTool != null){ if(button == KeyCode.MOUSE_MIDDLE && lastTool != null){
tool = lastTool; tool = lastTool;
@@ -190,14 +183,6 @@ public class MapView extends Element implements GestureListener{
this.tool = tool; this.tool = tool;
} }
public void clearStack(){
stack.clear();
}
public OperationStack getStack(){
return stack;
}
public boolean isGrid(){ public boolean isGrid(){
return grid; return grid;
} }
@@ -206,26 +191,6 @@ public class MapView extends Element implements GestureListener{
this.grid = grid; this.grid = grid;
} }
public void undo(){
if(stack.canUndo()){
stack.undo(editor);
}
}
public void redo(){
if(stack.canRedo()){
stack.redo(editor);
}
}
public void addTileOp(TileOperation t){
op.addOperation(t);
}
public boolean checkForDuplicates(short x, short y){
return op.checkDuplicate(x, y);
}
@Override @Override
public void act(float delta){ public void act(float delta){
super.act(delta); super.act(delta);
@@ -259,14 +224,14 @@ public class MapView extends Element implements GestureListener{
} }
private Point2 project(float x, float y){ private Point2 project(float x, float y){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height()); float ratio = 1f / ((float) editor.width() / editor.height());
float size = Math.min(width, height); float size = Math.min(width, height);
float sclwidth = size * zoom; float sclwidth = size * zoom;
float sclheight = size * zoom * ratio; float sclheight = size * zoom * ratio;
x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.getMap().width(); x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.width();
y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * editor.getMap().height(); y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * editor.height();
if(editor.getDrawBlock().size % 2 == 0 && tool != EditorTool.eraser){ if(editor.drawBlock.size % 2 == 0 && tool != EditorTool.eraser){
return Tmp.g1.set((int) (x - 0.5f), (int) (y - 0.5f)); return Tmp.g1.set((int) (x - 0.5f), (int) (y - 0.5f));
}else{ }else{
return Tmp.g1.set((int) x, (int) y); return Tmp.g1.set((int) x, (int) y);
@@ -274,26 +239,26 @@ public class MapView extends Element implements GestureListener{
} }
private Vector2 unproject(int x, int y){ private Vector2 unproject(int x, int y){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height()); float ratio = 1f / ((float) editor.width() / editor.height());
float size = Math.min(width, height); float size = Math.min(width, height);
float sclwidth = size * zoom; float sclwidth = size * zoom;
float sclheight = size * zoom * ratio; float sclheight = size * zoom * ratio;
float px = ((float) x / editor.getMap().width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2; float px = ((float) x / editor.width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2;
float py = ((float) (y) / editor.getMap().height()) * sclheight float py = ((float) (y) / editor.height()) * sclheight
+ offsety * zoom - sclheight / 2 + getHeight() / 2; + offsety * zoom - sclheight / 2 + getHeight() / 2;
return vec.set(px, py); return vec.set(px, py);
} }
@Override @Override
public void draw(){ public void draw(){
float ratio = 1f / ((float) editor.getMap().width() / editor.getMap().height()); float ratio = 1f / ((float) editor.width() / editor.height());
float size = Math.min(width, height); float size = Math.min(width, height);
float sclwidth = size * zoom; float sclwidth = size * zoom;
float sclheight = size * zoom * ratio; float sclheight = size * zoom * ratio;
float centerx = x + width / 2 + offsetx * zoom; float centerx = x + width / 2 + offsetx * zoom;
float centery = y + height / 2 + offsety * zoom; float centery = y + height / 2 + offsety * zoom;
image.setImageSize(editor.getMap().width(), editor.getMap().height()); image.setImageSize(editor.width(), editor.height());
if(!ScissorStack.pushScissors(rect.set(x, y, width, height))){ if(!ScissorStack.pushScissors(rect.set(x, y, width, height))){
return; return;
@@ -318,18 +283,18 @@ public class MapView extends Element implements GestureListener{
int index = 0; int index = 0;
for(int i = 0; i < MapEditor.brushSizes.length; i++){ for(int i = 0; i < MapEditor.brushSizes.length; i++){
if(editor.getBrushSize() == MapEditor.brushSizes[i]){ if(editor.brushSize == MapEditor.brushSizes[i]){
index = i; index = i;
break; break;
} }
} }
float scaling = zoom * Math.min(width, height) / editor.getMap().width(); float scaling = zoom * Math.min(width, height) / editor.width();
Draw.color(Pal.accent); Draw.color(Pal.accent);
Lines.stroke(Unit.dp.scl(2f)); Lines.stroke(Unit.dp.scl(2f));
if((!editor.getDrawBlock().isMultiblock() || tool == EditorTool.eraser) && tool != EditorTool.fill){ if((!editor.drawBlock.isMultiblock() || tool == EditorTool.eraser) && tool != EditorTool.fill){
if(tool == EditorTool.line && drawing){ if(tool == EditorTool.line && drawing){
Vector2 v1 = unproject(startx, starty).add(x, y); Vector2 v1 = unproject(startx, starty).add(x, y);
float sx = v1.x, sy = v1.y; float sx = v1.x, sy = v1.y;
@@ -348,11 +313,11 @@ public class MapView extends Element implements GestureListener{
if((tool.edit || tool == EditorTool.line) && (!mobile || drawing)){ if((tool.edit || tool == EditorTool.line) && (!mobile || drawing)){
Point2 p = project(mousex, mousey); Point2 p = project(mousex, mousey);
Vector2 v = unproject(p.x, p.y).add(x, y); Vector2 v = unproject(p.x, p.y).add(x, y);
float offset = (editor.getDrawBlock().size % 2 == 0 ? scaling / 2f : 0f); float offset = (editor.drawBlock.size % 2 == 0 ? scaling / 2f : 0f);
Lines.square( Lines.square(
v.x + scaling / 2f + offset, v.x + scaling / 2f + offset,
v.y + scaling / 2f + offset, v.y + scaling / 2f + offset,
scaling * editor.getDrawBlock().size / 2f); scaling * editor.drawBlock.size / 2f);
} }
} }
@@ -252,10 +252,10 @@ public interface BuilderTrait extends Entity, TeamTrait{
TileEntity core = unit.getClosestCore(); TileEntity core = unit.getClosestCore();
if(core == null || tile.block() != Blocks.air || dst(tile.worldx(), tile.worldy()) > mineDistance if(core == null || tile.block() != Blocks.air || dst(tile.worldx(), tile.worldy()) > mineDistance
|| tile.floor().itemDrop == null || !unit.acceptsItem(tile.floor().itemDrop) || !canMine(tile.floor().itemDrop)){ || tile.drop() == null || !unit.acceptsItem(tile.drop()) || !canMine(tile.drop())){
setMineTile(null); setMineTile(null);
}else{ }else{
Item item = tile.floor().itemDrop; Item item = tile.drop();
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(tile.worldx(), tile.worldy()), 0.4f); unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(tile.worldx(), tile.worldy()), 0.4f);
if(Mathf.chance(Time.delta() * (0.06 - item.hardness * 0.01) * getMinePower())){ if(Mathf.chance(Time.delta() * (0.06 - item.hardness * 0.01) * getMinePower())){
@@ -269,7 +269,6 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
//TODO better smoke effect, this one is awful //TODO better smoke effect, this one is awful
if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) && if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) &&
Mathf.chance(0.009f * Time.delta() * (1f - health / tile.block().health))){ Mathf.chance(0.009f * Time.delta() * (1f - health / tile.block().health))){
Effects.effect(Fx.smoke, x + Mathf.range(4), y + Mathf.range(4)); Effects.effect(Fx.smoke, x + Mathf.range(4), y + Mathf.range(4));
} }
@@ -137,7 +137,7 @@ public class MinimapRenderer implements Disposable{
private int colorFor(Tile tile){ private int colorFor(Tile tile){
tile = tile.target(); tile = tile.target();
return MapIO.colorFor(tile.floor(), tile.block(), tile.getTeam()); return MapIO.colorFor(tile.floor(), tile.block(), tile.oreBlock(), tile.getTeam());
} }
@Override @Override
@@ -230,9 +230,9 @@ public abstract class InputHandler implements InputProcessor{
boolean canMine(Tile tile){ boolean canMine(Tile tile){
return !Core.scene.hasMouse() return !Core.scene.hasMouse()
&& tile.floor().itemDrop != null && tile.floor().itemDrop.hardness <= player.mech.drillPower && tile.drop() != null && tile.drop().hardness <= player.mech.drillPower
&& !tile.floor().playerUnmineable && !tile.floor().playerUnmineable
&& player.acceptsItem(tile.floor().itemDrop) && player.acceptsItem(tile.drop())
&& tile.block() == Blocks.air && player.dst(tile.worldx(), tile.worldy()) <= Player.mineDistance; && tile.block() == Blocks.air && player.dst(tile.worldx(), tile.worldy()) <= Player.mineDistance;
} }
+393 -133
View File
@@ -7,35 +7,50 @@ import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.Pixmap; import io.anuke.arc.graphics.Pixmap;
import io.anuke.arc.graphics.Pixmap.Format; import io.anuke.arc.graphics.Pixmap.Format;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Pack; import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Strings;
import io.anuke.arc.util.Structs; import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.MappableContent;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.maps.MapMeta;
import io.anuke.mindustry.maps.MapTileData;
import io.anuke.mindustry.maps.MapTileData.DataPosition;
import io.anuke.mindustry.maps.MapTileData.TileDataMarker;
import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.CachedTile;
import io.anuke.mindustry.world.LegacyColorMapper; import io.anuke.mindustry.world.LegacyColorMapper;
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock; 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.io.*;
import java.util.Arrays;
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.content;
/** /** Reads and writes map files.*/
* Reads and writes map files.
*/
//TODO name mapping
public class MapIO{ public class MapIO{
public static final int version = 1;
private static final int[] pngHeader = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; private static final int[] pngHeader = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
private static final int version = 0; private static ObjectMap<String, Block> missingBlocks;
private static IntIntMap defaultBlockMap = new IntIntMap();
private static void initBlocks(){
if(missingBlocks != null) return;
missingBlocks = ObjectMap.of(
"stained-stone", Blocks.moss
);
}
public static boolean isImage(FileHandle file){ public static boolean isImage(FileHandle file){
try(InputStream stream = file.read()){ try(InputStream stream = file.read(32)){
for(int i1 : pngHeader){ for(int i1 : pngHeader){
if(stream.read() != i1){ if(stream.read() != i1){
return false; return false;
@@ -47,44 +62,277 @@ public class MapIO{
} }
} }
private static void loadDefaultBlocks(){ public static Pixmap generatePreview(Map map) throws IOException{
for(Block block : content.blocks()){ Pixmap floor = new Pixmap(map.width, map.height, Format.RGBA8888);
defaultBlockMap.put(block.id, block.id); Pixmap wall = new Pixmap(map.width, map.height, Format.RGBA8888);
} int black = Color.rgba8888(Color.BLACK);
CachedTile tile = new CachedTile(){
@Override
public void setFloor(Floor type){
floor.drawPixel(x, floor.getHeight() - 1 - y, colorFor(type, Blocks.air, Blocks.air, getTeam()));
}
@Override
public void setOreByte(byte b){
if(b != 0)
floor.drawPixel(x, floor.getHeight() - 1 - y, colorFor(floor(), Blocks.air, content.block(b), getTeam()));
}
@Override
protected void changed(){
super.changed();
int c = colorFor(Blocks.air, block(), Blocks.air, getTeam());
if(c != black) wall.drawPixel(x, floor.getHeight() - 1 - y, c);
}
};
readTiles(map, (x, y) -> {
tile.x = (short)x;
tile.y = (short)y;
return tile;
});
floor.drawPixmap(wall, 0, 0);
wall.dispose();
return floor;
} }
public static Pixmap generatePixmap(MapTileData data){ public static Pixmap generatePreview(Tile[][] tiles){
Pixmap pixmap = new Pixmap(data.width(), data.height(), Format.RGBA8888); Pixmap pixmap = new Pixmap(tiles.length, tiles[0].length, Format.RGBA8888);
data.position(0, 0); for(int x = 0; x < pixmap.getWidth(); x++){
for(int y = 0; y < pixmap.getHeight(); y++){
TileDataMarker marker = data.newDataMarker(); Tile tile = tiles[x][y];
pixmap.drawPixel(x, pixmap.getHeight() - 1 - y, colorFor(tile.floor(), tile.block(), tile.oreBlock(), tile.getTeam()));
for(int y = 0; y < data.height(); y++){
for(int x = 0; x < data.width(); x++){
data.read(marker);
Block floor = content.block(marker.floor);
Block wall = content.block(marker.wall);
int color = colorFor(floor, wall, Team.all[marker.team]);
pixmap.drawPixel(x, pixmap.getHeight() - 1 - y, color);
} }
} }
data.position(0, 0);
return pixmap; return pixmap;
} }
/**Reads a pixmap in the old (3.5) map format.*/ public static int colorFor(Block floor, Block wall, Block ore, Team team){
public static MapTileData readLegacyPixmap(Pixmap pixmap){ if(wall.synthetic()){
MapTileData data = new MapTileData(pixmap.getWidth(), pixmap.getHeight()); return team.intColor;
}
return Color.rgba8888(wall.solid ? wall.color : ore == Blocks.air ? floor.color : ore.color);
}
for(int x = 0; x < data.width(); x++){ public static void writeMap(FileHandle file, Map map, Tile[][] tiles) throws IOException{
for(int y = 0; y < data.height(); y++){ 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.getOreByte());
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.getOreByte() != tile.getOreByte()){
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(); //key
stream.readUTF(); //val
}
}
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.setOreByte(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.setOreByte(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++){
for(int y = 0; y < pixmap.getHeight(); y++){
int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y); int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y);
LegacyBlock block = LegacyColorMapper.get(color); LegacyBlock block = LegacyColorMapper.get(color);
Tile tile = tiles[x][y];
data.write(x, y, DataPosition.floor, block.floor.id); tile.setFloor(block.floor);
data.write(x, y, DataPosition.wall, block.wall.id); tile.setBlock(block.wall);
if(block.ore != null) tile.setOre(block.ore);
//place core //place core
if(color == Color.rgba8888(Color.GREEN)){ if(color == Color.rgba8888(Color.GREEN)){
@@ -93,121 +341,133 @@ public class MapIO{
int worldx = dx - 1 + x; int worldx = dx - 1 + x;
int worldy = dy - 1 + y; int worldy = dy - 1 + y;
//multiblock parts
if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){ if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){
data.write(worldx, worldy, DataPosition.wall, Blocks.part.id); Tile write = tiles[worldx][worldy];
data.write(worldx, worldy, DataPosition.rotationTeam, Pack.byteByte((byte)0, (byte)Team.blue.ordinal())); write.setBlock(Blocks.part);
data.write(worldx, worldy, DataPosition.link, Pack.byteByte((byte) (dx - 1 + 8), (byte) (dy - 1 + 8))); write.setTeam(Team.blue);
write.setLinkByte(Pack.byteByte((byte) (dx - 1 + 8), (byte) (dy - 1 + 8)));
} }
} }
} }
data.write(x, y, DataPosition.wall, Blocks.coreShard.id); //actual core parts
data.write(x, y, DataPosition.rotationTeam, Pack.byteByte((byte)0, (byte)Team.blue.ordinal())); tile.setBlock(Blocks.coreShard);
tile.setTeam(Team.blue);
} }
} }
} }
return data;
} }
public static void writeMap(OutputStream stream, ObjectMap<String, String> tags, MapTileData data) throws IOException{ /**Reads a pixmap in the old 4.0 .mmap format.*/
if(defaultBlockMap == null){ private static void readLegacyMmapTiles(FileHandle file, Tile[][] tiles) throws IOException{
loadDefaultBlocks(); readLegacyMmapTiles(file, (x, y) -> tiles[x][y]);
}
MapMeta meta = new MapMeta(version, tags, data.width(), data.height(), defaultBlockMap);
DataOutputStream ds = new DataOutputStream(stream);
writeMapMeta(ds, meta);
ds.write(data.toArray());
ds.close();
} }
/** /**Reads a mmap in the old 4.0 .mmap format.*/
* Reads tile data, skipping meta. private static void readLegacyMmapTiles(FileHandle file, TileProvider tiles) throws IOException{
*/ try(DataInputStream stream = new DataInputStream(file.read(bufferSize))){
public static MapTileData readTileData(DataInputStream stream, boolean readOnly) throws IOException{ stream.readInt(); //version
MapMeta meta = readMapMeta(stream); byte tagAmount = stream.readByte();
return readTileData(stream, meta, readOnly);
}
for(int i = 0; i < tagAmount; i++){
stream.readUTF(); //key
stream.readUTF(); //val
}
/** initBlocks();
* Does not skip meta. Call after reading meta.
*/ //block id -> real id map
public static MapTileData readTileData(DataInputStream stream, MapMeta meta, boolean readOnly) throws IOException{ IntIntMap map = new IntIntMap();
byte[] bytes = new byte[stream.available()]; IntIntMap oreMap = new IntIntMap();
stream.readFully(bytes);
return new MapTileData(bytes, meta.width, meta.height, meta.blockMap, readOnly); short blocks = stream.readShort();
} for(int i = 0; i < blocks; i++){
short id = stream.readShort();
/** String name = stream.readUTF();
* Reads tile data, skipping meta tags. Block block = content.getByName(ContentType.block, name);
*/ if(block == null){
public static MapTileData readTileData(Map map, boolean readOnly){ //substitute for replacement in missingBlocks if possible
try(DataInputStream ds = new DataInputStream(map.stream.get())){ if(missingBlocks.containsKey(name)){
return MapIO.readTileData(ds, readOnly); block = missingBlocks.get(name);
}catch(IOException e){ }else if(name.startsWith("ore-")){ //an ore floor combination
throw new RuntimeException(e); String[] split = name.split("-");
} String itemName = split[1], floorName = Strings.join("-", Arrays.copyOfRange(split, 2, split.length));
} Item item = content.getByName(ContentType.item, itemName);
Block oreBlock = item == null ? null : content.getByName(ContentType.block, "ore-" + item.name);
public static MapMeta readMapMeta(DataInputStream stream) throws IOException{ Block floor = missingBlocks.get(floorName, content.getByName(ContentType.block, floorName));
ObjectMap<String, String> tags = new ObjectMap<>(); if(oreBlock != null && floor != null){
IntIntMap map = new IntIntMap(); oreMap.put(id, oreBlock.id);
block = floor;
int version = stream.readInt(); }else{
block = Blocks.air;
byte tagAmount = stream.readByte(); }
}else{
for(int i = 0; i < tagAmount; i++){ block = Blocks.air;
String name = stream.readUTF(); }
String value = stream.readUTF();
tags.put(name, value); }
} map.put(id, block.id);
}
short blocks = stream.readShort(); short width = stream.readShort(), height = stream.readShort();
for(int i = 0; i < blocks; i++){
short id = stream.readShort(); for(int y = 0; y < height; y++){
String name = stream.readUTF(); for(int x = 0; x < width; x++){
Block block = content.getByName(ContentType.block, name); Tile tile = tiles.get(x, y);
if(block == null){ byte floorb = stream.readByte();
block = Blocks.air; byte blockb = stream.readByte();
byte link = stream.readByte();
byte rotTeamb = 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().synthetic()){
tile.setTeam(Team.all[Mathf.clamp(Pack.rightByte(rotTeamb), 0, Team.all.length)]);
}
if(tile.block() == Blocks.part){
tile.setLinkByte(link);
}
if(oreMap.containsKey(floorb)){
tile.setOreByte((byte)oreMap.get(floorb, 0));
}
}
} }
map.put(id, block.id);
} }
int width = stream.readShort();
int height = stream.readShort();
return new MapMeta(version, tags, width, height, map);
} }
public static void writeMapMeta(DataOutputStream stream, MapMeta meta) throws IOException{ private static Map readLegacyMap(FileHandle file, boolean custom) throws IOException{
stream.writeInt(meta.version); try(DataInputStream stream = new DataInputStream(file.read(bufferSize))){
stream.writeByte((byte) meta.tags.size); ObjectMap<String, String> tags = new ObjectMap<>();
for(Entry<String, String> entry : meta.tags.entries()){ int version = stream.readInt();
stream.writeUTF(entry.key); if(version != 0) throw new IOException("Attempted to read non-legacy map in legacy method!");
stream.writeUTF(entry.value); byte tagAmount = stream.readByte();
for(int i = 0; i < tagAmount; i++){
String name = stream.readUTF();
String value = stream.readUTF();
tags.put(name, value);
}
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);
} }
stream.writeShort(content.blocks().size);
for(Block block : content.blocks()){
stream.writeShort(block.id);
stream.writeUTF(block.name);
}
stream.writeShort(meta.width);
stream.writeShort(meta.height);
} }
public static int colorFor(Block floor, Block wall, Team team){ //endregion
if(wall.synthetic()){
return team.intColor; interface TileProvider{
} Tile get(int x, int y);
return Color.rgba8888(wall.solid ? wall.color : floor.color);
} }
} }
@@ -14,8 +14,8 @@ import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Serialization; import io.anuke.mindustry.gen.Serialization;
import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BlockPart;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -43,18 +43,37 @@ public abstract class SaveFileVersion{
} }
public void writeMap(DataOutputStream stream) throws IOException{ public void writeMap(DataOutputStream stream) throws IOException{
//write world size //write world size
stream.writeShort(world.width()); stream.writeShort(world.width());
stream.writeShort(world.height()); stream.writeShort(world.height());
//floor first
for(int i = 0; i < world.width() * world.height(); i++){ for(int i = 0; i < world.width() * world.height(); i++){
Tile tile = world.tile(i % world.width(), i / world.width()); Tile tile = world.tile(i % world.width(), i / world.width());
stream.writeByte(tile.getFloorID()); stream.writeByte(tile.getFloorID());
stream.writeByte(tile.getOreByte());
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.getOreByte() != tile.getOreByte()){
break;
}
consecutives++;
}
stream.writeByte(consecutives);
i += consecutives;
}
//blocks
for(int i = 0; i < world.width() * world.height(); i++){
Tile tile = world.tile(i % world.width(), i / world.width());
stream.writeByte(tile.getBlockID()); stream.writeByte(tile.getBlockID());
if(tile.block() instanceof BlockPart){ if(tile.block() == Blocks.part){
stream.writeByte(tile.link); stream.writeByte(tile.link);
}else if(tile.entity != null){ }else if(tile.entity != null){
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation
@@ -67,13 +86,14 @@ public abstract class SaveFileVersion{
tile.entity.writeConfig(stream); tile.entity.writeConfig(stream);
tile.entity.write(stream); tile.entity.write(stream);
}else if(tile.block() == Blocks.air){ }else{
//write consecutive non-entity blocks
int consecutives = 0; int consecutives = 0;
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){ for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
Tile nextTile = world.tile(j % world.width(), j / world.width()); Tile nextTile = world.tile(j % world.width(), j / world.width());
if(nextTile.getFloorID() != tile.getFloorID() || nextTile.block() != Blocks.air){ if(nextTile.block() != tile.block()){
break; break;
} }
@@ -94,14 +114,34 @@ public abstract class SaveFileVersion{
Tile[][] tiles = world.createTiles(width, height); Tile[][] tiles = world.createTiles(width, height);
//read floor and create tiles first
for(int i = 0; i < width * height; i++){ for(int i = 0; i < width * height; i++){
int x = i % width, y = i / width; int x = i % width, y = i / width;
byte floorid = stream.readByte(); byte floorid = stream.readByte();
byte wallid = stream.readByte(); byte oreid = stream.readByte();
int consecutives = stream.readUnsignedByte();
Tile tile = new Tile(x, y, floorid, wallid); tiles[x][y] = new Tile(x, y, floorid, (byte)0);
tiles[x][y].setOreByte(oreid);
if(wallid == Blocks.part.id){ 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.setOreByte(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(); tile.link = stream.readByte();
}else if(tile.entity != null){ }else if(tile.entity != null){
byte tr = stream.readByte(); byte tr = stream.readByte();
@@ -121,19 +161,16 @@ public abstract class SaveFileVersion{
tile.entity.readConfig(stream); tile.entity.readConfig(stream);
tile.entity.read(stream); tile.entity.read(stream);
}else if(wallid == 0){ }else{
int consecutives = stream.readUnsignedByte(); int consecutives = stream.readUnsignedByte();
for(int j = i + 1; j < i + 1 + consecutives; j++){ for(int j = i + 1; j < i + 1 + consecutives; j++){
int newx = j % width, newy = j / width; int newx = j % width, newy = j / width;
Tile newTile = new Tile(newx, newy, floorid, wallid); tiles[newx][newy].setBlock(block);
tiles[newx][newy] = newTile;
} }
i += consecutives; i += consecutives;
} }
tiles[x][y] = tile;
} }
content.setTemporaryMapper(null); content.setTemporaryMapper(null);
+5 -5
View File
@@ -48,7 +48,7 @@ public class SaveIO{
} }
public static DataInputStream getSlotStream(int slot){ 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){ public static boolean isSaveValid(int slot){
@@ -60,7 +60,7 @@ public class SaveIO{
} }
public static boolean isSaveValid(FileHandle file){ 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){ public static boolean isSaveValid(DataInputStream stream){
@@ -95,7 +95,7 @@ public class SaveIO{
} }
public static void write(FileHandle file){ public static void write(FileHandle file){
write(new DeflaterOutputStream(file.write(false)){ write(new DeflaterOutputStream(file.write(false, bufferSize)){
byte[] tmp = {0}; byte[] tmp = {0};
public void write(int var1) throws IOException { public void write(int var1) throws IOException {
@@ -120,12 +120,12 @@ public class SaveIO{
public static void load(FileHandle file) throws SaveException{ public static void load(FileHandle file) throws SaveException{
try{ try{
//try and load; if any exception at all occurs //try and load; if any exception at all occurs
load(new InflaterInputStream(file.read())); load(new InflaterInputStream(file.read(bufferSize)));
}catch(SaveException e){ }catch(SaveException e){
e.printStackTrace(); e.printStackTrace();
FileHandle backup = file.sibling(file.name() + "-backup." + file.extension()); FileHandle backup = file.sibling(file.name() + "-backup." + file.extension());
if(backup.exists()){ if(backup.exists()){
load(new InflaterInputStream(backup.read())); load(new InflaterInputStream(backup.read(bufferSize)));
}else{ }else{
throw new SaveException(e.getCause()); throw new SaveException(e.getCause());
} }
+1 -1
View File
@@ -19,7 +19,7 @@ public class SaveMeta{
this.build = build; this.build = build;
this.timestamp = timestamp; this.timestamp = timestamp;
this.timePlayed = timePlayed; this.timePlayed = timePlayed;
this.map = world.maps.getByName(map); this.map = world.maps.all().find(m -> m.fileName().equals(map));
this.wave = wave; this.wave = wave;
this.rules = rules; this.rules = rules;
} }
@@ -1,6 +1,6 @@
package io.anuke.mindustry.io.versions; package io.anuke.mindustry.io.versions;
import io.anuke.arc.util.Strings; import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.util.Time; import io.anuke.arc.util.Time;
import io.anuke.mindustry.game.Version; import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.gen.Serialization; import io.anuke.mindustry.gen.Serialization;
@@ -34,8 +34,8 @@ public class Save16 extends SaveFileVersion{
state.rules.spawns = content.<Zone>getByID(ContentType.zone, state.rules.zone).rules.get().spawns; state.rules.spawns = content.<Zone>getByID(ContentType.zone, state.rules.zone).rules.get().spawns;
} }
String mapname = stream.readUTF(); String mapname = stream.readUTF();
Map map = world.maps.getByName(mapname); Map map = world.maps.all().find(m -> m.fileName().equals(mapname));
if(map == null) map = new Map(Strings.capitalize(mapname), 1, 1); if(map == null) map = new Map(customMapDirectory.child(mapname), 1, 1, new ObjectMap<>(), true);
world.setMap(map); world.setMap(map);
int wave = stream.readInt(); int wave = stream.readInt();
@@ -62,7 +62,7 @@ public class Save16 extends SaveFileVersion{
//--GENERAL STATE-- //--GENERAL STATE--
Serialization.writeRules(stream, state.rules); Serialization.writeRules(stream, state.rules);
stream.writeUTF(world.getMap().name); //map name stream.writeUTF(world.getMap().fileName()); //map name
stream.writeInt(state.wave); //wave stream.writeInt(state.wave); //wave
stream.writeFloat(state.wavetime); //wave countdown stream.writeFloat(state.wavetime); //wave countdown
+49 -18
View File
@@ -1,44 +1,75 @@
package io.anuke.mindustry.maps; package io.anuke.mindustry.maps;
import io.anuke.arc.graphics.Texture; import io.anuke.arc.Core;
import io.anuke.arc.collection.ObjectMap; import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.function.Supplier; import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Texture;
import java.io.InputStream; import io.anuke.mindustry.io.MapIO;
public class Map{ public class Map{
/** Internal map name. This is the filename, without any extensions.*/
public final String name;
/** Whether this is a custom map.*/ /** Whether this is a custom map.*/
public final boolean custom; public final boolean custom;
/** Metadata. Author description, display name, etc.*/ /** Metadata. Author description, display name, etc.*/
public final MapMeta meta; public final ObjectMap<String, String> tags;
/** Supplies a new input stream with the data of this map.*/ /** Base file of this map.*/
public final Supplier<InputStream> stream; public final FileHandle file;
/** Format version.*/
public final int version;
/** Map width/height, shorts.*/
public int width, height;
/** Preview texture.*/ /** Preview texture.*/
public Texture texture; public Texture texture;
/** Build that this map was created in. -1 = unknown or custom build.*/
public int build;
public Map(String name, MapMeta meta, boolean custom, Supplier<InputStream> streamSupplier){ public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom, int version, int build){
this.name = name;
this.custom = custom; this.custom = custom;
this.meta = meta; this.tags = tags;
this.stream = streamSupplier; this.file = file;
this.width = width;
this.height = height;
this.version = version;
this.build = build;
} }
public Map(String unknownName, int width, int height){ public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom, int version){
this(unknownName, new MapMeta(0, new ObjectMap<>(), width, height, null), true, () -> null); 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(){
return file.nameWithoutExtension();
} }
public String getDisplayName(){ public String getDisplayName(){
return meta.tags.get("name", name); return tags.get("name", fileName());
}
public String author(){
return tag("author");
}
public String description(){
return tag("description");
}
public String name(){
return tag("name");
}
public String tag(String name){
return tags.containsKey(name) && !tags.get(name).trim().isEmpty() ? tags.get(name): Core.bundle.get("unknown");
} }
@Override @Override
public String toString(){ public String toString(){
return "Map{" + return "Map{" +
"name='" + name + '\'' + "file='" + file + '\'' +
", custom=" + custom + ", custom=" + custom +
", meta=" + meta + ", tags=" + tags +
'}'; '}';
} }
} }
@@ -1,46 +0,0 @@
package io.anuke.mindustry.maps;
import io.anuke.arc.Core;
import io.anuke.arc.collection.IntIntMap;
import io.anuke.arc.collection.ObjectMap;
//todo: specify preferred game rules here; can be overriden
public class MapMeta{
public final int version;
public final ObjectMap<String, String> tags;
public final int width, height;
public final IntIntMap blockMap;
public MapMeta(int version, ObjectMap<String, String> tags, int width, int height, IntIntMap blockMap){
this.version = version;
this.tags = tags;
this.width = width;
this.height = height;
this.blockMap = blockMap;
}
public String author(){
return tag("author");
}
public String description(){
return tag("description");
}
public String name(){
return tag("name");
}
public String tag(String name){
return tags.containsKey(name) && !tags.get(name).trim().isEmpty() ? tags.get(name): Core.bundle.get("unknown");
}
@Override
public String toString(){
return "MapMeta{" +
"tags=" + tags +
", width=" + width +
", height=" + height +
'}';
}
}
@@ -1,175 +0,0 @@
package io.anuke.mindustry.maps;
import io.anuke.arc.collection.IntIntMap;
import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Structs;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.world.Block;
import java.nio.ByteBuffer;
public class MapTileData{
/**
* Tile size: 5 bytes. <br>
* 0: ground tile <br>
* 1: wall tile <br>
* 2: rotation + team <br>
* 3: link (x/y) <br>
* 4: elevation <br>
*/
private final static int TILE_SIZE = 5;
private final ByteBuffer buffer;
private final int width, height;
private final boolean readOnly;
private IntIntMap map;
public MapTileData(int width, int height){
this.width = width;
this.height = height;
this.map = null;
this.readOnly = false;
buffer = ByteBuffer.allocate(width * height * TILE_SIZE);
}
public MapTileData(byte[] bytes, int width, int height, IntIntMap mapping, boolean readOnly){
buffer = ByteBuffer.wrap(bytes);
this.width = width;
this.height = height;
this.map = mapping;
this.readOnly = readOnly;
if(mapping != null && !readOnly){
buffer.position(0);
TileDataMarker marker = new TileDataMarker();
for(int i = 0; i < width * height; i++){
read(marker);
//strip blockparts from map data, as they can be invalid
if(marker.wall == Blocks.part.id){
marker.wall = Blocks.air.id;
}
buffer.position(i * TILE_SIZE);
//write mapped marker
write(marker);
}
buffer.position(0);
for(int x = 0; x < width; x ++){
for(int y = 0; y < height; y ++){
//add missing blockparts
Block drawBlock = Vars.content.block(read(x, y, DataPosition.wall));
if(drawBlock.isMultiblock()){
int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2;
for(int dx = 0; dx < drawBlock.size; dx++){
for(int dy = 0; dy < drawBlock.size; dy++){
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, width, height) && !(dx + offsetx == 0 && dy + offsety == 0)){
write(worldx, worldy, DataPosition.wall, Blocks.part.id);
write(worldx, worldy, DataPosition.link, Pack.byteByte((byte) (dx + offsetx + 8), (byte) (dy + offsety + 8)));
}
}
}
}
}
}
buffer.position(0);
this.map = null;
}
}
public byte[] toArray(){
return buffer.array();
}
public int width(){
return width;
}
public int height(){
return height;
}
/**
* Write a byte to a specific position.
*/
public void write(int x, int y, DataPosition position, byte data){
buffer.put((x + width * y) * TILE_SIZE + position.ordinal(), data);
}
/**
* Gets a byte at a specific position.
*/
public byte read(int x, int y, DataPosition position){
return buffer.get((x + width * y) * TILE_SIZE + position.ordinal());
}
/**
* Reads and returns the next tile data.
*/
public TileDataMarker read(TileDataMarker marker){
marker.read(buffer);
return marker;
}
/**
* Writes this tile data marker.
*/
public void write(TileDataMarker marker){
marker.write(buffer);
}
/**
* Sets read position to the specified coordinates
*/
public void position(int x, int y){
buffer.position((x + width * y) * TILE_SIZE);
}
public TileDataMarker newDataMarker(){
return new TileDataMarker();
}
public enum DataPosition{
floor, wall, link, rotationTeam, elevation
}
public class TileDataMarker{
public byte floor, wall;
public byte link;
public byte rotation;
public byte team;
public byte elevation;
public void read(ByteBuffer buffer){
floor = buffer.get();
wall = buffer.get();
link = buffer.get();
byte rt = buffer.get();
elevation = buffer.get();
rotation = Pack.leftByte(rt);
team = Pack.rightByte(rt);
if(map != null){
floor = (byte) map.get(floor, Blocks.stone.id);
wall = (byte) map.get(wall, 0);
}
}
public void write(ByteBuffer buffer){
if(readOnly) throw new IllegalArgumentException("This data is read-only.");
buffer.put(floor);
buffer.put(wall);
buffer.put(link);
buffer.put(Pack.byteByte(rotation, team));
buffer.put(elevation);
}
}
}
+73 -84
View File
@@ -1,166 +1,155 @@
package io.anuke.mindustry.maps; package io.anuke.mindustry.maps;
import io.anuke.arc.Core; import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle; import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Texture; import io.anuke.arc.graphics.Texture;
import io.anuke.arc.collection.Array;
import io.anuke.arc.util.Disposable; import io.anuke.arc.util.Disposable;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.mindustry.io.MapIO;
import io.anuke.arc.function.Supplier;
import io.anuke.arc.util.Log; import io.anuke.arc.util.Log;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.world.Tile;
import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Maps implements Disposable{ public class Maps implements Disposable{
/**List of all built-in maps.*/ /** List of all built-in maps. */
private static final String[] defaultMapNames = {}; private static final String[] defaultMapNames = {"impact0079"};
/**Tile format version.*/ /** All maps stored in an ordered array. */
private static final int version = 0; private Array<Map> maps = new Array<>();
/**Maps map names to the real maps.*/ /** Returns a list of all maps, including custom ones. */
private ObjectMap<String, Map> maps = new ObjectMap<>();
/**All maps stored in an ordered array.*/
private Array<Map> allMaps = new Array<>();
/**Temporary array used for returning things.*/
private Array<Map> returnArray = new Array<>();
/**Returns a list of all maps, including custom ones.*/
public Array<Map> all(){ public Array<Map> all(){
return allMaps; return maps;
} }
/**Returns a list of only custom maps.*/ /** Returns a list of only custom maps. */
public Array<Map> customMaps(){ public Array<Map> customMaps(){
returnArray.clear(); return maps.select(m -> m.custom);
for(Map map : allMaps){
if(map.custom) returnArray.add(map);
}
return returnArray;
} }
/**Returns a list of only default maps.*/ /** Returns a list of only default maps. */
public Array<Map> defaultMaps(){ public Array<Map> defaultMaps(){
returnArray.clear(); return maps.select(m -> !m.custom);
for(Map map : allMaps){
if(!map.custom) returnArray.add(map);
}
return returnArray;
} }
/**Returns map by internal name.*/ public Map byName(String name){
public Map getByName(String name){ return maps.find(m -> m.name().equals(name));
return maps.get(name);
} }
/**Loads a map from the map folder and returns it. Should only be used for zone maps. /**
* Does not add this map to the map list.*/ * Loads a map from the map folder and returns it. Should only be used for zone maps.
* Does not add this map to the map list.
*/
public Map loadInternalMap(String name){ public Map loadInternalMap(String name){
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension); FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
try(DataInputStream ds = new DataInputStream(file.read())) { try{
return new Map(name, MapIO.readMapMeta(ds), false, file::read); return MapIO.readMap(file, false);
}catch(IOException e){ }catch(IOException e){
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/**Load all maps. Should be called at application start.*/ /** Load all maps. Should be called at application start. */
public void load(){ public void load(){
Time.mark();
try{ try{
for (String name : defaultMapNames) { for(String name : defaultMapNames){
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension); FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
loadMap(file.nameWithoutExtension(), file::read, false); loadMap(file, false);
} }
}catch (IOException e){ }catch(IOException e){
throw new RuntimeException(e); throw new RuntimeException(e);
} }
loadCustomMaps(); loadCustomMaps();
Log.info("Time to load maps: {0}", Time.elapsed());
} }
/**Save a map. This updates all values and stored data necessary.*/ /** Save a custom map to the directory. This updates all values and stored data necessary.
public void saveMap(String name, MapTileData data, ObjectMap<String, String> tags){ * The tags are copied to prevent mutation later.*/
public void saveMap(ObjectMap<String, String> baseTags, Tile[][] data){
try{ try{
//create copy of tags to prevent mutation later ObjectMap<String, String> tags = new ObjectMap<>(baseTags);
ObjectMap<String, String> newTags = new ObjectMap<>(); String name = tags.get("name");
newTags.putAll(tags); if(name == null) throw new IllegalArgumentException("Can't save a map with no name. How did this happen?");
tags = newTags;
FileHandle file = customMapDirectory.child(name + "." + mapExtension); FileHandle file = customMapDirectory.child(name + "." + mapExtension);
MapIO.writeMap(file.write(false), tags, data);
if(maps.containsKey(name)){ //find map with the same exact display name
if(maps.get(name).texture != null) { Map other = maps.find(m -> m.getDisplayName().equals(name));
maps.get(name).texture.dispose();
maps.get(name).texture = null; if(other != null){
//dispose of map if it's already there
if(other.texture != null){
other.texture.dispose();
other.texture = null;
} }
allMaps.removeValue(maps.get(name), true); maps.remove(other);
} }
Map map = new Map(name, new MapMeta(version, tags, data.width(), data.height(), null), true, getStreamFor(name)); //create map, write it, etc etc etc
Map map = new Map(file, data.length, data[0].length, tags, true);
MapIO.writeMap(file, map, data);
if(!headless){ if(!headless){
map.texture = new Texture(MapIO.generatePixmap(data)); map.texture = new Texture(MapIO.generatePreview(data));
} }
allMaps.add(map); maps.add(map);
}catch(IOException e){
maps.put(name, map);
}catch (IOException e){
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
/**Removes a map completely.*/ /** Import a map, then save it. This updates all values and stored data necessary. */
public void importMap(FileHandle file, Map map) throws IOException{
file.copyTo(customMapDirectory.child(file.name()));
if(!headless){
map.texture = new Texture(MapIO.generatePreview(map));
}
maps.add(map);
}
/** Removes a map completely. */
public void removeMap(Map map){ public void removeMap(Map map){
if(map.texture != null){ if(map.texture != null){
map.texture.dispose(); map.texture.dispose();
map.texture = null; map.texture = null;
} }
maps.remove(map.name); maps.remove(map);
allMaps.removeValue(map, true); map.file.delete();
customMapDirectory.child(map.name + "." + mapExtension).delete();
} }
private void loadMap(String name, Supplier<InputStream> supplier, boolean custom) throws IOException{ private void loadMap(FileHandle file, boolean custom) throws IOException{
try(DataInputStream ds = new DataInputStream(supplier.get())) { Map map = MapIO.readMap(file, custom);
MapMeta meta = MapIO.readMapMeta(ds);
Map map = new Map(name, meta, custom, supplier);
if (!headless){ if(!headless){
map.texture = new Texture(MapIO.generatePixmap(MapIO.readTileData(ds, meta, true))); map.texture = new Texture(MapIO.generatePreview(map));
}
maps.put(map.name, map);
allMaps.add(map);
} }
maps.add(map);
} }
private void loadCustomMaps(){ private void loadCustomMaps(){
for(FileHandle file : customMapDirectory.list()){ for(FileHandle file : customMapDirectory.list()){
try{ try{
if(file.extension().equalsIgnoreCase(mapExtension)){ if(file.extension().equalsIgnoreCase(mapExtension)){
loadMap(file.nameWithoutExtension(), file::read, true); loadMap(file, true);
} }
}catch (Exception e){ }catch(Exception e){
Log.err("Failed to load custom map file '{0}'!", file); Log.err("Failed to load custom map file '{0}'!", file);
Log.err(e); Log.err(e);
} }
} }
} }
/**Returns an input stream supplier for a given map name.*/
private Supplier<InputStream> getStreamFor(String name){
return customMapDirectory.child(name + "." + mapExtension)::read;
}
@Override @Override
public void dispose() { public void dispose(){
} }
} }
@@ -6,8 +6,6 @@ import io.anuke.arc.util.noise.Simplex;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.OreBlock;
public class BasicGenerator extends RandomGenerator{ public class BasicGenerator extends RandomGenerator{
private Array<Item> ores; private Array<Item> ores;
@@ -31,13 +29,14 @@ public class BasicGenerator extends RandomGenerator{
public void generate(int x, int y){ public void generate(int x, int y){
floor = Blocks.stone; floor = Blocks.stone;
if(ores != null && ((Floor) floor).hasOres){ if(ores != null){
int offsetX = x - 4, offsetY = y + 23; int offsetX = x - 4, offsetY = y + 23;
for(int i = ores.size - 1; i >= 0; i--){ for(int i = ores.size - 1; i >= 0; i--){
Item entry = ores.get(i); Item entry = ores.get(i);
if(Math.abs(0.5f - sim.octaveNoise2D(2, 0.7, 1f / (50 + i * 2), offsetX, offsetY)) > 0.23f && if(Math.abs(0.5f - sim.octaveNoise2D(2, 0.7, 1f / (50 + i * 2), offsetX, offsetY)) > 0.23f &&
Math.abs(0.5f - sim2.octaveNoise2D(1, 1, 1f / (40 + i * 4), offsetX, offsetY)) > 0.32f){ Math.abs(0.5f - sim2.octaveNoise2D(1, 1, 1f / (40 + i * 4), offsetX, offsetY)) > 0.32f){
floor = OreBlock.get(floor, entry);
//floor = OreBlock.get(floor, entry);
break; break;
} }
} }
@@ -9,19 +9,17 @@ import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.Items; import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.io.MapIO; import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.maps.MapTileData;
import io.anuke.mindustry.maps.MapTileData.TileDataMarker;
import io.anuke.mindustry.type.ItemStack; import io.anuke.mindustry.type.ItemStack;
import io.anuke.mindustry.type.Loadout; import io.anuke.mindustry.type.Loadout;
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.Floor; import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.OreBlock;
import io.anuke.mindustry.world.blocks.StaticWall; import io.anuke.mindustry.world.blocks.StaticWall;
import io.anuke.mindustry.world.blocks.storage.CoreBlock; import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import io.anuke.mindustry.world.blocks.storage.StorageBlock; import io.anuke.mindustry.world.blocks.storage.StorageBlock;
import static io.anuke.mindustry.Vars.content; import java.io.IOException;
import static io.anuke.mindustry.Vars.world; import static io.anuke.mindustry.Vars.world;
public class MapGenerator extends Generator{ public class MapGenerator extends Generator{
@@ -73,112 +71,112 @@ public class MapGenerator extends Generator{
public void init(Loadout loadout){ public void init(Loadout loadout){
this.loadout = loadout; this.loadout = loadout;
map = world.maps.loadInternalMap(mapName); map = world.maps.loadInternalMap(mapName);
width = map.meta.width; width = map.width;
height = map.meta.height; height = map.height;
} }
@Override @Override
public void generate(Tile[][] tiles){ public void generate(Tile[][] tiles){
MapTileData data = MapIO.readTileData(map, true); try{
for(int x = 0; x < width; x++){
data.position(0, 0); for(int y = 0; y < height; y++){
TileDataMarker marker = data.newDataMarker(); tiles[x][y] = new Tile(x, y);
Array<Point2> players = new Array<>();
Array<Point2> enemies = new Array<>();
for(int y = 0; y < data.height(); y++){
for(int x = 0; x < data.width(); x++){
data.read(marker);
if(content.block(marker.wall) instanceof CoreBlock){
players.add(new Point2(x, y));
marker.wall = 0;
} }
if(enemySpawns != -1 && content.block(marker.wall) == Blocks.spawn){
enemies.add(new Point2(x, y));
marker.wall = 0;
}
tiles[x][y] = new Tile(x, y, marker.floor, marker.wall == Blocks.part.id ? 0 : marker.wall, marker.rotation, marker.team);
} }
}
Simplex simplex = new Simplex(Mathf.random(99999)); MapIO.readTiles(map, tiles);
Array<Point2> players = new Array<>();
Array<Point2> enemies = new Array<>();
for(int x = 0; x < data.width(); x++){ for(int x = 0; x < width; x++){
for(int y = 0; y < data.height(); y++){ for(int y = 0; y < height; y++){
final double scl = 10; if(tiles[x][y].block() instanceof CoreBlock){
Tile tile = tiles[x][y]; players.add(new Point2(x, y));
int newX = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x, y) * distortion + x), 0, data.width()-1); tiles[x][y].setBlock(Blocks.air);
int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, data.height()-1); }
if((tile.block() instanceof StaticWall if(tiles[x][y].block() == Blocks.spawn){
enemies.add(new Point2(x, y));
tiles[x][y].setBlock(Blocks.air);
}
}
}
Simplex simplex = new Simplex(Mathf.random(99999));
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
final double scl = 10;
Tile tile = tiles[x][y];
int newX = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x, y) * distortion + x), 0, width - 1);
int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, height - 1);
if((tile.block() instanceof StaticWall
&& tiles[newX][newY].block() instanceof StaticWall) && tiles[newX][newY].block() instanceof StaticWall)
|| (tile.block() == Blocks.air && !tiles[newX][newY].block().synthetic()) || (tile.block() == Blocks.air && !tiles[newX][newY].block().synthetic())
|| (tiles[newX][newY].block() == Blocks.air && tile.block() instanceof StaticWall)){ || (tiles[newX][newY].block() == Blocks.air && tile.block() instanceof StaticWall)){
tile.setBlock(tiles[newX][newY].block()); tile.setBlock(tiles[newX][newY].block());
}
if(distortFloor){
tile.setFloor(tiles[newX][newY].floor());
}
for(Decoration decor : decorations){
if(tile.block() == Blocks.air && !(decor.wall instanceof Floor) && tile.floor() == decor.floor && Mathf.chance(decor.chance)){
tile.setBlock(decor.wall);
}else if(tile.floor() == decor.floor && decor.wall instanceof Floor && Mathf.chance(decor.chance)){
tile.setFloor((Floor)decor.wall);
} }
}
if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock)){ if(distortFloor){
for(ItemStack stack : storageDrops){ tile.setFloor(tiles[newX][newY].floor());
if(Mathf.chance(0.3)){ }
tile.entity.items.add(stack.item, Math.min(Mathf.random(stack.amount), tile.block().itemCapacity));
for(Decoration decor : decorations){
if(tile.block() == Blocks.air && !(decor.wall instanceof Floor) && tile.floor() == decor.floor && Mathf.chance(decor.chance)){
tile.setBlock(decor.wall);
}else if(tile.floor() == decor.floor && decor.wall instanceof Floor && Mathf.chance(decor.chance)){
tile.setFloor((Floor)decor.wall);
} }
} }
}
}
}
if(enemySpawns != -1){ if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock)){
if(enemySpawns > enemies.size){ for(ItemStack stack : storageDrops){
throw new IllegalArgumentException("Enemy spawn pool greater than map spawn number."); if(Mathf.chance(0.3)){
} tile.entity.items.add(stack.item, Math.min(Mathf.random(stack.amount), tile.block().itemCapacity));
enemies.shuffle();
for(int i = 0; i < enemySpawns; i++){
Point2 point = enemies.get(i);
tiles[point.x][point.y].setBlock(Blocks.spawn);
int rad = 10, frad = 12;
for(int x = -rad; x <= rad; x++){
for(int y = -rad; y <= rad; y++){
int wx = x + point.x, wy = y + point.y;
double dst = Mathf.dst(x, y);
if(dst < frad && Structs.inBounds(wx, wy, tiles) && (dst <= rad || Mathf.chance(0.5))){
Tile tile = tiles[wx][wy];
if(tile.floor() instanceof OreBlock){
OreBlock block = (OreBlock)tile.floor();
tile.setFloor(block.base);
} }
} }
} }
} }
} }
if(enemySpawns != -1){
if(enemySpawns > enemies.size){
throw new IllegalArgumentException("Enemy spawn pool greater than map spawn number.");
}
enemies.shuffle();
for(int i = 0; i < enemySpawns; i++){
Point2 point = enemies.get(i);
tiles[point.x][point.y].setBlock(Blocks.spawn);
int rad = 10, frad = 12;
for(int x = -rad; x <= rad; x++){
for(int y = -rad; y <= rad; y++){
int wx = x + point.x, wy = y + point.y;
double dst = Mathf.dst(x, y);
if(dst < frad && Structs.inBounds(wx, wy, tiles) && (dst <= rad || Mathf.chance(0.5))){
Tile tile = tiles[wx][wy];
tile.clearOre();
}
}
}
}
}
Point2 core = players.random();
if(core == null){
throw new IllegalArgumentException("All zone maps must have a core.");
}
loadout.setup(core.x, core.y);
world.prepareTiles(tiles);
world.setMap(map);
}catch(IOException e){
throw new RuntimeException(e);
} }
Point2 core = players.random();
if(core == null){
throw new IllegalArgumentException("All zone maps must have a core.");
}
loadout.setup(core.x, core.y);
world.prepareTiles(tiles);
world.setMap(map);
} }
public static class Decoration{ public static class Decoration{
@@ -27,10 +27,10 @@ public class NetworkIO{
try(DataOutputStream stream = new DataOutputStream(os)){ try(DataOutputStream stream = new DataOutputStream(os)){
//--GENERAL STATE-- //--GENERAL STATE--
Serialization.writeRules(stream, state.rules); Serialization.writeRules(stream, state.rules);
stream.writeUTF(world.getMap().name); //map name stream.writeUTF(world.getMap().name()); //map name
//write tags //write tags
ObjectMap<String, String> tags = world.getMap().meta.tags; ObjectMap<String, String> tags = world.getMap().tags;
stream.writeByte(tags.size); stream.writeByte(tags.size);
for(Entry<String, String> entry : tags.entries()){ for(Entry<String, String> entry : tags.entries()){
stream.writeUTF(entry.key); stream.writeUTF(entry.key);
@@ -105,7 +105,7 @@ public class NetworkIO{
//map //map
world.spawner.read(stream); world.spawner.read(stream);
SaveIO.getSaveWriter().readMap(stream); SaveIO.getSaveWriter().readMap(stream);
world.setMap(new Map(map, 0, 0)); world.setMap(new Map(customMapDirectory.child(map), 0, 0, new ObjectMap<>(), true));
state.teams = new Teams(); state.teams = new Teams();
@@ -141,7 +141,7 @@ public class NetworkIO{
int maxlen = 32; int maxlen = 32;
String host = (headless ? "Server" : players[0].name); String host = (headless ? "Server" : players[0].name);
String map = world.getMap() == null ? "None" : world.getMap().name; String map = world.getMap() == null ? "None" : world.getMap().name();
host = host.substring(0, Math.min(host.length(), maxlen)); host = host.substring(0, Math.min(host.length(), maxlen));
map = map.substring(0, Math.min(map.length(), maxlen)); map = map.substring(0, Math.min(map.length(), maxlen));
+5 -4
View File
@@ -5,9 +5,12 @@ import io.anuke.arc.collection.Array;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.TextureRegion; import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.scene.ui.layout.Table; import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.UnlockableContent; import io.anuke.mindustry.game.UnlockableContent;
import io.anuke.mindustry.ui.ContentDisplay; import io.anuke.mindustry.ui.ContentDisplay;
import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.OreBlock;
import static io.anuke.mindustry.Vars.content;
public class Item extends UnlockableContent implements Comparable<Item>{ public class Item extends UnlockableContent implements Comparable<Item>{
public final Color color; public final Color color;
@@ -28,8 +31,6 @@ public class Item extends UnlockableContent implements Comparable<Item>{
* 1 cost = 1 tick added to build time * 1 cost = 1 tick added to build time
*/ */
public float cost = 3f; public float cost = 3f;
/**Whether this item has ores generated for it.*/
public boolean genOre = false;
/**If true, item is always unlocked.*/ /**If true, item is always unlocked.*/
public boolean alwaysUnlocked = false; public boolean alwaysUnlocked = false;
@@ -101,6 +102,6 @@ public class Item extends UnlockableContent implements Comparable<Item>{
/**Allocates a new array containing all items the generate ores.*/ /**Allocates a new array containing all items the generate ores.*/
public static Array<Item> getAllOres(){ public static Array<Item> getAllOres(){
return Vars.content.items().select(i -> i.genOre); return content.blocks().select(b -> b instanceof OreBlock).map(b -> ((Floor)b).itemDrop);
} }
} }
@@ -3,13 +3,10 @@ package io.anuke.mindustry.type;
import io.anuke.arc.collection.Array; import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.IntMap; import io.anuke.arc.collection.IntMap;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.game.Content; import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Pos; import io.anuke.mindustry.world.Pos;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.OreBlock;
import io.anuke.mindustry.world.blocks.storage.CoreBlock; import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import static io.anuke.mindustry.Vars.defaultTeam; import static io.anuke.mindustry.Vars.defaultTeam;
@@ -27,7 +24,7 @@ public class Loadout extends Content{
put('2', new BlockEntry(Blocks.coreFoundation)); put('2', new BlockEntry(Blocks.coreFoundation));
put('3', new BlockEntry(Blocks.coreNucleus)); put('3', new BlockEntry(Blocks.coreNucleus));
put('C', new BlockEntry(Blocks.mechanicalDrill, Items.copper)); put('C', new BlockEntry(Blocks.mechanicalDrill, Blocks.oreCopper));
}}; }};
private final IntMap<BlockEntry> blocks = new IntMap<>(); private final IntMap<BlockEntry> blocks = new IntMap<>();
@@ -79,8 +76,7 @@ public class Loadout extends Content{
tile.setRotation((byte)entry.value.rotation); tile.setRotation((byte)entry.value.rotation);
if(entry.value.ore != null){ if(entry.value.ore != null){
for(Tile t : tile.getLinkedTiles(outArray)){ for(Tile t : tile.getLinkedTiles(outArray)){
Floor floor = t.floor(); t.setOre(entry.value.ore);
t.setFloor(OreBlock.get(floor, entry.value.ore) == null ? OreBlock.get(Blocks.stone, entry.value.ore) : OreBlock.get(floor, entry.value.ore));
} }
} }
} }
@@ -93,10 +89,10 @@ public class Loadout extends Content{
static class BlockEntry{ static class BlockEntry{
final Block block; final Block block;
final Item ore; final Block ore;
final int rotation; final int rotation;
BlockEntry(Block block, Item ore){ BlockEntry(Block block, Block ore){
this.block = block; this.block = block;
this.ore = ore; this.ore = ore;
this.rotation = 0; this.rotation = 0;
@@ -106,7 +106,7 @@ public class CustomGameDialog extends FloatingDialog{
image.row(); image.row();
image.add("[accent]" + map.getDisplayName()).pad(3f).growX().wrap().get().setAlignment(Align.center, Align.center); image.add("[accent]" + map.getDisplayName()).pad(3f).growX().wrap().get().setAlignment(Align.center, Align.center);
image.row(); image.row();
image.label((() -> Core.bundle.format("level.highscore", Core.settings.getInt("hiscore" + map.name, 0)))).pad(3f); image.label((() -> Core.bundle.format("level.highscore", Core.settings.getInt("hiscore" + map.fileName(), 0)))).pad(3f);
BorderImage border = new BorderImage(map.texture, 3f); BorderImage border = new BorderImage(map.texture, 3f);
border.setScaling(Scaling.fit); border.setScaling(Scaling.fit);
@@ -113,7 +113,7 @@ public class LoadDialog extends FloatingDialog{
button.defaults().padBottom(3); button.defaults().padBottom(3);
button.row(); button.row();
button.add(Core.bundle.format("save.map", color + (slot.getMap() == null ? Core.bundle.get("unknown") : slot.getMap().meta.name()))); button.add(Core.bundle.format("save.map", color + (slot.getMap() == null ? Core.bundle.get("unknown") : slot.getMap().getDisplayName())));
button.row(); button.row();
button.add(Core.bundle.format("save.wave", color + slot.getWave())); button.add(Core.bundle.format("save.wave", color + slot.getWave()));
button.row(); button.row();
@@ -15,12 +15,8 @@ import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform; import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.MapIO; import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.maps.MapMeta;
import io.anuke.mindustry.maps.MapTileData;
import io.anuke.mindustry.ui.BorderImage; import io.anuke.mindustry.ui.BorderImage;
import java.io.DataInputStream;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class MapsDialog extends FloatingDialog{ public class MapsDialog extends FloatingDialog{
@@ -33,22 +29,25 @@ public class MapsDialog extends FloatingDialog{
buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> { buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> {
Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> { Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> {
try{ try{
DataInputStream stream = new DataInputStream(file.read()); Map map = MapIO.readMap(file, true);
MapMeta meta = MapIO.readMapMeta(stream); String name = map.tags.get("name", file.nameWithoutExtension());
MapTileData data = MapIO.readTileData(stream, meta, true);
stream.close();
String name = meta.tags.get("name", file.nameWithoutExtension()); Map conflict = world.maps.all().find(m -> m.fileName().equals(file.nameWithoutExtension()) || m.name().equals(file.name()));
if(world.maps.getByName(name) != null && !world.maps.getByName(name).custom){ if(conflict != null && !conflict.custom){
ui.showError(Core.bundle.format("editor.import.exists", name)); ui.showError(Core.bundle.format("editor.import.exists", name));
}else if(world.maps.getByName(name) != null){ }else if(conflict != null){
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> { ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
world.maps.saveMap(name, data, meta.tags); try{
setup(); world.maps.importMap(file, map);
setup();
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false)));
Log.err(e);
}
}); });
}else{ }else{
world.maps.saveMap(name, data, meta.tags); world.maps.importMap(file, map);
setup(); setup();
} }
@@ -89,7 +88,7 @@ public class MapsDialog extends FloatingDialog{
TextButton button = maps.addButton("", "clear", () -> showMapInfo(map)).width(mapsize).pad(8).get(); TextButton button = maps.addButton("", "clear", () -> showMapInfo(map)).width(mapsize).pad(8).get();
button.clearChildren(); button.clearChildren();
button.margin(9); button.margin(9);
button.add(map.meta.tags.get("name", map.name)).width(mapsize - 18f).center().get().setEllipsis(true); button.add(map.getDisplayName()).width(mapsize - 18f).center().get().setEllipsis(true);
button.row(); button.row();
button.addImage("white").growX().pad(4).color(Color.GRAY); button.addImage("white").growX().pad(4).color(Color.GRAY);
button.row(); button.row();
@@ -129,15 +128,15 @@ public class MapsDialog extends FloatingDialog{
t.add("$editor.name").padRight(10).color(Color.GRAY).padTop(0); t.add("$editor.name").padRight(10).color(Color.GRAY).padTop(0);
t.row(); t.row();
t.add(map.meta.tags.get("name", map.name)).growX().wrap().padTop(2); t.add(map.getDisplayName()).growX().wrap().padTop(2);
t.row(); t.row();
t.add("$editor.author").padRight(10).color(Color.GRAY); t.add("$editor.author").padRight(10).color(Color.GRAY);
t.row(); t.row();
t.add(map.meta.author()).growX().wrap().padTop(2); t.add(map.author()).growX().wrap().padTop(2);
t.row(); t.row();
t.add("$editor.description").padRight(10).color(Color.GRAY).top(); t.add("$editor.description").padRight(10).color(Color.GRAY).top();
t.row(); t.row();
t.add(map.meta.description()).growX().wrap().padTop(2); t.add(map.description()).growX().wrap().padTop(2);
t.row(); t.row();
t.add("$editor.oregen.info").padRight(10).color(Color.GRAY); t.add("$editor.oregen.info").padRight(10).color(Color.GRAY);
}).height(mapsize).width(mapsize); }).height(mapsize).width(mapsize);
@@ -146,7 +145,7 @@ public class MapsDialog extends FloatingDialog{
table.addImageTextButton("$editor.openin", "icon-load-map", 16 * 2, () -> { table.addImageTextButton("$editor.openin", "icon-load-map", 16 * 2, () -> {
try{ try{
Vars.ui.editor.beginEditMap(map.stream.get()); Vars.ui.editor.beginEditMap(map.file);
dialog.hide(); dialog.hide();
hide(); hide();
}catch(Exception e){ }catch(Exception e){
@@ -156,7 +155,7 @@ public class MapsDialog extends FloatingDialog{
}).fillX().height(54f).marginLeft(10); }).fillX().height(54f).marginLeft(10);
table.addImageTextButton("$delete", "icon-trash-16", 16 * 2, () -> { table.addImageTextButton("$delete", "icon-trash-16", 16 * 2, () -> {
ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name), () -> { ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> {
world.maps.removeMap(map); world.maps.removeMap(map);
dialog.hide(); dialog.hide();
setup(); setup();
@@ -13,6 +13,7 @@ import io.anuke.arc.scene.ui.ButtonGroup;
import io.anuke.arc.scene.ui.Image; import io.anuke.arc.scene.ui.Image;
import io.anuke.arc.scene.ui.ImageButton; import io.anuke.arc.scene.ui.ImageButton;
import io.anuke.arc.scene.ui.layout.Table; import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.type.TileEntity; import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.game.EventType.UnlockEvent; import io.anuke.mindustry.game.EventType.UnlockEvent;
@@ -26,7 +27,6 @@ import io.anuke.mindustry.type.ItemStack;
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.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.OreBlock;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -340,6 +340,6 @@ public class PlacementFragment extends Fragment{
/** Returns the block currently being hovered over in the world. */ /** Returns the block currently being hovered over in the world. */
Block tileDisplayBlock(){ Block tileDisplayBlock(){
return hoverTile == null ? null : hoverTile.block().synthetic() ? hoverTile.block() : hoverTile.floor() instanceof OreBlock ? hoverTile.floor() : null; return hoverTile == null ? null : hoverTile.block().synthetic() ? hoverTile.block() : hoverTile.oreBlock() != Blocks.air ? hoverTile.oreBlock() : null;
} }
} }
@@ -0,0 +1,51 @@
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
public Team getTeam(){
return Team.all[getTeamID()];
}
@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);
}
}
}
@@ -3,10 +3,8 @@ package io.anuke.mindustry.world;
import io.anuke.arc.collection.IntMap; import io.anuke.arc.collection.IntMap;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Color;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.game.ContentList; import io.anuke.mindustry.game.ContentList;
import io.anuke.mindustry.world.blocks.Floor; import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.blocks.OreBlock;
public class LegacyColorMapper implements ContentList{ public class LegacyColorMapper implements ContentList{
private static IntMap<LegacyBlock> blockMap = new IntMap<>(); private static IntMap<LegacyBlock> blockMap = new IntMap<>();
@@ -38,10 +36,14 @@ public class LegacyColorMapper implements ContentList{
map("6e501e", Blocks.stainedStoneRed); map("6e501e", Blocks.stainedStoneRed);
map("ed5334", Blocks.stainedStoneRed); map("ed5334", Blocks.stainedStoneRed);
map("292929", Blocks.tar); map("292929", Blocks.tar);
map("c3a490", OreBlock.get(Blocks.stone, Items.copper)); map("c3a490", Blocks.stone, Blocks.air, Blocks.oreCopper);
map("161616", OreBlock.get(Blocks.stone, Items.coal)); map("161616", Blocks.stone, Blocks.air, Blocks.oreCoal);
map("6277bc", OreBlock.get(Blocks.stone, Items.titanium)); map("6277bc", Blocks.stone, Blocks.air, Blocks.oreTitanium);
map("83bc58", OreBlock.get(Blocks.stone, Items.thorium)); map("83bc58", Blocks.stone, Blocks.air, Blocks.oreThorium);
}
private void map(String color, Block block, Block wall, Block ore){
blockMap.put(Color.rgba8888(Color.valueOf(color)), new LegacyBlock(block, wall, ore));
} }
private void map(String color, Block block, Block wall){ private void map(String color, Block block, Block wall){
@@ -55,10 +57,18 @@ public class LegacyColorMapper implements ContentList{
public static class LegacyBlock{ public static class LegacyBlock{
public final Floor floor; public final Floor floor;
public final Block wall; public final Block wall;
public final Block ore;
public LegacyBlock(Block floor, Block wall){ public LegacyBlock(Block floor, Block wall){
this.floor = (Floor) floor; this.floor = (Floor) floor;
this.wall = wall; this.wall = wall;
this.ore = null;
}
public LegacyBlock(Block floor, Block wall, Block ore){
this.floor = (Floor) floor;
this.wall = wall;
this.ore = ore;
} }
} }
+40 -3
View File
@@ -9,9 +9,10 @@ import io.anuke.arc.math.geom.Position;
import io.anuke.arc.math.geom.Vector2; import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.util.Pack; import io.anuke.arc.util.Pack;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.entities.traits.TargetTrait; import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.blocks.BlockPart; import io.anuke.mindustry.world.blocks.BlockPart;
import io.anuke.mindustry.world.blocks.Floor; import io.anuke.mindustry.world.blocks.Floor;
import io.anuke.mindustry.world.modules.ConsumeModule; import io.anuke.mindustry.world.modules.ConsumeModule;
@@ -39,10 +40,13 @@ public class Tile implements Position, TargetTrait{
private byte rotation; private byte rotation;
/** Team ordinal. */ /** Team ordinal. */
private byte team; private byte team;
/**Ore that is on top of this (floor) block.*/
private byte ore = 0;
public Tile(int x, int y){ public Tile(int x, int y){
this.x = (short) x; this.x = (short) x;
this.y = (short) y; this.y = (short) y;
wall = floor = (Floor)Blocks.air;
} }
public Tile(int x, int y, byte floor, byte wall){ public Tile(int x, int y, byte floor, byte wall){
@@ -159,6 +163,7 @@ public class Tile implements Position, TargetTrait{
public void setFloor(Floor type){ public void setFloor(Floor type){
this.floor = type; this.floor = type;
this.ore = 0;
} }
public byte getRotation(){ public byte getRotation(){
@@ -213,6 +218,14 @@ public class Tile implements Position, TargetTrait{
return link != 0; return link != 0;
} }
public byte getLinkByte(){
return link;
}
public void setLinkByte(byte b){
this.link = b;
}
/** Sets this to a linked tile, which sets the block to a part. dx and dy can only be -8-7. */ /** Sets this to a linked tile, which sets the block to a part. dx and dy can only be -8-7. */
public void setLinked(byte dx, byte dy){ public void setLinked(byte dx, byte dy){
setBlock(Blocks.part); setBlock(Blocks.part);
@@ -316,6 +329,30 @@ public class Tile implements Position, TargetTrait{
return getTeam() == Team.none || team == getTeam(); return getTeam() == Team.none || team == getTeam();
} }
public byte getOreByte(){
return ore;
}
public void setOreByte(byte ore){
this.ore = ore;
}
public void setOre(Block floor){
setOreByte(floor.id);
}
public void clearOre(){
this.ore = 0;
}
public Floor oreBlock(){
return (Floor)content.block(ore);
}
public Item drop(){
return ore == 0 ? floor.itemDrop : ((Floor)content.block(ore)).itemDrop;
}
public void updateOcclusion(){ public void updateOcclusion(){
cost = 1; cost = 1;
boolean occluded = false; boolean occluded = false;
@@ -343,7 +380,7 @@ public class Tile implements Position, TargetTrait{
} }
} }
private void preChanged(){ protected void preChanged(){
block().removed(this); block().removed(this);
if(entity != null){ if(entity != null){
entity.removeFromProximity(); entity.removeFromProximity();
@@ -351,7 +388,7 @@ public class Tile implements Position, TargetTrait{
team = 0; team = 0;
} }
private void changed(){ protected void changed(){
if(entity != null){ if(entity != null){
entity.remove(); entity.remove();
entity = null; entity = null;
@@ -7,6 +7,7 @@ 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.Geometry;
import io.anuke.arc.math.geom.Point2; import io.anuke.arc.math.geom.Point2;
import io.anuke.mindustry.content.Blocks;
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.entities.Effects.Effect; import io.anuke.mindustry.entities.Effects.Effect;
@@ -45,8 +46,6 @@ public class Floor extends Block{
public Liquid liquidDrop = null; public Liquid liquidDrop = null;
/** item that drops from this block, used for drills */ /** item that drops from this block, used for drills */
public Item itemDrop = null; public Item itemDrop = null;
/** Whether ores generate on this block. */
public boolean hasOres = false;
/** whether this block can be drowned in */ /** whether this block can be drowned in */
public boolean isLiquid; public boolean isLiquid;
/** Heat of this block, 0 at baseline. Used for calculating output of thermal generators.*/ /** Heat of this block, 0 at baseline. Used for calculating output of thermal generators.*/
@@ -110,6 +109,10 @@ public class Floor extends Block{
Mathf.random.setSeed(tile.pos()); Mathf.random.setSeed(tile.pos());
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy()); Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
Floor floor = tile.oreBlock();
if(floor != Blocks.air){
floor.draw(tile);
}
drawEdges(tile); drawEdges(tile);
} }
@@ -1,28 +1,18 @@
package io.anuke.mindustry.world.blocks; package io.anuke.mindustry.world.blocks;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.graphics.g2d.Draw; import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
public class OreBlock extends Floor{ public class OreBlock extends Floor{
private static final ObjectMap<Item, ObjectMap<Block, Floor>> oreBlockMap = new ObjectMap<>();
public Floor base; public OreBlock(Item ore){
super("ore-" + ore.name);
public OreBlock(Item ore, Floor base){ this.localizedName = ore.localizedName();
super("ore-" + ore.name + "-" + base.name);
this.localizedName = ore.localizedName() + " " + base.localizedName;
this.itemDrop = ore; this.itemDrop = ore;
this.base = base;
this.variants = 3; this.variants = 3;
this.edge = base.name;
this.blendGroup = base.blendGroup;
this.color.set(ore.color); this.color.set(ore.color);
oreBlockMap.getOr(ore, ObjectMap::new).put(base, this);
} }
@Override @Override
@@ -38,22 +28,5 @@ public class OreBlock extends Floor{
@Override @Override
public void draw(Tile tile){ public void draw(Tile tile){
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.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
public boolean doEdge(Floor floor, boolean f){
return floor != base && super.doEdge(floor, f);
}
@Override
protected boolean edgeOnto(Floor other){
return other != base;
}
public static Floor get(Block floor, Item item){
if(!oreBlockMap.containsKey(item) || !oreBlockMap.get(item).containsKey(floor)) return null;
return oreBlockMap.get(item).get(floor);
} }
} }
@@ -18,6 +18,7 @@ import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.graphics.Layer; import io.anuke.mindustry.graphics.Layer;
import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.graphics.Pal;
import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.ItemType;
import io.anuke.mindustry.ui.Bar; import io.anuke.mindustry.ui.Bar;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
@@ -182,7 +183,7 @@ public class Drill extends Block{
} }
itemArray.sort((item1, item2) -> Integer.compare(oreCount.get(item1, 0), oreCount.get(item2, 0))); itemArray.sort((item1, item2) -> Integer.compare(oreCount.get(item1, 0), oreCount.get(item2, 0)));
itemArray.sort((item1, item2) -> item1.genOre && !item2.genOre ? 1 : item1.genOre == item2.genOre ? 0 : -1); itemArray.sort((item1, item2) -> Boolean.compare(item1.type == ItemType.material, item2.type == ItemType.material));
if(itemArray.size == 0){ if(itemArray.size == 0){
return; return;
@@ -259,12 +260,12 @@ public class Drill extends Block{
} }
public Item getDrop(Tile tile){ public Item getDrop(Tile tile){
return tile.floor().itemDrop; return tile.drop();
} }
public boolean isValid(Tile tile){ public boolean isValid(Tile tile){
if(tile == null) return false; if(tile == null) return false;
Item drops = tile.floor().itemDrop; Item drops = tile.drop();
return drops != null && drops.hardness <= tier; return drops != null && drops.hardness <= tier;
} }
@@ -54,9 +54,9 @@ public class DesktopPlatform extends Platform{
if(world.getMap() == null){ if(world.getMap() == null){
presence.details = "Unknown Map"; presence.details = "Unknown Map";
}else if(!state.rules.waves){ }else if(!state.rules.waves){
presence.details = Strings.capitalize(world.getMap().name); presence.details = Strings.capitalize(world.getMap().getDisplayName());
}else{ }else{
presence.details = Strings.capitalize(world.getMap().name) + " | Wave " + state.wave; presence.details = Strings.capitalize(world.getMap().getDisplayName()) + " | Wave " + state.wave;
presence.largeImageText = "Wave " + state.wave; presence.largeImageText = "Wave " + state.wave;
} }
+1 -1
View File
@@ -113,7 +113,7 @@ public class IOSLauncher extends IOSApplication.Delegate {
ui.editor.show(); ui.editor.show();
} }
ui.editor.beginEditMap(file.read()); ui.editor.beginEditMap(file);
}); });
} }
}); });
@@ -46,7 +46,6 @@ public class ServerControl implements ApplicationListener{
private FileHandle currentLogFile; private FileHandle currentLogFile;
private boolean inExtraRound; private boolean inExtraRound;
private Task lastTask; private Task lastTask;
private RulePreset lastPreset;
public ServerControl(String[] args){ public ServerControl(String[] args){
Core.settings.defaults( Core.settings.defaults(
@@ -144,11 +143,11 @@ public class ServerControl implements ApplicationListener{
Call.onInfoMessage((state.rules.pvp Call.onInfoMessage((state.rules.pvp
? "[YELLOW]The " + event.winner.name() + " team is victorious![]" : "[SCARLET]Game over![]") ? "[YELLOW]The " + event.winner.name() + " team is victorious![]" : "[SCARLET]Game over![]")
+ "\nNext selected map:[accent] "+map.name+"[]" + "\nNext selected map:[accent] "+map.name()+"[]"
+ (map.meta.author() != null ? " by[accent] " + map.meta.author() + "[]" : "") + "."+ + (map.author() != null ? " by[accent] " + map.author() + "[]" : "") + "."+
"\nNew game begins in " + roundExtraTime + " seconds."); "\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; Map fmap = map;
@@ -199,7 +198,7 @@ public class ServerControl implements ApplicationListener{
if(lastTask != null) lastTask.cancel(); 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){ if(result == null){
err("No map with name &y'{0}'&lr found.", arg[0]); err("No map with name &y'{0}'&lr found.", arg[0]);
@@ -252,7 +251,7 @@ public class ServerControl implements ApplicationListener{
if(!world.maps.all().isEmpty()){ if(!world.maps.all().isEmpty()){
info("Maps:"); info("Maps:");
for(Map map : world.maps.all()){ 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{ }else{
info("No maps found."); info("No maps found.");
@@ -265,7 +264,7 @@ public class ServerControl implements ApplicationListener{
info("Status: &rserver closed"); info("Status: &rserver closed");
}else{ }else{
info("Status:"); 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){ if(state.rules.waves){
info("&ly {0} enemies.", unitGroups[Team.red.ordinal()].size()); info("&ly {0} enemies.", unitGroups[Team.red.ordinal()].size());
+2 -2
View File
@@ -194,8 +194,8 @@ public class ApplicationTests{
resetWorld(); resetWorld();
SaveIO.loadFromSlot(0); SaveIO.loadFromSlot(0);
assertEquals(world.width(), map.meta.width); assertEquals(world.width(), map.width);
assertEquals(world.height(), map.meta.height); assertEquals(world.height(), map.height);
assertTrue(state.teams.get(defaultTeam).cores.size > 0); assertTrue(state.teams.get(defaultTeam).cores.size > 0);
} }
+11
View File
@@ -0,0 +1,11 @@
import org.junit.jupiter.api.BeforeAll;
public class MapTests{
@BeforeAll
static void launchApplication(){
ApplicationTests.launchApplication();
}
//TODO
}
+1
View File
@@ -88,6 +88,7 @@ def antialias = {File file ->
ImageIO.write(out, "png", file) ImageIO.write(out, "png", file)
} }
task swapColors(){ task swapColors(){
doLast{ doLast{
if (project.hasProperty("colors")) { if (project.hasProperty("colors")) {
+3 -4
View File
@@ -206,11 +206,10 @@ public class Generators {
OreBlock ore = (OreBlock)block; OreBlock ore = (OreBlock)block;
Item item = ore.itemDrop; Item item = ore.itemDrop;
Block base = ore.base;
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
//get base image to draw on //get base image to draw on
Image image = ImagePacker.get(base.name + (i+1)); Image image = new Image(32, 32);
Image shadow = ImagePacker.get(item.name + (i+1)); Image shadow = ImagePacker.get(item.name + (i+1));
int offset = image.width()/tilesize; int offset = image.width()/tilesize;
@@ -228,8 +227,8 @@ public class Generators {
} }
image.draw(ImagePacker.get(item.name + (i+1))); image.draw(ImagePacker.get(item.name + (i+1)));
image.save("../blocks/environment/ore-" + item.name + "-" + base.name + (i+1)); image.save("../blocks/environment/ore-" + item.name + (i+1));
image.save("../editor/editor-ore-" + item.name + "-" + base.name + (i+1)); image.save("../editor/editor-ore-" + item.name + (i+1));
//save icons //save icons
image.save(block.name + "-icon-full"); image.save(block.name + "-icon-full");