Merge pull request #93 from Anuken/multithread-test-again

Multithreading Support
This commit is contained in:
Anuken
2018-02-07 21:26:07 -05:00
committed by GitHub
64 changed files with 670 additions and 466 deletions

View File

@@ -9,8 +9,10 @@ import android.os.Bundle;
import android.telephony.TelephonyManager; import android.telephony.TelephonyManager;
import com.badlogic.gdx.backends.android.AndroidApplication; import com.badlogic.gdx.backends.android.AndroidApplication;
import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration;
import io.anuke.kryonet.DefaultThreadImpl;
import io.anuke.kryonet.KryoClient; import io.anuke.kryonet.KryoClient;
import io.anuke.kryonet.KryoServer; import io.anuke.kryonet.KryoServer;
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.TextField;
@@ -85,6 +87,11 @@ public class AndroidLauncher extends AndroidApplication{
} }
} }
} }
@Override
public ThreadProvider getThreadProvider() {
return new DefaultThreadImpl();
}
}; };
if(doubleScaleTablets && isTablet(this.getContext())){ if(doubleScaleTablets && isTablet(this.getContext())){

View File

@@ -22,7 +22,7 @@ allprojects {
appName = "Mindustry" appName = "Mindustry"
gdxVersion = '1.9.8' gdxVersion = '1.9.8'
aiVersion = '1.8.1' aiVersion = '1.8.1'
uCoreVersion = 'f40fdf3' uCoreVersion = 'a480029'
} }
repositories { repositories {

View File

@@ -217,6 +217,7 @@ setting.sensitivity.name=Controller Sensitivity
setting.saveinterval.name=Autosave Interval setting.saveinterval.name=Autosave Interval
setting.seconds={0} Seconds setting.seconds={0} Seconds
setting.fullscreen.name=Fullscreen setting.fullscreen.name=Fullscreen
setting.multithread.name=Multithreading [scarlet](extremely unstable!)
setting.fps.name=Show FPS setting.fps.name=Show FPS
setting.vsync.name=VSync setting.vsync.name=VSync
setting.lasers.name=Show Power Lasers setting.lasers.name=Show Power Lasers

View File

@@ -28,4 +28,11 @@ public class Mindustry extends ModuleCore {
module(netClient = new NetClient()); module(netClient = new NetClient());
module(netCommon = new NetCommon()); module(netCommon = new NetCommon());
} }
@Override
public void render(){
super.render();
threads.handleRender();
}
} }

View File

@@ -10,9 +10,11 @@ import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.effect.Shield; import io.anuke.mindustry.entities.effect.Shield;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.net.ClientDebug; import io.anuke.mindustry.net.ClientDebug;
import io.anuke.mindustry.net.ServerDebug; import io.anuke.mindustry.net.ServerDebug;
import io.anuke.ucore.UCore; import io.anuke.ucore.UCore;
import io.anuke.ucore.entities.EffectEntity;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityGroup;
import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.ui.layout.Unit;
@@ -112,6 +114,7 @@ public class Vars{
public static final int webPort = 6568; public static final int webPort = 6568;
public static final GameState state = new GameState(); public static final GameState state = new GameState();
public static final ThreadHandler threads = new ThreadHandler(Platform.instance.getThreadProvider());
public static final ServerDebug serverDebug = new ServerDebug(); public static final ServerDebug serverDebug = new ServerDebug();
public static final ClientDebug clientDebug = new ClientDebug(); public static final ClientDebug clientDebug = new ClientDebug();
@@ -132,4 +135,5 @@ public class Vars{
public static final EntityGroup<TileEntity> tileGroup = Entities.addGroup(TileEntity.class, false); public static final EntityGroup<TileEntity> tileGroup = Entities.addGroup(TileEntity.class, false);
public static final EntityGroup<Bullet> bulletGroup = Entities.addGroup(Bullet.class); public static final EntityGroup<Bullet> bulletGroup = Entities.addGroup(Bullet.class);
public static final EntityGroup<Shield> shieldGroup = Entities.addGroup(Shield.class); public static final EntityGroup<Shield> shieldGroup = Entities.addGroup(Shield.class);
public static final EntityGroup<EffectEntity> effectGroup = Entities.addGroup(EffectEntity.class);
} }

View File

@@ -11,7 +11,6 @@ import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Log;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -26,6 +25,10 @@ public class Pathfind{
/**temporary vector2 for calculations*/ /**temporary vector2 for calculations*/
Vector2 vector = new Vector2(); Vector2 vector = new Vector2();
Vector2 v1 = new Vector2();
Vector2 v2 = new Vector2();
Vector2 v3 = new Vector2();
/**Finds the position on the path an enemy should move to. /**Finds the position on the path an enemy should move to.
* If the path is not yet calculated, this returns the enemy's position (i. e. "don't move") * If the path is not yet calculated, this returns the enemy's position (i. e. "don't move")
* @param enemy The enemy to find a path for * @param enemy The enemy to find a path for
@@ -76,8 +79,8 @@ public class Pathfind{
if(projectLen < 8 || !onLine(projection, prev.worldx(), prev.worldy(), target.worldx(), target.worldy())){ if(projectLen < 8 || !onLine(projection, prev.worldx(), prev.worldy(), target.worldx(), target.worldy())){
canProject = false; canProject = false;
}else{ }else{
projection.add(Angles.translation(Angles.angle(prev.worldx(), prev.worldy(), projection.add(v1.set(projectLen, 0).rotate(Angles.angle(prev.worldx(), prev.worldy(),
target.worldx(), target.worldy()), projectLen)); target.worldx(), target.worldy())));
} }
float dst = Vector2.dst(enemy.x, enemy.y, target.worldx(), target.worldy()); float dst = Vector2.dst(enemy.x, enemy.y, target.worldx(), target.worldy());
@@ -122,7 +125,8 @@ public class Pathfind{
public void update(){ public void update(){
//go through each spawnpoint, and if it's not found a path yet, update it //go through each spawnpoint, and if it's not found a path yet, update it
for(SpawnPoint point : world.getSpawns()){ for(int i = 0; i < world.getSpawns().size; i ++){
SpawnPoint point = world.getSpawns().get(i);
if(point.request == null || point.finder == null){ if(point.request == null || point.finder == null){
continue; continue;
} }
@@ -204,7 +208,7 @@ public class Pathfind{
} }
/**Finds the closest tile to a position, in an array of tiles.*/ /**Finds the closest tile to a position, in an array of tiles.*/
private static int findClosest(Tile[] tiles, float x, float y){ private int findClosest(Tile[] tiles, float x, float y){
int cindex = -2; int cindex = -2;
float dst = Float.MAX_VALUE; float dst = Float.MAX_VALUE;
@@ -222,23 +226,23 @@ public class Pathfind{
} }
/**Returns whether a point is on a line.*/ /**Returns whether a point is on a line.*/
private static boolean onLine(Vector2 vector, float x1, float y1, float x2, float y2){ private boolean onLine(Vector2 vector, float x1, float y1, float x2, float y2){
return MathUtils.isEqual(vector.dst(x1, y1) + vector.dst(x2, y2), Vector2.dst(x1, y1, x2, y2), 0.01f); return MathUtils.isEqual(vector.dst(x1, y1) + vector.dst(x2, y2), Vector2.dst(x1, y1, x2, y2), 0.01f);
} }
/**Returns distance from a point to a line segment.*/ /**Returns distance from a point to a line segment.*/
private static float pointLineDist(float x, float y, float x2, float y2, float px, float py){ private float pointLineDist(float x, float y, float x2, float y2, float px, float py){
float l2 = Vector2.dst2(x, y, x2, y2); float l2 = Vector2.dst2(x, y, x2, y2);
float t = Math.max(0, Math.min(1, Vector2.dot(px - x, py - y, x2 - x, y2 - y) / l2)); float t = Math.max(0, Math.min(1, Vector2.dot(px - x, py - y, x2 - x, y2 - y) / l2));
Vector2 projection = Tmp.v1.set(x, y).add(Tmp.v2.set(x2, y2).sub(x, y).scl(t)); // Projection falls on the segment Vector2 projection = v1.set(x, y).add(v2.set(x2, y2).sub(x, y).scl(t)); // Projection falls on the segment
return projection.dst(px, py); return projection.dst(px, py);
} }
//TODO documentation //TODO documentation
private static Vector2 projectPoint(float x1, float y1, float x2, float y2, float pointx, float pointy){ private Vector2 projectPoint(float x1, float y1, float x2, float y2, float pointx, float pointy){
float px = x2-x1, py = y2-y1, dAB = px*px + py*py; float px = x2-x1, py = y2-y1, dAB = px*px + py*py;
float u = ((pointx - x1) * px + (pointy - y1) * py) / dAB; float u = ((pointx - x1) * px + (pointy - y1) * py) / dAB;
float x = x1 + u * px, y = y1 + u * py; float x = x1 + u * px, y = y1 + u * py;
return Tmp.v3.set(x, y); //this is D return v3.set(x, y); //this is D
} }
} }

View File

