enemies){
this.team = team;
this.enemies = enemies;
}
}
+
+ /** Represents a block made by this team that was destroyed somewhere on the map.
+ * This does not include deconstructed blocks.*/
+ @Struct
+ public class BrokenBlockStruct{
+ public short x, y, rotation, block;
+ }
}
diff --git a/core/src/io/anuke/mindustry/game/TypeID.java b/core/src/io/anuke/mindustry/game/TypeID.java
new file mode 100644
index 0000000000..c054f9d8e9
--- /dev/null
+++ b/core/src/io/anuke/mindustry/game/TypeID.java
@@ -0,0 +1,19 @@
+package io.anuke.mindustry.game;
+
+import io.anuke.arc.function.Supplier;
+import io.anuke.mindustry.entities.traits.TypeTrait;
+import io.anuke.mindustry.type.ContentType;
+
+public class TypeID extends MappableContent{
+ public final Supplier extends TypeTrait> constructor;
+
+ public TypeID(String name, Supplier extends TypeTrait> constructor){
+ super(name);
+ this.constructor = constructor;
+ }
+
+ @Override
+ public ContentType getContentType(){
+ return ContentType.typeid;
+ }
+}
diff --git a/core/src/io/anuke/mindustry/graphics/BlockRenderer.java b/core/src/io/anuke/mindustry/graphics/BlockRenderer.java
index 50d904682d..292052479c 100644
--- a/core/src/io/anuke/mindustry/graphics/BlockRenderer.java
+++ b/core/src/io/anuke/mindustry/graphics/BlockRenderer.java
@@ -1,22 +1,16 @@
package io.anuke.mindustry.graphics;
-import io.anuke.arc.Core;
-import io.anuke.arc.Events;
-import io.anuke.arc.collection.Array;
-import io.anuke.arc.collection.Sort;
+import io.anuke.arc.*;
+import io.anuke.arc.collection.*;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.Texture.TextureFilter;
-import io.anuke.arc.graphics.g2d.Draw;
-import io.anuke.arc.graphics.g2d.Fill;
+import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.graphics.glutils.FrameBuffer;
-import io.anuke.arc.util.Disposable;
-import io.anuke.arc.util.Tmp;
+import io.anuke.arc.util.*;
import io.anuke.mindustry.content.Blocks;
-import io.anuke.mindustry.game.EventType.TileChangeEvent;
-import io.anuke.mindustry.game.EventType.WorldLoadEvent;
+import io.anuke.mindustry.game.EventType.*;
import io.anuke.mindustry.game.Team;
-import io.anuke.mindustry.world.Block;
-import io.anuke.mindustry.world.Tile;
+import io.anuke.mindustry.world.*;
import static io.anuke.arc.Core.camera;
import static io.anuke.mindustry.Vars.*;
diff --git a/core/src/io/anuke/mindustry/graphics/Bloom.java b/core/src/io/anuke/mindustry/graphics/Bloom.java
new file mode 100644
index 0000000000..734992d8c0
--- /dev/null
+++ b/core/src/io/anuke/mindustry/graphics/Bloom.java
@@ -0,0 +1,417 @@
+package io.anuke.mindustry.graphics;
+
+import io.anuke.arc.Core;
+import io.anuke.arc.graphics.*;
+import io.anuke.arc.graphics.Pixmap.Format;
+import io.anuke.arc.graphics.VertexAttributes.Usage;
+import io.anuke.arc.graphics.glutils.FrameBuffer;
+import io.anuke.arc.graphics.glutils.Shader;
+
+/**
+ * Bloomlib allow easy but efficient way to add bloom effect as post process
+ * effect
+ *
+ * @author kalle_h
+ */
+public class Bloom{
+
+ /**
+ * To use implement bloom more like a glow. Texture alpha channel can be
+ * used as mask which part are glowing and which are not. see more info at:
+ * http://www.gamasutra.com/view/feature/2107/realtime_glow.php
+ *
+ * NOTE: need to be set before bloom instance is created. After that this
+ * does nothing.
+ */
+ public static boolean useAlphaChannelAsMask = false;
+
+ /** how many blur pass */
+ public int blurPasses = 1;
+
+ private Shader tresholdShader;
+ private Shader bloomShader;
+
+ private Mesh fullScreenQuad;
+
+ private Texture pingPongTex1;
+ private Texture pingPongTex2;
+ private Texture original;
+
+ private FrameBuffer frameBuffer;
+ private FrameBuffer pingPongBuffer1;
+ private FrameBuffer pingPongBuffer2;
+
+ private Shader blurShader;
+
+ private float bloomIntensity;
+ private float originalIntensity;
+ private float threshold;
+ private int w;
+ private int h;
+ private boolean blending = false;
+ private boolean capturing = false;
+ private float r = 0f;
+ private float g = 0f;
+ private float b = 0f;
+ private float a = 1f;
+ private boolean disposeFBO = true;
+
+ /**
+ * IMPORTANT NOTE CALL THIS WHEN RESUMING
+ */
+ public void resume(){
+ bloomShader.begin();
+ {
+ bloomShader.setUniformi("u_texture0", 0);
+ bloomShader.setUniformi("u_texture1", 1);
+ }
+ bloomShader.end();
+
+ setSize(w, h);
+ setThreshold(threshold);
+ setBloomIntesity(bloomIntensity);
+ setOriginalIntesity(originalIntensity);
+
+ original = frameBuffer.getTexture();
+ pingPongTex1 = pingPongBuffer1.getTexture();
+ pingPongTex2 = pingPongBuffer2.getTexture();
+ }
+
+ /**
+ * Initialize bloom class that capsulate original scene capturate,
+ * tresholding, gaussian blurring and blending. Default values: depth = true
+ * blending = false 32bits = true
+ */
+ public Bloom(){
+ initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4,
+ null, false, false, true);
+ }
+
+ public Bloom(boolean useBlending){
+ initialize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4,
+ null, false, useBlending, true);
+ }
+
+ /**
+ * Initialize bloom class that capsulate original scene capturate,
+ * tresholding, gaussian blurring and blending.
+ *
+ * @param FBO_W
+ * @param FBO_H how big fbo is used for bloom texture, smaller = more blur and
+ * lot faster but aliasing can be problem
+ * @param hasDepth do rendering need depth buffer
+ * @param useBlending does fbo need alpha channel and is blending enabled when final
+ * image is rendered. This allow to combine background graphics
+ * and only do blooming on certain objects param use32bitFBO does
+ * fbo use higher precision than 16bits.
+ */
+ public Bloom(int FBO_W, int FBO_H, boolean hasDepth, boolean useBlending,
+ boolean use32bitFBO){
+ initialize(FBO_W, FBO_H, null, hasDepth, useBlending, use32bitFBO);
+
+ }
+
+ /**
+ * EXPERT FUNCTIONALITY. no error checking. Use this only if you know what
+ * you are doing. Remember that bloom.capture() clear the screen so use
+ * continue instead if that is a problem.
+ *
+ * Initialize bloom class that capsulate original scene capturate,
+ * tresholding, gaussian blurring and blending.
+ *
+ * * @param sceneIsCapturedHere diposing is user responsibility.
+ *
+ * @param FBO_W
+ * @param FBO_H how big fbo is used for bloom texture, smaller = more blur and
+ * lot faster but aliasing can be problem
+ * @param useBlending does fbo need alpha channel and is blending enabled when final
+ * image is rendered. This allow to combine background graphics
+ * and only do blooming on certain objects param use32bitFBO does
+ * fbo use higher precision than 16bits.
+ */
+ public Bloom(int FBO_W, int FBO_H, FrameBuffer sceneIsCapturedHere,
+ boolean useBlending, boolean use32bitFBO){
+
+ initialize(FBO_W, FBO_H, sceneIsCapturedHere, false, useBlending,
+ use32bitFBO);
+ disposeFBO = false;
+ }
+
+ private void initialize(int FBO_W, int FBO_H, FrameBuffer fbo,
+ boolean hasDepth, boolean useBlending, boolean use32bitFBO){
+ blending = useBlending;
+ Format format = null;
+
+ if(use32bitFBO){
+ if(useBlending){
+ format = Format.RGBA8888;
+ }else{
+ format = Format.RGB888;
+ }
+
+ }else{
+ if(useBlending){
+ format = Format.RGBA4444;
+ }else{
+ format = Format.RGB565;
+ }
+ }
+ if(fbo == null){
+ frameBuffer = new FrameBuffer(format, Core.graphics.getWidth(),
+ Core.graphics.getHeight(), hasDepth);
+ }else{
+ frameBuffer = fbo;
+ }
+
+ pingPongBuffer1 = new FrameBuffer(format, FBO_W, FBO_H, false);
+
+ pingPongBuffer2 = new FrameBuffer(format, FBO_W, FBO_H, false);
+
+ original = frameBuffer.getTexture();
+ pingPongTex1 = pingPongBuffer1.getTexture();
+ pingPongTex2 = pingPongBuffer2.getTexture();
+
+ fullScreenQuad = createFullScreenQuad();
+ final String alpha = useBlending ? "alpha_" : "";
+
+ bloomShader = createShader("screenspace", alpha + "bloom");
+
+ if(useAlphaChannelAsMask){
+ tresholdShader = createShader("screenspace", "maskedtreshold");
+ }else{
+ tresholdShader = createShader("screenspace", alpha + "threshold");
+ }
+
+ blurShader = createShader("blurspace", alpha + "gaussian");
+
+ setSize(FBO_W, FBO_H);
+ setBloomIntesity(2.5f);
+ setOriginalIntesity(0.8f);
+ setThreshold(0.5f);
+
+ bloomShader.begin();
+ {
+ bloomShader.setUniformi("u_texture0", 0);
+ bloomShader.setUniformi("u_texture1", 1);
+ }
+ bloomShader.end();
+ }
+
+ /**
+ * Set clearing color for capturing buffer
+ *
+ * @param r
+ * @param g
+ * @param b
+ * @param a
+ */
+ public void setClearColor(float r, float g, float b, float a){
+ this.r = r;
+ this.g = g;
+ this.b = b;
+ this.a = a;
+ }
+
+ /**
+ * Call this before rendering scene.
+ */
+ public void capture(){
+ if(!capturing){
+ capturing = true;
+ frameBuffer.begin();
+ Core.gl.glClearColor(r, g, b, a);
+ Core.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
+
+ }
+ }
+
+ /**
+ * Pause capturing to fbo.
+ */
+ public void capturePause(){
+ if(capturing){
+ capturing = false;
+ frameBuffer.end();
+ }
+ }
+
+ /** Start capturing again after pause, no clearing is done to framebuffer */
+ public void captureContinue(){
+ if(!capturing){
+ capturing = true;
+ frameBuffer.begin();
+ }
+ }
+
+ /**
+ * Call this after scene. Renders the bloomed scene.
+ */
+ public void render(){
+ if(capturing){
+ capturing = false;
+ frameBuffer.end();
+ }
+
+ Core.gl.glDisable(GL20.GL_BLEND);
+ Core.gl.glDisable(GL20.GL_DEPTH_TEST);
+ Core.gl.glDepthMask(false);
+
+ gaussianBlur();
+
+ if(blending){
+ Core.gl.glEnable(GL20.GL_BLEND);
+ Core.gl.glBlendFunc(GL20.GL_SRC_ALPHA, GL20.GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ pingPongTex1.bind(1);
+ original.bind(0);
+ bloomShader.begin();
+ {
+ fullScreenQuad.render(bloomShader, GL20.GL_TRIANGLE_FAN);
+ }
+ bloomShader.end();
+
+ }
+
+ private void gaussianBlur(){
+
+ // cut bright areas of the picture and blit to smaller fbo
+
+ original.bind(0);
+ pingPongBuffer1.begin();
+ {
+ tresholdShader.begin();
+ {
+ // tresholdShader.setUniformi("u_texture0", 0);
+ fullScreenQuad.render(tresholdShader, GL20.GL_TRIANGLE_FAN, 0,
+ 4);
+ }
+ tresholdShader.end();
+ }
+ pingPongBuffer1.end();
+
+ for(int i = 0; i < blurPasses; i++){
+
+ pingPongTex1.bind(0);
+
+ // horizontal
+ pingPongBuffer2.begin();
+ {
+ blurShader.begin();
+ {
+ blurShader.setUniformf("dir", 1f, 0f);
+ fullScreenQuad.render(blurShader, GL20.GL_TRIANGLE_FAN, 0,
+ 4);
+ }
+ blurShader.end();
+ }
+ pingPongBuffer2.end();
+
+ pingPongTex2.bind(0);
+ // vertical
+ pingPongBuffer1.begin();
+ {
+ blurShader.begin();
+ {
+ blurShader.setUniformf("dir", 0f, 1f);
+
+ fullScreenQuad.render(blurShader, GL20.GL_TRIANGLE_FAN, 0,
+ 4);
+ }
+ blurShader.end();
+ }
+ pingPongBuffer1.end();
+ }
+ }
+
+ /**
+ * set intensity for bloom. higher mean more brightening for spots that are
+ * over threshold
+ *
+ * @param intensity multiplier for blurred texture in combining phase. must be
+ * positive.
+ */
+ public void setBloomIntesity(float intensity){
+ bloomIntensity = intensity;
+ bloomShader.begin();
+ {
+ bloomShader.setUniformf("BloomIntensity", intensity);
+ }
+ bloomShader.end();
+ }
+
+ /**
+ * set intensity for original scene. under 1 mean darkening and over 1 means
+ * lightening
+ *
+ * @param intensity multiplier for captured texture in combining phase. must be
+ * positive.
+ */
+ public void setOriginalIntesity(float intensity){
+ originalIntensity = intensity;
+ bloomShader.begin();
+ {
+ bloomShader.setUniformf("OriginalIntensity", intensity);
+ }
+ bloomShader.end();
+ }
+
+ /**
+ * Treshold for bright parts. everything under threshold is clamped to 0
+ *
+ * @param threshold must be in range 0..1
+ */
+ public void setThreshold(float threshold){
+ this.threshold = threshold;
+ tresholdShader.begin();
+ {
+ tresholdShader.setUniformf("threshold", threshold,
+ 1f / (1 - threshold));
+ }
+ tresholdShader.end();
+ }
+
+ private void setSize(int FBO_W, int FBO_H){
+ w = FBO_W;
+ h = FBO_H;
+ blurShader.begin();
+ blurShader.setUniformf("size", FBO_W, FBO_H);
+ blurShader.end();
+ }
+
+ /**
+ * Call this when application is exiting.
+ */
+ public void dispose(){
+ if(disposeFBO)
+ frameBuffer.dispose();
+
+ fullScreenQuad.dispose();
+
+ pingPongBuffer1.dispose();
+ pingPongBuffer2.dispose();
+
+ blurShader.dispose();
+ bloomShader.dispose();
+ tresholdShader.dispose();
+
+ }
+
+ private static Mesh createFullScreenQuad(){
+ float[] verts = {-1, -1, 0, 0, 1, -1, 1, 0, 1, 1, 1, 1, -1, 1, 0, 1};
+ Mesh tmpMesh = new Mesh(true, 4, 0, new VertexAttribute(
+ Usage.Position, 2, "a_position"), new VertexAttribute(
+ Usage.TextureCoordinates, 2, "a_texCoord0"));
+
+ tmpMesh.setVertices(verts);
+ return tmpMesh;
+
+ }
+
+ private static Shader createShader(String vertexName, String fragmentName){
+ String vertexShader = Core.files.internal("bloomshaders/" + vertexName + ".vertex.glsl").readString();
+ String fragmentShader = Core.files.internal("bloomshaders/" + fragmentName + ".fragment.glsl").readString();
+ return new Shader(vertexShader, fragmentShader);
+ }
+
+}
diff --git a/core/src/io/anuke/mindustry/graphics/Layer.java b/core/src/io/anuke/mindustry/graphics/Layer.java
index f84f6aa3c2..206da3d6e0 100644
--- a/core/src/io/anuke/mindustry/graphics/Layer.java
+++ b/core/src/io/anuke/mindustry/graphics/Layer.java
@@ -10,7 +10,5 @@ public enum Layer{
/** "High" blocks, like turrets. */
turret,
/** Power lasers. */
- power,
- /** Extra lasers, like healing turrets. */
- laser
+ power
}
diff --git a/core/src/io/anuke/mindustry/graphics/MenuRenderer.java b/core/src/io/anuke/mindustry/graphics/MenuRenderer.java
new file mode 100644
index 0000000000..fb5831c903
--- /dev/null
+++ b/core/src/io/anuke/mindustry/graphics/MenuRenderer.java
@@ -0,0 +1,300 @@
+package io.anuke.mindustry.graphics;
+
+import io.anuke.arc.Core;
+import io.anuke.arc.collection.Array;
+import io.anuke.arc.function.PositionConsumer;
+import io.anuke.arc.graphics.Camera;
+import io.anuke.arc.graphics.Color;
+import io.anuke.arc.graphics.g2d.*;
+import io.anuke.arc.graphics.glutils.FrameBuffer;
+import io.anuke.arc.math.*;
+import io.anuke.arc.scene.ui.layout.Unit;
+import io.anuke.arc.util.*;
+import io.anuke.arc.util.noise.RidgedPerlin;
+import io.anuke.arc.util.noise.Simplex;
+import io.anuke.mindustry.content.Blocks;
+import io.anuke.mindustry.content.UnitTypes;
+import io.anuke.mindustry.type.UnitType;
+import io.anuke.mindustry.world.*;
+import io.anuke.mindustry.world.blocks.Floor;
+import io.anuke.mindustry.world.blocks.OreBlock;
+
+import static io.anuke.mindustry.Vars.*;
+
+public class MenuRenderer implements Disposable{
+ private static final float darkness = 0.3f;
+ private final int width = !mobile ? 100 : 60, height = !mobile ? 50 : 40;
+
+ private int cacheFloor, cacheWall;
+ private Camera camera = new Camera();
+ private Matrix3 mat = new Matrix3();
+ private FrameBuffer shadows;
+ private CacheBatch batch;
+ private float time = 0f;
+ private float flyerRot = 45f;
+ private int flyers = Mathf.chance(0.2) ? Mathf.random(35) : Mathf.random(15);
+ private UnitType flyerType = Structs.select(UnitTypes.wraith, UnitTypes.wraith, UnitTypes.ghoul, UnitTypes.phantom, UnitTypes.phantom, UnitTypes.revenant);
+
+ public MenuRenderer(){
+ Time.mark();
+ generate();
+ cache();
+ Log.info("Time to generate menu: {0}", Time.elapsed());
+ }
+
+ private void generate(){
+ Tile[][] tiles = world.createTiles(width, height);
+ Array ores = content.blocks().select(b -> b instanceof OreBlock);
+ shadows = new FrameBuffer(width, height);
+ int offset = Mathf.random(100000);
+ Simplex s1 = new Simplex(offset);
+ Simplex s2 = new Simplex(offset + 1);
+ Simplex s3 = new Simplex(offset + 2);
+ RidgedPerlin rid = new RidgedPerlin(1 + offset, 1);
+ Block[] selected = Structs.select(
+ new Block[]{Blocks.sand, Blocks.sandRocks},
+ new Block[]{Blocks.shale, Blocks.shaleRocks},
+ new Block[]{Blocks.ice, Blocks.icerocks},
+ new Block[]{Blocks.sand, Blocks.sandRocks},
+ new Block[]{Blocks.shale, Blocks.shaleRocks},
+ new Block[]{Blocks.ice, Blocks.icerocks},
+ new Block[]{Blocks.moss, Blocks.sporePine}
+ );
+ Block[] selected2 = Structs.select(
+ new Block[]{Blocks.ignarock, Blocks.duneRocks},
+ new Block[]{Blocks.ignarock, Blocks.duneRocks},
+ new Block[]{Blocks.stone, Blocks.rocks},
+ new Block[]{Blocks.stone, Blocks.rocks},
+ new Block[]{Blocks.moss, Blocks.sporerocks},
+ new Block[]{Blocks.salt, Blocks.saltRocks}
+ );
+
+ Block ore1 = ores.random();
+ ores.remove(ore1);
+ Block ore2 = ores.random();
+
+ double tr1 = Mathf.random(0.65f, 0.85f);
+ double tr2 = Mathf.random(0.65f, 0.85f);
+ boolean doheat = Mathf.chance(0.25);
+ boolean tendrils = Mathf.chance(0.25);
+ boolean tech = Mathf.chance(0.25);
+ int secSize = 10;
+
+ Block floord = selected[0], walld = selected[1];
+ Block floord2 = selected2[0], walld2 = selected2[1];
+
+ for(int x = 0; x < width; x++){
+ for(int y = 0; y < height; y++){
+ Block floor = floord;
+ Block ore = Blocks.air;
+ Block wall = Blocks.air;
+
+ if(s1.octaveNoise2D(3, 0.5, 1/20.0, x, y) > 0.5){
+ wall = walld;
+ }
+
+ if(s3.octaveNoise2D(3, 0.5, 1/20.0, x, y) > 0.5){
+ floor = floord2;
+ if(wall != Blocks.air){
+ wall = walld2;
+ }
+ }
+
+ if(s2.octaveNoise2D(3, 0.3, 1/30.0, x, y) > tr1){
+ ore = ore1;
+ }
+
+ if(s2.octaveNoise2D(2, 0.2, 1/15.0, x, y+99999) > tr2){
+ ore = ore2;
+ }
+
+ if(doheat){
+ double heat = s3.octaveNoise2D(4, 0.6, 1 / 50.0, x, y + 9999);
+ double base = 0.65;
+
+ if(heat > base){
+ ore = Blocks.air;
+ wall = Blocks.air;
+ floor = Blocks.ignarock;
+
+ if(heat > base + 0.1){
+ floor = Blocks.hotrock;
+
+ if(heat > base + 0.15){
+ floor = Blocks.magmarock;
+ }
+ }
+ }
+ }
+
+ if(tech){
+ int mx = x % secSize, my = y % secSize;
+ int sclx = x / secSize, scly = y / secSize;
+ if(s1.octaveNoise2D(2, 1f / 10f, 0.5f, sclx, scly) > 0.4f && (mx == 0 || my == 0 || mx == secSize - 1 || my == secSize - 1)){
+ floor = Blocks.darkPanel3;
+ if(Mathf.dst(mx, my, secSize/2, secSize/2) > secSize/2f + 1){
+ floor = Blocks.darkPanel4;
+ }
+
+
+ if(wall != Blocks.air && Mathf.chance(0.7)){
+ wall = Blocks.darkMetal;
+ }
+ }
+ }
+
+ if(tendrils){
+ if(rid.getValue(x, y, 1f / 17f) > 0f){
+ floor = Mathf.chance(0.2) ? Blocks.sporeMoss : Blocks.moss;
+
+ if(wall != Blocks.air){
+ wall = Blocks.sporerocks;
+ }
+ }
+ }
+
+ Tile tile;
+ tiles[x][y] = (tile = new CachedTile());
+ tile.x = (short)x;
+ tile.y = (short)y;
+ tile.setFloor((Floor) floor);
+ tile.setBlock(wall);
+ tile.setOverlay(ore);
+ }
+ }
+ }
+
+ private void cache(){
+
+ //draw shadows
+ Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight());
+ shadows.beginDraw(Color.CLEAR);
+ Draw.color(Color.BLACK);
+ for(int x = 0; x < width; x++){
+ for(int y = 0; y < height; y++){
+ if(world.rawTile(x, y).block() != Blocks.air){
+ Fill.rect(x + 0.5f, y + 0.5f, 1, 1);
+ }
+ }
+ }
+ Draw.color();
+ shadows.endDraw();
+
+ SpriteBatch prev = Core.batch;
+
+ Core.batch = batch = new CacheBatch(new SpriteCache(width * height * 6, false));
+ batch.beginCache();
+
+ for(int x = 0; x < width; x++){
+ for(int y = 0; y < height; y++){
+ Tile tile = world.rawTile(x, y);
+ tile.floor().draw(tile);
+ }
+ }
+
+ for(int x = 0; x < width; x++){
+ for(int y = 0; y < height; y++){
+ Tile tile = world.rawTile(x, y);
+ if(tile.overlay() != Blocks.air){
+ tile.overlay().draw(tile);
+ }
+ }
+ }
+
+ cacheFloor = batch.endCache();
+ batch.beginCache();
+
+ for(int x = 0; x < width; x++){
+ for(int y = 0; y < height; y++){
+ Tile tile = world.rawTile(x, y);
+ if(tile.block() != Blocks.air){
+ tile.block().draw(tile);
+ }
+ }
+ }
+
+ cacheWall = batch.endCache();
+
+ Core.batch = prev;
+ }
+
+ public void render(){
+ time += Time.delta();
+ float scaling = Math.max(Unit.dp.scl(4f), Math.max(Core.graphics.getWidth() / ((width - 1f) * tilesize), Core.graphics.getHeight() / ((height - 1f) * tilesize)));
+ camera.position.set(width * tilesize / 2f, height * tilesize / 2f);
+ camera.resize(Core.graphics.getWidth() / scaling,
+ Core.graphics.getHeight() / scaling);
+
+ mat.set(Draw.proj());
+ Draw.flush();
+ Draw.proj(camera.projection());
+ batch.setProjection(camera.projection());
+ batch.beginDraw();
+ batch.drawCache(cacheFloor);
+ batch.endDraw();
+ Draw.rect(Draw.wrap(shadows.getTexture()),
+ width * tilesize / 2f - 4f, height * tilesize / 2f - 4f,
+ width * tilesize, -height * tilesize);
+ Draw.flush();
+ batch.beginDraw();
+ batch.drawCache(cacheWall);
+ batch.endDraw();
+
+ drawFlyers();
+
+ Draw.proj(mat);
+ Draw.color(0f, 0f, 0f, darkness);
+ Fill.crect(0, 0, Core.graphics.getWidth(), Core.graphics.getHeight());
+ Draw.color();
+ }
+
+ private void drawFlyers(){
+ Draw.color(0f, 0f, 0f, 0.4f);
+
+ float size = Math.max(flyerType.iconRegion.getWidth(), flyerType.iconRegion.getHeight()) * Draw.scl * 1.6f;
+
+ flyers((x, y) -> {
+ Draw.rect(flyerType.region, x - 12f, y - 13f, flyerRot - 90);
+ });
+
+ flyers((x, y) -> {
+ Draw.rect("circle-shadow", x, y, size, size);
+ });
+ Draw.color();
+
+ flyers((x, y) -> {
+ float engineOffset = flyerType.engineOffset, engineSize = flyerType.engineSize, rotation = flyerRot;
+
+ Draw.color(Pal.engine);
+ Fill.circle(x + Angles.trnsx(rotation + 180, engineOffset), y + Angles.trnsy(rotation + 180, engineOffset),
+ engineSize + Mathf.absin(Time.time(), 2f, engineSize / 4f));
+
+ Draw.color(Color.WHITE);
+ Fill.circle(x + Angles.trnsx(rotation + 180, engineOffset - 1f), y + Angles.trnsy(rotation + 180, engineOffset - 1f),
+ (engineSize + Mathf.absin(Time.time(), 2f, engineSize / 4f)) / 2f);
+ Draw.color();
+
+ Draw.rect(flyerType.region, x, y, flyerRot - 90);
+ });
+ }
+
+ private void flyers(PositionConsumer cons){
+ float tw = width * tilesize * 1f + tilesize;
+ float th = height * tilesize * 1f + tilesize;
+ float range = 500f;
+ float offset = -100f;
+
+ for(int i = 0; i < flyers; i++){
+ Tmp.v1.trns(flyerRot, time * (2f + flyerType.speed));
+
+ cons.accept((Mathf.randomSeedRange(i, range) + Tmp.v1.x + Mathf.absin(time + Mathf.randomSeedRange(i + 2, 500), 10f, 3.4f) + offset) % (tw + Mathf.randomSeed(i + 5, 0, 500)),
+ (Mathf.randomSeedRange(i + 1, range) + Tmp.v1.y + Mathf.absin(time + Mathf.randomSeedRange(i + 3, 500), 10f, 3.4f) + offset) % th);
+ }
+ }
+
+ @Override
+ public void dispose(){
+ batch.dispose();
+ shadows.dispose();
+ }
+}
diff --git a/core/src/io/anuke/mindustry/graphics/MinimapRenderer.java b/core/src/io/anuke/mindustry/graphics/MinimapRenderer.java
index 9777f4e833..85b390d939 100644
--- a/core/src/io/anuke/mindustry/graphics/MinimapRenderer.java
+++ b/core/src/io/anuke/mindustry/graphics/MinimapRenderer.java
@@ -1,23 +1,20 @@
package io.anuke.mindustry.graphics;
-import io.anuke.arc.Core;
-import io.anuke.arc.Events;
-import io.anuke.arc.collection.Array;
+import io.anuke.arc.*;
+import io.anuke.arc.collection.*;
import io.anuke.arc.graphics.*;
-import io.anuke.arc.graphics.Pixmap.Format;
+import io.anuke.arc.graphics.Pixmap.*;
import io.anuke.arc.graphics.g2d.*;
-import io.anuke.arc.math.Mathf;
-import io.anuke.arc.math.geom.Rectangle;
-import io.anuke.arc.util.Disposable;
-import io.anuke.mindustry.entities.Units;
-import io.anuke.mindustry.entities.type.Unit;
-import io.anuke.mindustry.game.EventType.TileChangeEvent;
-import io.anuke.mindustry.game.EventType.WorldLoadEvent;
-import io.anuke.mindustry.io.MapIO;
-import io.anuke.mindustry.world.Tile;
+import io.anuke.arc.math.*;
+import io.anuke.arc.math.geom.*;
+import io.anuke.arc.util.*;
+import io.anuke.mindustry.entities.*;
+import io.anuke.mindustry.entities.type.*;
+import io.anuke.mindustry.game.EventType.*;
+import io.anuke.mindustry.io.*;
+import io.anuke.mindustry.world.*;
-import static io.anuke.mindustry.Vars.tilesize;
-import static io.anuke.mindustry.Vars.world;
+import static io.anuke.mindustry.Vars.*;
public class MinimapRenderer implements Disposable{
private static final float baseSize = 16f;
@@ -129,8 +126,9 @@ public class MinimapRenderer implements Disposable{
}
private int colorFor(Tile tile){
+ if(tile == null) return 0;
tile = tile.link();
- return MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam());
+ return Tmp.c1.set(MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam())).mul(tile.block().cacheLayer == CacheLayer.walls ? 1f - tile.rotation() / 4f : 1f).rgba();
}
@Override
diff --git a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java
index 494099befb..bccb17ebb7 100644
--- a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java
+++ b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java
@@ -22,6 +22,7 @@ import static io.anuke.mindustry.Vars.*;
public class OverlayRenderer{
private static final float indicatorLength = 14f;
+ private static final float spawnerMargin = tilesize*11f;
private static final Rectangle rect = new Rectangle();
private float buildFadeTime;
@@ -92,6 +93,16 @@ public class OverlayRenderer{
}
}
+ Lines.stroke(2f);
+ Draw.color(Color.GRAY, Color.LIGHT_GRAY, Mathf.absin(Time.time(), 8f, 1f));
+
+ for(Tile tile : world.spawner.getGroundSpawns()){
+ if(tile.withinDst(player.x, player.y, state.rules.dropZoneRadius + spawnerMargin)){
+ Draw.alpha(Mathf.clamp(1f - (player.dst(tile) - state.rules.dropZoneRadius) / spawnerMargin));
+ Lines.dashCircle(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius);
+ }
+ }
+
Draw.reset();
//draw selected block bars and info
diff --git a/core/src/io/anuke/mindustry/graphics/Pal.java b/core/src/io/anuke/mindustry/graphics/Pal.java
index aa4ca6f7e7..40c4867abc 100644
--- a/core/src/io/anuke/mindustry/graphics/Pal.java
+++ b/core/src/io/anuke/mindustry/graphics/Pal.java
@@ -11,6 +11,8 @@ public class Pal{
bulletYellow = Color.valueOf("ffeec9"),
bulletYellowBack = Color.valueOf("f9c87a"),
+ darkMetal = Color.valueOf("6e7080"),
+
missileYellow = Color.valueOf("ffd2ae"),
missileYellowBack = Color.valueOf("e58956"),
@@ -50,10 +52,11 @@ public class Pal{
bar = Color.SLATE,
accent = Color.valueOf("ffd37f"),
stat = Color.valueOf("ffd37f"),
- locked = Color.valueOf("989aa4"),
+ gray = Color.valueOf("454545"),
accentBack = Color.valueOf("d4816b"),
place = Color.valueOf("6335f8"),
remove = Color.valueOf("e55454"),
+ noplace = Color.valueOf("ffa697"),
removeBack = Color.valueOf("a73e3e"),
placeRotate = accent,
breakInvalid = Color.valueOf("d44b3d"),
diff --git a/core/src/io/anuke/mindustry/graphics/Pixelator.java b/core/src/io/anuke/mindustry/graphics/Pixelator.java
index e8a9e0d2f3..8cc252eebd 100644
--- a/core/src/io/anuke/mindustry/graphics/Pixelator.java
+++ b/core/src/io/anuke/mindustry/graphics/Pixelator.java
@@ -55,7 +55,7 @@ public class Pixelator implements Disposable{
Draw.rect(Draw.wrap(buffer.getTexture()), Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height);
Draw.blend();
- renderer.drawAndInterpolate(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName);
+ renderer.draw(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName);
Core.camera.position.set(px, py);
Core.settings.put("animatedwater", hadWater);
diff --git a/core/src/io/anuke/mindustry/graphics/Shapes.java b/core/src/io/anuke/mindustry/graphics/Shapes.java
index 7e629d347b..3ab13f8c74 100644
--- a/core/src/io/anuke/mindustry/graphics/Shapes.java
+++ b/core/src/io/anuke/mindustry/graphics/Shapes.java
@@ -7,24 +7,22 @@ import io.anuke.arc.util.Tmp;
public class Shapes{
- public static void laser(String line, String edge, float x, float y, float x2, float y2, float scale){
+ public static void laser(TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2, float scale){
laser(line, edge, x, y, x2, y2, Mathf.angle(x2 - x, y2 - y), scale);
}
- public static void laser(String line, String edge, float x, float y, float x2, float y2){
+ public static void laser(TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2){
laser(line, edge, x, y, x2, y2, Mathf.angle(x2 - x, y2 - y), 1f);
}
- public static void laser(String line, String edge, float x, float y, float x2, float y2, float rotation, float scale){
- TextureRegion region = Core.atlas.find(edge);
-
+ public static void laser(TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2, float rotation, float scale){
Tmp.v1.trns(rotation, 8f * scale * Draw.scl);
- Draw.rect(Core.atlas.find(edge), x, y, region.getWidth() * scale * Draw.scl, region.getHeight() * scale * Draw.scl, rotation + 180);
- Draw.rect(Core.atlas.find(edge), x2, y2, region.getWidth() * scale * Draw.scl, region.getHeight() * scale * Draw.scl, rotation);
+ Draw.rect(edge, x, y, edge.getWidth() * scale * Draw.scl, edge.getHeight() * scale * Draw.scl, rotation + 180);
+ Draw.rect(edge, x2, y2, edge.getWidth() * scale * Draw.scl, edge.getHeight() * scale * Draw.scl, rotation);
Lines.stroke(12f * scale);
- Lines.line(Core.atlas.find(line), x + Tmp.v1.x, y + Tmp.v1.y, x2 - Tmp.v1.x, y2 - Tmp.v1.y, CapStyle.none, 0f);
+ Lines.line(line, x + Tmp.v1.x, y + Tmp.v1.y, x2 - Tmp.v1.x, y2 - Tmp.v1.y, CapStyle.none, 0f);
Lines.stroke(1f);
}
diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java
index 2198f002ef..87b8a26a32 100644
--- a/core/src/io/anuke/mindustry/input/DesktopInput.java
+++ b/core/src/io/anuke/mindustry/input/DesktopInput.java
@@ -141,7 +141,7 @@ public class DesktopInput extends InputHandler{
if(state.is(State.menu) || Core.scene.hasDialog()) return;
- //zoom and rotate things
+ //zoom things
if(Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && (Core.input.keyDown(Binding.zoom_hold))){
renderer.scaleCamera(Core.input.axisTap(Binding.zoom));
}
@@ -214,7 +214,7 @@ public class DesktopInput extends InputHandler{
mode = placing;
}else if(selected != null){
//only begin shooting if there's no cursor event
- if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.getPlaceQueue().size == 0 && !droppingItem &&
+ if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.buildQueue().size == 0 && !droppingItem &&
!tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){
player.isShooting = true;
}
@@ -222,7 +222,7 @@ public class DesktopInput extends InputHandler{
player.isShooting = true;
}
}else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) &&
- !(player.getCurrentRequest() != null && player.getCurrentRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){
+ !(player.buildRequest() != null && player.buildRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){
if(block == null){
player.clearBuilding();
}
diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java
index f69c9331a6..fae86625e8 100644
--- a/core/src/io/anuke/mindustry/input/InputHandler.java
+++ b/core/src/io/anuke/mindustry/input/InputHandler.java
@@ -42,7 +42,7 @@ public abstract class InputHandler implements InputProcessor{
/** Distance on the back from where items originate. */
final static float backTrns = 3f;
- public final OverlayFragment frag = new OverlayFragment(this);
+ public final OverlayFragment frag = new OverlayFragment();
public Block block;
public int rotation;
@@ -324,7 +324,7 @@ public abstract class InputHandler implements InputProcessor{
}
public boolean validBreak(int x, int y){
- return Build.validBreak(player.getTeam(), x, y) && Mathf.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance;
+ return Build.validBreak(player.getTeam(), x, y);
}
public void placeBlock(int x, int y, Block block, int rotation){
diff --git a/core/src/io/anuke/mindustry/input/MobileInput.java b/core/src/io/anuke/mindustry/input/MobileInput.java
index a4b741e1a8..0bdbbbc7b3 100644
--- a/core/src/io/anuke/mindustry/input/MobileInput.java
+++ b/core/src/io/anuke/mindustry/input/MobileInput.java
@@ -20,6 +20,7 @@ import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Effects;
import io.anuke.mindustry.entities.Units;
+import io.anuke.mindustry.entities.traits.BuilderTrait.*;
import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.entities.type.Unit;
@@ -120,6 +121,19 @@ public class MobileInput extends InputHandler implements GestureListener{
return true;
}
}
+
+ for(BuildRequest req : player.buildQueue()){
+ Tile other = world.tile(req.x, req.y);
+
+ if(other == null || req.breaking) continue;
+
+ r1.setSize(req.block.size * tilesize);
+ r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset());
+
+ if(r2.overlaps(r1)){
+ return true;
+ }
+ }
return false;
}
@@ -261,11 +275,11 @@ public class MobileInput extends InputHandler implements GestureListener{
@Override
public void buildUI(Table table){
- table.addImage("blank").color(Pal.accent).height(3f).colspan(4).growX();
+ table.addImage("whiteui").color(Pal.gray).height(4f).colspan(4).growX();
table.row();
table.left().margin(0f).defaults().size(48f);
- table.addImageButton("icon-break", "clear-toggle-partial", 16 * 2f, () -> {
+ table.addImageButton("icon-break-small", "clear-toggle-partial", iconsizesmall, () -> {
mode = mode == breaking ? block == null ? none : placing : breaking;
lastBlock = block;
if(mode == breaking){
@@ -274,17 +288,17 @@ public class MobileInput extends InputHandler implements GestureListener{
}).update(l -> l.setChecked(mode == breaking));
//diagonal swap button
- table.addImageButton("icon-diagonal", "clear-toggle-partial", 16 * 2f, () -> {
+ table.addImageButton("icon-diagonal-small", "clear-toggle-partial", iconsizesmall, () -> {
Core.settings.put("swapdiagonal", !Core.settings.getBool("swapdiagonal"));
Core.settings.save();
}).update(l -> l.setChecked(Core.settings.getBool("swapdiagonal")));
//rotate button
- table.addImageButton("icon-arrow", "clear-partial", 16 * 2f, () -> rotation = Mathf.mod(rotation + 1, 4))
+ table.addImageButton("icon-arrow-small", "clear-partial", iconsizesmall, () -> rotation = Mathf.mod(rotation + 1, 4))
.update(i -> i.getImage().setRotationOrigin(rotation * 90, Align.center)).visible(() -> block != null && block.rotate);
//confirm button
- table.addImageButton("icon-check", "clear-partial", 16 * 2f, () -> {
+ table.addImageButton("icon-check-small", "clear-partial", iconsizesmall, () -> {
for(PlaceRequest request : selection){
Tile tile = request.tile();
@@ -475,7 +489,10 @@ public class MobileInput extends InputHandler implements GestureListener{
//call tap events
if(pointer == 0 && !selecting && mode == none){
- tryTapPlayer(worldx, worldy);
+ if(!tryTapPlayer(worldx, worldy) && Core.settings.getBool("keyboard")){
+ //shoot on touch down when in keyboard mode
+ player.isShooting = true;
+ }
}
return false;
@@ -598,6 +615,27 @@ public class MobileInput extends InputHandler implements GestureListener{
mode = none;
}
+ //zoom things
+ if(Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && (Core.input.keyDown(Binding.zoom_hold))){
+ renderer.scaleCamera(Core.input.axisTap(Binding.zoom));
+ }
+
+ if(!Core.settings.getBool("keyboard")){
+ //move camera around
+ float camSpeed = 6f;
+ Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta() * camSpeed));
+ }
+
+ if(Core.settings.getBool("keyboard")){
+ if(Core.input.keyRelease(Binding.select)){
+ player.isShooting = false;
+ }
+
+ if(player.isShooting && !canShoot()){
+ player.isShooting = false;
+ }
+ }
+
//reset state when not placing
if(mode == none){
selecting = false;
@@ -678,7 +716,7 @@ public class MobileInput extends InputHandler implements GestureListener{
@Override
public boolean pan(float x, float y, float deltaX, float deltaY){
- if(Core.scene.hasDialog()) return false;
+ if(Core.scene.hasDialog() || Core.settings.getBool("keyboard")) return false;
float scale = Core.camera.width / Core.graphics.getWidth();
deltaX *= scale;
@@ -723,6 +761,7 @@ public class MobileInput extends InputHandler implements GestureListener{
@Override
public boolean zoom(float initialDistance, float distance){
+ if(Core.settings.getBool("keyboard")) return false;
if(lastDistance == -1) lastDistance = initialDistance;
float amount = (Mathf.sign(distance > lastDistance) * 0.04f) * Time.delta();
diff --git a/core/src/io/anuke/mindustry/io/Changelogs.java b/core/src/io/anuke/mindustry/io/Changelogs.java
deleted file mode 100644
index 33d888be47..0000000000
--- a/core/src/io/anuke/mindustry/io/Changelogs.java
+++ /dev/null
@@ -1,54 +0,0 @@
-package io.anuke.mindustry.io;
-
-import io.anuke.arc.collection.Array;
-import io.anuke.arc.function.Consumer;
-import io.anuke.arc.util.serialization.JsonReader;
-import io.anuke.arc.util.serialization.JsonValue;
-import io.anuke.mindustry.net.Net;
-
-import static io.anuke.mindustry.Vars.releasesURL;
-
-public class Changelogs{
-
- public static void getChangelog(Consumer> success, Consumer fail){
- Net.http(releasesURL, "GET", result -> {
- JsonReader reader = new JsonReader();
- JsonValue value = reader.parse(result).child;
- Array out = new Array<>();
-
- while(value != null){
- String name = value.getString("name");
- String description = value.getString("body").replace("\r", "");
- int id = value.getInt("id");
- int build = Integer.parseInt(value.getString("tag_name").substring(1));
- out.add(new VersionInfo(name, description, id, build, value.getString("published_at")));
- value = value.next;
- }
-
- success.accept(out);
- }, fail);
- }
-
- public static class VersionInfo{
- public final String name, description, date;
- public final int id, build;
-
- public VersionInfo(String name, String description, int id, int build, String date){
- this.name = name;
- this.description = description;
- this.id = id;
- this.build = build;
- this.date = date;
- }
-
- @Override
- public String toString(){
- return "VersionInfo{" +
- "name='" + name + '\'' +
- ", description='" + description + '\'' +
- ", id=" + id +
- ", build=" + build +
- '}';
- }
- }
-}
diff --git a/core/src/io/anuke/mindustry/io/JsonIO.java b/core/src/io/anuke/mindustry/io/JsonIO.java
index 3c1ce9520c..6cea5fb1c1 100644
--- a/core/src/io/anuke/mindustry/io/JsonIO.java
+++ b/core/src/io/anuke/mindustry/io/JsonIO.java
@@ -1,17 +1,19 @@
package io.anuke.mindustry.io;
-import io.anuke.arc.util.serialization.Json;
-import io.anuke.arc.util.serialization.JsonValue;
-import io.anuke.mindustry.Vars;
-import io.anuke.mindustry.game.Rules;
-import io.anuke.mindustry.game.SpawnGroup;
+import io.anuke.arc.collection.*;
+import io.anuke.arc.util.serialization.*;
+import io.anuke.mindustry.*;
+import io.anuke.mindustry.game.*;
+import io.anuke.mindustry.game.Teams.*;
import io.anuke.mindustry.type.*;
+import io.anuke.mindustry.world.*;
@SuppressWarnings("unchecked")
public class JsonIO{
private static Json json = new Json(){{
setIgnoreUnknownFields(true);
setElementType(Rules.class, "spawns", SpawnGroup.class);
+ setElementType(Rules.class, "loadout", ItemStack.class);
setSerializer(Zone.class, new Serializer(){
@Override
@@ -36,6 +38,70 @@ public class JsonIO{
return Vars.content.getByName(ContentType.item, jsonData.asString());
}
});
+
+ //TODO extremely hacky and disgusting
+ for(Block block : Vars.content.blocks()){
+ Class type = block.getClass();
+ if(type.isAnonymousClass()) type = type.getSuperclass();
+
+ setSerializer(type, new Serializer(){
+ @Override
+ public void write(Json json, Block object, Class knownType){
+ json.writeValue(object.name);
+ }
+
+ @Override
+ public Block read(Json json, JsonValue jsonData, Class type){
+ return Vars.content.getByName(ContentType.block, jsonData.asString());
+ }
+ });
+ }
+
+ setSerializer(Block.class, new Serializer(){
+ @Override
+ public void write(Json json, Block object, Class knownType){
+ json.writeValue(object.name);
+ }
+
+ @Override
+ public Block read(Json json, JsonValue jsonData, Class type){
+ return Vars.content.getByName(ContentType.block, jsonData.asString());
+ }
+ });
+
+ setSerializer(TeamData.class, new Serializer(){
+ @Override
+ public void write(Json json, TeamData object, Class knownType){
+ json.writeObjectStart();
+ json.writeValue("brokenBlocks", object.brokenBlocks.toArray());
+ json.writeValue("team", object.team.ordinal());
+ json.writeObjectEnd();
+ }
+
+ @Override
+ public TeamData read(Json json, JsonValue jsonData, Class type){
+ long[] blocks = jsonData.get("brokenBlocks").asLongArray();
+ Team team = Team.all[jsonData.getInt("team", 0)];
+ TeamData out = new TeamData(team, EnumSet.of(new Team[]{}));
+ out.brokenBlocks = new LongQueue(blocks);
+ return out;
+ }
+ });
+
+ setSerializer(ItemStack.class, new Serializer(){
+ @Override
+ public void write(Json json, ItemStack object, Class knownType){
+ json.writeObjectStart();
+ json.writeValue("item", object.item);
+ json.writeValue("amount", object.amount);
+ json.writeObjectEnd();
+ }
+
+ @Override
+ public ItemStack read(Json json, JsonValue jsonData, Class type){
+ return new ItemStack(json.getSerializer(Item.class).read(json, jsonData.get("item"), Item.class), jsonData.getInt("amount"));
+ }
+ });
}};
public static String write(Object object){
diff --git a/core/src/io/anuke/mindustry/io/MapIO.java b/core/src/io/anuke/mindustry/io/MapIO.java
index 8daf1a7e2a..066d87d488 100644
--- a/core/src/io/anuke/mindustry/io/MapIO.java
+++ b/core/src/io/anuke/mindustry/io/MapIO.java
@@ -1,20 +1,18 @@
package io.anuke.mindustry.io;
-import io.anuke.arc.collection.StringMap;
-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.util.io.CounterInputStream;
-import io.anuke.mindustry.content.Blocks;
-import io.anuke.mindustry.game.Team;
-import io.anuke.mindustry.game.Version;
-import io.anuke.mindustry.maps.Map;
+import io.anuke.arc.collection.*;
+import io.anuke.arc.files.*;
+import io.anuke.arc.graphics.*;
+import io.anuke.arc.graphics.Pixmap.*;
+import io.anuke.arc.util.io.*;
+import io.anuke.mindustry.content.*;
+import io.anuke.mindustry.game.*;
+import io.anuke.mindustry.maps.*;
import io.anuke.mindustry.world.*;
-import io.anuke.mindustry.world.blocks.storage.CoreBlock;
+import io.anuke.mindustry.world.blocks.storage.*;
import java.io.*;
-import java.util.zip.InflaterInputStream;
+import java.util.zip.*;
import static io.anuke.mindustry.Vars.*;
@@ -64,9 +62,8 @@ public class MapIO{
}
public static Pixmap generatePreview(Map map) throws IOException{
- //by default, it does not have an enemy core or any other cores
- map.tags.put("enemycore", "false");
- map.tags.put("othercore", "false");
+ map.spawns = 0;
+ map.teams.clear();
try(InputStream is = new InflaterInputStream(map.file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){
SaveIO.readHeader(stream);
@@ -93,15 +90,7 @@ public class MapIO{
public void setTeam(Team team){
super.setTeam(team);
if(block instanceof CoreBlock){
- if(team != defaultTeam){
- //map must have other team's cores
- map.tags.put("othercore", "true");
- }
-
- if(team == waveTeam){
- //map must have default enemy team's core
- map.tags.put("enemycore", "true");
- }
+ map.teams.add(team.ordinal());
}
}
};
@@ -127,6 +116,9 @@ public class MapIO{
}else{
floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(content.block(floorID), Blocks.air, Blocks.air, Team.none));
}
+ if(content.block(overlayID) == Blocks.spawn){
+ map.spawns ++;
+ }
return tile;
}
}));
diff --git a/core/src/io/anuke/mindustry/io/SaveFileReader.java b/core/src/io/anuke/mindustry/io/SaveFileReader.java
index 8384aca50e..a969776896 100644
--- a/core/src/io/anuke/mindustry/io/SaveFileReader.java
+++ b/core/src/io/anuke/mindustry/io/SaveFileReader.java
@@ -14,7 +14,9 @@ public abstract class SaveFileReader{
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput);
protected final ReusableByteOutStream byteOutputSmall = new ReusableByteOutStream();
protected final DataOutputStream dataBytesSmall = new DataOutputStream(byteOutputSmall);
- protected final ObjectMap fallback = ObjectMap.of();
+ protected final ObjectMap fallback = ObjectMap.of(
+
+ );
protected void region(String name, DataInput stream, CounterInputStream counter, IORunner cons) throws IOException{
counter.resetCount();
diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java
index 916c4ad414..cb79c0222d 100644
--- a/core/src/io/anuke/mindustry/io/SaveIO.java
+++ b/core/src/io/anuke/mindustry/io/SaveIO.java
@@ -6,6 +6,7 @@ import io.anuke.arc.util.io.CounterInputStream;
import io.anuke.arc.util.io.FastDeflaterOutputStream;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.io.versions.Save1;
+import io.anuke.mindustry.io.versions.Save2;
import io.anuke.mindustry.world.WorldContext;
import java.io.*;
@@ -18,7 +19,7 @@ public class SaveIO{
/** Format header. This is the string 'MSAV' in ASCII. */
public static final byte[] header = {77, 83, 65, 86};
public static final IntMap versions = new IntMap<>();
- public static final Array versionArray = Array.with(new Save1());
+ public static final Array versionArray = Array.with(new Save1(), new Save2());
static{
for(SaveVersion version : versionArray){
diff --git a/core/src/io/anuke/mindustry/io/SaveVersion.java b/core/src/io/anuke/mindustry/io/SaveVersion.java
index 1071fc9b08..b4f700a582 100644
--- a/core/src/io/anuke/mindustry/io/SaveVersion.java
+++ b/core/src/io/anuke/mindustry/io/SaveVersion.java
@@ -1,15 +1,14 @@
package io.anuke.mindustry.io;
-import io.anuke.arc.collection.Array;
-import io.anuke.arc.collection.StringMap;
-import io.anuke.arc.util.Time;
-import io.anuke.arc.util.io.CounterInputStream;
-import io.anuke.mindustry.entities.Entities;
-import io.anuke.mindustry.entities.EntityGroup;
+import io.anuke.arc.collection.*;
+import io.anuke.arc.util.*;
+import io.anuke.arc.util.io.*;
+import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.traits.*;
import io.anuke.mindustry.game.*;
-import io.anuke.mindustry.maps.Map;
-import io.anuke.mindustry.type.ContentType;
+import io.anuke.mindustry.gen.*;
+import io.anuke.mindustry.maps.*;
+import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*;
import java.io.*;
@@ -19,6 +18,9 @@ import static io.anuke.mindustry.Vars.*;
public abstract class SaveVersion extends SaveFileReader{
public final int version;
+ //HACK stores the last read build of the save file, valid after read meta call
+ protected int lastReadBuild;
+
public SaveVersion(int version){
this.version = version;
}
@@ -76,7 +78,9 @@ public abstract class SaveVersion extends SaveFileReader{
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
state.stats = JsonIO.read(Stats.class, map.get("stats", "{}"));
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
- if(state.rules.spawns.isEmpty()) state.rules.spawns = DefaultWaves.get();
+ if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get();
+ lastReadBuild = map.getInt("build", -1);
+
Map worldmap = world.maps.byName(map.get("mapname", "\\\\\\"));
world.setMap(worldmap == null ? new Map(StringMap.of(
"name", map.get("mapname", "Unknown"),
@@ -92,13 +96,13 @@ public abstract class SaveVersion extends SaveFileReader{
//floor + overlay
for(int i = 0; i < world.width() * world.height(); i++){
- Tile tile = world.tile(i % world.width(), i / world.width());
+ Tile tile = world.rawTile(i % world.width(), i / world.width());
stream.writeShort(tile.floorID());
stream.writeShort(tile.overlayID());
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());
+ Tile nextTile = world.rawTile(j % world.width(), j / world.width());
if(nextTile.floorID() != tile.floorID() || nextTile.overlayID() != tile.overlayID()){
break;
@@ -113,7 +117,7 @@ public abstract class SaveVersion extends SaveFileReader{
//blocks
for(int i = 0; i < world.width() * world.height(); i++){
- Tile tile = world.tile(i % world.width(), i / world.width());
+ Tile tile = world.rawTile(i % world.width(), i / world.width());
stream.writeShort(tile.blockID());
if(tile.entity != null){
@@ -126,7 +130,7 @@ public abstract class SaveVersion extends SaveFileReader{
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());
+ Tile nextTile = world.rawTile(j % world.width(), j / world.width());
if(nextTile.blockID() != tile.blockID()){
break;
@@ -220,7 +224,7 @@ public abstract class SaveVersion extends SaveFileReader{
SaveTrait save = (SaveTrait)entity;
//each entity is a separate chunk.
writeChunk(stream, true, out -> {
- out.writeByte(save.getTypeID());
+ out.writeByte(save.getTypeID().id);
out.writeByte(save.version());
save.writeSave(out);
});
@@ -239,7 +243,7 @@ public abstract class SaveVersion extends SaveFileReader{
readChunk(stream, true, in -> {
byte typeid = in.readByte();
byte version = in.readByte();
- SaveTrait trait = (SaveTrait)TypeTrait.getTypeByID(typeid).get();
+ SaveTrait trait = (SaveTrait)content.getByID(ContentType.typeid, typeid).constructor.get();
trait.readSave(in, version);
});
}
@@ -247,7 +251,6 @@ public abstract class SaveVersion extends SaveFileReader{
}
public void readContentHeader(DataInput stream) throws IOException{
-
byte mapped = stream.readByte();
MappableContent[][] map = new MappableContent[ContentType.values().length][0];
@@ -287,4 +290,17 @@ public abstract class SaveVersion extends SaveFileReader{
}
}
}
+
+ /** sometimes it's necessary to remap IDs after the content header is read.*/
+ public void remapContent(){
+ for(Team team : Team.all){
+ if(state.teams.isActive(team)){
+ LongQueue queue = state.teams.get(team).brokenBlocks;
+ for(int i = 0; i < queue.size; i++){
+ //remap broken block IDs
+ queue.set(i, BrokenBlock.block(queue.get(i), content.block(BrokenBlock.block(queue.get(i))).id));
+ }
+ }
+ }
+ }
}
diff --git a/core/src/io/anuke/mindustry/io/TypeIO.java b/core/src/io/anuke/mindustry/io/TypeIO.java
index c3dc0be2d3..97f92584f2 100644
--- a/core/src/io/anuke/mindustry/io/TypeIO.java
+++ b/core/src/io/anuke/mindustry/io/TypeIO.java
@@ -86,15 +86,15 @@ public class TypeIO{
@WriteClass(BaseUnit.class)
public static void writeBaseUnit(ByteBuffer buffer, BaseUnit unit){
- buffer.put((byte)unitGroups[unit.getTeam().ordinal()].getID());
+ buffer.put((byte)unit.getTeam().ordinal());
buffer.putInt(unit.getID());
}
@ReadClass(BaseUnit.class)
- public static BaseUnit writeBaseUnit(ByteBuffer buffer){
- byte gid = buffer.get();
+ public static BaseUnit readBaseUnit(ByteBuffer buffer){
+ byte tid = buffer.get();
int id = buffer.getInt();
- return (BaseUnit)Entities.getGroup(gid).getByID(id);
+ return unitGroups[tid].getByID(id);
}
@WriteClass(Tile.class)
@@ -139,6 +139,10 @@ public class TypeIO{
int position = buffer.getInt();
BuildRequest currentRequest;
+ if(world.tile(position) == null){
+ continue;
+ }
+
if(type == 1){ //remove
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position));
}else{ //place
@@ -193,6 +197,16 @@ public class TypeIO{
return Effects.getEffect(buffer.getShort());
}
+ @WriteClass(UnitType.class)
+ public static void writeUnitType(ByteBuffer buffer, UnitType effect){
+ buffer.putShort(effect.id);
+ }
+
+ @ReadClass(UnitType.class)
+ public static UnitType readUnitType(ByteBuffer buffer){
+ return content.getByID(ContentType.unit, buffer.getShort());
+ }
+
@WriteClass(Color.class)
public static void writeColor(ByteBuffer buffer, Color color){
buffer.putInt(Color.rgba8888(color));
diff --git a/core/src/io/anuke/mindustry/io/versions/LegacyTypeTable.java b/core/src/io/anuke/mindustry/io/versions/LegacyTypeTable.java
new file mode 100644
index 0000000000..6205313247
--- /dev/null
+++ b/core/src/io/anuke/mindustry/io/versions/LegacyTypeTable.java
@@ -0,0 +1,129 @@
+package io.anuke.mindustry.io.versions;
+
+import io.anuke.arc.function.Supplier;
+import io.anuke.mindustry.entities.bullet.Bullet;
+import io.anuke.mindustry.entities.effect.*;
+import io.anuke.mindustry.entities.type.Player;
+import io.anuke.mindustry.entities.type.base.*;
+
+/*
+Latest data: [build 81]
+
+0 = Player
+1 = Fire
+2 = Puddle
+3 = Draug
+4 = Spirit
+5 = Phantom
+6 = Dagger
+7 = Crawler
+8 = Titan
+9 = Fortress
+10 = Eruptor
+11 = Wraith
+12 = Ghoul
+13 = Revenant
+
+Before removal of lightining/bullet: [build 80]
+
+0 = Player
+1 = Fire
+2 = Puddle
+3 = Bullet
+4 = Lightning
+5 = Draug
+6 = Spirit
+7 = Phantom
+8 = Dagger
+9 = Crawler
+10 = Titan
+11 = Fortress
+12 = Eruptor
+13 = Wraith
+14 = Ghoul
+15 = Revenant
+
+Before addition of new units: [build 79 and below]
+
+0 = Player
+1 = Fire
+2 = Puddle
+3 = Bullet
+4 = Lightning
+5 = Spirit
+6 = Dagger
+7 = Crawler
+8 = Titan
+9 = Fortress
+10 = Eruptor
+11 = Wraith
+12 = Ghoul
+13 = Phantom
+14 = Revenant
+ */
+public class LegacyTypeTable{
+ private static final Supplier[] build81Table = {
+ Player::new,
+ Fire::new,
+ Puddle::new,
+ Draug::new,
+ Spirit::new,
+ Phantom::new,
+ Dagger::new,
+ Crawler::new,
+ Titan::new,
+ Fortress::new,
+ Eruptor::new,
+ Wraith::new,
+ Ghoul::new,
+ Revenant::new
+ };
+
+ private static final Supplier[] build80Table = {
+ Player::new,
+ Fire::new,
+ Puddle::new,
+ Bullet::new, //TODO reading these may crash
+ Lightning::new,
+ Draug::new,
+ Spirit::new,
+ Phantom::new,
+ Dagger::new,
+ Crawler::new,
+ Titan::new,
+ Fortress::new,
+ Eruptor::new,
+ Wraith::new,
+ Ghoul::new,
+ Revenant::new
+ };
+
+ private static final Supplier[] build79Table = {
+ Player::new,
+ Fire::new,
+ Puddle::new,
+ Bullet::new, //TODO reading these may crash
+ Lightning::new,
+ Spirit::new,
+ Dagger::new,
+ Crawler::new,
+ Titan::new,
+ Fortress::new,
+ Eruptor::new,
+ Wraith::new,
+ Ghoul::new,
+ Phantom::new,
+ Revenant::new
+ };
+
+ public static Supplier[] getTable(int build){
+ if(build == -1 || build == 81){
+ //return most recent one since that's probably is; not guaranteed
+ return build81Table;
+ }else if(build == 80){
+ return build80Table;
+ }else{
+ return build79Table;
+ }
+ }
+}
diff --git a/core/src/io/anuke/mindustry/io/versions/Save1.java b/core/src/io/anuke/mindustry/io/versions/Save1.java
index f9ea40b8ce..545ae99eb2 100644
--- a/core/src/io/anuke/mindustry/io/versions/Save1.java
+++ b/core/src/io/anuke/mindustry/io/versions/Save1.java
@@ -1,10 +1,34 @@
package io.anuke.mindustry.io.versions;
+import io.anuke.arc.function.Supplier;
+import io.anuke.mindustry.entities.traits.SaveTrait;
import io.anuke.mindustry.io.SaveVersion;
+import java.io.DataInput;
+import java.io.IOException;
+
public class Save1 extends SaveVersion{
public Save1(){
super(1);
}
+
+ @Override
+ public void readEntities(DataInput stream) throws IOException{
+ Supplier[] table = LegacyTypeTable.getTable(lastReadBuild);
+
+ byte groups = stream.readByte();
+
+ for(int i = 0; i < groups; i++){
+ int amount = stream.readInt();
+ for(int j = 0; j < amount; j++){
+ readChunk(stream, true, in -> {
+ byte typeid = in.readByte();
+ byte version = in.readByte();
+ SaveTrait trait = (SaveTrait)table[typeid].get();
+ trait.readSave(in, version);
+ });
+ }
+ }
+ }
}
diff --git a/core/src/io/anuke/mindustry/io/versions/Save2.java b/core/src/io/anuke/mindustry/io/versions/Save2.java
new file mode 100644
index 0000000000..66d61964c5
--- /dev/null
+++ b/core/src/io/anuke/mindustry/io/versions/Save2.java
@@ -0,0 +1,9 @@
+package io.anuke.mindustry.io.versions;
+
+import io.anuke.mindustry.io.SaveVersion;
+
+public class Save2 extends SaveVersion{
+ public Save2(){
+ super(2);
+ }
+}
diff --git a/core/src/io/anuke/mindustry/maps/Map.java b/core/src/io/anuke/mindustry/maps/Map.java
index 07ed0efb1d..89165b14c8 100644
--- a/core/src/io/anuke/mindustry/maps/Map.java
+++ b/core/src/io/anuke/mindustry/maps/Map.java
@@ -1,13 +1,15 @@
package io.anuke.mindustry.maps;
import io.anuke.arc.Core;
-import io.anuke.arc.collection.StringMap;
+import io.anuke.arc.collection.*;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Texture;
import io.anuke.mindustry.Vars;
-import io.anuke.mindustry.game.DefaultWaves;
-import io.anuke.mindustry.game.Rules;
+import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.JsonIO;
+import io.anuke.mindustry.maps.filters.*;
+
+import static io.anuke.mindustry.Vars.world;
public class Map implements Comparable