Merge remote-tracking branch 'origin/master'

This commit is contained in:
Anuken
2019-09-19 19:02:05 -04:00
41 changed files with 267 additions and 144 deletions

View File

@@ -40,6 +40,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
batch = new SpriteBatch();
assets = new AssetManager();
assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader());
assets.load("sprites/error.png", Texture.class);
atlas = TextureAtlas.blankAtlas();
Vars.net = new Net(platform.getNet());

View File

@@ -122,8 +122,6 @@ public class Vars implements Loadable{
public static FileHandle saveDirectory;
/** data subdirectory used for plugins */
public static FileHandle pluginDirectory;
/** old map file extension, for conversion */
public static final String oldMapExtension = "mmap";
/** map file extension */
public static final String mapExtension = "msav";
/** save file extension */
@@ -139,7 +137,7 @@ public class Vars implements Loadable{
public static EntityCollisions collisions;
public static DefaultWaves defaultWaves;
public static LoopControl loops;
public static Platform platform;
public static Platform platform = new Platform(){};
public static Plugins plugins;
public static World world;

View File

@@ -46,6 +46,7 @@ public class StatusEffects implements ContentList{
if(unit.getTeam() == waveTeam){
Events.fire(Trigger.shock);
}
result.set(this, time);
}));
opposite(() -> burning);
}};

View File

