Saving/loading of game state done

This commit is contained in:
Anuken 2017-08-07 15:09:01 -04:00
parent 53b812dde4
commit 3c9cc97400
15 changed files with 383 additions and 38 deletions

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd">
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "https://raw.githubusercontent.com/gwtproject/gwt/master/distro-source/core/src/gwt-module.dtd">
<module>
<source path="io/anuke/mindustry" />
</module>

View File

@ -16,6 +16,7 @@ import io.anuke.mindustry.entities.enemies.*;
import io.anuke.mindustry.input.AndroidInput;
import io.anuke.mindustry.input.GestureHandler;
import io.anuke.mindustry.input.Input;
import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.ProductionBlocks;
import io.anuke.ucore.core.*;
@ -33,7 +34,6 @@ public class Control extends RendererModule{
boolean hiscore = false;
final Array<Weapon> weapons = new Array<>();
//final ObjectMap<Weapon, Boolean> weapons = new ObjectMap<Weapon, Boolean>();
int wave = 1;
float wavetime;
@ -149,6 +149,12 @@ public class Control extends RendererModule{
return weapons;
}
public void setWaveData(int enemies, int wave, float wavetime){
this.wave = wave;
this.wavetime = wavetime;
this.enemies = enemies;
}
void runWave(){
int amount = wave;
Sounds.play("spawn");
@ -269,13 +275,26 @@ public class Control extends RendererModule{
@Override
public void update(){
if(Inputs.keyUp(Keys.ESCAPE) && debug)
Gdx.app.exit();
if(debug){
if(Inputs.keyUp(Keys.ESCAPE))
Gdx.app.exit();
//camera.zoom = MathUtils.lerp(camera.zoom, targetzoom, 0.5f*delta());
if(Inputs.keyUp(Keys.SPACE))
Effects.sound("shoot", World.core.worldx(), World.core.worldy());
if(Inputs.keyUp(Keys.SPACE) && debug)
Effects.sound("shoot", World.core.worldx(), World.core.worldy());
if(Inputs.keyUp(Keys.O)){
Timers.mark();
SaveIO.write(Gdx.files.local("mapsave.mds"));
log("Save time taken: " + Timers.elapsed());
}
if(Inputs.keyUp(Keys.P)){
Timers.mark();
SaveIO.load(Gdx.files.local("mapsave.mds"));
log("Load time taken: " + Timers.elapsed());
Renderer.clearTiles();
}
}
if(GameState.is(State.menu)){
clearScreen();

View File

@ -57,4 +57,8 @@ public class Inventory{
items.put(req.item, items.get(req.item, 0)-req.amount);
ui.updateItems();
}
public static ObjectMap<Item, Integer> getItems(){
return items;
}
}

View File

@ -136,6 +136,13 @@ public class Renderer{
Draw.thickness(2f);
Draw.square(x, y, tilesize / 2 + MathUtils.sin(Timers.time() / 6f) + 1);
if(android){
//TODO
Draw.thickness(1f);
Draw.color(Color.ORANGE);
Draw.square(x + tilesize/2, y + tilesize/2, tilesize/4);
}
if(player.recipe.result.rotate){
Draw.color("orange");
Tmp.v1.set(7, 0).rotate(player.rotation * 90);

View File

@ -18,8 +18,7 @@ public class Vars{
public static final int baseCameraScale = Math.round(Unit.dp.inPixels(4));
public static final int zoomScale = Math.round(Unit.dp.inPixels(1));
//not marked as final, because of warnings
public static boolean debug = false;
public static final boolean debug = true;
//turret and enemy shoot speed inverse multiplier
public static final int multiplier = android ? 3 : 1;

View File

@ -5,6 +5,7 @@ import static io.anuke.mindustry.Vars.*;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
@ -26,6 +27,7 @@ import io.anuke.ucore.util.Mathf;
public class World{
public static int worldsize = 128;
public static int pixsize = worldsize*tilesize;
private static int seed;
private static Pixmap[] mapPixmaps;
private static Texture[] mapTextures;
@ -87,6 +89,10 @@ public class World{
}
public static void loadMap(int id){
loadMap(id, MathUtils.random(0, 99999));
}
public static void loadMap(int id, int seed){
spawnpoints.clear();
@ -104,6 +110,7 @@ public class World{
Entities.resizeTree(0, 0, pixsize, pixsize);
World.seed = seed;
Generator.generate(mapPixmaps[id]);
Pathfind.reset();
@ -137,6 +144,10 @@ public class World{
tiles[x][y].rotation = rot;
}
public static int getSeed(){
return seed;
}
public static boolean validPlace(int x, int y, Block type){
if(!cursorNear() && !android)

View File

@ -18,7 +18,6 @@ public class TileEntity extends Entity{
public int maxhealth, health;
public boolean dead = false;
public TileEntity init(Tile tile){
this.tile = tile;
x = tile.worldx();

View File

@ -15,20 +15,22 @@ import io.anuke.ucore.entities.*;
import io.anuke.ucore.util.Timers;
public class Enemy extends DestructibleEntity{
public Vector2 direction = new Vector2();
protected float speed = 0.3f;
protected float reload = 40;
protected float range = 60;
protected float length = 4;
protected float rotatespeed = 8f;
protected BulletType bullet = BulletType.small;
protected String shootsound = "enemyshoot";
public Tile[] path;
public float xvelocity, yvelocity;
public float speed = 0.3f;
public int node = -1;
public Entity target;
public int spawn;
public float reload = 40;
public float range = 60;
public BulletType bullet = BulletType.small;
public float length = 4;
public float rotatespeed = 8f;
public String shootsound = "enemyshoot";
public boolean dead = false;
public int node = -1;
public Vector2 direction = new Vector2();
public float xvelocity, yvelocity;
public Entity target;
public Enemy(int spawn){
this.spawn = spawn;
@ -64,7 +66,7 @@ public class Enemy extends DestructibleEntity{
}
}
public void shoot(){
void shoot(){
vector.set(length, 0).rotate(direction.angle());
Bullet out = new Bullet(bullet, this, x+vector.x, y+vector.y, direction.angle()).add();
out.damage = bullet.damage*Vars.multiplier;
@ -97,7 +99,7 @@ public class Enemy extends DestructibleEntity{
move();
xvelocity = x - lastx;
yvelocity = y-lasty;
yvelocity = y - lasty;
if(target == null){
direction.add(xvelocity, yvelocity);

View File

@ -0,0 +1,299 @@
package io.anuke.mindustry.io;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.reflect.ClassReflection;
import io.anuke.mindustry.Inventory;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.World;
import io.anuke.mindustry.entities.Weapon;
import io.anuke.mindustry.entities.enemies.*;
import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.Entity;
/*
* Save format:
*
* --STATE DATA--
* Wave (int)
* Wave countdown time (float)
*
* 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)
* health (int)
*
*
* --MAP DATA--
* Map ID (byte)
* Seed (int)
* Amount of tiles (int)
* (tile list)
* Tile position, as a single integer, in the format x+y*width
* Tile type (boolean)- whether the block has a tile entity attached
* Block ID - the block ID
* (the following only applies to tile entity blocks)
* Block health (int)
* Amount of items (byte)
* (item list)
* Item ID (byte)
* Item amount (int)
*
*/
public class SaveIO{
//TODO automatic registration of types?
private static final ObjectMap<Class<? extends Enemy>, Byte> enemyIDs = new ObjectMap<Class<? extends Enemy>, Byte>(){{
put(Enemy.class, (byte)0);
put(FastEnemy.class, (byte)1);
put(BossEnemy.class, (byte)2);
put(FlameEnemy.class, (byte)3);
}};
private static final ObjectMap<Byte, Class<? extends Enemy>> idEnemies = new ObjectMap<Byte, Class<? extends Enemy>>(){{
for(Class<? extends Enemy> value : enemyIDs.keys())
put(enemyIDs.get(value), value);
}};
public static void write(FileHandle file){
try(DataOutputStream stream = new DataOutputStream(file.write(false))){
//--GENERAL STATE--
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--
stream.writeByte(Inventory.getItems().size); //amount of items
for(Item item : Inventory.getItems().keys()){
stream.writeByte(item.ordinal()); //item ID
stream.writeInt(Inventory.getAmount(item)); //item amount
}
//--ENEMIES--
int totalEnemies = 0;
for(Entity entity : Entities.all()){
if(entity instanceof Enemy){
totalEnemies ++;
}
}
stream.writeInt(totalEnemies); //enemy amount
for(Entity entity : Entities.all()){
if(entity instanceof Enemy){
Enemy enemy = (Enemy)entity;
stream.writeByte(enemyIDs.get(enemy.getClass())); //type
stream.writeByte(enemy.spawn); //lane
stream.writeFloat(enemy.x); //x
stream.writeFloat(enemy.y); //y
stream.writeInt(enemy.health); //health
}
}
//--MAP DATA--
//map ID
stream.writeByte(World.getMap());
//seed
stream.writeInt(World.getSeed());
int totalblocks = 0;
for(int x = 0; x < World.width(); x ++){
for(int y = 0; y < World.height(); y ++){
Tile tile = World.tile(x, y);
if(tile.breakable()){
totalblocks ++;
}
}
}
//tile amount
stream.writeInt(totalblocks);
for(int x = 0; x < World.width(); x ++){
for(int y = 0; y < World.height(); y ++){
Tile tile = World.tile(x, y);
if(tile.breakable()){
stream.writeInt(x + y*World.width()); //tile pos
stream.writeBoolean(tile.entity != null); //whether it has a tile entity
stream.writeInt(tile.block().id); //block ID
if(tile.entity != null){
stream.writeInt(tile.entity.health); //health
stream.writeByte(tile.entity.items.size); //amount of items
for(Item item : tile.entity.items.keys()){
stream.writeByte(item.ordinal()); //item ID
stream.writeInt(tile.entity.items.get(item)); //item amount
}
}
}
}
}
}catch (IOException e){
throw new RuntimeException(e);
}
}
public static void load(FileHandle file){
try(DataInputStream stream = new DataInputStream(file.read())){
Item[] itemEnums = Item.values();
//general state
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.camera.position.set(playerx, playery, 0);
//weapons
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();
Inventory.getItems().clear();
for(int i = 0; i < totalItems; i ++){
Item item = itemEnums[stream.readByte()];
int amount = stream.readInt();
Inventory.getItems().put(item, amount);
}
Vars.ui.updateItems();
//enemies
int enemies = stream.readInt();
for(int i = 0; i < enemies; i ++){
byte type = stream.readByte();
int lane = stream.readByte();
float x = stream.readFloat();
float y = stream.readFloat();
int health = stream.readInt();
try{
Enemy enemy = (Enemy)ClassReflection.getConstructor(idEnemies.get(type), int.class).newInstance(lane);
enemy.health = health;
enemy.x = x;
enemy.y = y;
enemy.add();
}catch (Exception e){
throw new RuntimeException(e);
}
}
Vars.control.setWaveData(enemies, wave, wavetime);
//map
int mapid = stream.readByte();
int seed = stream.readInt();
int tiles = stream.readInt();
World.loadMap(mapid, seed);
for(int x = 0; x < World.width(); x ++){
for(int y = 0; y < World.height(); y ++){
Tile tile = World.tile(x, y);
//remove breakables like rocks
if(tile.breakable()){
World.tile(x, y).setBlock(Blocks.air);
}
}
}
for(int i = 0; i < tiles; i ++){
int pos = stream.readInt();
boolean hasEntity = stream.readBoolean();
int blockid = stream.readInt();
Tile tile = World.tile(pos % World.width(), pos / World.width());
tile.setBlock(Block.getByID(blockid));
if(hasEntity){
int health = stream.readInt();
int items = stream.readByte();
tile.entity.health = health;
for(int j = 0; j < items; j ++){
int itemid = stream.readByte();
int itemamount = stream.readInt();
tile.entity.items.put(itemEnums[itemid], itemamount);
}
}
}
}catch (IOException e){
throw new RuntimeException(e);
}
}
}

View File

@ -1,7 +0,0 @@
package io.anuke.mindustry.io;
import io.anuke.mindustry.world.Tile;
public class SaveState{
public Tile[][] tiles;
}

View File

@ -3,6 +3,7 @@ package io.anuke.mindustry.world;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.World;
import io.anuke.mindustry.entities.TileEntity;
@ -13,6 +14,7 @@ import io.anuke.ucore.util.Mathf;
public class Block{
private static int lastid;
private static Array<Block> blocks = new Array<Block>();
protected static Vector2 vector = new Vector2();
protected static Vector2 vector2 = new Vector2();
@ -30,6 +32,8 @@ public class Block{
public boolean vary = true;
public Block(String name) {
blocks.add(this);
this.name = name;
this.solid = false;
this.id = lastid++;
@ -187,4 +191,12 @@ public class Block{
}
}
public static Array<Block> getAllBlocks(){
return blocks;
}
public static Block getByID(int id){
return blocks.get(id);
}
}

View File

@ -2,7 +2,6 @@ package io.anuke.mindustry.world;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.ObjectMap;
import io.anuke.mindustry.World;
@ -28,7 +27,8 @@ public class Generator{
/**Returns world size.*/
public static void generate(Pixmap pixmap){
Noise.setSeed(MathUtils.random(0, 99999));
Noise.setSeed(World.getSeed());
for(int x = 0; x < pixmap.getWidth(); x ++){
for(int y = 0; y < pixmap.getHeight(); y ++){
Block floor = Blocks.stone;

BIN
desktop/mapsave.mds Normal file

Binary file not shown.

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd">
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "https://raw.githubusercontent.com/gwtproject/gwt/master/distro-source/core/src/gwt-module.dtd">
<module rename-to="html">
<inherits name='com.badlogic.gdx.backends.gdx_backends_gwt' />
<inherits name='com.badlogic.gdx.ai' />

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "http://google-web-toolkit.googlecode.com/svn/trunk/distro-source/core/src/gwt-module.dtd">
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit trunk//EN" "https://raw.githubusercontent.com/gwtproject/gwt/master/distro-source/core/src/gwt-module.dtd">
<module rename-to="html">
<inherits name='com.badlogic.gdx.backends.gdx_backends_gwt' />
<inherits name='com.badlogic.gdx.physics.box2d.box2d-gwt' />