Merge branches 'master' and 'new-map-format' of https://github.com/Anuken/Mindustry
This commit is contained in:
@@ -30,6 +30,8 @@ import java.util.Locale;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public class Vars{
|
||||
/**IO buffer size.*/
|
||||
public static final int bufferSize = 8192;
|
||||
/**global charset*/
|
||||
public static final Charset charset = Charset.forName("UTF-8");
|
||||
/**main application name, capitalized*/
|
||||
|
||||
@@ -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 y = Math.max(0, tile.y - oreQuadrantSize / 2); y < tile.y + oreQuadrantSize / 2 && y < world.height(); 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;
|
||||
}
|
||||
}
|
||||
@@ -241,9 +241,9 @@ public class BlockIndexer{
|
||||
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++){
|
||||
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);
|
||||
|
||||
//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){
|
||||
ores.get(tile.floor().itemDrop).add(world.tile(
|
||||
if(tile.drop() != null && scanOres.contains(tile.drop()) && tile.block() == Blocks.air){
|
||||
ores.get(tile.drop()).add(world.tile(
|
||||
//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(qy * oreQuadrantSize + oreQuadrantSize / 2, 0, world.height() - 1)));
|
||||
|
||||
@@ -7,7 +7,6 @@ import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.graphics.CacheLayer;
|
||||
import io.anuke.mindustry.type.Category;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.ItemStack;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
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.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 static Block
|
||||
@@ -39,6 +39,9 @@ public class Blocks implements ContentList{
|
||||
iceSnow, sandWater, duneRocks, sandRocks, stainedRocks, moss, stainedRocksRed, stainedStoneRed, stainedRocksYellow, stainedStoneYellow, stainedBoulder, grass, salt,
|
||||
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks,
|
||||
|
||||
//ores
|
||||
oreCopper, oreLead, oreScrap, oreCoal, oreTitanium, oreThorium,
|
||||
|
||||
//crafting
|
||||
siliconSmelter, kiln, graphitePress, plastaniumCompressor, multiPress, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer,
|
||||
melter, separator, sporePress, pulverizer, incinerator,
|
||||
@@ -145,7 +148,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
stone = new Floor("stone"){{
|
||||
hasOres = true;
|
||||
|
||||
}};
|
||||
|
||||
craters = new Floor("craters"){{
|
||||
@@ -170,12 +173,10 @@ public class Blocks implements ContentList{
|
||||
|
||||
sand = new Floor("sand"){{
|
||||
itemDrop = Items.sand;
|
||||
hasOres = true;
|
||||
playerUnmineable = true;
|
||||
}};
|
||||
|
||||
holostone = new Floor("holostone"){{
|
||||
hasOres = true;
|
||||
edgeStyle = "blocky";
|
||||
}};
|
||||
|
||||
@@ -226,7 +227,6 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
pine = new StaticWall("pine"){{
|
||||
//fillsTile = false;
|
||||
variants = 0;
|
||||
}};
|
||||
|
||||
@@ -253,7 +253,6 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
stainedStoneRed = new Floor("stained-stone-red"){{
|
||||
hasOres = true;
|
||||
variants = 1;
|
||||
}};
|
||||
|
||||
@@ -308,6 +307,16 @@ public class Blocks implements ContentList{
|
||||
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
|
||||
//region crafting
|
||||
|
||||
@@ -1418,23 +1427,6 @@ public class Blocks implements ContentList{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ public class Items implements ContentList{
|
||||
type = ItemType.material;
|
||||
hardness = 1;
|
||||
cost = 0.6f;
|
||||
genOre = true;
|
||||
alwaysUnlocked = true;
|
||||
}};
|
||||
|
||||
@@ -23,7 +22,6 @@ public class Items implements ContentList{
|
||||
type = ItemType.material;
|
||||
hardness = 1;
|
||||
cost = 0.9f;
|
||||
genOre = true;
|
||||
}};
|
||||
|
||||
metaglass = new Item("metaglass", Color.valueOf("ebeef5")){{
|
||||
@@ -40,14 +38,12 @@ public class Items implements ContentList{
|
||||
explosiveness = 0.4f;
|
||||
flammability = 1f;
|
||||
hardness = 2;
|
||||
genOre = true;
|
||||
}};
|
||||
|
||||
titanium = new Item("titanium", Color.valueOf("8da1e3")){{
|
||||
type = ItemType.material;
|
||||
hardness = 3;
|
||||
cost = 1.1f;
|
||||
genOre = true;
|
||||
}};
|
||||
|
||||
thorium = new Item("thorium", Color.valueOf("f9a3c7")){{
|
||||
@@ -56,11 +52,10 @@ public class Items implements ContentList{
|
||||
hardness = 4;
|
||||
radioactivity = 1f;
|
||||
cost = 1.4f;
|
||||
genOre = true;
|
||||
}};
|
||||
|
||||
scrap = new Item("scrap", Color.valueOf("777777")){{
|
||||
genOre = true;
|
||||
|
||||
}};
|
||||
|
||||
silicon = new Item("silicon", Color.valueOf("53565c")){{
|
||||
|
||||
@@ -23,8 +23,6 @@ import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.io.MapIO;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
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.generators.Generator;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
@@ -235,12 +233,15 @@ public class World implements ApplicationListener{
|
||||
beginMapLoad();
|
||||
this.currentMap = map;
|
||||
|
||||
int width = map.meta.width, height = map.meta.height;
|
||||
|
||||
createTiles(width, height);
|
||||
|
||||
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){
|
||||
Log.err(e);
|
||||
if(!headless){
|
||||
@@ -260,7 +261,7 @@ public class World implements ApplicationListener{
|
||||
if(state.teams.get(players[0].getTeam()).cores.size == 0){
|
||||
ui.showError("$map.nospawn");
|
||||
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;
|
||||
for(Team team : Team.all){
|
||||
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. */
|
||||
void loadTileData(Tile[][] tiles, MapTileData data){
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void loadTileData(Tile[][] tiles){
|
||||
prepareTiles(tiles);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +1,70 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.collection.IntSet;
|
||||
import io.anuke.arc.util.Pack;
|
||||
import io.anuke.mindustry.maps.MapTileData;
|
||||
import io.anuke.mindustry.maps.MapTileData.TileDataMarker;
|
||||
import io.anuke.annotations.Annotations.Struct;
|
||||
import io.anuke.arc.collection.LongArray;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.gen.TileOp;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
|
||||
import static io.anuke.mindustry.Vars.content;
|
||||
|
||||
public class DrawOperation{
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
private LongArray array = new LongArray();
|
||||
|
||||
public boolean isEmpty(){
|
||||
return operations.size == 0;
|
||||
return array.isEmpty();
|
||||
}
|
||||
|
||||
public boolean checkDuplicate(short x, short y){
|
||||
int i = Pack.shortInt(x, y);
|
||||
if(checks.contains(i)) return true;
|
||||
|
||||
checks.add(i);
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addOperation(TileOperation op){
|
||||
operations.add(op);
|
||||
public void addOperation(long op){
|
||||
array.add(op);
|
||||
}
|
||||
|
||||
public void undo(MapEditor editor){
|
||||
for(int i = operations.size - 1; i >= 0; i--){
|
||||
TileOperation op = operations.get(i);
|
||||
data.position(op.x, op.y);
|
||||
data.write(op.from);
|
||||
editor.renderer().updatePoint(op.x, op.y);
|
||||
for(int i = array.size - 1; i >= 0; i--){
|
||||
long l = array.get(i);
|
||||
set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.from(l));
|
||||
}
|
||||
}
|
||||
|
||||
public void redo(MapEditor editor){
|
||||
for(TileOperation op : operations){
|
||||
data.position(op.x, op.y);
|
||||
data.write(op.to);
|
||||
editor.renderer().updatePoint(op.x, op.y);
|
||||
for(int i = 0; i < array.size; i++){
|
||||
long l = array.get(i);
|
||||
set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.to(l));
|
||||
}
|
||||
}
|
||||
|
||||
public static class TileOperation{
|
||||
public short x, y;
|
||||
public TileDataMarker from;
|
||||
public TileDataMarker to;
|
||||
void set(MapEditor editor, Tile tile, byte type, byte to){
|
||||
editor.load(() -> {
|
||||
if(type == OpType.floor.ordinal()){
|
||||
tile.setFloor((Floor)content.block(to));
|
||||
}else if(type == OpType.block.ordinal()){
|
||||
tile.setBlock(content.block(to));
|
||||
}else if(type == OpType.rotation.ordinal()){
|
||||
tile.setRotation(to);
|
||||
}else if(type == OpType.team.ordinal()){
|
||||
tile.setTeam(Team.all[to]);
|
||||
}else if(type == OpType.ore.ordinal()){
|
||||
tile.setOreByte(to);
|
||||
}
|
||||
});
|
||||
editor.renderer().updatePoint(tile.x, tile.y);
|
||||
}
|
||||
|
||||
public TileOperation(short x, short y, TileDataMarker from, TileDataMarker to){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
}
|
||||
@Struct
|
||||
class TileOpStruct{
|
||||
short x;
|
||||
short y;
|
||||
byte type;
|
||||
byte from;
|
||||
byte to;
|
||||
}
|
||||
|
||||
public enum OpType{
|
||||
floor,
|
||||
block,
|
||||
rotation,
|
||||
team,
|
||||
ore
|
||||
}
|
||||
}
|
||||
|
||||
105
core/src/io/anuke/mindustry/editor/EditorTile.java
Normal file
105
core/src/io/anuke/mindustry/editor/EditorTile.java
Normal file
@@ -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.Structs;
|
||||
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.Pos;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public enum EditorTool{
|
||||
pick{
|
||||
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);
|
||||
byte bw = editor.getMap().read(x, y, DataPosition.wall);
|
||||
byte link = editor.getMap().read(x, y, DataPosition.link);
|
||||
Tile tile = editor.tile(x, y);
|
||||
|
||||
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);
|
||||
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.setDrawBlock(block);
|
||||
ui.editor.updateSelectedBlock();
|
||||
editor.drawBlock = block == Blocks.air ? floor : block;
|
||||
}
|
||||
},
|
||||
pencil{
|
||||
@@ -67,7 +64,7 @@ public enum EditorTool{
|
||||
|
||||
@Override
|
||||
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{
|
||||
@@ -81,56 +78,57 @@ public enum EditorTool{
|
||||
}
|
||||
|
||||
IntArray stack = new IntArray();
|
||||
int width;
|
||||
byte be, dest;
|
||||
boolean floor;
|
||||
MapTileData data;
|
||||
Block dest;
|
||||
boolean isfloor;
|
||||
MapEditor data;
|
||||
|
||||
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
|
||||
pencil.touched(editor, x, y);
|
||||
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);
|
||||
byte bw = data.read(x, y, DataPosition.wall);
|
||||
boolean synth = editor.getDrawBlock().synthetic();
|
||||
byte brt = Pack.byteByte((byte) editor.getDrawRotation(), (byte) editor.getDrawTeam().ordinal());
|
||||
dest = isfloor ? floor : block;
|
||||
Block draw = editor.drawBlock;
|
||||
|
||||
dest = floor ? bf : bw;
|
||||
byte draw = editor.getDrawBlock().id;
|
||||
|
||||
if(dest == draw || content.block(bw) instanceof BlockPart || content.block(bw).isMultiblock()){
|
||||
if(dest == draw || block == Blocks.part || block.isMultiblock()){
|
||||
return;
|
||||
}
|
||||
|
||||
width = editor.getMap().width();
|
||||
int height = editor.getMap().height();
|
||||
int width = editor.width();
|
||||
int height = editor.height();
|
||||
|
||||
IntPositionConsumer writer = (px, py) -> {
|
||||
TileDataMarker prev = editor.getPrev(px, py, false);
|
||||
Tile write = editor.tile(px, py);
|
||||
|
||||
if(floor){
|
||||
data.write(px, py, DataPosition.floor, draw);
|
||||
if(isfloor){
|
||||
write.setFloor((Floor)draw);
|
||||
}else{
|
||||
data.write(px, py, DataPosition.wall, draw);
|
||||
write.setBlock(draw);
|
||||
}
|
||||
|
||||
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()){
|
||||
//fill all of the same type regardless of borders
|
||||
for(int cx = 0; cx < width; cx++){
|
||||
for(int cy = 0; cy < height; cy++){
|
||||
if(eq(cx, cy)){
|
||||
@@ -139,28 +137,28 @@ public enum EditorTool{
|
||||
}
|
||||
}
|
||||
}else if(isAlt2()){
|
||||
//fill all teams.
|
||||
for(int cx = 0; cx < width; cx++){
|
||||
for(int cy = 0; cy < height; cy++){
|
||||
byte w = data.read(cx, cy, DataPosition.wall);
|
||||
if(content.block(w).synthetic()){
|
||||
TileDataMarker prev = editor.getPrev(cx, cy, false);
|
||||
data.write(cx, cy, DataPosition.rotationTeam, (byte)editor.getDrawTeam().ordinal());
|
||||
editor.onWrite(cx, cy, prev);
|
||||
Tile write = editor.tile(cx, cy);
|
||||
if(write.block().synthetic()){
|
||||
write.setTeam(editor.drawTeam);
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//normal fill
|
||||
int x1;
|
||||
boolean spanAbove, spanBelow;
|
||||
|
||||
stack.clear();
|
||||
|
||||
stack.add(asi(x, y));
|
||||
stack.add(Pos.get(x, y));
|
||||
|
||||
while(stack.size > 0){
|
||||
int popped = stack.pop();
|
||||
x = popped % width;
|
||||
y = popped / width;
|
||||
x = Pos.x(popped);
|
||||
y = Pos.y(popped);
|
||||
|
||||
x1 = x;
|
||||
while(x1 >= 0 && eq(x1, y)) x1--;
|
||||
@@ -170,14 +168,14 @@ public enum EditorTool{
|
||||
writer.accept(x1, y);
|
||||
|
||||
if(!spanAbove && y > 0 && eq(x1, y - 1)){
|
||||
stack.add(asi(x1, y - 1));
|
||||
stack.add(Pos.get(x1, y - 1));
|
||||
spanAbove = true;
|
||||
}else if(spanAbove && y > 0 && eq(x1, y - 1)){
|
||||
}else if(spanAbove && eq(x1, y - 1)){
|
||||
spanAbove = false;
|
||||
}
|
||||
|
||||
if(!spanBelow && y < height - 1 && eq(x1, y + 1)){
|
||||
stack.add(asi(x1, y + 1));
|
||||
stack.add(Pos.get(x1, y + 1));
|
||||
spanBelow = true;
|
||||
}else if(spanBelow && y < height - 1 && eq(x1, y + 1)){
|
||||
spanBelow = false;
|
||||
@@ -189,15 +187,9 @@ public enum EditorTool{
|
||||
}
|
||||
|
||||
boolean eq(int px, int py){
|
||||
byte nbf = data.read(px, py, DataPosition.floor);
|
||||
byte nbw = data.read(px, py, DataPosition.wall);
|
||||
byte nbe = data.read(px, py, DataPosition.elevation);
|
||||
Tile tile = data.tile(px, py);
|
||||
|
||||
return (floor ? nbf : nbw) == dest && nbe == be;
|
||||
}
|
||||
|
||||
int asi(int x, int y){
|
||||
return x + y * width;
|
||||
return (isfloor ? tile.floor() : tile.block()) == dest;
|
||||
}
|
||||
},
|
||||
zoom;
|
||||
|
||||
@@ -1,88 +1,150 @@
|
||||
package io.anuke.mindustry.editor;
|
||||
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Log;
|
||||
import io.anuke.arc.util.Pack;
|
||||
import io.anuke.arc.util.Structs;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.editor.DrawOperation.TileOperation;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.maps.MapTileData;
|
||||
import io.anuke.mindustry.maps.MapTileData.DataPosition;
|
||||
import io.anuke.mindustry.maps.MapTileData.TileDataMarker;
|
||||
import io.anuke.mindustry.gen.TileOp;
|
||||
import io.anuke.mindustry.io.MapIO;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
|
||||
import static io.anuke.mindustry.Vars.content;
|
||||
import java.io.IOException;
|
||||
|
||||
public class MapEditor{
|
||||
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20};
|
||||
|
||||
private MapTileData map;
|
||||
private ObjectMap<String, String> tags = new ObjectMap<>();
|
||||
private MapRenderer renderer = new MapRenderer(this);
|
||||
private Tile[][] tiles;
|
||||
|
||||
private int brushSize = 1;
|
||||
private int rotation;
|
||||
private Block drawBlock = Blocks.stone;
|
||||
private Team drawTeam = Team.blue;
|
||||
private OperationStack stack = new OperationStack();
|
||||
private DrawOperation currentOp;
|
||||
private boolean loading;
|
||||
|
||||
public MapTileData getMap(){
|
||||
return map;
|
||||
}
|
||||
public int brushSize = 1;
|
||||
public int rotation;
|
||||
public Block drawBlock = Blocks.stone;
|
||||
public Team drawTeam = Team.blue;
|
||||
|
||||
public ObjectMap<String, String> getTags(){
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void beginEdit(MapTileData map, ObjectMap<String, String> tags, boolean clear){
|
||||
this.map = map;
|
||||
this.brushSize = 1;
|
||||
this.tags = tags;
|
||||
public void beginEdit(int width, int height){
|
||||
reset();
|
||||
|
||||
if(clear){
|
||||
for(int x = 0; x < map.width(); x++){
|
||||
for(int y = 0; y < map.height(); y++){
|
||||
map.write(x, y, DataPosition.floor, Blocks.stone.id);
|
||||
loading = true;
|
||||
tiles = createTiles(width, height);
|
||||
renderer.resize(width(), height());
|
||||
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;
|
||||
renderer.resize(map.width(), map.height());
|
||||
tags = new ObjectMap<>();
|
||||
}
|
||||
|
||||
public int getDrawRotation(){
|
||||
return rotation;
|
||||
public Tile[][] tiles(){
|
||||
return tiles;
|
||||
}
|
||||
|
||||
public void setDrawRotation(int rotation){
|
||||
this.rotation = rotation;
|
||||
public Tile tile(int x, int y){
|
||||
return tiles[x][y];
|
||||
}
|
||||
|
||||
public Team getDrawTeam(){
|
||||
return drawTeam;
|
||||
public int width(){
|
||||
return tiles.length;
|
||||
}
|
||||
|
||||
public void setDrawTeam(Team team){
|
||||
this.drawTeam = team;
|
||||
}
|
||||
|
||||
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 int height(){
|
||||
return tiles[0].length;
|
||||
}
|
||||
|
||||
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){
|
||||
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;
|
||||
|
||||
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 offsety = -(drawBlock.size - 1) / 2;
|
||||
@@ -113,68 +172,61 @@ public class MapEditor{
|
||||
int worldx = dx + offsetx + x;
|
||||
int worldy = dy + offsety + y;
|
||||
|
||||
if(Structs.inBounds(worldx, worldy, map.width(), map.height())){
|
||||
TileDataMarker prev = getPrev(worldx, worldy, false);
|
||||
if(Structs.inBounds(worldx, worldy, width(), height())){
|
||||
Tile tile = tiles[worldx][worldy];
|
||||
|
||||
if(i == 1){
|
||||
map.write(worldx, worldy, DataPosition.wall, partID);
|
||||
map.write(worldx, worldy, DataPosition.rotationTeam, rotationTeam);
|
||||
map.write(worldx, worldy, DataPosition.link, Pack.byteByte((byte) (dx + offsetx + 8), (byte) (dy + offsety + 8)));
|
||||
tile.setBlock(Blocks.part);
|
||||
tile.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
||||
}else{
|
||||
byte link = map.read(worldx, worldy, DataPosition.link);
|
||||
byte block = map.read(worldx, worldy, DataPosition.wall);
|
||||
byte link = tile.getLinkByte();
|
||||
Block block = tile.block();
|
||||
|
||||
if(link != 0){
|
||||
removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8));
|
||||
}else if(content.block(block).isMultiblock()){
|
||||
}else if(block.isMultiblock()){
|
||||
removeLinked(worldx, worldy);
|
||||
}
|
||||
}
|
||||
|
||||
onWrite(worldx, worldy, prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TileDataMarker prev = getPrev(x, y, false);
|
||||
|
||||
map.write(x, y, DataPosition.wall, writeID);
|
||||
map.write(x, y, DataPosition.link, (byte) 0);
|
||||
map.write(x, y, DataPosition.rotationTeam, rotationTeam);
|
||||
|
||||
onWrite(x, y, prev);
|
||||
Tile tile = tiles[x][y];
|
||||
tile.setBlock(drawBlock);
|
||||
tile.setTeam(drawTeam);
|
||||
}else{
|
||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||
if(Mathf.dst(rx, ry) <= brushSize - 0.5f && (chance >= 0.999 || Mathf.chance(chance))){
|
||||
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;
|
||||
}
|
||||
|
||||
TileDataMarker prev = getPrev(wx, wy, true);
|
||||
Tile tile = tiles[wx][wy];
|
||||
|
||||
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);
|
||||
}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));
|
||||
}
|
||||
}
|
||||
|
||||
if(isfloor){
|
||||
map.write(wx, wy, DataPosition.floor, writeID);
|
||||
tile.setFloor((Floor)drawBlock);
|
||||
}else{
|
||||
map.write(wx, wy, DataPosition.wall, writeID);
|
||||
map.write(wx, wy, DataPosition.link, (byte) 0);
|
||||
map.write(wx, wy, DataPosition.rotationTeam, rotationTeam);
|
||||
tile.setBlock(drawBlock);
|
||||
if(drawBlock.synthetic()){
|
||||
tile.setTeam(drawTeam);
|
||||
}
|
||||
}
|
||||
|
||||
onWrite(x + rx, y + ry, prev);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -182,77 +234,86 @@ public class MapEditor{
|
||||
}
|
||||
|
||||
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 offsety = -(block.size - 1) / 2;
|
||||
for(int dx = 0; dx < block.size; dx++){
|
||||
for(int dy = 0; dy < block.size; dy++){
|
||||
int worldx = x + dx + offsetx, worldy = y + dy + offsety;
|
||||
if(Structs.inBounds(worldx, worldy, map.width(), map.height())){
|
||||
TileDataMarker prev = getPrev(worldx, worldy, false);
|
||||
|
||||
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);
|
||||
if(Structs.inBounds(worldx, worldy, width(), height())){
|
||||
tiles[worldx][worldy].setTeam(Team.none);
|
||||
tiles[worldx][worldy].setBlock(Blocks.air);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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(){
|
||||
return renderer;
|
||||
}
|
||||
|
||||
public void resize(int width, int height){
|
||||
MapTileData previous = map;
|
||||
int offsetX = -(width - previous.width())/2, offsetY = -(height - previous.height())/2;
|
||||
clearOp();
|
||||
|
||||
map = new MapTileData(width, height);
|
||||
for(int x = 0; x < map.width(); x++){
|
||||
for(int y = 0; y < map.height(); y++){
|
||||
Tile[][] previous = tiles;
|
||||
int offsetX = -(width - width())/2, offsetY = -(height - height())/2;
|
||||
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;
|
||||
if(Structs.inBounds(px, py, previous.width(), previous.height())){
|
||||
map.write(x, y, DataPosition.floor, previous.read(px, py, DataPosition.floor));
|
||||
map.write(x, y, DataPosition.wall, previous.read(px, py, DataPosition.wall));
|
||||
map.write(x, y, DataPosition.link, previous.read(px, py, DataPosition.link));
|
||||
map.write(x, y, DataPosition.rotationTeam, previous.read(px, py, DataPosition.rotationTeam));
|
||||
if(Structs.inBounds(px, py, previous.length, previous[0].length)){
|
||||
tiles[x][y] = previous[px][py];
|
||||
tiles[x][y].x = (short)x;
|
||||
tiles[x][y].y = (short)y;
|
||||
}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);
|
||||
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.collection.Array;
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.function.Consumer;
|
||||
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.io.MapIO;
|
||||
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.world.Block;
|
||||
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.storage.CoreBlock;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class MapEditorDialog extends Dialog implements Disposable{
|
||||
private MapEditor editor;
|
||||
public final MapEditor editor;
|
||||
private MapView view;
|
||||
private MapInfoDialog infoDialog;
|
||||
private MapLoadDialog loadDialog;
|
||||
@@ -48,8 +43,6 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
private boolean shownWithMap = false;
|
||||
private Array<Block> blocksOut = new Array<>();
|
||||
|
||||
private ButtonGroup<ImageButton> blockgroup;
|
||||
|
||||
public MapEditorDialog(){
|
||||
super("", "dialog");
|
||||
|
||||
@@ -89,17 +82,12 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
createDialog("$editor.import",
|
||||
"$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable) loadDialog::show,
|
||||
"$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{
|
||||
DataInputStream stream = new DataInputStream(file.read());
|
||||
|
||||
MapMeta meta = MapIO.readMapMeta(stream);
|
||||
MapTileData data = MapIO.readTileData(stream, meta, false);
|
||||
|
||||
editor.beginEdit(data, meta.tags, false);
|
||||
view.clearStack();
|
||||
//TODO what if it's an image? users should be warned for their stupidity
|
||||
editor.beginEdit(MapIO.readMap(file, true));
|
||||
}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);
|
||||
}
|
||||
}), true, mapExtension),
|
||||
@@ -108,39 +96,32 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
Platform.instance.showFileChooser("$loadimage", "Image Files", file ->
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
MapTileData data = MapIO.readLegacyPixmap(new Pixmap(file));
|
||||
|
||||
editor.beginEdit(data, editor.getTags(), false);
|
||||
view.clearStack();
|
||||
Pixmap pixmap = new Pixmap(file);
|
||||
Tile[][] tiles = editor.createTiles(pixmap.getWidth(), pixmap.getHeight());
|
||||
editor.load(() -> MapIO.readLegacyPixmap(pixmap, tiles));
|
||||
editor.beginEdit(tiles);
|
||||
}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);
|
||||
}
|
||||
}), true, "png")
|
||||
));
|
||||
}), true, "png")));
|
||||
|
||||
t.addImageTextButton("$editor.export", "icon-save-map", isize, () -> createDialog("$editor.export",
|
||||
"$editor.exportfile", "$editor.exportfile.description", "icon-file", (Runnable) () ->
|
||||
Platform.instance.showFileChooser("$saveimage", "Map Files", file -> {
|
||||
t.addImageTextButton("$editor.export", "icon-save-map", isize, () ->
|
||||
Platform.instance.showFileChooser("$editor.savemap", "Map Files", file -> {
|
||||
file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension);
|
||||
FileHandle result = file;
|
||||
ui.loadAnd(() -> {
|
||||
|
||||
try{
|
||||
if(!editor.getTags().containsKey("name")){
|
||||
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){
|
||||
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);
|
||||
}
|
||||
});
|
||||
}, false, mapExtension)));
|
||||
|
||||
t.row();
|
||||
|
||||
t.row();
|
||||
}, false, mapExtension));
|
||||
});
|
||||
|
||||
menu.cont.row();
|
||||
@@ -151,24 +132,19 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
}).padTop(-5).size(swidth * 2f + 10, 60f);
|
||||
|
||||
resizeDialog = new MapResizeDialog(editor, (x, y) -> {
|
||||
if(!(editor.getMap().width() == x && editor.getMap().height() == y)){
|
||||
if(!(editor.width() == x && editor.height() == y)){
|
||||
ui.loadAnd(() -> {
|
||||
editor.resize(x, y);
|
||||
view.clearStack();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
loadDialog = new MapLoadDialog(map ->
|
||||
ui.loadAnd(() -> {
|
||||
try(DataInputStream stream = new DataInputStream(map.stream.get())){
|
||||
MapMeta meta = MapIO.readMapMeta(stream);
|
||||
MapTileData data = MapIO.readTileData(stream, meta, false);
|
||||
|
||||
editor.beginEdit(data, meta.tags, false);
|
||||
view.clearStack();
|
||||
try{
|
||||
editor.beginEdit(map);
|
||||
}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);
|
||||
}
|
||||
}));
|
||||
@@ -200,10 +176,10 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
shown(() -> {
|
||||
saved = true;
|
||||
Platform.instance.beginForceLandscape();
|
||||
view.clearStack();
|
||||
editor.clearOp();
|
||||
Core.scene.setScrollFocus(view);
|
||||
if(!shownWithMap){
|
||||
editor.beginEdit(new MapTileData(200, 200), new ObjectMap<>(), true);
|
||||
editor.beginEdit(200, 200);
|
||||
}
|
||||
shownWithMap = false;
|
||||
|
||||
@@ -211,6 +187,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
});
|
||||
|
||||
hidden(() -> {
|
||||
editor.clearOp();
|
||||
Platform.instance.updateRPC();
|
||||
Platform.instance.endForceLandscape();
|
||||
});
|
||||
@@ -225,13 +202,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
String name = editor.getTags().get("name", "");
|
||||
|
||||
if(name.isEmpty()){
|
||||
ui.showError("$editor.save.noname");
|
||||
infoDialog.show();
|
||||
Core.app.post(() -> ui.showError("$editor.save.noname"));
|
||||
}else{
|
||||
Map map = world.maps.getByName(name);
|
||||
Map map = world.maps.all().find(m -> m.name().equals(name));
|
||||
if(map != null && !map.custom){
|
||||
ui.showError("$editor.save.overwrite");
|
||||
}else{
|
||||
world.maps.saveMap(name, editor.getMap(), editor.getTags());
|
||||
world.maps.saveMap(editor.getTags(), editor.tiles());
|
||||
ui.showInfoFade("$editor.saved");
|
||||
}
|
||||
}
|
||||
@@ -293,18 +271,16 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
editor.renderer().dispose();
|
||||
}
|
||||
|
||||
public void beginEditMap(InputStream is){
|
||||
public void beginEditMap(FileHandle file){
|
||||
ui.loadAnd(() -> {
|
||||
try{
|
||||
Map map = MapIO.readMap(file, true);
|
||||
shownWithMap = true;
|
||||
DataInputStream stream = new DataInputStream(is);
|
||||
MapMeta meta = MapIO.readMapMeta(stream);
|
||||
editor.beginEdit(MapIO.readTileData(stream, meta, false), meta.tags, false);
|
||||
is.close();
|
||||
editor.beginEdit(map);
|
||||
show();
|
||||
}catch(Exception 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;
|
||||
}
|
||||
|
||||
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(){
|
||||
return Core.scene.getScrollFocus() == pane || Core.scene.getKeyboardFocus() != this;
|
||||
}
|
||||
@@ -370,15 +336,15 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
tools.row();
|
||||
|
||||
ImageButton undo = tools.addImageButton("icon-undo", "clear", 16 * 2f, () -> view.undo()).get();
|
||||
ImageButton redo = tools.addImageButton("icon-redo", "clear", 16 * 2f, () -> view.redo()).get();
|
||||
ImageButton undo = tools.addImageButton("icon-undo", "clear", 16 * 2f, editor::undo).get();
|
||||
ImageButton redo = tools.addImageButton("icon-redo", "clear", 16 * 2f, editor::redo).get();
|
||||
|
||||
addTool.accept(EditorTool.pick);
|
||||
|
||||
tools.row();
|
||||
|
||||
undo.setDisabled(() -> !view.getStack().canUndo());
|
||||
redo.setDisabled(() -> !view.getStack().canRedo());
|
||||
undo.setDisabled(() -> !editor.canUndo());
|
||||
redo.setDisabled(() -> !editor.canRedo());
|
||||
|
||||
undo.update(() -> undo.getImage().setColor(undo.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.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().setRotation(editor.getDrawRotation() * 90);
|
||||
rotate.getImage().setRotation(editor.rotation * 90);
|
||||
rotate.getImage().setOrigin(Align.center);
|
||||
});
|
||||
|
||||
@@ -415,8 +381,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
button.margin(4f);
|
||||
button.getImageCell().grow();
|
||||
button.getStyle().imageUpColor = team.color;
|
||||
button.clicked(() -> editor.setDrawTeam(team));
|
||||
button.update(() -> button.setChecked(editor.getDrawTeam() == team));
|
||||
button.clicked(() -> editor.drawTeam = team);
|
||||
button.update(() -> button.setChecked(editor.drawTeam == team));
|
||||
teamgroup.add(button);
|
||||
tools.add(button);
|
||||
|
||||
@@ -429,7 +395,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
mid.table("underline", t -> {
|
||||
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.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)){
|
||||
editor.setDrawRotation((editor.getDrawRotation() + 1) % 4);
|
||||
editor.rotation = Mathf.mod(editor.rotation + 1, 4);
|
||||
}
|
||||
|
||||
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)
|
||||
if(UIUtils.ctrl()){
|
||||
if(Core.input.keyTap(KeyCode.Z)){
|
||||
view.undo();
|
||||
editor.undo();
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(KeyCode.Y)){
|
||||
view.redo();
|
||||
editor.redo();
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(KeyCode.S)){
|
||||
@@ -498,7 +470,6 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
pane.setFadeScrollBars(false);
|
||||
pane.setOverscroll(true, false);
|
||||
ButtonGroup<ImageButton> group = new ButtonGroup<>();
|
||||
blockgroup = group;
|
||||
|
||||
int i = 0;
|
||||
|
||||
@@ -521,9 +492,9 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
ImageButton button = new ImageButton("white", "clear-toggle");
|
||||
button.getStyle().imageUp = new TextureRegionDrawable(region);
|
||||
button.clicked(() -> editor.setDrawBlock(block));
|
||||
button.clicked(() -> editor.drawBlock = block);
|
||||
button.resizeImage(8 * 4f);
|
||||
button.update(() -> button.setChecked(editor.getDrawBlock() == block));
|
||||
button.update(() -> button.setChecked(editor.drawBlock == block));
|
||||
group.add(button);
|
||||
content.add(button).size(50f);
|
||||
|
||||
@@ -534,7 +505,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
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.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.math.Mathf;
|
||||
import io.anuke.arc.util.Disposable;
|
||||
import io.anuke.arc.util.Pack;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.graphics.IndexedRenderer;
|
||||
import io.anuke.mindustry.maps.MapTileData.DataPosition;
|
||||
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;
|
||||
|
||||
public class MapRenderer implements Disposable{
|
||||
@@ -102,27 +100,24 @@ public class MapRenderer implements Disposable{
|
||||
private void render(int wx, int wy){
|
||||
int x = wx / chunksize, y = wy / chunksize;
|
||||
IndexedRenderer mesh = chunks[x][y];
|
||||
byte bf = editor.getMap().read(wx, wy, DataPosition.floor);
|
||||
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)];
|
||||
Tile tile = editor.tiles()[wx][wy];
|
||||
|
||||
Block floor = content.block(bf);
|
||||
Block wall = content.block(bw);
|
||||
Team team = tile.getTeam();
|
||||
Block floor = tile.floor();
|
||||
Block wall = tile.block();
|
||||
|
||||
TextureRegion region;
|
||||
|
||||
int idxWall = (wx % chunksize) + (wy % 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();
|
||||
|
||||
if(wall.rotate){
|
||||
mesh.draw(idxWall, region,
|
||||
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{
|
||||
mesh.draw(idxWall, region,
|
||||
wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl)/2f,
|
||||
@@ -140,10 +135,12 @@ public class MapRenderer implements Disposable{
|
||||
if(wall.update || wall.destructible){
|
||||
mesh.setColor(team.color);
|
||||
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();
|
||||
offsetX = tilesize/2f - region.getWidth()/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{
|
||||
region = Core.atlas.find("clear-editor");
|
||||
}
|
||||
|
||||
@@ -1,22 +1,20 @@
|
||||
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.scene.ui.layout.Table;
|
||||
import io.anuke.mindustry.maps.MapTileData;
|
||||
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
||||
|
||||
public class MapResizeDialog extends FloatingDialog{
|
||||
private static final int minSize = 50, maxSize = 500, increment = 50;
|
||||
int width, height;
|
||||
|
||||
public MapResizeDialog(MapEditor editor, BiConsumer<Integer, Integer> cons){
|
||||
public MapResizeDialog(MapEditor editor, IntPositionConsumer cons){
|
||||
super("$editor.resizemap");
|
||||
shown(() -> {
|
||||
cont.clear();
|
||||
MapTileData data = editor.getMap();
|
||||
width = data.width();
|
||||
height = data.height();
|
||||
width = editor.width();
|
||||
height = editor.height();
|
||||
|
||||
Table table = new Table();
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public class MapSaveDialog extends FloatingDialog{
|
||||
shown(() -> {
|
||||
cont.clear();
|
||||
cont.label(() -> {
|
||||
Map map = world.maps.getByName(field.getText());
|
||||
Map map = world.maps.byName(field.getText());
|
||||
if(map != null){
|
||||
if(map.custom){
|
||||
return "$editor.overwrite";
|
||||
@@ -69,7 +69,7 @@ public class MapSaveDialog extends FloatingDialog{
|
||||
if(field.getText().isEmpty()){
|
||||
return true;
|
||||
}
|
||||
Map map = world.maps.getByName(field.getText());
|
||||
Map map = world.maps.byName(field.getText());
|
||||
return map != null && !map.custom;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import io.anuke.arc.scene.event.Touchable;
|
||||
import io.anuke.arc.scene.ui.TextField;
|
||||
import io.anuke.arc.scene.ui.layout.Unit;
|
||||
import io.anuke.arc.util.Tmp;
|
||||
import io.anuke.mindustry.editor.DrawOperation.TileOperation;
|
||||
import io.anuke.mindustry.graphics.Pal;
|
||||
import io.anuke.mindustry.input.Binding;
|
||||
import io.anuke.mindustry.ui.GridImage;
|
||||
@@ -29,8 +28,9 @@ import static io.anuke.mindustry.Vars.ui;
|
||||
public class MapView extends Element implements GestureListener{
|
||||
private MapEditor editor;
|
||||
private EditorTool tool = EditorTool.pencil;
|
||||
private OperationStack stack = new OperationStack();
|
||||
private DrawOperation op;
|
||||
//private OperationStack stack = new OperationStack();
|
||||
//private DrawOperation op;
|
||||
//private GridBits used;
|
||||
private Bresenham2 br = new Bresenham2();
|
||||
private boolean updated = false;
|
||||
private float offsetx, offsety;
|
||||
@@ -87,8 +87,6 @@ public class MapView extends Element implements GestureListener{
|
||||
mousex = x;
|
||||
mousey = y;
|
||||
|
||||
op = new DrawOperation(editor.getMap());
|
||||
|
||||
updated = false;
|
||||
|
||||
Point2 p = project(x, y);
|
||||
@@ -135,12 +133,7 @@ public class MapView extends Element implements GestureListener{
|
||||
updated = true;
|
||||
}
|
||||
|
||||
if(op != null && updated){
|
||||
if(!op.isEmpty()){
|
||||
stack.add(op);
|
||||
}
|
||||
op = null;
|
||||
}
|
||||
editor.flushOp();
|
||||
|
||||
if(button == KeyCode.MOUSE_MIDDLE && lastTool != null){
|
||||
tool = lastTool;
|
||||
@@ -190,14 +183,6 @@ public class MapView extends Element implements GestureListener{
|
||||
this.tool = tool;
|
||||
}
|
||||
|
||||
public void clearStack(){
|
||||
stack.clear();
|
||||
}
|
||||
|
||||
public OperationStack getStack(){
|
||||
return stack;
|
||||
}
|
||||
|
||||
public boolean isGrid(){
|
||||
return grid;
|
||||
}
|
||||
@@ -206,26 +191,6 @@ public class MapView extends Element implements GestureListener{
|
||||
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
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
@@ -259,14 +224,14 @@ public class MapView extends Element implements GestureListener{
|
||||
}
|
||||
|
||||
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 sclwidth = size * zoom;
|
||||
float sclheight = size * zoom * ratio;
|
||||
x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.getMap().width();
|
||||
y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * editor.getMap().height();
|
||||
x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.width();
|
||||
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));
|
||||
}else{
|
||||
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){
|
||||
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 sclwidth = size * zoom;
|
||||
float sclheight = size * zoom * ratio;
|
||||
float px = ((float) x / editor.getMap().width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2;
|
||||
float py = ((float) (y) / editor.getMap().height()) * sclheight
|
||||
float px = ((float) x / editor.width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2;
|
||||
float py = ((float) (y) / editor.height()) * sclheight
|
||||
+ offsety * zoom - sclheight / 2 + getHeight() / 2;
|
||||
return vec.set(px, py);
|
||||
}
|
||||
|
||||
@Override
|
||||
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 sclwidth = size * zoom;
|
||||
float sclheight = size * zoom * ratio;
|
||||
float centerx = x + width / 2 + offsetx * 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))){
|
||||
return;
|
||||
@@ -318,18 +283,18 @@ public class MapView extends Element implements GestureListener{
|
||||
|
||||
int index = 0;
|
||||
for(int i = 0; i < MapEditor.brushSizes.length; i++){
|
||||
if(editor.getBrushSize() == MapEditor.brushSizes[i]){
|
||||
if(editor.brushSize == MapEditor.brushSizes[i]){
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float scaling = zoom * Math.min(width, height) / editor.getMap().width();
|
||||
float scaling = zoom * Math.min(width, height) / editor.width();
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
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){
|
||||
Vector2 v1 = unproject(startx, starty).add(x, 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)){
|
||||
Point2 p = project(mousex, mousey);
|
||||
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(
|
||||
v.x + 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();
|
||||
|
||||
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);
|
||||
}else{
|
||||
Item item = tile.floor().itemDrop;
|
||||
Item item = tile.drop();
|
||||
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())){
|
||||
|
||||
@@ -269,7 +269,6 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
||||
//TODO better smoke effect, this one is awful
|
||||
if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) &&
|
||||
Mathf.chance(0.009f * Time.delta() * (1f - health / tile.block().health))){
|
||||
|
||||
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){
|
||||
tile = tile.target();
|
||||
return MapIO.colorFor(tile.floor(), tile.block(), tile.getTeam());
|
||||
return MapIO.colorFor(tile.floor(), tile.block(), tile.oreBlock(), tile.getTeam());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -230,9 +230,9 @@ public abstract class InputHandler implements InputProcessor{
|
||||
|
||||
boolean canMine(Tile tile){
|
||||
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
|
||||
&& player.acceptsItem(tile.floor().itemDrop)
|
||||
&& player.acceptsItem(tile.drop())
|
||||
&& tile.block() == Blocks.air && player.dst(tile.worldx(), tile.worldy()) <= Player.mineDistance;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,35 +7,50 @@ import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.Pixmap;
|
||||
import io.anuke.arc.graphics.Pixmap.Format;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.util.Pack;
|
||||
import io.anuke.arc.util.Strings;
|
||||
import io.anuke.arc.util.Structs;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.game.MappableContent;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.game.Version;
|
||||
import io.anuke.mindustry.maps.Map;
|
||||
import io.anuke.mindustry.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.Item;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.CachedTile;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper;
|
||||
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.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;
|
||||
|
||||
/**
|
||||
* Reads and writes map files.
|
||||
*/
|
||||
//TODO name mapping
|
||||
/** Reads and writes map files.*/
|
||||
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 version = 0;
|
||||
private static IntIntMap defaultBlockMap = new IntIntMap();
|
||||
private static ObjectMap<String, Block> missingBlocks;
|
||||
|
||||
private static void initBlocks(){
|
||||
if(missingBlocks != null) return;
|
||||
|
||||
missingBlocks = ObjectMap.of(
|
||||
"stained-stone", Blocks.moss
|
||||
);
|
||||
}
|
||||
|
||||
public static boolean isImage(FileHandle file){
|
||||
try(InputStream stream = file.read()){
|
||||
try(InputStream stream = file.read(32)){
|
||||
for(int i1 : pngHeader){
|
||||
if(stream.read() != i1){
|
||||
return false;
|
||||
@@ -47,44 +62,277 @@ public class MapIO{
|
||||
}
|
||||
}
|
||||
|
||||
private static void loadDefaultBlocks(){
|
||||
for(Block block : content.blocks()){
|
||||
defaultBlockMap.put(block.id, block.id);
|
||||
}
|
||||
public static Pixmap generatePreview(Map map) throws IOException{
|
||||
Pixmap floor = new Pixmap(map.width, map.height, Format.RGBA8888);
|
||||
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){
|
||||
Pixmap pixmap = new Pixmap(data.width(), data.height(), Format.RGBA8888);
|
||||
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);
|
||||
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);
|
||||
public static Pixmap generatePreview(Tile[][] tiles){
|
||||
Pixmap pixmap = new Pixmap(tiles.length, tiles[0].length, Format.RGBA8888);
|
||||
for(int x = 0; x < pixmap.getWidth(); x++){
|
||||
for(int y = 0; y < pixmap.getHeight(); y++){
|
||||
Tile tile = tiles[x][y];
|
||||
pixmap.drawPixel(x, pixmap.getHeight() - 1 - y, colorFor(tile.floor(), tile.block(), tile.oreBlock(), tile.getTeam()));
|
||||
}
|
||||
}
|
||||
|
||||
data.position(0, 0);
|
||||
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
/**Reads a pixmap in the old (3.5) map format.*/
|
||||
public static MapTileData readLegacyPixmap(Pixmap pixmap){
|
||||
MapTileData data = new MapTileData(pixmap.getWidth(), pixmap.getHeight());
|
||||
public static int colorFor(Block floor, Block wall, Block ore, Team team){
|
||||
if(wall.synthetic()){
|
||||
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++){
|
||||
for(int y = 0; y < data.height(); y++){
|
||||
public static void writeMap(FileHandle file, Map map, Tile[][] tiles) throws IOException{
|
||||
OutputStream output = file.write(false, bufferSize);
|
||||
|
||||
{
|
||||
DataOutputStream stream = new DataOutputStream(output);
|
||||
stream.writeInt(version);
|
||||
stream.writeInt(Version.build);
|
||||
stream.writeShort(tiles.length);
|
||||
stream.writeShort(tiles[0].length);
|
||||
stream.writeByte((byte)map.tags.size);
|
||||
|
||||
for(Entry<String, String> entry : map.tags.entries()){
|
||||
stream.writeUTF(entry.key);
|
||||
stream.writeUTF(entry.value);
|
||||
}
|
||||
}
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
|
||||
int width = map.width, height = map.height;
|
||||
|
||||
SaveIO.getSaveWriter().writeContentHeader(stream);
|
||||
|
||||
//floor first
|
||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
||||
Tile tile = tiles[i % width][i / width];
|
||||
stream.writeByte(tile.getFloorID());
|
||||
stream.writeByte(tile.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);
|
||||
LegacyBlock block = LegacyColorMapper.get(color);
|
||||
Tile tile = tiles[x][y];
|
||||
|
||||
data.write(x, y, DataPosition.floor, block.floor.id);
|
||||
data.write(x, y, DataPosition.wall, block.wall.id);
|
||||
tile.setFloor(block.floor);
|
||||
tile.setBlock(block.wall);
|
||||
if(block.ore != null) tile.setOre(block.ore);
|
||||
|
||||
//place core
|
||||
if(color == Color.rgba8888(Color.GREEN)){
|
||||
@@ -93,121 +341,133 @@ public class MapIO{
|
||||
int worldx = dx - 1 + x;
|
||||
int worldy = dy - 1 + y;
|
||||
|
||||
//multiblock parts
|
||||
if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){
|
||||
data.write(worldx, worldy, DataPosition.wall, Blocks.part.id);
|
||||
data.write(worldx, worldy, DataPosition.rotationTeam, Pack.byteByte((byte)0, (byte)Team.blue.ordinal()));
|
||||
data.write(worldx, worldy, DataPosition.link, Pack.byteByte((byte) (dx - 1 + 8), (byte) (dy - 1 + 8)));
|
||||
Tile write = tiles[worldx][worldy];
|
||||
write.setBlock(Blocks.part);
|
||||
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);
|
||||
data.write(x, y, DataPosition.rotationTeam, Pack.byteByte((byte)0, (byte)Team.blue.ordinal()));
|
||||
//actual core parts
|
||||
tile.setBlock(Blocks.coreShard);
|
||||
tile.setTeam(Team.blue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void writeMap(OutputStream stream, ObjectMap<String, String> tags, MapTileData data) throws IOException{
|
||||
if(defaultBlockMap == null){
|
||||
loadDefaultBlocks();
|
||||
}
|
||||
|
||||
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 pixmap in the old 4.0 .mmap format.*/
|
||||
private static void readLegacyMmapTiles(FileHandle file, Tile[][] tiles) throws IOException{
|
||||
readLegacyMmapTiles(file, (x, y) -> tiles[x][y]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads tile data, skipping meta.
|
||||
*/
|
||||
public static MapTileData readTileData(DataInputStream stream, boolean readOnly) throws IOException{
|
||||
MapMeta meta = readMapMeta(stream);
|
||||
return readTileData(stream, meta, readOnly);
|
||||
}
|
||||
/**Reads a mmap in the old 4.0 .mmap format.*/
|
||||
private static void readLegacyMmapTiles(FileHandle file, TileProvider tiles) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(file.read(bufferSize))){
|
||||
stream.readInt(); //version
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
stream.readUTF(); //key
|
||||
stream.readUTF(); //val
|
||||
}
|
||||
|
||||
/**
|
||||
* Does not skip meta. Call after reading meta.
|
||||
*/
|
||||
public static MapTileData readTileData(DataInputStream stream, MapMeta meta, boolean readOnly) throws IOException{
|
||||
byte[] bytes = new byte[stream.available()];
|
||||
stream.readFully(bytes);
|
||||
return new MapTileData(bytes, meta.width, meta.height, meta.blockMap, readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads tile data, skipping meta tags.
|
||||
*/
|
||||
public static MapTileData readTileData(Map map, boolean readOnly){
|
||||
try(DataInputStream ds = new DataInputStream(map.stream.get())){
|
||||
return MapIO.readTileData(ds, readOnly);
|
||||
}catch(IOException e){
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static MapMeta readMapMeta(DataInputStream stream) throws IOException{
|
||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
||||
IntIntMap map = new IntIntMap();
|
||||
|
||||
int version = stream.readInt();
|
||||
|
||||
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++){
|
||||
short id = stream.readShort();
|
||||
String name = stream.readUTF();
|
||||
Block block = content.getByName(ContentType.block, name);
|
||||
if(block == null){
|
||||
block = Blocks.air;
|
||||
initBlocks();
|
||||
|
||||
//block id -> real id map
|
||||
IntIntMap map = new IntIntMap();
|
||||
IntIntMap oreMap = new IntIntMap();
|
||||
|
||||
short blocks = stream.readShort();
|
||||
for(int i = 0; i < blocks; i++){
|
||||
short id = stream.readShort();
|
||||
String name = stream.readUTF();
|
||||
Block block = content.getByName(ContentType.block, name);
|
||||
if(block == null){
|
||||
//substitute for replacement in missingBlocks if possible
|
||||
if(missingBlocks.containsKey(name)){
|
||||
block = missingBlocks.get(name);
|
||||
}else if(name.startsWith("ore-")){ //an ore floor combination
|
||||
String[] split = name.split("-");
|
||||
String itemName = split[1], floorName = 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);
|
||||
Block floor = missingBlocks.get(floorName, content.getByName(ContentType.block, floorName));
|
||||
if(oreBlock != null && floor != null){
|
||||
oreMap.put(id, oreBlock.id);
|
||||
block = floor;
|
||||
}else{
|
||||
block = Blocks.air;
|
||||
}
|
||||
}else{
|
||||
block = Blocks.air;
|
||||
}
|
||||
|
||||
}
|
||||
map.put(id, block.id);
|
||||
}
|
||||
short width = stream.readShort(), height = stream.readShort();
|
||||
|
||||
for(int y = 0; y < height; y++){
|
||||
for(int x = 0; x < width; x++){
|
||||
Tile tile = tiles.get(x, y);
|
||||
byte floorb = stream.readByte();
|
||||
byte blockb = stream.readByte();
|
||||
byte 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{
|
||||
stream.writeInt(meta.version);
|
||||
stream.writeByte((byte) meta.tags.size);
|
||||
private static Map readLegacyMap(FileHandle file, boolean custom) throws IOException{
|
||||
try(DataInputStream stream = new DataInputStream(file.read(bufferSize))){
|
||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
||||
|
||||
for(Entry<String, String> entry : meta.tags.entries()){
|
||||
stream.writeUTF(entry.key);
|
||||
stream.writeUTF(entry.value);
|
||||
int version = stream.readInt();
|
||||
if(version != 0) throw new IOException("Attempted to read non-legacy map in legacy method!");
|
||||
byte tagAmount = stream.readByte();
|
||||
|
||||
for(int i = 0; i < tagAmount; i++){
|
||||
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){
|
||||
if(wall.synthetic()){
|
||||
return team.intColor;
|
||||
}
|
||||
return Color.rgba8888(wall.solid ? wall.color : floor.color);
|
||||
//endregion
|
||||
|
||||
interface TileProvider{
|
||||
Tile get(int x, int y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ import io.anuke.mindustry.game.Rules;
|
||||
import io.anuke.mindustry.game.Team;
|
||||
import io.anuke.mindustry.gen.Serialization;
|
||||
import io.anuke.mindustry.type.ContentType;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
@@ -43,18 +43,37 @@ public abstract class SaveFileVersion{
|
||||
}
|
||||
|
||||
public void writeMap(DataOutputStream stream) throws IOException{
|
||||
|
||||
//write world size
|
||||
stream.writeShort(world.width());
|
||||
stream.writeShort(world.height());
|
||||
|
||||
//floor first
|
||||
for(int i = 0; i < world.width() * world.height(); i++){
|
||||
Tile tile = world.tile(i % world.width(), i / world.width());
|
||||
|
||||
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());
|
||||
|
||||
if(tile.block() instanceof BlockPart){
|
||||
if(tile.block() == Blocks.part){
|
||||
stream.writeByte(tile.link);
|
||||
}else if(tile.entity != null){
|
||||
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation
|
||||
@@ -67,13 +86,14 @@ public abstract class SaveFileVersion{
|
||||
|
||||
tile.entity.writeConfig(stream);
|
||||
tile.entity.write(stream);
|
||||
}else if(tile.block() == Blocks.air){
|
||||
}else{
|
||||
//write consecutive non-entity blocks
|
||||
int consecutives = 0;
|
||||
|
||||
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
|
||||
Tile nextTile = world.tile(j % world.width(), j / world.width());
|
||||
|
||||
if(nextTile.getFloorID() != tile.getFloorID() || nextTile.block() != Blocks.air){
|
||||
if(nextTile.block() != tile.block()){
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -94,14 +114,34 @@ public abstract class SaveFileVersion{
|
||||
|
||||
Tile[][] tiles = world.createTiles(width, height);
|
||||
|
||||
//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 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();
|
||||
}else if(tile.entity != null){
|
||||
byte tr = stream.readByte();
|
||||
@@ -121,19 +161,16 @@ public abstract class SaveFileVersion{
|
||||
|
||||
tile.entity.readConfig(stream);
|
||||
tile.entity.read(stream);
|
||||
}else if(wallid == 0){
|
||||
}else{
|
||||
int consecutives = stream.readUnsignedByte();
|
||||
|
||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||
int newx = j % width, newy = j / width;
|
||||
Tile newTile = new Tile(newx, newy, floorid, wallid);
|
||||
tiles[newx][newy] = newTile;
|
||||
tiles[newx][newy].setBlock(block);
|
||||
}
|
||||
|
||||
i += consecutives;
|
||||
}
|
||||
|
||||
tiles[x][y] = tile;
|
||||
}
|
||||
|
||||
content.setTemporaryMapper(null);
|
||||
|
||||
@@ -48,7 +48,7 @@ public class SaveIO{
|
||||
}
|
||||
|
||||
public static DataInputStream getSlotStream(int slot){
|
||||
return new DataInputStream(new InflaterInputStream(fileFor(slot).read()));
|
||||
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize)));
|
||||
}
|
||||
|
||||
public static boolean isSaveValid(int slot){
|
||||
@@ -60,7 +60,7 @@ public class SaveIO{
|
||||
}
|
||||
|
||||
public static boolean isSaveValid(FileHandle file){
|
||||
return isSaveValid(new DataInputStream(new InflaterInputStream(file.read())));
|
||||
return isSaveValid(new DataInputStream(new InflaterInputStream(file.read(bufferSize))));
|
||||
}
|
||||
|
||||
public static boolean isSaveValid(DataInputStream stream){
|
||||
@@ -95,7 +95,7 @@ public class SaveIO{
|
||||
}
|
||||
|
||||
public static void write(FileHandle file){
|
||||
write(new DeflaterOutputStream(file.write(false)){
|
||||
write(new DeflaterOutputStream(file.write(false, bufferSize)){
|
||||
byte[] tmp = {0};
|
||||
|
||||
public void write(int var1) throws IOException {
|
||||
@@ -120,12 +120,12 @@ public class SaveIO{
|
||||
public static void load(FileHandle file) throws SaveException{
|
||||
try{
|
||||
//try and load; if any exception at all occurs
|
||||
load(new InflaterInputStream(file.read()));
|
||||
load(new InflaterInputStream(file.read(bufferSize)));
|
||||
}catch(SaveException e){
|
||||
e.printStackTrace();
|
||||
FileHandle backup = file.sibling(file.name() + "-backup." + file.extension());
|
||||
if(backup.exists()){
|
||||
load(new InflaterInputStream(backup.read()));
|
||||
load(new InflaterInputStream(backup.read(bufferSize)));
|
||||
}else{
|
||||
throw new SaveException(e.getCause());
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ public class SaveMeta{
|
||||
this.build = build;
|
||||
this.timestamp = timestamp;
|
||||
this.timePlayed = timePlayed;
|
||||
this.map = world.maps.getByName(map);
|
||||
this.map = world.maps.all().find(m -> m.fileName().equals(map));
|
||||
this.wave = wave;
|
||||
this.rules = rules;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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.mindustry.game.Version;
|
||||
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;
|
||||
}
|
||||
String mapname = stream.readUTF();
|
||||
Map map = world.maps.getByName(mapname);
|
||||
if(map == null) map = new Map(Strings.capitalize(mapname), 1, 1);
|
||||
Map map = world.maps.all().find(m -> m.fileName().equals(mapname));
|
||||
if(map == null) map = new Map(customMapDirectory.child(mapname), 1, 1, new ObjectMap<>(), true);
|
||||
world.setMap(map);
|
||||
|
||||
int wave = stream.readInt();
|
||||
@@ -62,7 +62,7 @@ public class Save16 extends SaveFileVersion{
|
||||
|
||||
//--GENERAL STATE--
|
||||
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.writeFloat(state.wavetime); //wave countdown
|
||||
|
||||
@@ -1,44 +1,75 @@
|
||||
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.function.Supplier;
|
||||
|
||||
import java.io.InputStream;
|
||||
import io.anuke.arc.files.FileHandle;
|
||||
import io.anuke.arc.graphics.Texture;
|
||||
import io.anuke.mindustry.io.MapIO;
|
||||
|
||||
public class Map{
|
||||
/** Internal map name. This is the filename, without any extensions.*/
|
||||
public final String name;
|
||||
/** Whether this is a custom map.*/
|
||||
public final boolean custom;
|
||||
/** Metadata. Author description, display name, etc.*/
|
||||
public final MapMeta meta;
|
||||
/** Supplies a new input stream with the data of this map.*/
|
||||
public final Supplier<InputStream> stream;
|
||||
public final ObjectMap<String, String> tags;
|
||||
/** Base file of this map.*/
|
||||
public final FileHandle file;
|
||||
/** Format version.*/
|
||||
public final int version;
|
||||
/** Map width/height, shorts.*/
|
||||
public int width, height;
|
||||
/** Preview texture.*/
|
||||
public Texture texture;
|
||||
/** Build that this map was created in. -1 = unknown or custom build.*/
|
||||
public int build;
|
||||
|
||||
public Map(String name, MapMeta meta, boolean custom, Supplier<InputStream> streamSupplier){
|
||||
this.name = name;
|
||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom, int version, int build){
|
||||
this.custom = custom;
|
||||
this.meta = meta;
|
||||
this.stream = streamSupplier;
|
||||
this.tags = tags;
|
||||
this.file = file;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.version = version;
|
||||
this.build = build;
|
||||
}
|
||||
|
||||
public Map(String unknownName, int width, int height){
|
||||
this(unknownName, new MapMeta(0, new ObjectMap<>(), width, height, null), true, () -> null);
|
||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom, int version){
|
||||
this(file, width, height, tags, custom, version, -1);
|
||||
}
|
||||
|
||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom){
|
||||
this(file, width, height, tags, custom, MapIO.version);
|
||||
}
|
||||
|
||||
public String fileName(){
|
||||
return file.nameWithoutExtension();
|
||||
}
|
||||
|
||||
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
|
||||
public String toString(){
|
||||
return "Map{" +
|
||||
"name='" + name + '\'' +
|
||||
"file='" + file + '\'' +
|
||||
", 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,166 +1,155 @@
|
||||
package io.anuke.mindustry.maps;
|
||||
|
||||
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.graphics.Texture;
|
||||
import io.anuke.arc.collection.Array;
|
||||
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.Time;
|
||||
import io.anuke.mindustry.io.MapIO;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Maps implements Disposable{
|
||||
/**List of all built-in maps.*/
|
||||
private static final String[] defaultMapNames = {};
|
||||
/**Tile format version.*/
|
||||
private static final int version = 0;
|
||||
/** List of all built-in maps. */
|
||||
private static final String[] defaultMapNames = {"impact0079"};
|
||||
/** All maps stored in an ordered array. */
|
||||
private Array<Map> maps = new Array<>();
|
||||
|
||||
/**Maps map names to the real maps.*/
|
||||
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.*/
|
||||
/** Returns a list of all maps, including custom ones. */
|
||||
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(){
|
||||
returnArray.clear();
|
||||
for(Map map : allMaps){
|
||||
if(map.custom) returnArray.add(map);
|
||||
}
|
||||
return returnArray;
|
||||
return maps.select(m -> m.custom);
|
||||
}
|
||||
|
||||
/**Returns a list of only default maps.*/
|
||||
/** Returns a list of only default maps. */
|
||||
public Array<Map> defaultMaps(){
|
||||
returnArray.clear();
|
||||
for(Map map : allMaps){
|
||||
if(!map.custom) returnArray.add(map);
|
||||
}
|
||||
return returnArray;
|
||||
return maps.select(m -> !m.custom);
|
||||
}
|
||||
|
||||
/**Returns map by internal name.*/
|
||||
public Map getByName(String name){
|
||||
return maps.get(name);
|
||||
public Map byName(String name){
|
||||
return maps.find(m -> m.name().equals(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){
|
||||
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
|
||||
|
||||
try(DataInputStream ds = new DataInputStream(file.read())) {
|
||||
return new Map(name, MapIO.readMapMeta(ds), false, file::read);
|
||||
try{
|
||||
return MapIO.readMap(file, false);
|
||||
}catch(IOException 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(){
|
||||
Time.mark();
|
||||
try{
|
||||
for (String name : defaultMapNames) {
|
||||
for(String name : defaultMapNames){
|
||||
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);
|
||||
}
|
||||
|
||||
loadCustomMaps();
|
||||
Log.info("Time to load maps: {0}", Time.elapsed());
|
||||
}
|
||||
|
||||
/**Save a map. This updates all values and stored data necessary.*/
|
||||
public void saveMap(String name, MapTileData data, ObjectMap<String, String> tags){
|
||||
/** Save a custom map to the directory. This updates all values and stored data necessary.
|
||||
* The tags are copied to prevent mutation later.*/
|
||||
public void saveMap(ObjectMap<String, String> baseTags, Tile[][] data){
|
||||
|
||||
try{
|
||||
//create copy of tags to prevent mutation later
|
||||
ObjectMap<String, String> newTags = new ObjectMap<>();
|
||||
newTags.putAll(tags);
|
||||
tags = newTags;
|
||||
|
||||
ObjectMap<String, String> tags = new ObjectMap<>(baseTags);
|
||||
String name = tags.get("name");
|
||||
if(name == null) throw new IllegalArgumentException("Can't save a map with no name. How did this happen?");
|
||||
FileHandle file = customMapDirectory.child(name + "." + mapExtension);
|
||||
MapIO.writeMap(file.write(false), tags, data);
|
||||
|
||||
if(maps.containsKey(name)){
|
||||
if(maps.get(name).texture != null) {
|
||||
maps.get(name).texture.dispose();
|
||||
maps.get(name).texture = null;
|
||||
//find map with the same exact display name
|
||||
Map other = maps.find(m -> m.getDisplayName().equals(name));
|
||||
|
||||
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){
|
||||
map.texture = new Texture(MapIO.generatePixmap(data));
|
||||
map.texture = new Texture(MapIO.generatePreview(data));
|
||||
}
|
||||
allMaps.add(map);
|
||||
|
||||
maps.put(name, map);
|
||||
}catch (IOException e){
|
||||
maps.add(map);
|
||||
}catch(IOException 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){
|
||||
if(map.texture != null){
|
||||
map.texture.dispose();
|
||||
map.texture = null;
|
||||
}
|
||||
|
||||
maps.remove(map.name);
|
||||
allMaps.removeValue(map, true);
|
||||
|
||||
customMapDirectory.child(map.name + "." + mapExtension).delete();
|
||||
maps.remove(map);
|
||||
map.file.delete();
|
||||
}
|
||||
|
||||
private void loadMap(String name, Supplier<InputStream> supplier, boolean custom) throws IOException{
|
||||
try(DataInputStream ds = new DataInputStream(supplier.get())) {
|
||||
MapMeta meta = MapIO.readMapMeta(ds);
|
||||
Map map = new Map(name, meta, custom, supplier);
|
||||
private void loadMap(FileHandle file, boolean custom) throws IOException{
|
||||
Map map = MapIO.readMap(file, custom);
|
||||
|
||||
if (!headless){
|
||||
map.texture = new Texture(MapIO.generatePixmap(MapIO.readTileData(ds, meta, true)));
|
||||
}
|
||||
|
||||
maps.put(map.name, map);
|
||||
allMaps.add(map);
|
||||
if(!headless){
|
||||
map.texture = new Texture(MapIO.generatePreview(map));
|
||||
}
|
||||
|
||||
maps.add(map);
|
||||
}
|
||||
|
||||
private void loadCustomMaps(){
|
||||
for(FileHandle file : customMapDirectory.list()){
|
||||
try{
|
||||
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(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**Returns an input stream supplier for a given map name.*/
|
||||
private Supplier<InputStream> getStreamFor(String name){
|
||||
return customMapDirectory.child(name + "." + mapExtension)::read;
|
||||
}
|
||||
|
||||
@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.type.Item;
|
||||
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{
|
||||
private Array<Item> ores;
|
||||
@@ -31,13 +29,14 @@ public class BasicGenerator extends RandomGenerator{
|
||||
public void generate(int x, int y){
|
||||
floor = Blocks.stone;
|
||||
|
||||
if(ores != null && ((Floor) floor).hasOres){
|
||||
if(ores != null){
|
||||
int offsetX = x - 4, offsetY = y + 23;
|
||||
for(int i = ores.size - 1; i >= 0; i--){
|
||||
Item entry = ores.get(i);
|
||||
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){
|
||||
floor = OreBlock.get(floor, entry);
|
||||
|
||||
//floor = OreBlock.get(floor, entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,19 +9,17 @@ import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.content.Items;
|
||||
import io.anuke.mindustry.io.MapIO;
|
||||
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.Loadout;
|
||||
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.blocks.StaticWall;
|
||||
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
|
||||
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;
|
||||
|
||||
public class MapGenerator extends Generator{
|
||||
@@ -73,112 +71,112 @@ public class MapGenerator extends Generator{
|
||||
public void init(Loadout loadout){
|
||||
this.loadout = loadout;
|
||||
map = world.maps.loadInternalMap(mapName);
|
||||
width = map.meta.width;
|
||||
height = map.meta.height;
|
||||
width = map.width;
|
||||
height = map.height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(Tile[][] tiles){
|
||||
MapTileData data = MapIO.readTileData(map, true);
|
||||
|
||||
data.position(0, 0);
|
||||
TileDataMarker marker = data.newDataMarker();
|
||||
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;
|
||||
try{
|
||||
for(int x = 0; x < width; x++){
|
||||
for(int y = 0; y < height; y++){
|
||||
tiles[x][y] = new Tile(x, y);
|
||||
}
|
||||
|
||||
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 y = 0; y < data.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, data.width()-1);
|
||||
int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, data.height()-1);
|
||||
for(int x = 0; x < width; x++){
|
||||
for(int y = 0; y < height; y++){
|
||||
if(tiles[x][y].block() instanceof CoreBlock){
|
||||
players.add(new Point2(x, y));
|
||||
tiles[x][y].setBlock(Blocks.air);
|
||||
}
|
||||
|
||||
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)
|
||||
|| (tile.block() == Blocks.air && !tiles[newX][newY].block().synthetic())
|
||||
|| (tiles[newX][newY].block() == Blocks.air && tile.block() instanceof StaticWall)){
|
||||
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);
|
||||
tile.setBlock(tiles[newX][newY].block());
|
||||
}
|
||||
}
|
||||
|
||||
if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock)){
|
||||
for(ItemStack stack : storageDrops){
|
||||
if(Mathf.chance(0.3)){
|
||||
tile.entity.items.add(stack.item, Math.min(Mathf.random(stack.amount), tile.block().itemCapacity));
|
||||
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(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];
|
||||
if(tile.floor() instanceof OreBlock){
|
||||
OreBlock block = (OreBlock)tile.floor();
|
||||
tile.setFloor(block.base);
|
||||
if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock)){
|
||||
for(ItemStack stack : storageDrops){
|
||||
if(Mathf.chance(0.3)){
|
||||
tile.entity.items.add(stack.item, Math.min(Mathf.random(stack.amount), tile.block().itemCapacity));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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{
|
||||
|
||||
@@ -27,10 +27,10 @@ public class NetworkIO{
|
||||
try(DataOutputStream stream = new DataOutputStream(os)){
|
||||
//--GENERAL STATE--
|
||||
Serialization.writeRules(stream, state.rules);
|
||||
stream.writeUTF(world.getMap().name); //map name
|
||||
stream.writeUTF(world.getMap().name()); //map name
|
||||
|
||||
//write tags
|
||||
ObjectMap<String, String> tags = world.getMap().meta.tags;
|
||||
ObjectMap<String, String> tags = world.getMap().tags;
|
||||
stream.writeByte(tags.size);
|
||||
for(Entry<String, String> entry : tags.entries()){
|
||||
stream.writeUTF(entry.key);
|
||||
@@ -105,7 +105,7 @@ public class NetworkIO{
|
||||
//map
|
||||
world.spawner.read(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();
|
||||
|
||||
@@ -141,7 +141,7 @@ public class NetworkIO{
|
||||
int maxlen = 32;
|
||||
|
||||
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));
|
||||
map = map.substring(0, Math.min(map.length(), maxlen));
|
||||
|
||||
@@ -5,9 +5,12 @@ import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.graphics.Color;
|
||||
import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.arc.scene.ui.layout.Table;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.game.UnlockableContent;
|
||||
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 final Color color;
|
||||
@@ -28,8 +31,6 @@ public class Item extends UnlockableContent implements Comparable<Item>{
|
||||
* 1 cost = 1 tick added to build time
|
||||
*/
|
||||
public float cost = 3f;
|
||||
/**Whether this item has ores generated for it.*/
|
||||
public boolean genOre = false;
|
||||
/**If true, item is always unlocked.*/
|
||||
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.*/
|
||||
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.IntMap;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.content.Items;
|
||||
import io.anuke.mindustry.game.Content;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Pos;
|
||||
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 static io.anuke.mindustry.Vars.defaultTeam;
|
||||
@@ -27,7 +24,7 @@ public class Loadout extends Content{
|
||||
put('2', new BlockEntry(Blocks.coreFoundation));
|
||||
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<>();
|
||||
@@ -79,8 +76,7 @@ public class Loadout extends Content{
|
||||
tile.setRotation((byte)entry.value.rotation);
|
||||
if(entry.value.ore != null){
|
||||
for(Tile t : tile.getLinkedTiles(outArray)){
|
||||
Floor floor = t.floor();
|
||||
t.setFloor(OreBlock.get(floor, entry.value.ore) == null ? OreBlock.get(Blocks.stone, entry.value.ore) : OreBlock.get(floor, entry.value.ore));
|
||||
t.setOre(entry.value.ore);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,10 +89,10 @@ public class Loadout extends Content{
|
||||
|
||||
static class BlockEntry{
|
||||
final Block block;
|
||||
final Item ore;
|
||||
final Block ore;
|
||||
final int rotation;
|
||||
|
||||
BlockEntry(Block block, Item ore){
|
||||
BlockEntry(Block block, Block ore){
|
||||
this.block = block;
|
||||
this.ore = ore;
|
||||
this.rotation = 0;
|
||||
|
||||
@@ -106,7 +106,7 @@ public class CustomGameDialog extends FloatingDialog{
|
||||
image.row();
|
||||
image.add("[accent]" + map.getDisplayName()).pad(3f).growX().wrap().get().setAlignment(Align.center, Align.center);
|
||||
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);
|
||||
border.setScaling(Scaling.fit);
|
||||
|
||||
@@ -113,7 +113,7 @@ public class LoadDialog extends FloatingDialog{
|
||||
|
||||
button.defaults().padBottom(3);
|
||||
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.add(Core.bundle.format("save.wave", color + slot.getWave()));
|
||||
button.row();
|
||||
|
||||
@@ -15,12 +15,8 @@ import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.core.Platform;
|
||||
import io.anuke.mindustry.io.MapIO;
|
||||
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 java.io.DataInputStream;
|
||||
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class MapsDialog extends FloatingDialog{
|
||||
@@ -33,22 +29,25 @@ public class MapsDialog extends FloatingDialog{
|
||||
buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> {
|
||||
Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> {
|
||||
try{
|
||||
DataInputStream stream = new DataInputStream(file.read());
|
||||
MapMeta meta = MapIO.readMapMeta(stream);
|
||||
MapTileData data = MapIO.readTileData(stream, meta, true);
|
||||
stream.close();
|
||||
Map map = MapIO.readMap(file, true);
|
||||
String name = map.tags.get("name", file.nameWithoutExtension());
|
||||
|
||||
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));
|
||||
}else if(world.maps.getByName(name) != null){
|
||||
}else if(conflict != null){
|
||||
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
|
||||
world.maps.saveMap(name, data, meta.tags);
|
||||
setup();
|
||||
try{
|
||||
world.maps.importMap(file, map);
|
||||
setup();
|
||||
}catch(Exception e){
|
||||
ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false)));
|
||||
Log.err(e);
|
||||
}
|
||||
});
|
||||
}else{
|
||||
world.maps.saveMap(name, data, meta.tags);
|
||||
world.maps.importMap(file, map);
|
||||
setup();
|
||||
}
|
||||
|
||||
@@ -89,7 +88,7 @@ public class MapsDialog extends FloatingDialog{
|
||||
TextButton button = maps.addButton("", "clear", () -> showMapInfo(map)).width(mapsize).pad(8).get();
|
||||
button.clearChildren();
|
||||
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.addImage("white").growX().pad(4).color(Color.GRAY);
|
||||
button.row();
|
||||
@@ -129,15 +128,15 @@ public class MapsDialog extends FloatingDialog{
|
||||
|
||||
t.add("$editor.name").padRight(10).color(Color.GRAY).padTop(0);
|
||||
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.add("$editor.author").padRight(10).color(Color.GRAY);
|
||||
t.row();
|
||||
t.add(map.meta.author()).growX().wrap().padTop(2);
|
||||
t.add(map.author()).growX().wrap().padTop(2);
|
||||
t.row();
|
||||
t.add("$editor.description").padRight(10).color(Color.GRAY).top();
|
||||
t.row();
|
||||
t.add(map.meta.description()).growX().wrap().padTop(2);
|
||||
t.add(map.description()).growX().wrap().padTop(2);
|
||||
t.row();
|
||||
t.add("$editor.oregen.info").padRight(10).color(Color.GRAY);
|
||||
}).height(mapsize).width(mapsize);
|
||||
@@ -146,7 +145,7 @@ public class MapsDialog extends FloatingDialog{
|
||||
|
||||
table.addImageTextButton("$editor.openin", "icon-load-map", 16 * 2, () -> {
|
||||
try{
|
||||
Vars.ui.editor.beginEditMap(map.stream.get());
|
||||
Vars.ui.editor.beginEditMap(map.file);
|
||||
dialog.hide();
|
||||
hide();
|
||||
}catch(Exception e){
|
||||
@@ -156,7 +155,7 @@ public class MapsDialog extends FloatingDialog{
|
||||
}).fillX().height(54f).marginLeft(10);
|
||||
|
||||
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);
|
||||
dialog.hide();
|
||||
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.ImageButton;
|
||||
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.entities.type.TileEntity;
|
||||
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.Icon;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.OreBlock;
|
||||
|
||||
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. */
|
||||
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;
|
||||
}
|
||||
}
|
||||
51
core/src/io/anuke/mindustry/world/CachedTile.java
Normal file
51
core/src/io/anuke/mindustry/world/CachedTile.java
Normal file
@@ -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.graphics.Color;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.content.Items;
|
||||
import io.anuke.mindustry.game.ContentList;
|
||||
import io.anuke.mindustry.world.blocks.Floor;
|
||||
import io.anuke.mindustry.world.blocks.OreBlock;
|
||||
|
||||
public class LegacyColorMapper implements ContentList{
|
||||
private static IntMap<LegacyBlock> blockMap = new IntMap<>();
|
||||
@@ -38,10 +36,14 @@ public class LegacyColorMapper implements ContentList{
|
||||
map("6e501e", Blocks.stainedStoneRed);
|
||||
map("ed5334", Blocks.stainedStoneRed);
|
||||
map("292929", Blocks.tar);
|
||||
map("c3a490", OreBlock.get(Blocks.stone, Items.copper));
|
||||
map("161616", OreBlock.get(Blocks.stone, Items.coal));
|
||||
map("6277bc", OreBlock.get(Blocks.stone, Items.titanium));
|
||||
map("83bc58", OreBlock.get(Blocks.stone, Items.thorium));
|
||||
map("c3a490", Blocks.stone, Blocks.air, Blocks.oreCopper);
|
||||
map("161616", Blocks.stone, Blocks.air, Blocks.oreCoal);
|
||||
map("6277bc", Blocks.stone, Blocks.air, Blocks.oreTitanium);
|
||||
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){
|
||||
@@ -55,10 +57,18 @@ public class LegacyColorMapper implements ContentList{
|
||||
public static class LegacyBlock{
|
||||
public final Floor floor;
|
||||
public final Block wall;
|
||||
public final Block ore;
|
||||
|
||||
public LegacyBlock(Block floor, Block wall){
|
||||
this.floor = (Floor) floor;
|
||||
this.wall = wall;
|
||||
this.ore = null;
|
||||
}
|
||||
|
||||
public LegacyBlock(Block floor, Block wall, Block ore){
|
||||
this.floor = (Floor) floor;
|
||||
this.wall = wall;
|
||||
this.ore = ore;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,10 @@ import io.anuke.arc.math.geom.Position;
|
||||
import io.anuke.arc.math.geom.Vector2;
|
||||
import io.anuke.arc.util.Pack;
|
||||
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.type.TileEntity;
|
||||
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.Floor;
|
||||
import io.anuke.mindustry.world.modules.ConsumeModule;
|
||||
@@ -39,10 +40,13 @@ public class Tile implements Position, TargetTrait{
|
||||
private byte rotation;
|
||||
/** Team ordinal. */
|
||||
private byte team;
|
||||
/**Ore that is on top of this (floor) block.*/
|
||||
private byte ore = 0;
|
||||
|
||||
public Tile(int x, int y){
|
||||
this.x = (short) x;
|
||||
this.y = (short) y;
|
||||
wall = floor = (Floor)Blocks.air;
|
||||
}
|
||||
|
||||
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){
|
||||
this.floor = type;
|
||||
this.ore = 0;
|
||||
}
|
||||
|
||||
public byte getRotation(){
|
||||
@@ -213,6 +218,14 @@ public class Tile implements Position, TargetTrait{
|
||||
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. */
|
||||
public void setLinked(byte dx, byte dy){
|
||||
setBlock(Blocks.part);
|
||||
@@ -316,6 +329,30 @@ public class Tile implements Position, TargetTrait{
|
||||
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(){
|
||||
cost = 1;
|
||||
boolean occluded = false;
|
||||
@@ -343,7 +380,7 @@ public class Tile implements Position, TargetTrait{
|
||||
}
|
||||
}
|
||||
|
||||
private void preChanged(){
|
||||
protected void preChanged(){
|
||||
block().removed(this);
|
||||
if(entity != null){
|
||||
entity.removeFromProximity();
|
||||
@@ -351,7 +388,7 @@ public class Tile implements Position, TargetTrait{
|
||||
team = 0;
|
||||
}
|
||||
|
||||
private void changed(){
|
||||
protected void changed(){
|
||||
if(entity != null){
|
||||
entity.remove();
|
||||
entity = null;
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.anuke.arc.graphics.g2d.TextureRegion;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Geometry;
|
||||
import io.anuke.arc.math.geom.Point2;
|
||||
import io.anuke.mindustry.content.Blocks;
|
||||
import io.anuke.mindustry.content.Fx;
|
||||
import io.anuke.mindustry.content.StatusEffects;
|
||||
import io.anuke.mindustry.entities.Effects.Effect;
|
||||
@@ -45,8 +46,6 @@ public class Floor extends Block{
|
||||
public Liquid liquidDrop = null;
|
||||
/** item that drops from this block, used for drills */
|
||||
public Item itemDrop = null;
|
||||
/** Whether ores generate on this block. */
|
||||
public boolean hasOres = false;
|
||||
/** whether this block can be drowned in */
|
||||
public boolean isLiquid;
|
||||
/** 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());
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
package io.anuke.mindustry.world.blocks;
|
||||
|
||||
import io.anuke.arc.collection.ObjectMap;
|
||||
import io.anuke.arc.graphics.g2d.Draw;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
public class OreBlock extends Floor{
|
||||
private static final ObjectMap<Item, ObjectMap<Block, Floor>> oreBlockMap = new ObjectMap<>();
|
||||
|
||||
public Floor base;
|
||||
|
||||
public OreBlock(Item ore, Floor base){
|
||||
super("ore-" + ore.name + "-" + base.name);
|
||||
this.localizedName = ore.localizedName() + " " + base.localizedName;
|
||||
public OreBlock(Item ore){
|
||||
super("ore-" + ore.name);
|
||||
this.localizedName = ore.localizedName();
|
||||
this.itemDrop = ore;
|
||||
this.base = base;
|
||||
this.variants = 3;
|
||||
this.edge = base.name;
|
||||
this.blendGroup = base.blendGroup;
|
||||
this.color.set(ore.color);
|
||||
|
||||
oreBlockMap.getOr(ore, ObjectMap::new).put(base, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,22 +28,5 @@ public class OreBlock extends Floor{
|
||||
@Override
|
||||
public void draw(Tile tile){
|
||||
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.Pal;
|
||||
import io.anuke.mindustry.type.Item;
|
||||
import io.anuke.mindustry.type.ItemType;
|
||||
import io.anuke.mindustry.ui.Bar;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
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) -> 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){
|
||||
return;
|
||||
@@ -259,12 +260,12 @@ public class Drill extends Block{
|
||||
}
|
||||
|
||||
public Item getDrop(Tile tile){
|
||||
return tile.floor().itemDrop;
|
||||
return tile.drop();
|
||||
}
|
||||
|
||||
public boolean isValid(Tile tile){
|
||||
if(tile == null) return false;
|
||||
Item drops = tile.floor().itemDrop;
|
||||
Item drops = tile.drop();
|
||||
return drops != null && drops.hardness <= tier;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user