@@ -371,11 +371,13 @@ public class Control implements ApplicationListener, Loadable{
@Override
public void update(){
//TODO find out why this happens on Android
if(assets == null) return;
saves.update();
//update and load any requested assets
if(assets != null){
assets.update();
}
assets.update();
input.updateController();

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.core;
import io.anuke.arc.*;
import io.anuke.arc.Input.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.*;
import io.anuke.arc.function.*;
import io.anuke.arc.math.*;
@@ -25,6 +26,14 @@ public interface Platform{
/** Steam: Share a map on the workshop.*/
default void publishMap(Map map){}
/** Steam: Return external workshop maps to be loaded.*/
default Array<FileHandle> getExternalMaps(){
return Array.with();
}
/** Steam: View a map listing on the workshop.*/
default void viewMapListing(Map map){}
/** Steam: Open workshop for maps.*/
default void openWorkshop(){}

View File

@@ -325,8 +325,9 @@ public class UI implements ApplicationListener, Loadable{
cont.row();
cont.addImage().width(300f).pad(2).height(4f).color(Color.scarlet);
cont.row();
cont.add(text).pad(2f).growX().wrap();
buttons.addButton("$ok", this::hide).size(120, 50).pad(4);
cont.add(text).pad(2f).growX().wrap().get().setAlignment(Align.center);
cont.row();
cont.addButton("$ok", this::hide).size(120, 50).pad(4);
}}.show();
}

View File

@@ -154,10 +154,21 @@ public class MapEditorDialog extends Dialog implements Disposable{
if(steam){
menu.cont.addImageTextButton("$editor.publish.workshop", Icon.linkSmall, () -> {
Map map = save();
if(map != null){
platform.publishMap(map);
if(map == null) return;
if(map.tags.get("description", "").length() < 4){
ui.showErrorMessage("$editor.nodescription");
return;
}
}).padTop(-3).size(swidth * 2f + 10, 60f);
if(!Gamemode.survival.valid(map)){
ui.showErrorMessage("$map.nospawn");
return;
}
platform.publishMap(map);
}).padTop(-3).size(swidth * 2f + 10, 60f).update(b -> b.setText(editor.getTags().containsKey("steamid") ? "$view.workshop" : "$editor.publish.workshop"));
menu.cont.row();
}
@@ -276,7 +287,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
});
}
private Map save(){
public Map save(){
String name = editor.getTags().get("name", "").trim();
editor.getTags().put("rules", JsonIO.write(state.rules));
editor.getTags().remove("width");

View File

@@ -54,7 +54,7 @@ public class MapLoadDialog extends FloatingDialog{
for(Map map : maps.all()){
TextButton button = new TextButton(map.name(), Styles.togglet);
button.add(new BorderImage(map.texture, 2f).setScaling(Scaling.fit)).size(16 * 4f);
button.add(new BorderImage(map.safeTexture(), 2f).setScaling(Scaling.fit)).size(16 * 4f);
button.getCells().reverse();
button.clicked(() -> selected = map);
button.getLabelCell().grow().left().padLeft(5f);

View File

@@ -18,6 +18,8 @@ public class EntityGroup<T extends Entity>{
private final Array<T> entityArray = new Array<>(false, 32);
private final Array<T> entitiesToRemove = new Array<>(false, 32);
private final Array<T> entitiesToAdd = new Array<>(false, 32);
private final Array<T> intersectArray = new Array<>();
private final Rectangle intersectRect = new Rectangle();
private IntMap<T> map;
private QuadTree tree;
private Consumer<T> removeListener;
@@ -161,6 +163,15 @@ public class EntityGroup<T extends Entity>{
tree().getIntersect(out, x, y, width, height);
}
@SuppressWarnings("unchecked")
public Array<T> intersect(float x, float y, float width, float height){
intersectArray.clear();
//don't waste time for empty groups
if(isEmpty()) return intersectArray;
tree().getIntersect(intersectArray, intersectRect.set(x, y, width, height));
return intersectArray;
}
public QuadTree tree(){
if(!useTree) throw new RuntimeException("This group does not support quadtrees! Enable quadtrees when creating it.");
return tree;

View File

@@ -47,7 +47,7 @@ public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{
}
/** Do not invoke! */
@Remote(called = Loc.server)
@Remote(called = Loc.server, unreliable = true)
public static void createLighting(int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){
Lightning l = Pools.obtain(Lightning.class, Lightning::new);

View File

@@ -16,6 +16,7 @@ import io.anuke.mindustry.game.*;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.*;
import io.anuke.mindustry.world.blocks.defense.DeflectorWall.*;
import io.anuke.mindustry.world.blocks.units.CommandCenter.*;
import io.anuke.mindustry.world.blocks.units.UnitFactory.*;
@@ -273,7 +274,7 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return;
}
if(!isFlying() && (world.tileWorld(x, y) != null && world.tileWorld(x, y).solid())){
if(!isFlying() && (world.tileWorld(x, y) != null && !(world.tileWorld(x, y).block() instanceof BuildBlock) && world.tileWorld(x, y).solid())){
kill();
}

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.entities.type;
import io.anuke.annotations.Annotations.*;
import io.anuke.arc.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.*;
@@ -212,15 +213,25 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
float radScl = 1.5f;
float fsize = getSize() / radScl;
moveVector.setZero();
float cx = x - fsize/2f, cy = y - fsize/2f;
Units.nearby(x - fsize/2f, y - fsize/2f, fsize, fsize, en -> {
if(en == this || en.isFlying() != isFlying()) return;
for(Team team : Team.all){
avoid(unitGroups[team.ordinal()].intersect(cx, cy, fsize, fsize));
}
avoid(playerGroup.intersect(cx, cy, fsize, fsize));
velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta());
}
private void avoid(Array<? extends Unit> arr){
float radScl = 1.5f;
for(Unit en : arr){
if(en.isFlying() != isFlying()) continue;
float dst = dst(en);
float scl = Mathf.clamp(1f - dst / (getSize()/(radScl*2f) + en.getSize()/(radScl*2f)));
moveVector.add(Tmp.v1.set((x - en.x) * scl, (y - en.y) * scl).limit(0.4f));
});
velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta());
}
}
public @Nullable TileEntity getClosestCore(){

View File

@@ -32,12 +32,12 @@ public class Statuses implements Saveable{
if(statuses.size > 0){
//check for opposite effects
for(StatusEntry entry : statuses){
if(entry.effect == null) continue;
//extend effect
if(entry.effect == effect){
entry.time = Math.max(entry.time, duration);
return;
}else if(entry.effect.reactsWith(effect)){ //find opposite
globalResult.effect = entry.effect;
entry.effect.getTransition(unit, effect, entry.time, duration, globalResult);
entry.time = globalResult.time;
@@ -84,10 +84,6 @@ public class Statuses implements Saveable{
removals.clear();
for(StatusEntry entry : statuses){
if(entry.effect == null){
removals.add(entry);
continue;
}
entry.time = Math.max(entry.time - Time.delta(), 0);
applied.set(entry.effect.id);

View File

@@ -187,7 +187,7 @@ public class Tutorial{
}
},;
protected final String line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name();
protected String line = "";
protected final Function<String, String> text;
protected Array<String> sentences;
protected final BooleanProvider done;
@@ -203,7 +203,10 @@ public class Tutorial{
/** displayed tutorial stage text.*/
public String text(){
if(sentences == null) this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
if(sentences == null){
this.line = Core.bundle.has("tutorial." + name() + ".mobile") && mobile ? "tutorial." + name() + ".mobile" : "tutorial." + name();
this.sentences = Array.select(Core.bundle.get(line).split("\n"), s -> !s.isEmpty());
}
String line = sentences.get(control.tutorial.sentence);
return line.contains("{") ? text.get(line) : line;
}

View File

@@ -58,7 +58,7 @@ public abstract class InputHandler implements InputProcessor{
@Remote(targets = Loc.both, forward = true, called = Loc.server)
public static void transferInventory(Player player, Tile tile){
if(!player.timer.get(Player.timerTransfer, 40)) return;
if(player == null || player.timer == null || !player.timer.get(Player.timerTransfer, 40)) return;
if(net.server() && (player.item().amount <= 0 || player.isTransferring|| !tile.interactable(player.getTeam()))){
throw new ValidateException(player, "Player cannot transfer an item.");
}

View File

@@ -67,33 +67,7 @@ public class MobileInput extends InputHandler implements GestureListener{
public MobileInput(){
Events.on(ClientLoadEvent.class, e -> {
GestureDetector dec = new GestureDetector(20, 0.5f, 0.4f, 0.15f, this){
boolean clearMouse = false;
@Override
public boolean touchDown(int x, int y, int pointer, KeyCode button){
if(Core.scene.hasMouse(x, y)){
clearMouse = true;
return false;
}
return super.touchDown(x, y, pointer, button);
}
@Override
public boolean touchDragged(int x, int y, int pointer){
if(!clearMouse){
return super.touchDragged(x, y, pointer);
}
return false;
}
@Override
public boolean touchUp(int x, int y, int pointer, KeyCode button){
clearMouse = false;
return super.touchUp(x, y, pointer, button);
}
};
Core.input.getInputProcessors().insert(0, dec);
Core.input.getInputProcessors().add(new GestureDetector(20, 0.5f, 0.4f, 0.15f, this));
});
}

View File

@@ -20,6 +20,8 @@ public class Map implements Comparable<Map>{
public final FileHandle file;
/** Format version. */
public final int version;
/** Whether this map is managed, e.g. downloaded from the Steam workshop.*/
public boolean workshop;
/** Map width/height, shorts. */
public int width, height;
/** Preview texture. */
@@ -57,8 +59,12 @@ public class Map implements Comparable<Map>{
return Core.settings.getInt("hiscore" + file.nameWithoutExtension(), 0);
}
public Texture safeTexture(){
return texture == null ? Core.assets.get("sprites/error.png") : texture;
}
public FileHandle previewFile(){
return Vars.mapPreviewDirectory.child(file.nameWithoutExtension() + ".png");
return Vars.mapPreviewDirectory.child((workshop ? file.parent().name() : file.nameWithoutExtension()) + ".png");
}
public FileHandle cacheFile(){
@@ -127,6 +133,8 @@ public class Map implements Comparable<Map>{
@Override
public int compareTo(Map map){
int work = -Boolean.compare(workshop, map.workshop);
if(work != 0) return work;
int type = -Boolean.compare(custom, map.custom);
if(type != 0) return type;
int modes = Boolean.compare(Gamemode.pvp.valid(this), Gamemode.pvp.valid(map));

View File

@@ -6,6 +6,7 @@ import io.anuke.arc.assets.loaders.resolvers.*;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.util.*;
import io.anuke.mindustry.*;
import io.anuke.mindustry.game.*;
@@ -20,7 +21,7 @@ public class MapPreviewLoader extends TextureLoader{
try{
super.loadAsync(manager, fileName, file.sibling(file.nameWithoutExtension()), parameter);
}catch(Exception e){
e.printStackTrace();
Log.err(e);
MapPreviewParameter param = (MapPreviewParameter)parameter;
Vars.maps.queueNewPreview(param.map);
}
@@ -31,11 +32,11 @@ public class MapPreviewLoader extends TextureLoader{
try{
return super.loadSync(manager, fileName, file, parameter);
}catch(Throwable e){
e.printStackTrace();
Log.err(e);
try{
return new Texture(file);
}catch(Throwable e2){
e2.printStackTrace();
Log.err(e2);
return new Texture("sprites/error.png");
}
}

View File

@@ -81,6 +81,7 @@ public class Maps{
/** Load all maps. Should be called at application start. */
public void load(){
//defaults; must work
try{
for(String name : defaultMapNames){
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
@@ -90,7 +91,27 @@ public class Maps{
throw new RuntimeException(e);
}
loadCustomMaps();
//custom
for(FileHandle file : customMapDirectory.list()){
try{
if(file.extension().equalsIgnoreCase(mapExtension)){
loadMap(file, true);
}
}catch(Exception e){
Log.err("Failed to load custom map file '{0}'!", file);
Log.err(e);
}
}
//workshop
for(FileHandle file : platform.getExternalMaps()){
try{
loadMap(file, false).workshop = true;
}catch(Exception e){
Log.err("Failed to load workshop map file '{0}'!", file);
Log.err(e);
}
}
}
public void reload(){
@@ -174,14 +195,6 @@ public class Maps{
}
}
/** 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();
@@ -203,7 +216,6 @@ public class Maps{
if(error[0] != null){
throw new IOException(error[0]);
}
}
/** Attempts to run the following code;
@@ -314,7 +326,7 @@ public class Maps{
for(Map map : maps){
//try to load preview
if(map.previewFile().exists()){
//this may fail, but calls createNewPreview
//this may fail, but calls queueNewPreview
Core.assets.load(new AssetDescriptor<>(map.previewFile().path() + "." + mapExtension, Texture.class, new MapPreviewParameter(map))).loaded = t -> map.texture = (Texture)t;
try{
@@ -332,7 +344,7 @@ public class Maps{
private void createAllPreviews(){
Core.app.post(() -> {
for(Map map : previewList){
createNewPreview(map, e -> Core.app.post(() -> map.texture = new Texture("sprites/error.png")));
createNewPreview(map, e -> Core.app.post(() -> map.texture = Core.assets.get("sprites/error.png")));
}
previewList.clear();
});
@@ -407,16 +419,4 @@ public class Maps{
return map;
}
private void loadCustomMaps(){
for(FileHandle file : customMapDirectory.list()){
try{
if(file.extension().equalsIgnoreCase(mapExtension)){
loadMap(file, true);
}
}catch(Exception e){
Log.err("Failed to load custom map file '{0}'!", file);
Log.err(e);
}
}
}
}

View File

@@ -25,8 +25,8 @@ public class CrashSender{
try{
exception.printStackTrace();
//don't create crash logs for me (anuke) or custom builds, as it's expected
if(System.getProperty("user.name").equals("anuke") || Version.build == -1) return;
//don't create crash logs for custom builds, as it's expected
if(Version.build == -1) return;
//attempt to load version regardless
if(Version.number == 0){

View File

@@ -46,7 +46,9 @@ public class Net{
t = t.getCause();
}
String error = t.getMessage() == null ? "" : t.getMessage().toLowerCase();
String baseError = Strings.getFinalMesage(e);
String error = baseError == null ? "" : baseError.toLowerCase();
String type = t.getClass().toString().toLowerCase();
boolean isError = false;

View File

@@ -67,7 +67,7 @@ public class Plugins{
URLClassLoader classLoader = new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, ClassLoader.getSystemClassLoader());
Class<?> main = classLoader.loadClass(meta.main);
metas.put(main, meta);
return new LoadedPlugin(jar, zip, (Plugin)main.newInstance(), meta);
return new LoadedPlugin(jar, zip, (Plugin)main.getDeclaredConstructor().newInstance(), meta);
}
/** Represents a plugin that has been loaded from a jar file.*/

View File

@@ -5,7 +5,7 @@ import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.function.Supplier;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.*;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.Effects;
import io.anuke.mindustry.entities.Effects.Effect;

View File

@@ -48,7 +48,7 @@ public class CustomGameDialog extends FloatingDialog{
maps.row();
}
ImageButton image = new ImageButton(new TextureRegion(map.texture), Styles.cleari);
ImageButton image = new ImageButton(new TextureRegion(map.safeTexture()), Styles.cleari);
image.margin(5);
image.top();
@@ -72,7 +72,7 @@ public class CustomGameDialog extends FloatingDialog{
image.add(img).size(images);
BorderImage border = new BorderImage(map.texture, 3f);
BorderImage border = new BorderImage(map.safeTexture(), 3f);
border.setScaling(Scaling.fit);
image.replaceImage(border);

View File

@@ -69,7 +69,7 @@ public class MapPlayDialog extends FloatingDialog{
cont.row();
cont.addImageTextButton("$customize", Icon.toolsSmall, () -> dialog.show(rules, () -> rules = map.applyRules(selectedGamemode))).width(230);
cont.row();
cont.add(new BorderImage(map.texture, 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit);
cont.add(new BorderImage(map.safeTexture(), 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit);
//only maps with survival are valid for high scores
if(Gamemode.survival.valid(map)){
cont.row();

View File

@@ -4,7 +4,6 @@ import io.anuke.arc.*;
import io.anuke.arc.graphics.*;
import io.anuke.arc.input.*;
import io.anuke.arc.math.*;
import io.anuke.arc.scene.event.*;
import io.anuke.arc.scene.ui.*;
import io.anuke.arc.scene.ui.layout.*;
import io.anuke.arc.util.*;
@@ -53,12 +52,18 @@ public class MapsDialog extends FloatingDialog{
buttons.addImageTextButton("$editor.newmap", Icon.add, () -> {
ui.showTextInput("$editor.newmap", "$name", "", text -> {
ui.loadAnd(() -> {
Runnable show = () -> ui.loadAnd(() -> {
hide();
ui.editor.show();
ui.editor.editor.getTags().put("name", text);
Events.fire(new MapMakeEvent());
});
if(maps.byName(text) != null){
ui.showErrorMessage("$editor.exists");
}else{
show.run();
}
});
}).size(210f, 64f);
@@ -137,9 +142,9 @@ public class MapsDialog extends FloatingDialog{
button.row();
button.addImage().growX().pad(4).color(Pal.gray);
button.row();
button.stack(new Image(map.texture).setScaling(Scaling.fit), new BorderImage(map.texture).setScaling(Scaling.fit)).size(mapsize - 20f);
button.stack(new Image(map.safeTexture()).setScaling(Scaling.fit), new BorderImage(map.safeTexture()).setScaling(Scaling.fit)).size(mapsize - 20f);
button.row();
button.add(map.custom ? "$custom" : "$builtin").color(Color.gray).padTop(3);
button.add(map.custom ? "$custom" : map.workshop ? "$workshop" : "$builtin").color(Color.gray).padTop(3);
i++;
}
@@ -160,7 +165,7 @@ public class MapsDialog extends FloatingDialog{
float mapsize = Core.graphics.isPortrait() ? 160f : 300f;
Table table = dialog.cont;
table.stack(new Image(map.texture).setScaling(Scaling.fit), new BorderImage(map.texture).setScaling(Scaling.fit)).size(mapsize);
table.stack(new Image(map.safeTexture()).setScaling(Scaling.fit), new BorderImage(map.safeTexture()).setScaling(Scaling.fit)).size(mapsize);
table.table(Styles.black, desc -> {
desc.top();
@@ -199,13 +204,17 @@ public class MapsDialog extends FloatingDialog{
}
}).fillX().height(54f).marginLeft(10);
table.addImageTextButton("$delete", Icon.trash16Small, () -> {
ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> {
maps.removeMap(map);
dialog.hide();
setup();
});
}).fillX().height(54f).marginLeft(10).disabled(!map.custom).touchable(map.custom ? Touchable.enabled : Touchable.disabled);
table.addImageTextButton(map.workshop ? "$view.workshop" : "$delete", map.workshop ? Icon.linkSmall : Icon.trash16Small, () -> {
if(map.workshop){
platform.viewMapListing(map);
}else{
ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> {
maps.removeMap(map);
dialog.hide();
setup();
});
}
}).fillX().height(54f).marginLeft(10).disabled(!map.workshop && !map.custom);
dialog.show();
}

View File

@@ -32,7 +32,7 @@ import static io.anuke.mindustry.Vars.*;
public class BlockInventoryFragment extends Fragment{
private final static float holdWithdraw = 20f;
private Table table;
private Table table = new Table();
private Tile tile;
private float holdTime = 0f;
private boolean holding;
@@ -52,7 +52,6 @@ public class BlockInventoryFragment extends Fragment{
@Override
public void build(Group parent){
table = new Table();
table.setName("inventory");
table.setTransform(true);
parent.setTransform(true);

View File

@@ -572,9 +572,6 @@ public class HudFragment extends Fragment{
}
shown = !shown;
if(flip != null){
flip.getParent().act(Core.graphics.getDeltaTime());
}
}
private void addWaveTable(Button table){

View File

@@ -55,7 +55,7 @@ public class CooledTurret extends Turret{
Liquid liquid = entity.liquids.current();
float used = Math.min(Math.min(entity.liquids.get(liquid), maxUsed * Time.delta()), Math.max(0, ((reload - entity.reload) / coolantMultiplier) / liquid.heatCapacity)) * baseReloadSpeed(tile);
entity.reload += (used * liquid.heatCapacity) / liquid.heatCapacity;
entity.reload += used * liquid.heatCapacity * coolantMultiplier;
entity.liquids.remove(liquid, used);
if(Mathf.chance(0.06 * used)){

View File

@@ -100,7 +100,7 @@ public abstract class Turret extends Block{
stats.add(BlockStat.shootRange, range / tilesize, StatUnit.blocks);
stats.add(BlockStat.inaccuracy, (int)inaccuracy, StatUnit.degrees);
stats.add(BlockStat.reload, 60f / reload * shots, StatUnit.none);
stats.add(BlockStat.reload, 60f / reload, StatUnit.none);
stats.add(BlockStat.shots, shots, StatUnit.none);
stats.add(BlockStat.targetsAir, targetAir);
stats.add(BlockStat.targetsGround, targetGround);

View File

@@ -24,7 +24,6 @@ public class ItemBridge extends Block{
protected int timerTransport = timers++;
protected int range;
protected float transportTime = 2f;
protected IntArray removals = new IntArray();
protected TextureRegion endRegion, bridgeRegion, arrowRegion;
private static int lastPlaced = Pos.invalid;
@@ -162,21 +161,15 @@ public class ItemBridge extends Block{
entity.time += entity.cycleSpeed * entity.delta();
entity.time2 += (entity.cycleSpeed - 1f) * entity.delta();
removals.clear();
IntSetIterator it = entity.incoming.iterator();
while(it.hasNext){
int i = it.next();
Tile other = world.tile(i);
if(!linkValid(tile, other, false)){
removals.add(i);
it.remove();
}
}
for(int j = 0; j < removals.size; j++)
entity.incoming.remove(removals.get(j));
Tile other = world.tile(entity.link);
if(!linkValid(tile, other)){
entity.link = Pos.invalid;
@@ -184,7 +177,7 @@ public class ItemBridge extends Block{
entity.uptime = 0f;
}else{
if(entity.cons.valid()){
if(entity.cons.valid() && (!hasPower || Mathf.isZero(1f - entity.power.satisfaction))){
entity.uptime = Mathf.lerpDelta(entity.uptime, 1f, 0.04f);
}else{
entity.uptime = Mathf.lerpDelta(entity.uptime, 0f, 0.02f);

View File

@@ -12,7 +12,6 @@ import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.*;
import io.anuke.mindustry.entities.Effects.*;
import io.anuke.mindustry.entities.type.*;
import io.anuke.mindustry.entities.type.Bullet;
import io.anuke.mindustry.gen.*;
import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.type.*;
@@ -120,26 +119,28 @@ public class MassDriver extends Block{
if(
tile.entity.items.total() >= minDistribute && //must shoot minimum amount of items
link.block().itemCapacity - link.entity.items.total() >= minDistribute && //must have minimum amount of space
entity.reload <= 0.0001f //must have reloaded
link.block().itemCapacity - link.entity.items.total() >= minDistribute //must have minimum amount of space
){
MassDriverEntity other = link.entity();
other.waitingShooters.add(tile);
//align to target location
entity.rotation = Mathf.slerpDelta(entity.rotation, targetRotation, rotateSpeed * entity.power.satisfaction);
if(entity.reload <= 0.0001f){
//fire when it's the first in the queue and angles are ready.
if(other.currentShooter() == tile &&
//align to target location
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 &&
other.state == DriverState.accepting &&
Angles.near(entity.rotation, targetRotation, 2f) && Angles.near(other.rotation, targetRotation + 180f, 2f)){
//actually fire
fire(tile, link);
//remove waiting shooters, it's done firing
other.waitingShooters.remove(tile);
//set both states to idle
entity.state = DriverState.idle;
other.state = DriverState.idle;
//actually fire
fire(tile, link);
//remove waiting shooters, it's done firing
other.waitingShooters.remove(tile);
//set both states to idle
entity.state = DriverState.idle;
other.state = DriverState.idle;
}
}
}
}

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.world.blocks.distribution;
import io.anuke.arc.collection.Array;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.world.*;
@@ -63,7 +64,7 @@ public class Router extends Block{
for(int i = 0; i < proximity.size; i++){
Tile other = proximity.get((i + counter) % proximity.size);
if(set) tile.rotation((byte)((tile.rotation() + 1) % proximity.size));
if(other == from) continue;
if(other == from && from.block() == Blocks.overflowGate) continue;
if(other.block().acceptItem(item, other, Edges.getFacingEdge(tile, other))){
return other;
}

View File

@@ -127,6 +127,13 @@ public class GenericCrafter extends Block{
}
}
@Override
public boolean outputsItems(){
return outputItem != null;
}
@Override
public boolean canProduce(Tile tile){
if(outputItem != null && tile.entity.items.get(outputItem.item) >= itemCapacity){

View File

@@ -97,8 +97,10 @@ public class CoreBlock extends StorageBlock{
entity.storageCapacity += other.block().itemCapacity + other.entity.proximity().sum(e -> isContainer(e) ? e.block().itemCapacity : 0);
}
for(Item item : content.items()){
entity.items.set(item, Math.min(entity.items.get(item), entity.storageCapacity));
if(!world.isGenerating()){
for(Item item : content.items()){
entity.items.set(item, Math.min(entity.items.get(item), entity.storageCapacity));
}
}
for(Tile other : state.teams.get(tile.getTeam()).cores){

View File

@@ -77,8 +77,9 @@ public class LaunchPad extends StorageBlock{
for(Item item : Vars.content.items()){
Events.fire(Trigger.itemLaunch);
Effects.effect(Fx.padlaunch, tile);
data.addItem(item, entity.items.get(item));
entity.items.set(item, 0);
int used = Math.min(entity.items.get(item), itemCapacity);
data.addItem(item, used);
entity.items.remove(item, used);
}
}
}