mirror of
https://github.com/Anuken/Mindustry.git
synced 2025-03-13 19:39:04 +07:00
Netcode changes, new pathfinding, fixed enemies jittering when stuck
This commit is contained in:
parent
1762af5d4c
commit
597a883275
@ -21,7 +21,7 @@ allprojects {
|
||||
appName = "Mindustry"
|
||||
gdxVersion = '1.9.8'
|
||||
aiVersion = '1.8.1'
|
||||
uCoreVersion = '9d4c546';
|
||||
uCoreVersion = '459e8ae';
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -45,10 +45,17 @@ public class Mindustry extends ModuleCore {
|
||||
I18NBundle.setExceptionOnMissingKey(false);
|
||||
|
||||
if(externalBundle){
|
||||
try {
|
||||
FileHandle handle = Gdx.files.local("bundle");
|
||||
|
||||
Locale locale = Locale.ENGLISH;
|
||||
Core.bundle = I18NBundle.createBundle(handle, locale);
|
||||
}catch (Exception e){
|
||||
e.printStackTrace();
|
||||
platforms.showError("Failed to find bundle!\nMake sure you have bundle.properties in the same directory\nas the jar file.\n\nIf the problem persists, try running it through the command prompt:\n" +
|
||||
"Hold left-shift, then right click and select 'open command prompt here'.\nThen, type in 'java -jar mindustry.jar' without quotes.");
|
||||
Gdx.app.exit();
|
||||
}
|
||||
}else{
|
||||
FileHandle handle = Gdx.files.internal("bundles/bundle");
|
||||
|
||||
|
@ -1,36 +0,0 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.Heuristic;
|
||||
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
public class HeuristicImpl implements Heuristic<Tile>{
|
||||
/**How many times more it costs to go through a destructible block than an empty block.*/
|
||||
static final float solidMultiplier = 5f;
|
||||
/**How many times more it costs to go through a tile that touches a solid block.*/
|
||||
static final float occludedMultiplier = 5f;
|
||||
|
||||
@Override
|
||||
public float estimate(Tile node, Tile other){
|
||||
return estimateStatic(node, other);
|
||||
}
|
||||
|
||||
/**Estimate the cost of walking between two tiles.*/
|
||||
public static float estimateStatic(Tile node, Tile other){
|
||||
//Get Manhattan distance cost
|
||||
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
|
||||
|
||||
//If either one of the tiles is a breakable solid block (that is, it's player-made),
|
||||
//increase the cost by the tilesize times the solid block multiplier
|
||||
//Also add the block health, so blocks with more health cost more to traverse
|
||||
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
|
||||
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
|
||||
|
||||
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
|
||||
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
}
|
65
core/src/io/anuke/mindustry/ai/Heuristics.java
Normal file
65
core/src/io/anuke/mindustry/ai/Heuristics.java
Normal file
@ -0,0 +1,65 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.Heuristic;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.types.defense.Turret;
|
||||
import io.anuke.mindustry.world.blocks.types.production.Drill;
|
||||
import io.anuke.mindustry.world.blocks.types.production.Generator;
|
||||
import io.anuke.mindustry.world.blocks.types.production.Pump;
|
||||
import io.anuke.mindustry.world.blocks.types.production.Smelter;
|
||||
|
||||
public class Heuristics {
|
||||
/**How many times more it costs to go through a destructible block than an empty block.*/
|
||||
static final float solidMultiplier = 5f;
|
||||
/**How many times more it costs to go through a tile that touches a solid block.*/
|
||||
static final float occludedMultiplier = 5f;
|
||||
|
||||
public static class FastestHeuristic implements Heuristic<Tile> {
|
||||
|
||||
@Override
|
||||
public float estimate(Tile node, Tile other){
|
||||
//Get Manhattan distance cost
|
||||
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
|
||||
|
||||
//If either one of the tiles is a breakable solid block (that is, it's player-made),
|
||||
//increase the cost by the tilesize times the solid block multiplier
|
||||
//Also add the block health, so blocks with more health cost more to traverse
|
||||
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
|
||||
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
|
||||
|
||||
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
|
||||
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
|
||||
|
||||
return cost;
|
||||
}
|
||||
}
|
||||
|
||||
public static class DestrutiveHeuristic implements Heuristic<Tile> {
|
||||
|
||||
@Override
|
||||
public float estimate(Tile node, Tile other){
|
||||
//Get Manhattan distance cost
|
||||
float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy());
|
||||
|
||||
//If either one of the tiles is a breakable solid block (that is, it's player-made),
|
||||
//increase the cost by the tilesize times the solid block multiplier
|
||||
//Also add the block health, so blocks with more health cost more to traverse
|
||||
if(node.breakable() && node.block().solid) cost += Vars.tilesize* solidMultiplier + node.block().health;
|
||||
if(other.breakable() && other.block().solid) cost += Vars.tilesize* solidMultiplier + other.block().health;
|
||||
|
||||
//if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls
|
||||
if(node.occluded) cost += Vars.tilesize*occludedMultiplier;
|
||||
|
||||
//generators are free!
|
||||
if(generator(other) || generator(node)) cost = 0;
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
private boolean generator(Tile tile){
|
||||
return tile.block() instanceof Generator || tile.block() instanceof Turret
|
||||
|| tile.block() instanceof Pump || tile.block() instanceof Drill || tile.block() instanceof Smelter;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,13 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.Heuristic;
|
||||
import com.badlogic.gdx.ai.pfa.PathFinderRequest;
|
||||
import com.badlogic.gdx.ai.pfa.PathSmoother;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.ai.Heuristics.DestrutiveHeuristic;
|
||||
import io.anuke.mindustry.entities.enemies.Enemy;
|
||||
import io.anuke.mindustry.entities.enemies.EnemyType;
|
||||
import io.anuke.mindustry.game.SpawnPoint;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.ucore.UCore;
|
||||
@ -20,7 +21,7 @@ public class Pathfind{
|
||||
private static final long maxTime = 1000000 * 5;
|
||||
|
||||
/**Heuristic for determining cost between two tiles*/
|
||||
HeuristicImpl heuristic = new HeuristicImpl();
|
||||
Heuristic<Tile> heuristic = new DestrutiveHeuristic();
|
||||
/**Tile graph, for determining conenctions between two tiles*/
|
||||
TileGraph graph = new TileGraph();
|
||||
/**Smoother that removes extra nodes from a path.*/
|
||||
@ -54,17 +55,17 @@ public class Pathfind{
|
||||
}
|
||||
|
||||
//if an enemy is idle for a while, it's probably stuck
|
||||
if(enemy.idletime > EnemyType.maxIdle){
|
||||
//if(enemy.idletime > EnemyType.maxIdle){
|
||||
|
||||
Tile target = path[enemy.node];
|
||||
if(Vars.world.raycastWorld(enemy.x, enemy.y, target.worldx(), target.worldy()) != null) {
|
||||
if (enemy.node > 1)
|
||||
enemy.node = enemy.node - 1;
|
||||
enemy.idletime = 0;
|
||||
}
|
||||
//Tile target = path[enemy.node];
|
||||
//if(Vars.world.raycastWorld(enemy.x, enemy.y, target.worldx(), target.worldy()) != null) {
|
||||
// if (enemy.node > 1)
|
||||
// enemy.node = enemy.node - 1;
|
||||
// enemy.idletime = 0;
|
||||
//}
|
||||
|
||||
//else, must be blocked by a playermade block, do nothing
|
||||
}
|
||||
//}
|
||||
|
||||
//-1 is only possible here if both pathfindings failed, which should NOT happen
|
||||
//check graph code
|
||||
@ -83,6 +84,12 @@ public class Pathfind{
|
||||
|
||||
Tile target = path[enemy.node];
|
||||
|
||||
//a bridge has broken
|
||||
if(!Vars.world.passable(target.x, target.y)){
|
||||
remakePath();
|
||||
return vector.set(enemy.x, enemy.y);
|
||||
}
|
||||
|
||||
float projectLen = Vector2.dst(prev.worldx(), prev.worldy(), target.worldx(), target.worldy()) / 6f;
|
||||
|
||||
Vector2 projection = projectPoint(prev.worldx(), prev.worldy(),
|
||||
@ -124,6 +131,15 @@ public class Pathfind{
|
||||
|
||||
}
|
||||
|
||||
private void remakePath(){
|
||||
for(int i = 0; i < Vars.control.enemyGroup.amount(); i ++){
|
||||
Enemy enemy = Vars.control.enemyGroup.all().get(i);
|
||||
enemy.node = -1;
|
||||
}
|
||||
|
||||
resetPaths();
|
||||
}
|
||||
|
||||
/**Update the pathfinders and continue calculating the path if it hasn't been calculated yet.
|
||||
* This method is run each frame.*/
|
||||
public void update(){
|
||||
|
@ -1,29 +0,0 @@
|
||||
package io.anuke.mindustry.ai;
|
||||
|
||||
import com.badlogic.gdx.ai.pfa.Connection;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
|
||||
public class TileConnection implements Connection<Tile>{
|
||||
Tile a, b;
|
||||
|
||||
public TileConnection(Tile a, Tile b){
|
||||
this.a = a;
|
||||
this.b = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getCost(){
|
||||
return HeuristicImpl.estimateStatic(a, b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getFromNode(){
|
||||
return a;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tile getToNode(){
|
||||
return b;
|
||||
}
|
||||
|
||||
}
|
@ -7,23 +7,10 @@ import io.anuke.mindustry.world.Tile;
|
||||
|
||||
/**Tilegraph that ignores player-made tiles.*/
|
||||
public class TileGraph implements OptimizedGraph<Tile> {
|
||||
private Array<Connection<Tile>> tempConnections = new Array<Connection<Tile>>(4);
|
||||
|
||||
/**Used for the default Graph implementation. Returns a result similar to connectionsOf()*/
|
||||
/**return nothing, as this isn't used*/
|
||||
@Override
|
||||
public Array<Connection<Tile>> getConnections(Tile fromNode){
|
||||
tempConnections.clear();
|
||||
|
||||
if(!fromNode.passable())
|
||||
return tempConnections;
|
||||
|
||||
for(Tile tile : fromNode.getNearby()){
|
||||
if(tile != null && (tile.passable()))
|
||||
tempConnections.add(new TileConnection(fromNode, tile));
|
||||
}
|
||||
|
||||
return tempConnections;
|
||||
}
|
||||
public Array<Connection<Tile>> getConnections(Tile fromNode){ return null; }
|
||||
|
||||
/**Used for the OptimizedPathFinder implementation.*/
|
||||
@Override
|
||||
|
@ -8,14 +8,13 @@ import com.badlogic.gdx.utils.Array;
|
||||
import io.anuke.mindustry.Mindustry;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.core.GameState.State;
|
||||
import io.anuke.mindustry.entities.*;
|
||||
import io.anuke.mindustry.entities.Bullet;
|
||||
import io.anuke.mindustry.entities.Player;
|
||||
import io.anuke.mindustry.entities.TileEntity;
|
||||
import io.anuke.mindustry.entities.effect.Shield;
|
||||
import io.anuke.mindustry.entities.enemies.Enemy;
|
||||
import io.anuke.mindustry.entities.enemies.EnemyTypes;
|
||||
import io.anuke.mindustry.game.Difficulty;
|
||||
import io.anuke.mindustry.game.EnemySpawn;
|
||||
import io.anuke.mindustry.game.SpawnPoint;
|
||||
import io.anuke.mindustry.game.WaveCreator;
|
||||
import io.anuke.mindustry.game.*;
|
||||
import io.anuke.mindustry.graphics.Fx;
|
||||
import io.anuke.mindustry.input.AndroidInput;
|
||||
import io.anuke.mindustry.input.DesktopInput;
|
||||
@ -25,7 +24,9 @@ import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.resource.Item;
|
||||
import io.anuke.mindustry.resource.ItemStack;
|
||||
import io.anuke.mindustry.resource.Weapon;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Map;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.ProductionBlocks;
|
||||
import io.anuke.ucore.UCore;
|
||||
import io.anuke.ucore.core.*;
|
||||
@ -63,7 +64,7 @@ public class Control extends Module{
|
||||
float wavetime;
|
||||
float extrawavetime;
|
||||
int enemies = 0;
|
||||
io.anuke.mindustry.game.GameMode mode = io.anuke.mindustry.game.GameMode.waves;
|
||||
GameMode mode = GameMode.waves;
|
||||
|
||||
Tile core;
|
||||
Array<io.anuke.mindustry.game.SpawnPoint> spawnpoints = new Array<>();
|
||||
@ -180,6 +181,7 @@ public class Control extends Module{
|
||||
"rotate_alt", new Axis(Input.CONTROLLER_DPAD_RIGHT, Input.CONTROLLER_DPAD_LEFT),
|
||||
"rotate", new Axis(Input.CONTROLLER_A, Input.CONTROLLER_B),
|
||||
"player_list", Input.CONTROLLER_START,
|
||||
"chat", Input.ENTER,
|
||||
"weapon_1", Input.NUM_1,
|
||||
"weapon_2", Input.NUM_2,
|
||||
"weapon_3", Input.NUM_3,
|
||||
@ -279,7 +281,7 @@ public class Control extends Module{
|
||||
return core;
|
||||
}
|
||||
|
||||
public Array<io.anuke.mindustry.game.SpawnPoint> getSpawnPoints(){
|
||||
public Array<SpawnPoint> getSpawnPoints(){
|
||||
return spawnpoints;
|
||||
}
|
||||
|
||||
@ -292,7 +294,7 @@ public class Control extends Module{
|
||||
}
|
||||
|
||||
public void addSpawnPoint(Tile tile){
|
||||
io.anuke.mindustry.game.SpawnPoint point = new SpawnPoint();
|
||||
SpawnPoint point = new SpawnPoint();
|
||||
point.start = tile;
|
||||
spawnpoints.add(point);
|
||||
}
|
||||
@ -310,11 +312,11 @@ public class Control extends Module{
|
||||
Timers.run(18, ()-> ui.loadfrag.hide());
|
||||
}
|
||||
|
||||
public io.anuke.mindustry.game.GameMode getMode(){
|
||||
public GameMode getMode(){
|
||||
return mode;
|
||||
}
|
||||
|
||||
public void setMode(io.anuke.mindustry.game.GameMode mode){
|
||||
public void setMode(GameMode mode){
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
@ -597,7 +599,7 @@ public class Control extends Module{
|
||||
}
|
||||
|
||||
if(Inputs.keyTap(Keys.U)){
|
||||
Vars.showUI = !Vars.showUI;
|
||||
Vars.showPaths = !Vars.showPaths;
|
||||
}
|
||||
|
||||
if(Inputs.keyTap(Keys.O)){
|
||||
|
@ -26,6 +26,7 @@ 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.Entities;
|
||||
import io.anuke.ucore.entities.Entity;
|
||||
import io.anuke.ucore.modules.Module;
|
||||
|
||||
@ -37,7 +38,7 @@ public class NetClient extends Module {
|
||||
Color.GOLD, Color.PINK, Color.SKY, Color.GOLD, Color.VIOLET,
|
||||
Color.GREEN, Color.CORAL, Color.CYAN, Color.CHARTREUSE};
|
||||
boolean connecting = false;
|
||||
boolean gotEntities = false, gotData = false;
|
||||
boolean gotData = false;
|
||||
boolean kicked = false;
|
||||
IntSet requests = new IntSet();
|
||||
float playerSyncTime = 2;
|
||||
@ -48,22 +49,23 @@ public class NetClient extends Module {
|
||||
Net.handle(Connect.class, packet -> {
|
||||
requests.clear();
|
||||
connecting = true;
|
||||
gotEntities = false;
|
||||
gotData = false;
|
||||
kicked = false;
|
||||
|
||||
Gdx.app.postRunnable(() -> {
|
||||
Vars.ui.loadfrag.hide();
|
||||
Vars.ui.loadfrag.show("$text.connecting.data");
|
||||
});
|
||||
|
||||
Entities.clear();
|
||||
|
||||
ConnectPacket c = new ConnectPacket();
|
||||
c.name = Vars.player.name;
|
||||
c.android = Vars.android;
|
||||
Net.send(c, SendMode.tcp);
|
||||
});
|
||||
|
||||
Timers.runTask(dataTimeout, () -> {
|
||||
if(!gotEntities){
|
||||
if(!gotData){
|
||||
Gdx.app.error("Mindustry", "Failed to load data!");
|
||||
Vars.ui.loadfrag.hide();
|
||||
Net.disconnect();
|
||||
@ -90,32 +92,18 @@ public class NetClient extends Module {
|
||||
NetworkIO.load(data.stream);
|
||||
Vars.player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2);
|
||||
|
||||
GameState.set(State.playing);
|
||||
connecting = false;
|
||||
Vars.ui.loadfrag.hide();
|
||||
Vars.ui.join.hide();
|
||||
gotData = true;
|
||||
});
|
||||
});
|
||||
|
||||
Net.handle(EntityDataPacket.class, data -> {
|
||||
|
||||
Gdx.app.postRunnable(() -> {
|
||||
Timers.run(10f, () -> { //TODO hack. should only run once world data is recieved
|
||||
Vars.control.playerGroup.remap(Vars.player, data.playerid);
|
||||
|
||||
for(int i = 0; i < data.weapons.length; i ++){
|
||||
Vars.control.addWeapon((Weapon) Upgrade.getByID(data.weapons[i]));
|
||||
}
|
||||
Vars.player.weaponLeft = Vars.player.weaponRight = Vars.control.getWeapons().peek();
|
||||
Vars.ui.hudfrag.updateWeapons();
|
||||
gotEntities = true;
|
||||
});
|
||||
Net.send(new ConnectConfirmPacket(), SendMode.tcp);
|
||||
GameState.set(State.playing);
|
||||
});
|
||||
});
|
||||
|
||||
Net.handle(SyncPacket.class, packet -> {
|
||||
if(!gotEntities) return;
|
||||
if(!gotData) return;
|
||||
|
||||
//TODO awful code
|
||||
for(int i = 0; i < packet.ids.length; i ++){
|
||||
@ -177,11 +165,13 @@ public class NetClient extends Module {
|
||||
Gdx.app.postRunnable(() -> {
|
||||
//duplicates.
|
||||
if(Vars.control.enemyGroup.getByID(spawn.id) != null) return;
|
||||
|
||||
Enemy enemy = new Enemy(EnemyType.getByID(spawn.type));
|
||||
enemy.set(spawn.x, spawn.y);
|
||||
enemy.tier = spawn.tier;
|
||||
enemy.lane = spawn.lane;
|
||||
enemy.id = spawn.id;
|
||||
enemy.health = spawn.health;
|
||||
enemy.add();
|
||||
|
||||
Effects.effect(Fx.spawn, enemy);
|
||||
@ -221,7 +211,7 @@ public class NetClient extends Module {
|
||||
});
|
||||
|
||||
Net.handle(BlockSyncPacket.class, packet -> {
|
||||
if(!gotEntities) return;
|
||||
if(!gotData) return;
|
||||
|
||||
DataInputStream stream = new DataInputStream(packet.stream);
|
||||
|
||||
@ -306,7 +296,7 @@ public class NetClient extends Module {
|
||||
if(!Net.client() || !Net.active()) return;
|
||||
|
||||
if(!GameState.is(State.menu) && Net.active()){
|
||||
if(gotEntities && gotData) sync();
|
||||
if(gotData) sync();
|
||||
}else if(!connecting){
|
||||
Net.disconnect();
|
||||
}
|
||||
|
@ -43,18 +43,7 @@ public class NetServer extends Module{
|
||||
|
||||
UCore.log("Sending world data to client (ID="+id+")");
|
||||
|
||||
WorldData data = new WorldData();
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
NetworkIO.write(stream);
|
||||
|
||||
UCore.log("Packed " + stream.size() + " uncompressed bytes of data.");
|
||||
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
||||
|
||||
Net.sendStream(id, data);
|
||||
|
||||
Gdx.app.postRunnable(() -> {
|
||||
EntityDataPacket dp = new EntityDataPacket();
|
||||
|
||||
Player player = new Player();
|
||||
player.clientid = id;
|
||||
player.name = packet.name;
|
||||
@ -62,18 +51,29 @@ public class NetServer extends Module{
|
||||
player.set(Vars.control.core.worldx(), Vars.control.core.worldy() - Vars.tilesize*2);
|
||||
player.getInterpolator().last.set(player.x, player.y);
|
||||
player.getInterpolator().target.set(player.x, player.y);
|
||||
player.add();
|
||||
connections.put(id, player);
|
||||
|
||||
dp.playerid = player.id;
|
||||
dp.weapons = weapons.get(packet.name, new ByteArray()).toArray();
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
NetworkIO.write(player.id, weapons.get(packet.name, new ByteArray()), stream);
|
||||
|
||||
Net.sendTo(id, dp, SendMode.tcp);
|
||||
UCore.log("Packed " + stream.size() + " uncompressed bytes of data.");
|
||||
|
||||
sendMessage("[accent]"+Bundles.format("text.server.connected", packet.name));
|
||||
WorldData data = new WorldData();
|
||||
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
||||
|
||||
Net.sendStream(id, data);
|
||||
});
|
||||
});
|
||||
|
||||
Net.handleServer(ConnectConfirmPacket.class, packet -> {
|
||||
Player player = connections.get(Net.getLastConnection());
|
||||
|
||||
if(player == null) return;
|
||||
|
||||
Gdx.app.postRunnable(player::add);
|
||||
sendMessage("[accent]"+Bundles.format("text.server.connected", player.name));
|
||||
});
|
||||
|
||||
Net.handleServer(Disconnect.class, packet -> {
|
||||
Player player = connections.get(packet.id);
|
||||
|
||||
@ -197,6 +197,7 @@ public class NetServer extends Module{
|
||||
e.tier = (byte)enemy.tier;
|
||||
e.lane = (byte)enemy.lane;
|
||||
e.type = enemy.type.id;
|
||||
e.health = (short)enemy.health;
|
||||
Net.sendTo(dest, e, SendMode.tcp);
|
||||
Gdx.app.error("Mindustry", "Replying to entity request("+Net.getLastConnection()+"): enemy, " + id);
|
||||
}else{
|
||||
|
@ -10,7 +10,7 @@ import io.anuke.mindustry.entities.TileEntity;
|
||||
import io.anuke.mindustry.io.Maps;
|
||||
import io.anuke.mindustry.net.Net;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.world.Generator;
|
||||
import io.anuke.mindustry.world.WorldGenerator;
|
||||
import io.anuke.mindustry.world.Map;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Blocks;
|
||||
@ -180,7 +180,7 @@ public class World extends Module{
|
||||
Entities.resizeTree(0, 0, map.getWidth() * tilesize, map.getHeight() * tilesize);
|
||||
|
||||
this.seed = seed;
|
||||
Generator.generate(map.pixmap, tiles);
|
||||
WorldGenerator.generate(map.pixmap, tiles);
|
||||
|
||||
if(control.getCore() == null) return;
|
||||
|
||||
|
@ -123,7 +123,7 @@ public class Player extends DestructibleEntity implements Syncable{
|
||||
|
||||
dashing = Inputs.keyDown("dash");
|
||||
|
||||
float speed = dashing ? Player.dashSpeed : Player.speed;
|
||||
float speed = dashing ? (debug ? Player.dashSpeed * 5f : Player.dashSpeed) : Player.speed;
|
||||
|
||||
if(health < maxhealth && Timers.get(this, "regen", 20))
|
||||
health ++;
|
||||
|
@ -30,6 +30,7 @@ public class Enemy extends DestructibleEntity implements Syncable{
|
||||
public Entity target;
|
||||
public float hitTime;
|
||||
public int tier = 1;
|
||||
public Vector2 totalMove = new Vector2();
|
||||
|
||||
public Enemy(EnemyType type){
|
||||
this.type = type;
|
||||
|
@ -18,6 +18,7 @@ import io.anuke.ucore.core.Graphics;
|
||||
import io.anuke.ucore.core.Timers;
|
||||
import io.anuke.ucore.entities.Entities;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
import io.anuke.ucore.util.Strings;
|
||||
import io.anuke.ucore.util.Tmp;
|
||||
|
||||
import static io.anuke.mindustry.Vars.world;
|
||||
@ -30,8 +31,7 @@ public class EnemyType {
|
||||
|
||||
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;
|
||||
public final static float maxIdleLife = 60f*13f; //13 seconds idle = death
|
||||
public final static float maxIdleLife = 60f*2f; //2 seconds idle = death
|
||||
public final static float hitDuration = 5f;
|
||||
|
||||
public final String name;
|
||||
@ -56,6 +56,7 @@ public class EnemyType {
|
||||
|
||||
protected final int timerTarget = timeid ++;
|
||||
protected final int timerReload = timeid ++;
|
||||
protected final int timerReset = timeid ++;
|
||||
|
||||
public EnemyType(String name){
|
||||
this.id = lastid++;
|
||||
@ -76,6 +77,14 @@ public class EnemyType {
|
||||
Draw.color();
|
||||
|
||||
Graphics.flush();
|
||||
|
||||
if(Vars.showPaths){
|
||||
Draw.tscl(0.25f);
|
||||
Draw.text((int)enemy.idletime + "\n" + Strings.toFixed(enemy.totalMove.x, 2) + ", "
|
||||
+ Strings.toFixed(enemy.totalMove.x, 2), enemy.x, enemy.y);
|
||||
Draw.tscl(Vars.fontscale);
|
||||
}
|
||||
|
||||
Shaders.outline.lighten = 0f;
|
||||
}
|
||||
|
||||
@ -90,15 +99,24 @@ public class EnemyType {
|
||||
move(enemy);
|
||||
|
||||
enemy.velocity.set(enemy.x - lastx, enemy.y - lasty).scl(1f / Timers.delta());
|
||||
enemy.totalMove.add(enemy.velocity);
|
||||
|
||||
float minv = 0.07f;
|
||||
|
||||
if(enemy.timer.get(timerReset, 60)){
|
||||
enemy.totalMove.setZero();
|
||||
}
|
||||
|
||||
if(enemy.velocity.len() < minv && enemy.node > 0 && enemy.target == null){
|
||||
enemy.idletime += Timers.delta();
|
||||
}else{
|
||||
enemy.idletime = 0;
|
||||
}
|
||||
|
||||
if(enemy.timer.getTime(timerReset) > 40 && enemy.totalMove.len() < 0.3f && enemy.node > 0 && enemy.target == null){
|
||||
enemy.idletime = 999999f;
|
||||
}
|
||||
|
||||
Tile tile = world.tileWorld(enemy.x, enemy.y);
|
||||
if(tile != null && tile.floor().liquid && tile.block() == Blocks.air){
|
||||
enemy.damage(enemy.health+1); //drown
|
||||
|
@ -1,15 +1,15 @@
|
||||
package io.anuke.mindustry.io;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.ByteArray;
|
||||
import com.badlogic.gdx.utils.TimeUtils;
|
||||
import io.anuke.mindustry.Vars;
|
||||
import io.anuke.mindustry.entities.enemies.Enemy;
|
||||
import io.anuke.mindustry.entities.enemies.EnemyType;
|
||||
import io.anuke.mindustry.game.GameMode;
|
||||
import io.anuke.mindustry.resource.Upgrade;
|
||||
import io.anuke.mindustry.resource.Weapon;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.game.GameMode;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.WorldGenerator;
|
||||
import io.anuke.mindustry.world.blocks.Blocks;
|
||||
import io.anuke.mindustry.world.blocks.types.BlockPart;
|
||||
import io.anuke.mindustry.world.blocks.types.Rock;
|
||||
@ -19,9 +19,9 @@ import io.anuke.ucore.entities.Entities;
|
||||
import java.io.*;
|
||||
|
||||
public class NetworkIO {
|
||||
private static final int fileVersionID = 13;
|
||||
private static final int fileVersionID = 14;
|
||||
|
||||
public static void write(OutputStream os){
|
||||
public static void write(int playerID, ByteArray upgrades, OutputStream os){
|
||||
|
||||
try(DataOutputStream stream = new DataOutputStream(os)){
|
||||
|
||||
@ -36,28 +36,20 @@ public class NetworkIO {
|
||||
|
||||
stream.writeInt(Vars.control.getWave()); //wave
|
||||
stream.writeFloat(Vars.control.getWaveCountdown()); //wave countdown
|
||||
stream.writeInt(Vars.control.enemyGroup.amount()); //enemy amount
|
||||
|
||||
stream.writeInt(playerID); //player remap ID
|
||||
|
||||
//--INVENTORY--
|
||||
|
||||
for(int i = 0; i < Vars.control.getItems().length; i ++){
|
||||
for(int i = 0; i < Vars.control.getItems().length; i ++){ //items
|
||||
stream.writeInt(Vars.control.getItems()[i]);
|
||||
}
|
||||
|
||||
//--ENEMIES--
|
||||
Array<Enemy> enemies = Vars.control.enemyGroup.all();
|
||||
stream.writeByte(upgrades.size); //upgrade data
|
||||
|
||||
stream.writeInt(enemies.size); //enemy amount
|
||||
|
||||
for(int i = 0; i < enemies.size; i ++){
|
||||
Enemy enemy = enemies.get(i);
|
||||
stream.writeInt(enemy.id);
|
||||
stream.writeByte(enemy.type.id); //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
|
||||
for(int i = 0; i < upgrades.size; i ++){
|
||||
stream.writeByte(upgrades.get(i));
|
||||
}
|
||||
|
||||
//--MAP DATA--
|
||||
@ -166,7 +158,7 @@ public class NetworkIO {
|
||||
Timers.resetTime(timerTime + (TimeUtils.timeSinceMillis(timestamp) / 1000f) * 60f);
|
||||
|
||||
if(version != fileVersionID){
|
||||
throw new RuntimeException("Save file version mismatch!");
|
||||
throw new RuntimeException("Netcode version mismatch!");
|
||||
}
|
||||
|
||||
//general state
|
||||
@ -175,9 +167,13 @@ public class NetworkIO {
|
||||
|
||||
int wave = stream.readInt();
|
||||
float wavetime = stream.readFloat();
|
||||
int enemies = stream.readInt();
|
||||
|
||||
Vars.control.setWaveData(enemies, wave, wavetime);
|
||||
Vars.control.setMode(GameMode.values()[mode]);
|
||||
|
||||
int pid = stream.readInt();
|
||||
|
||||
//inventory
|
||||
for(int i = 0; i < Vars.control.getItems().length; i ++){
|
||||
Vars.control.getItems()[i] = stream.readInt();
|
||||
@ -187,38 +183,18 @@ public class NetworkIO {
|
||||
|
||||
Vars.control.getWeapons().clear();
|
||||
Vars.control.getWeapons().add(Weapon.blaster);
|
||||
Vars.player.weaponLeft = Vars.player.weaponRight = Weapon.blaster;
|
||||
Vars.ui.hudfrag.updateWeapons();
|
||||
|
||||
//enemies
|
||||
byte weapons = stream.readByte();
|
||||
|
||||
Entities.clear();
|
||||
|
||||
int enemies = stream.readInt();
|
||||
|
||||
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();
|
||||
|
||||
Enemy enemy = new Enemy(EnemyType.getByID(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);
|
||||
for(int i = 0; i < weapons; i ++){
|
||||
Vars.control.getWeapons().add((Weapon) Upgrade.getByID(stream.readByte()));
|
||||
}
|
||||
|
||||
Vars.control.setWaveData(enemies, wave, wavetime);
|
||||
Vars.player.weaponLeft = Vars.player.weaponRight = Vars.control.getWeapons().peek();
|
||||
Vars.ui.hudfrag.updateWeapons();
|
||||
|
||||
Entities.clear();
|
||||
Vars.player.id = pid;
|
||||
Vars.player.add();
|
||||
|
||||
//map
|
||||
@ -246,7 +222,7 @@ public class NetworkIO {
|
||||
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());
|
||||
Block result = WorldGenerator.rocks.get(tile.floor());
|
||||
if(result != null) tile.setBlock(result);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ public abstract class PlatformFunction{
|
||||
public String format(Date date){return "invalid";}
|
||||
public String format(int number){return "invalid";}
|
||||
public void openLink(String link){}
|
||||
public void showError(String text){}
|
||||
public void addDialog(TextField field){
|
||||
addDialog(field, 16);
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ public class Save13 extends SaveFileVersion {
|
||||
for(int i = 0; i < rocks; i ++){
|
||||
int pos = stream.readInt();
|
||||
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
|
||||
Block result = Generator.rocks.get(tile.floor());
|
||||
Block result = WorldGenerator.rocks.get(tile.floor());
|
||||
if(result != null) tile.setBlock(result);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@ import io.anuke.mindustry.resource.Upgrade;
|
||||
import io.anuke.mindustry.resource.Weapon;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.mindustry.game.GameMode;
|
||||
import io.anuke.mindustry.world.Generator;
|
||||
import io.anuke.mindustry.world.WorldGenerator;
|
||||
import io.anuke.mindustry.world.Tile;
|
||||
import io.anuke.mindustry.world.blocks.Blocks;
|
||||
import io.anuke.mindustry.world.blocks.types.BlockPart;
|
||||
@ -165,7 +165,7 @@ public class Save14 extends SaveFileVersion{
|
||||
for(int i = 0; i < rocks; i ++){
|
||||
int pos = stream.readInt();
|
||||
Tile tile = Vars.world.tile(pos % Vars.world.width(), pos / Vars.world.width());
|
||||
Block result = Generator.rocks.get(tile.floor());
|
||||
Block result = WorldGenerator.rocks.get(tile.floor());
|
||||
if(result != null) tile.setBlock(result);
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,6 @@ public class Packets {
|
||||
|
||||
}
|
||||
|
||||
public static class EntityDataPacket{
|
||||
public int playerid;
|
||||
public byte[] weapons;
|
||||
}
|
||||
|
||||
public static class SyncPacket{
|
||||
public int[] ids;
|
||||
public float[][] data;
|
||||
@ -37,6 +32,10 @@ public class Packets {
|
||||
public boolean android;
|
||||
}
|
||||
|
||||
public static class ConnectConfirmPacket{
|
||||
|
||||
}
|
||||
|
||||
public static class DisconnectPacket{
|
||||
public int playerid;
|
||||
}
|
||||
@ -85,6 +84,7 @@ public class Packets {
|
||||
public static class EnemySpawnPacket{
|
||||
public byte type, lane, tier;
|
||||
public float x, y;
|
||||
public short health;
|
||||
public int id;
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,6 @@ public class Registrator {
|
||||
StreamChunk.class,
|
||||
WorldData.class,
|
||||
SyncPacket.class,
|
||||
EntityDataPacket.class,
|
||||
PositionPacket.class,
|
||||
ShootPacket.class,
|
||||
PlacePacket.class,
|
||||
@ -39,6 +38,7 @@ public class Registrator {
|
||||
BlockTapPacket.class,
|
||||
BlockConfigPacket.class,
|
||||
EntityRequestPacket.class,
|
||||
ConnectConfirmPacket.class,
|
||||
|
||||
Class.class,
|
||||
byte[].class,
|
||||
|
@ -16,7 +16,7 @@ import io.anuke.ucore.graphics.Hue;
|
||||
import io.anuke.ucore.noise.Noise;
|
||||
import io.anuke.ucore.util.Mathf;
|
||||
|
||||
public class Generator{
|
||||
public class WorldGenerator {
|
||||
public static final ObjectMap<Block, Block> rocks = new ObjectMap(){{
|
||||
put(Blocks.stone, Blocks.rock);
|
||||
put(Blocks.snow, Blocks.icerock);
|
@ -19,7 +19,7 @@ public class CoreBlock extends Block {
|
||||
|
||||
@Override
|
||||
public int handleDamage(Tile tile, int amount){
|
||||
return Vars.debug ? amount : amount;
|
||||
return Vars.debug ? 0 : amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,6 +81,11 @@ public class DesktopLauncher {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void showError(String text){
|
||||
JOptionPane.showMessageDialog(null, text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateRPC() {
|
||||
DiscordRichPresence presence = new DiscordRichPresence();
|
||||
|
Loading…
Reference in New Issue
Block a user