mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-07-31 07:00:06 +07:00
Implemented full multiplayer; block syncing still unfinished
This commit is contained in:
@ -21,7 +21,7 @@ allprojects {
|
||||
appName = "Mindustry"
|
||||
gdxVersion = '1.9.8'
|
||||
aiVersion = '1.8.1'
|
||||
uCoreVersion = '4fb2c63';
|
||||
uCoreVersion = '59488d7';
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -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:
|
||||
|
@ -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 },
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<Tile>(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);
|
||||
|
||||
|
@ -48,7 +48,7 @@ public class Control extends Module{
|
||||
final Array<Weapon> weapons = new Array<>();
|
||||
final int[] items = new int[Item.getAllItems().size];
|
||||
|
||||
public final EntityGroup<Enemy> enemyGroup = Entities.addGroup(Enemy.class);
|
||||
public final EntityGroup<Enemy> enemyGroup = Entities.addGroup(Enemy.class).enableMapping();
|
||||
public final EntityGroup<TileEntity> tileGroup = Entities.addGroup(TileEntity.class, false);
|
||||
public final EntityGroup<Bullet> bulletGroup = Entities.addGroup(Bullet.class);
|
||||
public final EntityGroup<Shield> 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;
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -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<Player> 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());
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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<Enemy> 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<Enemy> 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 extends Entity> T add(){
|
||||
return (T) add(Vars.control.enemyGroup);
|
||||
public Enemy add(){
|
||||
return add(Vars.control.enemyGroup);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
304
core/src/io/anuke/mindustry/io/NetworkIO.java
Normal file
304
core/src/io/anuke/mindustry/io/NetworkIO.java
Normal file
@ -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<Enemy> 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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<Class<? extends Enemy>> enemyIDs = Array.with(
|
||||
public static final Array<Class<? extends Enemy>> enemyIDs = Array.with(
|
||||
Enemy.class,
|
||||
FastEnemy.class,
|
||||
RapidEnemy.class,
|
||||
@ -98,7 +99,7 @@ public class SaveIO{
|
||||
EmpEnemy.class
|
||||
);
|
||||
|
||||
private static final ObjectMap<Class<? extends Enemy>, Byte> idEnemies = new ObjectMap<Class<? extends Enemy>, Byte>(){{
|
||||
public static final ObjectMap<Class<? extends Enemy>, Byte> idEnemies = new ObjectMap<Class<? extends Enemy>, 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;
|
||||
|
@ -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<? extends Enemy> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -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> enemy = new SyncType<Enemy>() {
|
||||
@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<T extends Entity> {
|
||||
public SyncType<T> type;
|
||||
public Vector2 target = new Vector2();
|
||||
public Vector2 last = new Vector2();
|
||||
public float targetrot;
|
||||
|
||||
public Interpolator(SyncType<T> type){
|
||||
|
@ -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)));
|
||||
}
|
||||
|
@ -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"){{
|
||||
|
@ -17,7 +17,7 @@ import io.anuke.ucore.noise.Noise;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
public class Generator{
|
||||
static final ObjectMap<Block, Block> rocks = new ObjectMap(){{
|
||||
public static final ObjectMap<Block, Block> rocks = new ObjectMap(){{
|
||||
put(Blocks.stone, Blocks.rock);
|
||||
put(Blocks.snow, Blocks.icerock);
|
||||
put(Blocks.grass, Blocks.shrub);
|
||||
|
@ -8,7 +8,6 @@ import io.anuke.mindustry.ai.SmoothGraphPath;
|
||||
public class SpawnPoint{
|
||||
public Tile start;
|
||||
public Tile[] pathTiles;
|
||||
public Tile[] tempTiles;
|
||||
public PathFinder<Tile> finder;
|
||||
public SmoothGraphPath path = new SmoothGraphPath();
|
||||
public PathFinderRequest<Tile> request;
|
||||
|
@ -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<Tile> 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;
|
||||
|
@ -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);
|
||||
|
13
core/src/io/anuke/mindustry/world/blocks/types/Rock.java
Normal file
13
core/src/io/anuke/mindustry/world/blocks/types/Rock.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user