This commit is contained in:
Anuken
2019-06-06 12:05:46 -04:00
96 changed files with 4557 additions and 4179 deletions

View File

@@ -105,6 +105,8 @@ public class Vars{
public static FileHandle screenshotDirectory;
/** data subdirectory used for custom mmaps */
public static FileHandle customMapDirectory;
/** tmp subdirectory for map conversion */
public static FileHandle tmpDirectory;
/** data subdirectory used for saves */
public static FileHandle saveDirectory;
/** old map file extension, for conversion */
@@ -206,5 +208,6 @@ public class Vars{
screenshotDirectory = dataDirectory.child("screenshots/");
customMapDirectory = dataDirectory.child("maps/");
saveDirectory = dataDirectory.child("saves/");
tmpDirectory = dataDirectory.child("tmp/");
}
}

View File

@@ -5,8 +5,7 @@ import io.anuke.arc.collection.IntArray;
import io.anuke.arc.collection.IntQueue;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Point2;
import io.anuke.arc.util.Structs;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.*;
import io.anuke.mindustry.game.EventType.TileChangeEvent;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.game.Team;
@@ -40,6 +39,10 @@ public class Pathfinder{
});
}
public void updateSolid(Tile tile){
update(tile, tile.getTeam());
}
public void update(){
if(Net.client() || paths == null) return;
@@ -95,6 +98,10 @@ public class Pathfinder{
if(paths != null && paths[team.ordinal()] != null && paths[team.ordinal()].weights != null){
PathData path = paths[team.ordinal()];
if(!path.frontier.isEmpty()){
return;
}
//impassable tiles have a weight of float.max
if(!passable(tile, team)){
path.weights[tile.x][tile.y] = Float.MAX_VALUE;

View File

@@ -14,12 +14,13 @@ import io.anuke.mindustry.entities.type.BaseUnit;
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.Tile;
import static io.anuke.mindustry.Vars.*;
public class WaveSpawner{
private Array<FlyerSpawn> flySpawns = new Array<>();
private Array<GroundSpawn> groundSpawns = new Array<>();
private Array<Tile> groundSpawns = new Array<>();
private boolean spawning = false;
public WaveSpawner(){
@@ -30,6 +31,10 @@ public class WaveSpawner{
return groundSpawns.size;
}
public Array<Tile> getGroundSpawns(){
return groundSpawns;
}
/** @return true if the player is near a ground spawn point. */
public boolean playerNear(){
return groundSpawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius);
@@ -59,9 +64,9 @@ public class WaveSpawner{
}
}
}else{
for(GroundSpawn spawn : groundSpawns){
spawnX = spawn.x * tilesize;
spawnY = spawn.y * tilesize;
for(Tile spawn : groundSpawns){
spawnX = spawn.worldx();
spawnY = spawn.worldy();
spread = tilesize * 2;
for(int i = 0; i < spawned; i++){
@@ -102,10 +107,7 @@ public class WaveSpawner{
}
private void addSpawns(int x, int y){
GroundSpawn spawn = new GroundSpawn();
spawn.x = x;
spawn.y = y;
groundSpawns.add(spawn);
groundSpawns.add(world.tile(x, y));
FlyerSpawn fspawn = new FlyerSpawn();
fspawn.angle = Angles.angle(world.width() / 2f, world.height() / 2f, x, y);

View File

@@ -114,6 +114,9 @@ public class Blocks implements ContentList{
}
spawn = new OverlayFloor("spawn"){
{
variants = 0;
}
public void draw(Tile tile){}
};
@@ -496,19 +499,7 @@ public class Blocks implements ContentList{
GenericCrafterEntity entity = tile.entity();
Draw.rect(reg(bottomRegion), tile.drawx(), tile.drawy());
float progress = 0.5f;
Shaders.build.region = reg(weaveRegion);
Shaders.build.progress = progress;
Shaders.build.color.set(Pal.accent);
Shaders.build.color.a = entity.warmup;
Shaders.build.time = -entity.totalProgress / 10f;
Draw.shader(Shaders.build, false);
Shaders.build.apply();
Draw.rect(reg(weaveRegion), tile.drawx(), tile.drawy(), entity.totalProgress);
Draw.shader();
Draw.color(Pal.accent);
Draw.alpha(entity.warmup);
@@ -940,7 +931,7 @@ public class Blocks implements ContentList{
itemCapacity = 120;
reloadTime = 200f;
range = 440f;
consumes.power(2f);
consumes.power(1.75f);
}};
//endregion
@@ -1299,8 +1290,8 @@ public class Blocks implements ContentList{
Items.scrap, Bullets.flakScrap,
Items.lead, Bullets.flakLead
);
reload = 15f;
range = 180f;
reload = 16f;
range = 175f;
size = 2;
burstSpacing = 5f;
shots = 2;
@@ -1499,7 +1490,7 @@ public class Blocks implements ContentList{
ammo(Items.graphite, Bullets.fuseShot);
reload = 40f;
shootShake = 4f;
range = 80f;
range = 110f;
recoil = 5f;
restitution = 0.1f;
size = 3;
@@ -1544,7 +1535,7 @@ public class Blocks implements ContentList{
reload = 50f;
firingMoveFract = 0.5f;
shootDuration = 220f;
powerUse = 10f;
powerUse = 14f;
health = 200 * size * size;
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.5f)).update(false);

View File

@@ -579,11 +579,12 @@ public class Bullets implements ContentList{
fuseShot = new BulletType(0.01f, 70){
int rays = 3;
float rayLength = 80f;
float rayLength = 120f;
{
hitEffect = Fx.hitFuse;
lifetime = 13f;
shootEffect = smokeEffect = Fx.none;
lifetime = 10f;
despawnEffect = Fx.none;
pierce = true;
}
@@ -599,14 +600,15 @@ public class Bullets implements ContentList{
public void draw(Bullet b){
super.draw(b);
Draw.color(Color.WHITE, Pal.surge, b.fin());
//Draw.alpha(b.fout());
for(int i = 0; i < 7; i++){
Tmp.v1.trns(b.rot(), i * 8f);
float sl = Mathf.clamp(b.fout() - 0.5f) * (80f - i * 10);
Shapes.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, 4f, sl, b.rot() + 90);
Shapes.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, 4f, sl, b.rot() - 90);
}
Shapes.tri(b.x, b.y, 13f, (rayLength + 50) * b.fout(), b.rot());
Shapes.tri(b.x, b.y, 13f, 10f * b.fout(), b.rot() + 180f);
Shapes.tri(b.x, b.y, 20f * b.fout(), (rayLength + 50), b.rot());
Shapes.tri(b.x, b.y, 20f * b.fout(), 10f, b.rot() + 180f);
Draw.reset();
}
};

View File

@@ -35,7 +35,7 @@ public class Mechs implements ContentList{
boostSpeed = 0.95f;
buildPower = 1.2f;
engineColor = Color.valueOf("ffd37f");
health = 300f;
health = 250f;
weapon = new Weapon("blaster"){{
length = 1.5f;
@@ -51,10 +51,6 @@ public class Mechs implements ContentList{
player.healBy(Time.delta() * 0.09f);
}
@Override
public boolean alwaysUnlocked(){
return true;
}
};
delta = new Mech("delta-mech", false){
@@ -66,7 +62,7 @@ public class Mechs implements ContentList{
boostSpeed = 0.95f;
itemCapacity = 15;
mass = 0.9f;
health = 250f;
health = 150f;
buildPower = 0.9f;
weaponOffsetX = -1;
weaponOffsetY = -1;
@@ -75,7 +71,7 @@ public class Mechs implements ContentList{
weapon = new Weapon("shockgun"){{
shake = 2f;
length = 1f;
reload = 40f;
reload = 45f;
shotDelay = 3f;
roundrobin = true;
shots = 2;
@@ -163,12 +159,12 @@ public class Mechs implements ContentList{
weaponOffsetX = 1;
weaponOffsetY = 0;
engineColor = Color.valueOf("feb380");
health = 300f;
health = 320f;
buildPower = 1.5f;
weapon = new Weapon("swarmer"){{
length = 1.5f;
recoil = 4f;
reload = 60f;
reload = 45f;
shots = 4;
spacing = 8f;
inaccuracy = 8f;
@@ -240,6 +236,11 @@ public class Mechs implements ContentList{
bullet = Bullets.standardCopper;
}};
}
@Override
public boolean alwaysUnlocked(){
return true;
}
};
javelin = new Mech("javelin-ship", true){
@@ -316,7 +317,7 @@ public class Mechs implements ContentList{
itemCapacity = 30;
engineColor = Color.valueOf("84f491");
cellTrnsY = 1f;
buildPower = 2f;
buildPower = 2.5f;
weapon = new Weapon("bomber"){{
length = 0f;
width = 2f;
@@ -329,7 +330,7 @@ public class Mechs implements ContentList{
velocityRnd = 1f;
inaccuracy = 20f;
ignoreRotation = true;
bullet = new BombBulletType(14f, 25f, "shell"){{
bullet = new BombBulletType(16f, 25f, "shell"){{
bulletWidth = 10f;
bulletHeight = 14f;
hitEffect = Fx.flakExplosion;

View File

@@ -135,7 +135,7 @@ public class Control implements ApplicationListener{
Net.host(port);
player.isAdmin = true;
}catch(IOException e){
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true)));
Core.app.post(() -> state.set(State.menu));
}
}

View File

@@ -234,7 +234,7 @@ public class NetServer implements ApplicationListener{
}
player.remove();
netServer.connections.remove(player.con.id);
Log.info("&lc{0} has disconnected.", player.name);
Log.info("&lm[{1}] &lc{0} has disconnected.", player.name, player.uuid);
}
private static float compound(float speed, float drag){
@@ -248,16 +248,16 @@ public class NetServer implements ApplicationListener{
@Remote(targets = Loc.client, unreliable = true)
public static void onClientShapshot(
Player player,
int snapshotID,
float x, float y,
float pointerX, float pointerY,
float rotation, float baseRotation,
float xVelocity, float yVelocity,
Tile mining,
boolean boosting, boolean shooting, boolean chatting,
BuildRequest[] requests,
float viewX, float viewY, float viewWidth, float viewHeight
Player player,
int snapshotID,
float x, float y,
float pointerX, float pointerY,
float rotation, float baseRotation,
float xVelocity, float yVelocity,
Tile mining,
boolean boosting, boolean shooting, boolean chatting,
BuildRequest[] requests,
float viewX, float viewY, float viewWidth, float viewHeight
){
NetConnection connection = player.con;
if(connection == null || snapshotID < connection.lastRecievedClientSnapshot) return;
@@ -296,7 +296,7 @@ public class NetServer implements ApplicationListener{
}
vector.set(x - player.getInterpolator().target.x, y - player.getInterpolator().target.y);
vector.limit(maxMove);
//vector.limit(maxMove);
float prevx = player.x, prevy = player.y;
player.set(player.getInterpolator().target.x, player.getInterpolator().target.y);
@@ -372,7 +372,7 @@ public class NetServer implements ApplicationListener{
player.add();
player.con.hasConnected = true;
Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
Log.info("&y{0} has connected.", player.name);
Log.info("&lm[{1}] &y{0} has connected. ", player.name, player.uuid);
}
public boolean isWaitingForPlayers(){

View File

@@ -4,6 +4,7 @@ import io.anuke.arc.Core;
import io.anuke.arc.Input.TextInput;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.function.Predicate;
import io.anuke.arc.math.RandomXS128;
import io.anuke.arc.scene.ui.TextField;
import io.anuke.arc.util.serialization.Base64Coder;
@@ -66,14 +67,14 @@ public abstract class Platform{
}
/**
* Show a file chooser. Desktop only.
* Show a file chooser.
* @param text File chooser title text
* @param content Description of the type of files to be loaded
* @param cons Selection listener
* @param open Whether to open or save files
* @param filetype File extension to filter
*/
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, String filetype){
public void showFileChooser(String text, String content, Consumer<FileHandle> cons, boolean open, Predicate<String> filetype){
}
/** Hide the app. Android only. */

View File

@@ -25,8 +25,7 @@ import io.anuke.mindustry.ui.dialogs.*;
import io.anuke.mindustry.ui.fragments.*;
import static io.anuke.arc.scene.actions.Actions.*;
import static io.anuke.mindustry.Vars.control;
import static io.anuke.mindustry.Vars.disableUI;
import static io.anuke.mindustry.Vars.*;
public class UI implements ApplicationListener{
private FreeTypeFontGenerator generator;
@@ -244,8 +243,17 @@ public class UI implements ApplicationListener{
}
public void showError(String text){
new Dialog("$error.title", "dialog"){{
cont.margin(15).add(text).width(400f).wrap().get().setAlignment(Align.center, Align.center);
new Dialog("", "dialog"){{
setFillParent(true);
cont.add("$error.title");
cont.row();
cont.margin(15).pane(t -> {
Label l = t.add(text).pad(14f).get();
l.setAlignment(Align.center, Align.left);
if(mobile){
t.getCell(l).wrap().width(400f);
}
});
buttons.addButton("$ok", this::hide).size(90, 50).pad(4);
}}.show();
}

View File

@@ -12,8 +12,13 @@ import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.world;
public class DrawOperation{
private MapEditor editor;
private LongArray array = new LongArray();
public DrawOperation(MapEditor editor) {
this.editor = editor;
}
public boolean isEmpty(){
return array.isEmpty();
}
@@ -22,23 +27,25 @@ public class DrawOperation{
array.add(op);
}
public void undo(MapEditor editor){
public void undo(){
for(int i = array.size - 1; i >= 0; i--){
long l = array.get(i);
array.set(i, TileOp.get(TileOp.x(l), TileOp.y(l), TileOp.type(l), get(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l))));
set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.value(l));
updateTile(i);
}
}
public void redo(MapEditor editor){
public void redo(){
for(int i = 0; i < array.size; i++){
long l = array.get(i);
array.set(i, TileOp.get(TileOp.x(l), TileOp.y(l), TileOp.type(l), get(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l))));
set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.value(l));
updateTile(i);
}
}
short get(Tile tile, byte type){
private void updateTile(int i) {
long l = array.get(i);
array.set(i, TileOp.get(TileOp.x(l), TileOp.y(l), TileOp.type(l), getTile(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l))));
setTile(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.value(l));
}
short getTile(Tile tile, byte type){
if(type == OpType.floor.ordinal()){
return tile.floorID();
}else if(type == OpType.block.ordinal()){
@@ -53,7 +60,7 @@ public class DrawOperation{
throw new IllegalArgumentException("Invalid type.");
}
void set(MapEditor editor, Tile tile, byte type, short to){
void setTile(Tile tile, byte type, short to){
editor.load(() -> {
if(type == OpType.floor.ordinal()){
tile.setFloor((Floor)content.block(to));

View File

@@ -99,7 +99,7 @@ public class EditorTile extends Tile{
}
if(overlayID() == overlay) return;
op(OpType.overlay, overlay);
op(OpType.overlay, this.overlay);
super.setOverlayID(overlay);
}

View File

@@ -50,7 +50,7 @@ public enum EditorTool{
IntArray stack = new IntArray();
Block dest;
boolean isfloor;
boolean isFloor;
MapEditor data;
public void touched(MapEditor editor, int x, int y){
@@ -64,14 +64,14 @@ public enum EditorTool{
}
data = editor;
isfloor = editor.drawBlock instanceof Floor;
isFloor = editor.drawBlock instanceof Floor;
Block floor = tile.floor();
Block block = tile.block();
boolean synth = editor.drawBlock.synthetic();
Block draw = editor.drawBlock;
dest = draw instanceof OverlayFloor ? tile.overlay() : isfloor ? floor : block;
dest = draw instanceof OverlayFloor ? tile.overlay() : isFloor ? floor : block;
if(dest == draw || block instanceof BlockPart || block.isMultiblock()){
return;
@@ -85,7 +85,7 @@ public enum EditorTool{
IntPositionConsumer writer = (px, py) -> {
Tile write = editor.tile(px, py);
if(isfloor){
if(isFloor){
if(alt && !(draw instanceof OverlayFloor)){
Block ore = write.overlay();
write.setFloor((Floor)draw);
@@ -167,7 +167,7 @@ public enum EditorTool{
boolean eq(int px, int py){
Tile tile = data.tile(px, py);
return (data.drawBlock instanceof OverlayFloor ? tile.overlay() : isfloor ? tile.floor() : tile.block()) == dest && !(data.drawBlock instanceof OverlayFloor && tile.floor().isLiquid);
return (data.drawBlock instanceof OverlayFloor ? tile.overlay() : isFloor ? tile.floor() : tile.block()) == dest && !(data.drawBlock instanceof OverlayFloor && tile.floor().isLiquid);
}
},
spray{

View File

@@ -240,13 +240,13 @@ public class MapEditor{
public void undo(){
if(stack.canUndo()){
stack.undo(this);
stack.undo();
}
}
public void redo(){
if(stack.canRedo()){
stack.redo(this);
stack.redo();
}
}
@@ -267,7 +267,7 @@ public class MapEditor{
public void addTileOp(long data){
if(loading) return;
if(currentOp == null) currentOp = new DrawOperation();
if(currentOp == null) currentOp = new DrawOperation(this);
currentOp.addOperation(data);
renderer.updatePoint(TileOp.x(data), TileOp.y(data));

View File

@@ -21,9 +21,9 @@ import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.JsonIO;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.dialogs.FileChooser;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon;
@@ -93,14 +93,16 @@ public class MapEditorDialog extends Dialog implements Disposable{
"$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable)loadDialog::show,
"$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable)() ->
Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> {
try{
//TODO what if it's an image? users should be warned for their stupidity
editor.beginEdit(MapIO.createMap(file, true));
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
}
}), true, mapExtension),
world.maps.tryCatchMapError(() -> {
if(MapIO.isImage(file)){
ui.showInfo("$editor.errorimage");
}else if(file.extension().equalsIgnoreCase(oldMapExtension)){
editor.beginEdit(world.maps.makeLegacyMap(file));
}else{
editor.beginEdit(MapIO.createMap(file, true));
}
});
}), true, FileChooser.anyMapFiles),
"$editor.importimage", "$editor.importimage.description", "icon-file-image", (Runnable)() ->
Platform.instance.showFileChooser("$loadimage", "Image Files", file ->
@@ -110,10 +112,10 @@ public class MapEditorDialog extends Dialog implements Disposable{
editor.beginEdit(pixmap);
pixmap.dispose();
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
Log.err(e);
}
}), true, "png"))
}), true, FileChooser.pngFiles))
);
t.addImageTextButton("$editor.export", "icon-save-map", isize, () ->
@@ -127,11 +129,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
}
MapIO.writeMap(result, editor.createMap(result));
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, true)));
Log.err(e);
}
});
}, false, mapExtension));
}, false, FileChooser.mapFiles));
});
menu.cont.row();
@@ -153,12 +155,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
}
});
loadDialog = new MapLoadDialog(map ->
ui.loadAnd(() -> {
loadDialog = new MapLoadDialog(map -> ui.loadAnd(() -> {
try{
editor.beginEdit(map);
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
Log.err(e);
}
}));
@@ -188,13 +189,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
});
shown(() -> {
//clear units, rules and other unnecessary stuff
logic.reset();
saved = true;
if(!Core.settings.getBool("landscape")) Platform.instance.beginForceLandscape();
editor.clearOp();
Core.scene.setScrollFocus(view);
if(!shownWithMap){
//clear units, rules and other unnecessary stuff
logic.reset();
state.rules = new Rules();
editor.beginEdit(200, 200);
}
@@ -231,7 +233,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
hide();
//only reset the player; logic.reset() will clear entities, which we do not want
player.reset();
state.rules = Gamemode.editor.apply(new Rules());
state.rules = Gamemode.editor.apply(lastSavedRules.copy());
world.setMap(new Map(StringMap.of(
"name", "Editor Playtesting",
"width", editor.width(),
@@ -256,6 +258,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
private void save(){
String name = editor.getTags().get("name", "").trim();
editor.getTags().put("rules", JsonIO.write(state.rules));
player.dead = true;
if(name.isEmpty()){
infoDialog.show();
@@ -335,7 +338,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
show();
}catch(Exception e){
Log.err(e);
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
}
});
}

View File

@@ -27,21 +27,22 @@ import static io.anuke.mindustry.Vars.*;
@SuppressWarnings("unchecked")
public class MapGenerateDialog extends FloatingDialog{
private final Supplier<GenerateFilter>[] filterTypes = new Supplier[]{NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new, RiverNoiseFilter::new, OreFilter::new, MedianFilter::new};
private final MapEditor editor;
private Pixmap pixmap;
private Texture texture;
private GenerateInput input = new GenerateInput();
private Array<GenerateFilter> filters = new Array<>();
private int scaling = mobile ? 3 : 1;
private Supplier<GenerateFilter>[] filterTypes = new Supplier[]{NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new, RiverNoiseFilter::new, OreFilter::new};
private Table filterTable;
private AsyncExecutor executor = new AsyncExecutor(1);
private AsyncResult<Void> result;
private boolean generating;
private DummyTile returnTile = new DummyTile();
private GenTile returnTile = new GenTile();
private DummyTile[][] buffer1, buffer2;
private GenTile[][] buffer1, buffer2;
public MapGenerateDialog(MapEditor editor){
super("$editor.generate");
@@ -98,12 +99,12 @@ public class MapGenerateDialog extends FloatingDialog{
rebuildFilters();
}
DummyTile[][] create(){
DummyTile[][] out = new DummyTile[editor.width() / scaling][editor.height() / scaling];
GenTile[][] create(){
GenTile[][] out = new GenTile[editor.width() / scaling][editor.height() / scaling];
for(int x = 0; x < out.length; x++){
for(int y = 0; y < out[0].length; y++){
out[x][y] = new DummyTile();
out[x][y] = new GenTile();
}
}
return out;
@@ -187,7 +188,7 @@ public class MapGenerateDialog extends FloatingDialog{
selection.show();
}
DummyTile dset(Tile tile){
GenTile dset(Tile tile){
returnTile.set(tile);
return returnTile;
}
@@ -208,11 +209,11 @@ public class MapGenerateDialog extends FloatingDialog{
}
//writeback buffer
DummyTile[][] writeTiles = new DummyTile[editor.width()][editor.height()];
GenTile[][] writeTiles = new GenTile[editor.width()][editor.height()];
for(int x = 0; x < editor.width(); x++){
for(int y = 0; y < editor.height(); y++){
writeTiles[x][y] = new DummyTile();
writeTiles[x][y] = new GenTile();
}
}
@@ -233,7 +234,7 @@ public class MapGenerateDialog extends FloatingDialog{
for(int x = 0; x < editor.width(); x++){
for(int y = 0; y < editor.height(); y++){
Tile tile = editor.tile(x, y);
DummyTile write = writeTiles[x][y];
GenTile write = writeTiles[x][y];
tile.rotation(write.rotation);
tile.setFloor((Floor)content.block(write.floor));
@@ -278,7 +279,7 @@ public class MapGenerateDialog extends FloatingDialog{
for(int px = 0; px < pixmap.getWidth(); px++){
for(int py = 0; py < pixmap.getHeight(); py++){
int x = px * scaling, y = py * scaling;
DummyTile tile = buffer1[px][py];
GenTile tile = buffer1[px][py];
input.begin(editor, x, y, content.block(tile.floor), content.block(tile.block), content.block(tile.ore));
filter.apply(input);
buffer2[px][py].set(input.floor, input.block, input.ore, Team.all[tile.team], tile.rotation);
@@ -299,7 +300,7 @@ public class MapGenerateDialog extends FloatingDialog{
Tile tile = editor.tile(px * scaling, py * scaling);
color = MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), Team.none);
}else{
DummyTile tile = buffer1[px][py];
GenTile tile = buffer1[px][py];
color = MapIO.colorFor(content.block(tile.floor), content.block(tile.block), content.block(tile.ore), Team.none);
}
pixmap.drawPixel(px, pixmap.getHeight() - 1 - py, color);
@@ -321,7 +322,7 @@ public class MapGenerateDialog extends FloatingDialog{
});
}
public static class DummyTile{
public static class GenTile{
public byte team, rotation;
public short block, floor, ore;
@@ -333,7 +334,7 @@ public class MapGenerateDialog extends FloatingDialog{
this.rotation = (byte)rotation;
}
void set(DummyTile other){
void set(GenTile other){
this.floor = other.floor;
this.block = other.block;
this.ore = other.ore;

View File

@@ -12,13 +12,13 @@ import io.anuke.mindustry.ui.dialogs.FloatingDialog;
public class MapInfoDialog extends FloatingDialog{
private final MapEditor editor;
private final WaveInfoDialog waveinfo;
private final CustomRulesDialog ruleinfo = new CustomRulesDialog();
private final WaveInfoDialog waveInfo;
private final CustomRulesDialog ruleInfo = new CustomRulesDialog();
public MapInfoDialog(MapEditor editor){
super("$editor.mapinfo");
this.editor = editor;
this.waveinfo = new WaveInfoDialog(editor);
this.waveInfo = new WaveInfoDialog(editor);
addCloseButton();
@@ -58,11 +58,11 @@ public class MapInfoDialog extends FloatingDialog{
cont.row();
cont.add("$editor.rules").padRight(8).left();
cont.addButton("$edit", () -> ruleinfo.show(Vars.state.rules, () -> Vars.state.rules = new Rules())).left().width(200f);;
cont.addButton("$edit", () -> ruleInfo.show(Vars.state.rules, () -> Vars.state.rules = new Rules())).left().width(200f);;
cont.row();
cont.add("$editor.waves").padRight(8).left();
cont.addButton("$edit", waveinfo::show).left().width(200f);
cont.addButton("$edit", waveInfo::show).left().width(200f);
name.change();
description.change();

View File

@@ -19,7 +19,7 @@ import io.anuke.mindustry.world.blocks.BlockPart;
import static io.anuke.mindustry.Vars.tilesize;
public class MapRenderer implements Disposable{
private static final int chunksize = 64;
private static final int chunkSize = 64;
private IndexedRenderer[][] chunks;
private IntSet updates = new IntSet();
private IntSet delayedUpdates = new IntSet();
@@ -41,11 +41,11 @@ public class MapRenderer implements Disposable{
}
}
chunks = new IndexedRenderer[(int)Math.ceil((float)width / chunksize)][(int)Math.ceil((float)height / chunksize)];
chunks = new IndexedRenderer[(int)Math.ceil((float)width / chunkSize)][(int)Math.ceil((float)height / chunkSize)];
for(int x = 0; x < chunks.length; x++){
for(int y = 0; y < chunks[0].length; y++){
chunks[x][y] = new IndexedRenderer(chunksize * chunksize * 2);
chunks[x][y] = new IndexedRenderer(chunkSize * chunkSize * 2);
}
}
this.width = width;
@@ -97,7 +97,7 @@ public class MapRenderer implements Disposable{
}
private void render(int wx, int wy){
int x = wx / chunksize, y = wy / chunksize;
int x = wx / chunkSize, y = wy / chunkSize;
IndexedRenderer mesh = chunks[x][y];
Tile tile = editor.tiles()[wx][wy];
@@ -107,8 +107,8 @@ public class MapRenderer implements Disposable{
TextureRegion region;
int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize;
int idxDecal = (wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize;
int idxWall = (wx % chunkSize) + (wy % chunkSize) * chunkSize;
int idxDecal = (wx % chunkSize) + (wy % chunkSize) * chunkSize + chunkSize * chunkSize;
if(wall != Blocks.air && (wall.synthetic() || wall instanceof BlockPart)){
region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
@@ -145,8 +145,8 @@ public class MapRenderer implements Disposable{
}
mesh.draw(idxDecal, region,
wx * tilesize + offsetX, wy * tilesize + offsetY,
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl);
wx * tilesize + offsetX, wy * tilesize + offsetY,
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl);
mesh.setColor(Color.WHITE);
}

View File

@@ -34,18 +34,18 @@ public class OperationStack{
return !(index > -1 || stack.size + index < 0);
}
public void undo(MapEditor editor){
public void undo(){
if(!canUndo()) return;
stack.get(stack.size - 1 + index).undo(editor);
stack.get(stack.size - 1 + index).undo();
index--;
}
public void redo(MapEditor editor){
public void redo(){
if(!canRedo()) return;
index++;
stack.get(stack.size - 1 + index).redo(editor);
stack.get(stack.size - 1 + index).redo();
}
}

View File

@@ -1,6 +1,6 @@
package io.anuke.mindustry.editor.generation;
import io.anuke.mindustry.editor.MapGenerateDialog.DummyTile;
import io.anuke.mindustry.editor.MapGenerateDialog.GenTile;
import io.anuke.mindustry.editor.generation.FilterOption.SliderOption;
import io.anuke.mindustry.world.blocks.Floor;
@@ -11,14 +11,14 @@ public class DistortFilter extends GenerateFilter{
{
options(
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 400f),
new SliderOption("mag", () -> mag, f -> mag = f, 0.5f, 100f)
new SliderOption("scale", () -> scl, f -> scl = f, 1f, 400f),
new SliderOption("mag", () -> mag, f -> mag = f, 0.5f, 100f)
);
}
@Override
public void apply(){
DummyTile tile = in.tile(in.x / (in.scaling) + (noise(in.x, in.y, scl, mag) - mag / 2f) / in.scaling, in.y / (in.scaling) + (noise(in.x, in.y + o, scl, mag) - mag / 2f) / in.scaling);
GenTile tile = in.tile(in.x / (in.scaling) + (noise(in.x, in.y, scl, mag) - mag / 2f) / in.scaling, in.y / (in.scaling) + (noise(in.x, in.y + o, scl, mag) - mag / 2f) / in.scaling);
in.floor = content.block(tile.floor);
if(!content.block(tile.block).synthetic() && !in.block.synthetic()) in.block = content.block(tile.block);

View File

@@ -7,14 +7,14 @@ import io.anuke.arc.util.noise.RidgedPerlin;
import io.anuke.arc.util.noise.Simplex;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.editor.MapEditor;
import io.anuke.mindustry.editor.MapGenerateDialog.DummyTile;
import io.anuke.mindustry.editor.MapGenerateDialog.GenTile;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.blocks.Floor;
public abstract class GenerateFilter{
protected float o = (float)(Math.random() * 10000000.0);
protected long seed;
protected GenerateInput in;
protected transient float o = (float)(Math.random() * 10000000.0);
protected transient long seed;
protected transient GenerateInput in;
public FilterOption[] options;
@@ -88,12 +88,12 @@ public abstract class GenerateFilter{
pnoise.setSeed((int)(filter.seed + 1));
}
DummyTile tile(float x, float y){
GenTile tile(float x, float y){
return buffer.get(Mathf.clamp((int)x, 0, width - 1), Mathf.clamp((int)y, 0, height - 1));
}
public interface TileProvider{
DummyTile get(int x, int y);
GenTile get(int x, int y);
}
}
}

View File

@@ -0,0 +1,46 @@
package io.anuke.mindustry.editor.generation;
import io.anuke.arc.collection.IntArray;
import io.anuke.arc.math.Mathf;
import io.anuke.mindustry.editor.MapGenerateDialog.GenTile;
import io.anuke.mindustry.editor.generation.FilterOption.SliderOption;
import static io.anuke.mindustry.Vars.content;
public class MedianFilter extends GenerateFilter{
float radius = 2;
float percentile = 0.5f;
IntArray blocks = new IntArray(), floors = new IntArray();
{
options(
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f),
new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f)
);
}
@Override
public void apply(){
int rad = (int)radius;
blocks.clear();
floors.clear();
for(int x = -rad; x <= rad; x++){
for(int y = -rad; y <= rad; y++){
if(Mathf.dst2(x, y) > rad*rad) continue;
GenTile tile = in.tile(in.x + x, in.y + y);
blocks.add(tile.block);
floors.add(tile.floor);
}
}
floors.sort();
blocks.sort();
int index = Math.min((int)(floors.size * percentile), floors.size - 1);
int floor = floors.get(index), block = blocks.get(index);
in.floor = content.block(floor);
if(!content.block(block).synthetic() && !in.block.synthetic()) in.block = content.block(block);
}
}

View File

@@ -28,6 +28,7 @@ public class LiquidBulletType extends BulletType{
statusDuration = 90f;
despawnEffect = Fx.none;
hitEffect = Fx.hitLiquid;
smokeEffect = Fx.none;
shootEffect = Fx.none;
drag = 0.009f;
knockback = 0.55f;

View File

@@ -1,12 +1,12 @@
package io.anuke.mindustry.entities.effect;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.math.Mathf;
import io.anuke.mindustry.entities.EntityGroup;
import io.anuke.mindustry.entities.impl.TimedEntity;
import io.anuke.mindustry.entities.traits.BelowLiquidTrait;
import io.anuke.mindustry.entities.traits.DrawTrait;
import io.anuke.mindustry.graphics.Pal;
import static io.anuke.mindustry.Vars.groundEffectGroup;
@@ -14,7 +14,6 @@ import static io.anuke.mindustry.Vars.groundEffectGroup;
* Class for creating block rubble on the ground.
*/
public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait{
private static final Color color = Color.valueOf("3a3635");
@Override
public float lifetime(){
@@ -23,7 +22,7 @@ public abstract class Decal extends TimedEntity implements BelowLiquidTrait, Dra
@Override
public void draw(){
Draw.color(color.r, color.g, color.b, 1f - Mathf.curve(fin(), 0.98f));
Draw.color(Pal.rubble.r, Pal.rubble.g, Pal.rubble.b, 1f - Mathf.curve(fin(), 0.98f));
drawDecal();
Draw.color();
}

View File

@@ -1,13 +1,11 @@
package io.anuke.mindustry.entities.effect;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.arc.collection.IntMap;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Point2;
import io.anuke.arc.util.Structs;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.pooling.Pool.Poolable;
import io.anuke.arc.util.pooling.Pools;
import io.anuke.arc.util.*;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.impl.TimedEntity;
@@ -22,7 +20,7 @@ import java.io.*;
import static io.anuke.mindustry.Vars.*;
public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
public class Fire extends TimedEntity implements SaveTrait, SyncTrait{
private static final IntMap<Fire> map = new IntMap<>();
private static final float baseLifetime = 1000f, spreadChance = 0.05f, fireballChance = 0.07f;
@@ -36,6 +34,11 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
public Fire(){
}
@Remote
public static void onRemoveFire(int fid){
fireGroup.removeByID(fid);
}
/** Start a fire on the tile. If there already is a file there, refreshes its lifetime. */
public static void create(Tile tile){
if(Net.client() || tile == null) return; //not clientside.
@@ -43,7 +46,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
Fire fire = map.get(tile.pos());
if(fire == null){
fire = Pools.obtain(Fire.class, Fire::new);
fire = new Fire();
fire.tile = tile;
fire.lifetime = baseLifetime;
fire.set(tile.worldx(), tile.worldy());
@@ -94,12 +97,12 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
time = Mathf.clamp(time + Time.delta(), 0, lifetime());
if(time >= lifetime() || tile == null){
remove();
if(Net.client()){
return;
}
if(Net.client()){
if(time >= lifetime() || tile == null){
remove();
return;
}
@@ -176,6 +179,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
x = Pos.x(pos) * tilesize;
y = Pos.y(pos) * tilesize;
tile = world.tile(pos);
}
@Override
@@ -184,6 +188,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
tile = null;
baseFlammability = -1;
puddleFlammability = 0f;
incrementID();
}
@Override
@@ -198,9 +203,9 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
@Override
public void removed(){
if(tile != null){
Call.onRemoveFire(id);
map.remove(tile.pos());
}
reset();
}
@Override

View File

@@ -58,4 +58,9 @@ public abstract class BaseEntity implements Entity{
public String toString(){
return getClass() + " " + id;
}
/** Increments this entity's ID. Used for pooled entities.*/
public void incrementID(){
id = lastid++;
}
}

View File

@@ -165,7 +165,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
output.writeInt(Pos.get(request.x, request.y));
output.writeFloat(request.progress);
if(!request.breaking){
output.writeByte(request.block.id);
output.writeShort(request.block.id);
output.writeByte(request.rotation);
}
}else{
@@ -189,7 +189,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
if(type == 1){ //remove
request = new BuildRequest(Pos.x(position), Pos.y(position));
}else{ //place
byte block = input.readByte();
short block = input.readShort();
byte rotation = input.readByte();
request = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
}

View File

@@ -23,6 +23,7 @@ import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.units.UnitFactory.UnitFactoryEntity;
import io.anuke.mindustry.world.meta.BlockFlag;
import java.io.*;
@@ -264,7 +265,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
avoidOthers();
if(spawner != noSpawner && (world.tile(spawner) == null || world.tile(spawner).entity == null)){
if(spawner != noSpawner && (world.tile(spawner) == null || !(world.tile(spawner).entity instanceof UnitFactoryEntity))){
kill();
}

View File

@@ -559,7 +559,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
updateShooting(); //server simulates player shooting
}
return;
}else{
}else if(world.isZone()){
//unlock mech when used
data.unlockContent(mech);
}
@@ -665,10 +665,10 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
attractDst = 0f;
if(tapping){
velocity.setAngle(Mathf.slerpDelta(velocity.angle(), angleTo(moveTarget), 0.1f));
velocity.setAngle(angleTo(moveTarget));
}
if(dst(moveTarget) < 2f){
if(dst(moveTarget) <= 2f * Time.delta()){
if(tapping){
Tile tile = ((TileEntity)moveTarget).tile;
tile.block().tapped(tile, this);
@@ -680,7 +680,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
moveTarget = null;
}
movement.set(targetX - x, targetY - y).limit(isBoosting && !mech.flying ? mech.boostSpeed : mech.speed);
movement.set((targetX - x) / Time.delta(), (targetY - y) / Time.delta()).limit(isBoosting && !mech.flying ? mech.boostSpeed : mech.speed);
movement.setAngle(Mathf.slerp(movement.angle(), velocity.angle(), 0.05f));
if(dst(targetX, targetY) < attractDst){
@@ -795,7 +795,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
}
public boolean isShooting(){
return isShooting && (!isBoosting || mech.flying);
return isShooting && (!isBoosting || mech.flying) && mining == null;
}
public void updateRespawning(){

View File

@@ -9,8 +9,7 @@ import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.Tmp;
import io.anuke.arc.util.*;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.*;
@@ -256,6 +255,16 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
kill();
}
//apply knockback based on spawns
if(getTeam() != waveTeam){
float relativeSize = state.rules.dropZoneRadius + getSize()/2f + 1f;
for(Tile spawn : world.spawner.getGroundSpawns()){
if(withinDst(spawn.worldx(), spawn.worldy(), relativeSize)){
velocity.add(Tmp.v1.set(this).sub(spawn.worldx(), spawn.worldy()).setLength(0.1f + 1f - dst(spawn) / relativeSize).scl(0.45f * Time.delta()));
}
}
}
if(isFlying()){
drownTime = 0f;
move(velocity.x * Time.delta(), velocity.y * Time.delta());

View File

@@ -1,6 +1,5 @@
package io.anuke.mindustry.game;
import io.anuke.arc.Events.Event;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.traits.BuilderTrait;
import io.anuke.mindustry.entities.type.Unit;
@@ -10,7 +9,7 @@ import io.anuke.mindustry.world.Tile;
public class EventType{
/** Called when a zone's requirements are met. */
public static class ZoneRequireCompleteEvent implements Event{
public static class ZoneRequireCompleteEvent{
public final Zone zone, required;
public ZoneRequireCompleteEvent(Zone zone, Zone required){
@@ -20,7 +19,7 @@ public class EventType{
}
/** Called when a zone's requirements are met. */
public static class ZoneConfigureCompleteEvent implements Event{
public static class ZoneConfigureCompleteEvent{
public final Zone zone;
public ZoneConfigureCompleteEvent(Zone zone){
@@ -29,23 +28,23 @@ public class EventType{
}
/** Called when the game is first loaded. */
public static class GameLoadEvent implements Event{
public static class GameLoadEvent{
}
public static class PlayEvent implements Event{
public static class PlayEvent{
}
public static class ResetEvent implements Event{
public static class ResetEvent{
}
public static class WaveEvent implements Event{
public static class WaveEvent{
}
public static class GameOverEvent implements Event{
public static class GameOverEvent{
public final Team winner;
public GameOverEvent(Team winner){
@@ -54,12 +53,12 @@ public class EventType{
}
/** Called when a game begins and the world is loaded. */
public static class WorldLoadEvent implements Event{
public static class WorldLoadEvent{
}
/** Called from the logic thread. Do not access graphics here! */
public static class TileChangeEvent implements Event{
public static class TileChangeEvent{
public final Tile tile;
public TileChangeEvent(Tile tile){
@@ -67,7 +66,7 @@ public class EventType{
}
}
public static class StateChangeEvent implements Event{
public static class StateChangeEvent{
public final State from, to;
public StateChangeEvent(State from, State to){
@@ -76,7 +75,7 @@ public class EventType{
}
}
public static class UnlockEvent implements Event{
public static class UnlockEvent{
public final UnlockableContent content;
public UnlockEvent(UnlockableContent content){
@@ -88,7 +87,7 @@ public class EventType{
* Called when block building begins by placing down the BuildBlock.
* The tile's block will nearly always be a BuildBlock.
*/
public static class BlockBuildBeginEvent implements Event{
public static class BlockBuildBeginEvent{
public final Tile tile;
public final Team team;
public final boolean breaking;
@@ -100,7 +99,7 @@ public class EventType{
}
}
public static class BlockBuildEndEvent implements Event{
public static class BlockBuildEndEvent{
public final Tile tile;
public final Team team;
public final boolean breaking;
@@ -116,7 +115,7 @@ public class EventType{
* Called when a player or drone begins building something.
* This does not necessarily happen when a new BuildBlock is created.
*/
public static class BuildSelectEvent implements Event{
public static class BuildSelectEvent{
public final Tile tile;
public final Team team;
public final BuilderTrait builder;
@@ -130,7 +129,7 @@ public class EventType{
}
}
public static class BlockDestroyEvent implements Event{
public static class BlockDestroyEvent{
public final Tile tile;
public BlockDestroyEvent(Tile tile){
@@ -138,7 +137,7 @@ public class EventType{
}
}
public static class UnitDestroyEvent implements Event{
public static class UnitDestroyEvent{
public final Unit unit;
public UnitDestroyEvent(Unit unit){
@@ -146,7 +145,7 @@ public class EventType{
}
}
public static class ResizeEvent implements Event{
public static class ResizeEvent{
}
}

View File

@@ -28,10 +28,10 @@ public enum Gamemode{
rules.respawnTime = 60 * 10;
rules.buildCostMultiplier = 0.5f;
rules.buildSpeedMultiplier = 2f;
rules.playerDamageMultiplier = 0.45f;
rules.playerHealthMultiplier = 0.8f;
rules.playerDamageMultiplier = 0.33f;
rules.playerHealthMultiplier = 0.5f;
rules.unitBuildSpeedMultiplier = 3f;
rules.unitHealthMultiplier = 2f;
rules.unitHealthMultiplier = 3f;
rules.attackMode = true;
}),
editor(true, rules -> {

View File

@@ -28,7 +28,7 @@ public class Teams{
/** Returns whether a team is active, e.g. whether it has any cores remaining. */
public boolean isActive(Team team){
//the enemy wave team is always active
return (Vars.state.rules.waves && team == Vars.waveTeam) || get(team).cores.size > 0;
return team == Vars.waveTeam || get(team).cores.size > 0;
}
/** Returns a set of all teams that are enemies of this team. */

View File

@@ -35,6 +35,7 @@ public class Pal{
darkishGray = new Color(0.3f, 0.3f, 0.3f, 1f),
darkerGray = new Color(0.2f, 0.2f, 0.2f, 1f),
ammo = Color.valueOf("ff8947"),
rubble = Color.valueOf("1c1817"),
boostTo = Color.valueOf("ffad4d"),
boostFrom = Color.valueOf("ff7f57"),

View File

@@ -40,6 +40,8 @@ public class LegacyMapIO{
for(int x = 0; x < map.width; x++){
for(int y = 0; y < map.height; y++){
tiles[x][y] = new CachedTile();
tiles[x][y].x = (short)x;
tiles[x][y].y = (short)y;
}
}
state.rules.spawns = groups;
@@ -53,6 +55,9 @@ public class LegacyMapIO{
//meta is uncompressed
int version = stream.readInt();
if(version != 1){
throw new IOException("Outdated legacy map format");
}
int build = stream.readInt();
short width = stream.readShort(), height = stream.readShort();
byte tagAmount = stream.readByte();

View File

@@ -71,6 +71,7 @@ public abstract class SaveVersion extends SaveFileReader{
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
state.stats = JsonIO.read(Stats.class, map.get("stats", "{}"));
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
if(state.rules.spawns.isEmpty()) state.rules.spawns = DefaultWaves.get();
Map worldmap = world.maps.byName(map.get("mapname", "\\\\\\"));
world.setMap(worldmap == null ? new Map(StringMap.of(
"name", map.get("mapname", "Unknown"),
@@ -170,10 +171,14 @@ public abstract class SaveVersion extends SaveFileReader{
tile.setBlock(block);
if(tile.entity != null){
readChunk(stream, true, in -> {
byte version = in.readByte();
tile.entity.read(in, version);
});
try{
readChunk(stream, true, in -> {
byte version = in.readByte();
tile.entity.read(in, version);
});
}catch(Exception e){
throw new IOException("Failed to read tile entity of block: " + block, e);
}
}else{
int consecutives = stream.readUnsignedByte();

View File

@@ -5,6 +5,7 @@ import io.anuke.arc.collection.StringMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Texture;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.game.DefaultWaves;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.io.JsonIO;
@@ -57,7 +58,9 @@ public class Map implements Comparable<Map>{
/** This creates a new instance.*/
public Rules rules(){
return JsonIO.read(Rules.class, tags.get("rules", "{}"));
Rules result = JsonIO.read(Rules.class, tags.get("rules", "{}"));
if(result.spawns.isEmpty()) result.spawns = DefaultWaves.get();
return result;
}
/** Whether this map has a core of the enemy 'wave' team. Default: true.

View File

@@ -3,12 +3,13 @@ package io.anuke.mindustry.maps;
import io.anuke.arc.Core;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.ExceptionRunnable;
import io.anuke.arc.graphics.Texture;
import io.anuke.arc.util.Disposable;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.*;
import io.anuke.arc.util.serialization.Json;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.io.*;
import io.anuke.mindustry.io.LegacyMapIO;
import io.anuke.mindustry.io.MapIO;
import java.io.IOException;
import java.io.StringWriter;
@@ -17,7 +18,7 @@ import static io.anuke.mindustry.Vars.*;
public class Maps implements Disposable{
/** List of all built-in maps. Filenames only. */
private static final String[] defaultMapNames = {"fortress"};
private static final String[] defaultMapNames = {"fortress", "shoreline", "labyrinth", "islands"};
/** All maps stored in an ordered array. */
private Array<Map> maps = new Array<>();
/** Serializer for meta. */
@@ -116,6 +117,14 @@ public class Maps implements Disposable{
}
}
/** Creates a legacy map by converting it to a non-legacy map and pasting it in a temp directory.
* Should be followed up by {@link #importMap(FileHandle)} .*/
public Map makeLegacyMap(FileHandle file) throws IOException{
FileHandle dst = tmpDirectory.child("conversion_map." + mapExtension);
LegacyMapIO.convertMap(file, dst);
return MapIO.createMap(dst, true);
}
/** Import a map, then save it. This updates all values and stored data necessary. */
public void importMap(FileHandle file) throws IOException{
FileHandle dest = findFile();
@@ -124,6 +133,24 @@ public class Maps implements Disposable{
loadMap(dest, true);
}
/** Attempts to run the following code;
* catches any errors and attempts to display them in a readable way.*/
public void tryCatchMapError(ExceptionRunnable run){
try{
run.run();
}catch(Exception e){
Log.err(e);
if("Outdated legacy map format".equals(e.getMessage())){
ui.showError("$editor.errorlegacy");
}else if(e.getMessage() != null && e.getMessage().contains("Incorrect header!")){
ui.showError("$editor.errorheader");
}else{
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true)));
}
}
}
/** Removes a map completely. */
public void removeMap(Map map){
if(map.texture != null){

View File

@@ -6,8 +6,7 @@ import io.anuke.arc.collection.*;
import io.anuke.arc.function.BiConsumer;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.net.HttpRequestBuilder;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.*;
import io.anuke.arc.util.pooling.Pools;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.gen.Call;
@@ -67,7 +66,7 @@ public class Net{
}else if(error.equals("alreadyconnected")){
error = Core.bundle.get("error.alreadyconnected");
}else if(!error.isEmpty()){
error = Core.bundle.get("error.any") + "\n" + t.getClass().getSimpleName() + "\n" + (t.getMessage() == null ? "" : t.getMessage());
error = Core.bundle.get("error.any") + "\n" + Strings.parseException(e, true);
}
ui.showText("", Core.bundle.format("connectfail", error));

View File

@@ -6,6 +6,8 @@ import io.anuke.mindustry.type.ItemStack;
/** An item image with text. */
public class ItemDisplay extends Table{
public final Item item;
public final int amount;
public ItemDisplay(Item item){
this(item, 0);
@@ -14,5 +16,8 @@ public class ItemDisplay extends Table{
public ItemDisplay(Item item, int amount){
add(new ItemImage(new ItemStack(item, amount))).size(8 * 4);
add(item.localizedName()).padLeft(4);
this.item = item;
this.amount = amount;
}
}

View File

@@ -10,8 +10,15 @@ import io.anuke.mindustry.world.meta.StatUnit;
/** An ItemDisplay, but for liquids. */
public class LiquidDisplay extends Table{
public final Liquid liquid;
public final float amount;
public final boolean perSecond;
public LiquidDisplay(Liquid liquid, float amount, boolean perSecond){
this.liquid = liquid;
this.amount = amount;
this.perSecond = perSecond;
add(new Stack(){{
add(new Image(liquid.getContentIcon()));

View File

@@ -12,7 +12,8 @@ import io.anuke.mindustry.ui.BorderImage;
import static io.anuke.mindustry.Vars.world;
public class CustomGameDialog extends FloatingDialog{
public class
CustomGameDialog extends FloatingDialog{
private MapPlayDialog dialog = new MapPlayDialog();
public CustomGameDialog(){

View File

@@ -12,6 +12,7 @@ import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.*;
import io.anuke.arc.util.pooling.Pools;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform;
import java.util.Arrays;
@@ -29,12 +30,26 @@ public class FileChooser extends FloatingDialog{
private Predicate<FileHandle> filter;
private Consumer<FileHandle> selectListener;
private boolean open;
private int lastWidth = Core.graphics.getWidth(), lastHeight = Core.graphics.getHeight();
public static final Predicate<String> pngFiles = str -> str.equals("png");
public static final Predicate<String> anyMapFiles = str -> str.equals(Vars.oldMapExtension) || str.equals(Vars.mapExtension);
public static final Predicate<String> mapFiles = str -> str.equals(Vars.mapExtension);
public static final Predicate<String> saveFiles = str -> str.equals(Vars.saveExtension);
public FileChooser(String title, Predicate<FileHandle> filter, boolean open, Consumer<FileHandle> result){
super(title);
this.open = open;
this.filter = filter;
this.selectListener = result;
update(() -> {
if(Core.graphics.getWidth() != lastWidth || Core.graphics.getHeight() != lastHeight){
updateFiles(false);
lastHeight = Core.graphics.getHeight();
lastWidth = Core.graphics.getWidth();
}
});
}
private void setupWidgets(){
@@ -268,14 +283,6 @@ public class FileChooser extends FloatingDialog{
return this;
}
public void fileSelected(Consumer<FileHandle> listener){
this.selectListener = listener;
}
public interface FileHandleFilter{
boolean accept(FileHandle file);
}
public class FileHistory{
private Array<FileHandle> history = new Array<>();
private int index;

View File

@@ -56,7 +56,7 @@ public class HostDialog extends FloatingDialog{
Net.host(Vars.port);
player.isAdmin = true;
}catch(IOException e){
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true)));
}
ui.loadfrag.hide();
hide();

View File

@@ -90,16 +90,16 @@ public class LoadDialog extends FloatingDialog{
slot.exportFile(file);
setup();
}catch(IOException e){
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, true)));
}
}, false, saveExtension);
}, false, FileChooser.saveFiles);
}else{
try{
FileHandle file = Core.files.local("save-" + slot.getName() + "." + Vars.saveExtension);
slot.exportFile(file);
Platform.instance.shareFile(file);
}catch(Exception e){
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("save.export.fail", Strings.parseException(e, true)));
}
}
}).size(14 * 3).right();
@@ -155,12 +155,12 @@ public class LoadDialog extends FloatingDialog{
setup();
}catch(IOException e){
e.printStackTrace();
ui.showError(Core.bundle.format("save.import.fail", Strings.parseException(e, false)));
ui.showError(Core.bundle.format("save.import.fail", Strings.parseException(e, true)));
}
}else{
ui.showError("$save.import.invalid");
}
}, true, saveExtension);
}, true, FileChooser.saveFiles);
}).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4);
}

View File

@@ -16,7 +16,7 @@ public class MapPlayDialog extends FloatingDialog{
Difficulty difficulty = Difficulty.normal;
CustomRulesDialog dialog = new CustomRulesDialog();
Rules rules;
Gamemode selectedGamemode;
Gamemode selectedGamemode = Gamemode.survival;
public MapPlayDialog(){
super("");
@@ -28,6 +28,8 @@ public class MapPlayDialog extends FloatingDialog{
cont.clearChildren();
rules = map.rules();
rules = selectedGamemode.apply(map.rules());
Table selmode = new Table();
selmode.add("$level.mode").colspan(4);
selmode.row();
@@ -43,8 +45,8 @@ public class MapPlayDialog extends FloatingDialog{
}
modes.addButton(mode.toString(), "toggle", () -> {
selectedGamemode = selectedGamemode == mode ? null : mode;
rules = selectedGamemode == null ? map.rules() : mode.apply(map.rules());
selectedGamemode = mode;
rules = mode.apply(map.rules());
}).update(b -> b.setChecked(selectedGamemode == mode)).size(140f, 54f);
if(i++ % 2 == 1) modes.row();
}

View File

@@ -5,7 +5,7 @@ import io.anuke.arc.graphics.Color;
import io.anuke.arc.scene.event.Touchable;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.*;
import io.anuke.arc.util.Scaling;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.io.MapIO;
@@ -23,8 +23,19 @@ public class MapsDialog extends FloatingDialog{
addCloseButton();
buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> {
Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> {
try{
Map map = MapIO.createMap(file, true);
world.maps.tryCatchMapError(() -> {
if(MapIO.isImage(file)){
ui.showError("$editor.errorimage");
return;
}
Map map;
if(file.extension().equalsIgnoreCase(mapExtension)){
map = MapIO.createMap(file, true);
}else{
map = world.maps.makeLegacyMap(file);
}
String name = map.tags.get("name");
if(name == null){
ui.showError("$editor.errorname");
@@ -34,27 +45,21 @@ public class MapsDialog extends FloatingDialog{
Map conflict = world.maps.all().find(m -> m.name().equals(name));
if(conflict != null && !conflict.custom){
ui.showError(Core.bundle.format("editor.import.exists", name));
ui.showInfo(Core.bundle.format("editor.import.exists", name));
}else if(conflict != null){
ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
try{
world.maps.tryCatchMapError(() -> {
world.maps.importMap(file);
setup();
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
}
});
});
}else{
world.maps.importMap(file);
world.maps.importMap(map.file);
setup();
}
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
}
}, true, mapExtension);
});
}, true, FileChooser.anyMapFiles);
}).size(230f, 64f);
shown(this::setup);

View File

@@ -178,7 +178,6 @@ public class SettingsMenuDialog extends SettingsDialog{
graphics.sliderPref("fpscap", 125, 5, 125, 5, s -> (s > 120 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s)));
graphics.sliderPref("chatopacity", 100, 0, 100, 5, s -> s + "%");
if(!mobile){
graphics.checkPref("vsync", true, b -> Core.graphics.setVSync(b));
graphics.checkPref("fullscreen", false, b -> {
@@ -221,6 +220,22 @@ public class SettingsMenuDialog extends SettingsDialog{
graphics.checkPref("animatedshields", !mobile);
graphics.checkPref("lasers", true);
graphics.checkPref("pixelate", false);
//TODO is this necessary?
/*
graphics.checkPref("linear", false, b -> {
for(Texture tex : Core.atlas.getTextures()){
TextureFilter filter = b ? TextureFilter.Linear : TextureFilter.Nearest;
tex.setFilter(filter, filter);
}
});
if(Core.settings.getBool("linear")){
for(Texture tex : Core.atlas.getTextures()){
TextureFilter filter = TextureFilter.Linear;
tex.setFilter(filter, filter);
}
}*/
}
private void back(){

View File

@@ -73,8 +73,6 @@ public class Block extends BlockStorage{
public Layer layer2 = null;
/** whether this block can be replaced in all cases */
public boolean alwaysReplace = false;
/** whether this block has instant transfer checking. used for calculations to prevent infinite loops. */
public boolean instantTransfer = false;
/** The block group. Unless {@link #canReplace} is overriden, blocks in the same group can replace each other. */
public BlockGroup group = BlockGroup.none;
/** List of block flags. Used for AI indexing. */

View File

@@ -1,6 +1,5 @@
package io.anuke.mindustry.world;
import io.anuke.arc.collection.IntMap;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.world.modules.*;
@@ -10,7 +9,6 @@ import io.anuke.mindustry.world.modules.*;
* Prevents garbage when loading previews.
*/
public class CachedTile extends Tile{
private static IntMap<TileEntity> entities = new IntMap<>();
public CachedTile(){
super(0, 0);
@@ -23,7 +21,8 @@ public class CachedTile extends Tile{
@Override
protected void preChanged(){
super.setTeam(Team.none);
//this basically overrides the old tile code and doesn't remove from proximity
team = 0;
}
@Override
@@ -33,19 +32,14 @@ public class CachedTile extends Tile{
Block block = block();
if(block.hasEntity()){
//cache all entity types so only one is ever created per block type. do not add it.
if(!entities.containsKey(block.id)){
TileEntity n = block.newEntity();
n.cons = new ConsumeModule(entity);
n.tile = this;
if(block.hasItems) n.items = new ItemModule();
if(block.hasLiquids) n.liquids = new LiquidModule();
if(block.hasPower) n.power = new PowerModule();
entities.put(block.id, n);
}
entity = entities.get(block.id);
TileEntity n = block.newEntity();
n.cons = new ConsumeModule(entity);
n.tile = this;
n.block = block;
if(block.hasItems) n.items = new ItemModule();
if(block.hasLiquids) n.liquids = new LiquidModule();
if(block.hasPower) n.power = new PowerModule();
entity = n;
}
}
}

View File

@@ -0,0 +1,77 @@
package io.anuke.mindustry.world;
import io.anuke.annotations.Annotations.Struct;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.gen.BufferItem;
import io.anuke.mindustry.type.Item;
import java.io.*;
import static io.anuke.mindustry.Vars.content;
public class DirectionalItemBuffer{
public final long[][] buffers;
public final int[] indexes;
private final float speed;
public DirectionalItemBuffer(int capacity, float speed){
this.buffers = new long[4][capacity];
this.indexes = new int[5];
this.speed = speed;
}
public boolean accepts(int buffer){
return indexes[buffer] < buffers[buffer].length;
}
public void accept(int buffer, Item item){
if(!accepts(buffer)) return;
buffers[buffer][indexes[buffer]++] = BufferItem.get((byte)item.id, Time.time());
}
public Item poll(int buffer){
if(indexes[buffer] > 0){
long l = buffers[buffer][0];
float time = BufferItem.time(l);
if(Time.time() >= time + speed || Time.time() < time){
return content.item(BufferItem.item(l));
}
}
return null;
}
public void remove(int buffer){
System.arraycopy(buffers[buffer], 1, buffers[buffer], 0, indexes[buffer] - 1);
indexes[buffer] --;
}
public void write(DataOutput stream) throws IOException{
for(int i = 0; i < 4; i++){
stream.writeByte(indexes[i]);
stream.writeByte(buffers[i].length);
for(long l : buffers[i]){
stream.writeLong(l);
}
}
}
public void read(DataInput stream) throws IOException{
for(int i = 0; i < 4; i++){
indexes[i] = stream.readByte();
byte length = stream.readByte();
for(int j = 0; j < length; j++){
long value = stream.readLong();
if(j < buffers[i].length){
buffers[i][j] = value;
}
}
}
}
@Struct
class BufferItemStruct{
byte item;
float time;
}
}

View File

@@ -439,6 +439,6 @@ public class Tile implements Position, TargetTrait{
@Override
public String toString(){
return floor.name + ":" + block.name + ":" + content.block(overlay) + "[" + x + "," + y + "] " + "entity=" + (entity == null ? "null" : (entity.getClass()));
return floor.name + ":" + block.name + ":" + content.block(overlay) + "[" + x + "," + y + "] " + "entity=" + (entity == null ? "null" : (entity.getClass())) + ":" + getTeam();
}
}

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.world.blocks;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.IntSet;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf;
@@ -56,6 +57,7 @@ public class Floor extends Block{
protected byte eq = 0;
protected Array<Block> blenders = new Array<>();
protected IntSet blended = new IntSet();
protected TextureRegion edgeRegion, edgierRegion;
public Floor(String name){
super(name);
@@ -82,6 +84,8 @@ public class Floor extends Block{
edges = Core.atlas.find(name + "-edge").split(size, size);
}
region = variantRegions[0];
edgeRegion = Core.atlas.find("edge");
edgierRegion = Core.atlas.find("edgier");
}
@Override
@@ -140,14 +144,32 @@ public class Floor extends Block{
Point2 point = Geometry.d8[i];
Tile other = tile.getNearby(point);
if(other != null && other.floor() == block){
TextureRegion region = edge((Floor)block, type(i), 2 - (point.x + 1), 2 - (point.y + 1));
TextureRegion region = edge((Floor)block, 2 - (point.x + 1), 2 - (point.y + 1));
Draw.rect(region, tile.worldx(), tile.worldy());
if(!sameLayer && block.cacheLayer.ordinal() > cacheLayer.ordinal()){
Draw.rect(block.variantRegions()[0], tile.worldx() + point.x*tilesize, tile.worldy() + point.y*tilesize);
}
}
}
}
}
//'new' style of edges with shadows instead of colors, not used currently
protected void drawEdgesFlat(Tile tile, boolean sameLayer){
for(int i = 0; i < 4; i++){
Tile other = tile.getNearby(i);
if(other != null && doEdge(other.floor(), sameLayer)){
Color color = other.floor().color;
Draw.color(color.r, color.g, color.b, 1f);
Draw.rect(edgeRegion, tile.worldx(), tile.worldy(), i*90);
}
}
Draw.color();
}
protected TextureRegion[][] edges(){
return ((Floor)blendGroup).edges;
}
@@ -160,28 +182,12 @@ public class Floor extends Block{
return true;
}
int type(int i){
if(!eq(i - 1) && !eq(i + 1)){
//case 0: touching
return 0;
}else if(eq(i - 1) && eq(i - 2) && eq(i + 1) && eq(i + 2)){
//case 2: surrounded
return 2;
}else if(eq(i - 1) && eq(i + 1)){
//case 1: flat
return 1;
}else{
//case 0 is rounded, so it's the safest choice, should work for most possibilities
return 0;
}
}
boolean eq(int i){
return (eq & (1 << Mathf.mod(i, 8))) != 0;
}
TextureRegion edge(Floor block, int type, int x, int y){
return block.edges()[x + type * 3][2 - y];
TextureRegion edge(Floor block, int x, int y){
return block.edges()[x][2 - y];
}
}

View File

@@ -16,6 +16,8 @@ import io.anuke.mindustry.world.Tile;
import java.io.*;
import static io.anuke.mindustry.Vars.world;
public class Door extends Wall{
protected final Rectangle rect = new Rectangle();
@@ -68,6 +70,7 @@ public class Door extends Wall{
}
entity.open = !entity.open;
world.pathfinder.updateSolid(tile);
if(!entity.open){
Effects.effect(closefx, tile.drawx(), tile.drawy());
}else{

View File

@@ -1,10 +1,10 @@
package io.anuke.mindustry.world.blocks.distribution;
import io.anuke.arc.util.*;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.gen.BufferItem;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.meta.BlockGroup;
import java.io.*;
@@ -19,7 +19,6 @@ public class Junction extends Block{
super(name);
update = true;
solid = true;
instantTransfer = true;
group = BlockGroup.transportation;
}
@@ -31,18 +30,17 @@ public class Junction extends Block{
@Override
public void update(Tile tile){
JunctionEntity entity = tile.entity();
DirectionalItemBuffer buffer = entity.buffer;
for(int i = 0; i < 4; i++){
Buffer buffer = entity.buffers[i];
if(buffer.index > 0){
if(buffer.index > buffer.items.length) buffer.index = buffer.items.length;
long l = buffer.items[0];
float time = Float.intBitsToFloat(Pack.leftInt(l));
if(buffer.indexes[i] > 0){
if(buffer.indexes[i] > capacity) buffer.indexes[i] = capacity;
long l = buffer.buffers[i][0];
float time = BufferItem.time(l);
if(Time.time() >= time + speed || Time.time() < time){
Item item = content.item(Pack.rightInt(l));
Item item = content.item(BufferItem.item(l));
Tile dest = tile.getNearby(i);
//skip blocks that don't want the item, keep waiting until they do
@@ -51,8 +49,8 @@ public class Junction extends Block{
}
dest.block().handleItem(item, dest, tile);
System.arraycopy(buffer.items, 1, buffer.items, 0, buffer.index - 1);
buffer.index--;
System.arraycopy(buffer.buffers[i], 1, buffer.buffers[i], 0, buffer.indexes[i] - 1);
buffer.indexes[i] --;
}
}
}
@@ -61,9 +59,8 @@ public class Junction extends Block{
@Override
public void handleItem(Item item, Tile tile, Tile source){
JunctionEntity entity = tile.entity();
long value = Pack.longInt(Float.floatToIntBits(Time.time()), item.id);
int relative = source.relativeTo(tile.x, tile.y);
entity.buffers[relative].add(value);
entity.buffer.accept(relative, item);
}
@Override
@@ -71,7 +68,7 @@ public class Junction extends Block{
JunctionEntity entity = tile.entity();
int relative = source.relativeTo(tile.x, tile.y);
if(entity == null || relative == -1 || entity.buffers[relative].full())
if(entity == null || relative == -1 || !entity.buffer.accepts(relative))
return false;
Tile to = tile.getNearby(relative);
return to != null && to.link().entity != null;
@@ -83,55 +80,18 @@ public class Junction extends Block{
}
class JunctionEntity extends TileEntity{
Buffer[] buffers = {new Buffer(), new Buffer(), new Buffer(), new Buffer()};
DirectionalItemBuffer buffer = new DirectionalItemBuffer(capacity, speed);
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
for(Buffer b : buffers){
b.write(stream);
}
buffer.write(stream);
}
@Override
public void read(DataInput stream, byte revision) throws IOException{
super.read(stream, revision);
for(Buffer b : buffers){
b.read(stream);
}
}
}
class Buffer{
long[] items = new long[capacity];
int index;
void add(long id){
if(full()) return;
items[index++] = id;
}
boolean full(){
return index >= items.length - 1;
}
void write(DataOutput stream) throws IOException{
stream.writeByte((byte)index);
stream.writeByte((byte)items.length);
for(long l : items){
stream.writeLong(l);
}
}
void read(DataInput stream) throws IOException{
index = stream.readByte();
byte length = stream.readByte();
for(int i = 0; i < length; i++){
long l = stream.readLong();
if(i < items.length){
items[i] = l;
}
}
buffer.read(stream);
}
}
}

View File

@@ -78,7 +78,7 @@ public class MassDriver extends Block{
//reload regardless of state
if(entity.reload > 0f){
entity.reload = Mathf.clamp(entity.reload - entity.delta() / reloadTime);
entity.reload = Mathf.clamp(entity.reload - entity.delta() / reloadTime * entity.power.satisfaction);
}
//cleanup waiting shooters that are not valid
@@ -114,7 +114,7 @@ public class MassDriver extends Block{
}
//align to shooter rotation
entity.rotation = Mathf.slerpDelta(entity.rotation, tile.angleTo(entity.currentShooter()), rotateSpeed);
entity.rotation = Mathf.slerpDelta(entity.rotation, tile.angleTo(entity.currentShooter()), rotateSpeed * entity.power.satisfaction);
}else if(entity.state == DriverState.shooting){
//if there's nothing to shoot at OR someone wants to shoot at this thing, bail
if(!hasLink || !entity.waitingShooters.isEmpty()){
@@ -133,7 +133,7 @@ public class MassDriver extends Block{
other.waitingShooters.add(tile);
//align to target location
entity.rotation = Mathf.slerpDelta(entity.rotation, targetRotation, rotateSpeed);
entity.rotation = Mathf.slerpDelta(entity.rotation, targetRotation, rotateSpeed * entity.power.satisfaction);
//fire when it's the first in the queue and angles are ready.
if(other.currentShooter() == tile &&
@@ -214,7 +214,8 @@ public class MassDriver extends Block{
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
return tile.entity.items.total() < itemCapacity;
//mass drivers that ouput only cannot accept items
return tile.entity.items.total() < itemCapacity && linkValid(tile);
}
@Override

View File

@@ -1,41 +1,56 @@
package io.anuke.mindustry.world.blocks.distribution;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Edges;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.meta.BlockGroup;
public class OverflowGate extends Router{
import java.io.*;
public class OverflowGate extends Block{
protected int bufferCapacity = 10;
protected float speed = 45f;
public OverflowGate(String name){
super(name);
hasItems = true;
speed = 1f;
solid = true;
update = true;
group = BlockGroup.transportation;
}
@Override
public void update(Tile tile){
SplitterEntity entity = tile.entity();
OverflowGateEntity entity = tile.entity();
if(entity.lastItem == null && entity.items.total() > 0){
entity.items.clear();
}
if(entity.lastItem != null){
entity.time += 1f / speed * Time.delta();
Tile target = getTileTarget(tile, entity.lastItem, entity.lastInput, false);
if(target != null && (entity.time >= 1f)){
getTileTarget(tile, entity.lastItem, entity.lastInput, true);
target.block().handleItem(entity.lastItem, target, Edges.getFacingEdge(tile, target));
entity.items.remove(entity.lastItem, 1);
entity.lastItem = null;
for(int i = 0; i < 4; i++){
Item item = entity.buffer.poll(i);
if(item != null){
Tile other = getTileTarget(tile, item, tile.getNearby(i), true);
if(other != null && other.block().acceptItem(item, other, tile)){
other.block().handleItem(item, other, tile);
entity.buffer.remove(i);
}
}
}
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
OverflowGateEntity entity = tile.entity();
return entity.buffer.accepts(tile.relativeTo(source.x, source.y));
}
@Override
public void handleItem(Item item, Tile tile, Tile source){
OverflowGateEntity entity = tile.entity();
int buffer = tile.relativeTo(source.x, source.y);
if(entity.buffer.accepts(buffer)){
entity.buffer.accept(buffer, item);
}
}
Tile getTileTarget(Tile tile, Item item, Tile src, boolean flip){
int from = tile.relativeTo(src.x, src.y);
if(from == -1) return null;
@@ -70,4 +85,32 @@ public class OverflowGate extends Router{
return to;
}
@Override
public TileEntity newEntity(){
return new OverflowGateEntity();
}
public class OverflowGateEntity extends TileEntity{
DirectionalItemBuffer buffer = new DirectionalItemBuffer(bufferCapacity, speed);
@Override
public byte version(){
return 1;
}
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
buffer.write(stream);
}
@Override
public void read(DataInput stream, byte revision) throws IOException{
super.read(stream, revision);
if(revision == 1){
buffer.read(stream);
}
}
}
}

View File

@@ -21,7 +21,7 @@ public class Router extends Block{
@Override
public void update(Tile tile){
SplitterEntity entity = tile.entity();
RouterEntity entity = tile.entity();
if(entity.lastItem == null && entity.items.total() > 0){
entity.items.clear();
@@ -42,14 +42,14 @@ public class Router extends Block{
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
SplitterEntity entity = tile.entity();
RouterEntity entity = tile.entity();
return tile.getTeam() == source.getTeam() && entity.lastItem == null && entity.items.total() == 0;
}
@Override
public void handleItem(Item item, Tile tile, Tile source){
SplitterEntity entity = tile.entity();
RouterEntity entity = tile.entity();
entity.items.add(item, 1);
entity.lastItem = item;
entity.time = 0f;
@@ -72,7 +72,7 @@ public class Router extends Block{
@Override
public int removeStack(Tile tile, Item item, int amount){
SplitterEntity entity = tile.entity();
RouterEntity entity = tile.entity();
int result = super.removeStack(tile, item, amount);
if(result != 0 && item == entity.lastItem){
entity.lastItem = null;
@@ -82,10 +82,10 @@ public class Router extends Block{
@Override
public TileEntity newEntity(){
return new SplitterEntity();
return new RouterEntity();
}
public class SplitterEntity extends TileEntity{
public class RouterEntity extends TileEntity{
Item lastItem;
Tile lastInput;
float time;

View File

@@ -1,7 +1,6 @@
package io.anuke.mindustry.world.blocks.distribution;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.annotations.Annotations.*;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.math.Mathf;
@@ -10,8 +9,7 @@ import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.ItemSelection;
import io.anuke.mindustry.world.meta.BlockGroup;
@@ -22,11 +20,13 @@ import static io.anuke.mindustry.Vars.content;
public class Sorter extends Block{
private static Item lastItem;
protected int bufferCapacity = 20;
protected float speed = 45f;
public Sorter(String name){
super(name);
update = true;
solid = true;
instantTransfer = true;
group = BlockGroup.transportation;
configurable = true;
}
@@ -57,24 +57,42 @@ public class Sorter extends Block{
if(entity.sortItem == null) return;
Draw.color(entity.sortItem.color);
Draw.rect("blank", tile.worldx(), tile.worldy(), 4f, 4f);
Draw.rect("center", tile.worldx(), tile.worldy());
Draw.color();
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
Tile to = getTileTarget(item, tile, source, false);
public void update(Tile tile){
SorterEntity entity = tile.entity();
return to != null && to.block().acceptItem(item, to, tile);
for(int i = 0; i < 4; i++){
Item item = entity.buffer.poll(i);
if(item != null){
Tile other = getTileTarget(item, tile, tile.getNearby(i), true);
if(other != null && other.block().acceptItem(item, other, tile)){
other.block().handleItem(item, other, tile);
entity.buffer.remove(i);
}
}
}
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
SorterEntity entity = tile.entity();
return entity.buffer.accepts(tile.relativeTo(source.x, source.y));
}
@Override
public void handleItem(Item item, Tile tile, Tile source){
Tile to = getTileTarget(item, tile, source, true);
to.block().handleItem(item, to, tile);
SorterEntity entity = tile.entity();
int buffer = tile.relativeTo(source.x, source.y);
if(entity.buffer.accepts(buffer)){
entity.buffer.accept(buffer, item);
}
}
@Nullable
Tile getTileTarget(Item item, Tile dest, Tile source, boolean flip){
SorterEntity entity = dest.entity();
@@ -87,10 +105,8 @@ public class Sorter extends Block{
}else{
Tile a = dest.getNearby(Mathf.mod(dir - 1, 4));
Tile b = dest.getNearby(Mathf.mod(dir + 1, 4));
boolean ac = a != null && !(a.block().instantTransfer && source.block().instantTransfer) &&
a.block().acceptItem(item, a, dest);
boolean bc = b != null && !(b.block().instantTransfer && source.block().instantTransfer) &&
b.block().acceptItem(item, b, dest);
boolean ac = a != null && a.block().acceptItem(item, a, dest);
boolean bc = b != null && b.block().acceptItem(item, b, dest);
if(ac && !bc){
to = a;
@@ -101,12 +117,10 @@ public class Sorter extends Block{
}else{
if(dest.rotation() == 0){
to = a;
if(flip)
dest.rotation((byte)1);
if(flip) dest.rotation((byte)1);
}else{
to = b;
if(flip)
dest.rotation((byte)0);
if(flip) dest.rotation((byte)0);
}
}
}
@@ -128,19 +142,29 @@ public class Sorter extends Block{
return new SorterEntity();
}
public static class SorterEntity extends TileEntity{
public Item sortItem;
public class SorterEntity extends TileEntity{
DirectionalItemBuffer buffer = new DirectionalItemBuffer(bufferCapacity, speed);
Item sortItem;
@Override
public byte version(){
return 1;
}
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
stream.writeShort(sortItem == null ? -1 : sortItem.id);
buffer.write(stream);
}
@Override
public void read(DataInput stream, byte revision) throws IOException{
super.read(stream, revision);
sortItem = content.item(stream.readShort());
if(revision == 1){
buffer.read(stream);
}
}
}
}

View File

@@ -1,16 +1,46 @@
package io.anuke.mindustry.world.blocks.sandbox;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.arc.Core;
import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.distribution.Sorter;
import io.anuke.mindustry.world.blocks.ItemSelection;
import io.anuke.mindustry.world.meta.BlockGroup;
public class ItemSource extends Sorter{
import java.io.*;
import static io.anuke.mindustry.Vars.content;
public class ItemSource extends Block{
private static Item lastItem;
public ItemSource(String name){
super(name);
hasItems = true;
update = true;
solid = true;
group = BlockGroup.transportation;
configurable = true;
}
@Remote(targets = Loc.both, called = Loc.both, forward = true)
public static void setItemSourceItem(Player player, Tile tile, Item item){
ItemSourceEntity entity = tile.entity();
if(entity != null){
entity.outputItem = item;
}
}
@Override
public void playerPlaced(Tile tile){
Core.app.post(() -> Call.setItemSourceItem(null, tile, lastItem));
}
@Override
@@ -25,17 +55,59 @@ public class ItemSource extends Sorter{
}
@Override
public void update(Tile tile){
SorterEntity entity = tile.entity();
if(entity.sortItem == null) return;
public void draw(Tile tile){
super.draw(tile);
entity.items.set(entity.sortItem, 1);
tryDump(tile, entity.sortItem);
entity.items.set(entity.sortItem, 0);
ItemSourceEntity entity = tile.entity();
if(entity.outputItem == null) return;
Draw.color(entity.outputItem.color);
Draw.rect("center", tile.worldx(), tile.worldy());
Draw.color();
}
@Override
public void update(Tile tile){
ItemSourceEntity entity = tile.entity();
if(entity.outputItem == null) return;
entity.items.set(entity.outputItem, 1);
tryDump(tile, entity.outputItem);
entity.items.set(entity.outputItem, 0);
}
@Override
public void buildTable(Tile tile, Table table){
ItemSourceEntity entity = tile.entity();
ItemSelection.buildItemTable(table, () -> entity.outputItem, item -> {
lastItem = item;
Call.setItemSourceItem(null, tile, item);
});
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
return false;
}
@Override
public TileEntity newEntity(){
return new ItemSourceEntity();
}
public class ItemSourceEntity extends TileEntity{
Item outputItem;
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
stream.writeShort(outputItem == null ? -1 : outputItem.id);
}
@Override
public void read(DataInput stream, byte revision) throws IOException{
super.read(stream, revision);
outputItem = content.item(stream.readShort());
}
}
}

View File

@@ -66,7 +66,7 @@ public class LiquidSource extends Block{
if(entity.source != null){
Draw.color(entity.source.color);
Draw.rect("blank", tile.worldx(), tile.worldy(), 4f, 4f);
Draw.rect("center", tile.worldx(), tile.worldy());
Draw.color();
}
}

View File

@@ -81,8 +81,8 @@ public class MechPad extends Block{
protected static boolean checkValidTap(Tile tile, Player player){
MechFactoryEntity entity = tile.entity();
return Math.abs(player.x - tile.drawx()) <= tile.block().size * tilesize / 2f &&
Math.abs(player.y - tile.drawy()) <= tile.block().size * tilesize / 2f && entity.cons.valid() && entity.player == null;
return Math.abs(player.x - tile.drawx()) <= tile.block().size * tilesize &&
Math.abs(player.y - tile.drawy()) <= tile.block().size * tilesize && entity.cons.valid() && entity.player == null;
}
@Override