diff --git a/core/assets-raw/sprites_replacement/items/item-sand.png b/core/assets-raw/sprites_replacement/items/item-sand.png index ec9c497fd3..0acb132b0d 100644 Binary files a/core/assets-raw/sprites_replacement/items/item-sand.png and b/core/assets-raw/sprites_replacement/items/item-sand.png differ diff --git a/core/assets-raw/sprites_replacement/items/item-thorium.png b/core/assets-raw/sprites_replacement/items/item-thorium.png index 80bd29633c..709a4a1046 100644 Binary files a/core/assets-raw/sprites_replacement/items/item-thorium.png and b/core/assets-raw/sprites_replacement/items/item-thorium.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index c57663cc56..37be0efdb7 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -376,6 +376,7 @@ blocks.drillspeed = Base Drill Speed blocks.boosteffect = Boost Effect blocks.maxunits = Max Active Units blocks.health = Health +blocks.buildtime = Build Time blocks.inaccuracy = Inaccuracy blocks.shots = Shots blocks.reload = Shots/Second @@ -447,7 +448,7 @@ setting.sensitivity.name = Controller Sensitivity setting.saveinterval.name = Save Interval setting.seconds = {0} Seconds setting.fullscreen.name = Fullscreen -setting.borderless.name = Borderless Window +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) setting.fps.name = Show FPS setting.vsync.name = VSync setting.lasers.name = Show Power Lasers @@ -735,7 +736,7 @@ block.pneumatic-drill.name = Pneumatic Drill block.laser-drill.name = Laser Drill block.water-extractor.name = Water Extractor block.cultivator.name = Cultivator -block.dart-mech-pad.name = Dart Ship Pad +block.dart-mech-pad.name = Alpha Mech Pad block.delta-mech-pad.name = Delta Mech Pad block.javelin-ship-pad.name = Javelin Ship Pad block.trident-ship-pad.name = Trident Ship Pad diff --git a/core/assets/maps/tarFields.mmap b/core/assets/maps/tarFields.mmap index 402a7c7f07..525ee369aa 100644 Binary files a/core/assets/maps/tarFields.mmap and b/core/assets/maps/tarFields.mmap differ diff --git a/core/assets/sprites/sprites.png b/core/assets/sprites/sprites.png index 23fb474283..81a49984a7 100644 Binary files a/core/assets/sprites/sprites.png and b/core/assets/sprites/sprites.png differ diff --git a/core/assets/sprites/sprites_fallback4.png b/core/assets/sprites/sprites_fallback4.png index 55ebd4a1e9..8f535d5f0d 100644 Binary files a/core/assets/sprites/sprites_fallback4.png and b/core/assets/sprites/sprites_fallback4.png differ diff --git a/core/assets/sprites/sprites_fallback5.png b/core/assets/sprites/sprites_fallback5.png index ce68c0c782..446c926ff3 100644 Binary files a/core/assets/sprites/sprites_fallback5.png and b/core/assets/sprites/sprites_fallback5.png differ diff --git a/core/src/io/anuke/mindustry/ai/Pathfinder.java b/core/src/io/anuke/mindustry/ai/Pathfinder.java index 7710c48588..8ee044e32b 100644 --- a/core/src/io/anuke/mindustry/ai/Pathfinder.java +++ b/core/src/io/anuke/mindustry/ai/Pathfinder.java @@ -66,7 +66,7 @@ public class Pathfinder{ if(other == null) continue; if(values[dx][dy] < value && (target == null || values[dx][dy] < tl) && - !other.solid() && + !other.solid() && other.floor().drownTime <= 0 && !(point.x != 0 && point.y != 0 && (world.solid(tile.x + point.x, tile.y) || world.solid(tile.x, tile.y + point.y)))){ //diagonal corner trap target = other; tl = values[dx][dy]; diff --git a/core/src/io/anuke/mindustry/ai/WaveSpawner.java b/core/src/io/anuke/mindustry/ai/WaveSpawner.java index de00bafe45..3a1eb65fce 100644 --- a/core/src/io/anuke/mindustry/ai/WaveSpawner.java +++ b/core/src/io/anuke/mindustry/ai/WaveSpawner.java @@ -95,7 +95,7 @@ public class WaveSpawner{ Time.run(Math.min(i * 5, 60 * 2), () -> shockwave(unit)); } - Time.run(20f, () -> Effects.effect(Fx.spawnShockwave, spawn.x * tilesize, spawn.y * tilesize)); + Time.run(20f, () -> Effects.effect(Fx.spawnShockwave, spawn.x * tilesize, spawn.y * tilesize, state.rules.dropZoneRadius)); //would be interesting to see player structures survive this without hacks Time.run(40f, () -> Damage.damage(waveTeam, spawn.x * tilesize, spawn.y * tilesize, state.rules.dropZoneRadius, 99999999f, true)); } diff --git a/core/src/io/anuke/mindustry/content/Blocks.java b/core/src/io/anuke/mindustry/content/Blocks.java index 466a3dba3b..d95fa55176 100644 --- a/core/src/io/anuke/mindustry/content/Blocks.java +++ b/core/src/io/anuke/mindustry/content/Blocks.java @@ -862,6 +862,7 @@ public class Blocks implements ContentList{ requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 150, Items.silicon, 250)); size = 3; consumes.item(Items.phasefabric).boost(); + consumes.power(3f); }}; shockMine = new ShockMine("shock-mine"){{ @@ -899,7 +900,7 @@ public class Blocks implements ContentList{ itemBridge = new BufferedItemBridge("bridge-conveyor"){{ requirements(Category.distribution, ItemStack.with(Items.lead, 8, Items.copper, 8)); range = 4; - speed = 60f; + speed = 70f; bufferCapacity = 15; }}; @@ -935,6 +936,7 @@ public class Blocks implements ContentList{ itemCapacity = 120; reloadTime = 200f; range = 440f; + consumes.power(2f); }}; //endregion @@ -1029,13 +1031,13 @@ public class Blocks implements ContentList{ battery = new Battery("battery"){{ requirements(Category.power, ItemStack.with(Items.copper, 8, Items.lead, 40)); - consumes.powerBuffered(4000f, 1f); + consumes.powerBuffered(4000f); }}; batteryLarge = new Battery("battery-large"){{ requirements(Category.power, ItemStack.with(Items.titanium, 40, Items.lead, 80, Items.silicon, 40)); size = 3; - consumes.powerBuffered(50000f, 1f); + consumes.powerBuffered(50000f); }}; combustionGenerator = new BurnerGenerator("combustion-generator"){{ @@ -1104,7 +1106,7 @@ public class Blocks implements ContentList{ size = 4; health = 900; powerProduction = 110f; - itemDuration = 40f; + itemDuration = 60f; consumes.power(25f); consumes.item(Items.blastCompound); consumes.liquid(Liquids.cryofluid, 0.26f); @@ -1375,8 +1377,7 @@ public class Blocks implements ContentList{ recoil = 2f; reload = 90f; cooldown = 0.03f; - powerUsed = 1 / 3f; - consumes.powerBuffered(600f); + powerUse = 2.5f; shootShake = 2f; shootEffect = Fx.lancerLaserShoot; smokeEffect = Fx.lancerLaserShootSmoke; @@ -1394,9 +1395,8 @@ public class Blocks implements ContentList{ reload = 24f; shootCone = 40f; rotatespeed = 8f; - powerUsed = 1f / 2f; + powerUse = 0.9f; targetAir = false; - consumes.powerBuffered(60f, 60f); range = 95f; shootEffect = Fx.lightningShoot; heatColor = Color.RED; @@ -1540,10 +1540,9 @@ public class Blocks implements ContentList{ reload = 50f; firingMoveFract = 0.5f; shootDuration = 220f; - powerUsed = 1f / 2f; + powerUse = 10f; health = 200 * size * size; - consumes.powerBuffered(1200f); consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.5f)).update(false); }}; @@ -1637,6 +1636,7 @@ public class Blocks implements ContentList{ repairPoint = new RepairPoint("repair-point"){{ requirements(Category.units, ItemStack.with(Items.lead, 30, Items.copper, 30, Items.silicon, 30)); repairSpeed = 0.1f; + powerUse = 1f; }}; //endregion @@ -1644,51 +1644,51 @@ public class Blocks implements ContentList{ dartPad = new MechPad("dart-mech-pad"){{ requirements(Category.upgrade, ItemStack.with(Items.lead, 200, Items.graphite, 100, Items.copper, 150)); - mech = Mechs.dart; + mech = Mechs.alpha; size = 2; - consumes.powerBuffered(50f); + consumes.power(0.5f); }}; deltaPad = new MechPad("delta-mech-pad"){{ requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.titanium, 350, Items.copper, 400, Items.silicon, 450, Items.thorium, 300)); mech = Mechs.delta; size = 2; - consumes.powerBuffered(70f); + consumes.power(0.7f); }}; tauPad = new MechPad("tau-mech-pad"){{ requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.titanium, 250, Items.copper, 250, Items.silicon, 250)); mech = Mechs.tau; size = 2; - consumes.powerBuffered(100f); + consumes.power(1f); }}; omegaPad = new MechPad("omega-mech-pad"){{ requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.graphite, 550, Items.silicon, 650, Items.thorium, 600, Items.surgealloy, 240)); mech = Mechs.omega; size = 3; - consumes.powerBuffered(120f); + consumes.power(1.2f); }}; javelinPad = new MechPad("javelin-ship-pad"){{ requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.silicon, 450, Items.titanium, 500, Items.plastanium, 400, Items.phasefabric, 200)); mech = Mechs.javelin; size = 2; - consumes.powerBuffered(80f); + consumes.power(0.8f); }}; tridentPad = new MechPad("trident-ship-pad"){{ requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.copper, 250, Items.silicon, 250, Items.titanium, 300, Items.plastanium, 200)); mech = Mechs.trident; size = 2; - consumes.powerBuffered(100f); + consumes.power(1f); }}; glaivePad = new MechPad("glaive-ship-pad"){{ requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.silicon, 650, Items.titanium, 700, Items.plastanium, 600, Items.surgealloy, 200)); mech = Mechs.glaive; size = 3; - consumes.powerBuffered(120f); + consumes.power(1.2f); }}; //endregion diff --git a/core/src/io/anuke/mindustry/content/Bullets.java b/core/src/io/anuke/mindustry/content/Bullets.java index 449add9ab3..864e49460c 100644 --- a/core/src/io/anuke/mindustry/content/Bullets.java +++ b/core/src/io/anuke/mindustry/content/Bullets.java @@ -632,7 +632,6 @@ public class Bullets implements ContentList{ }}; oilShot = new LiquidBulletType(Liquids.oil){{ - speed = 2f; drag = 0.03f; }}; diff --git a/core/src/io/anuke/mindustry/content/Fx.java b/core/src/io/anuke/mindustry/content/Fx.java index 8cd9ad826c..7b0771eb28 100644 --- a/core/src/io/anuke/mindustry/content/Fx.java +++ b/core/src/io/anuke/mindustry/content/Fx.java @@ -583,7 +583,7 @@ public class Fx implements ContentList{ spawnShockwave = new Effect(20f, 400f, e -> { Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); Lines.stroke(e.fout() * 3f + 0.5f); - Lines.poly(e.x, e.y, 60, e.fin() * 450f); + Lines.poly(e.x, e.y, 60, e.fin() * (e.rotation + 50f)); Draw.reset(); }); diff --git a/core/src/io/anuke/mindustry/content/Mechs.java b/core/src/io/anuke/mindustry/content/Mechs.java index b6fffbc3e5..050ff47c16 100644 --- a/core/src/io/anuke/mindustry/content/Mechs.java +++ b/core/src/io/anuke/mindustry/content/Mechs.java @@ -359,6 +359,6 @@ public class Mechs implements ContentList{ } }; - starter = alpha; + starter = dart; } } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 534d98093c..2cdd52ad3b 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -7,7 +7,7 @@ import io.anuke.arc.collection.IntSet; import io.anuke.arc.graphics.Color; import io.anuke.arc.math.RandomXS128; import io.anuke.arc.util.*; -import io.anuke.arc.util.io.ReusableByteArrayInputStream; +import io.anuke.arc.util.io.ReusableByteInStream; import io.anuke.arc.util.serialization.Base64Coder; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; @@ -50,7 +50,7 @@ public class NetClient implements ApplicationListener{ /** List of entities that were removed, and need not be added while syncing. */ private IntSet removed = new IntSet(); /** Byte stream for reading in snapshots. */ - private ReusableByteArrayInputStream byteStream = new ReusableByteArrayInputStream(); + private ReusableByteInStream byteStream = new ReusableByteInStream(); private DataInputStream dataStream = new DataInputStream(byteStream); public NetClient(){ diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 9c5f6ed830..605dce9128 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -13,7 +13,7 @@ import io.anuke.arc.math.geom.Rectangle; import io.anuke.arc.math.geom.Vector2; import io.anuke.arc.util.*; import io.anuke.arc.util.io.ByteBufferOutput; -import io.anuke.arc.util.io.CountableByteArrayOutputStream; +import io.anuke.arc.util.io.ReusableByteOutStream; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.Entities; @@ -56,7 +56,7 @@ public class NetServer implements ApplicationListener{ private ByteBufferOutput outputBuffer = new ByteBufferOutput(writeBuffer); /** Stream for writing player sync data to. */ - private CountableByteArrayOutputStream syncStream = new CountableByteArrayOutputStream(); + private ReusableByteOutStream syncStream = new ReusableByteOutStream(); /** Data stream for writing player sync data to. */ private DataOutputStream dataStream = new DataOutputStream(syncStream); diff --git a/core/src/io/anuke/mindustry/entities/effect/Fire.java b/core/src/io/anuke/mindustry/entities/effect/Fire.java index 18c5c48ebe..fe447c1dcd 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Fire.java +++ b/core/src/io/anuke/mindustry/entities/effect/Fire.java @@ -1,7 +1,5 @@ package io.anuke.mindustry.entities.effect; -import io.anuke.annotations.Annotations.Loc; -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; @@ -74,11 +72,6 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{ } } - @Remote(called = Loc.server) - public static void onFireRemoved(int fireid){ - fireGroup.removeByID(fireid); - } - @Override public float lifetime(){ return lifetime; @@ -94,18 +87,17 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{ Effects.effect(Fx.fireSmoke, x + Mathf.range(4f), y + Mathf.range(4f)); } - if(Net.client()){ - return; - } - time = Mathf.clamp(time + Time.delta(), 0, lifetime()); if(time >= lifetime() || tile == null){ - Call.onFireRemoved(getID()); remove(); return; } + if(Net.client()){ + return; + } + TileEntity entity = tile.target().entity; boolean damage = entity != null; diff --git a/core/src/io/anuke/mindustry/entities/type/Player.java b/core/src/io/anuke/mindustry/entities/type/Player.java index 6be33085c0..6a4e40125b 100644 --- a/core/src/io/anuke/mindustry/entities/type/Player.java +++ b/core/src/io/anuke/mindustry/entities/type/Player.java @@ -416,8 +416,6 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{ Draw.color(0f, 0f, 0f, 0.3f * (textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime)); Fill.rect(x, y + textHeight + layout.height - layout.height/2f, layout.width + 2, layout.height + 3); font.draw(text, x - width/2f, y + textHeight + layout.height, width, Align.center, true); - - textFadeTime -= Time.delta() / (60 * 5); } Draw.reset(); @@ -494,6 +492,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{ @Override public void update(){ hitTime -= Time.delta(); + textFadeTime -= Time.delta() / (60 * 5); if(Float.isNaN(x) || Float.isNaN(y)){ velocity.set(0f, 0f); diff --git a/core/src/io/anuke/mindustry/entities/units/UnitDrops.java b/core/src/io/anuke/mindustry/entities/units/UnitDrops.java index fb18a9b9fb..142f2f3d65 100644 --- a/core/src/io/anuke/mindustry/entities/units/UnitDrops.java +++ b/core/src/io/anuke/mindustry/entities/units/UnitDrops.java @@ -19,7 +19,7 @@ public class UnitDrops{ TileEntity core = unit.getClosestEnemyCore(); - if(core == null){ + if(core == null || core.dst(unit) > Vars.mineTransferRange){ return; } @@ -37,8 +37,9 @@ public class UnitDrops{ if(Mathf.chance(0.03)){ int amount = Mathf.random(20, 40); amount = core.tile.block().acceptStack(item, amount, core.tile, null); - if(amount > 0) + if(amount > 0){ Call.transferItemTo(item, amount, unit.x + Mathf.range(2f), unit.y + Mathf.range(2f), core.tile); + } } } } diff --git a/core/src/io/anuke/mindustry/game/DefaultWaves.java b/core/src/io/anuke/mindustry/game/DefaultWaves.java index b572d412c5..4a4899da30 100644 --- a/core/src/io/anuke/mindustry/game/DefaultWaves.java +++ b/core/src/io/anuke/mindustry/game/DefaultWaves.java @@ -69,8 +69,6 @@ public class DefaultWaves{ begin = 16; unitScaling = 1; spacing = 2; - - max = 12; }}, new SpawnGroup(UnitTypes.dagger){{ @@ -119,8 +117,7 @@ public class DefaultWaves{ begin = 40; unitAmount = 2; spacing = 2; - unitScaling = 3; - max = 8; + unitScaling = 2; }}, new SpawnGroup(UnitTypes.wraith){{ @@ -129,7 +126,6 @@ public class DefaultWaves{ unitScaling = 3; spacing = 5; effect = StatusEffects.overdrive; - max = 8; }}, new SpawnGroup(UnitTypes.revenant){{ @@ -137,7 +133,7 @@ public class DefaultWaves{ unitAmount = 2; unitScaling = 3; spacing = 5; - max = 8; + max = 16; }}, new SpawnGroup(UnitTypes.ghoul){{ @@ -145,7 +141,6 @@ public class DefaultWaves{ unitAmount = 2; unitScaling = 3; spacing = 4; - max = 8; }}, new SpawnGroup(UnitTypes.eruptor){{ @@ -181,7 +176,6 @@ public class DefaultWaves{ unitAmount = 2; unitScaling = 3; spacing = 4; - max = 8; }} ); } diff --git a/core/src/io/anuke/mindustry/game/Saves.java b/core/src/io/anuke/mindustry/game/Saves.java index 3078637c01..718d3a7df4 100644 --- a/core/src/io/anuke/mindustry/game/Saves.java +++ b/core/src/io/anuke/mindustry/game/Saves.java @@ -224,7 +224,7 @@ public class Saves{ } public Zone getZone(){ - return content.getByID(ContentType.zone, meta.rules.zone); + return meta == null || meta.rules == null ? null : content.getByID(ContentType.zone, meta.rules.zone); } public int getBuild(){ diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index 665eec4d3c..0327108f1d 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -8,6 +8,7 @@ import io.anuke.arc.graphics.g2d.Lines; import io.anuke.arc.math.Mathf; import io.anuke.arc.math.geom.Geometry; import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.scene.ui.TextField; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.graphics.Pal; @@ -17,6 +18,7 @@ import io.anuke.mindustry.net.Net; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; +import static io.anuke.arc.Core.scene; import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.input.PlaceMode.*; @@ -130,7 +132,7 @@ public class DesktopInput extends InputHandler{ player.isShooting = false; } - if(!state.is(State.menu) && Core.input.keyTap(Binding.minimap) && !ui.chatfrag.chatOpen()){ + if(!state.is(State.menu) && Core.input.keyTap(Binding.minimap) && !ui.chatfrag.chatOpen() && !(scene.getKeyboardFocus() instanceof TextField)){ if(!ui.minimap.isShown()){ ui.minimap.show(); }else{ diff --git a/core/src/io/anuke/mindustry/input/MobileInput.java b/core/src/io/anuke/mindustry/input/MobileInput.java index 876e2732c7..7b7f53b098 100644 --- a/core/src/io/anuke/mindustry/input/MobileInput.java +++ b/core/src/io/anuke/mindustry/input/MobileInput.java @@ -590,7 +590,6 @@ public class MobileInput extends InputHandler implements GestureListener{ @Override public void update(){ - clampCamera(); if(state.is(State.menu) || player.isDead()){ selection.clear(); removals.clear(); @@ -730,17 +729,6 @@ public class MobileInput extends InputHandler implements GestureListener{ return true; } - void clampCamera(){ - if(player.isDead()) return; - - Vector2 v = Core.camera.position; - //change to 1/2 to clamp to viewport - float scaling = 1f; - - v.x = clerp(v.x, player.x - Core.camera.width*scaling, player.x + Core.camera.width*scaling); - v.y = clerp(v.y, player.y - Core.camera.height*scaling, player.y + Core.camera.height*scaling); - } - float clerp(float value, float min, float max){ final float alpha = 0.07f; return value < min ? Mathf.lerpDelta(value, min, alpha) : value > max ? Mathf.lerpDelta(value, max, alpha) : value; diff --git a/core/src/io/anuke/mindustry/io/TypeIO.java b/core/src/io/anuke/mindustry/io/TypeIO.java index 367207d9b9..d785cbb1b3 100644 --- a/core/src/io/anuke/mindustry/io/TypeIO.java +++ b/core/src/io/anuke/mindustry/io/TypeIO.java @@ -214,12 +214,13 @@ public class TypeIO{ @WriteClass(Liquid.class) public static void writeLiquid(ByteBuffer buffer, Liquid liquid){ - buffer.put(liquid.id); + buffer.put(liquid == null ? -1 : liquid.id); } @ReadClass(Liquid.class) public static Liquid readLiquid(ByteBuffer buffer){ - return content.liquid(buffer.get()); + byte id = buffer.get(); + return id == -1 ? null : content.liquid(buffer.get()); } @WriteClass(BulletType.class) diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index 92448d9c90..7b6b770e65 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -67,7 +67,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"); + error = Core.bundle.get("error.any") + "\n" + t.getClass().getSimpleName() + "\n" + (t.getMessage() == null ? "" : t.getMessage()); } ui.showText("", Core.bundle.format("connectfail", error)); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java index 9049435f3f..919b4dfe31 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -190,10 +190,16 @@ public class SettingsMenuDialog extends SettingsDialog{ } }); + graphics.checkPref("borderlesswindow", false, b -> Core.graphics.setUndecorated(b)); + Core.graphics.setVSync(Core.settings.getBool("vsync")); if(Core.settings.getBool("fullscreen")){ Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode()); } + + if(Core.settings.getBool("borderlesswindow")){ + Core.graphics.setUndecorated(true); + } }else{ graphics.checkPref("landscape", false, b -> { if(b){ diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index f3d37565c4..c3aa1ec35e 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -328,6 +328,10 @@ public class Block extends BlockStorage{ setBars(); consumes.init(); + + if(!outputsPower && consumes.hasPower() && consumes.getPower().buffered){ + throw new IllegalArgumentException("Consumer using buffered power: " + name); + } } @Override @@ -409,6 +413,7 @@ public class Block extends BlockStorage{ public void setStats(){ stats.add(BlockStat.size, "{0}x{0}", size); stats.add(BlockStat.health, health, StatUnit.none); + stats.add(BlockStat.buildTime, buildCost / 60, StatUnit.seconds); consumes.display(stats); @@ -428,15 +433,17 @@ public class Block extends BlockStorage{ }else{ current = entity -> entity.liquids.current(); } - bars.add("liquid", entity -> new Bar(() -> entity.liquids.get(current.get(entity)) <= 0.001f ? Core.bundle.get("bar.liquid") : current.get(entity).localizedName(), () -> current.get(entity).color, () -> entity.liquids.get(current.get(entity)) / liquidCapacity)); + bars.add("liquid", entity -> new Bar(() -> entity.liquids.get(current.get(entity)) <= 0.001f ? Core.bundle.get("bar.liquid") : current.get(entity).localizedName(), + () -> current.get(entity).color, () -> entity.liquids.get(current.get(entity)) / liquidCapacity)); } if(hasPower && consumes.hasPower()){ - boolean buffered = consumes.getPower().isBuffered; - float capacity = consumes.getPower().powerCapacity; + ConsumePower cons = consumes.getPower(); + boolean buffered = cons.buffered; + float capacity = cons.capacity; bars.add("power", entity -> new Bar(() -> buffered ? Core.bundle.format("bar.poweramount", Float.isNaN(entity.power.satisfaction * capacity) ? "" : (int)(entity.power.satisfaction * capacity)) : - Core.bundle.get("bar.power"), () -> Pal.powerBar, () -> entity.power.satisfaction)); + Core.bundle.get("bar.power"), () -> Pal.powerBar, () -> Mathf.isZero(cons.requestedPower(entity)) && entity.power.graph.getPowerProduced() + entity.power.graph.getBatteryStored() > 0f ? 1f : entity.power.satisfaction)); } if(hasItems && configurable){ @@ -487,8 +494,8 @@ public class Block extends BlockStorage{ explosiveness += tile.entity.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2f); } - if(consumes.hasPower() && consumes.getPower().isBuffered){ - power += tile.entity.power.satisfaction * consumes.getPower().powerCapacity; + if(consumes.hasPower() && consumes.getPower().buffered){ + power += tile.entity.power.satisfaction * consumes.getPower().capacity; } if(hasLiquids){ diff --git a/core/src/io/anuke/mindustry/world/Tile.java b/core/src/io/anuke/mindustry/world/Tile.java index 8b418769dd..3bb1503058 100644 --- a/core/src/io/anuke/mindustry/world/Tile.java +++ b/core/src/io/anuke/mindustry/world/Tile.java @@ -382,7 +382,7 @@ public class Tile implements Position, TargetTrait{ Point2 point = Geometry.d8[i]; Tile tile = world.tile(x + point.x, y + point.y); if(tile != null && tile.floor.isLiquid){ - cost += 3; + cost += 4; } if(tile != null && tile.solid()){ occluded = true; @@ -390,17 +390,35 @@ public class Tile implements Position, TargetTrait{ } } + //+24 + if(occluded){ cost += 2; } + //+26 + if(target().synthetic()){ cost += Mathf.clamp(target().block().health / 10f, 0, 20); } + //+46 + if(floor.isLiquid){ cost += 10; } + + //+56 + + if(floor.drownTime > 0){ + cost += 70; + } + + //+126 + + if(cost < 0){ + cost = Byte.MAX_VALUE; + } } protected void preChanged(){ diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java b/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java index 6ee12b2212..6b9e611893 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java @@ -35,7 +35,6 @@ public class ForceProjector extends Block{ protected float cooldownBrokenBase = 0.35f; protected float basePowerDraw = 0.2f; protected float powerDamage = 0.1f; - protected final ConsumeForceProjectorPower consumePower; protected TextureRegion topRegion; private static Tile paramTile; @@ -45,13 +44,7 @@ public class ForceProjector extends Block{ if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && paramBlock.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){ trait.absorb(); Effects.effect(Fx.absorb, trait); - float relativeDamagePowerDraw = trait.getShieldDamage() * paramBlock.powerDamage / paramBlock.consumePower.powerCapacity; paramEntity.hit = 1f; - - paramEntity.power.satisfaction -= Math.min(relativeDamagePowerDraw, paramEntity.power.satisfaction); - if(paramEntity.power.satisfaction <= 0.0001f){ - paramEntity.buildup += trait.getShieldDamage() * paramEntity.warmup * 2f; - } paramEntity.buildup += trait.getShieldDamage() * paramEntity.warmup; } }; @@ -65,8 +58,6 @@ public class ForceProjector extends Block{ hasLiquids = true; hasItems = true; consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).boost().update(false); - consumePower = new ConsumeForceProjectorPower(60f, 60f); - consumes.add(consumePower); } @Override @@ -126,10 +117,7 @@ public class ForceProjector extends Block{ // - There is not enough base power in the buffer => Draw all power and break shield // - The generator is in the AI base and uses cheat mode => Only draw power from shots being absorbed - float relativePowerDraw = 0.0f; - if(!cheat){ - relativePowerDraw = basePowerDraw / consumePower.powerCapacity; - } + float relativePowerDraw = cheat ? 0f : 1f; if(entity.power.satisfaction < relativePowerDraw){ entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f); @@ -139,7 +127,6 @@ public class ForceProjector extends Block{ } }else{ entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.1f); - entity.power.satisfaction -= Math.min(entity.power.satisfaction, relativePowerDraw * Time.delta()); } if(entity.buildup > 0){ @@ -271,7 +258,6 @@ public class ForceProjector extends Block{ public void drawSimple(){ if(realRadius(entity) < 0.5f) return; - ; float rad = realRadius(entity); @@ -289,15 +275,4 @@ public class ForceProjector extends Block{ return shieldGroup; } } - - public class ConsumeForceProjectorPower extends ConsumePower{ - public ConsumeForceProjectorPower(float powerCapacity, float ticksToFill){ - super(powerCapacity / ticksToFill, powerCapacity, true); - } - - @Override - public boolean valid(TileEntity entity){ - return entity.power.satisfaction >= basePowerDraw / powerCapacity && super.valid(entity); - } - } } diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/PowerTurret.java b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/PowerTurret.java index 111a7024ec..1c11cd3da2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/PowerTurret.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/PowerTurret.java @@ -2,13 +2,10 @@ package io.anuke.mindustry.world.blocks.defense.turrets; import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.StatUnit; public abstract class PowerTurret extends CooledTurret{ - /** The percentage of power which will be used per shot. */ - protected float powerUsed = 0.5f; protected BulletType shootType; + protected float powerUse = 1f; public PowerTurret(String name){ super(name); @@ -16,28 +13,30 @@ public abstract class PowerTurret extends CooledTurret{ } @Override - public void setStats(){ - super.setStats(); - - stats.add(BlockStat.powerShot, powerUsed * consumes.getPower().powerCapacity, StatUnit.powerUnits); - } - - @Override - public boolean hasAmmo(Tile tile){ - // Allow shooting as long as the turret is at least at 50% power - return tile.entity.power.satisfaction >= powerUsed; + public void init(){ + consumes.powerCond(powerUse, entity -> ((TurretEntity)entity).target != null); + super.init(); } @Override public BulletType useAmmo(Tile tile){ - if(tile.isEnemyCheat()) return shootType; - // Make sure that power can not go negative in case of threading issues or similar - tile.entity.power.satisfaction -= Math.min(powerUsed, tile.entity.power.satisfaction); + //nothing used directly return shootType; } + @Override + public boolean hasAmmo(Tile tile){ + //you can always rotate, but never shoot if there's no power + return true; + } + @Override public BulletType peekAmmo(Tile tile){ return shootType; } + + @Override + protected float baseReloadSpeed(Tile tile){ + return tile.isEnemyCheat() ? 1f : tile.entity.power.satisfaction; + } } diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/Turret.java index e753b69a2e..1780ba40c9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/Turret.java @@ -58,8 +58,7 @@ public abstract class Turret extends Block{ protected TextureRegion baseRegion, heatRegion; - protected BiConsumer drawer = (tile, entity) -> - Draw.rect(region, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90); + protected BiConsumer drawer = (tile, entity) -> Draw.rect(region, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90); protected BiConsumer heatDrawer = (tile, entity) -> { if(entity.heat <= 0.00001f) return; Draw.color(heatColor, entity.heat); @@ -195,8 +194,7 @@ public abstract class Turret extends Block{ protected void findTarget(Tile tile){ TurretEntity entity = tile.entity(); - entity.target = Units.closestTarget(tile.getTeam(), - tile.drawx(), tile.drawy(), range, e -> !e.isDead() && (!e.isFlying() || targetAir) && (e.isFlying() || targetGround)); + entity.target = Units.closestTarget(tile.getTeam(), tile.drawx(), tile.drawy(), range, e -> !e.isDead() && (!e.isFlying() || targetAir) && (e.isFlying() || targetGround)); } protected void turnToTarget(Tile tile, float targetRot){ @@ -248,7 +246,7 @@ public abstract class Turret extends Block{ entity.reload = 0f; }else{ - entity.reload += tile.entity.delta() * peekAmmo(tile).reloadMultiplier; + entity.reload += tile.entity.delta() * peekAmmo(tile).reloadMultiplier * baseReloadSpeed(tile); } } @@ -296,6 +294,10 @@ public abstract class Turret extends Block{ tile.drawy() - Angles.trnsy(entity.rotation, ammoEjectBack), entity.rotation); } + protected float baseReloadSpeed(Tile tile){ + return 1f; + } + protected boolean isTurret(Tile tile){ return (tile.entity instanceof TurretEntity); } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/Junction.java b/core/src/io/anuke/mindustry/world/blocks/distribution/Junction.java index cdae6e25e0..b3f0227266 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/Junction.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/Junction.java @@ -72,7 +72,7 @@ public class Junction extends Block{ if(entity == null || relative == -1 || entity.buffers[relative].full()) return false; Tile to = tile.getNearby(relative); - return to != null; + return to != null && to.target().entity != null; } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/LiquidJunction.java b/core/src/io/anuke/mindustry/world/blocks/distribution/LiquidJunction.java index 8e57c24777..a160348107 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/LiquidJunction.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/LiquidJunction.java @@ -43,8 +43,8 @@ public class LiquidJunction extends LiquidBlock{ dir = (dir + 4) % 4; Tile to = tile.getNearby(dir).target(); - if(to.block().hasLiquids && to.block().acceptLiquid(to, tile, liquid, Math.min(to.block().liquidCapacity - to.entity.liquids.get(liquid) - 0.00001f, amount))){ - to.block().handleLiquid(to, tile, liquid, Math.min(to.block().liquidCapacity - to.entity.liquids.get(liquid) - 0.00001f, amount)); + if(to.block().hasLiquids && to.block().acceptLiquid(to, tile, liquid, amount)){ + to.block().handleLiquid(to, tile, liquid, amount); } } @@ -55,6 +55,6 @@ public class LiquidJunction extends LiquidBlock{ Tile to = dest.getNearby(dir); if(to == null) return false; to = to.target(); - return to != null && to.entity != null && to.block().hasLiquids && to.block().acceptLiquid(to, dest, liquid, Math.min(to.block().liquidCapacity - to.entity.liquids.get(liquid) - 0.00001f, amount)); + return to != null && to.entity != null && to.block().hasLiquids && to.block().acceptLiquid(to, dest, liquid, amount); } } diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java index cdf537563d..03feb4e4ef 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -41,7 +41,6 @@ public class MassDriver extends Block{ protected Effect smokeEffect = Fx.shootBigSmoke2; protected Effect recieveEffect = Fx.mineBig; protected float shake = 3f; - protected float powerPercentageUsed = 0.95f; protected TextureRegion baseRegion; public MassDriver(String name){ @@ -52,7 +51,6 @@ public class MassDriver extends Block{ hasItems = true; layer = Layer.turret; hasPower = true; - consumes.powerBuffered(30f); outlineIcon = true; } @@ -74,13 +72,6 @@ public class MassDriver extends Block{ baseRegion = Core.atlas.find(name + "-base"); } - @Override - public void setStats(){ - super.setStats(); - - stats.add(BlockStat.powerShot, consumes.getPower().powerCapacity * powerPercentageUsed, StatUnit.powerUnits); - } - @Override public void update(Tile tile){ MassDriverEntity entity = tile.entity(); @@ -112,6 +103,11 @@ public class MassDriver extends Block{ tryDump(tile); } + //skip when there's no power + if(!entity.cons.valid()){ + return; + } + if(entity.state == DriverState.accepting){ //if there's nothing shooting at this, bail if(entity.currentShooter() == null){ @@ -132,7 +128,6 @@ public class MassDriver extends Block{ if( tile.entity.items.total() >= minDistribute && //must shoot minimum amount of items - tile.entity.power.satisfaction >= powerPercentageUsed && //must have power link.block().itemCapacity - link.entity.items.total() >= minDistribute && //must have minimum amount of space entity.reload <= 0.0001f //must have reloaded ){ @@ -235,7 +230,6 @@ public class MassDriver extends Block{ //reset reload, use power. entity.reload = 1f; - entity.power.satisfaction -= Math.min(entity.power.satisfaction, powerPercentageUsed); DriverBulletData data = Pools.obtain(DriverBulletData.class, DriverBulletData::new); data.from = entity; diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ConditionalConsumePower.java b/core/src/io/anuke/mindustry/world/blocks/power/ConditionalConsumePower.java new file mode 100644 index 0000000000..e94cfab6ba --- /dev/null +++ b/core/src/io/anuke/mindustry/world/blocks/power/ConditionalConsumePower.java @@ -0,0 +1,20 @@ +package io.anuke.mindustry.world.blocks.power; + +import io.anuke.arc.function.Predicate; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.world.consumers.ConsumePower; + +/** A power consumer that only activates sometimes. */ +public class ConditionalConsumePower extends ConsumePower{ + private final Predicate consume; + + public ConditionalConsumePower(float usage, Predicate consume){ + super(usage, 0, false); + this.consume = consume; + } + + @Override + public float requestedPower(TileEntity entity){ + return consume.test(entity) ? usage : 0f; + } +} diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ImpactReactor.java b/core/src/io/anuke/mindustry/world/blocks/power/ImpactReactor.java index 6a4823069a..71e018bb56 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ImpactReactor.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ImpactReactor.java @@ -58,7 +58,7 @@ public class ImpactReactor extends PowerGenerator{ bars.add("poweroutput", entity -> new Bar(() -> Core.bundle.format("bar.poweroutput", - Strings.fixed(Math.max(entity.block.getPowerProduction(entity.tile) - consumes.getPower().powerPerTick, 0) * 60 * entity.timeScale, 1)), + Strings.fixed(Math.max(entity.block.getPowerProduction(entity.tile) - consumes.getPower().usage, 0) * 60 * entity.timeScale, 1)), () -> Pal.powerBar, () -> ((GeneratorEntity)entity).productionEfficiency)); } @@ -76,7 +76,7 @@ public class ImpactReactor extends PowerGenerator{ public void update(Tile tile){ FusionReactorEntity entity = tile.entity(); - if(entity.cons.valid()){ + if(entity.cons.valid() && entity.power.satisfaction >= 0.99f){ entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, warmupSpeed); if(Mathf.isEqual(entity.warmup, 1f, 0.001f)){ entity.warmup = 1f; diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java index c4bcdb4fdc..d8ac8b05d9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -52,7 +52,7 @@ public class PowerGraph{ if(consumes.hasPower()){ ConsumePower consumePower = consumes.getPower(); if(otherConsumersAreValid(consumer, consumePower)){ - powerNeeded += consumePower.requestedPower(consumer.block(), consumer.entity) * consumer.entity.delta(); + powerNeeded += consumePower.requestedPower(consumer.entity) * consumer.entity.delta(); } } } @@ -64,7 +64,7 @@ public class PowerGraph{ for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; if(consumes.hasPower()){ - totalAccumulator += battery.entity.power.satisfaction * consumes.getPower().powerCapacity; + totalAccumulator += battery.entity.power.satisfaction * consumes.getPower().capacity; } } return totalAccumulator; @@ -73,9 +73,9 @@ public class PowerGraph{ public float getBatteryCapacity(){ float totalCapacity = 0f; for(Tile battery : batteries){ - Consumers consumes = battery.block().consumes; - if(consumes.hasPower()){ - totalCapacity += consumes.getPower().requestedPower(battery.block(), battery.entity) * battery.entity.delta(); + if(battery.block().consumes.hasPower()){ + ConsumePower power = battery.block().consumes.getPower(); + totalCapacity += (1f - battery.entity.power.satisfaction) * power.capacity; } } return totalCapacity; @@ -91,7 +91,7 @@ public class PowerGraph{ Consumers consumes = battery.block().consumes; if(consumes.hasPower()){ ConsumePower consumePower = consumes.getPower(); - if(consumePower.powerCapacity > 0f){ + if(consumePower.capacity > 0f){ battery.entity.power.satisfaction = Math.max(0.0f, battery.entity.power.satisfaction - consumedPowerPercentage); } } @@ -101,15 +101,16 @@ public class PowerGraph{ public float chargeBatteries(float excess){ float capacity = getBatteryCapacity(); + //how much of the missing in each battery % is charged + float chargedPercent = Math.min(excess/capacity, 1f); if(Mathf.isEqual(capacity, 0f)) return 0f; for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; if(consumes.hasPower()){ ConsumePower consumePower = consumes.getPower(); - if(consumePower.powerCapacity > 0f){ - float additionalPowerPercentage = Math.min(1.0f, excess / consumePower.powerCapacity); - battery.entity.power.satisfaction = Math.min(1.0f, battery.entity.power.satisfaction + additionalPowerPercentage); + if(consumePower.capacity > 0f){ + battery.entity.power.satisfaction += (1f-battery.entity.power.satisfaction) * chargedPercent; } } } @@ -117,21 +118,29 @@ public class PowerGraph{ } public void distributePower(float needed, float produced){ - //distribute even if not needed. this is because some might be requiring power but not requesting it; it updates consumers + //distribute even if not needed. this is because some might be requiring power but not using it; it updates consumers float coverage = Mathf.isZero(needed) && Mathf.isZero(produced) ? 0f : Mathf.isZero(needed) ? 1f : Math.min(1, produced / needed); for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; if(consumes.hasPower()){ ConsumePower consumePower = consumes.getPower(); - //currently satisfies power even if it's not required yet - if(consumePower.isBuffered){ - if(!Mathf.isZero(consumePower.powerCapacity)){ + if(consumePower.buffered){ + if(!Mathf.isZero(consumePower.capacity)){ // Add an equal percentage of power to all buffers, based on the global power coverage in this graph - float maximumRate = consumePower.requestedPower(consumer.block(), consumer.entity()) * coverage * consumer.entity.delta(); - consumer.entity.power.satisfaction = Mathf.clamp(consumer.entity.power.satisfaction + maximumRate / consumePower.powerCapacity); + float maximumRate = consumePower.requestedPower(consumer.entity) * coverage * consumer.entity.delta(); + consumer.entity.power.satisfaction = Mathf.clamp(consumer.entity.power.satisfaction + maximumRate / consumePower.capacity); } }else{ - consumer.entity.power.satisfaction = coverage; + //valid consumers get power as usual + if(otherConsumersAreValid(consumer, consumePower)){ + consumer.entity.power.satisfaction = coverage; + }else{ //invalid consumers get an estimate, if they were to activate + consumer.entity.power.satisfaction = Math.min(1, produced / (needed + consumePower.usage * consumer.entity.delta())); + //just in case + if(Float.isNaN(consumer.entity.power.satisfaction)){ + consumer.entity.power.satisfaction = 0f; + } + } } } } @@ -171,7 +180,7 @@ public class PowerGraph{ } public void add(Tile tile){ - if(tile.block().consumes.hasPower() && !tile.block().consumes.getPower().isBuffered){ + if(tile.block().consumes.hasPower() && !tile.block().consumes.getPower().buffered){ //reset satisfaction to zero in case of direct consumer. There is no reason to clear power from buffered consumers. tile.entity.power.satisfaction = 0.0f; } @@ -179,7 +188,7 @@ public class PowerGraph{ tile.entity.power.graph = this; all.add(tile); - if(tile.block().outputsPower && tile.block().consumesPower && !tile.block().consumes.getPower().isBuffered){ + if(tile.block().outputsPower && tile.block().consumesPower && !tile.block().consumes.getPower().buffered){ producers.add(tile); consumers.add(tile); }else if(tile.block().outputsPower && tile.block().consumesPower){ diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Drill.java b/core/src/io/anuke/mindustry/world/blocks/production/Drill.java index 93d9857012..6cce3928d7 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Drill.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Drill.java @@ -258,6 +258,10 @@ public class Drill extends Block{ return new DrillEntity(); } + public int tier(){ + return tier; + } + public Item getDrop(Tile tile){ return tile.drop(); } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java b/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java index 7376b0738b..3b1f50544e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java @@ -135,6 +135,14 @@ public class GenericCrafter extends Block{ return itemCapacity; } + public Item outputItem(){ + return outputItem == null ? null : outputItem.item; + } + + public Liquid outputLiquid(){ + return outputLiquid == null ? null : outputLiquid.liquid; + } + public static class GenericCrafterEntity extends TileEntity{ public float progress; public float totalProgress; diff --git a/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java b/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java index 25e7723842..85e361bddc 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java @@ -9,6 +9,7 @@ import io.anuke.arc.math.geom.Geometry; import io.anuke.arc.util.Time; import io.anuke.mindustry.Vars; import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.content.Mechs; import io.anuke.mindustry.entities.Effects; import io.anuke.mindustry.entities.traits.SpawnerTrait; import io.anuke.mindustry.entities.type.Player; @@ -30,7 +31,6 @@ import static io.anuke.mindustry.Vars.tilesize; public class MechPad extends Block{ protected Mech mech; protected float buildTime = 60 * 5; - protected float requiredSatisfaction = 0.999f; public MechPad(String name){ super(name); @@ -56,11 +56,8 @@ public class MechPad extends Block{ if(player == null || !(tile.block() instanceof MechPad) || !checkValidTap(tile, player)) return; MechFactoryEntity entity = tile.entity(); - MechPad pad = (MechPad)tile.block(); - if(entity.power.satisfaction < pad.requiredSatisfaction) return; - - entity.power.satisfaction -= Math.min(entity.power.satisfaction, pad.requiredSatisfaction); + if(!entity.cons.valid()) return; player.beginRespawning(entity); } @@ -73,7 +70,8 @@ public class MechPad extends Block{ Effects.effect(Fx.spawn, entity); if(entity.player == null) return; - entity.player.mech = ((MechPad)tile.block()).mech; + Mech mech = ((MechPad)tile.block()).mech; + entity.player.mech = entity.player.mech == mech ? Mechs.starter : mech; entity.progress = 0; entity.player.onRespawn(tile); @@ -102,7 +100,7 @@ public class MechPad extends Block{ if(checkValidTap(tile, player)){ Call.onMechFactoryTap(player, tile); - }else if(player.isLocal && mobile && !player.isDead() && (entity.power.satisfaction >= requiredSatisfaction) && entity.player == null){ + }else if(player.isLocal && mobile && !player.isDead() && entity.cons.valid() && entity.player == null){ //deselect on double taps player.moveTarget = player.moveTarget == tile.entity ? null : tile.entity; } @@ -115,7 +113,7 @@ public class MechPad extends Block{ Draw.rect(Core.atlas.find(name), tile.drawx(), tile.drawy()); if(entity.player != null){ - TextureRegion region = mech.iconRegion; + TextureRegion region = (entity.player.mech == mech ? Mechs.starter.iconRegion : mech.iconRegion); Shaders.build.region = region; Shaders.build.progress = entity.progress; diff --git a/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java b/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java index 835c987c1a..7eb2b6b40f 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java @@ -14,8 +14,7 @@ import io.anuke.mindustry.entities.type.Unit; import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.consumers.ConsumePower; -import io.anuke.mindustry.world.meta.*; +import io.anuke.mindustry.world.meta.BlockFlag; public class RepairPoint extends Block{ private static Rectangle rect = new Rectangle(); @@ -24,9 +23,7 @@ public class RepairPoint extends Block{ protected float repairRadius = 50f; protected float repairSpeed = 0.3f; - protected float powerPerEvent = 0.06f; - protected ConsumePower consumePower; - + protected float powerUse; protected TextureRegion baseRegion; public RepairPoint(String name){ @@ -37,7 +34,6 @@ public class RepairPoint extends Block{ layer = Layer.turret; layer2 = Layer.laser; hasPower = true; - consumePower = consumes.powerBuffered(20f); outlineIcon = true; } @@ -49,9 +45,9 @@ public class RepairPoint extends Block{ } @Override - public void setStats(){ - super.setStats(); - stats.add(BlockStat.powerUse, powerPerEvent * 60f, StatUnit.powerSecond); + public void init(){ + consumes.powerCond(powerUse, entity -> ((RepairPointEntity)entity).target != null); + super.init(); } @Override @@ -100,18 +96,13 @@ public class RepairPoint extends Block{ RepairPointEntity entity = tile.entity(); boolean targetIsBeingRepaired = false; - if(entity.target != null && (entity.target.isDead() || entity.target.dst(tile) > repairRadius || - entity.target.health >= entity.target.maxHealth())){ + if(entity.target != null && (entity.target.isDead() || entity.target.dst(tile) > repairRadius || entity.target.health >= entity.target.maxHealth())){ entity.target = null; - }else if(entity.target != null){ - float relativeConsumption = powerPerEvent / consumePower.powerCapacity; - if(entity.power.satisfaction > 0.0f){ - entity.target.health += repairSpeed * Time.delta() * entity.strength * Mathf.clamp(entity.power.satisfaction / relativeConsumption); - entity.target.clampHealth(); - entity.rotation = Mathf.slerpDelta(entity.rotation, entity.angleTo(entity.target), 0.5f); - entity.power.satisfaction -= Math.min(entity.power.satisfaction, relativeConsumption); - targetIsBeingRepaired = true; - } + }else if(entity.target != null && entity.cons.valid()){ + entity.target.health += repairSpeed * Time.delta() * entity.strength * entity.power.satisfaction; + entity.target.clampHealth(); + entity.rotation = Mathf.slerpDelta(entity.rotation, entity.angleTo(entity.target), 0.5f); + targetIsBeingRepaired = true; } if(entity.target != null && targetIsBeingRepaired){ diff --git a/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java b/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java index afe5e8daaf..7d7077a371 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java @@ -25,11 +25,6 @@ import io.anuke.mindustry.world.meta.*; import java.io.*; public class UnitFactory extends Block{ - //for attack mode - protected float gracePeriodMultiplier = 15f; - protected float speedupTime = 60f * 60f * 20; - protected float maxSpeedup = 2f; - protected UnitType type; protected float produceTime = 1000f; protected float launchVelocity = 0f; @@ -160,25 +155,9 @@ public class UnitFactory extends Block{ return; } - if(tile.isEnemyCheat()){ - entity.warmup += entity.delta(); - } - - if(!tile.isEnemyCheat()){ - //player-made spawners have default behavior - if(entity.cons.valid()){ - entity.time += entity.delta() * entity.speedScl * Vars.state.rules.unitBuildSpeedMultiplier; - entity.buildTime += entity.delta() * entity.power.satisfaction * Vars.state.rules.unitBuildSpeedMultiplier; - entity.speedScl = Mathf.lerpDelta(entity.speedScl, 1f, 0.05f); - }else{ - entity.speedScl = Mathf.lerpDelta(entity.speedScl, 0f, 0.05f); - } - //check if grace period had passed - }else if(entity.warmup > produceTime * gracePeriodMultiplier){ - float speedMultiplier = Math.min(0.1f + (entity.warmup - produceTime * gracePeriodMultiplier) / speedupTime, maxSpeedup); - entity.time += entity.delta() * entity.speedScl; - //otherwise, it's an enemy, cheat by not requiring resources - entity.buildTime += entity.delta() * speedMultiplier; + if(entity.cons.valid() || tile.isEnemyCheat()){ + entity.time += entity.delta() * entity.speedScl * Vars.state.rules.unitBuildSpeedMultiplier * entity.power.satisfaction; + entity.buildTime += entity.delta() * entity.power.satisfaction * Vars.state.rules.unitBuildSpeedMultiplier; entity.speedScl = Mathf.lerpDelta(entity.speedScl, 1f, 0.05f); }else{ entity.speedScl = Mathf.lerpDelta(entity.speedScl, 0f, 0.05f); @@ -193,7 +172,6 @@ public class UnitFactory extends Block{ entity.cons.trigger(); } } - @Override public int getMaximumAccepted(Tile tile, Item item){ return capacities[item.id]; @@ -214,20 +192,19 @@ public class UnitFactory extends Block{ float buildTime; float time; float speedScl; - float warmup; //only for enemy spawners int spawned; @Override public void write(DataOutput stream) throws IOException{ stream.writeFloat(buildTime); - stream.writeFloat(warmup); + stream.writeFloat(0f); stream.writeInt(spawned); } @Override public void read(DataInput stream) throws IOException{ buildTime = stream.readFloat(); - warmup = stream.readFloat(); + stream.readFloat(); //unneeded information, will remove later spawned = stream.readInt(); } } diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java index b5b1cf563f..1df63e61e1 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -1,25 +1,23 @@ package io.anuke.mindustry.world.consumers; -import io.anuke.arc.math.Mathf; import io.anuke.arc.scene.ui.layout.Table; import io.anuke.mindustry.entities.type.TileEntity; -import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.meta.*; /** Consumer class for blocks which consume power while being connected to a power graph. */ public class ConsumePower extends Consume{ /** The maximum amount of power which can be processed per tick. This might influence efficiency or load a buffer. */ - public final float powerPerTick; + public final float usage; /** The maximum power capacity in power units. */ - public final float powerCapacity; + public final float capacity; /** True if the module can store power. */ - public final boolean isBuffered; + public final boolean buffered; - public ConsumePower(float powerPerTick, float powerCapacity, boolean isBuffered){ - this.powerPerTick = powerPerTick; - this.powerCapacity = powerCapacity; - this.isBuffered = isBuffered; + public ConsumePower(float usage, float capacity, boolean buffered){ + this.usage = usage; + this.capacity = capacity; + this.buffered = buffered; } @Override @@ -44,7 +42,7 @@ public class ConsumePower extends Consume{ @Override public boolean valid(TileEntity entity){ - if(isBuffered){ + if(buffered){ return true; }else{ return entity.power.satisfaction > 0f; @@ -53,10 +51,10 @@ public class ConsumePower extends Consume{ @Override public void display(BlockStats stats){ - if(isBuffered){ - stats.add(BlockStat.powerCapacity, powerCapacity, StatUnit.none); + if(buffered){ + stats.add(BlockStat.powerCapacity, capacity, StatUnit.none); }else{ - stats.add(BlockStat.powerUse, powerPerTick * 60f, StatUnit.powerSecond); + stats.add(BlockStat.powerUse, usage * 60f, StatUnit.powerSecond); } } @@ -66,12 +64,11 @@ public class ConsumePower extends Consume{ * @param entity The entity which contains the power module. * @return The amount of power which is requested per tick. */ - public float requestedPower(Block block, TileEntity entity){ - if(isBuffered){ - // Stop requesting power once the buffer is full. - return Mathf.isEqual(entity.power.satisfaction, 1.0f) ? 0.0f : powerPerTick; + public float requestedPower(TileEntity entity){ + if(buffered){ + return (1f-entity.power.satisfaction)*capacity; }else{ - return powerPerTick; + return usage; } } diff --git a/core/src/io/anuke/mindustry/world/consumers/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index d544e6481e..6839237d2b 100644 --- a/core/src/io/anuke/mindustry/world/consumers/Consumers.java +++ b/core/src/io/anuke/mindustry/world/consumers/Consumers.java @@ -1,8 +1,11 @@ package io.anuke.mindustry.world.consumers; +import io.anuke.arc.function.Predicate; import io.anuke.arc.util.Structs; import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.type.TileEntity; import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.blocks.power.ConditionalConsumePower; import io.anuke.mindustry.world.meta.BlockStats; public class Consumers{ @@ -35,7 +38,7 @@ public class Consumers{ } /** - * Creates a consumer which directly uses power without buffering it. The module will work while the available power is greater than or equal to the minimumSatisfaction percentage (0..1). + * Creates a consumer which directly uses power without buffering it. * @param powerPerTick The amount of power which is required each tick for 100% efficiency. * @return the created consumer object. */ @@ -43,22 +46,17 @@ public class Consumers{ return add(new ConsumePower(powerPerTick, 0.0f, false)); } - /** - * Creates a consumer which stores power and uses it only in case of certain events (e.g. a turret firing). - * It will take 180 ticks (three second) to fill the buffer, given enough power supplied. - * @param powerCapacity The maximum capacity in power units. - */ - public ConsumePower powerBuffered(float powerCapacity){ - return powerBuffered(powerCapacity, 60f * 3); + /** Creates a consumer which only consumes power when the condition is met. */ + public ConsumePower powerCond(float usage, Predicate cons){ + return add(new ConditionalConsumePower(usage, cons)); } /** - * Creates a consumer which stores power and uses it only in case of certain events (e.g. a turret firing). + * Creates a consumer which stores power. * @param powerCapacity The maximum capacity in power units. - * @param ticksToFill The number of ticks it shall take to fill the buffer. */ - public ConsumePower powerBuffered(float powerCapacity, float ticksToFill){ - return add(new ConsumePower(powerCapacity / ticksToFill, powerCapacity, true)); + public ConsumePower powerBuffered(float powerCapacity){ + return add(new ConsumePower(0f, powerCapacity, true)); } public ConsumeItems item(Item item){ diff --git a/core/src/io/anuke/mindustry/world/meta/BlockStat.java b/core/src/io/anuke/mindustry/world/meta/BlockStat.java index 705c19ac3c..364f0cd3c3 100644 --- a/core/src/io/anuke/mindustry/world/meta/BlockStat.java +++ b/core/src/io/anuke/mindustry/world/meta/BlockStat.java @@ -8,6 +8,7 @@ import java.util.Locale; public enum BlockStat{ health(StatCategory.general), size(StatCategory.general), + buildTime(StatCategory.general), itemCapacity(StatCategory.items), itemsMoved(StatCategory.items), diff --git a/core/src/io/anuke/mindustry/world/meta/StatCategory.java b/core/src/io/anuke/mindustry/world/meta/StatCategory.java index 5a498f0a45..fa7944c55f 100644 --- a/core/src/io/anuke/mindustry/world/meta/StatCategory.java +++ b/core/src/io/anuke/mindustry/world/meta/StatCategory.java @@ -1,5 +1,7 @@ package io.anuke.mindustry.world.meta; +import io.anuke.arc.Core; + /** A specific category for a stat. */ public enum StatCategory{ general, @@ -8,5 +10,9 @@ public enum StatCategory{ items, crafting, shooting, - optional, + optional; + + public String localized(){ + return Core.bundle.get("category." + name()); + } } diff --git a/run-server b/run-server index e5b07aa779..0358cb521b 100755 --- a/run-server +++ b/run-server @@ -5,4 +5,4 @@ if [[ $# -eq 0 ]] ; then fi ./gradlew server:dist -Pbuildversion=$1 -java -jar server/build/libs/server-release.jar +java -jar -XX:+HeapDumpOnOutOfMemoryError server/build/libs/server-release.jar diff --git a/server/src/io/anuke/mindustry/server/ServerControl.java b/server/src/io/anuke/mindustry/server/ServerControl.java index 5dcd013dac..3d1fa9f8f2 100644 --- a/server/src/io/anuke/mindustry/server/ServerControl.java +++ b/server/src/io/anuke/mindustry/server/ServerControl.java @@ -23,8 +23,8 @@ import io.anuke.mindustry.net.Packets.KickReason; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemType; -import java.io.IOException; -import java.net.BindException; +import java.io.*; +import java.net.*; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Scanner; @@ -36,6 +36,7 @@ public class ServerControl implements ApplicationListener{ private static final int roundExtraTime = 12; //in bytes: 512 kb is max private static final int maxLogLength = 1024 * 512; + private static final int commandSocketPort = 6859; private final CommandHandler handler = new CommandHandler(""); private final FileHandle logFolder = Core.files.local("logs/"); @@ -44,6 +45,9 @@ public class ServerControl implements ApplicationListener{ private boolean inExtraRound; private Task lastTask; + private Thread socketThread; + private PrintWriter socketOutput; + public ServerControl(String[] args){ Core.settings.defaults( "shufflemode", "normal", @@ -52,7 +56,8 @@ public class ServerControl implements ApplicationListener{ "shuffle", true, "crashreport", false, "port", port, - "logging", true + "logging", true, + "socket", false ); Log.setLogger(new LogHandler(){ @@ -60,7 +65,7 @@ public class ServerControl implements ApplicationListener{ @Override public void debug(String text, Object... args){ - print("&lc&fb" + "[DEBUG] " + text, args); + print("&lc&fb" + "[DEBG] " + text, args); } @Override @@ -86,6 +91,14 @@ public class ServerControl implements ApplicationListener{ if(Core.settings.getBool("logging")){ logToFile("[" + dateTime.format(LocalDateTime.now()) + "] " + format(text + "&fr", false, args)); } + + if(socketOutput != null){ + try{ + socketOutput.println(format(text + "&fr", false, args).replace("[DEBG] ", "").replace("[WARN] ", "").replace("[INFO] ", "").replace("[ERR!] ", "")); + }catch(Throwable e){ + err("Error occurred logging to socket: {0}", e.getClass().getSimpleName()); + } + } } }); @@ -161,6 +174,10 @@ public class ServerControl implements ApplicationListener{ info("&lcServer loaded. Type &ly'help'&lc for help."); System.out.print("> "); + + if(Core.settings.getBool("socket")){ + toggleSocket(true); + } } private void registerCommands(){ @@ -360,6 +377,19 @@ public class ServerControl implements ApplicationListener{ info("Strict mode is now {0}.", netServer.admins.getStrict() ? "on" : "off"); }); + handler.register("socketinput", "[on/off]", "Disables or enables a local TCP socket at port "+commandSocketPort+" to recieve commands from other applications", arg -> { + if(arg.length == 0){ + info("Socket input is currently &lc{0}.", Core.settings.getBool("socket") ? "on" : "off"); + return; + } + + boolean value = arg[0].equalsIgnoreCase("on"); + toggleSocket(value); + Core.settings.put("socket", value); + Core.settings.save(); + info("Socket input is now &lc{0}.", value ? "on" : "off"); + }); + handler.register("allow-custom-clients", "[on/off]", "Allow or disallow custom clients.", arg -> { if(arg.length == 0){ info("Custom clients are currently &lc{0}.", netServer.admins.allowsCustomClients() ? "allowed" : "disallowed"); @@ -615,39 +645,40 @@ public class ServerControl implements ApplicationListener{ Scanner scan = new Scanner(System.in); while(scan.hasNext()){ String line = scan.nextLine(); - - Core.app.post(() -> { - Response response = handler.handleMessage(line); - - if(response.type == ResponseType.unknownCommand){ - - int minDst = 0; - Command closest = null; - - for(Command command : handler.getCommandList()){ - int dst = Strings.levenshtein(command.text, response.runCommand); - if(dst < 3 && (closest == null || dst < minDst)){ - minDst = dst; - closest = command; - } - } - - if(closest != null){ - err("Command not found. Did you mean \"" + closest.text + "\"?"); - }else{ - err("Invalid command. Type 'help' for help."); - } - }else if(response.type == ResponseType.fewArguments){ - err("Too few command arguments. Usage: " + response.command.text + " " + response.command.paramText); - }else if(response.type == ResponseType.manyArguments){ - err("Too many command arguments. Usage: " + response.command.text + " " + response.command.paramText); - } - - System.out.print("> "); - }); + Core.app.post(() -> handleCommandString(line)); } } + private void handleCommandString(String line){ + Response response = handler.handleMessage(line); + + if(response.type == ResponseType.unknownCommand){ + + int minDst = 0; + Command closest = null; + + for(Command command : handler.getCommandList()){ + int dst = Strings.levenshtein(command.text, response.runCommand); + if(dst < 3 && (closest == null || dst < minDst)){ + minDst = dst; + closest = command; + } + } + + if(closest != null){ + err("Command not found. Did you mean \"" + closest.text + "\"?"); + }else{ + err("Invalid command. Type 'help' for help."); + } + }else if(response.type == ResponseType.fewArguments){ + err("Too few command arguments. Usage: " + response.command.text + " " + response.command.paramText); + }else if(response.type == ResponseType.manyArguments){ + err("Too many command arguments. Usage: " + response.command.text + " " + response.command.paramText); + } + + System.out.print("> "); + } + private void play(boolean wait, Runnable run){ inExtraRound = true; Runnable r = () -> { @@ -723,4 +754,38 @@ public class ServerControl implements ApplicationListener{ currentLogFile.writeString(text + "\n", true); } + + private void toggleSocket(boolean on){ + if(on && socketThread == null){ + socketThread = new Thread(() -> { + try{ + try(ServerSocket socket = new ServerSocket()){ + socket.bind(new InetSocketAddress("localhost", commandSocketPort)); + while(true){ + Socket client = socket.accept(); + info("&lmRecieved command socket connection: &lb{0}", socket.getLocalSocketAddress()); + BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); + socketOutput = new PrintWriter(client.getOutputStream(), true); + String line; + while(client.isConnected() && (line = in.readLine()) != null){ + String result = line; + Core.app.post(() -> handleCommandString(result)); + } + info("&lmLost command socket connection: &lb{0}", socket.getLocalSocketAddress()); + socketOutput = null; + } + } + }catch(IOException e){ + err("Terminating socket server."); + e.printStackTrace(); + } + }); + socketThread.setDaemon(true); + socketThread.start(); + }else if(socketThread != null){ + socketThread.interrupt(); + socketThread = null; + socketOutput = null; + } + } } diff --git a/tests/src/test/java/power/PowerTestFixture.java b/tests/src/test/java/power/PowerTestFixture.java index fd3bff5eca..c7f8f0f355 100644 --- a/tests/src/test/java/power/PowerTestFixture.java +++ b/tests/src/test/java/power/PowerTestFixture.java @@ -43,9 +43,9 @@ public class PowerTestFixture{ }}; } - protected static Battery createFakeBattery(float capacity, float ticksToFill){ + protected static Battery createFakeBattery(float capacity){ return new Battery("fakebattery"){{ - consumes.powerBuffered(capacity, ticksToFill); + consumes.powerBuffered(capacity); }}; } @@ -55,12 +55,6 @@ public class PowerTestFixture{ }}; } - protected static Block createFakeBufferedConsumer(float capacity, float ticksToFill){ - return new PowerBlock("fakebufferedconsumer"){{ - consumes.powerBuffered(capacity, ticksToFill); - }}; - } - /** * Creates a fake tile on the given location using the given block. * @param x The X coordinate. diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index c52a9532f3..0b9ea6cb23 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -68,54 +68,6 @@ public class PowerTests extends PowerTestFixture{ assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); } - /** Tests the satisfaction of a single buffered consumer after a single update of the power graph which contains a single producer. */ - @TestFactory - DynamicTest[] bufferedConsumerSatisfactionIsAsExpected(){ - return new DynamicTest[]{ - // Note: powerPerTick may not be 0 in any of the test cases. This would equal a "ticksToFill" of infinite. - // Note: Due to a fixed delta of 0.5, only half of what is defined here will in fact be produced/consumed. Keep this in mind when defining expectedSatisfaction! - dynamicTest("01", () -> simulateBufferedConsumption(0.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power anywhere")), - dynamicTest("02", () -> simulateBufferedConsumption(0.0f, 1.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power provided")), - dynamicTest("03", () -> simulateBufferedConsumption(1.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power requested")), - dynamicTest("04", () -> simulateBufferedConsumption(1.0f, 1.0f, 1.0f, 0.0f, 0.5f, "Empty Buffer, Stable Power, One tick to fill")), - dynamicTest("05", () -> simulateBufferedConsumption(2.0f, 1.0f, 2.0f, 0.0f, 1.0f, "Empty Buffer, Stable Power, One delta to fill")), - dynamicTest("06", () -> simulateBufferedConsumption(1.0f, 1.0f, 0.1f, 0.0f, 0.05f, "Empty Buffer, Stable Power, multiple ticks to fill")), - dynamicTest("07", () -> simulateBufferedConsumption(1.2f, 0.5f, 1.0f, 0.0f, 1.0f, "Empty Buffer, Power excess, one delta to fill")), - dynamicTest("08", () -> simulateBufferedConsumption(1.0f, 0.5f, 0.1f, 0.0f, 0.1f, "Empty Buffer, Power excess, multiple ticks to fill")), - dynamicTest("09", () -> simulateBufferedConsumption(1.0f, 1.0f, 2.0f, 0.0f, 0.5f, "Empty Buffer, Power shortage, one delta to fill")), - dynamicTest("10", () -> simulateBufferedConsumption(0.5f, 1.0f, 0.1f, 0.0f, 0.05f, "Empty Buffer, Power shortage, multiple ticks to fill")), - dynamicTest("11", () -> simulateBufferedConsumption(0.0f, 1.0f, 0.1f, 0.5f, 0.5f, "Unchanged buffer with no power produced")), - dynamicTest("12", () -> simulateBufferedConsumption(1.0f, 1.0f, 0.1f, 1.0f, 1.0f, "Unchanged buffer when already full")), - dynamicTest("13", () -> simulateBufferedConsumption(0.2f, 1.0f, 0.5f, 0.5f, 0.6f, "Half buffer, power shortage")), - dynamicTest("14", () -> simulateBufferedConsumption(1.0f, 1.0f, 0.5f, 0.9f, 1.0f, "Buffer does not get exceeded")), - dynamicTest("15", () -> simulateBufferedConsumption(2.0f, 1.0f, 1.0f, 0.5f, 1.0f, "Half buffer, filled with excess")) - }; - } - - void simulateBufferedConsumption(float producedPower, float maxBuffer, float powerConsumedPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){ - Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); - producerTile.entity().productionEfficiency = 1f; - Tile bufferedConsumerTile = createFakeTile(0, 1, createFakeBufferedConsumer(maxBuffer, maxBuffer > 0.0f ? maxBuffer / powerConsumedPerTick : 1.0f)); - bufferedConsumerTile.entity.power.satisfaction = initialSatisfaction; - - PowerGraph powerGraph = new PowerGraph(); - powerGraph.add(producerTile); - powerGraph.add(bufferedConsumerTile); - - assertEquals(producedPower * Time.delta(), powerGraph.getPowerProduced(), Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Produced power did not match"); - float expectedPowerUsage; - if(initialSatisfaction == 1.0f){ - expectedPowerUsage = 0f; - }else{ - expectedPowerUsage = Math.min(maxBuffer, powerConsumedPerTick * Time.delta()); - } - assertEquals(expectedPowerUsage, powerGraph.getPowerNeeded(), Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Consumed power did not match"); - - // Update and check for the expected power satisfaction of the consumer - powerGraph.update(); - assertEquals(expectedSatisfaction, bufferedConsumerTile.entity.power.satisfaction, Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of buffered consumer did not match"); - } - /** * Tests the satisfaction of a single direct consumer after a single update of the power graph which contains a single producer and a single battery. * The used battery is created with a maximum capacity of 100 and receives ten power per tick. @@ -150,7 +102,7 @@ public class PowerTests extends PowerTestFixture{ powerGraph.add(directConsumerTile); } float maxCapacity = 100f; - Tile batteryTile = createFakeTile(0, 2, createFakeBattery(maxCapacity, 10)); + Tile batteryTile = createFakeTile(0, 2, createFakeBattery(maxCapacity)); batteryTile.entity.power.satisfaction = initialBatteryCapacity / maxCapacity; powerGraph.add(batteryTile);