@@ -4,12 +4,13 @@ import com.badlogic.gdx.ai.utils.Collision;
import com.badlogic.gdx.ai.utils.Ray; import com.badlogic.gdx.ai.utils.Ray;
import com.badlogic.gdx.ai.utils.RaycastCollisionDetector; import com.badlogic.gdx.ai.utils.RaycastCollisionDetector;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import static io.anuke.mindustry.Vars.*;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.util.Geometry; import io.anuke.ucore.util.Geometry;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world;
public class Raycaster implements RaycastCollisionDetector<Vector2>{ public class Raycaster implements RaycastCollisionDetector<Vector2>{
private boolean found = false; private boolean found = false;
@@ -75,7 +76,8 @@ public class Raycaster implements RaycastCollisionDetector<Vector2>{
if(tile == null || tile.solid()) return true; if(tile == null || tile.solid()) return true;
for(Tile near : tile.getNearby()){ for(int i = 0; i < 4; i ++){
Tile near = tile.getNearby(i);
if(near == null || near.solid()) return true; if(near == null || near.solid()) return true;
} }

View File

@@ -3,16 +3,15 @@ package io.anuke.mindustry.ai;
import com.badlogic.gdx.ai.pfa.DefaultGraphPath; import com.badlogic.gdx.ai.pfa.DefaultGraphPath;
import com.badlogic.gdx.ai.pfa.SmoothableGraphPath; import com.badlogic.gdx.ai.pfa.SmoothableGraphPath;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.util.Tmp;
public class SmoothGraphPath extends DefaultGraphPath<Tile> implements SmoothableGraphPath<Tile, Vector2>{ public class SmoothGraphPath extends DefaultGraphPath<Tile> implements SmoothableGraphPath<Tile, Vector2>{
private Vector2 vector = new Vector2();
@Override @Override
public Vector2 getNodePosition(int index){ public Vector2 getNodePosition(int index){
Tile tile = nodes.get(index); Tile tile = nodes.get(index);
return Tmp.v3.set(tile.worldx(), tile.worldy()); return vector.set(tile.worldx(), tile.worldy());
} }
@Override @Override

View File

@@ -4,11 +4,12 @@ import io.anuke.mindustry.world.Tile;
/**Tilegraph that ignores player-made tiles.*/ /**Tilegraph that ignores player-made tiles.*/
public class TileGraph implements OptimizedGraph<Tile> { public class TileGraph implements OptimizedGraph<Tile> {
private Tile[] tiles = new Tile[4];
/**Used for the OptimizedPathFinder implementation.*/ /**Used for the OptimizedPathFinder implementation.*/
@Override @Override
public Tile[] connectionsOf(Tile node){ public Tile[] connectionsOf(Tile node){
Tile[] nodes = node.getNearby(); Tile[] nodes = node.getNearby(tiles);
for(int i = 0; i < 4; i ++){ for(int i = 0; i < 4; i ++){
if(nodes[i] != null && !nodes[i].passable()){ if(nodes[i] != null && !nodes[i].passable()){
nodes[i] = null; nodes[i] = null;

View File

@@ -172,7 +172,7 @@ public class Control extends Module{
int last = Settings.getInt("hiscore" + world.getMap().name); int last = Settings.getInt("hiscore" + world.getMap().name);
if(state.wave > last && !state.mode.infiniteResources && !state.mode.toggleWaves){ if(state.wave > last && !state.mode.infiniteResources && !state.mode.disableWaveTimer){
Settings.putInt("hiscore" + world.getMap().name, state.wave); Settings.putInt("hiscore" + world.getMap().name, state.wave);
Settings.save(); Settings.save();
hiscore = true; hiscore = true;
@@ -345,6 +345,7 @@ public class Control extends Module{
} }
if(!state.is(State.paused) || Net.active()){ if(!state.is(State.paused) || Net.active()){
Entities.update(effectGroup);
if(respawntime > 0){ if(respawntime > 0){

View File

@@ -1,6 +1,5 @@
package io.anuke.mindustry.core; package io.anuke.mindustry.core;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
@@ -9,6 +8,7 @@ import io.anuke.mindustry.game.EnemySpawn;
import io.anuke.mindustry.game.EventType.GameOverEvent; import io.anuke.mindustry.game.EventType.GameOverEvent;
import io.anuke.mindustry.game.EventType.PlayEvent; import io.anuke.mindustry.game.EventType.PlayEvent;
import io.anuke.mindustry.game.EventType.ResetEvent; import io.anuke.mindustry.game.EventType.ResetEvent;
import io.anuke.mindustry.game.EventType.WaveEvent;
import io.anuke.mindustry.game.SpawnPoint; import io.anuke.mindustry.game.SpawnPoint;
import io.anuke.mindustry.game.WaveCreator; import io.anuke.mindustry.game.WaveCreator;
import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Fx;
@@ -35,10 +35,6 @@ import static io.anuke.mindustry.Vars.*;
public class Logic extends Module { public class Logic extends Module {
private final Array<EnemySpawn> spawns = WaveCreator.getSpawns(); private final Array<EnemySpawn> spawns = WaveCreator.getSpawns();
public Logic(){
Timers.setDeltaProvider(() -> Math.min(Gdx.graphics.getDeltaTime()*60f, 60));
}
@Override @Override
public void init(){ public void init(){
Entities.initPhysics(); Entities.initPhysics();
@@ -107,17 +103,23 @@ public class Logic extends Module {
state.wave ++; state.wave ++;
state.wavetime = wavespace * state.difficulty.timeScaling; state.wavetime = wavespace * state.difficulty.timeScaling;
state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling; state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling;
Events.fire(WaveEvent.class);
} }
@Override @Override
public void update(){ public void update(){
if(!state.is(State.paused) || Net.active()){ if(!state.is(State.paused) || Net.active()){
Timers.update(); Timers.update();
} }
if(!state.is(State.menu)){ if(!state.is(State.menu)){
if(world.getCore().block() != ProductionBlocks.core && !state.gameOver){ if(!Net.client())
world.pathfinder().update();
if(world.getCore() != null && world.getCore().block() != ProductionBlocks.core && !state.gameOver){
state.gameOver = true; state.gameOver = true;
NetEvents.handleGameOver(); NetEvents.handleGameOver();
Events.fire(GameOverEvent.class); Events.fire(GameOverEvent.class);
@@ -125,7 +127,7 @@ public class Logic extends Module {
if(!state.is(State.paused) || Net.active()){ if(!state.is(State.paused) || Net.active()){
if(!state.mode.toggleWaves){ if(!state.mode.disableWaveTimer){
if(state.enemies <= 0){ if(state.enemies <= 0){
state.wavetime -= delta(); state.wavetime -= delta();

View File

@@ -12,6 +12,7 @@ import com.badlogic.gdx.utils.FloatArray;
import com.badlogic.gdx.utils.Pools; import com.badlogic.gdx.utils.Pools;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.game.SpawnPoint; import io.anuke.mindustry.game.SpawnPoint;
import io.anuke.mindustry.graphics.BlockRenderer; import io.anuke.mindustry.graphics.BlockRenderer;
@@ -24,7 +25,6 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Blocks; import io.anuke.mindustry.world.blocks.Blocks;
import io.anuke.mindustry.world.blocks.ProductionBlocks; import io.anuke.mindustry.world.blocks.ProductionBlocks;
import io.anuke.ucore.core.*; import io.anuke.ucore.core.*;
import io.anuke.ucore.entities.DestructibleEntity;
import io.anuke.ucore.entities.EffectEntity; import io.anuke.ucore.entities.EffectEntity;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.function.Callable; import io.anuke.ucore.function.Callable;
@@ -50,6 +50,7 @@ public class Renderer extends RendererModule{
private int targetscale = baseCameraScale; private int targetscale = baseCameraScale;
private FloatArray shieldHits = new FloatArray(); private FloatArray shieldHits = new FloatArray();
private Array<Callable> shieldDraws = new Array<>(); private Array<Callable> shieldDraws = new Array<>();
private Rectangle rect = new Rectangle(), rect2 = new Rectangle();
private BlockRenderer blocks = new BlockRenderer(); private BlockRenderer blocks = new BlockRenderer();
public Renderer() { public Renderer() {
@@ -58,11 +59,11 @@ public class Renderer extends RendererModule{
Core.cameraScale = baseCameraScale; Core.cameraScale = baseCameraScale;
Effects.setEffectProvider((name, color, x, y, rotation) -> { Effects.setEffectProvider((name, color, x, y, rotation) -> {
if(Settings.getBool("effects")){ if(Settings.getBool("effects")){
Rectangle view = Tmp.r1.setSize(camera.viewportWidth, camera.viewportHeight) Rectangle view = rect.setSize(camera.viewportWidth, camera.viewportHeight)
.setCenter(camera.position.x, camera.position.y); .setCenter(camera.position.x, camera.position.y);
Rectangle pos = Tmp.r2.setSize(name.size).setCenter(x, y); Rectangle pos = rect2.setSize(name.size).setCenter(x, y);
if(view.overlaps(pos)){ if(view.overlaps(pos)){
new EffectEntity(name, color, rotation).set(x, y).add(); new EffectEntity(name, color, rotation).set(x, y).add(effectGroup);
} }
} }
}); });
@@ -190,6 +191,7 @@ public class Renderer extends RendererModule{
Graphics.shader(); Graphics.shader();
Entities.draw(bulletGroup); Entities.draw(bulletGroup);
Entities.draw(effectGroup);
drawShield(); drawShield();
@@ -242,13 +244,15 @@ public class Renderer extends RendererModule{
Draw.color(Color.RED); Draw.color(Color.RED);
for(Enemy enemy : enemyGroup.all()) { for(Enemy enemy : enemyGroup.all()) {
if (Tmp.r1.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y).overlaps(enemy.hitbox.getRect(enemy.x, enemy.y))) { if (rect.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y)
.overlaps(enemy.hitbox.getRect(enemy.x, enemy.y))) {
continue; continue;
} }
float angle = Angles.angle(camera.position.x, camera.position.y, enemy.x, enemy.y); float angle = Angles.angle(camera.position.x, camera.position.y, enemy.x, enemy.y);
Angles.translation(angle, Unit.dp.scl(20f)); float tx = Angles.trnsx(angle, Unit.dp.scl(20f));
Draw.rect("enemyarrow", camera.position.x + Angles.x(), camera.position.y + Angles.y(), angle); float ty = Angles.trnsy(angle, Unit.dp.scl(20f));
Draw.rect("enemyarrow", camera.position.x + tx, camera.position.y + ty, angle);
} }
Draw.color(); Draw.color();
@@ -445,11 +449,13 @@ public class Renderer extends RendererModule{
} }
} }
void drawHealth(DestructibleEntity dest){ void drawHealth(SyncEntity dest){
float x = dest.getDrawPosition().x;
float y = dest.getDrawPosition().y;
if(dest instanceof Player && snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate")){ if(dest instanceof Player && snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate")){
drawHealth((int) dest.x, (int) dest.y - 7f, dest.health, dest.maxhealth); drawHealth((int) x, (int) y - 7f, dest.health, dest.maxhealth);
}else{ }else{
drawHealth(dest.x, dest.y - 7f, dest.health, dest.maxhealth); drawHealth(x, y - 7f, dest.health, dest.maxhealth);
} }
} }

View File

@@ -0,0 +1,109 @@
package io.anuke.mindustry.core;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.util.Log;
import static io.anuke.mindustry.Vars.logic;
public class ThreadHandler {
private final ThreadProvider impl;
private float delta = 1f;
private long frame = 0;
private float framesSinceUpdate;
private boolean enabled;
private final Object updateLock = new Object();
private boolean rendered = true;
public ThreadHandler(ThreadProvider impl){
this.impl = impl;
Timers.setDeltaProvider(() -> impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f);
}
public int getFPS(){
return (int)(60/delta);
}
public long getFrameID(){
return frame;
}
public float getFramesSinceUpdate(){
return framesSinceUpdate;
}
public void handleRender(){
if(!enabled) return;
framesSinceUpdate += Timers.delta();
synchronized (updateLock) {
rendered = true;
updateLock.notify();
}
}
public void setEnabled(boolean enabled){
if(enabled){
logic.doUpdate = false;
Timers.runTask(2f, () -> {
impl.start(this::runLogic);
this.enabled = true;
});
}else{
this.enabled = false;
impl.stop();
Timers.runTask(2f, () -> {
logic.doUpdate = true;
});
}
}
public boolean isEnabled(){
return enabled;
}
private void runLogic(){
try {
while (true) {
long time = TimeUtils.millis();
logic.update();
long elapsed = TimeUtils.timeSinceMillis(time);
long target = (long) (1000 / 60f);
delta = Math.max(elapsed, target) / 1000f * 60f;
if (elapsed < target) {
impl.sleep(target - elapsed);
}
synchronized(updateLock) {
while(!rendered) {
updateLock.wait();
}
rendered = false;
}
frame ++;
framesSinceUpdate = 0;
}
} catch (InterruptedException ex) {
Log.info("Stopping logic thread.");
} catch (Exception ex) {
Gdx.app.postRunnable(() -> {
throw new RuntimeException(ex);
});
}
}
public interface ThreadProvider {
boolean isOnThread();
void sleep(long ms) throws InterruptedException;
void start(Runnable run);
void stop();
}
}

View File

@@ -8,7 +8,6 @@ import io.anuke.mindustry.ai.Pathfind;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.game.SpawnPoint; import io.anuke.mindustry.game.SpawnPoint;
import io.anuke.mindustry.io.Maps; import io.anuke.mindustry.io.Maps;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Map; import io.anuke.mindustry.world.Map;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
@@ -42,12 +41,6 @@ public class World extends Module{
currentMap = maps.getMap(0); currentMap = maps.getMap(0);
} }
@Override
public void update(){
if(!Net.client())
pathfind.update();
}
@Override @Override
public void dispose(){ public void dispose(){
maps.dispose(); maps.dispose();
@@ -140,18 +133,6 @@ public class World extends Module{
return tiles; return tiles;
} }
public Tile[] getNearby(int x, int y){
return getNearby(x, y, temptiles);
}
public Tile[] getNearby(int x, int y, Tile[] temptiles){
temptiles[0] = tile(x+1, y);
temptiles[1] = tile(x, y+1);
temptiles[2] = tile(x-1, y);
temptiles[3] = tile(x, y-1);
return temptiles;
}
private void createTiles(){ private void createTiles(){
for(int x = 0; x < tiles.length; x ++){ for(int x = 0; x < tiles.length; x ++){
for(int y = 0; y < tiles[0].length; y ++){ for(int y = 0; y < tiles[0].length; y ++){

View File

@@ -9,21 +9,26 @@ import io.anuke.ucore.util.Mathf;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Bullet extends BulletEntity{ public class Bullet extends BulletEntity{
public boolean absorbed = false;
public Bullet(BulletType type, Entity owner, float x, float y, float angle){ public Bullet(BulletType type, Entity owner, float x, float y, float angle){
super(type, owner, angle); super(type, owner, angle);
set(x, y); set(x, y);
this.type = type; this.type = type;
} }
public void absorb(){
absorbed = true;
remove();
}
public void draw(){ public void draw(){
type.draw(this); //interpolate position linearly at low tick speeds
if(SyncEntity.isSmoothing()){
x += threads.getFramesSinceUpdate() * velocity.x;
y += threads.getFramesSinceUpdate() * velocity.y;
type.draw(this);
x -= threads.getFramesSinceUpdate() * velocity.x;
y -= threads.getFramesSinceUpdate() * velocity.y;
}else{
type.draw(this);
}
} }
public float drawSize(){ public float drawSize(){

View File

@@ -252,10 +252,11 @@ public abstract class BulletType extends BaseBulletType<Bullet>{
Effects.effect(Fx.blastsmoke, b); Effects.effect(Fx.blastsmoke, b);
Effects.effect(Fx.blastexplosion, b); Effects.effect(Fx.blastexplosion, b);
Angles.circle(30, f->{ //TODO remove translation() usage
Angles.translation(f, 6f); Angles.circleVectors(30, 6f, (x, y) -> {
Bullet o = new Bullet(blastshot, b.owner, b.x + Angles.x(), b.y + Angles.y(), f).add(); float ang = Mathf.atan2(x, y);
Bullet o = new Bullet(blastshot, b.owner, b.x + x, b.y + y, ang).add();
o.damage = b.damage/9; o.damage = b.damage/9;
}); });
} }

View File

@@ -1,7 +1,7 @@
package io.anuke.mindustry.entities; package io.anuke.mindustry.entities;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.utils.TimeUtils; import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Fx;
import io.anuke.mindustry.graphics.Shaders; import io.anuke.mindustry.graphics.Shaders;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
@@ -16,7 +16,7 @@ import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp; import io.anuke.ucore.util.Translator;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -34,13 +34,15 @@ public class Player extends SyncEntity{
public Weapon weaponRight = Weapon.blaster; public Weapon weaponRight = Weapon.blaster;
public Mech mech = Mech.standard; public Mech mech = Mech.standard;
public float angle;
public float targetAngle = 0f; public float targetAngle = 0f;
public float stucktime = 0f; public float stucktime = 0f;
public boolean dashing = false; public boolean dashing = false;
public int clientid; public int clientid;
public boolean isLocal = false; public boolean isLocal = false;
private Vector2 movement = new Vector2();
private Translator tr = new Translator();
public Player(){ public Player(){
hitbox.setSize(5); hitbox.setSize(5);
@@ -102,7 +104,7 @@ public class Player extends SyncEntity{
} }
@Override @Override
public void draw(){ public void drawSmooth(){
if(isAndroid && isLocal){ if(isAndroid && isLocal){
angle = Mathf.lerpAngDelta(angle, targetAngle, 0.2f); angle = Mathf.lerpAngDelta(angle, targetAngle, 0.2f);
} }
@@ -121,12 +123,12 @@ public class Player extends SyncEntity{
if(!isAndroid) { if(!isAndroid) {
for (int i : Mathf.signs) { for (int i : Mathf.signs) {
Weapon weapon = i < 0 ? weaponLeft : weaponRight; Weapon weapon = i < 0 ? weaponLeft : weaponRight;
Angles.vector.set(3 * i, 2).rotate(angle - 90); tr.trns(angle - 90, 3*i, 2);
float w = i > 0 ? -8 : 8; float w = i > 0 ? -8 : 8;
if(snap){ if(snap){
Draw.rect(weapon.name + "-equip", (int)x + Angles.x(), (int)y + Angles.y(), w, 8, angle - 90); Draw.rect(weapon.name + "-equip", (int)x + tr.x, (int)y + tr.y, w, 8, angle - 90);
}else{ }else{
Draw.rect(weapon.name + "-equip", x + Angles.x(), y + Angles.y(), w, 8, angle - 90); Draw.rect(weapon.name + "-equip", x + tr.x, y + tr.y, w, 8, angle - 90);
} }
} }
} }
@@ -172,15 +174,15 @@ public class Player extends SyncEntity{
health = Mathf.clamp(health, -1, maxhealth); health = Mathf.clamp(health, -1, maxhealth);
vector.set(0, 0); movement.set(0, 0);
float xa = Inputs.getAxis("move_x"); float xa = Inputs.getAxis("move_x");
float ya = Inputs.getAxis("move_y"); float ya = Inputs.getAxis("move_y");
if(Math.abs(xa) < 0.3) xa = 0; if(Math.abs(xa) < 0.3) xa = 0;
if(Math.abs(ya) < 0.3) ya = 0; if(Math.abs(ya) < 0.3) ya = 0;
vector.y += ya*speed; movement.y += ya*speed;
vector.x += xa*speed; movement.x += xa*speed;
boolean shooting = !Inputs.keyDown("dash") && Inputs.keyDown("shoot") && control.input().recipe == null boolean shooting = !Inputs.keyDown("dash") && Inputs.keyDown("shoot") && control.input().recipe == null
&& !ui.hasMouse() && !control.input().onConfigurable(); && !ui.hasMouse() && !control.input().onConfigurable();
@@ -190,23 +192,22 @@ public class Player extends SyncEntity{
weaponRight.update(player, false); weaponRight.update(player, false);
} }
if(dashing && Timers.get(this, "dashfx", 3) && vector.len() > 0){ if(dashing && Timers.get(this, "dashfx", 3) && movement.len() > 0){
Angles.translation(angle + 180, 3f); Effects.effect(Fx.dashsmoke, x + Angles.trnsx(angle + 180f, 3f), y + Angles.trnsy(angle + 180f, 3f));
Effects.effect(Fx.dashsmoke, x + Angles.x(), y + Angles.y());
} }
vector.limit(speed); movement.limit(speed);
if(!noclip){ if(!noclip){
move(vector.x*Timers.delta(), vector.y*Timers.delta()); move(movement.x*Timers.delta(), movement.y*Timers.delta());
}else{ }else{
x += vector.x*Timers.delta(); x += movement.x*Timers.delta();
y += vector.y*Timers.delta(); y += movement.y*Timers.delta();
} }
if(!shooting){ if(!shooting){
if(!vector.isZero()) if(!movement.isZero())
angle = Mathf.lerpAngDelta(angle, vector.angle(), 0.13f); angle = Mathf.lerpAngDelta(angle, movement.angle(), 0.13f);
}else{ }else{
float angle = Angles.mouseAngle(x, y); float angle = Angles.mouseAngle(x, y);
this.angle = Mathf.lerpAngDelta(this.angle, angle, 0.1f); this.angle = Mathf.lerpAngDelta(this.angle, angle, 0.1f);
@@ -273,40 +274,24 @@ public class Player extends SyncEntity{
this.health = health; this.health = health;
this.dashing = dashing == 1; this.dashing = dashing == 1;
interpolator.targetrot = angle; interpolator.read(this.x, this.y, x, y, angle, time);
interpolator.time = 0f;
interpolator.last.set(this.x, this.y);
interpolator.target.set(x, y);
interpolator.spacing = Math.min(Math.max(((TimeUtils.timeSinceMillis(time) / 1000f) * 60f), 4f), 10);
} }
@Override @Override
public void interpolate() { public void interpolate() {
super.interpolate();
Interpolator i = interpolator; Interpolator i = interpolator;
i.time += 1f / i.spacing * Timers.delta(); float tx = x + Angles.trnsx(angle + 180f, 3f);
float ty = y + Angles.trnsy(angle + 180f, 3f);
Mathf.lerp2(Tmp.v2.set(i.last), i.target, i.time);
x = Tmp.v2.x;
y = Tmp.v2.y;
if(i.target.dst(x, y) > 128){
set(i.target.x, i.target.y);
i.time = 0f;
i.last.set(i.target);
}
angle = Mathf.lerpAngDelta(angle, i.targetrot, 0.6f);
if(isAndroid && i.target.dst(i.last) > 2f && Timers.get(this, "dashfx", 2)){ if(isAndroid && i.target.dst(i.last) > 2f && Timers.get(this, "dashfx", 2)){
Angles.translation(angle + 180, 3f); Effects.effect(Fx.dashsmoke, tx, ty);
Effects.effect(Fx.dashsmoke, x + Angles.x(), y + Angles.y());
} }
if(dashing && !dead && Timers.get(this, "dashfx", 3) && i.target.dst(i.last) > 1f){ if(dashing && !dead && Timers.get(this, "dashfx", 3) && i.target.dst(i.last) > 1f){
Angles.translation(angle + 180, 3f); Effects.effect(Fx.dashsmoke, tx, ty);
Effects.effect(Fx.dashsmoke, x + Angles.x(), y + Angles.y());
} }
} }

View File

@@ -1,28 +1,79 @@
package io.anuke.mindustry.entities; package io.anuke.mindustry.entities;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.ObjectIntMap; import com.badlogic.gdx.utils.ObjectIntMap;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.DestructibleEntity; import io.anuke.ucore.entities.DestructibleEntity;
import io.anuke.ucore.util.Mathf;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import static io.anuke.mindustry.Vars.threads;
public abstract class SyncEntity extends DestructibleEntity{ public abstract class SyncEntity extends DestructibleEntity{
private static ObjectIntMap<Class<? extends SyncEntity>> writeSizes = new ObjectIntMap<>(); private static ObjectIntMap<Class<? extends SyncEntity>> writeSizes = new ObjectIntMap<>();
protected transient Interpolator interpolator = new Interpolator(); protected transient Interpolator interpolator = new Interpolator();
//smoothed position/angle
private Vector3 spos = new Vector3();
public float angle;
static{ static{
setWriteSize(Enemy.class, 4 + 4 + 2 + 2); setWriteSize(Enemy.class, 4 + 4 + 2 + 2);
setWriteSize(Player.class, 4 + 4 + 4 + 2 + 1); setWriteSize(Player.class, 4 + 4 + 4 + 2 + 1);
} }
public static boolean isSmoothing(){
return threads.isEnabled() && threads.getFPS() <= Gdx.graphics.getFramesPerSecond() / 2f;
}
public abstract void writeSpawn(ByteBuffer data); public abstract void writeSpawn(ByteBuffer data);
public abstract void readSpawn(ByteBuffer data); public abstract void readSpawn(ByteBuffer data);
public abstract void write(ByteBuffer data); public abstract void write(ByteBuffer data);
public abstract void read(ByteBuffer data, long time); public abstract void read(ByteBuffer data, long time);
public abstract void interpolate();
public void interpolate(){
interpolator.update();
x = interpolator.pos.x;
y = interpolator.pos.y;
angle = interpolator.angle;
}
@Override
public final void draw(){
float x = this.x, y = this.y, angle = this.angle;
//interpolates data at low tick speeds.
if(isSmoothing()){
if(Vector2.dst(spos.x, spos.y, x, y) > 128){
spos.set(x, y, angle);
}
this.x = spos.x = Mathf.lerpDelta(spos.x, x, 0.2f);
this.y = spos.y = Mathf.lerpDelta(spos.y, y, 0.2f);
this.angle = spos.z = Mathf.lerpAngDelta(spos.z, angle, 0.3f);
}
drawSmooth();
this.x = x;
this.y = y;
this.angle = angle;
}
public Vector3 getDrawPosition(){
return isSmoothing() ? spos : spos.set(x, y, angle);
}
public void drawSmooth(){}
public int getWriteSize(){ public int getWriteSize(){
return getWriteSize(getClass()); return getWriteSize(getClass());
@@ -47,11 +98,40 @@ public abstract class SyncEntity extends DestructibleEntity{
return (T)this; return (T)this;
} }
public class Interpolator { public static class Interpolator {
//used for movement
public Vector2 target = new Vector2(); public Vector2 target = new Vector2();
public Vector2 last = new Vector2(); public Vector2 last = new Vector2();
public float targetrot; public float targetrot;
public float spacing = 1f; public float spacing = 1f;
public float time; public float time;
//current state
public Vector2 pos = new Vector2();
public float angle;
public void read(float cx, float cy, float x, float y, float angle, long sent){
targetrot = angle;
time = 0f;
last.set(cx, cy);
target.set(x, y);
spacing = Math.min(Math.max(((TimeUtils.timeSinceMillis(sent) / 1000f) * 60f), 4f), 10);
}
public void update(){
time += 1f / spacing * Timers.delta();
Mathf.lerp2(pos.set(last), target, time);
angle = Mathf.lerpAngDelta(angle, targetrot, 0.6f);
if(target.dst(pos) > 128){
pos.set(target);
last.set(target);
time = 0f;
}
}
} }
} }

View File

@@ -108,10 +108,10 @@ public class TileEntity extends Entity{
public void update(){ public void update(){
if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) && if(health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) &&
Mathf.chance(0.009f*Timers.delta()*(1f-health/tile.block().health))){ Mathf.chance(0.009f*Timers.delta()*(1f-health/tile.block().health))){
Effects.effect(Fx.smoke, x+Mathf.range(4), y+Mathf.range(4)); Effects.effect(Fx.smoke, x+Mathf.range(4), y+Mathf.range(4));
} }
if(health <= 0){ if(health <= 0){
onDeath(); onDeath();
} }

View File

@@ -11,20 +11,21 @@ import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.Entity; import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.entities.SolidEntity; import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Consumer;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Physics; import io.anuke.ucore.util.Physics;
import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class DamageArea{ public class DamageArea{
private static Rectangle rect = new Rectangle(); private static Rectangle rect = new Rectangle();
private static Translator tr = new Translator();
//only for entities, not tiles (yet!) //only for entities, not tiles (yet!)
public static void damageLine(Entity owner, Effect effect, float x, float y, float angle, float length, int damage){ public static void damageLine(Entity owner, Effect effect, float x, float y, float angle, float length, int damage){
Angles.translation(angle, length); tr.trns(angle, length);
rect.setPosition(x, y).setSize(Angles.x(), Angles.y()); rect.setPosition(x, y).setSize(tr.x, tr.y);
float x2 = Angles.x() + x, y2 = Angles.y() + y; float x2 = tr.x + x, y2 = tr.y + y;
if(rect.width < 0){ if(rect.width < 0){
rect.x += rect.width; rect.x += rect.width;

View File

@@ -3,21 +3,23 @@ package io.anuke.mindustry.entities.effect;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import static io.anuke.mindustry.Vars.*;
import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Fx;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.types.PowerAcceptor; import io.anuke.mindustry.world.blocks.types.PowerAcceptor;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.entities.TimedEntity; import io.anuke.ucore.entities.TimedEntity;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.tilesize;
import static io.anuke.mindustry.Vars.world;
public class EMP extends TimedEntity{ public class EMP extends TimedEntity{
static final int maxTargets = 8; static final int maxTargets = 8;
static Array<Tile> array = new Array<>(); static Array<Tile> array = new Array<>();
static Translator tr = new Translator();
int radius = 4; int radius = 4;
int damage = 6; int damage = 6;
@@ -80,8 +82,8 @@ public class EMP extends TimedEntity{
} }
for(int i = 0; i < 14 - targets.size; i ++){ for(int i = 0; i < 14 - targets.size; i ++){
Angles.translation(Mathf.randomSeed(i + id*77)*360f, radius * tilesize); tr.trns(Mathf.randomSeed(i + id*77)*360f, radius * tilesize);
drawLine(x + Angles.x(), y + Angles.y()); drawLine(x + tr.x, y + tr.y);
} }
Lines.stroke(fract()*2f); Lines.stroke(fract()*2f);

View File

@@ -4,19 +4,18 @@ import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
import com.badlogic.gdx.utils.ObjectSet; import com.badlogic.gdx.utils.ObjectSet;
import static io.anuke.mindustry.Vars.*;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.graphics.Fx; import io.anuke.mindustry.graphics.Fx;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Effects;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.Entity; import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.entities.SolidEntity; import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.enemyGroup;
public class TeslaOrb extends Entity{ public class TeslaOrb extends Entity{
private Array<Vector2> points = new Array<>(); private Array<Vector2> points = new Array<>();
@@ -25,6 +24,7 @@ public class TeslaOrb extends Entity{
private float range = 0; private float range = 0;
private float lifetime = 30f; private float lifetime = 30f;
private float life = 0f; private float life = 0f;
private Vector2 vector = new Vector2();
public TeslaOrb(float x, float y, float range, int damage){ public TeslaOrb(float x, float y, float range, int damage){
set(x, y); set(x, y);
@@ -83,7 +83,7 @@ public class TeslaOrb extends Entity{
float range = 1f; float range = 1f;
Vector2 previous = Tmp.v1.set(x, y); Vector2 previous = vector.set(x, y);
for(Vector2 enemy : points){ for(Vector2 enemy : points){

View File

@@ -1,19 +1,18 @@
package io.anuke.mindustry.entities.enemies; package io.anuke.mindustry.entities.enemies;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.TimeUtils;
import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.Bullet;
import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.SyncEntity; import io.anuke.mindustry.entities.SyncEntity;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.net.NetEvents; import io.anuke.mindustry.net.NetEvents;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.Entity; import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.entities.SolidEntity; import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Timer; import io.anuke.ucore.util.Timer;
import io.anuke.ucore.util.Tmp; import io.anuke.ucore.util.Translator;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -30,12 +29,15 @@ public class Enemy extends SyncEntity {
public Enemy spawner; public Enemy spawner;
public int spawned; public int spawned;
public float angle;
public Vector2 velocity = new Vector2(); public Vector2 velocity = new Vector2();
public Vector2 totalMove = new Vector2();
public Vector2 tpos = new Vector2(-999, -999);
public Entity target; public Entity target;
public float hitTime; public float hitTime;
public int tier = 1; public int tier = 1;
public Vector2 totalMove = new Vector2();
public TextureRegion region;
public Translator tr = new Translator();
public Enemy(EnemyType type){ public Enemy(EnemyType type){
this.type = type; this.type = type;
@@ -50,7 +52,7 @@ public class Enemy extends SyncEntity {
} }
@Override @Override
public void draw(){ public void drawSmooth(){
type.draw(this); type.draw(this);
} }
@@ -90,6 +92,7 @@ public class Enemy extends SyncEntity {
hitbox.setSize(type.hitsize); hitbox.setSize(type.hitsize);
hitboxTile.setSize(type.hitsizeTile); hitboxTile.setSize(type.hitsizeTile);
maxhealth = type.health * tier; maxhealth = type.health * tier;
region = Draw.region(type.name + "-t" + Mathf.clamp(tier, 1, 3));
heal(); heal();
} }
@@ -138,25 +141,7 @@ public class Enemy extends SyncEntity {
this.health = health; this.health = health;
interpolator.targetrot = angle / 2f; interpolator.read(this.x, this.y, x, y, angle, time);
interpolator.time = 0f;
interpolator.last.set(this.x, this.y);
interpolator.target.set(x, y);
interpolator.spacing = Math.min(Math.max(((TimeUtils.timeSinceMillis(time) / 1000f) * 60f), 4f), 10f);
}
@Override
public void interpolate() {
Interpolator i = interpolator;
i.time += 1f / i.spacing * Timers.delta();
Mathf.lerp2(Tmp.v2.set(i.last), i.target, i.time);
x = Tmp.v2.x;
y = Tmp.v2.y;
angle = Mathf.lerpAngDelta(angle, i.targetrot, 0.6f);
} }
public void shoot(BulletType bullet){ public void shoot(BulletType bullet){
@@ -166,13 +151,13 @@ public class Enemy extends SyncEntity {
public void shoot(BulletType bullet, float rotation){ public void shoot(BulletType bullet, float rotation){
if(!(Net.client())) { if(!(Net.client())) {
Angles.translation(angle + rotation, type.length); tr.trns(angle + rotation, type.length);
Bullet out = new Bullet(bullet, this, x + Angles.x(), y + Angles.y(), this.angle + rotation).add(); Bullet out = new Bullet(bullet, this, x + tr.x, y + tr.y, this.angle + rotation).add();
out.damage = (int) ((bullet.damage * (1 + (tier - 1) * 1f))); out.damage = (int) ((bullet.damage * (1 + (tier - 1) * 1f)));
type.onShoot(this, bullet, rotation); type.onShoot(this, bullet, rotation);
if(Net.server()){ if(Net.server()){
NetEvents.handleBullet(bullet, this, x + Angles.x(), y + Angles.y(), this.angle + rotation, (short)out.damage); NetEvents.handleBullet(bullet, this, x + tr.x, y + tr.y, this.angle + rotation, (short)out.damage);
} }
} }
} }

View File

@@ -19,7 +19,6 @@ import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -60,6 +59,10 @@ public class EnemyType {
protected final int timerReload = timeid ++; protected final int timerReload = timeid ++;
protected final int timerReset = timeid ++; protected final int timerReset = timeid ++;
protected final Vector2 shift = new Vector2();
protected final Vector2 move = new Vector2();
protected final Vector2 calc = new Vector2();
public EnemyType(String name){ public EnemyType(String name){
this.id = lastid++; this.id = lastid++;
this.name = name; this.name = name;
@@ -67,15 +70,13 @@ public class EnemyType {
} }
public void draw(Enemy enemy){ public void draw(Enemy enemy){
String region = name + "-t" + Mathf.clamp(enemy.tier, 1, 3);
Shaders.outline.color.set(tierColors[enemy.tier - 1]); Shaders.outline.color.set(tierColors[enemy.tier - 1]);
Shaders.outline.lighten = Mathf.clamp(enemy.hitTime/hitDuration); Shaders.outline.lighten = Mathf.clamp(enemy.hitTime/hitDuration);
Shaders.outline.region = Draw.region(region); Shaders.outline.region = enemy.region;
Shaders.outline.apply(); Shaders.outline.apply();
Draw.rect(region, enemy.x, enemy.y, enemy.angle - 90); Draw.rect(enemy.region, enemy.x, enemy.y, enemy.angle - 90);
Draw.color(); Draw.color();
Graphics.flush(); Graphics.flush();
@@ -160,14 +161,14 @@ public class EnemyType {
Vector2 vec; Vector2 vec;
if(nearCore){ if(nearCore){
vec = Tmp.v1.setZero(); vec = move.setZero();
if(targetCore) enemy.target = core.entity; if(targetCore) enemy.target = core.entity;
}else{ }else{
vec = world.pathfinder().find(enemy); vec = world.pathfinder().find(enemy);
vec.sub(enemy.x, enemy.y).limit(speed); vec.sub(enemy.x, enemy.y).limit(speed);
} }
Vector2 shift = Tmp.v3.setZero(); shift.setZero();
float shiftRange = enemy.hitbox.width + 2f; float shiftRange = enemy.hitbox.width + 2f;
float avoidRange = shiftRange + 4f; float avoidRange = shiftRange + 4f;
float attractRange = avoidRange + 7f; float attractRange = avoidRange + 7f;
@@ -182,11 +183,11 @@ public class EnemyType {
float scl = Mathf.clamp(1.4f - dst / shiftRange) * mass * 1f/mass; float scl = Mathf.clamp(1.4f - dst / shiftRange) * mass * 1f/mass;
shift.add((enemy.x - other.x) * scl, (enemy.y - other.y) * scl); shift.add((enemy.x - other.x) * scl, (enemy.y - other.y) * scl);
}else if(dst < avoidRange){ }else if(dst < avoidRange){
Tmp.v2.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed); calc.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed);
shift.add(Tmp.v2.scl(1.1f)); shift.add(calc.scl(1.1f));
}else if(dst < attractRange && !nearCore){ }else if(dst < attractRange && !nearCore){
Tmp.v2.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed); calc.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed);
shift.add(Tmp.v2.scl(-1)); shift.add(calc.scl(-1));
} }
}); });

View File

@@ -1,12 +1,10 @@
package io.anuke.mindustry.entities.enemies.types; package io.anuke.mindustry.entities.enemies.types;
import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.entities.Bullet; import io.anuke.mindustry.entities.Bullet;
import io.anuke.mindustry.entities.BulletType; import io.anuke.mindustry.entities.BulletType;
import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.entities.enemies.Enemy;
import io.anuke.mindustry.entities.enemies.EnemyType; import io.anuke.mindustry.entities.enemies.EnemyType;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.tilesize; import static io.anuke.mindustry.Vars.tilesize;
@@ -26,15 +24,16 @@ public class BlastType extends EnemyType {
public void behavior(Enemy enemy){ public void behavior(Enemy enemy){
float range = 10f; float range = 10f;
Vector2 offset = Tmp.v3.setZero(); float ox = 0, oy = 0;
if(enemy.target instanceof TileEntity){ if(enemy.target instanceof TileEntity){
TileEntity e = (TileEntity)enemy.target; TileEntity e = (TileEntity)enemy.target;
range = (e.tile.block().width * tilesize) /2f + 8f; range = (e.tile.block().width * tilesize) /2f + 8f;
offset.set(e.tile.block().getPlaceOffset()); ox = e.tile.block().getPlaceOffset().x;
oy = e.tile.block().getPlaceOffset().y;
} }
if(enemy.target != null && enemy.target.distanceTo(enemy.x - offset.x, enemy.y - offset.y) < range){ if(enemy.target != null && enemy.target.distanceTo(enemy.x - ox, enemy.y - oy) < range){
explode(enemy); explode(enemy);
} }
} }

View File

@@ -35,13 +35,13 @@ public class FortressType extends EnemyType {
world.getCore().worldy()) <= 90f){ world.getCore().worldy()) <= 90f){
if(Timers.get(this, "spawn", spawnTime) && enemy.spawned < maxSpawn){ if(Timers.get(this, "spawn", spawnTime) && enemy.spawned < maxSpawn){
Angles.translation(enemy.angle, 20f); enemy.tr.trns(enemy.angle, 20f);
Enemy s = new Enemy(EnemyTypes.fast); Enemy s = new Enemy(EnemyTypes.fast);
s.lane = enemy.lane; s.lane = enemy.lane;
s.tier = enemy.tier; s.tier = enemy.tier;
s.spawner = enemy; s.spawner = enemy;
s.set(enemy.x + Angles.x(), enemy.y + Angles.y()); s.set(enemy.x + enemy.tr.x, enemy.y + enemy.tr.y);
s.add(); s.add();
Effects.effect(Fx.spawn, enemy); Effects.effect(Fx.spawn, enemy);
@@ -53,7 +53,7 @@ public class FortressType extends EnemyType {
public void onShoot(Enemy enemy, BulletType type, float rotation){ public void onShoot(Enemy enemy, BulletType type, float rotation){
Effects.effect(Fx.largeCannonShot, enemy.x + Angles.x(), enemy.y + Angles.y(), enemy.angle); Effects.effect(Fx.largeCannonShot, enemy.x + enemy.tr.x, enemy.y + enemy.tr.y, enemy.angle);
Effects.shake(3f, 3f, enemy); Effects.shake(3f, 3f, enemy);
} }

View File

@@ -74,7 +74,7 @@ public class HealerType extends EnemyType {
if(target == null) return; if(target == null) return;
Angles.translation(enemy.angleTo(target), 5f); enemy.tr.trns(enemy.angleTo(target), 5f);
Shaders.outline.color.set(Color.CLEAR); Shaders.outline.color.set(Color.CLEAR);
Shaders.outline.apply(); Shaders.outline.apply();
@@ -82,7 +82,7 @@ public class HealerType extends EnemyType {
if(target.health < target.maxhealth){ if(target.health < target.maxhealth){
Draw.color(Hue.rgb(138, 244, 138, (MathUtils.sin(Timers.time()) + 1f) / 13f)); Draw.color(Hue.rgb(138, 244, 138, (MathUtils.sin(Timers.time()) + 1f) / 13f));
Draw.alpha(0.9f); Draw.alpha(0.9f);
Shapes.laser("laser", "laserend", enemy.x + Angles.x(), enemy.y + Angles.y(), target.x - Angles.x()/1.5f, target.y - Angles.y()/1.5f); Shapes.laser("laser", "laserend", enemy.x + enemy.tr.x, enemy.y + enemy.tr.y, target.x - enemy.tr.x/1.5f, target.y - enemy.tr.y/1.5f);
Draw.color(); Draw.color();
} }

View File

@@ -17,17 +17,14 @@ public class TankType extends EnemyType {
bullet = BulletType.small; bullet = BulletType.small;
length = 3f; length = 3f;
mass = 1.4f; mass = 1.4f;
length = 8f;
} }
@Override @Override
public void shoot(Enemy enemy){ public void shoot(Enemy enemy){
super.shoot(enemy); super.shoot(enemy);
Angles.translation(enemy.angle, 8f); Angles.shotgun(3, 8f, enemy.angle, f -> enemy.shoot(bullet, f));
Angles.shotgun(3, 8f, enemy.angle, f -> {
enemy.shoot(bullet, f);
});
} }
} }

View File

@@ -7,16 +7,16 @@ public enum GameMode{
sandbox{ sandbox{
{ {
infiniteResources = true; infiniteResources = true;
toggleWaves = true; disableWaveTimer = true;
} }
}, },
freebuild{ freebuild{
{ {
toggleWaves = true; disableWaveTimer = true;
} }
}; };
public boolean infiniteResources; public boolean infiniteResources;
public boolean toggleWaves; public boolean disableWaveTimer;
@Override @Override
public String toString(){ public String toString(){

View File

@@ -1,18 +1,19 @@
package io.anuke.mindustry.graphics; package io.anuke.mindustry.graphics;
import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.FloatArray;
import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Core;
import io.anuke.ucore.core.Settings; import io.anuke.ucore.core.Settings;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Shader; import io.anuke.ucore.graphics.Shader;
import io.anuke.ucore.scene.ui.layout.Unit; import io.anuke.ucore.scene.ui.layout.Unit;
import io.anuke.ucore.util.Tmp;
public class Shaders{ public class Shaders{
public static final Outline outline = new Outline(); public static final Outline outline = new Outline();
public static final Shield shield = new Shield(); public static final Shield shield = new Shield();
private static final Vector2 vec = new Vector2();
public static class Outline extends Shader{ public static class Outline extends Shader{
public Color color = new Color(); public Color color = new Color();
@@ -26,7 +27,7 @@ public class Shaders{
public void apply(){ public void apply(){
shader.setUniformf("u_color", color); shader.setUniformf("u_color", color);
shader.setUniformf("u_lighten", lighten); shader.setUniformf("u_lighten", lighten);
shader.setUniformf("u_texsize", Tmp.v1.set(region.getTexture().getWidth(), region.getTexture().getHeight())); shader.setUniformf("u_texsize", vec.set(region.getTexture().getWidth(), region.getTexture().getHeight()));
} }
} }
@@ -52,8 +53,8 @@ public class Shaders{
shader.setUniformf("u_color", color); shader.setUniformf("u_color", color);
shader.setUniformf("u_time", Timers.time() / Unit.dp.scl(1f)); shader.setUniformf("u_time", Timers.time() / Unit.dp.scl(1f));
shader.setUniformf("u_scaling", scaling); shader.setUniformf("u_scaling", scaling);
shader.setUniformf("u_offset", Tmp.v1.set(Core.camera.position.x, Core.camera.position.y)); shader.setUniformf("u_offset", vec.set(Core.camera.position.x, Core.camera.position.y));
shader.setUniformf("u_texsize", Tmp.v1.set(region.getTexture().getWidth() / scale, shader.setUniformf("u_texsize", vec.set(region.getTexture().getWidth() / scale,
region.getTexture().getHeight() / scale)); region.getTexture().getHeight() / scale));
} }

View File

@@ -23,7 +23,6 @@ import io.anuke.ucore.core.Sounds;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.SolidEntity; import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -36,6 +35,8 @@ public abstract class InputHandler extends InputAdapter{
public PlaceMode lastPlaceMode = placeMode; public PlaceMode lastPlaceMode = placeMode;
public PlaceMode lastBreakMode = breakMode; public PlaceMode lastBreakMode = breakMode;
private Rectangle rect = new Rectangle();
public abstract void update(); public abstract void update();
public abstract float getCursorX(); public abstract float getCursorX();
public abstract float getCursorY(); public abstract float getCursorY();
@@ -93,21 +94,21 @@ public abstract class InputHandler extends InputAdapter{
} }
} }
Tmp.r2.setSize(type.width * tilesize, type.height * tilesize); rect.setSize(type.width * tilesize, type.height * tilesize);
Vector2 offset = type.getPlaceOffset(); Vector2 offset = type.getPlaceOffset();
Tmp.r2.setCenter(offset.x + x * tilesize, offset.y + y * tilesize); rect.setCenter(offset.x + x * tilesize, offset.y + y * tilesize);
for(SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)){ for(SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)){
Rectangle rect = e.hitbox.getRect(e.x, e.y); Rectangle rect = e.hitbox.getRect(e.x, e.y);
if(Tmp.r2.overlaps(rect)){ if(this.rect.overlaps(rect)){
return false; return false;
} }
} }
if(type.solid || type.solidifes) { if(type.solid || type.solidifes) {
for (Player player : playerGroup.all()) { for (Player player : playerGroup.all()) {
if (!player.isAndroid && Tmp.r2.overlaps(player.hitbox.getRect(player.x, player.y))) { if (!player.isAndroid && rect.overlaps(player.hitbox.getRect(player.x, player.y))) {
return false; return false;
} }
} }
@@ -138,14 +139,14 @@ public abstract class InputHandler extends InputAdapter{
for(int dx = 0; dx < type.width; dx ++){ for(int dx = 0; dx < type.width; dx ++){
for(int dy = 0; dy < type.height; dy ++){ for(int dy = 0; dy < type.height; dy ++){
Tile other = world.tile(x + dx + offsetx, y + dy + offsety); Tile other = world.tile(x + dx + offsetx, y + dy + offsety);
if(other == null || other.block() != Blocks.air || isSpawnPoint(other)){ if(other == null || (other.block() != Blocks.air && !other.block().alwaysReplace) || isSpawnPoint(other)){
return false; return false;
} }
} }
} }
return true; return true;
}else{ }else{
if(tile.block() != type && type.canReplace(tile.block()) && tile.block().isMultiblock() == type.isMultiblock()){ if(tile.block() != type && (type.canReplace(tile.block()) || tile.block().alwaysReplace) && tile.block().isMultiblock() == type.isMultiblock()){
return true; return true;
} }
return tile.block() == Blocks.air; return tile.block() == Blocks.air;

View File

@@ -12,7 +12,7 @@ import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.graphics.Lines; import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp; import io.anuke.ucore.util.Translator;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -44,8 +44,8 @@ public enum PlaceMode{
if(control.input().recipe.result.rotate){ if(control.input().recipe.result.rotate){
Draw.color(Colors.get("placeRotate")); Draw.color(Colors.get("placeRotate"));
Tmp.v1.set(7, 0).rotate(control.input().rotation * 90); tr.trns(control.input().rotation * 90, 7, 0);
Lines.line(x, y, x + Tmp.v1.x, y + Tmp.v1.y); Lines.line(x, y, x + tr.x, y + tr.y);
} }
} }
@@ -293,8 +293,8 @@ public enum PlaceMode{
if(control.input().recipe.result.rotate){ if(control.input().recipe.result.rotate){
float cx = tx * t, cy = ty * t; float cx = tx * t, cy = ty * t;
Draw.color(Colors.get("placeRotate")); Draw.color(Colors.get("placeRotate"));
Tmp.v1.set(7, 0).rotate(rotation * 90); tr.trns(rotation * 90, 7, 0);
Lines.line(cx, cy, cx + Tmp.v1.x, cy + Tmp.v1.y); Lines.line(cx, cy, cx + tr.x, cy + tr.y);
} }
Draw.reset(); Draw.reset();
} }
@@ -368,6 +368,8 @@ public enum PlaceMode{
public boolean showCancel; public boolean showCancel;
public boolean delete = false; public boolean delete = false;
public boolean both = false; public boolean both = false;
private static final Translator tr = new Translator();
public void draw(int tilex, int tiley, int endx, int endy){ public void draw(int tilex, int tiley, int endx, int endy){

View File

@@ -1,5 +1,6 @@
package io.anuke.mindustry.io; package io.anuke.mindustry.io;
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
import io.anuke.ucore.scene.ui.TextField; import io.anuke.ucore.scene.ui.TextField;
import java.util.Date; import java.util.Date;
@@ -27,4 +28,27 @@ public abstract class Platform {
return true; return true;
} }
public boolean isDebug(){return false;} public boolean isDebug(){return false;}
public ThreadProvider getThreadProvider(){
return new ThreadProvider() {
@Override
public boolean isOnThread() {
return true;
}
@Override
public void sleep(long ms) {
}
@Override
public void start(Runnable run) {
}
@Override
public void stop() {
}
};
}
} }

View File

@@ -9,6 +9,7 @@ import com.badlogic.gdx.input.GestureDetector;
import com.badlogic.gdx.input.GestureDetector.GestureListener; import com.badlogic.gdx.input.GestureDetector.GestureListener;
import com.badlogic.gdx.math.Bresenham2; import com.badlogic.gdx.math.Bresenham2;
import com.badlogic.gdx.math.GridPoint2; import com.badlogic.gdx.math.GridPoint2;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack; import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack;
import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.Array;
@@ -43,6 +44,8 @@ public class MapView extends Element implements GestureListener{
private float zoom = 1f; private float zoom = 1f;
private boolean grid = false; private boolean grid = false;
private GridImage image = new GridImage(0, 0); private GridImage image = new GridImage(0, 0);
private Vector2 vec = new Vector2();
private Rectangle rect = new Rectangle();
private boolean drawing; private boolean drawing;
private int lastx, lasty; private int lastx, lasty;
@@ -214,7 +217,7 @@ public class MapView extends Element implements GestureListener{
float px = ((float)x / editor.texture().getWidth()) * sclwidth + offsetx*zoom - sclwidth/2 + getWidth()/2; float px = ((float)x / editor.texture().getWidth()) * sclwidth + offsetx*zoom - sclwidth/2 + getWidth()/2;
float py = (float)((float)(editor.texture().getHeight() - 1 - y) / editor.texture().getHeight()) * sclheight float py = (float)((float)(editor.texture().getHeight() - 1 - y) / editor.texture().getHeight()) * sclheight
+ offsety*zoom - sclheight/2 + getHeight()/2; + offsety*zoom - sclheight/2 + getHeight()/2;
return Tmp.v1.set(px, py); return vec.set(px, py);
} }
@Override @Override
@@ -229,7 +232,7 @@ public class MapView extends Element implements GestureListener{
image.setImageSize(editor.pixmap().getWidth(), editor.pixmap().getHeight()); image.setImageSize(editor.pixmap().getWidth(), editor.pixmap().getHeight());
batch.flush(); batch.flush();
boolean pop = ScissorStack.pushScissors(Tmp.r1.set(x + width/2 - size/2, y + height/2 - size/2, size, size)); boolean pop = ScissorStack.pushScissors(rect.set(x + width/2 - size/2, y + height/2 - size/2, size, size));
batch.draw(editor.texture(), centerx - sclwidth/2, centery - sclheight/2, sclwidth, sclheight); batch.draw(editor.texture(), centerx - sclwidth/2, centery - sclheight/2, sclwidth, sclheight);

View File

@@ -25,7 +25,7 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Net{ public class Net{
public static final int version = 19; public static final int version = 20;
private static boolean server; private static boolean server;
private static boolean active; private static boolean active;

View File

@@ -508,15 +508,15 @@ public class Packets {
@Override @Override
public void write(ByteBuffer buffer) { public void write(ByteBuffer buffer) {
buffer.putInt(position); buffer.putInt((rotation) | (position << 2));
buffer.put(rotation);
buffer.put(itemid); buffer.put(itemid);
} }
@Override @Override
public void read(ByteBuffer buffer) { public void read(ByteBuffer buffer) {
position = buffer.getInt(); int i = buffer.getInt();
rotation = buffer.get(); rotation = (byte)(i & 0x3);
position = i >> 2;
itemid = buffer.get(); itemid = buffer.get();
} }
} }

View File

@@ -13,6 +13,7 @@ import io.anuke.ucore.core.Timers;
import io.anuke.ucore.entities.Entity; import io.anuke.ucore.entities.Entity;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Translator;
public class Weapon extends Upgrade{ public class Weapon extends Upgrade{
public static final Weapon public static final Weapon
@@ -87,6 +88,8 @@ public class Weapon extends Upgrade{
float length = 3f; float length = 3f;
/**whether to shoot the weapons in different arms one after another, rather an all at once*/ /**whether to shoot the weapons in different arms one after another, rather an all at once*/
boolean roundrobin = false; boolean roundrobin = false;
/**translator for vector calulations*/
Translator tr = new Translator();
private Weapon(String name, float reload, BulletType type){ private Weapon(String name, float reload, BulletType type){
super(name); super(name);
@@ -100,15 +103,15 @@ public class Weapon extends Upgrade{
Timers.reset(p, "reload" + !left, reload/2f); Timers.reset(p, "reload" + !left, reload/2f);
} }
float ang = Angles.mouseAngle(p.x, p.y); float ang = Angles.mouseAngle(p.x, p.y);
Angles.vector.set(3f * Mathf.sign(left), length).rotate(ang - 90); tr.trns(ang - 90, 3f * Mathf.sign(left), length);
shoot(p, p.x + Angles.x(), p.y + Angles.y(), Angles.mouseAngle(p.x + Angles.x(), p.y + Angles.y())); shoot(p, p.x + tr.x, p.y + tr.y, Angles.mouseAngle(p.x + tr.x, p.y + tr.y));
} }
} }
void shootInternal(Player p, float x, float y, float rotation){ void shootInternal(Player p, float x, float y, float rotation){
Angles.shotgun(shots, spacing, rotation, f -> bullet(p, x, y, f + Mathf.range(inaccuracy))); Angles.shotgun(shots, spacing, rotation, f -> bullet(p, x, y, f + Mathf.range(inaccuracy)));
Angles.translation(rotation, 3f); tr.trns(rotation, 3f);
if(effect != null) Effects.effect(effect, x + Angles.x(), y + Angles.y(), rotation); if(effect != null) Effects.effect(effect, x + tr.x, y + tr.y, rotation);
Effects.shake(shake, shake, x, y); Effects.shake(shake, shake, x, y);
Effects.sound(shootsound, x, y); Effects.sound(shootsound, x, y);
} }
@@ -122,7 +125,7 @@ public class Weapon extends Upgrade{
} }
void bullet(Entity owner, float x, float y, float angle){ void bullet(Entity owner, float x, float y, float angle){
Angles.translation(angle, 3f); tr.trns(angle, 3f);
new Bullet(type, owner, x + Angles.x(), y + Angles.y(), angle).add(); new Bullet(type, owner, x + tr.x, y + tr.y, angle).add();
} }
} }

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.ui.dialogs; package io.anuke.mindustry.ui.dialogs;
import com.badlogic.gdx.graphics.g2d.TextureRegion; import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.math.Vector2;
import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.Difficulty;
import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.GameMode;
import io.anuke.mindustry.world.Map; import io.anuke.mindustry.world.Map;
@@ -16,7 +17,6 @@ import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.scene.utils.Elements; import io.anuke.ucore.scene.utils.Elements;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
@@ -133,13 +133,15 @@ public class LevelDialog extends FloatingDialog{
}); });
}).width(images+16).padBottom(-10f).grow().get(); }).width(images+16).padBottom(-10f).grow().get();
} }
Vector2 hit = new Vector2();
image.addListener(new ClickListener(){ image.addListener(new ClickListener(){
public void clicked(InputEvent event, float x, float y){ public void clicked(InputEvent event, float x, float y){
image.localToStageCoordinates(Tmp.v1.set(x, y)); image.localToStageCoordinates(hit.set(x, y));
if(delete[0] != null && (delete[0].getClickListener().isOver() || delete[0].getClickListener().isPressed() if(delete[0] != null && (delete[0].getClickListener().isOver() || delete[0].getClickListener().isPressed()
|| (Core.scene.hit(Tmp.v1.x, Tmp.v1.y, true) != null && || (Core.scene.hit(hit.x, hit.y, true) != null &&
Core.scene.hit(Tmp.v1.x, Tmp.v1.y, true).isDescendantOf(delete[0])))){ Core.scene.hit(hit.x, hit.y, true).isDescendantOf(delete[0])))){
return; return;
} }

View File

@@ -113,6 +113,14 @@ public class SettingsMenuDialog extends SettingsDialog{
game.sliderPref("sensitivity", 100, 10, 300, i -> i + "%"); game.sliderPref("sensitivity", 100, 10, 300, i -> i + "%");
game.sliderPref("saveinterval", 90, 10, 5*120, i -> Bundles.format("setting.seconds", i)); game.sliderPref("saveinterval", 90, 10, 5*120, i -> Bundles.format("setting.seconds", i));
if(!gwt){
graphics.checkPref("multithread", false, threads::setEnabled);
if(Settings.getBool("multithread")){
threads.setEnabled(true);
}
}
if(!android && !gwt) { if(!android && !gwt) {
graphics.checkPref("vsync", true, b -> Gdx.graphics.setVSync(b)); graphics.checkPref("vsync", true, b -> Gdx.graphics.setVSync(b));
graphics.checkPref("fullscreen", false, b -> { graphics.checkPref("fullscreen", false, b -> {
@@ -128,6 +136,7 @@ public class SettingsMenuDialog extends SettingsDialog{
Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode()); Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
} }
} }
graphics.checkPref("fps", false); graphics.checkPref("fps", false);
graphics.checkPref("lasers", true); graphics.checkPref("lasers", true);
graphics.checkPref("indicators", true); graphics.checkPref("indicators", true);

View File

@@ -2,7 +2,6 @@ package io.anuke.mindustry.ui.fragments;
import com.badlogic.gdx.Gdx; import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.files.FileHandle;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.entities.Player;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.scene.builders.button; import io.anuke.ucore.scene.builders.button;
@@ -50,7 +49,7 @@ public class DebugFragment implements Fragment {
row(); row();
new button("infammo", "toggle", () -> infiniteAmmo = !infiniteAmmo); new button("infammo", "toggle", () -> infiniteAmmo = !infiniteAmmo);
row(); row();
new button("wave", () -> logic.runWave()); new button("wave", () -> state.wavetime = 0f);
row(); row();
new button("clear", () -> { new button("clear", () -> {
enemyGroup.clear(); enemyGroup.clear();
@@ -58,9 +57,7 @@ public class DebugFragment implements Fragment {
netClient.clearRecieved(); netClient.clearRecieved();
}); });
row(); row();
new button("spawn", () -> {try{ Net.connect("localhost", Vars.port); }catch (Exception e){e.printStackTrace();}}); new button("spawn", () -> {});
row();
new button("stuff", () -> netClient.test());
row(); row();
}}.end(); }}.end();

View File

@@ -93,7 +93,7 @@ public class HudFragment implements Fragment{
visible(() -> !state.is(State.menu)); visible(() -> !state.is(State.menu));
Label fps = new Label(() -> (Settings.getBool("fps") ? (Gdx.graphics.getFramesPerSecond() + " FPS") + Label fps = new Label(() -> (Settings.getBool("fps") ? (Gdx.graphics.getFramesPerSecond() + " FPS") +
(Net.client() && !gwt ? " / Ping: " + Net.getPing() : "") : "")); (threads.isEnabled() ? " / " + threads.getFPS() + " TPS" : "") + (Net.client() && !gwt ? " / Ping: " + Net.getPing() : "") : ""));
row(); row();
add(fps).size(-1); add(fps).size(-1);
@@ -179,7 +179,7 @@ public class HudFragment implements Fragment{
new label(()-> state.enemies > 0 ? new label(()-> state.enemies > 0 ?
getEnemiesRemaining() : getEnemiesRemaining() :
(control.tutorial().active() || state.mode.toggleWaves) ? "$text.waiting" (control.tutorial().active() || state.mode.disableWaveTimer) ? "$text.waiting"
: Bundles.format("text.wave.waiting", (int) (state.wavetime / 60f))) : Bundles.format("text.wave.waiting", (int) (state.wavetime / 60f)))
.minWidth(126).padLeft(-6).padRight(-12).left(); .minWidth(126).padLeft(-6).padRight(-12).left();
@@ -194,7 +194,7 @@ public class HudFragment implements Fragment{
private void playButton(float uheight){ private void playButton(float uheight){
new imagebutton("icon-play", 30f, () -> { new imagebutton("icon-play", 30f, () -> {
logic.runWave(); state.wavetime = 0f;
}).height(uheight).fillX().right().padTop(-8f).padBottom(-12f).padRight(-36).width(40f).update(l->{ }).height(uheight).fillX().right().padTop(-8f).padBottom(-12f).padRight(-36).width(40f).update(l->{
boolean vis = state.enemies <= 0 && (Net.server() || !Net.active()); boolean vis = state.enemies <= 0 && (Net.server() || !Net.active());
boolean paused = state.is(State.paused) || !vis; boolean paused = state.is(State.paused) || !vis;

View File

@@ -19,7 +19,6 @@ import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.ucore.scene.ui.layout.Table;
import io.anuke.ucore.util.Bundles; import io.anuke.ucore.util.Bundles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Tmp;
import static io.anuke.mindustry.Vars.state; import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.tilesize; import static io.anuke.mindustry.Vars.tilesize;
@@ -30,8 +29,8 @@ public class Block{
private static ObjectMap<String, Block> map = new ObjectMap<>(); private static ObjectMap<String, Block> map = new ObjectMap<>();
protected static TextureRegion temp = new TextureRegion(); protected static TextureRegion temp = new TextureRegion();
protected Vector2 offset = new Vector2();
public Tile[] temptiles = new Tile[4];
/**internal name*/ /**internal name*/
public final String name; public final String name;
/**internal ID*/ /**internal ID*/
@@ -88,6 +87,8 @@ public class Block{
public Layer layer2 = null; public Layer layer2 = null;
/**list of displayed block status bars. Defaults to health bar.*/ /**list of displayed block status bars. Defaults to health bar.*/
public Array<BlockBar> bars = Array.with(new BlockBar(Color.RED, false, tile -> tile.entity.health / (float)tile.block().health)); public Array<BlockBar> bars = Array.with(new BlockBar(Color.RED, false, tile -> tile.entity.health / (float)tile.block().health));
/**whether this block can be replaced in all cases*/
public boolean alwaysReplace = false;
public Block(String name) { public Block(String name) {
this.name = name; this.name = name;
@@ -273,7 +274,7 @@ public class Block{
/**Offset for placing and drawing multiblocks.*/ /**Offset for placing and drawing multiblocks.*/
public Vector2 getPlaceOffset(){ public Vector2 getPlaceOffset(){
return Tmp.v3.set(((width + 1) % 2) * tilesize/2, ((height + 1) % 2) * tilesize/2); return offset.set(((width + 1) % 2) * tilesize/2, ((height + 1) % 2) * tilesize/2);
} }
public boolean isMultiblock(){ public boolean isMultiblock(){

View File

@@ -237,13 +237,13 @@ public class Tile{
if(rotation == 3) return world.tile(x, y - 1); if(rotation == 3) return world.tile(x, y - 1);
return null; return null;
} }
public Tile[] getNearby(){
return world.getNearby(x, y);
}
public Tile[] getNearby(Tile[] copy){ public Tile[] getNearby(Tile[] temptiles){
return world.getNearby(x, y, copy); temptiles[0] = world.tile(x+1, y);
temptiles[1] = world.tile(x, y+1);
temptiles[2] = world.tile(x-1, y);
temptiles[3] = world.tile(x, y-1);
return temptiles;
} }
public void updateOcclusion(){ public void updateOcclusion(){

View File

@@ -45,8 +45,6 @@ public class DistributionBlocks{
}}, }},
liquidjunction = new LiquidJunction("liquidjunction"){{ liquidjunction = new LiquidJunction("liquidjunction"){{
}},
liquiditemjunction = new LiquidItemJunction("liquiditemjunction"){{
}}, }},
powerbooster = new PowerBooster("powerbooster"){{ powerbooster = new PowerBooster("powerbooster"){{
powerRange = 4; powerRange = 4;

View File

@@ -40,12 +40,11 @@ public class WeaponBlocks{
@Override @Override
protected void shoot(Tile tile){ protected void shoot(Tile tile){
TurretEntity entity = tile.entity(); TurretEntity entity = tile.entity();
Angles.vector.set(4, -2).rotate(entity.rotation); for(int i : Mathf.signs){
bullet(tile, entity.rotation); tr.trns(entity.rotation, 4, -2 * i);
bullet(tile, entity.rotation);
Angles.vector.set(4, 2).rotate(entity.rotation); }
bullet(tile, entity.rotation);
} }
}, },
@@ -136,9 +135,10 @@ public class WeaponBlocks{
@Override @Override
public void shoot(Tile tile){ public void shoot(Tile tile){
TurretEntity entity = tile.entity(); TurretEntity entity = tile.entity();
Angles.translation(entity.rotation, 4);
new TeslaOrb(tile.worldx() + Angles.x(), tile.worldy() + Angles.y(), range, 9).add(); float len = 4f;
new TeslaOrb(tile.drawx() + Angles.trnsx(entity.rotation, len), tile.drawy() + Angles.trnsy(entity.rotation, len), range, 9).add();
} }
}, },
@@ -179,10 +179,10 @@ public class WeaponBlocks{
float space = 3.5f; float space = 3.5f;
for(int i = -1; i < 1; i ++){ for(int i = -1; i < 1; i ++){
Angles.vector.set(len, Mathf.sign(i) * space).rotate(entity.rotation); tr.trns(entity.rotation, len, Mathf.sign(i) * space);
bullet(tile, entity.rotation); bullet(tile, entity.rotation);
Effects.effect(shootEffect, tile.drawx() + Angles.x(), Effects.effect(shootEffect, tile.drawx() + tr.x,
tile.drawy()+ Angles.y(), entity.rotation); tile.drawy() + tr.y, entity.rotation);
} }
Effects.shake(1f, 1f, tile.worldx(), tile.worldy()); Effects.shake(1f, 1f, tile.worldx(), tile.worldy());

View File

@@ -20,10 +20,9 @@ public class BlendBlock extends Block{
Draw.rect(variants > 0 ? (name() + Mathf.randomSeed(tile.id(), 1, variants)) : name(), Draw.rect(variants > 0 ? (name() + Mathf.randomSeed(tile.id(), 1, variants)) : name(),
tile.worldx(), tile.worldy()); tile.worldx(), tile.worldy());
Tile[] nearby = tile.getNearby();
for(int i = 0; i < 4; i ++){ for(int i = 0; i < 4; i ++){
if(nearby[i] != null && !blend.test(nearby[i].block())){ Tile near = tile.getNearby(i);
if(near != null && !blend.test(near.block())){
Draw.rect(edge + "-" + i, tile.worldx(), tile.worldy()); Draw.rect(edge + "-" + i, tile.worldx(), tile.worldy());
} }
} }

View File

@@ -5,6 +5,7 @@ import io.anuke.mindustry.entities.TileEntity;
import io.anuke.mindustry.resource.Liquid; import io.anuke.mindustry.resource.Liquid;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
@@ -80,7 +81,7 @@ public class LiquidBlock extends Block implements LiquidAcceptor{
LiquidAcceptor other = (LiquidAcceptor)next.block(); LiquidAcceptor other = (LiquidAcceptor)next.block();
float flow = Math.min(other.getLiquidCapacity(next) - other.getLiquid(next) - 0.001f, float flow = Math.min(other.getLiquidCapacity(next) - other.getLiquid(next) - 0.001f,
Math.min(entity.liquidAmount/flowfactor, entity.liquidAmount)); Math.min(entity.liquidAmount/flowfactor * Timers.delta(), entity.liquidAmount));
if(flow <= 0f || entity.liquidAmount < flow) return; if(flow <= 0f || entity.liquidAmount < flow) return;

View File

@@ -9,5 +9,6 @@ public class Rock extends Block {
shadow = name+"shadow"; shadow = name+"shadow";
breakable = true; breakable = true;
breaktime = 10; breaktime = 10;
alwaysReplace = true;
} }
} }

View File

@@ -11,7 +11,6 @@ import io.anuke.ucore.core.Effects.Effect;
import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.Entities;
import io.anuke.ucore.entities.SolidEntity; import io.anuke.ucore.entities.SolidEntity;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Tmp;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -20,6 +19,8 @@ import java.io.IOException;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class Door extends Wall{ public class Door extends Wall{
protected final Rectangle rect = new Rectangle();
protected Effect openfx = Fx.dooropen; protected Effect openfx = Fx.dooropen;
protected Effect closefx = Fx.doorclose; protected Effect closefx = Fx.doorclose;
@@ -65,13 +66,13 @@ public class Door extends Wall{
boolean anyEntities(Tile tile){ boolean anyEntities(Tile tile){
int x = tile.x, y = tile.y; int x = tile.x, y = tile.y;
Block type = tile.block(); Block type = tile.block();
Tmp.r2.setSize(type.width * tilesize, type.height * tilesize); rect.setSize(type.width * tilesize, type.height * tilesize);
Tmp.r2.setCenter(tile.drawx(), tile.drawy()); rect.setCenter(tile.drawx(), tile.drawy());
for(SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)){ for(SolidEntity e : Entities.getNearby(enemyGroup, x * tilesize, y * tilesize, tilesize * 2f)){
Rectangle rect = e.hitbox.getRect(e.x, e.y); Rectangle rect = e.hitbox.getRect(e.x, e.y);
if(Tmp.r2.overlaps(rect)){ if(rect.overlaps(rect)){
return true; return true;
} }
} }
@@ -79,7 +80,7 @@ public class Door extends Wall{
for(SolidEntity e : Entities.getNearby(playerGroup, x * tilesize, y * tilesize, tilesize * 2f)){ for(SolidEntity e : Entities.getNearby(playerGroup, x * tilesize, y * tilesize, tilesize * 2f)){
Rectangle rect = e.hitbox.getRect(e.x, e.y); Rectangle rect = e.hitbox.getRect(e.x, e.y);
if(Tmp.r2.overlaps(rect)){ if(rect.overlaps(rect)){
return true; return true;
} }
} }

View File

@@ -34,7 +34,7 @@ public class LaserTurret extends PowerTurret{
TurretEntity entity = tile.entity(); TurretEntity entity = tile.entity();
Enemy enemy = entity.target; Enemy enemy = entity.target;
if(Angles.angleDist(entity.rotation, Angles.angle(tile.worldx(), tile.worldy(), enemy.x, enemy.y)) < cone){ if(Angles.angleDist(entity.rotation, Angles.angle(tile.drawx(), tile.drawy(), enemy.x, enemy.y)) < cone){
enemy.damage(damage); enemy.damage(damage);
Effects.effect(hiteffect, enemy.x + Mathf.range(3), enemy.y + Mathf.range(3)); Effects.effect(hiteffect, enemy.x + Mathf.range(3), enemy.y + Mathf.range(3));
} }
@@ -45,10 +45,10 @@ public class LaserTurret extends PowerTurret{
TurretEntity entity = tile.entity(); TurretEntity entity = tile.entity();
if(entity.target != null && if(entity.target != null &&
Angles.angleDist(entity.rotation, Angles.angle(tile.worldx(), tile.worldy(), entity.target.x, entity.target.y)) <= cone){ Angles.angleDist(entity.rotation, Angles.angle(tile.drawx(), tile.drawy(), entity.target.x, entity.target.y)) <= cone){
Angles.translation(entity.rotation, 4f); float len = 4f;
float x = tile.worldx() + Angles.x(), y = tile.worldy() + Angles.y(); float x = tile.drawx() + Angles.trnsx(entity.rotation, len), y = tile.drawy() + Angles.trnsy(entity.rotation, len);
float x2 = entity.target.x, y2 = entity.target.y; float x2 = entity.target.x, y2 = entity.target.y;
float lighten = (MathUtils.sin(Timers.time()/1.2f) + 1f) / 10f; float lighten = (MathUtils.sin(Timers.time()/1.2f) + 1f) / 10f;

View File

@@ -73,7 +73,7 @@ public class RepairTurret extends PowerTurret{
@Override @Override
public void drawSelect(Tile tile){ public void drawSelect(Tile tile){
Draw.color(Color.GREEN); Draw.color(Color.GREEN);
Lines.dashCircle(tile.worldx(), tile.worldy(), range); Lines.dashCircle(tile.drawx(), tile.drawy(), range);
Draw.reset(); Draw.reset();
} }
@@ -83,8 +83,9 @@ public class RepairTurret extends PowerTurret{
if(entity.power >= powerUsed && entity.blockTarget != null && Angles.angleDist(entity.angleTo(entity.blockTarget), entity.rotation) < 10){ if(entity.power >= powerUsed && entity.blockTarget != null && Angles.angleDist(entity.angleTo(entity.blockTarget), entity.rotation) < 10){
Tile targetTile = entity.blockTarget.tile; Tile targetTile = entity.blockTarget.tile;
Angles.translation(entity.rotation, 4f); float len = 4f;
float x = tile.drawx() + Angles.x(), y = tile.drawy() + Angles.y();
float x = tile.drawx() + Angles.trnsx(entity.rotation, len), y = tile.drawy() + Angles.trnsy(entity.rotation, len);
float x2 = targetTile.drawx(), y2 = targetTile.drawy(); float x2 = targetTile.drawx(), y2 = targetTile.drawy();
Draw.color(Hue.rgb(138, 244, 138, (MathUtils.sin(Timers.time()) + 1f) / 14f)); Draw.color(Hue.rgb(138, 244, 138, (MathUtils.sin(Timers.time()) + 1f) / 14f));

View File

@@ -21,6 +21,7 @@ import io.anuke.ucore.graphics.Lines;
import io.anuke.ucore.util.Angles; import io.anuke.ucore.util.Angles;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
import io.anuke.ucore.util.Translator;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -51,6 +52,7 @@ public class Turret extends Block{
protected Effect shootEffect = null; protected Effect shootEffect = null;
protected float shootShake = 0f; protected float shootShake = 0f;
protected int soundReload = 0; protected int soundReload = 0;
protected Translator tr = new Translator();
public Turret(String name) { public Turret(String name) {
super(name); super(name);
@@ -210,14 +212,14 @@ public class Turret extends Block{
protected void shoot(Tile tile){ protected void shoot(Tile tile){
TurretEntity entity = tile.entity(); TurretEntity entity = tile.entity();
Angles.translation(entity.rotation, width * tilesize / 2f); tr.trns(entity.rotation, width * tilesize/2);
for(int i = 0; i < shots; i ++){ for(int i = 0; i < shots; i ++){
if(Mathf.zero(shotDelayScale)){ if(Mathf.zero(shotDelayScale)){
bullet(tile, entity.rotation + Mathf.range(inaccuracy)); bullet(tile, entity.rotation + Mathf.range(inaccuracy));
}else{ }else{
Timers.run(i * shotDelayScale, ()->{ Timers.run(i * shotDelayScale, () -> {
Angles.translation(entity.rotation, width * tilesize / 2f); tr.trns(entity.rotation, width * tilesize/2f);
bullet(tile, entity.rotation + Mathf.range(inaccuracy)); bullet(tile, entity.rotation + Mathf.range(inaccuracy));
}); });
} }
@@ -225,8 +227,8 @@ public class Turret extends Block{
} }
if(shootEffect != null){ if(shootEffect != null){
Effects.effect(shootEffect, tile.drawx() + Angles.x(), Effects.effect(shootEffect, tile.drawx() + tr.x,
tile.drawy()+ Angles.y(), entity.rotation); tile.drawy() + tr.y, entity.rotation);
} }
if(shootShake > 0){ if(shootShake > 0){
@@ -235,7 +237,7 @@ public class Turret extends Block{
} }
protected void bullet(Tile tile, float angle){ protected void bullet(Tile tile, float angle){
Bullet out = new Bullet(bullet, tile.entity, tile.drawx() + Angles.x(), tile.drawy() + Angles.y(), angle).add(); new Bullet(bullet, tile.entity, tile.drawx() + tr.x, tile.drawy() + tr.y, angle).add();
} }
public static class TurretEntity extends TileEntity{ public static class TurretEntity extends TileEntity{

View File

@@ -9,10 +9,7 @@ import io.anuke.mindustry.world.Layer;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.ucore.core.Timers; import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Bits; import io.anuke.ucore.util.*;
import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings;
import io.anuke.ucore.util.Tmp;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -24,39 +21,43 @@ import java.util.List;
import static io.anuke.mindustry.Vars.tilesize; import static io.anuke.mindustry.Vars.tilesize;
public class Conveyor extends Block{ public class Conveyor extends Block{
private static ItemPos drawpos = new ItemPos();
private static ItemPos pos1 = new ItemPos(); private static ItemPos pos1 = new ItemPos();
private static ItemPos pos2 = new ItemPos(); private static ItemPos pos2 = new ItemPos();
private static LongArray removals = new LongArray();
private static final float itemSpace = 0.135f; private static final float itemSpace = 0.135f;
private static final float offsetScl = 128f*3f; private static final float offsetScl = 128f*3f;
private static final float itemSize = 4f; private static final float itemSize = 4f;
private static final float minmove = 1f / (Short.MAX_VALUE - 2);
private final Translator tr1 = new Translator();
private final Translator tr2 = new Translator();
public float speed = 0.02f; public float speed = 0.02f;
protected Conveyor(String name) { protected Conveyor(String name) {
super(name); super(name);
rotate = true; rotate = true;
update = true; update = true;
layer = Layer.overlay; layer = Layer.overlay;
} }
@Override @Override
public void getStats(Array<String> list){ public void getStats(Array<String> list){
super.getStats(list); super.getStats(list);
list.add("[iteminfo]Item Speed/second: " + Strings.toFixed(speed * 60, 1)); list.add("[iteminfo]Item Speed/second: " + Strings.toFixed(speed * 60, 1));
} }
@Override @Override
public boolean canReplace(Block other){ public boolean canReplace(Block other){
return other instanceof Conveyor || other instanceof Router || other instanceof Junction; return other instanceof Conveyor || other instanceof Router || other instanceof Junction;
} }
@Override @Override
public void draw(Tile tile){ public void draw(Tile tile){
byte rotation = tile.getRotation(); byte rotation = tile.getRotation();
Draw.rect(name() + Draw.rect(name() +
(Timers.time() % ((20 / 100f) / speed) < (10 / 100f) / speed && acceptItem(Item.stone, tile, null) ? "" : "move"), (Timers.time() % ((20 / 100f) / speed) < (10 / 100f) / speed && acceptItem(Item.stone, tile, null) ? "" : "move"),
tile.worldx(), tile.worldy(), rotation * 90); tile.worldx(), tile.worldy(), rotation * 90);
} }
@@ -64,78 +65,71 @@ public class Conveyor extends Block{
public boolean isLayer(Tile tile){ public boolean isLayer(Tile tile){
return tile.<ConveyorEntity>entity().convey.size > 0; return tile.<ConveyorEntity>entity().convey.size > 0;
} }
@Override @Override
public void drawLayer(Tile tile){ public void drawLayer(Tile tile){
ConveyorEntity entity = tile.entity(); ConveyorEntity entity = tile.entity();
byte rotation = tile.getRotation(); byte rotation = tile.getRotation();
for(int i = 0; i < entity.convey.size; i ++){ for(int i = 0; i < entity.convey.size; i ++){
ItemPos pos = pos1.set(entity.convey.get(i)); ItemPos pos = drawpos.set(entity.convey.get(i), ItemPos.drawShorts);
if(pos.item == null) continue; if(pos.item == null) continue;
Tmp.v1.set(tilesize, 0).rotate(rotation * 90); tr1.trns(rotation * 90, tilesize, 0);
Tmp.v2.set(-tilesize / 2, pos.x*tilesize/2).rotate(rotation * 90); tr2.trns(rotation * 90, -tilesize / 2, pos.x*tilesize/2);
Draw.rect(pos.item.region, Draw.rect(pos.item.region,
tile.x * tilesize + Tmp.v1.x * pos.y + Tmp.v2.x, tile.x * tilesize + tr1.x * pos.y + tr2.x,
tile.y * tilesize + Tmp.v1.y * pos.y + Tmp.v2.y, itemSize, itemSize); tile.y * tilesize + tr1.y * pos.y + tr2.y, itemSize, itemSize);
} }
} }
@Override @Override
public void update(Tile tile){ public void update(Tile tile){
ConveyorEntity entity = tile.entity(); ConveyorEntity entity = tile.entity();
entity.minitem = 1f; entity.minitem = 1f;
removals.clear();
float shift = entity.elapsed * speed; int minremove = Integer.MAX_VALUE;
for(int i = 0; i < entity.convey.size; i ++){ for(int i = 0; i < entity.convey.size; i ++){
long value = entity.convey.get(i); long value = entity.convey.get(i);
ItemPos pos = pos1.set(value); ItemPos pos = pos1.set(value, ItemPos.updateShorts);
pos.y += shift;
//..this should never happen, but in case it does, remove it and stop here
if(pos.item == null){ if(pos.item == null){
removals.add(value); entity.convey.removeValue(value);
continue; break;
} }
boolean canmove = i == entity.convey.size - 1 ||
!(pos2.set(entity.convey.get(i + 1)).y - pos.y < itemSpace * Math.max(Timers.delta(), 1f));
float minmove = 1f / (Short.MAX_VALUE - 2); float nextpos = (i == entity.convey.size - 1 ? 100f : pos2.set(entity.convey.get(i + 1), ItemPos.updateShorts).y) - itemSpace;
float maxmove = Math.min(nextpos - pos.y, speed * Timers.delta());
if(canmove){ if(maxmove > minmove){
pos.y += Math.max(speed * Timers.delta(), minmove); //TODO fix precision issues when at high FPS? pos.y += maxmove;
pos.x = Mathf.lerpDelta(pos.x, 0, 0.06f); pos.x = Mathf.lerpDelta(pos.x, 0, 0.06f);
}else{ }else{
pos.x = Mathf.lerpDelta(pos.x, pos.seed/offsetScl, 0.1f); pos.x = Mathf.lerpDelta(pos.x, pos.seed/offsetScl, 0.1f);
} }
pos.y = Mathf.clamp(pos.y); pos.y = Mathf.clamp(pos.y);
if(pos.y >= 0.9999f && offloadDir(tile, pos.item)){ if(pos.y >= 0.9999f && offloadDir(tile, pos.item)){
removals.add(value); minremove = Math.min(i, minremove);
}else{ }else{
value = pos.pack(); value = pos.pack();
if(pos.y < entity.minitem) if(pos.y < entity.minitem)
entity.minitem = pos.y; entity.minitem = pos.y;
entity.convey.set(i, value); entity.convey.set(i, value);
} }
} }
entity.elapsed = 0f; if(minremove != Integer.MAX_VALUE) entity.convey.truncate(minremove);
entity.convey.removeAll(removals);
} }
@Override @Override
public TileEntity getEntity(){ public TileEntity getEntity(){
return new ConveyorEntity(); return new ConveyorEntity();
@@ -145,25 +139,24 @@ public class Conveyor extends Block{
public boolean acceptItem(Item item, Tile tile, Tile source){ public boolean acceptItem(Item item, Tile tile, Tile source){
int direction = source == null ? 0 : Math.abs(source.relativeTo(tile.x, tile.y) - tile.getRotation()); int direction = source == null ? 0 : Math.abs(source.relativeTo(tile.x, tile.y) - tile.getRotation());
float minitem = tile.<ConveyorEntity>entity().minitem; float minitem = tile.<ConveyorEntity>entity().minitem;
return (((direction == 0) && minitem > 0.05f) || return (((direction == 0) && minitem > 0.05f) ||
((direction %2 == 1) && minitem > 0.52f)) && (source == null || !(source.block().rotate && (source.getRotation() + 2) % 4 == tile.getRotation())); ((direction %2 == 1) && minitem > 0.52f)) && (source == null || !(source.block().rotate && (source.getRotation() + 2) % 4 == tile.getRotation()));
} }
@Override @Override
public void handleItem(Item item, Tile tile, Tile source){ public void handleItem(Item item, Tile tile, Tile source){
byte rotation = tile.getRotation(); byte rotation = tile.getRotation();
int ch = Math.abs(source.relativeTo(tile.x, tile.y) - rotation); int ch = Math.abs(source.relativeTo(tile.x, tile.y) - rotation);
int ang = ((source.relativeTo(tile.x, tile.y) - rotation)); int ang = ((source.relativeTo(tile.x, tile.y) - rotation));
float pos = ch == 0 ? 0 : ch % 2 == 1 ? 0.5f : 1f; float pos = ch == 0 ? 0 : ch % 2 == 1 ? 0.5f : 1f;
float y = (ang == -1 || ang == 3) ? 1 : (ang == 1 || ang == -3) ? -1 : 0; float y = (ang == -1 || ang == 3) ? 1 : (ang == 1 || ang == -3) ? -1 : 0;
ConveyorEntity entity = tile.entity(); ConveyorEntity entity = tile.entity();
long result = ItemPos.packItem(item, y*0.9f, pos, (byte)Mathf.random(255)); long result = ItemPos.packItem(item, y*0.9f, pos, (byte)Mathf.random(255));
boolean inserted = false; boolean inserted = false;
for(int i = 0; i < entity.convey.size; i ++){ for(int i = 0; i < entity.convey.size; i ++){
if(compareItems(result, entity.convey.get(i)) < 0){ if(compareItems(result, entity.convey.get(i)) < 0){
entity.convey.insert(i, result); entity.convey.insert(i, result);
@@ -171,13 +164,13 @@ public class Conveyor extends Block{
break; break;
} }
} }
//this item must be greater than anything there... //this item must be greater than anything there...
if(!inserted){ if(!inserted){
entity.convey.add(result); entity.convey.add(result);
} }
} }
/** /**
* Conveyor data format: * Conveyor data format:
* [0] item ordinal * [0] item ordinal
@@ -189,80 +182,78 @@ public class Conveyor extends Block{
public static class ConveyorEntity extends TileEntity{ public static class ConveyorEntity extends TileEntity{
LongArray convey = new LongArray(); LongArray convey = new LongArray();
float minitem = 1, elapsed; float minitem = 1;
@Override @Override
public void write(DataOutputStream stream) throws IOException{ public void write(DataOutputStream stream) throws IOException{
stream.writeInt(convey.size); stream.writeInt(convey.size);
for(int i = 0; i < convey.size; i ++){ for(int i = 0; i < convey.size; i ++){
stream.writeInt(ItemPos.toInt(convey.get(i))); stream.writeInt(ItemPos.toInt(convey.get(i)));
} }
} }
@Override @Override
public void read(DataInputStream stream) throws IOException{ public void read(DataInputStream stream) throws IOException{
convey.clear(); convey.clear();
int amount = stream.readInt(); int amount = stream.readInt();
convey.ensureCapacity(amount); convey.ensureCapacity(amount);
for(int i = 0; i < amount; i ++){ for(int i = 0; i < amount; i ++){
convey.add(ItemPos.toLong(stream.readInt())); convey.add(ItemPos.toLong(stream.readInt()));
} }
sort(convey.items, convey.size); sort(convey.items, convey.size);
} }
@Override
public void readNetwork(DataInputStream stream, float elapsed) throws IOException{
read(stream);
this.elapsed = elapsed;
}
} }
private static void sort(long[] elements, int length){ private static void sort(long[] elements, int length){
List<Long> wrapper = new AbstractList<Long>() { List<Long> wrapper = new AbstractList<Long>() {
@Override @Override
public Long get(int index) { public Long get(int index) {
return elements[index]; return elements[index];
} }
@Override @Override
public int size() { public int size() {
return length; return length;
} }
@Override @Override
public Long set(int index, Long element) { public Long set(int index, Long element) {
long v = elements[index]; long v = elements[index];
elements[index] = element; elements[index] = element;
return v; return v;
} }
}; };
Collections.sort(wrapper, Conveyor::compareItems); Collections.sort(wrapper, Conveyor::compareItems);
} }
private static int compareItems(Long a, Long b){ private static int compareItems(Long a, Long b){
pos1.set(a); pos1.set(a, ItemPos.packShorts);
pos2.set(b); pos2.set(b, ItemPos.packShorts);
return Float.compare(pos1.y, pos2.y); return Float.compare(pos1.y, pos2.y);
} }
//Container class. Do not instantiate. //Container class. Do not instantiate.
static class ItemPos{ static class ItemPos{
private static short[] writeShort = new short[4]; private static short[] writeShort = new short[4];
private static byte[] writeByte = new byte[4]; private static byte[] writeByte = new byte[4];
private static short[] packShorts = new short[4];
private static short[] drawShorts = new short[4];
private static short[] updateShorts = new short[4];
Item item; Item item;
float x, y; float x, y;
byte seed; byte seed;
private ItemPos(){} private ItemPos(){}
ItemPos set(long lvalue){ ItemPos set(long lvalue, short[] values){
short[] values = Bits.getShorts(lvalue); Bits.getShorts(lvalue, values);
if(values[0] >= Item.getAllItems().size || values[0] < 0) if(values[0] >= Item.getAllItems().size || values[0] < 0)
item = null; item = null;
@@ -274,13 +265,13 @@ public class Conveyor extends Block{
seed = (byte)values[3]; seed = (byte)values[3];
return this; return this;
} }
long pack(){ long pack(){
return packItem(item, x, y, seed); return packItem(item, x, y, seed);
} }
static long packItem(Item item, float x, float y, byte seed){ static long packItem(Item item, float x, float y, byte seed){
short[] shorts = Bits.getShorts(); short[] shorts = packShorts;
shorts[0] = (short)item.id; shorts[0] = (short)item.id;
shorts[1] = (short)(x*Short.MAX_VALUE); shorts[1] = (short)(x*Short.MAX_VALUE);
shorts[2] = (short)((y - 1f)*Short.MAX_VALUE); shorts[2] = (short)((y - 1f)*Short.MAX_VALUE);
@@ -321,4 +312,4 @@ public class Conveyor extends Block{
return Bits.packLong(shorts); return Bits.packLong(shorts);
} }
} }
} }

View File

@@ -22,18 +22,6 @@ public class Junction extends Block{
public boolean canReplace(Block other){ public boolean canReplace(Block other){
return other instanceof Conveyor || other instanceof Router; return other instanceof Conveyor || other instanceof Router;
} }
@Override
public void handleItem(Item item, Tile tile, Tile source){
JunctionEntity entity = tile.entity();
boolean x = tile.x == source.x;
long value = Bits.packLong(NumberUtils.floatToIntBits(Timers.time()), Bits.packInt((short)item.id, source.relativeTo(tile.x, tile.y)));
if(x){
entity.bx.add(value);
}else {
entity.by.add(value);
}
}
@Override @Override
public void update(Tile tile){ public void update(Tile tile){
@@ -64,6 +52,18 @@ public class Junction extends Block{
} }
} }
@Override
public void handleItem(Item item, Tile tile, Tile source){
JunctionEntity entity = tile.entity();
boolean x = tile.x == source.x;
long value = Bits.packLong(NumberUtils.floatToIntBits(Timers.time()), Bits.packInt((short)item.id, source.relativeTo(tile.x, tile.y)));
if(x){
entity.bx.add(value);
}else {
entity.by.add(value);
}
}
@Override @Override
public boolean acceptItem(Item item, Tile tile, Tile source){ public boolean acceptItem(Item item, Tile tile, Tile source){
JunctionEntity entity = tile.entity(); JunctionEntity entity = tile.entity();
@@ -91,6 +91,7 @@ public class Junction extends Block{
int index; int index;
void add(long id){ void add(long id){
if(full()) return;
items[index++] = id; items[index++] = id;
} }

View File

@@ -1,64 +0,0 @@
package io.anuke.mindustry.world.blocks.types.distribution;
import io.anuke.mindustry.resource.Item;
import io.anuke.mindustry.resource.Liquid;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.types.LiquidBlock;
import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.core.Timers;
public class LiquidItemJunction extends LiquidBlock{
public LiquidItemJunction(String name) {
super(name);
rotate = true;
}
@Override
public void draw(Tile tile){
Draw.rect(name(), tile.worldx(), tile.worldy(), tile.getRotation() * 90);
}
@Override
public void handleLiquid(Tile tile, Tile source, Liquid liquid, float amount){
int dir = source.relativeTo(tile.x, tile.y);
dir = (dir+4)%4;
Tile to = tile.getNearby()[dir];
((LiquidBlock)to.block()).handleLiquid(to, tile, liquid, amount);
}
@Override
public boolean acceptLiquid(Tile dest, Tile source, Liquid liquid, float amount){
int dir = source.relativeTo(dest.x, dest.y);
dir = (dir+4)%4;
if(dir+dest.getRotation() % 2 == 1) return false;
Tile to = dest.getNearby()[dir];
return to != null && to.block() != this && to.block() instanceof LiquidBlock &&
((LiquidBlock)to.block()).acceptLiquid(to, dest, liquid, amount);
}
@Override
public void handleItem(Item item, Tile tile, Tile source){
int dir = source.relativeTo(tile.x, tile.y);
Tile to = tile.getNearby()[dir];
Timers.run(15, ()->{
if(to == null || to.entity == null) return;
to.block().handleItem(item, to, tile);
});
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
int dir = source.relativeTo(tile.x, tile.y);
if((dir+ tile.getRotation()) % 2 == 0) return false;
Tile to = tile.getNearby()[dir];
return to != null && to.block().acceptItem(item, to, tile);
}
}

View File

@@ -23,7 +23,7 @@ public class LiquidJunction extends Conduit{
public void handleLiquid(Tile tile, Tile source, Liquid liquid, float amount){ public void handleLiquid(Tile tile, Tile source, Liquid liquid, float amount){
int dir = source.relativeTo(tile.x, tile.y); int dir = source.relativeTo(tile.x, tile.y);
dir = (dir+4)%4; dir = (dir+4)%4;
Tile to = tile.getNearby()[dir]; Tile to = tile.getNearby(dir);
((LiquidBlock)to.block()).handleLiquid(to, tile, liquid, amount); ((LiquidBlock)to.block()).handleLiquid(to, tile, liquid, amount);
@@ -33,7 +33,7 @@ public class LiquidJunction extends Conduit{
public boolean acceptLiquid(Tile dest, Tile source, Liquid liquid, float amount){ public boolean acceptLiquid(Tile dest, Tile source, Liquid liquid, float amount){
int dir = source.relativeTo(dest.x, dest.y); int dir = source.relativeTo(dest.x, dest.y);
dir = (dir+4)%4; dir = (dir+4)%4;
Tile to = dest.getNearby()[dir]; Tile to = dest.getNearby(dir);
return to != null && to.block() != this && to.block() instanceof LiquidBlock && return to != null && to.block() != this && to.block() instanceof LiquidBlock &&
((LiquidBlock)to.block()).acceptLiquid(to, dest, liquid, amount); ((LiquidBlock)to.block()).acceptLiquid(to, dest, liquid, amount);
} }

View File

@@ -19,7 +19,7 @@ public class LiquidRouter extends Conduit{
if(entity.liquidAmount > 0){ if(entity.liquidAmount > 0){
if(tile.getExtra() != tile.getRotation()){ if(tile.getExtra() != tile.getRotation()){
tryMoveLiquid(tile, tile.getNearby()[tile.getRotation()]); tryMoveLiquid(tile, tile.getNearby(tile.getRotation()));
} }
tile.setRotation((byte)((tile.getRotation() + 1) % 4)); tile.setRotation((byte)((tile.getRotation() + 1) % 4));

View File

@@ -66,10 +66,10 @@ public class Sorter extends Block{
Tile to; Tile to;
if(item == entity.sortItem){ if(item == entity.sortItem){
to = dest.getNearby()[dir]; to = dest.getNearby(dir);
}else{ }else{
Tile a = dest.getNearby()[Mathf.mod(dir - 1, 4)]; Tile a = dest.getNearby(Mathf.mod(dir - 1, 4));
Tile b = dest.getNearby()[Mathf.mod(dir + 1, 4)]; Tile b = dest.getNearby(Mathf.mod(dir + 1, 4));
boolean ac = a.block().acceptItem(item, a, dest); boolean ac = a.block().acceptItem(item, a, dest);
boolean bc = b.block().acceptItem(item, b, dest); boolean bc = b.block().acceptItem(item, b, dest);

View File

@@ -31,8 +31,8 @@ public class Splitter extends Block{
if(dir == -1) return null; if(dir == -1) return null;
Tile to; Tile to;
Tile a = dest.getNearby()[Mathf.mod(dir - 1, 4)]; Tile a = dest.getNearby(Mathf.mod(dir - 1, 4));
Tile b = dest.getNearby()[Mathf.mod(dir + 1, 4)]; Tile b = dest.getNearby(Mathf.mod(dir + 1, 4));
boolean ac = a.block().acceptItem(item, a, dest); boolean ac = a.block().acceptItem(item, a, dest);
boolean bc = b.block().acceptItem(item, b, dest); boolean bc = b.block().acceptItem(item, b, dest);

View File

@@ -35,7 +35,7 @@ public class TunnelConveyor extends Block{
Tile to = tunnel.getNearby(tunnel.getRotation()); Tile to = tunnel.getNearby(tunnel.getRotation());
if(to == null) return; if(to == null) return;
entity.buffer[entity.index ++] = item.id; entity.buffer[entity.index ++] = Bits.packLong(NumberUtils.floatToIntBits(Timers.time()), item.id);
} }
@Override @Override
@@ -48,9 +48,7 @@ public class TunnelConveyor extends Block{
if(Timers.time() >= time + speed){ if(Timers.time() >= time + speed){
int val = Bits.getRightInt(l); Item item = Item.getByID(Bits.getRightInt(l));
Item item = Item.getByID(Bits.getLeftShort(val));
Tile tunnel = getDestTunnel(tile, item); Tile tunnel = getDestTunnel(tile, item);
if(tunnel == null) return; if(tunnel == null) return;

View File

@@ -25,6 +25,9 @@ public class Generator extends PowerBlock{
public static final int powerTime = 2; public static final int powerTime = 2;
public static boolean drawRangeOverlay = false; public static boolean drawRangeOverlay = false;
protected Translator t1 = new Translator();
protected Translator t2 = new Translator();
public int laserRange = 6; public int laserRange = 6;
public int laserDirections = 4; public int laserDirections = 4;
public float powerSpeed = 0.5f; public float powerSpeed = 0.5f;
@@ -177,10 +180,10 @@ public class Generator extends PowerBlock{
if(target != null){ if(target != null){
boolean interfering = isInterfering(target, rotation); boolean interfering = isInterfering(target, rotation);
Tmp.v1.set(Angles.translation(rotation * 90, target.block().width * tilesize / 2 + 2f + t1.trns(rotation * 90, target.block().width * tilesize / 2 + 2f +
(interfering ? Vector2.dst(tile.worldx(), tile.worldy(), target.worldx(), target.worldy()) / 2f - tilesize / 2f * target.block().width + 1 : 0))); (interfering ? Vector2.dst(tile.worldx(), tile.worldy(), target.worldx(), target.worldy()) / 2f - tilesize / 2f * target.block().width + 1 : 0));
Angles.translation(rotation * 90, width * tilesize / 2 + 2f); t2.trns(rotation * 90, width * tilesize / 2 + 2f);
if(!interfering){ if(!interfering){
Draw.tint(Hue.mix(Color.GRAY, Color.WHITE, 0.904f + Mathf.sin(Timers.time(), 1.7f, 0.06f))); Draw.tint(Hue.mix(Color.GRAY, Color.WHITE, 0.904f + Mathf.sin(Timers.time(), 1.7f, 0.06f)));
@@ -188,7 +191,7 @@ public class Generator extends PowerBlock{
Draw.tint(Hue.mix(Color.SCARLET, Color.WHITE, 0.902f + Mathf.sin(Timers.time(), 1.7f, 0.08f))); Draw.tint(Hue.mix(Color.SCARLET, Color.WHITE, 0.902f + Mathf.sin(Timers.time(), 1.7f, 0.08f)));
if(state.is(State.playing) && Mathf.chance(Timers.delta() * 0.033)){ if(state.is(State.playing) && Mathf.chance(Timers.delta() * 0.033)){
Effects.effect(Fx.laserspark, target.worldx() - Tmp.v1.x, target.worldy() - Tmp.v1.y); Effects.effect(Fx.laserspark, target.worldx() - t1.x, target.worldy() - t1.y);
} }
} }
@@ -197,9 +200,9 @@ public class Generator extends PowerBlock{
int relative = tile.relativeTo(target.x, target.y); int relative = tile.relativeTo(target.x, target.y);
if(relative == -1){ if(relative == -1){
Shapes.laser("laser", "laserend", tile.worldx() + Angles.x(), tile.worldy() + Angles.y(), Shapes.laser("laser", "laserend", tile.worldx() + t2.x, tile.worldy() + t2.y,
target.worldx() - Tmp.v1.x + Mathf.range(r), target.worldx() - t1.x + Mathf.range(r),
target.worldy() - Tmp.v1.y + Mathf.range(r), 0.7f); target.worldy() - t1.y + Mathf.range(r), 0.7f);
}else{ }else{
Draw.rect("laserfull", Draw.rect("laserfull",
tile.worldx() + Geometry.d4[relative].x * width * tilesize / 2f, tile.worldx() + Geometry.d4[relative].x * width * tilesize / 2f,

View File

@@ -14,7 +14,7 @@ import io.anuke.ucore.core.Timers;
import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Draw;
import io.anuke.ucore.util.Mathf; import io.anuke.ucore.util.Mathf;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
import io.anuke.ucore.util.Tmp; import io.anuke.ucore.util.Translator;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.DataOutputStream; import java.io.DataOutputStream;
@@ -25,6 +25,8 @@ import static io.anuke.mindustry.Vars.tilesize;
public class NuclearReactor extends LiquidPowerGenerator{ public class NuclearReactor extends LiquidPowerGenerator{
protected final int timerFuel = timers++; protected final int timerFuel = timers++;
protected final Translator tr = new Translator();
protected Item generateItem; protected Item generateItem;
protected int itemCapacity = 30; protected int itemCapacity = 30;
protected Color coolColor = new Color(1, 1, 1, 0f); protected Color coolColor = new Color(1, 1, 1, 0f);
@@ -137,15 +139,15 @@ public class NuclearReactor extends LiquidPowerGenerator{
for(int i = 0; i < 20; i ++){ for(int i = 0; i < 20; i ++){
Timers.run(Mathf.random(50), ()->{ Timers.run(Mathf.random(50), ()->{
Tmp.v1.setToRandomDirection().setLength(Mathf.random(40f)); tr.rnd(Mathf.random(40f));
Effects.effect(Fx.explosion, Tmp.v1.x + tile.worldx(), Tmp.v1.y + tile.worldy()); Effects.effect(Fx.explosion, tr.x + tile.worldx(), tr.y + tile.worldy());
}); });
} }
for(int i = 0; i < 70; i ++){ for(int i = 0; i < 70; i ++){
Timers.run(Mathf.random(80), ()->{ Timers.run(Mathf.random(80), ()->{
Tmp.v1.setToRandomDirection().setLength(Mathf.random(120f)); tr.rnd(Mathf.random(120f));
Effects.effect(Fx.nuclearsmoke, Tmp.v1.x + tile.worldx(), Tmp.v1.y + tile.worldy()); Effects.effect(Fx.nuclearsmoke, tr.x + tile.worldx(), tr.y + tile.worldy());
}); });
} }
} }

View File

@@ -3,7 +3,9 @@ package io.anuke.mindustry.desktop;
import club.minnced.discord.rpc.DiscordEventHandlers; import club.minnced.discord.rpc.DiscordEventHandlers;
import club.minnced.discord.rpc.DiscordRPC; import club.minnced.discord.rpc.DiscordRPC;
import club.minnced.discord.rpc.DiscordRichPresence; import club.minnced.discord.rpc.DiscordRichPresence;
import io.anuke.kryonet.DefaultThreadImpl;
import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
import io.anuke.mindustry.io.Platform; import io.anuke.mindustry.io.Platform;
import io.anuke.mindustry.net.Net; import io.anuke.mindustry.net.Net;
import io.anuke.ucore.util.Strings; import io.anuke.ucore.util.Strings;
@@ -85,4 +87,9 @@ public class DesktopPlatform extends Platform {
public boolean isDebug() { public boolean isDebug() {
return args.length > 0 && args[0].equalsIgnoreCase("-debug"); return args.length > 0 && args[0].equalsIgnoreCase("-debug");
} }
@Override
public ThreadProvider getThreadProvider() {
return new DefaultThreadImpl();
}
} }

View File

@@ -0,0 +1,40 @@
package io.anuke.kryonet;
import io.anuke.mindustry.core.ThreadHandler.ThreadProvider;
import io.anuke.ucore.util.Log;
public class DefaultThreadImpl implements ThreadProvider {
private Thread thread;
@Override
public boolean isOnThread() {
return Thread.currentThread() == thread;
}
@Override
public void sleep(long ms) throws InterruptedException{
Thread.sleep(ms);
}
@Override
public void start(Runnable run) {
if(thread != null){
thread.interrupt();
thread = null;
}
thread = new Thread(run);
thread.setDaemon(true);
thread.setName("Update Thread");
thread.start();
Log.info("Starting logic thread.");
}
@Override
public void stop() {
if(thread != null){
thread.interrupt();
thread = null;
}
}
}