From 2796ab980174ab2ee8f46845affc8451cc96569b Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 2 Jan 2018 15:47:08 -0500 Subject: [PATCH] Added new save file format system, wall balancing --- .../io/anuke/mindustry/AndroidLauncher.java | 12 +- core/src/io/anuke/mindustry/Mindustry.java | 26 +- .../io/anuke/mindustry/core/NetClient.java | 13 +- .../io/anuke/mindustry/core/NetServer.java | 14 +- core/src/io/anuke/mindustry/core/UI.java | 2 - .../anuke/mindustry/entities/EnemySpawn.java | 3 +- core/src/io/anuke/mindustry/io/NetworkIO.java | 5 +- .../anuke/mindustry/io/PlatformFunction.java | 5 +- .../anuke/mindustry/io/SaveFileVersion.java | 49 ++ core/src/io/anuke/mindustry/io/SaveIO.java | 423 +++--------------- core/src/io/anuke/mindustry/io/Saves.java | 6 + .../anuke/mindustry/io/versions/Save12.java | 300 +++++++++++++ .../anuke/mindustry/io/versions/Save13.java | 333 ++++++++++++++ .../io/anuke/mindustry/resource/Recipe.java | 22 +- .../mindustry/ui/fragments/MenuFragment.java | 12 +- core/src/io/anuke/mindustry/world/Block.java | 5 +- .../io/anuke/mindustry/world/BlockLoader.java | 19 + .../anuke/mindustry/world/blocks/Blocks.java | 14 +- .../mindustry/world/blocks/DefenseBlocks.java | 27 +- .../world/blocks/ProductionBlocks.java | 93 ++-- .../mindustry/world/blocks/SpecialBlocks.java | 4 +- .../blocks/types/defense/ShieldBlock.java | 5 +- .../types/defense/ShieldedWallBlock.java | 18 +- .../mindustry/desktop/DesktopLauncher.java | 35 +- .../anuke/mindustry/client/HtmlLauncher.java | 23 +- 25 files changed, 939 insertions(+), 529 deletions(-) create mode 100644 core/src/io/anuke/mindustry/io/SaveFileVersion.java create mode 100644 core/src/io/anuke/mindustry/io/versions/Save12.java create mode 100644 core/src/io/anuke/mindustry/io/versions/Save13.java create mode 100644 core/src/io/anuke/mindustry/world/BlockLoader.java diff --git a/android/src/io/anuke/mindustry/AndroidLauncher.java b/android/src/io/anuke/mindustry/AndroidLauncher.java index 6c432a933a..53c16c5ef9 100644 --- a/android/src/io/anuke/mindustry/AndroidLauncher.java +++ b/android/src/io/anuke/mindustry/AndroidLauncher.java @@ -11,7 +11,6 @@ import io.anuke.kryonet.KryoClient; import io.anuke.kryonet.KryoServer; import io.anuke.mindustry.io.PlatformFunction; import io.anuke.mindustry.net.Net; -import io.anuke.ucore.function.Callable; import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.layout.Unit; @@ -56,17 +55,16 @@ public class AndroidLauncher extends AndroidApplication{ } @Override - public void onSceneChange(String state, String details, String icon) { - - } + public void onSceneChange(String state, String details, String icon) { } @Override - public void onGameExit() { + public void onGameExit() { } + @Override + public void openDonations() { + showDonations(); } }; - - Mindustry.donationsCallable = new Callable(){ @Override public void run(){ showDonations(); } }; if(doubleScaleTablets && isTablet(this.getContext())){ Unit.dp.addition = 0.5f; diff --git a/core/src/io/anuke/mindustry/Mindustry.java b/core/src/io/anuke/mindustry/Mindustry.java index edbc35f299..0f42d36d3e 100644 --- a/core/src/io/anuke/mindustry/Mindustry.java +++ b/core/src/io/anuke/mindustry/Mindustry.java @@ -4,17 +4,15 @@ import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.I18NBundle; +import com.badlogic.gdx.utils.OrderedMap; import io.anuke.mindustry.core.*; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.io.PlatformFunction; import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.blocks.*; -import io.anuke.ucore.UCore; +import io.anuke.mindustry.world.BlockLoader; import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Inputs; import io.anuke.ucore.core.Timers; -import io.anuke.ucore.function.Callable; import io.anuke.ucore.modules.ModuleCore; import io.anuke.ucore.scene.ui.TextField; @@ -22,7 +20,6 @@ import java.util.Date; import java.util.Locale; public class Mindustry extends ModuleCore { - public static Callable donationsCallable; public static boolean hasDiscord = true; public static Array args = new Array<>(); public static PlatformFunction platforms = new PlatformFunction(){ @@ -31,14 +28,17 @@ public class Mindustry extends ModuleCore { @Override public void openLink(String link){ } @Override public void addDialog(TextField field){} @Override public void onSceneChange(String state, String details, String icon) {} - @Override public void onGameExit() { } + @Override public void onGameExit() {} + @Override public void openDonations() {} }; + public static OrderedMap idMap = new OrderedMap<>(); public static boolean externalBundle = false; @Override public void init(){ loadBundle(); + BlockLoader.load(); module(Vars.world = new World()); module(Vars.control = new Control()); @@ -68,24 +68,12 @@ public class Mindustry extends ModuleCore { Locale locale = Locale.getDefault(); Core.bundle = I18NBundle.createBundle(handle, locale); } - - - //always initialize blocks in this order, otherwise there are ID errors - Block[] blockClasses = { - Blocks.air, - DefenseBlocks.compositewall, - DistributionBlocks.conduit, - ProductionBlocks.coaldrill, - WeaponBlocks.chainturret, - SpecialBlocks.enemySpawn - }; - - UCore.log("Block classes: " + blockClasses.length); } @Override public void postInit(){ Vars.control.reset(); + Vars.control.getSaves().convertSaves(); } @Override diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 6cb6099246..5268402ae9 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -2,6 +2,7 @@ package io.anuke.mindustry.core; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.utils.TimeUtils; +import com.badlogic.gdx.utils.compression.Lzma; import com.badlogic.gdx.utils.reflect.ClassReflection; import com.badlogic.gdx.utils.reflect.ReflectionException; import io.anuke.mindustry.Vars; @@ -27,6 +28,8 @@ import io.anuke.ucore.entities.BaseBulletType; import io.anuke.ucore.entities.Entity; import io.anuke.ucore.modules.Module; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.IOException; import java.util.Arrays; @@ -76,8 +79,16 @@ public class NetClient extends Module { Net.handle(WorldData.class, data -> { Gdx.app.postRunnable(() -> { + ByteArrayOutputStream outc = new ByteArrayOutputStream(); + + try { + Lzma.decompress(data.stream, outc); + }catch (IOException e){ + throw new RuntimeException(e); + } + UCore.log("Recieved world data: " + data.stream.available() + " bytes."); - NetworkIO.load(data.stream); + NetworkIO.load(new ByteArrayInputStream(outc.toByteArray())); Vars.player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2); GameState.set(State.playing); diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index f8f8829272..37dd6daa73 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -5,6 +5,7 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.utils.IntArray; import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.TimeUtils; +import com.badlogic.gdx.utils.compression.Lzma; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.BulletType; @@ -56,8 +57,19 @@ public class NetServer extends Module{ UCore.log("Packed " + stream.size() + " uncompressed bytes of data."); + ByteArrayInputStream inc = new ByteArrayInputStream(stream.toByteArray()); + ByteArrayOutputStream outc = new ByteArrayOutputStream(); + + try { + Lzma.compress(inc, outc); + }catch (IOException e){ + throw new RuntimeException(e); + } + + UCore.log("Packed " + outc.size() + " COMPRESSED bytes of data."); + //TODO compress and uncompress when sending - data.stream = new ByteArrayInputStream(stream.toByteArray()); + data.stream = new ByteArrayInputStream(outc.toByteArray()); Net.sendStream(id, data); diff --git a/core/src/io/anuke/mindustry/core/UI.java b/core/src/io/anuke/mindustry/core/UI.java index f9ce282f31..251fd52f29 100644 --- a/core/src/io/anuke/mindustry/core/UI.java +++ b/core/src/io/anuke/mindustry/core/UI.java @@ -53,8 +53,6 @@ public class UI extends SceneModule{ Tooltip tooltip; Tile configTile; MapEditor editor; - String lastip = "localhost"; - int lastport = Vars.port; boolean wasPaused = false; private Fragment menufrag = new MenuFragment(), diff --git a/core/src/io/anuke/mindustry/entities/EnemySpawn.java b/core/src/io/anuke/mindustry/entities/EnemySpawn.java index a09be71f51..be96fb5bc1 100644 --- a/core/src/io/anuke/mindustry/entities/EnemySpawn.java +++ b/core/src/io/anuke/mindustry/entities/EnemySpawn.java @@ -5,7 +5,8 @@ import io.anuke.ucore.core.Settings; import io.anuke.ucore.util.Mathf; public class EnemySpawn{ - private static float[] scalings = {2f, 1.5f, 1f}; + /**Scaling multiplier for each difficulty. Easy, normal, hard.*/ + private static float[] scalings = {4f, 2.5f, 1.5f}; /**The enemy type spawned*/ public final Class type; diff --git a/core/src/io/anuke/mindustry/io/NetworkIO.java b/core/src/io/anuke/mindustry/io/NetworkIO.java index 4b8c60bf95..210139242b 100644 --- a/core/src/io/anuke/mindustry/io/NetworkIO.java +++ b/core/src/io/anuke/mindustry/io/NetworkIO.java @@ -16,9 +16,8 @@ import io.anuke.ucore.entities.Entities; import java.io.*; -import static io.anuke.mindustry.Vars.android; -import static io.anuke.mindustry.io.SaveIO.enemyIDs; -import static io.anuke.mindustry.io.SaveIO.idEnemies; +import static io.anuke.mindustry.io.SaveFileVersion.enemyIDs; +import static io.anuke.mindustry.io.SaveFileVersion.idEnemies; public class NetworkIO { private static final int fileVersionID = 13; diff --git a/core/src/io/anuke/mindustry/io/PlatformFunction.java b/core/src/io/anuke/mindustry/io/PlatformFunction.java index cbb44c5970..7fdc81e82b 100644 --- a/core/src/io/anuke/mindustry/io/PlatformFunction.java +++ b/core/src/io/anuke/mindustry/io/PlatformFunction.java @@ -1,9 +1,9 @@ package io.anuke.mindustry.io; -import java.util.Date; - import io.anuke.ucore.scene.ui.TextField; +import java.util.Date; + public interface PlatformFunction{ public String format(Date date); public String format(int number); @@ -11,4 +11,5 @@ public interface PlatformFunction{ public void addDialog(TextField field); public void onSceneChange(String state, String details, String icon); public void onGameExit(); + public void openDonations(); } diff --git a/core/src/io/anuke/mindustry/io/SaveFileVersion.java b/core/src/io/anuke/mindustry/io/SaveFileVersion.java new file mode 100644 index 0000000000..8c9e03e874 --- /dev/null +++ b/core/src/io/anuke/mindustry/io/SaveFileVersion.java @@ -0,0 +1,49 @@ +package io.anuke.mindustry.io; + +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.ObjectMap; +import io.anuke.mindustry.entities.enemies.*; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; + +public abstract class SaveFileVersion { + public static final Array> enemyIDs = Array.with( + Enemy.class, + FastEnemy.class, + RapidEnemy.class, + FlamerEnemy.class, + TankEnemy.class, + BlastEnemy.class, + MortarEnemy.class, + TestEnemy.class, + HealerEnemy.class, + TitanEnemy.class, + EmpEnemy.class + ); + + public static final ObjectMap, Byte> idEnemies = new ObjectMap, Byte>(){{ + for(int i = 0; i < enemyIDs.size; i ++){ + put(enemyIDs.get(i), (byte)i); + } + }}; + + public final int version; + + public SaveFileVersion(int version){ + this.version = version; + } + + public SaveMeta getData(DataInputStream stream) throws IOException{ + int version = stream.readInt(); //read version + long time = stream.readLong(); //read last saved time + byte mode = stream.readByte(); //read the gamemode + byte map = stream.readByte(); //read the map + int wave = stream.readInt(); //read the wave + return new SaveMeta(version, time, mode, map, wave); + } + + public abstract void read(DataInputStream stream) throws IOException; + public abstract void write(DataOutputStream stream) throws IOException; +} diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java index 457c158dc0..ca6416f657 100644 --- a/core/src/io/anuke/mindustry/io/SaveIO.java +++ b/core/src/io/anuke/mindustry/io/SaveIO.java @@ -1,112 +1,23 @@ package io.anuke.mindustry.io; import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Base64Coder; -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.TimeUtils; -import com.badlogic.gdx.utils.reflect.ClassReflection; -import io.anuke.mindustry.Mindustry; +import com.badlogic.gdx.utils.OrderedMap; import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.enemies.*; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.GameMode; -import io.anuke.mindustry.world.Map; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.core.Core; +import io.anuke.mindustry.io.versions.Save12; +import io.anuke.mindustry.io.versions.Save13; +import io.anuke.ucore.UCore; import io.anuke.ucore.core.Settings; -import io.anuke.ucore.entities.Entities; import java.io.*; -import java.util.Arrays; -import java.util.Date; -import static io.anuke.mindustry.Vars.android; - -/* - * Save format: - * - * --META-- - * - * Save file version ID (int) - * Last saved timestamp (long) - * - * --STATE DATA-- - * Wave (int) - * Wave countdown time (float) - * - * Gamemode Ordinal (byte) - * Map ordinal (byte) - * - * Player X (float) - * Player Y (float) - * Player health (int) - * - * Amount of weapons (byte) - * (weapon list) - * Weapon enum ordinal (byte) - * - * Amount of items (byte) - * (item list) - * Item ID (byte) - * Item amount (int) - * - * --ENEMY DATA-- - * Amount of enemies (int) - * (enemy list) - * enemy type ID (byte) - * spawn lane (byte) - * x (float) - * y (float) - * tier (byte) - * health (int) - * - * - * --MAP DATA-- - * Seed (int) - * Amount of tiles (int) - * (tile list) - * Tile position, as a single integer, in the format x+y*width - * Tile link - (byte) - * Tile type (boolean)- whether the block has a tile entity attached - * Block ID - the block ID (byte) - * (the following only applies to tile entity blocks) - * Block placerot (byte) - * Block health (int) - * Amount of items (byte) - * (item list) - * Item ID (byte) - * Item amount (int) - * Additional tile entity data (varies, check the TileEntity classes) - */ public class SaveIO{ - /**Save file version ID. Should be incremented every breaking release.*/ - private static final int fileVersionID = 12; - - //TODO automatic registration of types? - public static final Array> enemyIDs = Array.with( - Enemy.class, - FastEnemy.class, - RapidEnemy.class, - FlamerEnemy.class, - TankEnemy.class, - BlastEnemy.class, - MortarEnemy.class, - TestEnemy.class, - HealerEnemy.class, - TitanEnemy.class, - EmpEnemy.class - ); - - public static final ObjectMap, Byte> idEnemies = new ObjectMap, Byte>(){{ - for(int i = 0; i < enemyIDs.size; i ++){ - put(enemyIDs.get(i), (byte)i); - } + + public static final OrderedMap versions = new OrderedMap(){{ + put(12, new Save12()); + put(13, new Save13()); }}; - + public static void saveToSlot(int slot){ if(Vars.gwt){ ByteArrayOutputStream stream = new ByteArrayOutputStream(); @@ -146,6 +57,29 @@ public class SaveIO{ } } + public static boolean checkConvert(int slot){ + + try{ + DataInputStream stream = readSlotMeta(slot); + int version = stream.readInt(); + stream.close(); + + if(version != getVersion().version){ + UCore.log("Converting slot " + slot + ": " + version + " -> " + getVersion().version); + stream = readSlotMeta(slot); + SaveFileVersion target = versions.get(version); + target.read(stream); + stream.close(); + saveToSlot(slot); + return true; + } + return false; + + }catch (IOException e){ + throw new RuntimeException(e); + } + } + public static boolean isSaveValid(FileHandle file){ return isSaveValid(new DataInputStream(file.read())); } @@ -154,7 +88,7 @@ public class SaveIO{ try{ SaveMeta meta = getData(stream); - return meta.version == fileVersionID && meta.map != null; + return meta.map != null; }catch (Exception e){ return false; } @@ -167,12 +101,9 @@ public class SaveIO{ public static SaveMeta getData(DataInputStream stream){ try{ - int version = stream.readInt(); //read version - long time = stream.readLong(); //read last saved time - byte mode = stream.readByte(); //read the gamemode - byte map = stream.readByte(); //read the map - int wave = stream.readInt(); //read the wave - return new SaveMeta(version, time, mode, map, wave); + SaveMeta meta = getVersion().getData(stream); + stream.close(); + return meta; }catch (IOException e){ throw new RuntimeException(e); } @@ -187,132 +118,13 @@ public class SaveIO{ } public static void write(OutputStream os){ - - try(DataOutputStream stream = new DataOutputStream(os)){ - - //--META-- - stream.writeInt(fileVersionID); //version id - stream.writeLong(TimeUtils.millis()); //last saved - - //--GENERAL STATE-- - stream.writeByte(Vars.control.getMode().ordinal()); //gamemode - stream.writeByte(Vars.world.getMap().id); //map ID - - stream.writeInt(Vars.control.getWave()); //wave - stream.writeFloat(Vars.control.getWaveCountdown()); //wave countdown - - stream.writeFloat(Vars.player.x); //player x/y - stream.writeFloat(Vars.player.y); - - stream.writeInt(Vars.player.health); //player health - - stream.writeByte(Vars.control.getWeapons().size - 1); //amount of weapons - - //start at 1, because the first weapon is always the starter - ignore that - for(int i = 1; i < Vars.control.getWeapons().size; i ++){ - stream.writeByte(Vars.control.getWeapons().get(i).ordinal()); //weapon ordinal - } - - //--INVENTORY-- - - int l = Vars.control.getItems().length; - int itemsize = 0; - - for(int i = 0; i < l; i ++){ - if(Vars.control.getItems()[i] > 0){ - itemsize ++; - } - } - - stream.writeByte(itemsize); //amount of items - - for(int i = 0; i < l; i ++){ - if(Vars.control.getItems()[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(Vars.control.getItems()[i]); //item amount - } - } - - //--ENEMIES-- - - int totalEnemies = 0; - Array enemies = Vars.control.enemyGroup.all(); - - for(int i = 0; i < enemies.size; i ++){ - Enemy enemy = enemies.get(i); - if(idEnemies.containsKey(enemy.getClass())){ - totalEnemies ++; - } - } - - stream.writeInt(totalEnemies); //enemy amount - - for(int i = 0; i < enemies.size; i ++){ - Enemy enemy = enemies.get(i); - if(idEnemies.containsKey(enemy.getClass())){ - stream.writeByte(idEnemies.get(enemy.getClass())); //type - stream.writeByte(enemy.lane); //lane - stream.writeFloat(enemy.x); //x - stream.writeFloat(enemy.y); //y - stream.writeByte(enemy.tier); //tier - stream.writeInt(enemy.health); //health - } - } - - //--MAP DATA-- - - //seed - stream.writeInt(Vars.world.getSeed()); - - int totalblocks = 0; - - for(int x = 0; x < Vars.world.width(); x ++){ - for(int y = 0; y < Vars.world.height(); y ++){ - Tile tile = Vars.world.tile(x, y); - - if(tile.breakable()){ - totalblocks ++; - } - } - } - - //tile amount - stream.writeInt(totalblocks); - - for(int x = 0; x < Vars.world.width(); x ++){ - for(int y = 0; y < Vars.world.height(); y ++){ - Tile tile = Vars.world.tile(x, y); - - if(tile.breakable()){ - - stream.writeInt(x + y*Vars.world.width()); //tile pos - stream.writeByte(tile.link); - stream.writeBoolean(tile.entity != null); //whether it has a tile entity - stream.writeInt(tile.block().id); //block ID - - if(tile.entity != null){ - stream.writeByte(tile.getRotation()); //placerot - stream.writeInt(tile.entity.health); //health - int amount = 0; - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0) amount ++; - } - stream.writeByte(amount); //amount of items - - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(tile.entity.items[i]); //item amount - } - } - - tile.entity.write(stream); - } - } - } - } - + DataOutputStream stream; + + try{ + stream = new DataOutputStream(os); + getVersion().write(stream); + stream.close(); }catch (IOException e){ throw new RuntimeException(e); } @@ -324,150 +136,19 @@ public class SaveIO{ //TODO GWT support public static void load(InputStream is){ + + DataInputStream stream; - try(DataInputStream stream = new DataInputStream(is)){ - - int version = stream.readInt(); - /*long loadTime = */stream.readLong(); - - if(version != fileVersionID){ - throw new RuntimeException("Save file version mismatch!"); - } - - //general state - byte mode = stream.readByte(); - byte mapid = stream.readByte(); - - int wave = stream.readInt(); - float wavetime = stream.readFloat(); - - float playerx = stream.readFloat(); - float playery = stream.readFloat(); - - int playerhealth = stream.readInt(); - - Vars.player.x = playerx; - Vars.player.y = playery; - Vars.player.health = playerhealth; - Vars.control.setMode(GameMode.values()[mode]); - Core.camera.position.set(playerx, playery, 0); - - //weapons - - Vars.control.getWeapons().clear(); - Vars.control.getWeapons().add(Weapon.blaster); - Vars.player.weapon = Weapon.blaster; - - int weapons = stream.readByte(); - - for(int i = 0; i < weapons; i ++){ - Vars.control.addWeapon(Weapon.values()[stream.readByte()]); - } - - Vars.ui.updateWeapons(); - - //inventory - - int totalItems = stream.readByte(); - - Arrays.fill(Vars.control.getItems(), 0); - - for(int i = 0; i < totalItems; i ++){ - Item item = Item.getByID(stream.readByte()); - int amount = stream.readInt(); - Vars.control.getItems()[item.id] = amount; - } - - Vars.ui.updateItems(); - - //enemies - - Entities.clear(); - - int enemies = stream.readInt(); - - Array enemiesToUpdate = new Array<>(); - - for(int i = 0; i < enemies; i ++){ - byte type = stream.readByte(); - int lane = stream.readByte(); - float x = stream.readFloat(); - float y = stream.readFloat(); - byte tier = stream.readByte(); - int health = stream.readInt(); - - try{ - Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type)); - enemy.lane = lane; - enemy.health = health; - enemy.x = x; - enemy.y = y; - enemy.tier = tier; - enemy.add(Vars.control.enemyGroup); - enemiesToUpdate.add(enemy); - }catch (Exception e){ - throw new RuntimeException(e); - } - } - - Vars.control.setWaveData(enemies, wave, wavetime); - - if(!android) - Vars.player.add(); - - //map - - int seed = stream.readInt(); - int tiles = stream.readInt(); - - Vars.world.loadMap(Vars.world.maps().getMap(mapid), seed); - Vars.renderer.clearTiles(); - - for(Enemy enemy : enemiesToUpdate){ - enemy.node = -2; - } - - for(int x = 0; x < Vars.world.width(); x ++){ - for(int y = 0; y < Vars.world.height(); y ++){ - Tile tile = Vars.world.tile(x, y); - - //remove breakables like rocks - if(tile.breakable()){ - Vars.world.tile(x, y).setBlock(Blocks.air); - } - } - } - - for(int i = 0; i < tiles; i ++){ - int pos = stream.readInt(); - byte link = stream.readByte(); - boolean hasEntity = stream.readBoolean(); - int blockid = stream.readInt(); - - Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width()); - tile.setBlock(Block.getByID(blockid)); - tile.link = link; - - if(hasEntity){ - byte rotation = stream.readByte(); - int health = stream.readInt(); - int items = stream.readByte(); - - tile.entity.health = health; - tile.setRotation(rotation); - - for(int j = 0; j < items; j ++){ - int itemid = stream.readByte(); - int itemamount = stream.readInt(); - tile.entity.items[itemid] = itemamount; - } - - tile.entity.read(stream); - } - } - + try{ + stream = new DataInputStream(is); + getVersion().read(stream); + stream.close(); }catch (IOException e){ throw new RuntimeException(e); } } + + public static SaveFileVersion getVersion(){ + return versions.get(versions.orderedKeys().peek()); + } } diff --git a/core/src/io/anuke/mindustry/io/Saves.java b/core/src/io/anuke/mindustry/io/Saves.java index c1bd4e7cfc..5dceee5cc1 100644 --- a/core/src/io/anuke/mindustry/io/Saves.java +++ b/core/src/io/anuke/mindustry/io/Saves.java @@ -34,6 +34,12 @@ public class Saves { } } + public void convertSaves(){ + for(SaveSlot slot : saves){ + SaveIO.checkConvert(slot.index); + } + } + public SaveSlot getCurrent() { return current; } diff --git a/core/src/io/anuke/mindustry/io/versions/Save12.java b/core/src/io/anuke/mindustry/io/versions/Save12.java new file mode 100644 index 0000000000..763c9f520a --- /dev/null +++ b/core/src/io/anuke/mindustry/io/versions/Save12.java @@ -0,0 +1,300 @@ +package io.anuke.mindustry.io.versions; + +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.TimeUtils; +import com.badlogic.gdx.utils.reflect.ClassReflection; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.mindustry.io.SaveFileVersion; +import io.anuke.mindustry.resource.Item; +import io.anuke.mindustry.resource.Weapon; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.GameMode; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Blocks; +import io.anuke.ucore.core.Core; +import io.anuke.ucore.entities.Entities; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +import static io.anuke.mindustry.Vars.android; + +public class Save12 extends SaveFileVersion { + + public Save12(){ + super(12); + } + + @Override + public void read(DataInputStream stream) throws IOException { + + int version = stream.readInt(); + /*long loadTime = */stream.readLong(); + + if(version != this.version){ + throw new RuntimeException("Save file version mismatch!"); + } + + //general state + byte mode = stream.readByte(); + byte mapid = stream.readByte(); + + int wave = stream.readInt(); + float wavetime = stream.readFloat(); + + float playerx = stream.readFloat(); + float playery = stream.readFloat(); + + int playerhealth = stream.readInt(); + + Vars.player.x = playerx; + Vars.player.y = playery; + Vars.player.health = playerhealth; + Vars.control.setMode(GameMode.values()[mode]); + Core.camera.position.set(playerx, playery, 0); + + //weapons + + Vars.control.getWeapons().clear(); + Vars.control.getWeapons().add(Weapon.blaster); + Vars.player.weapon = Weapon.blaster; + + int weapons = stream.readByte(); + + for(int i = 0; i < weapons; i ++){ + Vars.control.addWeapon(Weapon.values()[stream.readByte()]); + } + + Vars.ui.updateWeapons(); + + //inventory + + int totalItems = stream.readByte(); + + Arrays.fill(Vars.control.getItems(), 0); + + for(int i = 0; i < totalItems; i ++){ + Item item = Item.getByID(stream.readByte()); + int amount = stream.readInt(); + Vars.control.getItems()[item.id] = amount; + } + + Vars.ui.updateItems(); + + //enemies + + Entities.clear(); + + int enemies = stream.readInt(); + + Array enemiesToUpdate = new Array<>(); + + for(int i = 0; i < enemies; i ++){ + byte type = stream.readByte(); + int lane = stream.readByte(); + float x = stream.readFloat(); + float y = stream.readFloat(); + byte tier = stream.readByte(); + int health = stream.readInt(); + + try{ + Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type)); + enemy.lane = lane; + enemy.health = health; + enemy.x = x; + enemy.y = y; + enemy.tier = tier; + enemy.add(Vars.control.enemyGroup); + enemiesToUpdate.add(enemy); + }catch (Exception e){ + throw new RuntimeException(e); + } + } + + Vars.control.setWaveData(enemies, wave, wavetime); + + if(!android) + Vars.player.add(); + + //map + + int seed = stream.readInt(); + int tiles = stream.readInt(); + + Vars.world.loadMap(Vars.world.maps().getMap(mapid), seed); + Vars.renderer.clearTiles(); + + for(Enemy enemy : enemiesToUpdate){ + enemy.node = -2; + } + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + //remove breakables like rocks + if(tile.breakable()){ + Vars.world.tile(x, y).setBlock(Blocks.air); + } + } + } + + for(int i = 0; i < tiles; i ++){ + int pos = stream.readInt(); + byte link = stream.readByte(); + boolean hasEntity = stream.readBoolean(); + int blockid = stream.readInt(); + + Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width()); + tile.setBlock(Block.getByID(blockid)); + tile.link = link; + + if(hasEntity){ + byte rotation = stream.readByte(); + int health = stream.readInt(); + int items = stream.readByte(); + + tile.entity.health = health; + tile.setRotation(rotation); + + for(int j = 0; j < items; j ++){ + int itemid = stream.readByte(); + int itemamount = stream.readInt(); + tile.entity.items[itemid] = itemamount; + } + + tile.entity.read(stream); + } + } + } + + @Override + public void write(DataOutputStream stream) throws IOException { + + //--META-- + stream.writeInt(version); //version id + stream.writeLong(TimeUtils.millis()); //last saved + + //--GENERAL STATE-- + stream.writeByte(Vars.control.getMode().ordinal()); //gamemode + stream.writeByte(Vars.world.getMap().id); //map ID + + stream.writeInt(Vars.control.getWave()); //wave + stream.writeFloat(Vars.control.getWaveCountdown()); //wave countdown + + stream.writeFloat(Vars.player.x); //player x/y + stream.writeFloat(Vars.player.y); + + stream.writeInt(Vars.player.health); //player health + + stream.writeByte(Vars.control.getWeapons().size - 1); //amount of weapons + + //start at 1, because the first weapon is always the starter - ignore that + for(int i = 1; i < Vars.control.getWeapons().size; i ++){ + stream.writeByte(Vars.control.getWeapons().get(i).ordinal()); //weapon ordinal + } + + //--INVENTORY-- + + int l = Vars.control.getItems().length; + int itemsize = 0; + + for(int i = 0; i < l; i ++){ + if(Vars.control.getItems()[i] > 0){ + itemsize ++; + } + } + + stream.writeByte(itemsize); //amount of items + + for(int i = 0; i < l; i ++){ + if(Vars.control.getItems()[i] > 0){ + stream.writeByte(i); //item ID + stream.writeInt(Vars.control.getItems()[i]); //item amount + } + } + + //--ENEMIES-- + + int totalEnemies = 0; + + Array enemies = Vars.control.enemyGroup.all(); + + for(int i = 0; i < enemies.size; i ++){ + Enemy enemy = enemies.get(i); + if(idEnemies.containsKey(enemy.getClass())){ + totalEnemies ++; + } + } + + stream.writeInt(totalEnemies); //enemy amount + + for(int i = 0; i < enemies.size; i ++){ + Enemy enemy = enemies.get(i); + if(idEnemies.containsKey(enemy.getClass())){ + stream.writeByte(idEnemies.get(enemy.getClass())); //type + stream.writeByte(enemy.lane); //lane + stream.writeFloat(enemy.x); //x + stream.writeFloat(enemy.y); //y + stream.writeByte(enemy.tier); //tier + stream.writeInt(enemy.health); //health + } + } + + //--MAP DATA-- + + //seed + stream.writeInt(Vars.world.getSeed()); + + int totalblocks = 0; + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + if(tile.breakable()){ + totalblocks ++; + } + } + } + + //tile amount + stream.writeInt(totalblocks); + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + if(tile.breakable()){ + + stream.writeInt(x + y*Vars.world.width()); //tile pos + stream.writeByte(tile.link); + stream.writeBoolean(tile.entity != null); //whether it has a tile entity + stream.writeInt(tile.block().id); //block ID + + if(tile.entity != null){ + stream.writeByte(tile.getRotation()); //placerot + stream.writeInt(tile.entity.health); //health + int amount = 0; + for(int i = 0; i < tile.entity.items.length; i ++){ + if(tile.entity.items[i] > 0) amount ++; + } + stream.writeByte(amount); //amount of items + + for(int i = 0; i < tile.entity.items.length; i ++){ + if(tile.entity.items[i] > 0){ + stream.writeByte(i); //item ID + stream.writeInt(tile.entity.items[i]); //item amount + } + } + + tile.entity.write(stream); + } + } + } + } + } +} diff --git a/core/src/io/anuke/mindustry/io/versions/Save13.java b/core/src/io/anuke/mindustry/io/versions/Save13.java new file mode 100644 index 0000000000..c79a5c8428 --- /dev/null +++ b/core/src/io/anuke/mindustry/io/versions/Save13.java @@ -0,0 +1,333 @@ +package io.anuke.mindustry.io.versions; + +import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.TimeUtils; +import com.badlogic.gdx.utils.reflect.ClassReflection; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.mindustry.io.SaveFileVersion; +import io.anuke.mindustry.resource.Item; +import io.anuke.mindustry.resource.Weapon; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.GameMode; +import io.anuke.mindustry.world.Generator; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Blocks; +import io.anuke.mindustry.world.blocks.types.BlockPart; +import io.anuke.mindustry.world.blocks.types.Rock; +import io.anuke.ucore.core.Core; +import io.anuke.ucore.entities.Entities; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; + +import static io.anuke.mindustry.Vars.android; + +public class Save13 extends SaveFileVersion { + + public Save13(){ + super(13); + } + + @Override + public void read(DataInputStream stream) throws IOException { + + int version = stream.readInt(); + /*long loadTime = */stream.readLong(); + + if(version != this.version){ + throw new RuntimeException("Save file version mismatch!"); + } + + //general state + byte mode = stream.readByte(); + byte mapid = stream.readByte(); + + int wave = stream.readInt(); + float wavetime = stream.readFloat(); + + float playerx = stream.readFloat(); + float playery = stream.readFloat(); + + int playerhealth = stream.readInt(); + + Vars.player.x = playerx; + Vars.player.y = playery; + Vars.player.health = playerhealth; + Vars.control.setMode(GameMode.values()[mode]); + Core.camera.position.set(playerx, playery, 0); + + //weapons + + Vars.control.getWeapons().clear(); + Vars.control.getWeapons().add(Weapon.blaster); + Vars.player.weapon = Weapon.blaster; + + int weapons = stream.readByte(); + + for(int i = 0; i < weapons; i ++){ + Vars.control.addWeapon(Weapon.values()[stream.readByte()]); + } + + Vars.ui.updateWeapons(); + + //inventory + + int totalItems = stream.readByte(); + + Arrays.fill(Vars.control.getItems(), 0); + + for(int i = 0; i < totalItems; i ++){ + Item item = Item.getByID(stream.readByte()); + int amount = stream.readInt(); + Vars.control.getItems()[item.id] = amount; + } + + Vars.ui.updateItems(); + + //enemies + + Entities.clear(); + + int enemies = stream.readInt(); + + Array enemiesToUpdate = new Array<>(); + + for(int i = 0; i < enemies; i ++){ + byte type = stream.readByte(); + int lane = stream.readByte(); + float x = stream.readFloat(); + float y = stream.readFloat(); + byte tier = stream.readByte(); + int health = stream.readShort(); + + try{ + Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type)); + enemy.lane = lane; + enemy.health = health; + enemy.x = x; + enemy.y = y; + enemy.tier = tier; + enemy.add(Vars.control.enemyGroup); + enemiesToUpdate.add(enemy); + }catch (Exception e){ + throw new RuntimeException(e); + } + } + + Vars.control.setWaveData(enemies, wave, wavetime); + + if(!android) + Vars.player.add(); + + //map + + int seed = stream.readInt(); + + Vars.world.loadMap(Vars.world.maps().getMap(mapid), seed); + Vars.renderer.clearTiles(); + + for(Enemy enemy : enemiesToUpdate){ + enemy.node = -2; + } + + int rocks = stream.readInt(); + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + //remove breakables like rocks + if(tile.breakable()){ + Vars.world.tile(x, y).setBlock(Blocks.air); + } + } + } + + for(int i = 0; i < rocks; i ++){ + int pos = stream.readInt(); + Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width()); + Block result = Generator.rocks.get(tile.floor()); + if(result != null) tile.setBlock(result); + } + + int tiles = stream.readInt(); + + for(int i = 0; i < tiles; i ++){ + int pos = stream.readInt(); + int blockid = stream.readInt(); + + Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width()); + tile.setBlock(Block.getByID(blockid)); + + if(blockid == Blocks.blockpart.id){ + tile.link = stream.readByte(); + } + + if(tile.entity != null){ + byte rotation = stream.readByte(); + short health = stream.readShort(); + int items = stream.readByte(); + + tile.entity.health = health; + tile.setRotation(rotation); + + for(int j = 0; j < items; j ++){ + int itemid = stream.readByte(); + int itemamount = stream.readInt(); + tile.entity.items[itemid] = itemamount; + } + + tile.entity.read(stream); + } + } + } + + @Override + public void write(DataOutputStream stream) throws IOException { + //--META-- + stream.writeInt(version); //version id + stream.writeLong(TimeUtils.millis()); //last saved + + //--GENERAL STATE-- + stream.writeByte(Vars.control.getMode().ordinal()); //gamemode + stream.writeByte(Vars.world.getMap().id); //map ID + + stream.writeInt(Vars.control.getWave()); //wave + stream.writeFloat(Vars.control.getWaveCountdown()); //wave countdown + + stream.writeFloat(Vars.player.x); //player x/y + stream.writeFloat(Vars.player.y); + + stream.writeInt(Vars.player.health); //player health + + stream.writeByte(Vars.control.getWeapons().size - 1); //amount of weapons + + //start at 1, because the first weapon is always the starter - ignore that + for(int i = 1; i < Vars.control.getWeapons().size; i ++){ + stream.writeByte(Vars.control.getWeapons().get(i).ordinal()); //weapon ordinal + } + + //--INVENTORY-- + + int l = Vars.control.getItems().length; + int itemsize = 0; + + for(int i = 0; i < l; i ++){ + if(Vars.control.getItems()[i] > 0){ + itemsize ++; + } + } + + stream.writeByte(itemsize); //amount of items + + for(int i = 0; i < l; i ++){ + if(Vars.control.getItems()[i] > 0){ + stream.writeByte(i); //item ID + stream.writeInt(Vars.control.getItems()[i]); //item amount + } + } + + //--ENEMIES-- + + int totalEnemies = 0; + + Array enemies = Vars.control.enemyGroup.all(); + + for(int i = 0; i < enemies.size; i ++){ + Enemy enemy = enemies.get(i); + if(idEnemies.containsKey(enemy.getClass())){ + totalEnemies ++; + } + } + + stream.writeInt(totalEnemies); //enemy amount + + for(int i = 0; i < enemies.size; i ++){ + Enemy enemy = enemies.get(i); + if(idEnemies.containsKey(enemy.getClass())){ + stream.writeByte(idEnemies.get(enemy.getClass())); //type + stream.writeByte(enemy.lane); //lane + stream.writeFloat(enemy.x); //x + stream.writeFloat(enemy.y); //y + stream.writeByte(enemy.tier); //tier + stream.writeShort(enemy.health); //health + } + } + + //--MAP DATA-- + + //seed + stream.writeInt(Vars.world.getSeed()); + + int totalblocks = 0; + int totalrocks = 0; + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + if(tile.breakable()){ + if(tile.block() instanceof Rock){ + totalrocks ++; + }else{ + totalblocks ++; + } + } + } + } + + //amount of rocks + stream.writeInt(totalrocks); + + //write all rocks + for(int x = 0; x < Vars.world.width(); x ++) { + for (int y = 0; y < Vars.world.height(); y++) { + Tile tile = Vars.world.tile(x, y); + + if (tile.block() instanceof Rock) { + stream.writeInt(tile.packedPosition()); + } + } + } + + //write all blocks + stream.writeInt(totalblocks); + + for(int x = 0; x < Vars.world.width(); x ++){ + for(int y = 0; y < Vars.world.height(); y ++){ + Tile tile = Vars.world.tile(x, y); + + if(tile.breakable() && !(tile.block() instanceof Rock)){ + + stream.writeInt(x + y*Vars.world.width()); //tile pos + stream.writeInt(tile.block().id); //block ID + + if(tile.block() instanceof BlockPart) stream.writeByte(tile.link); + + if(tile.entity != null){ + stream.writeByte(tile.getRotation()); //placerot + stream.writeShort(tile.entity.health); //health + byte amount = 0; + for(int i = 0; i < tile.entity.items.length; i ++){ + if(tile.entity.items[i] > 0) amount ++; + } + stream.writeByte(amount); //amount of items + + for(int i = 0; i < tile.entity.items.length; i ++){ + if(tile.entity.items[i] > 0){ + stream.writeByte(i); //item ID + stream.writeInt(tile.entity.items[i]); //item amount + } + } + + tile.entity.write(stream); + } + } + } + } + } + +} diff --git a/core/src/io/anuke/mindustry/resource/Recipe.java b/core/src/io/anuke/mindustry/resource/Recipe.java index d5299c1a4e..1b1a43dfc9 100644 --- a/core/src/io/anuke/mindustry/resource/Recipe.java +++ b/core/src/io/anuke/mindustry/resource/Recipe.java @@ -7,18 +7,18 @@ import io.anuke.mindustry.world.blocks.*; import static io.anuke.mindustry.resource.Section.*; public enum Recipe{ - stonewall(defense, DefenseBlocks.stonewall, stack(Item.stone, 2)), - ironwall(defense, DefenseBlocks.ironwall, stack(Item.iron, 2)), - steelwall(defense, DefenseBlocks.steelwall, stack(Item.steel, 2)), - titaniumwall(defense, DefenseBlocks.titaniumwall, stack(Item.titanium, 2)), - duriumwall(defense, DefenseBlocks.diriumwall, stack(Item.dirium, 2)), + stonewall(defense, DefenseBlocks.stonewall, stack(Item.stone, 12)), + ironwall(defense, DefenseBlocks.ironwall, stack(Item.iron, 12)), + steelwall(defense, DefenseBlocks.steelwall, stack(Item.steel, 11)), + titaniumwall(defense, DefenseBlocks.titaniumwall, stack(Item.titanium, 12)), + duriumwall(defense, DefenseBlocks.diriumwall, stack(Item.dirium, 12)), //compositewall(defense, DefenseBlocks.compositewall, stack(Item.dirium, 2), stack(Item.titanium, 2), stack(Item.steel, 2), stack(Item.iron, 2)), - steelwalllarge(defense, DefenseBlocks.steelwalllarge, stack(Item.steel, 8)), - titaniumwalllarge(defense, DefenseBlocks.titaniumwalllarge, stack(Item.titanium, 8)), - duriumwalllarge(defense, DefenseBlocks.diriumwalllarge, stack(Item.dirium, 8)), - door(defense, DefenseBlocks.door, stack(Item.steel, 3), stack(Item.iron, 3)), - largedoor(defense, DefenseBlocks.largedoor, stack(Item.steel, 3*4), stack(Item.iron, 3*4)), - titaniumshieldwall(defense, DefenseBlocks.titaniumshieldwall, stack(Item.titanium, 3)), + steelwalllarge(defense, DefenseBlocks.steelwalllarge, stack(Item.steel, 12*4)), + titaniumwalllarge(defense, DefenseBlocks.titaniumwalllarge, stack(Item.titanium, 12*4)), + duriumwalllarge(defense, DefenseBlocks.diriumwalllarge, stack(Item.dirium, 12*4)), + door(defense, DefenseBlocks.door, stack(Item.steel, 3), stack(Item.iron, 3*4)), + largedoor(defense, DefenseBlocks.largedoor, stack(Item.steel, 3*4), stack(Item.iron, 3*4*4)), + titaniumshieldwall(defense, DefenseBlocks.titaniumshieldwall, stack(Item.titanium, 16)), conveyor(distribution, DistributionBlocks.conveyor, stack(Item.stone, 1)), steelconveyor(distribution, DistributionBlocks.steelconveyor, stack(Item.steel, 1)), diff --git a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java index 9249ff3dea..40cdbf52bc 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/MenuFragment.java @@ -73,13 +73,11 @@ public class MenuFragment implements Fragment{ new imagebutton("icon-tools", isize, () -> ui.showPrefs()).text("$text.settings").padTop(4f); new imagebutton("icon-add", isize, () -> ui.showJoinGame()).text("$text.joingame").padTop(4f); - - if(Mindustry.donationsCallable != null){ - new imagebutton("icon-donate", isize, () -> { - Mindustry.donationsCallable.run(); - }).text("$text.donate").padTop(4f); - } - + + new imagebutton("icon-donate", isize, () -> { + Mindustry.platforms.openDonations(); + }).text("$text.donate").padTop(4f); + visible(()->GameState.is(State.menu)); }}.end(); }}.end(); diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index b84b4a09fd..edd96df28b 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -81,16 +81,15 @@ public class Block{ public Layer layer2 = null; public Block(String name) { - blocks.add(this); - this.name = name; this.formalName = Bundles.get("block." + name + ".name", name); this.description = Bundles.getOrNull("block." + name + ".description"); this.fullDescription = Bundles.getOrNull("block." + name + ".fulldescription"); this.solid = false; this.id = lastid++; + + blocks.add(this); } - public boolean isLayer(Tile tile){return true;} public boolean isLayer2(Tile tile){return true;} diff --git a/core/src/io/anuke/mindustry/world/BlockLoader.java b/core/src/io/anuke/mindustry/world/BlockLoader.java new file mode 100644 index 0000000000..f58935a811 --- /dev/null +++ b/core/src/io/anuke/mindustry/world/BlockLoader.java @@ -0,0 +1,19 @@ +package io.anuke.mindustry.world; + +import io.anuke.mindustry.world.blocks.*; + +public class BlockLoader { + + public static void load(){ + + Block[] blockClasses = { + Blocks.air, + DefenseBlocks.compositewall, + DistributionBlocks.conduit, + ProductionBlocks.coaldrill, + WeaponBlocks.chainturret, + SpecialBlocks.enemySpawn + //add any new block sections here + }; + } +} diff --git a/core/src/io/anuke/mindustry/world/blocks/Blocks.java b/core/src/io/anuke/mindustry/world/blocks/Blocks.java index 4e66ed2346..8af12778d2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/Blocks.java +++ b/core/src/io/anuke/mindustry/world/blocks/Blocks.java @@ -22,7 +22,9 @@ public class Blocks{ public void draw(Tile tile){} }, - blockpart = new BlockPart(), + blockpart = new BlockPart(){ + + }, deepwater = new Floor("deepwater"){{ variants = 0; @@ -99,15 +101,15 @@ public class Blocks{ drops = new ItemStack(Item.uranium, 1); }}, - dirt = new Floor("dirt"), + dirt = new Floor("dirt"){}, - sand = new Floor("sand"), + sand = new Floor("sand"){}, - ice = new Floor("ice"), + ice = new Floor("ice"){}, - snow = new Floor("snow"), + snow = new Floor("snow"){}, - grass = new Floor("grass"), + grass = new Floor("grass"){}, sandblock = new StaticBlock("sandblock"){{ solid = true; diff --git a/core/src/io/anuke/mindustry/world/blocks/DefenseBlocks.java b/core/src/io/anuke/mindustry/world/blocks/DefenseBlocks.java index 70f21a9d30..0a53b84e92 100644 --- a/core/src/io/anuke/mindustry/world/blocks/DefenseBlocks.java +++ b/core/src/io/anuke/mindustry/world/blocks/DefenseBlocks.java @@ -6,44 +6,45 @@ import io.anuke.mindustry.world.blocks.types.Wall; import io.anuke.mindustry.world.blocks.types.defense.*; public class DefenseBlocks{ + static final int wallHealthMultiplier = 4; public static final Block stonewall = new Wall("stonewall"){{ - health = 50; + health = 50*wallHealthMultiplier; }}, ironwall = new Wall("ironwall"){{ - health = 80; + health = 80*wallHealthMultiplier; }}, steelwall = new Wall("steelwall"){{ - health = 110; + health = 110*wallHealthMultiplier; }}, titaniumwall = new Wall("titaniumwall"){{ - health = 150; + health = 150*wallHealthMultiplier; }}, diriumwall = new Wall("duriumwall"){{ - health = 190; + health = 190*wallHealthMultiplier; }}, compositewall = new Wall("compositewall"){{ - health = 270; + health = 270*wallHealthMultiplier; }}, steelwalllarge = new Wall("steelwall-large"){{ - health = 110*4; + health = 110*4*wallHealthMultiplier; width = height = 2; }}, titaniumwalllarge = new Wall("titaniumwall-large"){{ - health = 150*4; + health = 150*4*wallHealthMultiplier; width = height = 2; }}, diriumwalllarge = new Wall("duriumwall-large"){{ - health = 190*4; + health = 190*4*wallHealthMultiplier; width = height = 2; }}, titaniumshieldwall = new ShieldedWallBlock("titaniumshieldwall"){{ - health = 150; + health = 150*wallHealthMultiplier; }}, repairturret = new RepairTurret("repairturret"){ @@ -65,16 +66,16 @@ public class DefenseBlocks{ shieldgenerator = new ShieldBlock("shieldgenerator"){ { - + health = 100*wallHealthMultiplier; } }, door = new Door("door"){{ - health = 90; + health = 90*wallHealthMultiplier; }}, largedoor = new Door("door-large"){{ openfx = Fx.dooropenlarge; closefx = Fx.doorcloselarge; - health = 90*4; + health = 90*4*wallHealthMultiplier; width = height = 2; }}; } diff --git a/core/src/io/anuke/mindustry/world/blocks/ProductionBlocks.java b/core/src/io/anuke/mindustry/world/blocks/ProductionBlocks.java index 9661edaa13..fcf21fbf72 100644 --- a/core/src/io/anuke/mindustry/world/blocks/ProductionBlocks.java +++ b/core/src/io/anuke/mindustry/world/blocks/ProductionBlocks.java @@ -1,6 +1,5 @@ package io.anuke.mindustry.world.blocks; -import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.resource.Item; @@ -14,14 +13,15 @@ import io.anuke.ucore.core.Effects; public class ProductionBlocks{ public static final Block - core = new CoreBlock("core"), + core = new CoreBlock("core"){}, - pump = new Pump("pump"){{ - }}, + pump = new Pump("pump"){}, - fluxpump = new Pump("fluxpump"){{ - pumpAmount = 3f; - }}, + fluxpump = new Pump("fluxpump"){ + { + pumpAmount = 3f; + } + }, smelter = new Crafter("smelter"){ { @@ -63,19 +63,6 @@ public class ProductionBlocks{ health = 70; } }, - - //TODO - /*siliconextractor = new LiquidCrafter("siliconextractor"){ - { - input = Item.sand; - inputAmount = 5; - inputLiquid = Liquid.water; - liquidAmount = 18.99f; - output = Item.silicon; - health = 50; - purifyTime = 50; - } - },*/ oilrefinery = new LiquidCrafter("oilrefinery"){ { @@ -116,32 +103,42 @@ public class ProductionBlocks{ } }, - stonedrill = new Drill("stonedrill"){{ - resource = Blocks.stone; - result = Item.stone; - time = 4; - }}, + stonedrill = new Drill("stonedrill"){ + { + resource = Blocks.stone; + result = Item.stone; + time = 4; + } + }, - irondrill = new Drill("irondrill"){{ - resource = Blocks.iron; - result = Item.iron; - }}, + irondrill = new Drill("irondrill"){ + { + resource = Blocks.iron; + result = Item.iron; + } + }, - coaldrill = new Drill("coaldrill"){{ - resource = Blocks.coal; - result = Item.coal; - }}, + coaldrill = new Drill("coaldrill"){ + { + resource = Blocks.coal; + result = Item.coal; + } + }, - uraniumdrill = new Drill("uraniumdrill"){{ - resource = Blocks.uranium; - result = Item.uranium; - time = 7; - }}, + uraniumdrill = new Drill("uraniumdrill"){ + { + resource = Blocks.uranium; + result = Item.uranium; + time = 7; + } + }, - titaniumdrill = new Drill("titaniumdrill"){{ - resource = Blocks.titanium; - result = Item.titanium; - }}, + titaniumdrill = new Drill("titaniumdrill"){ + { + resource = Blocks.titanium; + result = Item.titanium; + } + }, omnidrill = new Drill("omnidrill"){ { @@ -210,4 +207,16 @@ public class ProductionBlocks{ breaktime *= 2.3f; } }; + /* + siliconextractor = new LiquidCrafter("siliconextractor"){ + { + input = Item.sand; + inputAmount = 5; + inputLiquid = Liquid.water; + liquidAmount = 18.99f; + output = Item.sand; + health = 50; + purifyTime = 50; + } + }*/; } diff --git a/core/src/io/anuke/mindustry/world/blocks/SpecialBlocks.java b/core/src/io/anuke/mindustry/world/blocks/SpecialBlocks.java index 6868089d92..db80b10783 100644 --- a/core/src/io/anuke/mindustry/world/blocks/SpecialBlocks.java +++ b/core/src/io/anuke/mindustry/world/blocks/SpecialBlocks.java @@ -4,6 +4,6 @@ import io.anuke.mindustry.world.Block; public class SpecialBlocks{ public static final Block - playerSpawn = new Block("playerspawn"), - enemySpawn = new Block("enemyspawn"); + playerSpawn = new Block("playerspawn"){}, + enemySpawn = new Block("enemyspawn"){}; } diff --git a/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldBlock.java b/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldBlock.java index cf7cb91c64..8a4d2de1eb 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldBlock.java @@ -17,15 +17,14 @@ import io.anuke.ucore.util.Strings; public class ShieldBlock extends PowerBlock{ public float shieldRadius = 40f; public float powerDrain = 0.005f; - public float powerPerDamage = 0.13f; + public float powerPerDamage = 0.1f; public float maxRadius = 40f; - public float radiusScale = 200f; + public float radiusScale = 240f; public ShieldBlock(String name) { super(name); voltage = powerDrain; powerCapacity = 40f; - health = 100; } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldedWallBlock.java b/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldedWallBlock.java index 48492fb719..907543274c 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldedWallBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldedWallBlock.java @@ -1,19 +1,19 @@ package io.anuke.mindustry.world.blocks.types.defense; import com.badlogic.gdx.graphics.Color; - +import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.types.PowerBlock; import io.anuke.ucore.core.Draw; -import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Timers; +import io.anuke.ucore.util.Strings; public class ShieldedWallBlock extends PowerBlock{ static final float hitTime = 18f; static final Color hitColor = Color.SKY.cpy().mul(1.2f); - public float powerToDamage = 0.1f; + public float powerPerDamage = 0.08f; public ShieldedWallBlock(String name) { super(name); @@ -24,7 +24,7 @@ public class ShieldedWallBlock extends PowerBlock{ @Override public int handleDamage(Tile tile, int amount){ - float drain = amount * powerToDamage; + float drain = amount * powerPerDamage; ShieldedWallEntity entity = tile.entity(); if(entity.power > drain){ @@ -32,7 +32,7 @@ public class ShieldedWallBlock extends PowerBlock{ entity.hit = hitTime; return 0; }else if(entity.power > 0){ - int reduction = (int)(entity.power / powerToDamage); + int reduction = (int)(entity.power / powerPerDamage); entity.power = 0; return amount - reduction; @@ -40,6 +40,12 @@ public class ShieldedWallBlock extends PowerBlock{ return amount; } + + @Override + public void getStats(Array list){ + super.getStats(list); + list.add("[powerinfo]Power Drain/damage: " + Strings.toFixed(powerPerDamage, 2)); + } @Override public void draw(Tile tile){ @@ -47,7 +53,7 @@ public class ShieldedWallBlock extends PowerBlock{ ShieldedWallEntity entity = tile.entity(); - if(entity.power > powerToDamage){ + if(entity.power > powerPerDamage){ Vars.renderer.addShield(() -> Draw.rect("blank", tile.worldx(), tile.worldy(), Vars.tilesize, Vars.tilesize)); } diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java index a35cfa3ec0..f5789f1ea0 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopLauncher.java @@ -61,31 +61,32 @@ public class DesktopLauncher { } @Override - public void addDialog(TextField field){ - - } + public void addDialog(TextField field){} + + @Override + public void openDonations(){} @Override public void onSceneChange(String state, String details, String icon) { - DiscordRPC lib = DiscordRPC.INSTANCE; + DiscordRPC lib = DiscordRPC.INSTANCE; - String applicationId = "397335883319083018"; + String applicationId = "397335883319083018"; - DiscordEventHandlers handlers = new DiscordEventHandlers(); + DiscordEventHandlers handlers = new DiscordEventHandlers(); - lib.Discord_Initialize(applicationId, handlers, true, ""); + lib.Discord_Initialize(applicationId, handlers, true, ""); - DiscordRichPresence presence = new DiscordRichPresence(); - presence.startTimestamp = System.currentTimeMillis() / 1000; // epoch second - presence.state = state; - //presence.details = details; - presence.largeImageKey = "logo"; - presence.largeImageText = details; - lib.Discord_UpdatePresence(presence); - } + DiscordRichPresence presence = new DiscordRichPresence(); + presence.startTimestamp = System.currentTimeMillis() / 1000; // epoch second + presence.state = state; + //presence.details = details; + presence.largeImageKey = "logo"; + presence.largeImageText = details; + lib.Discord_UpdatePresence(presence); + } - @Override - public void onGameExit() { + @Override + public void onGameExit() { DiscordRPC.INSTANCE.Discord_Shutdown(); } }; diff --git a/html/src/io/anuke/mindustry/client/HtmlLauncher.java b/html/src/io/anuke/mindustry/client/HtmlLauncher.java index e35059459d..e0d854d11d 100644 --- a/html/src/io/anuke/mindustry/client/HtmlLauncher.java +++ b/html/src/io/anuke/mindustry/client/HtmlLauncher.java @@ -1,24 +1,26 @@ package io.anuke.mindustry.client; -import java.util.Date; - import com.badlogic.gdx.ApplicationListener; import com.badlogic.gdx.backends.gwt.GwtApplication; import com.badlogic.gdx.backends.gwt.GwtApplicationConfiguration; import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderCallback; import com.badlogic.gdx.backends.gwt.preloader.Preloader.PreloaderState; import com.google.gwt.core.client.GWT; -import com.google.gwt.dom.client.*; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.i18n.shared.DateTimeFormat; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.*; - import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.io.PlatformFunction; import io.anuke.ucore.scene.ui.TextField; +import java.util.Date; + public class HtmlLauncher extends GwtApplication { static final int WIDTH = 800; static final int HEIGHT = 600; @@ -109,19 +111,16 @@ public class HtmlLauncher extends GwtApplication { } @Override - public void addDialog(TextField field){ - - } + public void addDialog(TextField field){} @Override - public void onSceneChange(String state, String details, String icon) { - - } + public void onSceneChange(String state, String details, String icon) {} @Override - public void onGameExit() { + public void onGameExit() {} - } + @Override + public void openDonations() {} }; return new Mindustry();