diff --git a/build.gradle b/build.gradle index 7a96dfdab5..9e202046e3 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ allprojects { appName = "Mindustry" gdxVersion = '1.9.8' aiVersion = '1.8.1' - uCoreVersion = '4fb2c63'; + uCoreVersion = '59488d7'; } repositories { diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 009fc28948..291bf3401e 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -12,6 +12,8 @@ text.savegame=Save Game text.loadgame=Load Game text.joingame=Join Game text.quit=Quit +text.server.connected=A player has joined. +text.server.disconnected=A player has disconnected. text.hostserver=Host Server text.joingame.title=Join Game text.joingame.ip=IP: diff --git a/core/assets/ui/uiskin.json b/core/assets/ui/uiskin.json index 8c6e025c8f..ab6dec7b92 100644 --- a/core/assets/ui/uiskin.json +++ b/core/assets/ui/uiskin.json @@ -46,7 +46,7 @@ io.anuke.ucore.scene.ui.TextButton$TextButtonStyle: { toggle: {font: default-font, fontColor: white, checked: button-down, down: button-down, up: button, over: button-over, disabled: button, disabledFontColor: grey } }, io.anuke.ucore.scene.ui.ImageButton$ImageButtonStyle: { - default: {down: button-down, up: button, over: button-over }, + default: {down: button-down, up: button, over: button-over, imageDisabledColor: lightgray, imageUpColor: white }, empty: { imageDownColor: accent, imageUpColor: white}, emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: lightgray}, static: {up: button }, diff --git a/core/src/io/anuke/mindustry/Mindustry.java b/core/src/io/anuke/mindustry/Mindustry.java index 5c8a994b15..fe5f0e054e 100644 --- a/core/src/io/anuke/mindustry/Mindustry.java +++ b/core/src/io/anuke/mindustry/Mindustry.java @@ -1,16 +1,13 @@ package io.anuke.mindustry; -import java.util.Date; -import java.util.Locale; - import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; - import com.badlogic.gdx.utils.I18NBundle; 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; @@ -21,6 +18,9 @@ import io.anuke.ucore.function.Callable; import io.anuke.ucore.modules.ModuleCore; import io.anuke.ucore.scene.ui.TextField; +import java.util.Date; +import java.util.Locale; + public class Mindustry extends ModuleCore { public static Callable donationsCallable; public static boolean hasDiscord = true; @@ -81,16 +81,9 @@ public class Mindustry extends ModuleCore { @Override public void render(){ + super.render(); - try{ - super.render(); - }catch (RuntimeException e){ - //TODO display error log - //Gdx.app.getClipboard().setContents(e.getMessage()); - throw e; - } - - if(!GameState.is(State.paused)){ + if(!GameState.is(State.paused) || Net.active()){ Timers.update(); } diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index fda0b73ec8..7358306aac 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -20,7 +20,7 @@ public class Vars{ //respawn time in frames public static final float respawnduration = 60*4; //time between waves in frames (on normal mode) - public static final float wavespace = 60*60*(android ? 1 : 1); + public static final float wavespace = 10*60*(android ? 1 : 1); //TODO revert //waves can last no longer than 3 minutes, otherwise the next one spawns public static final float maxwavespace = 60*60*4f; //advance time the pathfinding starts at diff --git a/core/src/io/anuke/mindustry/ai/Pathfind.java b/core/src/io/anuke/mindustry/ai/Pathfind.java index 63d64d9250..e89ef2ea99 100644 --- a/core/src/io/anuke/mindustry/ai/Pathfind.java +++ b/core/src/io/anuke/mindustry/ai/Pathfind.java @@ -8,6 +8,7 @@ import com.badlogic.gdx.math.Vector2; import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.mindustry.net.Net; import io.anuke.mindustry.world.SpawnPoint; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.util.Angles; @@ -101,20 +102,28 @@ public class Pathfind{ } public void update(){ + int index = 0; + for(SpawnPoint point : Vars.control.getSpawnPoints()){ if(!point.request.pathFound){ try{ if(point.finder.search(point.request, ms)){ smoother.smoothPath(point.path); point.pathTiles = point.path.nodes.toArray(Tile.class); - point.tempTiles = point.path.nodes.toArray(Tile.class); + + if(Net.active() && Net.server()){ + Vars.netServer.handlePathFound(index, point.pathTiles); + } } }catch (ArrayIndexOutOfBoundsException e){ //no path point.request.pathFound = true; } } + + index ++; } + } public boolean finishedUpdating(){ @@ -133,7 +142,6 @@ public class Pathfind{ point.path.clear(); point.pathTiles = null; - point.tempTiles = null; point.request = new PathFinderRequest(point.start, Vars.control.getCore(), heuristic, point.path); point.request.statusChanged = true; //IMPORTANT! @@ -141,15 +149,15 @@ public class Pathfind{ } void findNode(Enemy enemy){ - if(enemy.spawn >= Vars.control.getSpawnPoints().size){ - enemy.spawn = 0; + if(enemy.lane >= Vars.control.getSpawnPoints().size){ + enemy.lane = 0; } - if(Vars.control.getSpawnPoints().get(enemy.spawn).pathTiles == null){ + if(Vars.control.getSpawnPoints().get(enemy.lane).pathTiles == null){ return; } - enemy.path = Vars.control.getSpawnPoints().get(enemy.spawn).pathTiles; + enemy.path = Vars.control.getSpawnPoints().get(enemy.lane).pathTiles; int closest = findClosest(enemy.path, enemy.x, enemy.y); diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 91e5bb4ccc..1f935e08e3 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -48,7 +48,7 @@ public class Control extends Module{ final Array weapons = new Array<>(); final int[] items = new int[Item.getAllItems().size]; - public final EntityGroup enemyGroup = Entities.addGroup(Enemy.class); + public final EntityGroup enemyGroup = Entities.addGroup(Enemy.class).enableMapping(); public final EntityGroup tileGroup = Entities.addGroup(TileEntity.class, false); public final EntityGroup bulletGroup = Entities.addGroup(Bullet.class); public final EntityGroup shieldGroup = Entities.addGroup(Shield.class); @@ -348,10 +348,11 @@ public class Control extends Module{ try{ Enemy enemy = ClassReflection.newInstance(spawn.type); enemy.set(tile.worldx() + Mathf.range(range), tile.worldy() + Mathf.range(range)); - enemy.spawn = fl; + enemy.lane = fl; enemy.tier = spawn.tier(wave, fl); + enemy.add(); + Effects.effect(Fx.spawn, enemy); - enemy.add(enemyGroup); Vars.netServer.handleEnemySpawn(enemy); @@ -612,7 +613,7 @@ public class Control extends Module{ if(!GameState.is(State.menu)){ input.update(); - if(Inputs.keyTap("pause") && !ui.isGameOver() && (GameState.is(State.paused) || GameState.is(State.playing))){ + if(Inputs.keyTap("pause") && !ui.isGameOver() && !Net.active() && (GameState.is(State.paused) || GameState.is(State.playing))){ GameState.set(GameState.is(State.playing) ? State.paused : State.playing); } @@ -626,7 +627,7 @@ public class Control extends Module{ } } - if(!GameState.is(State.paused)){ + if(!GameState.is(State.paused) || Net.active()){ if(respawntime > 0){ @@ -650,7 +651,7 @@ public class Control extends Module{ if(enemies <= 0){ wavetime -= delta(); - if(lastUpdated < wave + 1 && wavetime < Vars.aheadPathfinding){ //start updatingbeforehand + if(lastUpdated < wave + 1 && wavetime < Vars.aheadPathfinding){ //start updating beforehand world.pathfinder().updatePath(); lastUpdated = wave + 1; } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 5f581b30ad..f3c9511571 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -1,17 +1,29 @@ package io.anuke.mindustry.core; import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.utils.reflect.ClassReflection; +import com.badlogic.gdx.utils.reflect.ReflectionException; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.entities.Bullet; +import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.io.SaveIO; +import io.anuke.mindustry.entities.enemies.Enemy; +import io.anuke.mindustry.graphics.Fx; +import io.anuke.mindustry.io.NetworkIO; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Packets.*; +import io.anuke.mindustry.net.Syncable; +import io.anuke.mindustry.net.Syncable.Interpolator; import io.anuke.mindustry.resource.Weapon; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; import io.anuke.ucore.UCore; +import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Timers; +import io.anuke.ucore.entities.BaseBulletType; +import io.anuke.ucore.entities.Entity; import io.anuke.ucore.modules.Module; import java.util.Arrays; @@ -46,7 +58,8 @@ public class NetClient extends Module { Net.handle(WorldData.class, data -> { Gdx.app.postRunnable(() -> { UCore.log("Recieved world data: " + data.stream.available() + " bytes."); - SaveIO.load(data.stream); + NetworkIO.load(data.stream); + Vars.player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2); GameState.set(State.playing); connecting = false; @@ -76,11 +89,19 @@ public class NetClient extends Module { Net.handle(SyncPacket.class, packet -> { if(!gotEntities) return; + //TODO awful code for(int i = 0; i < packet.ids.length; i ++){ int id = packet.ids[i]; if(id != Vars.player.id){ - Player player = Vars.control.playerGroup.getByID(id); - player.getInterpolator().type.read(player, packet.data[i]); + Entity entity = null; + if(id >= packet.enemyStart){ + entity = Vars.control.enemyGroup.getByID(id); + }else { + entity = Vars.control.playerGroup.getByID(id); + } + + //augh + ((Interpolator)((Syncable)entity).getInterpolator()).type.read(entity, packet.data[i]); } } }); @@ -92,20 +113,62 @@ public class NetClient extends Module { weapon.shoot(player, packet.x, packet.y, packet.rotation); }); - Net.handleServer(PlacePacket.class, packet -> { + Net.handle(PlacePacket.class, packet -> { Vars.control.input.placeBlockInternal(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, false); }); - Net.handleServer(BreakPacket.class, packet -> { + Net.handle(BreakPacket.class, packet -> { Vars.control.input.breakBlockInternal(packet.x, packet.y, false); }); - Net.handleServer(StateSyncPacket.class, packet -> { + Net.handle(StateSyncPacket.class, packet -> { //TODO replace with arraycopy() for(int i = 0; i < packet.items.length; i ++){ Vars.control.items[i] = packet.items[i]; } Vars.control.setWaveData(packet.enemies, packet.wave, packet.countdown); + + Gdx.app.postRunnable(() -> { + Vars.ui.updateItems(); + }); + }); + + Net.handle(EnemySpawnPacket.class, spawn -> { + Gdx.app.postRunnable(() -> { + try{ + Enemy enemy = ClassReflection.newInstance(spawn.type); + enemy.set(spawn.x, spawn.y); + enemy.tier = spawn.tier; + enemy.lane = spawn.lane; + enemy.id = spawn.id; + enemy.add(); + + Effects.effect(Fx.spawn, enemy); + }catch (ReflectionException e){ + throw new RuntimeException(e); + } + }); + }); + + Net.handle(EnemyDeathPacket.class, spawn -> { + Enemy enemy = Vars.control.enemyGroup.getByID(spawn.id); + if(enemy != null) enemy.onDeath(); + }); + + Net.handle(PathPacket.class, packet -> { + Tile[] tiles = new Tile[packet.path.length]; + for(int i = 0; i < tiles.length; i ++){ + int c = packet.path[i]; + tiles[i] = Vars.world.tile(c % Vars.world.width(), c / Vars.world.width()); + } + Vars.control.spawnpoints.get(packet.index).pathTiles = tiles; + }); + + Net.handle(BulletPacket.class, packet -> { + //TODO shoot effects for enemies, clientside as well as serverside + BulletType type = (BulletType) BaseBulletType.getByID(packet.type); + Entity owner = Vars.control.enemyGroup.getByID(packet.owner); + Bullet bullet = new Bullet(type, owner, packet.x, packet.y, packet.angle).add(); }); } diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 00509eba94..6b1b87e349 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -5,9 +5,10 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.utils.IntMap; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.io.SaveIO; +import io.anuke.mindustry.io.NetworkIO; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net.SendMode; import io.anuke.mindustry.net.Packets.*; @@ -15,9 +16,11 @@ import io.anuke.mindustry.resource.ItemStack; import io.anuke.mindustry.resource.Recipe; import io.anuke.mindustry.resource.Weapon; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; import io.anuke.ucore.UCore; import io.anuke.ucore.core.Effects.Effect; import io.anuke.ucore.core.Timers; +import io.anuke.ucore.entities.Entity; import io.anuke.ucore.modules.Module; import java.io.ByteArrayInputStream; @@ -27,7 +30,7 @@ import java.util.Arrays; public class NetServer extends Module{ IntMap connections = new IntMap<>(); - float serverSyncTime = 4, itemSyncTime = 20, blockSyncTime = 120; + float serverSyncTime = 4, itemSyncTime = 10, blockSyncTime = 120; public NetServer(){ @@ -36,7 +39,7 @@ public class NetServer extends Module{ WorldData data = new WorldData(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); - SaveIO.write(stream); + NetworkIO.write(stream); UCore.log("Packed " + stream.size() + " uncompressed bytes of data."); @@ -46,6 +49,8 @@ public class NetServer extends Module{ Net.sendStream(packet.id, data); Gdx.app.postRunnable(() -> { + Vars.ui.showInfo("$text.server.connected"); + EntityDataPacket dp = new EntityDataPacket(); Player player = new Player(); @@ -60,10 +65,18 @@ public class NetServer extends Module{ UCore.log("Sending entities: " + Arrays.toString(dp.players)); + //TODO send pathfind positions + //TODO new denser format + //TODO save enemy nodes + Net.sendTo(packet.id, dp, SendMode.tcp); }); }); + Net.handleServer(Disconnect.class, packet -> { + Gdx.app.postRunnable(() -> Vars.ui.showInfo("$text.server.disconnected")); + }); + Net.handleServer(PositionPacket.class, pos -> { Player player = connections.get(Net.getLastConnection()); player.getInterpolator().type.read(player, pos.data); @@ -112,8 +125,45 @@ public class NetServer extends Module{ Net.send(packet, SendMode.udp); } - public void handleEnemySpawn(Enemy enemy){ + public void handleBullet(BulletType type, Entity owner, float x, float y, float angle, short damage){ + BulletPacket packet = new BulletPacket(); + packet.x = x; + packet.y = y; + packet.angle = angle; + packet.damage = damage; + packet.owner = owner.id; + packet.type = type.id; + Net.send(packet, SendMode.udp); + } + public void handlePathFound(int index, Tile[] tiles){ + PathPacket packet = new PathPacket(); + int[] out = new int[tiles.length]; + + for(int p = 0; p < out.length; p ++){ + out[p] = tiles[p].packedPosition(); + } + + packet.path = out; + packet.index = (byte)index; + Net.send(packet, SendMode.tcp); + } + + public void handleEnemySpawn(Enemy enemy){ + EnemySpawnPacket packet = new EnemySpawnPacket(); + packet.type = enemy.getClass(); + packet.lane = (byte)enemy.lane; + packet.tier = (byte)enemy.tier; + packet.x = enemy.x; + packet.y = enemy.y; + packet.id = enemy.id; + Net.send(packet, SendMode.tcp); + } + + public void handleEnemyDeath(Enemy enemy){ + EnemyDeathPacket packet = new EnemyDeathPacket(); + packet.id = enemy.id; + Net.send(packet, SendMode.tcp); } public void update(){ @@ -130,7 +180,7 @@ public class NetServer extends Module{ if(Timers.get("serverSync", serverSyncTime)){ SyncPacket packet = new SyncPacket(); - int amount = Vars.control.playerGroup.amount(); + int amount = Vars.control.playerGroup.amount() + Vars.control.enemyGroup.amount(); packet.ids = new int[amount]; packet.data = new float[amount][0]; @@ -144,6 +194,16 @@ public class NetServer extends Module{ index ++; } + packet.enemyStart = index; + + for(Enemy enemy : Vars.control.enemyGroup.all()){ + float[] out = enemy.getInterpolator().type.write(enemy); + packet.data[index] = out; + packet.ids[index] = enemy.id; + + index ++; + } + Net.send(packet, SendMode.udp); } @@ -160,7 +220,7 @@ public class NetServer extends Module{ if(Timers.get("serverBlockSync", blockSyncTime)){ BlockSyncPacket packet = new BlockSyncPacket(); - + //TODO } } @@ -170,7 +230,7 @@ public class NetServer extends Module{ ByteArrayOutputStream bs = new ByteArrayOutputStream(); DataOutputStream stream = new DataOutputStream(bs); - + //TODO packet.stream = new ByteArrayInputStream(bs.toByteArray()); } diff --git a/core/src/io/anuke/mindustry/core/UI.java b/core/src/io/anuke/mindustry/core/UI.java index ed174a83fb..fb374ab12b 100644 --- a/core/src/io/anuke/mindustry/core/UI.java +++ b/core/src/io/anuke/mindustry/core/UI.java @@ -24,6 +24,7 @@ import io.anuke.ucore.function.Listenable; import io.anuke.ucore.modules.SceneModule; import io.anuke.ucore.scene.Element; import io.anuke.ucore.scene.Skin; +import io.anuke.ucore.scene.actions.Actions; import io.anuke.ucore.scene.builders.build; import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.table; @@ -258,7 +259,7 @@ public class UI extends SceneModule{ prefs.hidden(()->{ if(!GameState.is(State.menu)){ - if(!wasPaused) + if(!wasPaused || Net.active()) GameState.set(State.playing); } }); @@ -269,7 +270,7 @@ public class UI extends SceneModule{ if(menu.getScene() != null){ wasPaused = menu.wasPaused; } - GameState.set(State.paused); + if(!Net.active()) GameState.set(State.paused); menu.hide(); } }); @@ -467,6 +468,10 @@ public class UI extends SceneModule{ if(tooltip != null) tooltip.hide(); } + + public void showInfo(String info){ + scene.table().add(info).get().getParent().actions(Actions.fadeOut(4f), Actions.removeActor()); + } public void showAbout(){ about.show(); diff --git a/core/src/io/anuke/mindustry/core/World.java b/core/src/io/anuke/mindustry/core/World.java index 8c5cd7195f..1cff20a251 100644 --- a/core/src/io/anuke/mindustry/core/World.java +++ b/core/src/io/anuke/mindustry/core/World.java @@ -1,25 +1,31 @@ package io.anuke.mindustry.core; -import static io.anuke.mindustry.Vars.*; - import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; - import io.anuke.mindustry.Vars; import io.anuke.mindustry.ai.Pathfind; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.io.Maps; -import io.anuke.mindustry.world.*; -import io.anuke.mindustry.world.blocks.*; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Generator; +import io.anuke.mindustry.world.Map; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Blocks; +import io.anuke.mindustry.world.blocks.DistributionBlocks; +import io.anuke.mindustry.world.blocks.ProductionBlocks; +import io.anuke.mindustry.world.blocks.WeaponBlocks; import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entity; import io.anuke.ucore.modules.Module; import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Tmp; +import static io.anuke.mindustry.Vars.*; + public class World extends Module{ private int seed; @@ -36,7 +42,8 @@ public class World extends Module{ @Override public void update(){ - pathfind.update(); + if(!(Net.active() && Net.client())) + pathfind.update(); } @Override diff --git a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java index 60e901d038..95a0f13451 100644 --- a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java +++ b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java @@ -3,21 +3,32 @@ package io.anuke.mindustry.entities.enemies; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.reflect.ClassReflection; - import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.Bullet; +import io.anuke.mindustry.entities.BulletType; +import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Shaders; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.net.Syncable; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.UCore; -import io.anuke.ucore.core.*; -import io.anuke.ucore.entities.*; -import io.anuke.ucore.util.*; +import io.anuke.ucore.core.Draw; +import io.anuke.ucore.core.Effects; +import io.anuke.ucore.core.Graphics; +import io.anuke.ucore.core.Timers; +import io.anuke.ucore.entities.DestructibleEntity; +import io.anuke.ucore.entities.Entities; +import io.anuke.ucore.entities.Entity; +import io.anuke.ucore.entities.SolidEntity; +import io.anuke.ucore.util.Angles; +import io.anuke.ucore.util.Mathf; +import io.anuke.ucore.util.Timer; +import io.anuke.ucore.util.Tmp; import static io.anuke.mindustry.Vars.world; -public class Enemy extends DestructibleEntity{ +public class Enemy extends DestructibleEntity implements Syncable{ public final static Color[] tierColors = { Color.valueOf("ffe451"), Color.valueOf("f48e20"), Color.valueOf("ff6757"), Color.valueOf("ff2d86") }; public final static int maxtier = 4; public final static float maxIdle = 60*1.5f; @@ -37,17 +48,19 @@ public class Enemy extends DestructibleEntity{ protected int damage; protected Enemy spawner; protected int spawned = 0; - protected float angle; protected boolean targetCore = false; protected boolean stopNearCore = true; protected float mass = 1f; protected String className; - + + protected Interpolator inter = new Interpolator(SyncType.enemy); + public float idletime = 0f; - public int spawn; + public int lane; public int node = -1; public Tile[] path; + public float angle; public float xvelocity, yvelocity; public Entity target; public int tier = 1; @@ -65,11 +78,20 @@ public class Enemy extends DestructibleEntity{ className = ClassReflection.getSimpleName(getClass()).toLowerCase(); } + public Interpolator getInterpolator() { + return inter; + } + public float drawSize(){ return 12; } void move(){ + if(Net.client() && Net.active()){ + inter.update(this); + return; + } + Tile core = Vars.control.getCore(); if(idletime > maxIdleLife){ @@ -152,9 +174,21 @@ public class Enemy extends DestructibleEntity{ } void shoot(BulletType bullet, float rotation){ - Angles.translation(angle + rotation, length); - Bullet out = new Bullet(bullet, this, x + Angles.x(), y + Angles.y(), this.angle + rotation).add(); - out.damage = (int) (damage * Vars.multiplier); + + if(!(Net.active() && Net.client())) { + Angles.translation(angle + rotation, length); + Bullet out = new Bullet(bullet, this, x + Angles.x(), y + Angles.y(), this.angle + rotation).add(); + out.damage = (int) (damage * Vars.multiplier); + onShoot(bullet, rotation); + + if(Net.active() && Net.server()){ + Vars.netServer.handleBullet(bullet, this, x + Angles.x(), y + Angles.y(), this.angle + rotation, (short) (damage * Vars.multiplier)); + } + } + } + + void onShoot(BulletType type, float rotation){ + } @Override @@ -184,6 +218,10 @@ public class Enemy extends DestructibleEntity{ Effects.sound("bang2", this); remove(); dead = true; + + if(Net.active() && Net.server()){ + Vars.netServer.handleEnemyDeath(this); + } } @Override @@ -258,7 +296,7 @@ public class Enemy extends DestructibleEntity{ } @Override - public T add(){ - return (T) add(Vars.control.enemyGroup); + public Enemy add(){ + return add(Vars.control.enemyGroup); } } diff --git a/core/src/io/anuke/mindustry/entities/enemies/FortressEnemy.java b/core/src/io/anuke/mindustry/entities/enemies/FortressEnemy.java index 45c9a659fa..2407149fa5 100644 --- a/core/src/io/anuke/mindustry/entities/enemies/FortressEnemy.java +++ b/core/src/io/anuke/mindustry/entities/enemies/FortressEnemy.java @@ -38,7 +38,7 @@ public class FortressEnemy extends Enemy{ Angles.translation(angle, 20f); FastEnemy enemy = new FastEnemy(); - enemy.spawn = spawn; + enemy.lane = lane; enemy.tier = this.tier; enemy.spawner = this; enemy.set(x + Angles.x(), y + Angles.y()); @@ -52,10 +52,9 @@ public class FortressEnemy extends Enemy{ speed = 0.001f; } } - - @Override - public void shoot(BulletType type){ - super.shoot(bullet); + + + void onShoot(BulletType type, float rotation){ Effects.effect(Fx.largeCannonShot, x + Angles.x(), y + Angles.y(), angle); Effects.shake(3f, 3f, this); } diff --git a/core/src/io/anuke/mindustry/io/NetworkIO.java b/core/src/io/anuke/mindustry/io/NetworkIO.java new file mode 100644 index 0000000000..44f09db95a --- /dev/null +++ b/core/src/io/anuke/mindustry/io/NetworkIO.java @@ -0,0 +1,304 @@ +package io.anuke.mindustry.io; + +import com.badlogic.gdx.files.FileHandle; +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.world.Block; +import io.anuke.mindustry.world.GameMode; +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.mindustry.world.blocks.types.production.Generator; +import io.anuke.ucore.UCore; +import io.anuke.ucore.core.Timers; +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; + +public class NetworkIO { + private static final int fileVersionID = 13; + + public static void write(OutputStream os){ + + try(DataOutputStream stream = new DataOutputStream(os)){ + + //--META-- + stream.writeInt(fileVersionID); //version id + stream.writeFloat(Timers.time()); //timer time + stream.writeLong(TimeUtils.millis()); //timestamp + + //--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 + + //--INVENTORY-- + + for(int i = 0; i < Vars.control.getItems().length; i ++){ + stream.writeInt(Vars.control.getItems()[i]); + } + + //--ENEMIES-- + + int totalEnemies = 0; + + for(Enemy entity : Vars.control.enemyGroup.all()){ + if(idEnemies.containsKey(entity.getClass())){ + totalEnemies ++; + } + } + + stream.writeInt(totalEnemies); //enemy amount + + for(Enemy enemy : Vars.control.enemyGroup.all()){ + if(idEnemies.containsKey(enemy.getClass())){ + stream.writeInt(enemy.id); + 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 + stream.writeShort(enemy.node); //current node + } + } + + //--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()); + } + } + } + + //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() && !(tile.block() instanceof Rock)){ + + stream.writeInt(x + y*Vars.world.width()); //tile pos + //TODO will break if block number gets over BYTE_MAX + stream.writeByte(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 + + //items + for(int i = 0; i < tile.entity.items.length; i ++){ + stream.writeInt(tile.entity.items[i]); + } + + //timer data + + //amount of active timers + byte times = 0; + + for(; times < tile.entity.timer.getTimes().length; times ++){ + if(tile.entity.timer.getTimes()[times] > 0){ + break; + } + } + + stream.writeByte(times); + + for(int i = 0; i < times; i ++){ + stream.writeFloat(tile.entity.timer.getTimes()[times]); + } + + tile.entity.write(stream); + } + } + } + } + + }catch (IOException e){ + throw new RuntimeException(e); + } + } + + public static void load(FileHandle file){ + load(file.read()); + } + + //TODO GWT support + public static void load(InputStream is){ + + try(DataInputStream stream = new DataInputStream(is)){ + + int version = stream.readInt(); + float timerTime = stream.readFloat(); + long timestamp = stream.readLong(); + + Timers.resetTime(timerTime + (TimeUtils.timeSinceMillis(timestamp) / 1000f) * 60f); + + 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(); + + Vars.control.setMode(GameMode.values()[mode]); + + //inventory + for(int i = 0; i < Vars.control.getItems().length; i ++){ + Vars.control.getItems()[i] = stream.readInt(); + } + + Vars.ui.updateItems(); + + //enemies + + Entities.clear(); + + int enemies = stream.readInt(); + + Array enemiesToUpdate = new Array<>(); + + for(int i = 0; i < enemies; i ++){ + int id = stream.readInt(); + byte type = stream.readByte(); + int lane = stream.readByte(); + float x = stream.readFloat(); + float y = stream.readFloat(); + byte tier = stream.readByte(); + short health = stream.readShort(); + short node = stream.readShort(); + + try{ + Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type)); + enemy.id = id; + enemy.lane = lane; + enemy.health = health; + enemy.x = x; + enemy.y = y; + enemy.tier = tier; + enemy.node = node; + 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(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); + } + } + } + + int rocks = stream.readInt(); + + 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 = io.anuke.mindustry.world.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(); + byte blockid = stream.readByte(); + + Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width()); + tile.setBlock(Block.getByID(blockid)); + + if(tile.block() == Blocks.blockpart){ + tile.link = stream.readByte(); + } + + if(tile.entity != null){ + byte rotation = stream.readByte(); + short health = stream.readShort(); + + tile.entity.health = health; + tile.setRotation(rotation); + + for(int j = 0; j < tile.entity.items.length; j ++){ + tile.entity.items[j] = stream.readInt(); + } + + byte timers = stream.readByte(); + for(int time = 0; time < timers; time ++){ + tile.entity.timer.getTimes()[time] = stream.readFloat(); + } + + tile.entity.read(stream); + } + } + + }catch (IOException e){ + throw new RuntimeException(e); + } + } +} diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java index 261226ea6f..f9c6567cbe 100644 --- a/core/src/io/anuke/mindustry/io/SaveIO.java +++ b/core/src/io/anuke/mindustry/io/SaveIO.java @@ -1,28 +1,29 @@ package io.anuke.mindustry.io; -import static io.anuke.mindustry.Vars.android; - -import java.io.*; -import java.util.Arrays; -import java.util.Date; - -import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.utils.Array; 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 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.*; +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.ucore.entities.Entities; +import java.io.*; +import java.util.Arrays; +import java.util.Date; + +import static io.anuke.mindustry.Vars.android; + /* * Save format: * @@ -84,7 +85,7 @@ public class SaveIO{ private static final int fileVersionID = 12; //TODO automatic registration of types? - private static final Array> enemyIDs = Array.with( + public static final Array> enemyIDs = Array.with( Enemy.class, FastEnemy.class, RapidEnemy.class, @@ -98,7 +99,7 @@ public class SaveIO{ EmpEnemy.class ); - private static final ObjectMap, Byte> idEnemies = new ObjectMap, Byte>(){{ + public static final ObjectMap, Byte> idEnemies = new ObjectMap, Byte>(){{ for(int i = 0; i < enemyIDs.size; i ++){ put(enemyIDs.get(i), (byte)i); } @@ -245,7 +246,7 @@ public class SaveIO{ for(Enemy enemy : Vars.control.enemyGroup.all()){ if(idEnemies.containsKey(enemy.getClass())){ stream.writeByte(idEnemies.get(enemy.getClass())); //type - stream.writeByte(enemy.spawn); //lane + stream.writeByte(enemy.lane); //lane stream.writeFloat(enemy.x); //x stream.writeFloat(enemy.y); //y stream.writeByte(enemy.tier); //tier @@ -391,7 +392,7 @@ public class SaveIO{ try{ Enemy enemy = ClassReflection.newInstance(enemyIDs.get(type)); - enemy.spawn = lane; + enemy.lane = lane; enemy.health = health; enemy.x = x; enemy.y = y; diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index 716cac8ed5..c0c9305976 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -1,6 +1,7 @@ package io.anuke.mindustry.net; import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.enemies.Enemy; /**Class for storing all packets.*/ public class Packets { @@ -27,6 +28,7 @@ public class Packets { public static class SyncPacket{ public int[] ids; public float[][] data; + public int enemyStart = 0; } public static class BlockSyncPacket extends Streamable{ @@ -55,6 +57,12 @@ public class Packets { public int playerid; } + public static class BulletPacket{ + public int type, owner; + public float x, y, angle; + public short damage; + } + public static class PlacePacket{ public int playerid; public byte rotation; @@ -66,4 +74,28 @@ public class Packets { public int playerid; public short x, y; } + + public static class EnemySpawnPacket{ + public Class type; + public byte lane, tier; + public float x, y; + public int id; + } + + public static class EnemyDeathPacket{ + public int id; + } + + public static class PathPacket{ + public int[] path; + public byte index; + } + + public static class BlockDestroyPacket{ + public int position; + } + + public static class BlockUpdatePacket{ + public int health; + } } diff --git a/core/src/io/anuke/mindustry/net/Registrator.java b/core/src/io/anuke/mindustry/net/Registrator.java index 3555e95854..96cc6219d2 100644 --- a/core/src/io/anuke/mindustry/net/Registrator.java +++ b/core/src/io/anuke/mindustry/net/Registrator.java @@ -3,6 +3,7 @@ package io.anuke.mindustry.net; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.enemies.*; import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Streamable.StreamBegin; import io.anuke.mindustry.net.Streamable.StreamChunk; @@ -24,12 +25,17 @@ public class Registrator { BreakPacket.class, StateSyncPacket.class, BlockSyncPacket.class, + EnemySpawnPacket.class, + PathPacket.class, + BulletPacket.class, + EnemyDeathPacket.class, Class.class, byte[].class, float[].class, float[][].class, int[].class, + int[][].class, Entity[].class, Player[].class, Array.class, @@ -37,7 +43,19 @@ public class Registrator { Entity.class, Player.class, - Mech.class + Mech.class, + + Enemy.class, + FastEnemy.class, + RapidEnemy.class, + FlamerEnemy.class, + TankEnemy.class, + BlastEnemy.class, + MortarEnemy.class, + TestEnemy.class, + HealerEnemy.class, + TitanEnemy.class, + EmpEnemy.class }; } } diff --git a/core/src/io/anuke/mindustry/net/Syncable.java b/core/src/io/anuke/mindustry/net/Syncable.java index 6a31fa5e93..225c78d46d 100644 --- a/core/src/io/anuke/mindustry/net/Syncable.java +++ b/core/src/io/anuke/mindustry/net/Syncable.java @@ -2,9 +2,11 @@ package io.anuke.mindustry.net; import com.badlogic.gdx.math.Vector2; import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.ucore.entities.Entity; import io.anuke.ucore.util.Mathf; +//TODO clean up this giant mess public interface Syncable { public Interpolator getInterpolator(); @@ -39,11 +41,38 @@ public interface Syncable { entity.angle = Mathf.lerpAngDelta(entity.angle, i.targetrot, 0.6f); } }; + + public static final SyncType enemy = new SyncType() { + @Override + public float[] write(Enemy entity) { + return new float[]{entity.x, entity.y, entity.angle, entity.health}; + } + + @Override + public void read(Enemy entity, float[] data) { + entity.getInterpolator().target.set(data[0], data[1]); + entity.getInterpolator().targetrot = data[2]; + entity.health = (int)data[3]; + } + + @Override + public void update(Enemy entity, Interpolator interpolator) { + Interpolator i = entity.getInterpolator(); + if(i.target.dst(entity.x, entity.y) > 16){ + entity.set(i.target.x, i.target.y); + } + + entity.x = Mathf.lerpDelta(entity.x, i.target.x, 0.4f); + entity.y = Mathf.lerpDelta(entity.y, i.target.y, 0.4f); + entity.angle = Mathf.lerpAngDelta(entity.angle, i.targetrot, 0.6f); + } + }; } public static class Interpolator { public SyncType type; public Vector2 target = new Vector2(); + public Vector2 last = new Vector2(); public float targetrot; public Interpolator(SyncType type){ diff --git a/core/src/io/anuke/mindustry/ui/MenuDialog.java b/core/src/io/anuke/mindustry/ui/MenuDialog.java index dd484104a5..ea5a67eccc 100644 --- a/core/src/io/anuke/mindustry/ui/MenuDialog.java +++ b/core/src/io/anuke/mindustry/ui/MenuDialog.java @@ -1,13 +1,10 @@ package io.anuke.mindustry.ui; -import static io.anuke.mindustry.Vars.ui; - import com.badlogic.gdx.utils.reflect.ClassReflection; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.net.Net; -import io.anuke.ucore.UCore; import io.anuke.ucore.core.Timers; import io.anuke.ucore.scene.Element; import io.anuke.ucore.scene.builders.build; @@ -19,20 +16,22 @@ import io.anuke.ucore.util.Strings; import java.io.IOException; +import static io.anuke.mindustry.Vars.ui; + public class MenuDialog extends FloatingDialog{ private SaveDialog save = new SaveDialog(); private LoadDialog load = new LoadDialog(); public boolean wasPaused = false; public MenuDialog() { - super("Paused"); + super("$text.menu"); setup(); } void setup(){ shown(() -> { wasPaused = GameState.is(State.paused); - GameState.set(State.paused); + if(!Net.active()) GameState.set(State.paused); }); if(!Vars.android){ @@ -40,7 +39,7 @@ public class MenuDialog extends FloatingDialog{ content().addButton("$text.back", () -> { hide(); - if(!wasPaused) + if(!wasPaused || Net.active()) GameState.set(State.playing); }); @@ -71,6 +70,7 @@ public class MenuDialog extends FloatingDialog{ }else{ try{ Net.host(result); + GameState.set(State.playing); }catch (IOException e){ Vars.ui.showError(Bundles.format("text.server.error", Strings.parseException(e, false))); } diff --git a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java index 3b655feac0..2b3f050554 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java @@ -1,34 +1,26 @@ package io.anuke.mindustry.ui.fragments; -import static io.anuke.mindustry.Vars.*; - -import com.badlogic.gdx.Game; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; - import com.badlogic.gdx.math.Interpolation; -import io.anuke.mindustry.Mindustry; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState; import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.world.GameMode; -import io.anuke.ucore.UCore; +import io.anuke.mindustry.net.Net; import io.anuke.ucore.core.Core; -import io.anuke.ucore.core.Draw; import io.anuke.ucore.core.Settings; import io.anuke.ucore.scene.actions.Actions; import io.anuke.ucore.scene.builders.imagebutton; import io.anuke.ucore.scene.builders.label; import io.anuke.ucore.scene.builders.table; import io.anuke.ucore.scene.event.Touchable; -import io.anuke.ucore.scene.ui.Image; import io.anuke.ucore.scene.ui.ImageButton; import io.anuke.ucore.scene.ui.Label; -import io.anuke.ucore.scene.ui.layout.Cell; import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.util.Bundles; +import static io.anuke.mindustry.Vars.*; + public class HudFragment implements Fragment{ private ImageButton menu, flip, pause; private Table respawntable; @@ -74,7 +66,8 @@ public class HudFragment implements Fragment{ pause = new imagebutton("icon-pause", isize, ()->{ GameState.set(GameState.is(State.paused) ? State.playing : State.paused); - }).update(i -> i.getStyle().imageUp = Core.skin.getDrawable(GameState.is(State.paused) ? "icon-play" : "icon-pause")).get(); + }).update(i -> i.getStyle().imageUp = Core.skin.getDrawable(GameState.is(State.paused) ? "icon-play" : "icon-pause")).cell + .disabled(b -> Net.active()).get(); }}.end(); @@ -105,7 +98,7 @@ public class HudFragment implements Fragment{ //paused table new table(){{ - visible(()->GameState.is(State.paused)); + visible(()->GameState.is(State.paused) && !Net.active()); atop(); new table("pane"){{ diff --git a/core/src/io/anuke/mindustry/world/Generator.java b/core/src/io/anuke/mindustry/world/Generator.java index 66444db661..693e4008d1 100644 --- a/core/src/io/anuke/mindustry/world/Generator.java +++ b/core/src/io/anuke/mindustry/world/Generator.java @@ -17,7 +17,7 @@ import io.anuke.ucore.noise.Noise; import io.anuke.ucore.util.Mathf; public class Generator{ - static final ObjectMap rocks = new ObjectMap(){{ + public static final ObjectMap rocks = new ObjectMap(){{ put(Blocks.stone, Blocks.rock); put(Blocks.snow, Blocks.icerock); put(Blocks.grass, Blocks.shrub); diff --git a/core/src/io/anuke/mindustry/world/SpawnPoint.java b/core/src/io/anuke/mindustry/world/SpawnPoint.java index 94a0293203..9735946ecd 100644 --- a/core/src/io/anuke/mindustry/world/SpawnPoint.java +++ b/core/src/io/anuke/mindustry/world/SpawnPoint.java @@ -8,7 +8,6 @@ import io.anuke.mindustry.ai.SmoothGraphPath; public class SpawnPoint{ public Tile start; public Tile[] pathTiles; - public Tile[] tempTiles; public PathFinder finder; public SmoothGraphPath path = new SmoothGraphPath(); public PathFinderRequest request; diff --git a/core/src/io/anuke/mindustry/world/Tile.java b/core/src/io/anuke/mindustry/world/Tile.java index 8fc45c73a7..52db18ee55 100644 --- a/core/src/io/anuke/mindustry/world/Tile.java +++ b/core/src/io/anuke/mindustry/world/Tile.java @@ -1,16 +1,15 @@ package io.anuke.mindustry.world; -import static io.anuke.mindustry.Vars.tilesize; - import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; - import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.ucore.util.Bits; import io.anuke.ucore.util.Mathf; +import static io.anuke.mindustry.Vars.tilesize; + public class Tile{ private static final Array tmpArray = new Array<>(); @@ -34,6 +33,10 @@ public class Tile{ this(x, y); iSetFloor(floor); } + + public int packedPosition(){ + return x + y * Vars.world.width(); + } private void iSetFloor(Block floor){ byte id = (byte)floor.id; diff --git a/core/src/io/anuke/mindustry/world/blocks/Blocks.java b/core/src/io/anuke/mindustry/world/blocks/Blocks.java index f8137dbde9..4e66ed2346 100644 --- a/core/src/io/anuke/mindustry/world/blocks/Blocks.java +++ b/core/src/io/anuke/mindustry/world/blocks/Blocks.java @@ -138,34 +138,23 @@ public class Blocks{ solid = true; }}, - shrub = new Block("shrub"){{ - shadow = "shrubshadow"; - breakable = true; - breaktime = 10; - }}, + shrub = new Rock("shrub"){ + + }, - rock = new Block("rock"){{ - shadow = "rockshadow"; - breakable = true; - breaktime = 15; + rock = new Rock("rock"){{ variants = 2; varyShadow = true; drops = new ItemStack(Item.stone, 3); }}, - icerock = new Block("icerock"){{ - shadow = "rockshadow"; - breakable = true; - breaktime = 15; + icerock = new Rock("icerock"){{ variants = 2; varyShadow = true; drops = new ItemStack(Item.stone, 3); }}, - blackrock = new Block("blackrock"){{ - shadow = "blackrockshadow"; - breakable = true; - breaktime = 15; + blackrock = new Rock("blackrock"){{ variants = 1; varyShadow = true; drops = new ItemStack(Item.stone, 3); diff --git a/core/src/io/anuke/mindustry/world/blocks/types/Rock.java b/core/src/io/anuke/mindustry/world/blocks/types/Rock.java new file mode 100644 index 0000000000..14a8681c3a --- /dev/null +++ b/core/src/io/anuke/mindustry/world/blocks/types/Rock.java @@ -0,0 +1,13 @@ +package io.anuke.mindustry.world.blocks.types; + +import io.anuke.mindustry.world.Block; + +public class Rock extends Block { + + public Rock(String name) { + super(name); + shadow = name+"shadow"; + breakable = true; + breaktime = 10; + } +} diff --git a/core/src/io/anuke/mindustry/world/blocks/types/production/Crafter.java b/core/src/io/anuke/mindustry/world/blocks/types/production/Crafter.java index ed34a4e14b..4d4da9bae5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/production/Crafter.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/production/Crafter.java @@ -1,15 +1,14 @@ package io.anuke.mindustry.world.blocks.types.production; -import java.util.Arrays; - import com.badlogic.gdx.utils.Array; - import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.resource.Item; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.core.Effects; +import java.util.Arrays; + public class Crafter extends Block{ protected final int timerDump = timers++; @@ -32,7 +31,7 @@ public class Crafter extends Block{ @Override public void update(Tile tile){ - if(tile.entity.timer.get(timerDump, 20) && tile.entity.hasItem(result)){ + if(tile.entity.timer.get(timerDump, 15) && tile.entity.hasItem(result)){ tryDump(tile, -1, result); } @@ -52,13 +51,11 @@ public class Crafter extends Block{ @Override public boolean acceptItem(Item item, Tile dest, Tile source){ - boolean craft = false; for(Item req : requirements){ if(item == req){ - craft = true; - break; + return true; } } - return craft; + return false; } } diff --git a/core/src/io/anuke/mindustry/world/blocks/types/production/LiquidCrafter.java b/core/src/io/anuke/mindustry/world/blocks/types/production/LiquidCrafter.java index 6b7fde431b..2b82636b58 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/production/LiquidCrafter.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/production/LiquidCrafter.java @@ -75,7 +75,7 @@ public class LiquidCrafter extends LiquidBlock{ Effects.effect(craftEffect, tile.worldx(), tile.worldy()); } - if(entity.timer.get(timerDump, 30)){ + if(entity.timer.get(timerDump, 15)){ tryDump(tile, -1, output); } }