From a894873d6b0cf70b1ec09922975399536d4be982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baltaz=C3=A1r=20Radics?= Date: Sun, 11 Nov 2018 17:34:32 +0100 Subject: [PATCH 01/67] reset my fork, started work on using power consumers --- .../io/anuke/mindustry/world/BaseBlock.java | 28 +++++++++---------- .../mindustry/world/blocks/BlockPart.java | 16 ----------- .../world/consumers/ConsumePower.java | 19 ++++++------- .../world/consumers/ConsumePowerExact.java | 16 ----------- .../mindustry/world/modules/PowerModule.java | 9 +----- 5 files changed, 23 insertions(+), 65 deletions(-) delete mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePowerExact.java diff --git a/core/src/io/anuke/mindustry/world/BaseBlock.java b/core/src/io/anuke/mindustry/world/BaseBlock.java index 6cc2c79119..bdd664e40c 100644 --- a/core/src/io/anuke/mindustry/world/BaseBlock.java +++ b/core/src/io/anuke/mindustry/world/BaseBlock.java @@ -25,13 +25,12 @@ public abstract class BaseBlock extends MappableContent{ public boolean outputsLiquid = false; public boolean singleLiquid = true; - public boolean consumesPower = true; + public boolean consumesPower; public boolean outputsPower; public int itemCapacity; public float liquidCapacity = 10f; public float liquidFlowFactor = 4.9f; - public float powerCapacity = 10f; public Consumers consumes = new Consumers(); public Producers produces = new Producers(); @@ -40,6 +39,18 @@ public abstract class BaseBlock extends MappableContent{ return true; } + public float getPowerProduction(Tile tile){ + return 0f; + } + + public float getPowerConsumption(Tile tile){ + return 0f; + } + + public float usePower(Tile tile){ + return 0f; + } + /**Returns the amount of items this block can accept.*/ public int acceptStack(Item item, int amount, Tile tile, Unit source){ if(acceptItem(item, tile, tile) && hasItems && (source == null || source.getTeam() == tile.getTeam())){ @@ -98,19 +109,6 @@ public abstract class BaseBlock extends MappableContent{ tile.entity.liquids.add(liquid, amount); } - public boolean acceptPower(Tile tile, Tile source, float amount){ - return true; - } - - /**Returns how much power is accepted.*/ - public float addPower(Tile tile, float amount){ - float canAccept = Math.min(powerCapacity - tile.entity.power.amount, amount); - - tile.entity.power.amount += canAccept; - - return canAccept; - } - public void tryDumpLiquid(Tile tile, Liquid liquid){ Array proximity = tile.entity.proximity(); int dump = tile.getDump(); diff --git a/core/src/io/anuke/mindustry/world/blocks/BlockPart.java b/core/src/io/anuke/mindustry/world/blocks/BlockPart.java index fdb3e34561..476829489e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/BlockPart.java +++ b/core/src/io/anuke/mindustry/world/blocks/BlockPart.java @@ -59,22 +59,6 @@ public class BlockPart extends Block{ block.handleLiquid(tile.getLinked(), source, liquid, amount); } - @Override - public float addPower(Tile tile, float amount){ - Block block = linked(tile); - if(block.hasPower){ - return block.addPower(tile.getLinked(), amount); - }else{ - return amount; - } - } - - @Override - public boolean acceptPower(Tile tile, Tile from, float amount){ - Block block = linked(tile); - return block.hasPower && block.acceptPower(tile.getLinked(), from, amount); - } - private Block linked(Tile tile){ return tile.getLinked().block(); } diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java index fcbb35995e..135e53cea3 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -1,11 +1,12 @@ package io.anuke.mindustry.world.consumers; +import io.anuke.ucore.scene.ui.layout.Table; + import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.BlockStats; import io.anuke.mindustry.world.meta.StatUnit; -import io.anuke.ucore.scene.ui.layout.Table; public class ConsumePower extends Consume{ protected final float use; @@ -24,15 +25,9 @@ public class ConsumePower extends Consume{ return "icon-power"; } - @Override - public void update(Block block, TileEntity entity){ - if(entity.power == null) return; - entity.power.amount -= Math.min(use(block, entity), entity.power.amount); - } - @Override public boolean valid(Block block, TileEntity entity){ - return entity.power != null && entity.power.amount >= use(block, entity); + return entity.power.satisfaction >= 1; } @Override @@ -40,7 +35,11 @@ public class ConsumePower extends Consume{ stats.add(BlockStat.powerUse, use * 60f, StatUnit.powerSecond); } - protected float use(Block block, TileEntity entity){ - return Math.min(use * entity.delta(), block.powerCapacity); + public float getUse(Block block, TileEntity entity){ + return use * entity.delta(); + } + + public void addPower(float amount) { + entity.power.satisfaction = amount / getUse(); } } diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerExact.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerExact.java deleted file mode 100644 index 8e9dfdd1b7..0000000000 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerExact.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.anuke.mindustry.world.consumers; - -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.world.Block; - -public class ConsumePowerExact extends ConsumePower{ - - public ConsumePowerExact(float use){ - super(use); - } - - @Override - protected float use(Block block, TileEntity entity){ - return this.use; - } -} diff --git a/core/src/io/anuke/mindustry/world/modules/PowerModule.java b/core/src/io/anuke/mindustry/world/modules/PowerModule.java index 96b889209a..25dafb4ae2 100644 --- a/core/src/io/anuke/mindustry/world/modules/PowerModule.java +++ b/core/src/io/anuke/mindustry/world/modules/PowerModule.java @@ -8,14 +8,12 @@ import java.io.DataOutput; import java.io.IOException; public class PowerModule extends BlockModule{ - public float amount; + public float satisfaction; public PowerGraph graph = new PowerGraph(); public IntArray links = new IntArray(); @Override public void write(DataOutput stream) throws IOException{ - stream.writeFloat(amount); - stream.writeShort(links.size); for(int i = 0; i < links.size; i++){ stream.writeInt(links.get(i)); @@ -24,11 +22,6 @@ public class PowerModule extends BlockModule{ @Override public void read(DataInput stream) throws IOException{ - amount = stream.readFloat(); - if(Float.isNaN(amount)){ - amount = 0f; - } - short amount = stream.readShort(); for(int i = 0; i < amount; i++){ links.add(stream.readInt()); From 8091bec6bf8861ed56997c0b2ae76bbd9434335a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baltaz=C3=A1r=20Radics?= Date: Tue, 13 Nov 2018 08:40:32 +0100 Subject: [PATCH 02/67] added buffered power consumer --- .../io/anuke/mindustry/world/BaseBlock.java | 8 ------- .../world/consumers/ConsumePowerBuffered.java | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 8 deletions(-) create mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java diff --git a/core/src/io/anuke/mindustry/world/BaseBlock.java b/core/src/io/anuke/mindustry/world/BaseBlock.java index bdd664e40c..258ecc0bb6 100644 --- a/core/src/io/anuke/mindustry/world/BaseBlock.java +++ b/core/src/io/anuke/mindustry/world/BaseBlock.java @@ -43,14 +43,6 @@ public abstract class BaseBlock extends MappableContent{ return 0f; } - public float getPowerConsumption(Tile tile){ - return 0f; - } - - public float usePower(Tile tile){ - return 0f; - } - /**Returns the amount of items this block can accept.*/ public int acceptStack(Item item, int amount, Tile tile, Unit source){ if(acceptItem(item, tile, tile) && hasItems && (source == null || source.getTeam() == tile.getTeam())){ diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java new file mode 100644 index 0000000000..3d06635dd6 --- /dev/null +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java @@ -0,0 +1,21 @@ +package io.anuke.mindustry.world.consumers; + +import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.consumers.ConsumePower; + +public class ConsumePowerBuffered extends ConsumePower{ + @Override + public float getUse(Block block, TileEntity entity){ + return use * (1 - entity.power.satisfaction); + } + + @Override + public void addPower(float amount){ + entity.power.satisfaction = Math.min(entity.power.satisfaction + (amount / use), 1); + } + + public void usePower(float amount){ + entity.power.satisfaction = Math.max(entity.power.satisfaction - (amount / use), 0); + } +} From 285ee665ee23b86574d3f4810c0c0654bdc9fdfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baltaz=C3=A1r=20Radics?= Date: Sun, 18 Nov 2018 23:12:54 +0100 Subject: [PATCH 03/67] rewritten PowerGraph --- .../mindustry/content/blocks/PowerBlocks.java | 8 +-- .../io/anuke/mindustry/world/BaseBlock.java | 2 + .../world/blocks/power/ItemGenerator.java | 30 +-------- .../world/blocks/power/PowerGenerator.java | 21 ++++++- .../world/blocks/power/PowerGraph.java | 62 +++++++++++-------- .../world/blocks/power/SolarGenerator.java | 34 ---------- .../world/consumers/ConsumePower.java | 45 -------------- .../world/consumers/ConsumePowerBuffered.java | 21 ------- .../mindustry/world/modules/PowerModule.java | 1 + 9 files changed, 63 insertions(+), 161 deletions(-) delete mode 100644 core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java delete mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePower.java delete mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index 1420f0c838..1c13f76742 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -43,13 +43,13 @@ public class PowerBlocks extends BlockList implements ContentList{ itemDuration = 220f; }}; - solarPanel = new SolarGenerator("solar-panel"){{ - generation = 0.0045f; + solarPanel = new PowerGenerator("solar-panel"){{ + powerGeneration = 0.0045f; }}; - largeSolarPanel = new SolarGenerator("solar-panel-large"){{ + largeSolarPanel = new PowerGenerator("solar-panel-large"){{ + powerGeneration = 0.055f; size = 3; - generation = 0.055f; }}; thoriumReactor = new NuclearReactor("thorium-reactor"){{ diff --git a/core/src/io/anuke/mindustry/world/BaseBlock.java b/core/src/io/anuke/mindustry/world/BaseBlock.java index 258ecc0bb6..bb4466221f 100644 --- a/core/src/io/anuke/mindustry/world/BaseBlock.java +++ b/core/src/io/anuke/mindustry/world/BaseBlock.java @@ -28,6 +28,8 @@ public abstract class BaseBlock extends MappableContent{ public boolean consumesPower; public boolean outputsPower; + public float basePowerUse = 0; + public int itemCapacity; public float liquidCapacity = 10f; public float liquidFlowFactor = 4.9f; diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java index 285cebb5fc..a8d8da9699 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java @@ -25,7 +25,6 @@ import static io.anuke.mindustry.Vars.tilesize; public abstract class ItemGenerator extends PowerGenerator{ protected float minItemEfficiency = 0.2f; - protected float powerOutput; protected float itemDuration = 70f; protected Effect generateEffect = BlockFx.generatespark, explodeEffect = BlockFx.generatespark; @@ -46,13 +45,6 @@ public abstract class ItemGenerator extends PowerGenerator{ topRegion = Draw.region(name + "-top"); } - @Override - public void setStats(){ - super.setStats(); - - stats.add(BlockStat.basePowerGeneration, powerOutput * 60f * 0.5f, StatUnit.powerSecond); - } - @Override public void setBars(){ super.setBars(); @@ -84,22 +76,16 @@ public abstract class ItemGenerator extends PowerGenerator{ public void update(Tile tile){ ItemGeneratorEntity entity = tile.entity(); - float maxPower = Math.min(powerCapacity - entity.power.amount, powerOutput * entity.delta()) * entity.efficiency; - if(entity.generateTime <= 0f && entity.items.total() > 0){ Effects.effect(generateEffect, tile.worldx() + Mathf.range(3f), tile.worldy() + Mathf.range(3f)); Item item = entity.items.take(); - entity.efficiency = getItemEfficiency(item); + entity.productionEfficiency = getItemEfficiency(item); entity.explosiveness = item.explosiveness; entity.generateTime = 1f; } - entity.power.graph.update(); - if(entity.generateTime > 0f){ - entity.generateTime -= 1f / itemDuration * entity.delta(); - entity.power.amount += maxPower; - entity.generateTime = Mathf.clamp(entity.generateTime); + entity.generateTime -= Math.min(1f / itemDuration * entity.delta(), entity.generateTime); if(Mathf.chance(entity.delta() * 0.06 * Mathf.clamp(entity.explosiveness - 0.25f))){ //this block is run last so that in the event of a block destruction, no code relies on the block type @@ -117,18 +103,6 @@ public abstract class ItemGenerator extends PowerGenerator{ } public static class ItemGeneratorEntity extends GeneratorEntity{ - public float efficiency; public float explosiveness; - - @Override - public void write(DataOutput stream) throws IOException{ - stream.writeFloat(efficiency); - } - - @Override - public void read(DataInput stream) throws IOException{ - efficiency = stream.readFloat(); - } } - } diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index 8423ffefa2..64c7c836eb 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -1,10 +1,15 @@ package io.anuke.mindustry.world.blocks.power; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.world.meta.BlockFlag; import io.anuke.ucore.util.EnumSet; +import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.meta.BlockFlag; +import io.anuke.mindustry.world.meta.BlockStat; + public class PowerGenerator extends PowerDistributor{ + public float powerProduction; + public BlockStat generationType = BlockStat.basePowerGeneration; public PowerGenerator(String name){ super(name); @@ -12,6 +17,17 @@ public class PowerGenerator extends PowerDistributor{ flags = EnumSet.of(BlockFlag.producer); } + @Override + public void setStats(){ + super.setStats(); + stats.add(generationType, baseGeneration * 60f, StatUnit.powerSecond); + } + + @Override + public float getPowerProduction(Tile tile){ + return powerProduction * tile.entity().productionEfficiency * tile.entity.delta(); + } + @Override public boolean outputsItems(){ return false; @@ -24,5 +40,6 @@ public class PowerGenerator extends PowerDistributor{ public static class GeneratorEntity extends TileEntity{ public float generateTime; + public float productionEfficiency = 1; } } 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 5fecfbeba3..d817d423a2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -17,6 +17,7 @@ public class PowerGraph{ private final ObjectSet producers = new ObjectSet<>(); private final ObjectSet consumers = new ObjectSet<>(); + private final ObjectSet batteries = new ObjectSet<>(); private final ObjectSet all = new ObjectSet<>(); private long lastFrameUpdated; @@ -38,38 +39,43 @@ public class PowerGraph{ lastFrameUpdated = threads.getFrameID(); - float totalInput = 0f; - + float powerProduced = 0f; for(Tile producer : producers){ - totalInput += producer.entity.power.amount; + totalInput += producer.block().getPowerProduction(producer); } - for(Tile producer : producers){ - float accumulator = producer.entity.power.amount; + float powerNeeded = 0f; + for(Tile consumer : consumers){ + powerNeeded += consumer.block().basePowerUse + consumer.entity.power.extraUse; + } - if(accumulator <= 0.0001f) continue; + float totalAccumulator = 0f; + float totalCapacity = 0f; + for(Tile battery : batteries){ + totalAccumulator += battery.entity.power.satisfaction * battery.block().basePowerUse; + totalCapacity += (1f - battery.entity.power.satisfaction) * battery.block().basePowerUse; + } - float toEach = accumulator / consumers.size; - float outputs = 0f; - - for(Tile tile : consumers){ - outputs += Math.min(tile.block().powerCapacity - tile.entity.power.amount, toEach) / toEach; + if(powerNeeded > powerProduced){ + float accumulatorUsed = Math.min(totalAccumulator, powerNeeded - powerProduced); + float thing = 1f - (accumulatorUsed / totalAccumulator); + for(Tile battery : batteries){ + battery.entity.power.satisfaction *= thing; } + powerProduced += accumulatorUsed; + } - float finalEach = toEach / outputs * Timers.delta(); - float buffer = 0f; + float powerSatisfaction = Math.max(1, powerProduced / powerNeeded); + for(Tile consumer : producers){ + consumer.power.satisfaction = powerSatisfaction; + } - if(Float.isNaN(finalEach) || Float.isInfinite(finalEach)){ - continue; + if(powerProduced > powerNeeded){ + powerProduced -= powerNeeded; + float thing = Math.min(1, powerProduced / totalCapacity); + for(tile battery : batteries){ + battery.power.satisfaction += (1 - battery.power.satisfaction) * thing; } - - for(Tile tile : consumers){ - float used = Math.min(tile.block().powerCapacity - tile.entity.power.amount, finalEach) * accumulator / totalInput; - buffer += used; - tile.entity.power.amount += used; - } - - producer.entity.power.amount -= buffer; } } @@ -83,11 +89,11 @@ public class PowerGraph{ tile.entity.power.graph = this; all.add(tile); - if(tile.block().outputsPower){ + if(tile.block().outputsPower && tile.block().consumesPower){ + batteries.add(tile); + }else if(tile.block().outputsPower){ producers.add(tile); - } - - if(tile.block().consumesPower){ + }else if(tile.block().consumesPower){ consumers.add(tile); } } @@ -99,6 +105,7 @@ public class PowerGraph{ all.clear(); producers.clear(); consumers.clear(); + batteries.clear(); } public void reflow(Tile tile){ @@ -146,6 +153,7 @@ public class PowerGraph{ return "PowerGraph{" + "producers=" + producers + ", consumers=" + consumers + + ", batteries=" + batteries + ", all=" + all + ", lastFrameUpdated=" + lastFrameUpdated + ", graphID=" + graphID + diff --git a/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java deleted file mode 100644 index 02c3bfc245..0000000000 --- a/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java +++ /dev/null @@ -1,34 +0,0 @@ -package io.anuke.mindustry.world.blocks.power; - -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.StatUnit; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.util.EnumSet; - -public class SolarGenerator extends PowerGenerator{ - /** - * power generated per frame - */ - protected float generation = 0.005f; - - public SolarGenerator(String name){ - super(name); - flags = EnumSet.of(); - } - - @Override - public void setStats(){ - super.setStats(); - - stats.add(BlockStat.basePowerGeneration, generation * 60f, StatUnit.powerSecond); - } - - @Override - public void update(Tile tile){ - addPower(tile, generation * Timers.delta()); - - tile.entity.power.graph.update(); - } - -} diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java deleted file mode 100644 index 135e53cea3..0000000000 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ /dev/null @@ -1,45 +0,0 @@ -package io.anuke.mindustry.world.consumers; - -import io.anuke.ucore.scene.ui.layout.Table; - -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.BlockStats; -import io.anuke.mindustry.world.meta.StatUnit; - -public class ConsumePower extends Consume{ - protected final float use; - - public ConsumePower(float use){ - this.use = use; - } - - @Override - public void buildTooltip(Table table){ - - } - - @Override - public String getIcon(){ - return "icon-power"; - } - - @Override - public boolean valid(Block block, TileEntity entity){ - return entity.power.satisfaction >= 1; - } - - @Override - public void display(BlockStats stats){ - stats.add(BlockStat.powerUse, use * 60f, StatUnit.powerSecond); - } - - public float getUse(Block block, TileEntity entity){ - return use * entity.delta(); - } - - public void addPower(float amount) { - entity.power.satisfaction = amount / getUse(); - } -} diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java deleted file mode 100644 index 3d06635dd6..0000000000 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.anuke.mindustry.world.consumers; - -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.consumers.ConsumePower; - -public class ConsumePowerBuffered extends ConsumePower{ - @Override - public float getUse(Block block, TileEntity entity){ - return use * (1 - entity.power.satisfaction); - } - - @Override - public void addPower(float amount){ - entity.power.satisfaction = Math.min(entity.power.satisfaction + (amount / use), 1); - } - - public void usePower(float amount){ - entity.power.satisfaction = Math.max(entity.power.satisfaction - (amount / use), 0); - } -} diff --git a/core/src/io/anuke/mindustry/world/modules/PowerModule.java b/core/src/io/anuke/mindustry/world/modules/PowerModule.java index 25dafb4ae2..363a406661 100644 --- a/core/src/io/anuke/mindustry/world/modules/PowerModule.java +++ b/core/src/io/anuke/mindustry/world/modules/PowerModule.java @@ -9,6 +9,7 @@ import java.io.IOException; public class PowerModule extends BlockModule{ public float satisfaction; + public float extraUse = 0f; public PowerGraph graph = new PowerGraph(); public IntArray links = new IntArray(); From 6ba8a6a60098a18f257e18694f3b53a842bc2f2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baltaz=C3=A1r=20Radics?= Date: Sun, 18 Nov 2018 23:47:03 +0100 Subject: [PATCH 04/67] Added support for buffered power consumers --- core/src/io/anuke/mindustry/world/BaseBlock.java | 1 + core/src/io/anuke/mindustry/world/Block.java | 7 +++++-- .../mindustry/world/blocks/power/PowerGraph.java | 14 +++++++++++--- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/BaseBlock.java b/core/src/io/anuke/mindustry/world/BaseBlock.java index 35f88b88af..4b02f03ae4 100644 --- a/core/src/io/anuke/mindustry/world/BaseBlock.java +++ b/core/src/io/anuke/mindustry/world/BaseBlock.java @@ -28,6 +28,7 @@ public abstract class BaseBlock extends MappableContent{ public boolean consumesPower; public boolean outputsPower; + public boolean bufferedPowerConsumer = false; public float basePowerUse = 0; public int itemCapacity; diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 093d9b77de..da24fe4f3c 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -330,7 +330,10 @@ public class Block extends BaseBlock { consumes.forEach(cons -> cons.display(stats)); - if(hasPower) stats.add(BlockStat.powerCapacity, powerCapacity, StatUnit.powerUnits); + if(hasPower){ + if(bufferedPowerConsumer) stats.add(BlockStat.powerUse, basePowerUse, StatUnit.powerUnits); + else stats.add(BlockStat.powerCapacity, basePowerUse, StatUnit.powerUnits); + } if(hasLiquids) stats.add(BlockStat.liquidCapacity, liquidCapacity, StatUnit.liquidUnits); if(hasItems) stats.add(BlockStat.itemCapacity, itemCapacity, StatUnit.items); } @@ -550,4 +553,4 @@ public class Block extends BaseBlock { "entity.graph", tile.entity.power != null && tile.entity.power.graph != null ? tile.entity.power.graph.getID() : null ); } -} \ No newline at end of file +} 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 6f6ce996e8..d67424cd80 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -45,7 +45,11 @@ public class PowerGraph{ float powerNeeded = 0f; for(Tile consumer : consumers){ - powerNeeded += consumer.block().basePowerUse + consumer.entity.power.extraUse; + if(consumer.block().bufferedPowerConsumer){ + powerNeeded += (1f - consumer.entity.power.satisfaction) * consumer.block().basePowerUse; + }else{ + powerNeeded += consumer.block().basePowerUse + consumer.entity.power.extraUse; + } } float totalAccumulator = 0f; @@ -64,9 +68,13 @@ public class PowerGraph{ powerProduced += accumulatorUsed; } - float powerSatisfaction = Math.max(1, powerProduced / powerNeeded); + float powerSatisfaction = Math.min(1, powerProduced / powerNeeded); for(Tile consumer : producers){ - consumer.power.satisfaction = powerSatisfaction; + if(consumer.block().bufferedPowerConsumer){ + consumer.power.satisfaction += (1 - consumer.power.satisfaction) * powerSatisfaction; + }else{ + consumer.power.satisfaction = powerSatisfaction; + } } if(powerProduced > powerNeeded){ From 8218cfc72ce090faebd76115d944bc83113d4de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Baltaz=C3=A1r=20Radics?= Date: Mon, 19 Nov 2018 22:00:45 +0100 Subject: [PATCH 05/67] cleaned up PowerGraph --- .../world/blocks/power/PowerGraph.java | 88 ++++++++++++------- 1 file changed, 58 insertions(+), 30 deletions(-) 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 d67424cd80..335db266b8 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -31,18 +31,15 @@ public class PowerGraph{ return graphID; } - public void update(){ - if(threads.getFrameID() == lastFrameUpdated || consumers.size == 0 || producers.size == 0){ - return; - } - - lastFrameUpdated = threads.getFrameID(); - + public float getPowerProduced(){ float powerProduced = 0f; for(Tile producer : producers){ totalInput += producer.block().getPowerProduction(producer); } + return powerProduced; + } + public float getPowrerNeeded(){ float powerNeeded = 0f; for(Tile consumer : consumers){ if(consumer.block().bufferedPowerConsumer){ @@ -51,39 +48,70 @@ public class PowerGraph{ powerNeeded += consumer.block().basePowerUse + consumer.entity.power.extraUse; } } + return powerNeeded; + } + public float getBatteryStored(){ float totalAccumulator = 0f; - float totalCapacity = 0f; for(Tile battery : batteries){ totalAccumulator += battery.entity.power.satisfaction * battery.block().basePowerUse; + } + } + + public float getBatteryCapacity(){ + float totalCapacity = 0f; + for(Tile battery : batteries){ totalCapacity += (1f - battery.entity.power.satisfaction) * battery.block().basePowerUse; } + } + + public float useBatteries(float needed){ + float stored = getBatteryStored(); + float used = Math.min(stored, needed); + float thing = 1f - (used / stored); + for(Tile battery : batteries){ + battery.entity.power.satisfaction *= thing; + } + return used; + } + + public float chargeBatteries(float excess){ + float capacity = getBatteryCapacity(); + float thing = Math.min(1, excess / capacity); + for(tile battery : batteries){ + battery.power.satisfaction += (1 - battery.power.satisfaction) * thing; + } + return Math.min(excess, capacity); + } + + public void distributePower(float needed, float produced){ + float satisfaction = Math.min(1, produced / needed); + for(Tile consumer : consumers){ + if(consumer.block().bufferedPowerConsumer){ + consumer.power.satisfaction += (1 - consumer.power.satisfaction) * satisfaction; + }else{ + consumer.power.satisfaction = satisfaction; + } + } + } + + public void update(){ + if(threads.getFrameID() == lastFrameUpdated || consumers.size == 0 || producers.size == 0){ + return; + } + + lastFrameUpdated = threads.getFrameID(); + + float powerNeeded = getPowrerNeeded(); + float powerProduced = getPowerProduced(); if(powerNeeded > powerProduced){ - float accumulatorUsed = Math.min(totalAccumulator, powerNeeded - powerProduced); - float thing = 1f - (accumulatorUsed / totalAccumulator); - for(Tile battery : batteries){ - battery.entity.power.satisfaction *= thing; - } - powerProduced += accumulatorUsed; + powerProduced += useBatteries(powerNeeded - powerProduced); + }else if(powerProduced > powerNeeded){ + powerProduced -= chargeBatteries(powerProduced - powerNeeded); } - float powerSatisfaction = Math.min(1, powerProduced / powerNeeded); - for(Tile consumer : producers){ - if(consumer.block().bufferedPowerConsumer){ - consumer.power.satisfaction += (1 - consumer.power.satisfaction) * powerSatisfaction; - }else{ - consumer.power.satisfaction = powerSatisfaction; - } - } - - if(powerProduced > powerNeeded){ - powerProduced -= powerNeeded; - float thing = Math.min(1, powerProduced / totalCapacity); - for(tile battery : batteries){ - battery.power.satisfaction += (1 - battery.power.satisfaction) * thing; - } - } + distributePower(powerNeeded, powerProduced); } public void add(PowerGraph graph){ From f5b8d8a60c20d506243bc05eaed71c62a89c4325 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Mon, 19 Nov 2018 22:11:11 +0100 Subject: [PATCH 06/67] Fixed compiler errors/typos in PowerGraph --- .../mindustry/world/blocks/power/PowerGraph.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) 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 335db266b8..6e00709e9f 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -34,12 +34,12 @@ public class PowerGraph{ public float getPowerProduced(){ float powerProduced = 0f; for(Tile producer : producers){ - totalInput += producer.block().getPowerProduction(producer); + powerProduced += producer.block().getPowerProduction(producer); } return powerProduced; } - public float getPowrerNeeded(){ + public float getPowerNeeded(){ float powerNeeded = 0f; for(Tile consumer : consumers){ if(consumer.block().bufferedPowerConsumer){ @@ -56,6 +56,7 @@ public class PowerGraph{ for(Tile battery : batteries){ totalAccumulator += battery.entity.power.satisfaction * battery.block().basePowerUse; } + return totalAccumulator; } public float getBatteryCapacity(){ @@ -63,6 +64,7 @@ public class PowerGraph{ for(Tile battery : batteries){ totalCapacity += (1f - battery.entity.power.satisfaction) * battery.block().basePowerUse; } + return totalCapacity; } public float useBatteries(float needed){ @@ -78,8 +80,8 @@ public class PowerGraph{ public float chargeBatteries(float excess){ float capacity = getBatteryCapacity(); float thing = Math.min(1, excess / capacity); - for(tile battery : batteries){ - battery.power.satisfaction += (1 - battery.power.satisfaction) * thing; + for(Tile battery : batteries){ + battery.entity.power.satisfaction += (1 - battery.entity.power.satisfaction) * thing; } return Math.min(excess, capacity); } @@ -88,9 +90,9 @@ public class PowerGraph{ float satisfaction = Math.min(1, produced / needed); for(Tile consumer : consumers){ if(consumer.block().bufferedPowerConsumer){ - consumer.power.satisfaction += (1 - consumer.power.satisfaction) * satisfaction; + consumer.entity.power.satisfaction += (1 - consumer.entity.power.satisfaction) * satisfaction; }else{ - consumer.power.satisfaction = satisfaction; + consumer.entity.power.satisfaction = satisfaction; } } } @@ -102,7 +104,7 @@ public class PowerGraph{ lastFrameUpdated = threads.getFrameID(); - float powerNeeded = getPowrerNeeded(); + float powerNeeded = getPowerNeeded(); float powerProduced = getPowerProduced(); if(powerNeeded > powerProduced){ From f561768c67659b10ce2410de064898d36fd319ae Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Tue, 20 Nov 2018 22:33:36 +0100 Subject: [PATCH 07/67] Handled compiler errors by either resolving them or commenting & adding a TODO --- .../content/blocks/CraftingBlocks.java | 28 ++++++++----------- .../mindustry/content/blocks/DebugBlocks.java | 13 +++++---- .../content/blocks/DefenseBlocks.java | 6 ++-- .../content/blocks/DistributionBlocks.java | 2 +- .../content/blocks/LiquidBlocks.java | 12 ++++---- .../mindustry/content/blocks/PowerBlocks.java | 23 ++++++++------- .../content/blocks/ProductionBlocks.java | 12 ++++---- .../content/blocks/TurretBlocks.java | 12 ++++---- .../mindustry/content/blocks/UnitBlocks.java | 16 +++++------ .../content/blocks/UpgradeBlocks.java | 18 ++++++------ .../maps/generation/FortressGenerator.java | 6 ++-- .../io/anuke/mindustry/world/BaseBlock.java | 3 ++ core/src/io/anuke/mindustry/world/Block.java | 10 +++---- .../world/blocks/defense/ForceProjector.java | 18 ++++++------ .../blocks/defense/turrets/PowerTurret.java | 10 +++++-- .../world/blocks/distribution/MassDriver.java | 9 ++++-- .../world/blocks/power/FusionReactor.java | 10 +++++-- .../blocks/power/ItemLiquidGenerator.java | 15 ++++++---- .../world/blocks/power/LiquidGenerator.java | 6 ++-- .../blocks/power/LiquidHeatGenerator.java | 2 ++ .../world/blocks/power/NuclearReactor.java | 11 ++++++-- .../world/blocks/power/PowerGenerator.java | 4 ++- .../world/blocks/power/PowerGraph.java | 2 ++ .../world/blocks/power/PowerNode.java | 1 - .../world/blocks/production/Incinerator.java | 2 +- .../mindustry/world/blocks/units/MechPad.java | 17 +++++++---- .../world/blocks/units/Reconstructor.java | 19 +++++++------ .../world/blocks/units/RepairPoint.java | 6 ++-- .../mindustry/world/consumers/Consumers.java | 6 ---- .../mindustry/world/modules/PowerModule.java | 5 ++++ 30 files changed, 174 insertions(+), 130 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/CraftingBlocks.java b/core/src/io/anuke/mindustry/content/blocks/CraftingBlocks.java index aed1ecf0a0..8666f382c0 100644 --- a/core/src/io/anuke/mindustry/content/blocks/CraftingBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/CraftingBlocks.java @@ -38,7 +38,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ fluxNeeded = 2; consumes.items(new ItemStack[]{new ItemStack(Items.copper, 1), new ItemStack(Items.lead, 2)}); - consumes.power(0.1f); + basePowerUse = 0.1f; }}; siliconsmelter = new PowerSmelter("silicon-smelter"){{ @@ -46,13 +46,12 @@ public class CraftingBlocks extends BlockList implements ContentList{ craftEffect = BlockFx.smeltsmoke; result = Items.silicon; craftTime = 40f; - powerCapacity = 20f; size = 2; hasLiquids = false; flameColor = Color.valueOf("ffef99"); consumes.items(new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.sand, 2)}); - consumes.power(0.05f); + basePowerUse = 0.05f; }}; plastaniumCompressor = new PlastaniumCompressor("plastanium-compressor"){{ @@ -61,7 +60,6 @@ public class CraftingBlocks extends BlockList implements ContentList{ craftTime = 60f; output = Items.plastanium; itemCapacity = 30; - powerCapacity = 40f; size = 2; health = 320; hasPower = hasLiquids = true; @@ -69,7 +67,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ updateEffect = BlockFx.plasticburn; consumes.liquid(Liquids.oil, 0.25f); - consumes.power(0.3f); + basePowerUse = 0.3f; consumes.item(Items.titanium, 2); }}; @@ -77,24 +75,22 @@ public class CraftingBlocks extends BlockList implements ContentList{ craftEffect = BlockFx.smeltsmoke; result = Items.phasefabric; craftTime = 120f; - powerCapacity = 50f; size = 2; consumes.items(new ItemStack[]{new ItemStack(Items.thorium, 4), new ItemStack(Items.sand, 10)}); - consumes.power(0.5f); + basePowerUse = 0.5f; }}; alloySmelter = new PowerSmelter("alloy-smelter"){{ craftEffect = BlockFx.smeltsmoke; result = Items.surgealloy; craftTime = 75f; - powerCapacity = 60f; size = 2; useFlux = true; fluxNeeded = 3; - consumes.power(0.4f); + basePowerUse = 0.4f; consumes.items(new ItemStack[]{new ItemStack(Items.titanium, 2), new ItemStack(Items.lead, 4), new ItemStack(Items.silicon, 3), new ItemStack(Items.copper, 3)}); }}; @@ -105,7 +101,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ size = 2; hasPower = true; - consumes.power(0.1f); + basePowerUse = 0.1f; consumes.item(Items.titanium); consumes.liquid(Liquids.water, 0.3f); }}; @@ -120,7 +116,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ consumes.liquid(Liquids.oil, 0.05f); consumes.item(Items.pyratite, 1); - consumes.power(0.04f); + basePowerUse = 0.04f; }}; pyratiteMixer = new PowerSmelter("pyratite-mixer"){{ @@ -132,7 +128,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ size = 2; - consumes.power(0.02f); + basePowerUse = 0.02f; consumes.items(new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.lead, 2), new ItemStack(Items.sand, 2)}); }}; @@ -144,7 +140,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ craftTime = 10f; hasLiquids = hasPower = true; - consumes.power(0.1f); + basePowerUse = 0.1f; consumes.item(Items.stone, 2); }}; @@ -189,7 +185,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ size = 2; consumes.item(Items.stone, 2); - consumes.power(0.2f); + basePowerUse = 0.2f; consumes.liquid(Liquids.water, 0.5f); }}; @@ -204,7 +200,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ hasLiquids = true; consumes.item(Items.biomatter, 1); - consumes.power(0.06f); + basePowerUse = 0.06f; }}; pulverizer = new Pulverizer("pulverizer"){{ @@ -217,7 +213,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ hasItems = hasPower = true; consumes.item(Items.stone, 1); - consumes.power(0.05f); + basePowerUse = 0.05f; }}; solidifier = new GenericCrafter("solidifer"){{ diff --git a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java index db663a422a..c7d034b89c 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java @@ -40,7 +40,8 @@ public class DebugBlocks extends BlockList implements ContentList{ public void load(){ powerVoid = new PowerBlock("powervoid"){ { - powerCapacity = Float.MAX_VALUE; + // TODO Adapt to new power system if necessary + basePowerUse = Float.MAX_VALUE; shadow = "shadow-round-1"; } @@ -53,23 +54,23 @@ public class DebugBlocks extends BlockList implements ContentList{ @Override public void init(){ super.init(); - stats.remove(BlockStat.powerCapacity); + // TODO Adapt to new power system if necessary + //stats.remove(BlockStat.powerCapacity); } }; powerInfinite = new PowerNode("powerinfinite"){ { - powerCapacity = 10000f; maxNodes = 100; outputsPower = true; consumesPower = false; shadow = "shadow-round-1"; } + // TODO Adapt to new power system if necessary @Override - public void update(Tile tile){ - super.update(tile); - tile.entity.power.amount = powerCapacity; + public float getPowerProduction(Tile tile){ + return 10000f; } }; diff --git a/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java index 6a8b586c7b..aeac0daeb3 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java @@ -71,19 +71,19 @@ public class DefenseBlocks extends BlockList implements ContentList{ }}; mendProjector = new MendProjector("mend-projector"){{ - consumes.power(0.2f); + basePowerUse = 0.2f; size = 2; consumes.item(Items.phasefabric).optional(true); }}; overdriveProjector = new OverdriveProjector("overdrive-projector"){{ - consumes.power(0.35f); + basePowerUse = 0.35f; size = 2; consumes.item(Items.phasefabric).optional(true); }}; forceProjector = new ForceProjector("force-projector"){{ - consumes.power(0.2f); + basePowerUse = 0.2f; size = 3; consumes.item(Items.phasefabric).optional(true); }}; diff --git a/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java index 825b7097cb..3d3df81740 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java @@ -35,7 +35,7 @@ public class DistributionBlocks extends BlockList implements ContentList{ phaseConveyor = new ItemBridge("phase-conveyor"){{ range = 11; hasPower = true; - consumes.power(0.05f); + basePowerUse = 0.05f; }}; sorter = new Sorter("sorter"); diff --git a/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java b/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java index a9e9f97155..69fd859c9a 100644 --- a/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java @@ -20,9 +20,10 @@ public class LiquidBlocks extends BlockList implements ContentList{ rotaryPump = new Pump("rotary-pump"){{ shadow = "shadow-rounded-2"; pumpAmount = 0.2f; - consumes.power(0.015f); + basePowerUse = 0.015f; liquidCapacity = 30f; - powerCapacity = 20f; + // TODO Verify: No longer buffered + basePowerUse = 20f / 60f; hasPower = true; size = 2; tier = 1; @@ -31,10 +32,11 @@ public class LiquidBlocks extends BlockList implements ContentList{ thermalPump = new Pump("thermal-pump"){{ shadow = "shadow-rounded-2"; pumpAmount = 0.275f; - consumes.power(0.03f); + basePowerUse = 0.03f; liquidCapacity = 40f; hasPower = true; - powerCapacity = 20f; + // TODO Verify: No longer buffered + basePowerUse = 20f / 60f; size = 2; tier = 2; }}; @@ -69,7 +71,7 @@ public class LiquidBlocks extends BlockList implements ContentList{ phaseConduit = new LiquidBridge("phase-conduit"){{ range = 11; hasPower = true; - consumes.power(0.05f); + basePowerUse = 0.05f; }}; } } diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index c5c7f58232..25ce084e29 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -13,41 +13,40 @@ public class PowerBlocks extends BlockList implements ContentList{ @Override public void load(){ combustionGenerator = new BurnerGenerator("combustion-generator"){{ - powerOutput = 0.09f; - powerCapacity = 40f; + powerProduction = 0.09f; itemDuration = 40f; }}; thermalGenerator = new LiquidHeatGenerator("thermal-generator"){{ maxLiquidGenerate = 4f; - powerCapacity = 40f; + // TODO: Adapt to new power system + powerProduction = -1; powerPerLiquid = 0.1f; generateEffect = BlockFx.redgeneratespark; size = 2; }}; turbineGenerator = new TurbineGenerator("turbine-generator"){{ - powerOutput = 0.28f; - powerCapacity = 40f; + // TODO: Adapt to new power system + powerProduction = 0.28f; + powerPerLiquid = 0.1f; itemDuration = 30f; - powerPerLiquid = 0.7f; consumes.liquid(Liquids.water, 0.05f); size = 2; }}; rtgGenerator = new DecayGenerator("rtg-generator"){{ - powerCapacity = 40f; size = 2; - powerOutput = 0.3f; + powerProduction = 0.3f; itemDuration = 220f; }}; solarPanel = new PowerGenerator("solar-panel"){{ - powerGeneration = 0.0045f; + powerProduction = 0.0045f; }}; largeSolarPanel = new PowerGenerator("solar-panel-large"){{ - powerGeneration = 0.055f; + powerProduction = 0.055f; size = 3; }}; @@ -63,12 +62,12 @@ public class PowerBlocks extends BlockList implements ContentList{ }}; battery = new Battery("battery"){{ - powerCapacity = 320f; + basePowerUse = 320f; }}; batteryLarge = new Battery("battery-large"){{ size = 3; - powerCapacity = 2000f; + basePowerUse = 2000f; }}; powerNode = new PowerNode("power-node"){{ diff --git a/core/src/io/anuke/mindustry/content/blocks/ProductionBlocks.java b/core/src/io/anuke/mindustry/content/blocks/ProductionBlocks.java index 8bd571620b..b1e8bff026 100644 --- a/core/src/io/anuke/mindustry/content/blocks/ProductionBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/ProductionBlocks.java @@ -38,7 +38,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ updateEffect = BlockFx.pulverizeMedium; drillEffect = BlockFx.mineBig; - consumes.power(0.11f); + basePowerUse = 0.11f; }}; blastDrill = new Drill("blast-drill"){{ @@ -53,7 +53,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ rotateSpeed = 6f; warmupSpeed = 0.01f; - consumes.power(0.3f); + basePowerUse = 0.3f; }}; plasmaDrill = new Drill("plasma-drill"){{ @@ -70,7 +70,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ drillEffect = BlockFx.mineHuge; warmupSpeed = 0.005f; - consumes.power(0.7f); + basePowerUse = 0.7f; }}; waterExtractor = new SolidPump("water-extractor"){{ @@ -80,7 +80,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ liquidCapacity = 30f; rotateSpeed = 1.4f; - consumes.power(0.09f); + basePowerUse = 0.09f; }}; oilExtractor = new Fracker("oil-extractor"){{ @@ -93,7 +93,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ liquidCapacity = 30f; consumes.item(Items.sand); - consumes.power(0.3f); + basePowerUse = 0.3f; consumes.liquid(Liquids.water, 0.15f); }}; @@ -104,7 +104,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ hasLiquids = true; hasPower = true; - consumes.power(0.08f); + basePowerUse = 0.08f; consumes.liquid(Liquids.water, 0.2f); }}; diff --git a/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java b/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java index bce9e3bb5a..b33c665ab3 100644 --- a/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java @@ -102,8 +102,8 @@ public class TurretBlocks extends BlockList implements ContentList{ recoil = 2f; reload = 100f; cooldown = 0.03f; - powerUsed = 20f; - powerCapacity = 60f; + powerUsed = 1 / 3f; + basePowerUse = 60f; // capacity shootShake = 2f; shootEffect = ShootFx.lancerLaserShoot; smokeEffect = ShootFx.lancerLaserShootSmoke; @@ -121,8 +121,8 @@ public class TurretBlocks extends BlockList implements ContentList{ shootShake = 1f; shootCone = 40f; rotatespeed = 8f; - powerUsed = 7f; - powerCapacity = 30f; + powerUsed = 7 / 30f; + basePowerUse = 30f; // capacity range = 150f; shootEffect = ShootFx.lightningShoot; heatColor = Color.RED; @@ -255,8 +255,8 @@ public class TurretBlocks extends BlockList implements ContentList{ recoil = 4f; size = 4; shootShake = 2f; - powerUsed = 60f; - powerCapacity = 120f; + powerUsed = 0.5f; + basePowerUse = 120f; // capacity range = 160f; reload = 200f; firingMoveFract = 0.1f; diff --git a/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java b/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java index b7d9809cbb..1aa39afd22 100644 --- a/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java @@ -20,7 +20,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.spirit; produceTime = 5700; size = 2; - consumes.power(0.08f); + basePowerUse = 0.08f; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 30)}); }}; @@ -28,7 +28,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.phantom; produceTime = 7300; size = 2; - consumes.power(0.2f); + basePowerUse = 0.2f; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 70), new ItemStack(Items.lead, 80), new ItemStack(Items.titanium, 80)}); }}; @@ -36,7 +36,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.wraith; produceTime = 1800; size = 2; - consumes.power(0.1f); + basePowerUse = 0.1f; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 10), new ItemStack(Items.titanium, 10)}); }}; @@ -44,7 +44,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.ghoul; produceTime = 3600; size = 3; - consumes.power(0.2f); + basePowerUse = 0.2f; shadow = "shadow-round-3"; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 30), new ItemStack(Items.titanium, 30), new ItemStack(Items.plastanium, 20)}); }}; @@ -53,7 +53,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.revenant; produceTime = 8000; size = 4; - consumes.power(0.3f); + basePowerUse = 0.3f; shadow = "shadow-round-4"; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 80), new ItemStack(Items.titanium, 80), new ItemStack(Items.plastanium, 50)}); }}; @@ -62,7 +62,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.dagger; produceTime = 1700; size = 2; - consumes.power(0.05f); + basePowerUse = 0.05f; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 10)}); }}; @@ -70,7 +70,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.titan; produceTime = 3400; size = 3; - consumes.power(0.15f); + basePowerUse = 0.15f; shadow = "shadow-round-3"; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 20), new ItemStack(Items.thorium, 30)}); }}; @@ -79,7 +79,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.fortress; produceTime = 5000; size = 3; - consumes.power(0.2f); + basePowerUse = 0.2f; shadow = "shadow-round-3"; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 40), new ItemStack(Items.thorium, 50)}); }}; diff --git a/core/src/io/anuke/mindustry/content/blocks/UpgradeBlocks.java b/core/src/io/anuke/mindustry/content/blocks/UpgradeBlocks.java index 5e991fd9e7..7cb28cdd28 100644 --- a/core/src/io/anuke/mindustry/content/blocks/UpgradeBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/UpgradeBlocks.java @@ -9,55 +9,57 @@ public class UpgradeBlocks extends BlockList{ @Override public void load(){ + // Note: MechPads are buffered; all basePowerUse values represent total capacity + alphaPad = new MechPad("alpha-mech-pad"){{ mech = Mechs.alpha; size = 2; - powerCapacity = 50f; + basePowerUse = 50f; }}; deltaPad = new MechPad("delta-mech-pad"){{ mech = Mechs.delta; size = 2; - powerCapacity = 70f; + basePowerUse = 70f; }}; tauPad = new MechPad("tau-mech-pad"){{ mech = Mechs.tau; size = 2; - powerCapacity = 100f; + basePowerUse = 100f; }}; omegaPad = new MechPad("omega-mech-pad"){{ mech = Mechs.omega; size = 3; - powerCapacity = 120f; + basePowerUse = 120f; }}; dartPad = new MechPad("dart-ship-pad"){{ mech = Mechs.dart; size = 2; - powerCapacity = 50f; + basePowerUse = 50f; shadow = "shadow-rounded-2"; }}; javelinPad = new MechPad("javelin-ship-pad"){{ mech = Mechs.javelin; size = 2; - powerCapacity = 80f; + basePowerUse = 80f; shadow = "shadow-rounded-2"; }}; tridentPad = new MechPad("trident-ship-pad"){{ mech = Mechs.trident; size = 2; - powerCapacity = 100f; + basePowerUse = 100f; shadow = "shadow-rounded-2"; }}; glaivePad = new MechPad("glaive-ship-pad"){{ mech = Mechs.glaive; size = 3; - powerCapacity = 120f; + basePowerUse = 120f; shadow = "shadow-round-3"; }}; } diff --git a/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java b/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java index def5cf1e0c..7cde4bc33c 100644 --- a/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java +++ b/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java @@ -25,7 +25,6 @@ import io.anuke.mindustry.world.blocks.defense.turrets.PowerTurret; import io.anuke.mindustry.world.blocks.defense.turrets.Turret; import io.anuke.mindustry.world.blocks.power.NuclearReactor; import io.anuke.mindustry.world.blocks.power.PowerGenerator; -import io.anuke.mindustry.world.blocks.power.SolarGenerator; import io.anuke.mindustry.world.blocks.storage.CoreBlock; import io.anuke.mindustry.world.blocks.storage.StorageBlock; import io.anuke.mindustry.world.blocks.units.UnitFactory; @@ -115,7 +114,8 @@ public class FortressGenerator{ seeder.get(PowerBlocks.solarPanel, tile -> tile.block() == PowerBlocks.largeSolarPanel && gen.random.chance(0.3)), //coal gens - seeder.get(PowerBlocks.combustionGenerator, tile -> tile.block() instanceof SolarGenerator && gen.random.chance(0.2)), + // TODO Verify - This used to be solar panel + seeder.get(PowerBlocks.combustionGenerator, tile -> tile.block() instanceof PowerGenerator && gen.random.chance(0.2)), //water extractors seeder.get(ProductionBlocks.waterExtractor, tile -> tile.block() instanceof NuclearReactor && gen.random.chance(0.5)), @@ -181,7 +181,7 @@ public class FortressGenerator{ Block block = tile.block(); if(block instanceof PowerTurret){ - tile.entity.power.amount = block.powerCapacity; + tile.entity.power.satisfaction = 1.0f; }else if(block instanceof ItemTurret){ ItemTurret turret = (ItemTurret)block; AmmoType[] type = turret.getAmmoTypes(); diff --git a/core/src/io/anuke/mindustry/world/BaseBlock.java b/core/src/io/anuke/mindustry/world/BaseBlock.java index 4b02f03ae4..6a729435ce 100644 --- a/core/src/io/anuke/mindustry/world/BaseBlock.java +++ b/core/src/io/anuke/mindustry/world/BaseBlock.java @@ -29,6 +29,9 @@ public abstract class BaseBlock extends MappableContent{ public boolean outputsPower; public boolean bufferedPowerConsumer = false; + /** In case of unbuffered consumers, this stores the amount of power which is required per tick in order to work at maximum efficiency. + * In case of buffered consumers, this stores the maximum power capacity. + */ public float basePowerUse = 0; public int itemCapacity; diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index da24fe4f3c..c04987bdd1 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -331,8 +331,8 @@ public class Block extends BaseBlock { consumes.forEach(cons -> cons.display(stats)); if(hasPower){ - if(bufferedPowerConsumer) stats.add(BlockStat.powerUse, basePowerUse, StatUnit.powerUnits); - else stats.add(BlockStat.powerCapacity, basePowerUse, StatUnit.powerUnits); + if(bufferedPowerConsumer) stats.add(BlockStat.powerCapacity, basePowerUse, StatUnit.powerUnits); + else stats.add(BlockStat.powerUse, basePowerUse * 60, StatUnit.powerUnits); } if(hasLiquids) stats.add(BlockStat.liquidCapacity, liquidCapacity, StatUnit.liquidUnits); if(hasItems) stats.add(BlockStat.itemCapacity, itemCapacity, StatUnit.items); @@ -340,7 +340,7 @@ public class Block extends BaseBlock { //TODO make this easier to config. public void setBars(){ - if(hasPower) bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.amount / powerCapacity)); + if(bufferedPowerConsumer) bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); if(hasLiquids) bars.add(new BlockBar(BarType.liquid, true, tile -> tile.entity.liquids.total() / liquidCapacity)); if(hasItems) @@ -406,8 +406,8 @@ public class Block extends BaseBlock { explosiveness += tile.entity.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2f); } - if(hasPower){ - power += tile.entity.power.amount; + if(bufferedPowerConsumer){ + power += tile.entity.power.satisfaction * tile.block().basePowerUse; } tempColor.mul(1f / units); 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 343faa4a9e..c89cee750b 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java @@ -51,7 +51,7 @@ public class ForceProjector extends Block { hasPower = true; canOverdrive = false; hasLiquids = true; - powerCapacity = 60f; + basePowerUse = 60f; hasItems = true; itemCapacity = 10; consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).optional(true).update(false); @@ -106,8 +106,9 @@ public class ForceProjector extends Block { } }else{ entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.1f); - float powerUse = Math.min(powerDamage * entity.delta() * (1f + entity.buildup / breakage), powerCapacity); - entity.power.amount -= powerUse; + // TODO Adapt power calculations to new power system +// float powerUse = Math.min(powerDamage * entity.delta() * (1f + entity.buildup / breakage), powerCapacity); +// entity.power.amount -= powerUse; } if(entity.buildup > 0){ @@ -144,11 +145,12 @@ public class ForceProjector extends Block { Effects.effect(BulletFx.absorb, trait); float hit = trait.getShieldDamage()*powerDamage; entity.hit = 1f; - entity.power.amount -= Math.min(hit, entity.power.amount); - - if(entity.power.amount <= 0.0001f){ - entity.buildup += trait.getShieldDamage() * entity.warmup*2f; - } + // TODO Adapt power calculations to new power system +// entity.power.amount -= Math.min(hit, entity.power.amount); +// +// if(entity.power.amount <= 0.0001f){ +// entity.buildup += trait.getShieldDamage() * entity.warmup*2f; +// } entity.buildup += trait.getShieldDamage() * entity.warmup; } }); 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 1ef8378252..1625953a3c 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 @@ -6,12 +6,15 @@ 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 AmmoType shootType; public PowerTurret(String name){ super(name); hasPower = true; + // TODO Verify for new power system + bufferedPowerConsumer = true; } @Override @@ -23,13 +26,16 @@ public abstract class PowerTurret extends CooledTurret{ @Override public boolean hasAmmo(Tile tile){ - return tile.entity.power.amount >= powerUsed; + // TODO Verify for new power system + // Allow shooting as long as the turret is at least at 50% power + return tile.entity.power.satisfaction >= powerUsed; } @Override public AmmoType useAmmo(Tile tile){ if(tile.isEnemyCheat()) return shootType; - tile.entity.power.amount -= powerUsed; + // 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); return shootType; } 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 8e33ea1e4d..e5c7141dce 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -36,6 +36,7 @@ import java.io.IOException; import static io.anuke.mindustry.Vars.*; +// TODO Adapt whole class to new power system public class MassDriver extends Block{ protected float range; protected float rotateSpeed = 0.04f; @@ -78,7 +79,8 @@ public class MassDriver extends Block{ MassDriverEntity other = target.entity(); entity.reload = 1f; - entity.power.amount = 0f; + + entity.power.satisfaction = 0f; DriverBulletData data = Pooling.obtain(DriverBulletData.class, DriverBulletData::new); data.from = entity; @@ -126,7 +128,7 @@ public class MassDriver extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.powerShot, powerCapacity, StatUnit.powerUnits); + stats.add(BlockStat.powerShot, basePowerUse, StatUnit.powerUnits); } @Override @@ -166,7 +168,8 @@ public class MassDriver extends Block{ entity.rotation = Mathf.slerpDelta(entity.rotation, tile.angleTo(waiter), rotateSpeed); }else if(tile.entity.items.total() >= minDistribute && linkValid(tile) && //only fire when at least at half-capacity and power - tile.entity.power.amount >= powerCapacity * 0.8f && + // TODO adapt + //tile.entity.power.amount >= powerCapacity * 0.8f && link.block().itemCapacity - link.entity.items.total() >= minDistribute && entity.reload <= 0.0001f){ MassDriverEntity other = link.entity(); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java b/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java index d8959d0038..632a291b3e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java @@ -24,7 +24,8 @@ public class FusionReactor extends PowerGenerator{ super(name); hasPower = true; hasLiquids = true; - powerCapacity = 100f; + // TODO Adapt to new power system + //powerCapacity = 100f; liquidCapacity = 30f; hasItems = true; } @@ -33,6 +34,8 @@ public class FusionReactor extends PowerGenerator{ public void setStats(){ super.setStats(); + // TODO Verify for new power system + stats.remove(BlockStat.basePowerGeneration); stats.add(BlockStat.basePowerGeneration, maxPowerProduced * 60f, StatUnit.powerSecond); } @@ -46,8 +49,9 @@ public class FusionReactor extends PowerGenerator{ entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.01f); } - float powerAdded = Math.min(powerCapacity - entity.power.amount, maxPowerProduced * Mathf.pow(entity.warmup, 4f) * Timers.delta()); - entity.power.amount += powerAdded; + // TODO Adapt to new power system + //float powerAdded = Math.min(powerCapacity - entity.power.amount, maxPowerProduced * Mathf.pow(entity.warmup, 4f) * Timers.delta()); + //entity.power.amount += powerAdded; entity.totalProgress += entity.warmup * Timers.delta(); tile.entity.power.graph.update(); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index fa1a44c5cf..4e7e57cc03 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -51,29 +51,34 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ if(liquid != null && entity.liquids.get(liquid) >= 0.001f && entity.cons.valid()){ float powerPerLiquid = getLiquidEfficiency(liquid) * this.powerPerLiquid; float used = Math.min(entity.liquids.get(liquid), maxLiquidGenerate * entity.delta()); - used = Math.min(used, (powerCapacity - entity.power.amount) / powerPerLiquid); + // TODO: Adapt to new power system + //used = Math.min(used, (powerCapacity - entity.power.amount) / powerPerLiquid); entity.liquids.remove(liquid, used); - entity.power.amount += used * powerPerLiquid; + // TODO: Adapt to new power system + //entity.power.amount += used * powerPerLiquid; if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); } }else if(entity.cons.valid()){ - float maxPower = Math.min(powerCapacity - entity.power.amount, powerOutput * entity.delta()) * entity.efficiency; + // TODO: Adapt to new power system + //float maxPower = Math.min(powerCapacity - entity.power.amount, powerOutput * entity.delta()) * entity.efficiency; if(entity.generateTime <= 0f && entity.items.total() > 0){ Effects.effect(generateEffect, tile.worldx() + Mathf.range(3f), tile.worldy() + Mathf.range(3f)); Item item = entity.items.take(); - entity.efficiency = getItemEfficiency(item); + // TODO: Adapt to new power system + //entity.efficiency = getItemEfficiency(item); entity.explosiveness = item.explosiveness; entity.generateTime = 1f; } if(entity.generateTime > 0f){ entity.generateTime -= 1f / itemDuration * entity.delta(); - entity.power.amount += maxPower; + // TODO: Adapt to new power system + //entity.power.amount += maxPower; entity.generateTime = Mathf.clamp(entity.generateTime); if(Mathf.chance(entity.delta() * 0.06 * Mathf.clamp(entity.explosiveness - 0.25f))){ diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java index 8a8ed8ca25..0b19f94fc0 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java @@ -53,10 +53,12 @@ public abstract class LiquidGenerator extends PowerGenerator{ if(entity.liquids.get(entity.liquids.current()) >= 0.001f){ float powerPerLiquid = getEfficiency(entity.liquids.current()) * this.powerPerLiquid; float used = Math.min(entity.liquids.currentAmount(), maxLiquidGenerate * entity.delta()); - used = Math.min(used, (powerCapacity - entity.power.amount) / powerPerLiquid); + // TODO Adapt to new power system + //used = Math.min(used, (powerCapacity - entity.power.amount) / powerPerLiquid); entity.liquids.remove(entity.liquids.current(), used); - entity.power.amount += used * powerPerLiquid; + // TODO Adapt to new power system + //entity.power.amount += used * powerPerLiquid; if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java index 67a5bed015..8b5665b4c5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java @@ -14,6 +14,8 @@ public class LiquidHeatGenerator extends LiquidGenerator{ public void setStats(){ super.setStats(); + // TODO Verify for new power system + stats.remove(BlockStat.basePowerGeneration); stats.add(BlockStat.basePowerGeneration, maxLiquidGenerate * powerPerLiquid * 60f, StatUnit.powerSecond); } diff --git a/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java b/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java index 5ea084d47a..2edeaa6f09 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java @@ -49,7 +49,8 @@ public class NuclearReactor extends PowerGenerator{ super(name); itemCapacity = 30; liquidCapacity = 50; - powerCapacity = 80f; + // TODO Adapt to new power system + //powerCapacity = 80f; hasItems = true; hasLiquids = true; @@ -75,6 +76,9 @@ public class NuclearReactor extends PowerGenerator{ public void setStats(){ super.setStats(); stats.add(BlockStat.inputLiquid, new LiquidFilterValue(liquid -> liquid.temperature <= 0.5f)); + + // TODO Verify for new power system + stats.remove(BlockStat.basePowerGeneration); stats.add(BlockStat.basePowerGeneration, powerMultiplier * 60f * 0.5f, StatUnit.powerSecond); } @@ -87,8 +91,9 @@ public class NuclearReactor extends PowerGenerator{ if(fuel > 0){ entity.heat += fullness * heating * Math.min(entity.delta(), 4f); - entity.power.amount += powerMultiplier * fullness * entity.delta(); - entity.power.amount = Mathf.clamp(entity.power.amount, 0f, powerCapacity); + // TODO Adapt to new power system + //entity.power.amount += powerMultiplier * fullness * entity.delta(); + //entity.power.amount = Mathf.clamp(entity.power.amount, 0f, powerCapacity); if(entity.timer.get(timerFuel, fuelUseTime)){ entity.items.remove(consumes.item(), 1); } diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index 64c7c836eb..b1ee6f73ce 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -1,5 +1,6 @@ package io.anuke.mindustry.world.blocks.power; +import io.anuke.mindustry.world.meta.StatUnit; import io.anuke.ucore.util.EnumSet; import io.anuke.mindustry.entities.TileEntity; @@ -8,6 +9,7 @@ import io.anuke.mindustry.world.meta.BlockFlag; import io.anuke.mindustry.world.meta.BlockStat; public class PowerGenerator extends PowerDistributor{ + /** The amount of power produced per tick. */ public float powerProduction; public BlockStat generationType = BlockStat.basePowerGeneration; @@ -20,7 +22,7 @@ public class PowerGenerator extends PowerDistributor{ @Override public void setStats(){ super.setStats(); - stats.add(generationType, baseGeneration * 60f, StatUnit.powerSecond); + stats.add(generationType, powerProduction, StatUnit.powerSecond); } @Override 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 6e00709e9f..68afdec034 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -87,6 +87,8 @@ public class PowerGraph{ } public void distributePower(float needed, float produced){ + if(needed == 0f){ return; } + float satisfaction = Math.min(1, produced / needed); for(Tile consumer : consumers){ if(consumer.block().bufferedPowerConsumer){ diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerNode.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerNode.java index 21d50003f1..42e55a53bf 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerNode.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerNode.java @@ -39,7 +39,6 @@ public class PowerNode extends PowerBlock{ super(name); expanded = true; layer = Layer.power; - powerCapacity = 5f; configurable = true; consumesPower = false; outputsPower = false; diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java b/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java index 0076498e26..46ce4c12a2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java @@ -26,7 +26,7 @@ public class Incinerator extends Block{ update = true; solid = true; - consumes.power(0.05f); + basePowerUse = 0.05f; } @Override 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 e2c226a348..581a16f745 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java @@ -17,7 +17,6 @@ import io.anuke.mindustry.graphics.Shaders; import io.anuke.mindustry.type.Mech; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.consumers.ConsumePowerExact; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Graphics; @@ -37,6 +36,7 @@ import static io.anuke.mindustry.Vars.tilesize; public class MechPad extends Block{ protected Mech mech; protected float buildTime = 60 * 5; + protected float requiredSatisfaction = 1f; protected TextureRegion openRegion; @@ -45,18 +45,19 @@ public class MechPad extends Block{ update = true; solidifes = true; hasPower = true; + bufferedPowerConsumer = true; } @Override public void init(){ - consumes.add(new ConsumePowerExact(powerCapacity * 0.8f)); super.init(); } @Override public void setStats(){ super.setStats(); - stats.remove(BlockStat.powerUse); + // TODO Verify for new power system + //stats.remove(BlockStat.powerUse); } @Override @@ -66,10 +67,14 @@ public class MechPad extends Block{ @Remote(targets = Loc.both, called = Loc.server) public static void onMechFactoryTap(Player player, Tile tile){ - if(player == null || !checkValidTap(tile, player)) return; + if(player == null || !checkValidTap(tile, player) || !(tile.block() instanceof MechPad)) return; MechFactoryEntity entity = tile.entity(); - entity.power.amount = 0f; + MechPad pad = (MechPad)tile.block(); + + if(entity.power.satisfaction < pad.requiredSatisfaction) return; + + entity.power.satisfaction -= Math.min(entity.power.satisfaction, pad.requiredSatisfaction); player.beginRespawning(entity); } @@ -102,7 +107,7 @@ public class MechPad extends Block{ protected static boolean checkValidTap(Tile tile, Player player){ MechFactoryEntity entity = tile.entity(); - return Math.abs(player.x - tile.drawx()) <= tile.block().size * tilesize / 2f && + return Math.abs(player.x - tile.drawx()) <= tile.block().size * tilesize / 2f && Math.abs(player.y - tile.drawy()) <= tile.block().size * tilesize / 2f && entity.cons.valid() && entity.player == null; } diff --git a/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java b/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java index 066f8584f2..5321273f29 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java @@ -33,7 +33,8 @@ import static io.anuke.mindustry.Vars.*; public class Reconstructor extends Block{ protected float departTime = 30f; protected float arriveTime = 40f; - protected float powerPerTeleport = 5f; + /** Stores the percentage of buffered power to be used upon teleporting. */ + protected float powerPerTeleport = 0.5f; protected Effect arriveEffect = Fx.spawn; protected TextureRegion openRegion; @@ -43,13 +44,15 @@ public class Reconstructor extends Block{ solidifes = true; hasPower = true; configurable = true; + basePowerUse = 30f; // capacity + bufferedPowerConsumer = true; } protected static boolean checkValidTap(Tile tile, ReconstructorEntity entity, Player player){ return validLink(tile, entity.link) && Math.abs(player.x - tile.drawx()) <= tile.block().size * tilesize / 2f && Math.abs(player.y - tile.drawy()) <= tile.block().size * tilesize / 2f && - entity.current == null && entity.power.amount >= ((Reconstructor) tile.block()).powerPerTeleport; + entity.current == null && entity.power.satisfaction >= ((Reconstructor) tile.block()).powerPerTeleport; } protected static boolean validLink(Tile tile, int position){ @@ -74,13 +77,13 @@ public class Reconstructor extends Block{ public static void reconstructPlayer(Player player, Tile tile){ ReconstructorEntity entity = tile.entity(); - if(!checkValidTap(tile, entity, player) || entity.power.amount < ((Reconstructor) tile.block()).powerPerTeleport) + if(!checkValidTap(tile, entity, player) || entity.power.satisfaction < ((Reconstructor) tile.block()).powerPerTeleport) return; entity.departing = true; entity.current = player; entity.solid = false; - entity.power.amount -= ((Reconstructor) tile.block()).powerPerTeleport; + entity.power.satisfaction -= Math.min(entity.power.satisfaction, ((Reconstructor) tile.block()).powerPerTeleport); entity.updateTime = 1f; entity.set(tile.drawx(), tile.drawy()); player.rotation = 90f; @@ -246,13 +249,13 @@ public class Reconstructor extends Block{ entity.updateTime -= Timers.delta() / departTime; if(entity.updateTime <= 0f){ //no power? death. - if(other.power.amount < powerPerTeleport){ + if(other.power.satisfaction < powerPerTeleport){ entity.current.setDead(true); //entity.current.setRespawning(false); entity.current = null; return; } - other.power.amount -= powerPerTeleport; + other.power.satisfaction -= Math.min(other.power.satisfaction, powerPerTeleport); other.current = entity.current; other.departing = false; other.current.set(other.x, other.y); @@ -276,8 +279,8 @@ public class Reconstructor extends Block{ if(validLink(tile, entity.link)){ Tile other = world.tile(entity.link); - if(other.entity.power.amount >= powerPerTeleport && Units.anyEntities(tile, 4f, unit -> unit.getTeam() == entity.getTeam() && unit instanceof Player) && - entity.power.amount >= powerPerTeleport){ + if(other.entity.power.satisfaction >= powerPerTeleport && Units.anyEntities(tile, 4f, unit -> unit.getTeam() == entity.getTeam() && unit instanceof Player) && + entity.power.satisfaction >= powerPerTeleport){ entity.solid = false; stayOpen = true; } 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 c581103634..1942dbb199 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java @@ -37,8 +37,9 @@ public class RepairPoint extends Block{ layer = Layer.turret; layer2 = Layer.laser; hasPower = true; - powerCapacity = 20f; - consumes.power(0.06f); + // TODO Adapt to new power system - Make it use power while repairing + basePowerUse = 20f; // capacity + bufferedPowerConsumer = true; } @Override @@ -89,6 +90,7 @@ public class RepairPoint extends Block{ }else if(entity.target != null){ entity.target.health += repairSpeed * Timers.delta() * entity.strength; entity.target.clampHealth(); + // TODO: Make it use power here and reset power once target goes null entity.rotation = Mathf.slerpDelta(entity.rotation, entity.angleTo(entity.target), 0.5f); } diff --git a/core/src/io/anuke/mindustry/world/consumers/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index cfabf5e7ca..e38607ad33 100644 --- a/core/src/io/anuke/mindustry/world/consumers/Consumers.java +++ b/core/src/io/anuke/mindustry/world/consumers/Consumers.java @@ -30,12 +30,6 @@ public class Consumers{ } } - public ConsumePower power(float amount){ - ConsumePower p = new ConsumePower(amount); - add(p); - return p; - } - public ConsumeLiquid liquid(Liquid liquid, float amount){ ConsumeLiquid c = new ConsumeLiquid(liquid, amount); add(c); diff --git a/core/src/io/anuke/mindustry/world/modules/PowerModule.java b/core/src/io/anuke/mindustry/world/modules/PowerModule.java index 363a406661..dead0056e5 100644 --- a/core/src/io/anuke/mindustry/world/modules/PowerModule.java +++ b/core/src/io/anuke/mindustry/world/modules/PowerModule.java @@ -8,7 +8,12 @@ import java.io.DataOutput; import java.io.IOException; public class PowerModule extends BlockModule{ + /** In case of unbuffered consumers, this is the percentage (1.0f = 100%) of the demanded power which can be supplied. + * Blocks will work at a reduced efficiency if this is not equal to 1.0f. + * In case of buffered consumers, this is the percentage of power stored in relation to the maximum capacity. + */ public float satisfaction; + /** Specifies power which is required additionally, e.g. while a force projector is being shot at. */ public float extraUse = 0f; public PowerGraph graph = new PowerGraph(); public IntArray links = new IntArray(); From 222d41c84adb8dd0c5bdf5047adb293ece2865ba Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Tue, 20 Nov 2018 20:58:46 +0100 Subject: [PATCH 08/67] Added Test stub --- tests/src/test/java/PowerTests.java | 91 +++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 tests/src/test/java/PowerTests.java diff --git a/tests/src/test/java/PowerTests.java b/tests/src/test/java/PowerTests.java new file mode 100644 index 0000000000..d0ea1e218c --- /dev/null +++ b/tests/src/test/java/PowerTests.java @@ -0,0 +1,91 @@ +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.blocks.Blocks; +import io.anuke.mindustry.content.blocks.PowerBlocks; +import io.anuke.mindustry.content.blocks.ProductionBlocks; +import io.anuke.mindustry.core.ContentLoader; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; +import io.anuke.mindustry.world.blocks.power.PowerGraph; +import io.anuke.mindustry.world.blocks.production.SolidPump; +import io.anuke.mindustry.world.modules.PowerModule; +import org.junit.jupiter.api.*; + +import java.lang.reflect.Field; +import java.util.LinkedList; +import java.util.List; + +import static io.anuke.mindustry.Vars.threads; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class PowerTests{ + + @BeforeAll + static void initializeDependencies(){ + Vars.content = new ContentLoader(); + Vars.content.load(); + } + + @BeforeEach + void initTest(){ + } + + /** + * Creates a fake tile on the given location using the given floor and block. + * @param x The X coordinate. + * @param y The y coordinate. + * @param floor The floor. + * @param block The block on the tile. + * @return The created tile or null in case of exceptions. + */ + private static Tile createFakeTile(int x, int y, Floor floor, Block block){ + try{ + Tile tile = new Tile(x, y); + Field field = Tile.class.getDeclaredField("wall"); + field.setAccessible(true); + field.set(tile, block); + field = Tile.class.getDeclaredField("floor"); + field.setAccessible(true); + field.set(tile, floor); + tile.entity = block.newEntity(); + tile.entity.power = new PowerModule(); + return tile; + }catch(Exception ex){ + return null; + } + } + + /** Makes sure calculations are accurate for the case where produced power = consumed power. */ + @Test + void test_balancedPower(){ + PowerGraph powerGraph = new PowerGraph(); + + // Create one water extractor (5.4 power consumed) + Tile waterExtractorTile = createFakeTile(0, 0, (Floor)Blocks.sand, ProductionBlocks.waterExtractor); + powerGraph.add(waterExtractorTile); + + // Create 20 small solar panels (20*0.27=5.4 power produced) + List solarPanelTiles = new LinkedList<>(); + float producedPowerSum = 0.0f; + for(int counter = 0; counter < 20; counter++){ + Tile solarPanelTile = createFakeTile( 2 + counter / 2, counter % 2, (Floor)Blocks.sand, PowerBlocks.solarPanel); + powerGraph.add(solarPanelTile); + solarPanelTiles.add(solarPanelTile); + } + + // If these lines fail, you probably changed power production/consumption and need to adapt this test + // TODO: Create fake blocks which are independent of such changes + powerGraph.update(); + + powerGraph.getPowerNeeded(); + powerGraph.getPowerProduced(); + +/* if(powerNeeded > powerProduced){ + powerProduced += useBatteries(powerNeeded - powerProduced); + }else if(powerProduced > powerNeeded){ + powerProduced -= chargeBatteries(powerProduced - powerNeeded); + } + + distributePower(powerNeeded, powerProduced);*/ + } +} From 558c89cc30b74a368066710fed94b88577146dae Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 21 Nov 2018 00:04:26 +0100 Subject: [PATCH 09/67] Finished first unit test (equal power input and output) and fixed bugs detected by it --- .../io/anuke/mindustry/world/BaseBlock.java | 4 +-- .../world/blocks/power/PowerGraph.java | 10 ++++--- tests/src/test/java/PowerTests.java | 28 +++++++++---------- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/BaseBlock.java b/core/src/io/anuke/mindustry/world/BaseBlock.java index 6a729435ce..6111e5a197 100644 --- a/core/src/io/anuke/mindustry/world/BaseBlock.java +++ b/core/src/io/anuke/mindustry/world/BaseBlock.java @@ -25,8 +25,8 @@ public abstract class BaseBlock extends MappableContent{ public boolean outputsLiquid = false; public boolean singleLiquid = true; - public boolean consumesPower; - public boolean outputsPower; + public boolean consumesPower = true; + public boolean outputsPower = false; public boolean bufferedPowerConsumer = false; /** In case of unbuffered consumers, this stores the amount of power which is required per tick in order to work at maximum efficiency. 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 68afdec034..9045ffb284 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -109,10 +109,12 @@ public class PowerGraph{ float powerNeeded = getPowerNeeded(); float powerProduced = getPowerProduced(); - if(powerNeeded > powerProduced){ - powerProduced += useBatteries(powerNeeded - powerProduced); - }else if(powerProduced > powerNeeded){ - powerProduced -= chargeBatteries(powerProduced - powerNeeded); + if(Math.abs(powerNeeded - powerProduced) > 0.0001f){ + if(powerNeeded > powerProduced){ + powerProduced += useBatteries(powerNeeded-powerProduced); + }else if(powerProduced > powerNeeded){ + powerProduced -= chargeBatteries(powerProduced-powerNeeded); + } } distributePower(powerNeeded, powerProduced); diff --git a/tests/src/test/java/PowerTests.java b/tests/src/test/java/PowerTests.java index d0ea1e218c..07f7e6e2ba 100644 --- a/tests/src/test/java/PowerTests.java +++ b/tests/src/test/java/PowerTests.java @@ -16,6 +16,7 @@ import java.util.LinkedList; import java.util.List; import static io.anuke.mindustry.Vars.threads; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; public class PowerTests{ @@ -60,32 +61,31 @@ public class PowerTests{ void test_balancedPower(){ PowerGraph powerGraph = new PowerGraph(); - // Create one water extractor (5.4 power consumed) + // Create one water extractor (5.4 power/Second = 0.09/tick) Tile waterExtractorTile = createFakeTile(0, 0, (Floor)Blocks.sand, ProductionBlocks.waterExtractor); powerGraph.add(waterExtractorTile); - // Create 20 small solar panels (20*0.27=5.4 power produced) + // Create 20 small solar panels (20*0.27=5.4 power/second = 0.09/tick) List solarPanelTiles = new LinkedList<>(); - float producedPowerSum = 0.0f; for(int counter = 0; counter < 20; counter++){ Tile solarPanelTile = createFakeTile( 2 + counter / 2, counter % 2, (Floor)Blocks.sand, PowerBlocks.solarPanel); powerGraph.add(solarPanelTile); solarPanelTiles.add(solarPanelTile); } + float powerNeeded = powerGraph.getPowerNeeded(); + float powerProduced = powerGraph.getPowerProduced(); + // If these lines fail, you probably changed power production/consumption and need to adapt this test + // OR their implementation is corrupt. // TODO: Create fake blocks which are independent of such changes - powerGraph.update(); + float epsilon = 0.00001f; + assertEquals(powerNeeded, 0.09f, epsilon); + assertEquals(powerProduced, 0.09f, epsilon); + // Note: The assertions above induce that powerNeeded = powerProduced (with floating point inaccuracy) - powerGraph.getPowerNeeded(); - powerGraph.getPowerProduced(); - -/* if(powerNeeded > powerProduced){ - powerProduced += useBatteries(powerNeeded - powerProduced); - }else if(powerProduced > powerNeeded){ - powerProduced -= chargeBatteries(powerProduced - powerNeeded); - } - - distributePower(powerNeeded, powerProduced);*/ + // Distribute power and make sure the water extractor is powered + powerGraph.distributePower(powerNeeded, powerProduced); + assertEquals(waterExtractorTile.entity.power.satisfaction, 1.0f, epsilon); } } From f789a3590197dd400420d112f7898dcf48fb3b4a Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 21 Nov 2018 20:07:05 +0100 Subject: [PATCH 10/67] Readded buffered and direct consumers as three classes --- .../world/consumers/ConsumePower.java | 48 +++++++++++++++++++ .../world/consumers/ConsumePowerBuffered.java | 43 +++++++++++++++++ .../world/consumers/ConsumePowerDirect.java | 43 +++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePower.java create mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java create mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java new file mode 100644 index 0000000000..8f4e829d1d --- /dev/null +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -0,0 +1,48 @@ +package io.anuke.mindustry.world.consumers; + +import io.anuke.ucore.scene.ui.layout.Table; + +import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.meta.BlockStat; +import io.anuke.mindustry.world.meta.BlockStats; +import io.anuke.mindustry.world.meta.StatUnit; + +/** Consumer class for blocks which consume power while being connected to a power graph. */ +public abstract class ConsumePower extends Consume{ + /** The maximum amount of power which can be processed per tick. This might influence efficiency or load a buffer. */ + protected final float powerPerTick; + + public ConsumePower(float powerPerTick){ + this.powerPerTick = powerPerTick; + } + + @Override + public void buildTooltip(Table table){ + // No tooltip for power + } + + @Override + public String getIcon(){ + return "icon-power"; + } + + @Override + public void update(Block block, TileEntity entity){ + // Nothing to do since PowerGraph directly updates entity.power.satisfaction + } + + // valid(...) is implemented in subclass + // display(...) is implemented in subclass + + /** + * Retrieves the amount of power which is requested for the given block and entity. + * @param block The block which needs power. + * @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){ + // TODO Is it possible to make the block not consume power while items/liquids are missing? + return powerPerTick; + } +} diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java new file mode 100644 index 0000000000..62eebdfb21 --- /dev/null +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java @@ -0,0 +1,43 @@ +package io.anuke.mindustry.world.consumers; + +import io.anuke.ucore.scene.ui.layout.Table; + +import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.meta.BlockStat; +import io.anuke.mindustry.world.meta.BlockStats; +import io.anuke.mindustry.world.meta.StatUnit; + +/** Consumer class for blocks which directly consume power without buffering it. */ +public class ConsumePowerBuffered extends ConsumePower{ + /** The maximum power capacity in power units. */ + protected final float powerCapacity; + + /** + * Adds a power buffer to the owner which takes ticksToFill number of ticks to be filled. + * Note that this object does not remove power from the buffer. + * @param powerCapacity The maximum capacity in power units. + * @param ticksToFill The number of ticks it shall take to fill the buffer. + */ + public ConsumePowerBuffered(float powerCapacity, float ticksToFill){ + super(powerCapacity / ticksToFill); + this.powerCapacity = powerCapacity; + } + + @Override + public boolean valid(Block block, TileEntity entity){ + // TODO - Verify: It might be necessary to know about the power required per shot/event here. + return true; + } + + @Override + public void display(BlockStats stats){ + stats.add(BlockStat.powerCapacity, powerCapacity, StatUnit.powerSecond); + } + + @Override + public float requestedPower(Block block, TileEntity entity){ + // Only request power until the capacity is full + return Math.max(powerPerTick, powerCapacity * (1 - entity.power.satisfaction)); + } +} diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java new file mode 100644 index 0000000000..0ce7d593a2 --- /dev/null +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java @@ -0,0 +1,43 @@ +package io.anuke.mindustry.world.consumers; + +import io.anuke.ucore.scene.ui.layout.Table; + +import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.meta.BlockStat; +import io.anuke.mindustry.world.meta.BlockStats; +import io.anuke.mindustry.world.meta.StatUnit; + +/** Consumer class for blocks which directly consume power without buffering it. */ +public class ConsumePowerDirect extends ConsumePower{ + /** The minimum power satisfaction (fraction of powerPerTick) which must be achieved before the module may work. */ + protected final float minimumSatisfaction; + + /** + * Makes the owner consume powerPerTick each tick and disables it unless 60% of that power is being supplied. + * @param powerPerTick The maximum amount of power which is required per tick for 100% efficiency. + */ + public ConsumePowerDirect(float powerPerTick){ + this(powerPerTick, 0.75f); + } + + /** + * Makes the owner consume powerPerTick each tick and disables it unless minimumSatisfaction (1.0 = 100%) of that power is being supplied. + * @param powerPerTick The maximum amount of power which is required per tick for 100% efficiency. + * @param minimumSatisfaction The percentage of powerPerTick which must be available for the module to work. + */ + public ConsumePowerDirect(float powerPerTick, float minimumSatisfaction){ + super(powerPerTick); + this.minimumSatisfaction = minimumSatisfaction; + } + + @Override + public boolean valid(Block block, TileEntity entity){ + return entity.power.satisfaction >= minimumSatisfaction; + } + + @Override + public void display(BlockStats stats){ + stats.add(BlockStat.powerUse, powerPerTick * 60f, StatUnit.powerSecond); + } +} From 405f153439b31dab089fd7986553d2a6799a2cec Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 21 Nov 2018 21:54:43 +0100 Subject: [PATCH 11/67] Reverted/Adapted blocks to new Consume interfaces --- .../content/blocks/CraftingBlocks.java | 24 +++---- .../mindustry/content/blocks/DebugBlocks.java | 2 +- .../content/blocks/DefenseBlocks.java | 6 +- .../content/blocks/DistributionBlocks.java | 2 +- .../content/blocks/LiquidBlocks.java | 10 +-- .../mindustry/content/blocks/PowerBlocks.java | 4 +- .../content/blocks/ProductionBlocks.java | 12 ++-- .../content/blocks/TurretBlocks.java | 6 +- .../mindustry/content/blocks/UnitBlocks.java | 16 ++--- .../content/blocks/UpgradeBlocks.java | 17 +++-- .../io/anuke/mindustry/world/BaseBlock.java | 6 -- core/src/io/anuke/mindustry/world/Block.java | 14 ++-- .../world/blocks/defense/ForceProjector.java | 2 +- .../blocks/defense/turrets/PowerTurret.java | 3 - .../world/blocks/distribution/MassDriver.java | 15 +++-- .../world/blocks/power/PowerGraph.java | 64 +++++++++++-------- .../world/blocks/production/Incinerator.java | 2 +- .../mindustry/world/blocks/units/MechPad.java | 1 - .../world/blocks/units/Reconstructor.java | 3 +- .../world/blocks/units/RepairPoint.java | 3 +- .../world/consumers/ConsumePowerBuffered.java | 2 +- .../world/consumers/ConsumePowerDirect.java | 8 --- .../mindustry/world/consumers/Consumers.java | 59 +++++++++++++++++ 23 files changed, 169 insertions(+), 112 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/CraftingBlocks.java b/core/src/io/anuke/mindustry/content/blocks/CraftingBlocks.java index 8666f382c0..6229ee9fb3 100644 --- a/core/src/io/anuke/mindustry/content/blocks/CraftingBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/CraftingBlocks.java @@ -38,7 +38,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ fluxNeeded = 2; consumes.items(new ItemStack[]{new ItemStack(Items.copper, 1), new ItemStack(Items.lead, 2)}); - basePowerUse = 0.1f; + consumes.powerDirect(0.1f); }}; siliconsmelter = new PowerSmelter("silicon-smelter"){{ @@ -51,7 +51,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ flameColor = Color.valueOf("ffef99"); consumes.items(new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.sand, 2)}); - basePowerUse = 0.05f; + consumes.powerDirect(0.05f); }}; plastaniumCompressor = new PlastaniumCompressor("plastanium-compressor"){{ @@ -67,7 +67,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ updateEffect = BlockFx.plasticburn; consumes.liquid(Liquids.oil, 0.25f); - basePowerUse = 0.3f; + consumes.powerDirect(0.3f); consumes.item(Items.titanium, 2); }}; @@ -78,7 +78,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ size = 2; consumes.items(new ItemStack[]{new ItemStack(Items.thorium, 4), new ItemStack(Items.sand, 10)}); - basePowerUse = 0.5f; + consumes.powerDirect(0.5f); }}; alloySmelter = new PowerSmelter("alloy-smelter"){{ @@ -90,7 +90,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ useFlux = true; fluxNeeded = 3; - basePowerUse = 0.4f; + consumes.powerDirect(0.4f); consumes.items(new ItemStack[]{new ItemStack(Items.titanium, 2), new ItemStack(Items.lead, 4), new ItemStack(Items.silicon, 3), new ItemStack(Items.copper, 3)}); }}; @@ -101,7 +101,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ size = 2; hasPower = true; - basePowerUse = 0.1f; + consumes.powerDirect(0.1f); consumes.item(Items.titanium); consumes.liquid(Liquids.water, 0.3f); }}; @@ -116,7 +116,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ consumes.liquid(Liquids.oil, 0.05f); consumes.item(Items.pyratite, 1); - basePowerUse = 0.04f; + consumes.powerDirect(0.04f); }}; pyratiteMixer = new PowerSmelter("pyratite-mixer"){{ @@ -128,7 +128,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ size = 2; - basePowerUse = 0.02f; + consumes.powerDirect(0.02f); consumes.items(new ItemStack[]{new ItemStack(Items.coal, 1), new ItemStack(Items.lead, 2), new ItemStack(Items.sand, 2)}); }}; @@ -140,7 +140,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ craftTime = 10f; hasLiquids = hasPower = true; - basePowerUse = 0.1f; + consumes.powerDirect(0.1f); consumes.item(Items.stone, 2); }}; @@ -185,7 +185,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ size = 2; consumes.item(Items.stone, 2); - basePowerUse = 0.2f; + consumes.powerDirect(0.2f); consumes.liquid(Liquids.water, 0.5f); }}; @@ -200,7 +200,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ hasLiquids = true; consumes.item(Items.biomatter, 1); - basePowerUse = 0.06f; + consumes.powerDirect(0.06f); }}; pulverizer = new Pulverizer("pulverizer"){{ @@ -213,7 +213,7 @@ public class CraftingBlocks extends BlockList implements ContentList{ hasItems = hasPower = true; consumes.item(Items.stone, 1); - basePowerUse = 0.05f; + consumes.powerDirect(0.05f); }}; solidifier = new GenericCrafter("solidifer"){{ diff --git a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java index c7d034b89c..a2fff36077 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java @@ -41,7 +41,7 @@ public class DebugBlocks extends BlockList implements ContentList{ powerVoid = new PowerBlock("powervoid"){ { // TODO Adapt to new power system if necessary - basePowerUse = Float.MAX_VALUE; + consumes.powerDirect(Float.MAX_VALUE); shadow = "shadow-round-1"; } diff --git a/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java index aeac0daeb3..e3038d569e 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java @@ -71,19 +71,19 @@ public class DefenseBlocks extends BlockList implements ContentList{ }}; mendProjector = new MendProjector("mend-projector"){{ - basePowerUse = 0.2f; + consumes.powerDirect(0.2f); size = 2; consumes.item(Items.phasefabric).optional(true); }}; overdriveProjector = new OverdriveProjector("overdrive-projector"){{ - basePowerUse = 0.35f; + consumes.powerDirect(0.35f); size = 2; consumes.item(Items.phasefabric).optional(true); }}; forceProjector = new ForceProjector("force-projector"){{ - basePowerUse = 0.2f; + consumes.powerDirect(0.2f); size = 3; consumes.item(Items.phasefabric).optional(true); }}; diff --git a/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java index 3d3df81740..15904f7e09 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java @@ -35,7 +35,7 @@ public class DistributionBlocks extends BlockList implements ContentList{ phaseConveyor = new ItemBridge("phase-conveyor"){{ range = 11; hasPower = true; - basePowerUse = 0.05f; + consumes.powerDirect(0.05f); }}; sorter = new Sorter("sorter"); diff --git a/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java b/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java index 69fd859c9a..b489a21611 100644 --- a/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java @@ -20,10 +20,10 @@ public class LiquidBlocks extends BlockList implements ContentList{ rotaryPump = new Pump("rotary-pump"){{ shadow = "shadow-rounded-2"; pumpAmount = 0.2f; - basePowerUse = 0.015f; + consumes.powerDirect(0.015f); liquidCapacity = 30f; // TODO Verify: No longer buffered - basePowerUse = 20f / 60f; + consumes.powerDirect(20f / 60f); hasPower = true; size = 2; tier = 1; @@ -32,11 +32,11 @@ public class LiquidBlocks extends BlockList implements ContentList{ thermalPump = new Pump("thermal-pump"){{ shadow = "shadow-rounded-2"; pumpAmount = 0.275f; - basePowerUse = 0.03f; + consumes.powerDirect(0.03f); liquidCapacity = 40f; hasPower = true; // TODO Verify: No longer buffered - basePowerUse = 20f / 60f; + consumes.powerDirect(20f / 60f); size = 2; tier = 2; }}; @@ -71,7 +71,7 @@ public class LiquidBlocks extends BlockList implements ContentList{ phaseConduit = new LiquidBridge("phase-conduit"){{ range = 11; hasPower = true; - basePowerUse = 0.05f; + consumes.powerDirect(0.05f); }}; } } diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index 25ce084e29..6b47d31a67 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -62,12 +62,12 @@ public class PowerBlocks extends BlockList implements ContentList{ }}; battery = new Battery("battery"){{ - basePowerUse = 320f; + consumes.powerBuffered(320f, 120f); }}; batteryLarge = new Battery("battery-large"){{ size = 3; - basePowerUse = 2000f; + consumes.powerBuffered(2000f, 600f); }}; powerNode = new PowerNode("power-node"){{ diff --git a/core/src/io/anuke/mindustry/content/blocks/ProductionBlocks.java b/core/src/io/anuke/mindustry/content/blocks/ProductionBlocks.java index b1e8bff026..2e68c38b99 100644 --- a/core/src/io/anuke/mindustry/content/blocks/ProductionBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/ProductionBlocks.java @@ -38,7 +38,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ updateEffect = BlockFx.pulverizeMedium; drillEffect = BlockFx.mineBig; - basePowerUse = 0.11f; + consumes.powerDirect(0.11f); }}; blastDrill = new Drill("blast-drill"){{ @@ -53,7 +53,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ rotateSpeed = 6f; warmupSpeed = 0.01f; - basePowerUse = 0.3f; + consumes.powerDirect(0.3f); }}; plasmaDrill = new Drill("plasma-drill"){{ @@ -70,7 +70,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ drillEffect = BlockFx.mineHuge; warmupSpeed = 0.005f; - basePowerUse = 0.7f; + consumes.powerDirect(0.7f); }}; waterExtractor = new SolidPump("water-extractor"){{ @@ -80,7 +80,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ liquidCapacity = 30f; rotateSpeed = 1.4f; - basePowerUse = 0.09f; + consumes.powerDirect(0.09f); }}; oilExtractor = new Fracker("oil-extractor"){{ @@ -93,7 +93,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ liquidCapacity = 30f; consumes.item(Items.sand); - basePowerUse = 0.3f; + consumes.powerDirect(0.3f); consumes.liquid(Liquids.water, 0.15f); }}; @@ -104,7 +104,7 @@ public class ProductionBlocks extends BlockList implements ContentList{ hasLiquids = true; hasPower = true; - basePowerUse = 0.08f; + consumes.powerDirect(0.08f); consumes.liquid(Liquids.water, 0.2f); }}; diff --git a/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java b/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java index b33c665ab3..6e3fc0dc03 100644 --- a/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java @@ -103,7 +103,7 @@ public class TurretBlocks extends BlockList implements ContentList{ reload = 100f; cooldown = 0.03f; powerUsed = 1 / 3f; - basePowerUse = 60f; // capacity + consumes.powerBuffered(60f); shootShake = 2f; shootEffect = ShootFx.lancerLaserShoot; smokeEffect = ShootFx.lancerLaserShootSmoke; @@ -122,7 +122,7 @@ public class TurretBlocks extends BlockList implements ContentList{ shootCone = 40f; rotatespeed = 8f; powerUsed = 7 / 30f; - basePowerUse = 30f; // capacity + consumes.powerBuffered(30f); range = 150f; shootEffect = ShootFx.lightningShoot; heatColor = Color.RED; @@ -256,7 +256,7 @@ public class TurretBlocks extends BlockList implements ContentList{ size = 4; shootShake = 2f; powerUsed = 0.5f; - basePowerUse = 120f; // capacity + consumes.powerBuffered(120f); range = 160f; reload = 200f; firingMoveFract = 0.1f; diff --git a/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java b/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java index 1aa39afd22..54560e6ab9 100644 --- a/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/UnitBlocks.java @@ -20,7 +20,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.spirit; produceTime = 5700; size = 2; - basePowerUse = 0.08f; + consumes.powerDirect(0.08f); consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 30)}); }}; @@ -28,7 +28,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.phantom; produceTime = 7300; size = 2; - basePowerUse = 0.2f; + consumes.powerDirect(0.2f); consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 70), new ItemStack(Items.lead, 80), new ItemStack(Items.titanium, 80)}); }}; @@ -36,7 +36,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.wraith; produceTime = 1800; size = 2; - basePowerUse = 0.1f; + consumes.powerDirect(0.1f); consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 10), new ItemStack(Items.titanium, 10)}); }}; @@ -44,7 +44,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.ghoul; produceTime = 3600; size = 3; - basePowerUse = 0.2f; + consumes.powerDirect(0.2f); shadow = "shadow-round-3"; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 30), new ItemStack(Items.titanium, 30), new ItemStack(Items.plastanium, 20)}); }}; @@ -53,7 +53,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.revenant; produceTime = 8000; size = 4; - basePowerUse = 0.3f; + consumes.powerDirect(0.3f); shadow = "shadow-round-4"; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 80), new ItemStack(Items.titanium, 80), new ItemStack(Items.plastanium, 50)}); }}; @@ -62,7 +62,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.dagger; produceTime = 1700; size = 2; - basePowerUse = 0.05f; + consumes.powerDirect(0.05f); consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 10)}); }}; @@ -70,7 +70,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.titan; produceTime = 3400; size = 3; - basePowerUse = 0.15f; + consumes.powerDirect(0.15f); shadow = "shadow-round-3"; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 20), new ItemStack(Items.thorium, 30)}); }}; @@ -79,7 +79,7 @@ public class UnitBlocks extends BlockList implements ContentList{ type = UnitTypes.fortress; produceTime = 5000; size = 3; - basePowerUse = 0.2f; + consumes.powerDirect(0.2f); shadow = "shadow-round-3"; consumes.items(new ItemStack[]{new ItemStack(Items.silicon, 40), new ItemStack(Items.thorium, 50)}); }}; diff --git a/core/src/io/anuke/mindustry/content/blocks/UpgradeBlocks.java b/core/src/io/anuke/mindustry/content/blocks/UpgradeBlocks.java index 7cb28cdd28..a5e20a0cca 100644 --- a/core/src/io/anuke/mindustry/content/blocks/UpgradeBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/UpgradeBlocks.java @@ -9,57 +9,56 @@ public class UpgradeBlocks extends BlockList{ @Override public void load(){ - // Note: MechPads are buffered; all basePowerUse values represent total capacity alphaPad = new MechPad("alpha-mech-pad"){{ mech = Mechs.alpha; size = 2; - basePowerUse = 50f; + consumes.powerBuffered(50f); }}; deltaPad = new MechPad("delta-mech-pad"){{ mech = Mechs.delta; size = 2; - basePowerUse = 70f; + consumes.powerBuffered(70f); }}; tauPad = new MechPad("tau-mech-pad"){{ mech = Mechs.tau; size = 2; - basePowerUse = 100f; + consumes.powerBuffered(100f); }}; omegaPad = new MechPad("omega-mech-pad"){{ mech = Mechs.omega; size = 3; - basePowerUse = 120f; + consumes.powerBuffered(120f); }}; dartPad = new MechPad("dart-ship-pad"){{ mech = Mechs.dart; size = 2; - basePowerUse = 50f; + consumes.powerBuffered(50f); shadow = "shadow-rounded-2"; }}; javelinPad = new MechPad("javelin-ship-pad"){{ mech = Mechs.javelin; size = 2; - basePowerUse = 80f; + consumes.powerBuffered(80f); shadow = "shadow-rounded-2"; }}; tridentPad = new MechPad("trident-ship-pad"){{ mech = Mechs.trident; size = 2; - basePowerUse = 100f; + consumes.powerBuffered(100f); shadow = "shadow-rounded-2"; }}; glaivePad = new MechPad("glaive-ship-pad"){{ mech = Mechs.glaive; size = 3; - basePowerUse = 120f; + consumes.powerBuffered(120f); shadow = "shadow-round-3"; }}; } diff --git a/core/src/io/anuke/mindustry/world/BaseBlock.java b/core/src/io/anuke/mindustry/world/BaseBlock.java index 6111e5a197..9a1b7dbfb1 100644 --- a/core/src/io/anuke/mindustry/world/BaseBlock.java +++ b/core/src/io/anuke/mindustry/world/BaseBlock.java @@ -28,12 +28,6 @@ public abstract class BaseBlock extends MappableContent{ public boolean consumesPower = true; public boolean outputsPower = false; - public boolean bufferedPowerConsumer = false; - /** In case of unbuffered consumers, this stores the amount of power which is required per tick in order to work at maximum efficiency. - * In case of buffered consumers, this stores the maximum power capacity. - */ - public float basePowerUse = 0; - public int itemCapacity; public float liquidCapacity = 10f; public float liquidFlowFactor = 4.9f; diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index c04987bdd1..36c80a493d 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -20,6 +20,7 @@ import io.anuke.mindustry.input.CursorType; import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemStack; +import io.anuke.mindustry.world.consumers.ConsumePowerBuffered; import io.anuke.mindustry.world.meta.*; import io.anuke.ucore.core.Timers; import io.anuke.ucore.graphics.Draw; @@ -330,17 +331,16 @@ public class Block extends BaseBlock { consumes.forEach(cons -> cons.display(stats)); - if(hasPower){ - if(bufferedPowerConsumer) stats.add(BlockStat.powerCapacity, basePowerUse, StatUnit.powerUnits); - else stats.add(BlockStat.powerUse, basePowerUse * 60, StatUnit.powerUnits); - } + // Note: Power stats are added by the consumers. if(hasLiquids) stats.add(BlockStat.liquidCapacity, liquidCapacity, StatUnit.liquidUnits); if(hasItems) stats.add(BlockStat.itemCapacity, itemCapacity, StatUnit.items); } //TODO make this easier to config. public void setBars(){ - if(bufferedPowerConsumer) bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); + if(consumes.has(ConsumePowerBuffered.class)){ + bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); + } if(hasLiquids) bars.add(new BlockBar(BarType.liquid, true, tile -> tile.entity.liquids.total() / liquidCapacity)); if(hasItems) @@ -406,8 +406,8 @@ public class Block extends BaseBlock { explosiveness += tile.entity.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2f); } - if(bufferedPowerConsumer){ - power += tile.entity.power.satisfaction * tile.block().basePowerUse; + if(consumes.has(ConsumePowerBuffered.class)){ + power += tile.entity.power.satisfaction * consumes.get(ConsumePowerBuffered.class).powerCapacity; } tempColor.mul(1f / units); 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 c89cee750b..9af93e6fa4 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java @@ -51,10 +51,10 @@ public class ForceProjector extends Block { hasPower = true; canOverdrive = false; hasLiquids = true; - basePowerUse = 60f; hasItems = true; itemCapacity = 10; consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).optional(true).update(false); + consumes.powerBuffered(60f); } @Override 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 1625953a3c..3f8f6682fd 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 @@ -13,8 +13,6 @@ public abstract class PowerTurret extends CooledTurret{ public PowerTurret(String name){ super(name); hasPower = true; - // TODO Verify for new power system - bufferedPowerConsumer = true; } @Override @@ -26,7 +24,6 @@ public abstract class PowerTurret extends CooledTurret{ @Override public boolean hasAmmo(Tile tile){ - // TODO Verify for new power system // Allow shooting as long as the turret is at least at 50% power return tile.entity.power.satisfaction >= powerUsed; } 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 e5c7141dce..db665d79f6 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -19,6 +19,7 @@ import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.consumers.ConsumePowerBuffered; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.StatUnit; import io.anuke.ucore.core.Effects; @@ -48,6 +49,7 @@ public class MassDriver extends Block{ protected Effect smokeEffect = ShootFx.shootBigSmoke2; protected Effect recieveEffect = BlockFx.mineBig; protected float shake = 3f; + protected final static float powerPercentageUsed = 0.8f; protected TextureRegion turretRegion; public MassDriver(String name){ @@ -58,6 +60,7 @@ public class MassDriver extends Block{ hasItems = true; layer = Layer.turret; hasPower = true; + consumes.powerBuffered(30f); } @Remote(targets = Loc.both, called = Loc.server, forward = true) @@ -80,7 +83,7 @@ public class MassDriver extends Block{ entity.reload = 1f; - entity.power.satisfaction = 0f; + entity.power.satisfaction -= Math.min(entity.power.satisfaction, powerPercentageUsed); DriverBulletData data = Pooling.obtain(DriverBulletData.class, DriverBulletData::new); data.from = entity; @@ -128,7 +131,10 @@ public class MassDriver extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.powerShot, basePowerUse, StatUnit.powerUnits); + if(!consumes.hasSubclassOf(ConsumePowerBuffered.class)){ + throw new RuntimeException("Mass Driver did not have a buffered power consumer object attached."); + } + stats.add(BlockStat.powerShot, consumes.getFirstSubclassOf(ConsumePowerBuffered.class).powerCapacity, StatUnit.powerUnits); } @Override @@ -167,9 +173,8 @@ public class MassDriver extends Block{ entity.rotation = Mathf.slerpDelta(entity.rotation, tile.angleTo(waiter), rotateSpeed); }else if(tile.entity.items.total() >= minDistribute && - linkValid(tile) && //only fire when at least at half-capacity and power - // TODO adapt - //tile.entity.power.amount >= powerCapacity * 0.8f && + linkValid(tile) && //only fire when at least at 80% power capacity + tile.entity.power.satisfaction > powerPercentageUsed && link.block().itemCapacity - link.entity.items.total() >= minDistribute && entity.reload <= 0.0001f){ MassDriverEntity other = link.entity(); 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 9045ffb284..d8276f16c3 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -1,10 +1,15 @@ package io.anuke.mindustry.world.blocks.power; +import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.IntSet; import com.badlogic.gdx.utils.ObjectSet; import com.badlogic.gdx.utils.Queue; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.consumers.Consume; +import io.anuke.mindustry.world.consumers.ConsumePower; +import io.anuke.mindustry.world.consumers.ConsumePowerBuffered; +import io.anuke.mindustry.world.consumers.Consumers; import static io.anuke.mindustry.Vars.threads; @@ -42,10 +47,9 @@ public class PowerGraph{ public float getPowerNeeded(){ float powerNeeded = 0f; for(Tile consumer : consumers){ - if(consumer.block().bufferedPowerConsumer){ - powerNeeded += (1f - consumer.entity.power.satisfaction) * consumer.block().basePowerUse; - }else{ - powerNeeded += consumer.block().basePowerUse + consumer.entity.power.extraUse; + Consumers consumes = consumer.block().consumes; + if(consumes.hasSubclassOf(ConsumePower.class)){ + powerNeeded += consumes.getFirstSubclassOf(ConsumePower.class).requestedPower(consumer.block(), consumer.entity); } } return powerNeeded; @@ -54,7 +58,10 @@ public class PowerGraph{ public float getBatteryStored(){ float totalAccumulator = 0f; for(Tile battery : batteries){ - totalAccumulator += battery.entity.power.satisfaction * battery.block().basePowerUse; + Consumers consumes = battery.block().consumes; + if(consumes.hasSubclassOf(ConsumePowerBuffered.class)){ + totalAccumulator += battery.entity.power.satisfaction * consumes.getFirstSubclassOf(ConsumePowerBuffered.class).powerCapacity; + } } return totalAccumulator; } @@ -62,23 +69,30 @@ public class PowerGraph{ public float getBatteryCapacity(){ float totalCapacity = 0f; for(Tile battery : batteries){ - totalCapacity += (1f - battery.entity.power.satisfaction) * battery.block().basePowerUse; + Consumers consumes = battery.block().consumes; + if(consumes.hasSubclassOf(ConsumePower.class)){ + totalCapacity += consumes.getFirstSubclassOf(ConsumePower.class).requestedPower(battery.block(), battery.entity); + } } return totalCapacity; } public float useBatteries(float needed){ float stored = getBatteryStored(); + if(MathUtils.isEqual(stored, 0f)){ return 0f; } + float used = Math.min(stored, needed); - float thing = 1f - (used / stored); + float percentageRemaining = 1f - (used / stored); for(Tile battery : batteries){ - battery.entity.power.satisfaction *= thing; + battery.entity.power.satisfaction *= percentageRemaining; } return used; } public float chargeBatteries(float excess){ float capacity = getBatteryCapacity(); + if(MathUtils.isEqual(capacity, 0f)){ return 0f; } + float thing = Math.min(1, excess / capacity); for(Tile battery : batteries){ battery.entity.power.satisfaction += (1 - battery.entity.power.satisfaction) * thing; @@ -87,14 +101,14 @@ public class PowerGraph{ } public void distributePower(float needed, float produced){ - if(needed == 0f){ return; } + if(MathUtils.isEqual(needed,0f)){ return; } - float satisfaction = Math.min(1, produced / needed); + float coverage = Math.min(1, produced / needed); for(Tile consumer : consumers){ - if(consumer.block().bufferedPowerConsumer){ - consumer.entity.power.satisfaction += (1 - consumer.entity.power.satisfaction) * satisfaction; + if(consumer.block().consumes.hasSubclassOf(ConsumePowerBuffered.class)){ + consumer.entity.power.satisfaction += (1 - consumer.entity.power.satisfaction) * coverage; }else{ - consumer.entity.power.satisfaction = satisfaction; + consumer.entity.power.satisfaction = coverage; } } } @@ -109,11 +123,11 @@ public class PowerGraph{ float powerNeeded = getPowerNeeded(); float powerProduced = getPowerProduced(); - if(Math.abs(powerNeeded - powerProduced) > 0.0001f){ + if(!MathUtils.isEqual(powerNeeded, powerProduced)){ if(powerNeeded > powerProduced){ - powerProduced += useBatteries(powerNeeded-powerProduced); + powerProduced += useBatteries(powerNeeded - powerProduced); }else if(powerProduced > powerNeeded){ - powerProduced -= chargeBatteries(powerProduced-powerNeeded); + powerProduced -= chargeBatteries(powerProduced - powerNeeded); } } @@ -141,7 +155,7 @@ public class PowerGraph{ public void clear(){ for(Tile other : all){ - if(other.entity != null && other.entity.power != null) other.entity.power.graph = null; + if(other.entity != null && other.entity.power != null){ other.entity.power.graph = null; } } all.clear(); producers.clear(); @@ -171,7 +185,7 @@ public class PowerGraph{ closedSet.clear(); for(Tile other : tile.block().getPowerConnections(tile, outArray1)){ - if(other.entity.power == null || other.entity.power.graph != null) continue; + if(other.entity.power == null || other.entity.power.graph != null){ continue; } PowerGraph graph = new PowerGraph(); queue.clear(); queue.addLast(other); @@ -192,12 +206,12 @@ public class PowerGraph{ @Override public String toString(){ return "PowerGraph{" + - "producers=" + producers + - ", consumers=" + consumers + - ", batteries=" + batteries + - ", all=" + all + - ", lastFrameUpdated=" + lastFrameUpdated + - ", graphID=" + graphID + - '}'; + "producers=" + producers + + ", consumers=" + consumers + + ", batteries=" + batteries + + ", all=" + all + + ", lastFrameUpdated=" + lastFrameUpdated + + ", graphID=" + graphID + + '}'; } } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java b/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java index 46ce4c12a2..5fa8d18eff 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java @@ -26,7 +26,7 @@ public class Incinerator extends Block{ update = true; solid = true; - basePowerUse = 0.05f; + consumes.powerDirect(0.05f); } @Override 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 581a16f745..a1fa070367 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java @@ -45,7 +45,6 @@ public class MechPad extends Block{ update = true; solidifes = true; hasPower = true; - bufferedPowerConsumer = true; } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java b/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java index 5321273f29..415fdbacf4 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/Reconstructor.java @@ -44,8 +44,7 @@ public class Reconstructor extends Block{ solidifes = true; hasPower = true; configurable = true; - basePowerUse = 30f; // capacity - bufferedPowerConsumer = true; + consumes.powerBuffered(30f); } protected static boolean checkValidTap(Tile tile, ReconstructorEntity entity, Player player){ 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 1942dbb199..6d0192634b 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java @@ -38,8 +38,7 @@ public class RepairPoint extends Block{ layer2 = Layer.laser; hasPower = true; // TODO Adapt to new power system - Make it use power while repairing - basePowerUse = 20f; // capacity - bufferedPowerConsumer = true; + consumes.powerBuffered(20f); } @Override diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java index 62eebdfb21..a835bd21c6 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java @@ -11,7 +11,7 @@ import io.anuke.mindustry.world.meta.StatUnit; /** Consumer class for blocks which directly consume power without buffering it. */ public class ConsumePowerBuffered extends ConsumePower{ /** The maximum power capacity in power units. */ - protected final float powerCapacity; + public final float powerCapacity; /** * Adds a power buffer to the owner which takes ticksToFill number of ticks to be filled. diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java index 0ce7d593a2..e746ec3cc3 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java @@ -13,14 +13,6 @@ public class ConsumePowerDirect extends ConsumePower{ /** The minimum power satisfaction (fraction of powerPerTick) which must be achieved before the module may work. */ protected final float minimumSatisfaction; - /** - * Makes the owner consume powerPerTick each tick and disables it unless 60% of that power is being supplied. - * @param powerPerTick The maximum amount of power which is required per tick for 100% efficiency. - */ - public ConsumePowerDirect(float powerPerTick){ - this(powerPerTick, 0.75f); - } - /** * Makes the owner consume powerPerTick each tick and disables it unless minimumSatisfaction (1.0 = 100%) of that power is being supplied. * @param powerPerTick The maximum amount of power which is required per tick for 100% efficiency. diff --git a/core/src/io/anuke/mindustry/world/consumers/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index e38607ad33..f75f10ad6f 100644 --- a/core/src/io/anuke/mindustry/world/consumers/Consumers.java +++ b/core/src/io/anuke/mindustry/world/consumers/Consumers.java @@ -36,6 +36,47 @@ public class Consumers{ return c; } + /** + * Creates a consumer which directly uses power without buffering it. The module will work while at least 60% of power is supplied. + * @param powerPerTick The amount of power which is required each tick for 100% efficiency. + * @return the created consumer object. + */ + public ConsumePower powerDirect(float powerPerTick){ + return powerDirect(powerPerTick, 0.6f); + } + + /** + * 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). + * @param powerPerTick The amount of power which is required each tick for 100% efficiency. + * @return the created consumer object. + */ + public ConsumePower powerDirect(float powerPerTick, float minimumSatisfaction){ + ConsumePower c = new ConsumePowerDirect(powerPerTick, minimumSatisfaction); + add(c); + return c; + } + + /** + * Creates a consumer which stores power and uses it only in case of certain events (e.g. a turret firing). + * It will take 60 ticks (one second) to fill the buffer, given enough power supplied. + * @param powerCapacity The maximum capacity in power units. + */ + public ConsumePower powerBuffered(float powerCapacity){ + // TODO Balance: How long should it take to fill a buffer? The lower this value, the more power will be "stolen" from direct consumers. + return powerBuffered(powerCapacity, 60); + } + + /** + * Creates a consumer which stores power and uses it only in case of certain events (e.g. a turret firing). + * @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){ + ConsumePower c = new ConsumePowerBuffered(powerCapacity, ticksToFill); + add(c); + return c; + } + public ConsumeItem item(Item item){ return item(item, 1); } @@ -81,6 +122,15 @@ public class Consumers{ return map.containsKey(type); } + public boolean hasSubclassOf(Class type){ + for(Consume consume : all()){ + if(type.isAssignableFrom(consume.getClass())){ + return true; + } + } + return false; + } + public T get(Class type){ if(!map.containsKey(type)){ throw new IllegalArgumentException("Block does not contain consumer of type '" + type + "'!"); @@ -88,6 +138,15 @@ public class Consumers{ return (T) map.get(type); } + public T getFirstSubclassOf(Class type){ + for(Consume consume : all()){ + if(type.isAssignableFrom(consume.getClass())){ + return (T)consume; + } + } + throw new IllegalArgumentException("Block does not contain consumer of type '" + type + "' (subclasses included)!"); + } + public Iterable all(){ return map.values(); } From d8ddb9dc82cce2f1bfc7b44760d882fb68759c63 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 21 Nov 2018 22:55:53 +0100 Subject: [PATCH 12/67] Merged the three ConsumePower classes into a single one --- core/src/io/anuke/mindustry/world/Block.java | 8 +-- .../world/blocks/distribution/MassDriver.java | 6 +-- .../world/blocks/power/PowerGraph.java | 17 +++--- .../world/consumers/ConsumePower.java | 54 +++++++++++++++++-- .../world/consumers/ConsumePowerBuffered.java | 43 --------------- .../world/consumers/ConsumePowerDirect.java | 35 ------------ .../mindustry/world/consumers/Consumers.java | 22 +------- 7 files changed, 67 insertions(+), 118 deletions(-) delete mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java delete mode 100644 core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 36c80a493d..8eebd822cc 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -20,7 +20,7 @@ import io.anuke.mindustry.input.CursorType; import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.ItemStack; -import io.anuke.mindustry.world.consumers.ConsumePowerBuffered; +import io.anuke.mindustry.world.consumers.ConsumePower; import io.anuke.mindustry.world.meta.*; import io.anuke.ucore.core.Timers; import io.anuke.ucore.graphics.Draw; @@ -338,7 +338,7 @@ public class Block extends BaseBlock { //TODO make this easier to config. public void setBars(){ - if(consumes.has(ConsumePowerBuffered.class)){ + if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); } if(hasLiquids) @@ -406,8 +406,8 @@ public class Block extends BaseBlock { explosiveness += tile.entity.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2f); } - if(consumes.has(ConsumePowerBuffered.class)){ - power += tile.entity.power.satisfaction * consumes.get(ConsumePowerBuffered.class).powerCapacity; + if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ + power += tile.entity.power.satisfaction * consumes.get(ConsumePower.class).powerCapacity; } tempColor.mul(1f / units); 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 db665d79f6..ea15719524 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -19,7 +19,7 @@ import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.consumers.ConsumePowerBuffered; +import io.anuke.mindustry.world.consumers.ConsumePower; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.StatUnit; import io.anuke.ucore.core.Effects; @@ -131,10 +131,10 @@ public class MassDriver extends Block{ public void setStats(){ super.setStats(); - if(!consumes.hasSubclassOf(ConsumePowerBuffered.class)){ + if(!consumes.has(ConsumePower.class) || !consumes.get(ConsumePower.class).isBuffered){ throw new RuntimeException("Mass Driver did not have a buffered power consumer object attached."); } - stats.add(BlockStat.powerShot, consumes.getFirstSubclassOf(ConsumePowerBuffered.class).powerCapacity, StatUnit.powerUnits); + stats.add(BlockStat.powerShot, consumes.get(ConsumePower.class).powerCapacity, StatUnit.powerUnits); } @Override 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 d8276f16c3..0afbe9d2da 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -6,9 +6,7 @@ import com.badlogic.gdx.utils.IntSet; import com.badlogic.gdx.utils.ObjectSet; import com.badlogic.gdx.utils.Queue; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.consumers.Consume; import io.anuke.mindustry.world.consumers.ConsumePower; -import io.anuke.mindustry.world.consumers.ConsumePowerBuffered; import io.anuke.mindustry.world.consumers.Consumers; import static io.anuke.mindustry.Vars.threads; @@ -48,8 +46,8 @@ public class PowerGraph{ float powerNeeded = 0f; for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; - if(consumes.hasSubclassOf(ConsumePower.class)){ - powerNeeded += consumes.getFirstSubclassOf(ConsumePower.class).requestedPower(consumer.block(), consumer.entity); + if(consumes.has(ConsumePower.class)){ + powerNeeded += consumes.get(ConsumePower.class).requestedPower(consumer.block(), consumer.entity); } } return powerNeeded; @@ -59,8 +57,8 @@ public class PowerGraph{ float totalAccumulator = 0f; for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.hasSubclassOf(ConsumePowerBuffered.class)){ - totalAccumulator += battery.entity.power.satisfaction * consumes.getFirstSubclassOf(ConsumePowerBuffered.class).powerCapacity; + if(consumes.has(ConsumePower.class)){ + totalAccumulator += battery.entity.power.satisfaction * consumes.get(ConsumePower.class).powerCapacity; } } return totalAccumulator; @@ -70,8 +68,8 @@ public class PowerGraph{ float totalCapacity = 0f; for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.hasSubclassOf(ConsumePower.class)){ - totalCapacity += consumes.getFirstSubclassOf(ConsumePower.class).requestedPower(battery.block(), battery.entity); + if(consumes.has(ConsumePower.class)){ + totalCapacity += consumes.get(ConsumePower.class).requestedPower(battery.block(), battery.entity); } } return totalCapacity; @@ -105,7 +103,8 @@ public class PowerGraph{ float coverage = Math.min(1, produced / needed); for(Tile consumer : consumers){ - if(consumer.block().consumes.hasSubclassOf(ConsumePowerBuffered.class)){ + Consumers consumes = consumer.block().consumes; + if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ consumer.entity.power.satisfaction += (1 - consumer.entity.power.satisfaction) * coverage; }else{ consumer.entity.power.satisfaction = coverage; diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java index 8f4e829d1d..4129f3345c 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -9,12 +9,40 @@ import io.anuke.mindustry.world.meta.BlockStats; import io.anuke.mindustry.world.meta.StatUnit; /** Consumer class for blocks which consume power while being connected to a power graph. */ -public abstract class ConsumePower extends Consume{ +public class ConsumePower extends Consume{ /** The maximum amount of power which can be processed per tick. This might influence efficiency or load a buffer. */ protected final float powerPerTick; + /** The minimum power satisfaction (fraction of powerPerTick) which must be achieved before the module may work. */ + protected final float minimumSatisfaction; + /** The maximum power capacity in power units. */ + public final float powerCapacity; + /** True if the module can store power. */ + public final boolean isBuffered; - public ConsumePower(float powerPerTick){ + protected ConsumePower(float powerPerTick, float minimumSatisfaction, float powerCapacity, boolean isBuffered){ this.powerPerTick = powerPerTick; + this.minimumSatisfaction = minimumSatisfaction; + this.powerCapacity = powerCapacity; + this.isBuffered = isBuffered; + } + + /** + * Makes the owner consume powerPerTick each tick and disables it unless minimumSatisfaction (1.0 = 100%) of that power is being supplied. + * @param powerPerTick The maximum amount of power which is required per tick for 100% efficiency. + * @param minimumSatisfaction The percentage of powerPerTick which must be available for the module to work. + */ + public static ConsumePower consumePowerBuffered(float powerPerTick, float minimumSatisfaction){ + return new ConsumePower(powerPerTick, minimumSatisfaction, 0.0f, true); + } + + /** + * Adds a power buffer to the owner which takes ticksToFill number of ticks to be filled. + * Note that this object does not remove power from the buffer. + * @param powerCapacity The maximum capacity in power units. + * @param ticksToFill The number of ticks it shall take to fill the buffer. + */ + public static ConsumePower consumePowerDirect(float powerCapacity, float ticksToFill){ + return new ConsumePower(powerCapacity / ticksToFill, 0.0f, powerCapacity, false); } @Override @@ -32,8 +60,24 @@ public abstract class ConsumePower extends Consume{ // Nothing to do since PowerGraph directly updates entity.power.satisfaction } - // valid(...) is implemented in subclass - // display(...) is implemented in subclass + @Override + public boolean valid(Block block, TileEntity entity){ + if(isBuffered){ + // TODO - Verify: It might be necessary to know about the power required per shot/event here. + return true; + }else{ + return entity.power.satisfaction >= minimumSatisfaction; + } + } + + @Override + public void display(BlockStats stats){ + if(isBuffered){ + stats.add(BlockStat.powerCapacity, powerCapacity, StatUnit.powerSecond); + }else{ + stats.add(BlockStat.powerUse, powerPerTick * 60f, StatUnit.powerSecond); + } + } /** * Retrieves the amount of power which is requested for the given block and entity. @@ -45,4 +89,6 @@ public abstract class ConsumePower extends Consume{ // TODO Is it possible to make the block not consume power while items/liquids are missing? return powerPerTick; } + + } diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java deleted file mode 100644 index a835bd21c6..0000000000 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerBuffered.java +++ /dev/null @@ -1,43 +0,0 @@ -package io.anuke.mindustry.world.consumers; - -import io.anuke.ucore.scene.ui.layout.Table; - -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.BlockStats; -import io.anuke.mindustry.world.meta.StatUnit; - -/** Consumer class for blocks which directly consume power without buffering it. */ -public class ConsumePowerBuffered extends ConsumePower{ - /** The maximum power capacity in power units. */ - public final float powerCapacity; - - /** - * Adds a power buffer to the owner which takes ticksToFill number of ticks to be filled. - * Note that this object does not remove power from the buffer. - * @param powerCapacity The maximum capacity in power units. - * @param ticksToFill The number of ticks it shall take to fill the buffer. - */ - public ConsumePowerBuffered(float powerCapacity, float ticksToFill){ - super(powerCapacity / ticksToFill); - this.powerCapacity = powerCapacity; - } - - @Override - public boolean valid(Block block, TileEntity entity){ - // TODO - Verify: It might be necessary to know about the power required per shot/event here. - return true; - } - - @Override - public void display(BlockStats stats){ - stats.add(BlockStat.powerCapacity, powerCapacity, StatUnit.powerSecond); - } - - @Override - public float requestedPower(Block block, TileEntity entity){ - // Only request power until the capacity is full - return Math.max(powerPerTick, powerCapacity * (1 - entity.power.satisfaction)); - } -} diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java deleted file mode 100644 index e746ec3cc3..0000000000 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePowerDirect.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.anuke.mindustry.world.consumers; - -import io.anuke.ucore.scene.ui.layout.Table; - -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.BlockStats; -import io.anuke.mindustry.world.meta.StatUnit; - -/** Consumer class for blocks which directly consume power without buffering it. */ -public class ConsumePowerDirect extends ConsumePower{ - /** The minimum power satisfaction (fraction of powerPerTick) which must be achieved before the module may work. */ - protected final float minimumSatisfaction; - - /** - * Makes the owner consume powerPerTick each tick and disables it unless minimumSatisfaction (1.0 = 100%) of that power is being supplied. - * @param powerPerTick The maximum amount of power which is required per tick for 100% efficiency. - * @param minimumSatisfaction The percentage of powerPerTick which must be available for the module to work. - */ - public ConsumePowerDirect(float powerPerTick, float minimumSatisfaction){ - super(powerPerTick); - this.minimumSatisfaction = minimumSatisfaction; - } - - @Override - public boolean valid(Block block, TileEntity entity){ - return entity.power.satisfaction >= minimumSatisfaction; - } - - @Override - public void display(BlockStats stats){ - stats.add(BlockStat.powerUse, powerPerTick * 60f, StatUnit.powerSecond); - } -} diff --git a/core/src/io/anuke/mindustry/world/consumers/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index f75f10ad6f..75b91a98e4 100644 --- a/core/src/io/anuke/mindustry/world/consumers/Consumers.java +++ b/core/src/io/anuke/mindustry/world/consumers/Consumers.java @@ -51,7 +51,7 @@ public class Consumers{ * @return the created consumer object. */ public ConsumePower powerDirect(float powerPerTick, float minimumSatisfaction){ - ConsumePower c = new ConsumePowerDirect(powerPerTick, minimumSatisfaction); + ConsumePower c = ConsumePower.consumePowerDirect(powerPerTick, minimumSatisfaction); add(c); return c; } @@ -72,7 +72,7 @@ public class Consumers{ * @param ticksToFill The number of ticks it shall take to fill the buffer. */ public ConsumePower powerBuffered(float powerCapacity, float ticksToFill){ - ConsumePower c = new ConsumePowerBuffered(powerCapacity, ticksToFill); + ConsumePower c = ConsumePower.consumePowerBuffered(powerCapacity, ticksToFill); add(c); return c; } @@ -122,15 +122,6 @@ public class Consumers{ return map.containsKey(type); } - public boolean hasSubclassOf(Class type){ - for(Consume consume : all()){ - if(type.isAssignableFrom(consume.getClass())){ - return true; - } - } - return false; - } - public T get(Class type){ if(!map.containsKey(type)){ throw new IllegalArgumentException("Block does not contain consumer of type '" + type + "'!"); @@ -138,15 +129,6 @@ public class Consumers{ return (T) map.get(type); } - public T getFirstSubclassOf(Class type){ - for(Consume consume : all()){ - if(type.isAssignableFrom(consume.getClass())){ - return (T)consume; - } - } - throw new IllegalArgumentException("Block does not contain consumer of type '" + type + "' (subclasses included)!"); - } - public Iterable all(){ return map.values(); } From 761728ac438a05870b9dfcb3b1fc95c958be6db1 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 21 Nov 2018 23:04:18 +0100 Subject: [PATCH 13/67] Fixed the ConsumePower constructors which got mixed up at some point --- .../io/anuke/mindustry/world/consumers/ConsumePower.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java index 4129f3345c..9aec5498de 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -31,8 +31,8 @@ public class ConsumePower extends Consume{ * @param powerPerTick The maximum amount of power which is required per tick for 100% efficiency. * @param minimumSatisfaction The percentage of powerPerTick which must be available for the module to work. */ - public static ConsumePower consumePowerBuffered(float powerPerTick, float minimumSatisfaction){ - return new ConsumePower(powerPerTick, minimumSatisfaction, 0.0f, true); + public static ConsumePower consumePowerDirect(float powerPerTick, float minimumSatisfaction){ + return new ConsumePower(powerPerTick, minimumSatisfaction, 0.0f, false); } /** @@ -41,8 +41,8 @@ public class ConsumePower extends Consume{ * @param powerCapacity The maximum capacity in power units. * @param ticksToFill The number of ticks it shall take to fill the buffer. */ - public static ConsumePower consumePowerDirect(float powerCapacity, float ticksToFill){ - return new ConsumePower(powerCapacity / ticksToFill, 0.0f, powerCapacity, false); + public static ConsumePower consumePowerBuffered(float powerCapacity, float ticksToFill){ + return new ConsumePower(powerCapacity / ticksToFill, 0.0f, powerCapacity, true); } @Override From 920491ddb25449c381b6c06ea6f7a09ce32c5bb2 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 21 Nov 2018 23:38:28 +0100 Subject: [PATCH 14/67] MassDriver now properly displays the power per shot --- .../mindustry/world/blocks/distribution/MassDriver.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) 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 ea15719524..f81c344278 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -61,6 +61,7 @@ public class MassDriver extends Block{ layer = Layer.turret; hasPower = true; consumes.powerBuffered(30f); + consumes.require(ConsumePower.class); } @Remote(targets = Loc.both, called = Loc.server, forward = true) @@ -131,10 +132,7 @@ public class MassDriver extends Block{ public void setStats(){ super.setStats(); - if(!consumes.has(ConsumePower.class) || !consumes.get(ConsumePower.class).isBuffered){ - throw new RuntimeException("Mass Driver did not have a buffered power consumer object attached."); - } - stats.add(BlockStat.powerShot, consumes.get(ConsumePower.class).powerCapacity, StatUnit.powerUnits); + stats.add(BlockStat.powerShot, consumes.get(ConsumePower.class).powerCapacity * powerPercentageUsed, StatUnit.powerUnits); } @Override From 7683e86d8b1cd62ab7e2b4d9a64f48b1ba990d60 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 21 Nov 2018 23:57:24 +0100 Subject: [PATCH 15/67] Added two unit tests for bounds checking --- tests/src/test/java/PowerTests.java | 45 +++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/tests/src/test/java/PowerTests.java b/tests/src/test/java/PowerTests.java index 07f7e6e2ba..c6b4540f55 100644 --- a/tests/src/test/java/PowerTests.java +++ b/tests/src/test/java/PowerTests.java @@ -39,7 +39,7 @@ public class PowerTests{ * @param block The block on the tile. * @return The created tile or null in case of exceptions. */ - private static Tile createFakeTile(int x, int y, Floor floor, Block block){ + private static Tile createFakeTile(int x, int y, Block block){ try{ Tile tile = new Tile(x, y); Field field = Tile.class.getDeclaredField("wall"); @@ -47,7 +47,7 @@ public class PowerTests{ field.set(tile, block); field = Tile.class.getDeclaredField("floor"); field.setAccessible(true); - field.set(tile, floor); + field.set(tile, (Floor)Blocks.sand); tile.entity = block.newEntity(); tile.entity.power = new PowerModule(); return tile; @@ -55,6 +55,7 @@ public class PowerTests{ return null; } } + private static final float epsilon = 0.00001f; /** Makes sure calculations are accurate for the case where produced power = consumed power. */ @Test @@ -62,13 +63,13 @@ public class PowerTests{ PowerGraph powerGraph = new PowerGraph(); // Create one water extractor (5.4 power/Second = 0.09/tick) - Tile waterExtractorTile = createFakeTile(0, 0, (Floor)Blocks.sand, ProductionBlocks.waterExtractor); + Tile waterExtractorTile = createFakeTile(0, 0, ProductionBlocks.waterExtractor); powerGraph.add(waterExtractorTile); // Create 20 small solar panels (20*0.27=5.4 power/second = 0.09/tick) List solarPanelTiles = new LinkedList<>(); for(int counter = 0; counter < 20; counter++){ - Tile solarPanelTile = createFakeTile( 2 + counter / 2, counter % 2, (Floor)Blocks.sand, PowerBlocks.solarPanel); + Tile solarPanelTile = createFakeTile( 2 + counter / 2, counter % 2, PowerBlocks.solarPanel); powerGraph.add(solarPanelTile); solarPanelTiles.add(solarPanelTile); } @@ -79,7 +80,6 @@ public class PowerTests{ // If these lines fail, you probably changed power production/consumption and need to adapt this test // OR their implementation is corrupt. // TODO: Create fake blocks which are independent of such changes - float epsilon = 0.00001f; assertEquals(powerNeeded, 0.09f, epsilon); assertEquals(powerProduced, 0.09f, epsilon); // Note: The assertions above induce that powerNeeded = powerProduced (with floating point inaccuracy) @@ -88,4 +88,39 @@ public class PowerTests{ powerGraph.distributePower(powerNeeded, powerProduced); assertEquals(waterExtractorTile.entity.power.satisfaction, 1.0f, epsilon); } + + /** Makes sure there are no problems with zero production. */ + @Test + void test_noProducers(){ + PowerGraph powerGraph = new PowerGraph(); + + Tile waterExtractorTile = createFakeTile(0, 0, ProductionBlocks.waterExtractor); + powerGraph.add(waterExtractorTile); + + float powerNeeded = powerGraph.getPowerNeeded(); + float powerProduced = powerGraph.getPowerProduced(); + + assertEquals(powerGraph.getPowerNeeded(), 0.09f, epsilon); + assertEquals(powerGraph.getPowerProduced(), 0.0f, epsilon); + + powerGraph.distributePower(powerNeeded, powerProduced); + assertEquals(waterExtractorTile.entity.power.satisfaction, 0.0f, epsilon); + } + + /** Makes sure there are no problems with zero consumers. */ + @Test + void test_noConsumers(){ + PowerGraph powerGraph = new PowerGraph(); + + Tile solarPanelTile = createFakeTile( 0, 0, PowerBlocks.solarPanel); + powerGraph.add(solarPanelTile); + + float powerNeeded = powerGraph.getPowerNeeded(); + float powerProduced = powerGraph.getPowerProduced(); + + assertEquals(powerGraph.getPowerNeeded(), 0.0f, epsilon); + assertEquals(powerGraph.getPowerProduced(), 0.0045f, epsilon); + + powerGraph.distributePower(powerNeeded, powerProduced); + } } From 560d388df4868ac82613941a5ebc44b27f3e262e Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Thu, 22 Nov 2018 23:30:49 +0100 Subject: [PATCH 16/67] Added Tests, fixed detected bugs and updated JUnit --- build.gradle | 5 +- .../world/blocks/power/PowerGraph.java | 31 ++-- .../mindustry/world/modules/PowerModule.java | 2 +- tests/src/test/java/FakeThreadHandler.java | 19 ++ tests/src/test/java/PowerTestFixture.java | 65 +++++++ tests/src/test/java/PowerTests.java | 165 ++++++++---------- 6 files changed, 179 insertions(+), 108 deletions(-) create mode 100644 tests/src/test/java/FakeThreadHandler.java create mode 100644 tests/src/test/java/PowerTestFixture.java diff --git a/build.gradle b/build.gradle index 92579ba541..3ee56f9ad0 100644 --- a/build.gradle +++ b/build.gradle @@ -190,8 +190,9 @@ project(":tests"){ dependencies { testImplementation project(":core") - testImplementation('org.junit.jupiter:junit-jupiter-api:5.1.0') - testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.1.0') + testImplementation "org.junit.jupiter:junit-jupiter-params:5.3.1" + testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1" testImplementation "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion" testImplementation "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" } 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 0afbe9d2da..b17493349a 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -99,15 +99,22 @@ public class PowerGraph{ } public void distributePower(float needed, float produced){ - if(MathUtils.isEqual(needed,0f)){ return; } + if(MathUtils.isEqual(needed, 0f)){ return; } float coverage = Math.min(1, produced / needed); for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; - if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ - consumer.entity.power.satisfaction += (1 - consumer.entity.power.satisfaction) * coverage; - }else{ - consumer.entity.power.satisfaction = coverage; + if(consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumes.get(ConsumePower.class); + if(consumePower.isBuffered){ + // Add a percentage of the requested amount, but limit it to the mission amount. + // TODO This can maybe be calculated without converting to absolute values first + float maximumRate = consumePower.requestedPower(consumer.block(), consumer.entity()) * coverage; + float missingAmount = consumePower.powerCapacity * (1 - consumer.entity.power.satisfaction); + consumer.entity.power.satisfaction += Math.min(missingAmount, maximumRate) / consumePower.powerCapacity; + }else{ + consumer.entity.power.satisfaction = coverage; + } } } } @@ -205,12 +212,12 @@ public class PowerGraph{ @Override public String toString(){ return "PowerGraph{" + - "producers=" + producers + - ", consumers=" + consumers + - ", batteries=" + batteries + - ", all=" + all + - ", lastFrameUpdated=" + lastFrameUpdated + - ", graphID=" + graphID + - '}'; + "producers=" + producers + + ", consumers=" + consumers + + ", batteries=" + batteries + + ", all=" + all + + ", lastFrameUpdated=" + lastFrameUpdated + + ", graphID=" + graphID + + '}'; } } diff --git a/core/src/io/anuke/mindustry/world/modules/PowerModule.java b/core/src/io/anuke/mindustry/world/modules/PowerModule.java index dead0056e5..edef76ba90 100644 --- a/core/src/io/anuke/mindustry/world/modules/PowerModule.java +++ b/core/src/io/anuke/mindustry/world/modules/PowerModule.java @@ -12,7 +12,7 @@ public class PowerModule extends BlockModule{ * Blocks will work at a reduced efficiency if this is not equal to 1.0f. * In case of buffered consumers, this is the percentage of power stored in relation to the maximum capacity. */ - public float satisfaction; + public float satisfaction = 0.0f; /** Specifies power which is required additionally, e.g. while a force projector is being shot at. */ public float extraUse = 0f; public PowerGraph graph = new PowerGraph(); diff --git a/tests/src/test/java/FakeThreadHandler.java b/tests/src/test/java/FakeThreadHandler.java new file mode 100644 index 0000000000..3588205e98 --- /dev/null +++ b/tests/src/test/java/FakeThreadHandler.java @@ -0,0 +1,19 @@ +import com.badlogic.gdx.Gdx; +import io.anuke.mindustry.core.ThreadHandler; +import io.anuke.ucore.core.Timers; + +/** Fake thread handler which produces a new frame each time getFrameID is called and always provides a delta of 1. */ +public class FakeThreadHandler extends ThreadHandler{ + private int fakeFrameId = 0; + + FakeThreadHandler(){ + super(); + + Timers.setDeltaProvider(() -> 1.0f); + } + @Override + public long getFrameID(){ + return ++fakeFrameId; + } + +} diff --git a/tests/src/test/java/PowerTestFixture.java b/tests/src/test/java/PowerTestFixture.java new file mode 100644 index 0000000000..2ecd5a7dc7 --- /dev/null +++ b/tests/src/test/java/PowerTestFixture.java @@ -0,0 +1,65 @@ +import io.anuke.mindustry.content.blocks.Blocks; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; +import io.anuke.mindustry.world.blocks.power.Battery; +import io.anuke.mindustry.world.blocks.power.PowerGenerator; +import io.anuke.mindustry.world.modules.PowerModule; + +import java.lang.reflect.Field; + +/** This class provides objects commonly used by power related unit tests. + * For now, this is a helper with static methods, but this might change. + * */ +public class PowerTestFixture{ + + protected static PowerGenerator createFakeProducerBlock(float producedPower){ + return new PowerGenerator("fakegen"){{ + powerProduction = producedPower; + }}; + } + + protected static Battery createFakeBattery(float capacity, float ticksToFill){ + return new Battery("fakebattery"){{ + consumes.powerBuffered(capacity, ticksToFill); + }}; + } + + protected static Block createFakeDirectConsumer(float powerPerTick, float minimumSatisfaction){ + return new Block("fakedirectconsumer"){{ + consumes.powerDirect(powerPerTick, minimumSatisfaction); + }}; + } + + protected static Block createFakeBufferedConsumer(float capacity, float ticksToFill){ + return new Block("fakebufferedconsumer"){{ + consumes.powerBuffered(capacity, ticksToFill); + }}; + } + /** + * Creates a fake tile on the given location using the given block. + * @param x The X coordinate. + * @param y The y coordinate. + * @param block The block on the tile. + * @return The created tile or null in case of exceptions. + */ + protected static Tile createFakeTile(int x, int y, Block block){ + try{ + Tile tile = new Tile(x, y); + + Field field = Tile.class.getDeclaredField("wall"); + field.setAccessible(true); + field.set(tile, block); + + field = Tile.class.getDeclaredField("floor"); + field.setAccessible(true); + field.set(tile, Blocks.sand); + + tile.entity = block.newEntity(); + tile.entity.power = new PowerModule(); + return tile; + }catch(Exception ex){ + return null; + } + } +} diff --git a/tests/src/test/java/PowerTests.java b/tests/src/test/java/PowerTests.java index c6b4540f55..096e2d126a 100644 --- a/tests/src/test/java/PowerTests.java +++ b/tests/src/test/java/PowerTests.java @@ -1,126 +1,105 @@ +import com.badlogic.gdx.math.MathUtils; import io.anuke.mindustry.Vars; -import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.content.blocks.PowerBlocks; import io.anuke.mindustry.content.blocks.ProductionBlocks; import io.anuke.mindustry.core.ContentLoader; -import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Floor; import io.anuke.mindustry.world.blocks.power.PowerGraph; -import io.anuke.mindustry.world.blocks.production.SolidPump; -import io.anuke.mindustry.world.modules.PowerModule; import org.junit.jupiter.api.*; +import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.ParameterizedTest; -import java.lang.reflect.Field; -import java.util.LinkedList; -import java.util.List; - -import static io.anuke.mindustry.Vars.threads; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; -public class PowerTests{ +public class PowerTests extends PowerTestFixture{ @BeforeAll static void initializeDependencies(){ Vars.content = new ContentLoader(); Vars.content.load(); + Vars.threads = new FakeThreadHandler(); } @BeforeEach void initTest(){ } - /** - * Creates a fake tile on the given location using the given floor and block. - * @param x The X coordinate. - * @param y The y coordinate. - * @param floor The floor. - * @param block The block on the tile. - * @return The created tile or null in case of exceptions. - */ - private static Tile createFakeTile(int x, int y, Block block){ - try{ - Tile tile = new Tile(x, y); - Field field = Tile.class.getDeclaredField("wall"); - field.setAccessible(true); - field.set(tile, block); - field = Tile.class.getDeclaredField("floor"); - field.setAccessible(true); - field.set(tile, (Floor)Blocks.sand); - tile.entity = block.newEntity(); - tile.entity.power = new PowerModule(); - return tile; - }catch(Exception ex){ - return null; + @Nested + class PowerGraphTests{ + + /** Tests the satisfaction of a single consumer after a single update of the power graph which contains a single producer. + * + * Assumption: When the consumer requests zero power, satisfaction does not change. Default is 0.0f. + */ + @TestFactory + DynamicTest[] testDirectConsumption(){ + return new DynamicTest[]{ + // Note: Unfortunately, the display names are not yet output through gradle. See https://github.com/gradle/gradle/issues/5975 + // That's why we inject the description into the test method for now. + dynamicTest("01", () -> test_directConsumptionCalculation(0.0f, 1.0f, 0.0f, "0.0 produced, 1.0 consumed (no power available)")), + dynamicTest("02", () -> test_directConsumptionCalculation(0.0f, 0.0f, 0.0f, "0.0 produced, 0.0 consumed (no power anywhere)")), + dynamicTest("03", () -> test_directConsumptionCalculation(1.0f, 0.0f, 0.0f, "1.0 produced, 0.0 consumed (no power requested)")), + dynamicTest("04", () -> test_directConsumptionCalculation(1.0f, 1.0f, 1.0f, "1.0 produced, 1.0 consumed (stable consumption)")), + dynamicTest("05", () -> test_directConsumptionCalculation(0.5f, 1.0f, 0.5f, "0.5 produced, 1.0 consumed (power shortage)")), + dynamicTest("06", () -> test_directConsumptionCalculation(1.0f, 0.5f, 1.0f, "1.0 produced, 0.5 consumed (power excess)")), + dynamicTest("07", () -> test_directConsumptionCalculation(0.09f, 0.09f - MathUtils.FLOAT_ROUNDING_ERROR / 10.0f, 1.0f, "floating point inaccuracy (stable consumption)")) + }; } - } - private static final float epsilon = 0.00001f; + void test_directConsumptionCalculation(float producedPower, float requiredPower, float expectedSatisfaction, String parameterDescription){ + Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); + Tile directConsumerTile = createFakeTile(0, 1, createFakeDirectConsumer(requiredPower, 0.6f)); - /** Makes sure calculations are accurate for the case where produced power = consumed power. */ - @Test - void test_balancedPower(){ - PowerGraph powerGraph = new PowerGraph(); + PowerGraph powerGraph = new PowerGraph(); + powerGraph.add(producerTile); + powerGraph.add(directConsumerTile); - // Create one water extractor (5.4 power/Second = 0.09/tick) - Tile waterExtractorTile = createFakeTile(0, 0, ProductionBlocks.waterExtractor); - powerGraph.add(waterExtractorTile); + assumeTrue(MathUtils.isEqual(producedPower, powerGraph.getPowerProduced())); + assumeTrue(MathUtils.isEqual(requiredPower, powerGraph.getPowerNeeded())); - // Create 20 small solar panels (20*0.27=5.4 power/second = 0.09/tick) - List solarPanelTiles = new LinkedList<>(); - for(int counter = 0; counter < 20; counter++){ - Tile solarPanelTile = createFakeTile( 2 + counter / 2, counter % 2, PowerBlocks.solarPanel); - powerGraph.add(solarPanelTile); - solarPanelTiles.add(solarPanelTile); + // Update and check for the expected power satisfaction of the consumer + powerGraph.update(); + assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); } - float powerNeeded = powerGraph.getPowerNeeded(); - float powerProduced = powerGraph.getPowerProduced(); + /** Tests the satisfaction of a single buffered consumer after a single update of the power graph which contains a single producer. */ + @TestFactory + DynamicTest[] testBufferedConsumption(){ + return new DynamicTest[]{ + // Note: powerPerTick may not be 0 in any of the test cases. This would equal a "ticksToFill" of infinite. + dynamicTest("01", () -> test_bufferedConsumptionCalculation(0.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power anywhere")), + dynamicTest("02", () -> test_bufferedConsumptionCalculation(0.0f, 1.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power provided")), + dynamicTest("03", () -> test_bufferedConsumptionCalculation(1.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power requested")), + dynamicTest("04", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, "Empty Buffer, Stable Power, One tick to fill")), + dynamicTest("05", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.1f, 0.0f, 0.1f, "Empty Buffer, Stable Power, multiple ticks to fill")), + dynamicTest("06", () -> test_bufferedConsumptionCalculation(1.0f, 0.5f, 0.5f, 0.0f, 1.0f, "Empty Buffer, Power excess, one tick to fill")), + dynamicTest("07", () -> test_bufferedConsumptionCalculation(1.0f, 0.5f, 0.1f, 0.0f, 0.2f, "Empty Buffer, Power excess, multiple ticks to fill")), + dynamicTest("08", () -> test_bufferedConsumptionCalculation(0.5f, 1.0f, 1.0f, 0.0f, 0.5f, "Empty Buffer, Power shortage, one tick to fill")), + dynamicTest("09", () -> test_bufferedConsumptionCalculation(0.5f, 1.0f, 0.1f, 0.0f, 0.1f, "Empty Buffer, Power shortage, multiple ticks to fill")), + dynamicTest("10", () -> test_bufferedConsumptionCalculation(0.0f, 1.0f, 0.1f, 0.5f, 0.5f, "Unchanged buffer with no power produced")), + dynamicTest("11", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.1f, 1.0f, 1.0f, "Unchanged buffer when already full")), + dynamicTest("12", () -> test_bufferedConsumptionCalculation(0.2f, 1.0f, 0.5f, 0.5f, 0.7f, "Half buffer, power shortage")), + dynamicTest("13", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.5f, 0.7f, 1.0f, "Buffer does not get exceeded")), + dynamicTest("14", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.5f, 0.5f, 1.0f, "Half buffer, filled with excess")) + }; + } + void test_bufferedConsumptionCalculation(float producedPower, float maxBuffer, float powerPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){ + Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); + Tile bufferedConsumerTile = createFakeTile(0, 1, createFakeBufferedConsumer(maxBuffer, maxBuffer > 0.0f ? maxBuffer/powerPerTick : 1.0f)); + bufferedConsumerTile.entity.power.satisfaction = initialSatisfaction; - // If these lines fail, you probably changed power production/consumption and need to adapt this test - // OR their implementation is corrupt. - // TODO: Create fake blocks which are independent of such changes - assertEquals(powerNeeded, 0.09f, epsilon); - assertEquals(powerProduced, 0.09f, epsilon); - // Note: The assertions above induce that powerNeeded = powerProduced (with floating point inaccuracy) + PowerGraph powerGraph = new PowerGraph(); + powerGraph.add(producerTile); + powerGraph.add(bufferedConsumerTile); - // Distribute power and make sure the water extractor is powered - powerGraph.distributePower(powerNeeded, powerProduced); - assertEquals(waterExtractorTile.entity.power.satisfaction, 1.0f, epsilon); - } + assumeTrue(MathUtils.isEqual(producedPower, powerGraph.getPowerProduced())); + //assumeTrue(MathUtils.isEqual(Math.min(maxBuffer, powerPerTick), powerGraph.getPowerNeeded())); - /** Makes sure there are no problems with zero production. */ - @Test - void test_noProducers(){ - PowerGraph powerGraph = new PowerGraph(); - - Tile waterExtractorTile = createFakeTile(0, 0, ProductionBlocks.waterExtractor); - powerGraph.add(waterExtractorTile); - - float powerNeeded = powerGraph.getPowerNeeded(); - float powerProduced = powerGraph.getPowerProduced(); - - assertEquals(powerGraph.getPowerNeeded(), 0.09f, epsilon); - assertEquals(powerGraph.getPowerProduced(), 0.0f, epsilon); - - powerGraph.distributePower(powerNeeded, powerProduced); - assertEquals(waterExtractorTile.entity.power.satisfaction, 0.0f, epsilon); - } - - /** Makes sure there are no problems with zero consumers. */ - @Test - void test_noConsumers(){ - PowerGraph powerGraph = new PowerGraph(); - - Tile solarPanelTile = createFakeTile( 0, 0, PowerBlocks.solarPanel); - powerGraph.add(solarPanelTile); - - float powerNeeded = powerGraph.getPowerNeeded(); - float powerProduced = powerGraph.getPowerProduced(); - - assertEquals(powerGraph.getPowerNeeded(), 0.0f, epsilon); - assertEquals(powerGraph.getPowerProduced(), 0.0045f, epsilon); - - powerGraph.distributePower(powerNeeded, powerProduced); + // Update and check for the expected power satisfaction of the consumer + powerGraph.update(); + assertEquals(expectedSatisfaction, bufferedConsumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of buffered consumer did not match"); + } } } From 99139dfccabece71685c943545bc4e02c19739fb Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Mon, 26 Nov 2018 08:58:16 +0100 Subject: [PATCH 17/67] Added unit tests for batteries... ...and fixed battery distribution until all tests passed --- .../world/blocks/power/PowerGraph.java | 20 ++++++++--- tests/src/test/java/PowerTests.java | 33 +++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) 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 b17493349a..8c07c31580 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -80,9 +80,15 @@ public class PowerGraph{ if(MathUtils.isEqual(stored, 0f)){ return 0f; } float used = Math.min(stored, needed); - float percentageRemaining = 1f - (used / stored); + float consumedPowerPercentage = Math.min(1.0f, needed / stored); for(Tile battery : batteries){ - battery.entity.power.satisfaction *= percentageRemaining; + Consumers consumes = battery.block().consumes; + if(consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumes.get(ConsumePower.class); + if(consumePower.powerCapacity > 0f){ + battery.entity.power.satisfaction = Math.max(0.0f, battery.entity.power.satisfaction - consumedPowerPercentage); + } + } } return used; } @@ -91,9 +97,15 @@ public class PowerGraph{ float capacity = getBatteryCapacity(); if(MathUtils.isEqual(capacity, 0f)){ return 0f; } - float thing = Math.min(1, excess / capacity); for(Tile battery : batteries){ - battery.entity.power.satisfaction += (1 - battery.entity.power.satisfaction) * thing; + Consumers consumes = battery.block().consumes; + if(consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumes.get(ConsumePower.class); + 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); + } + } } return Math.min(excess, capacity); } diff --git a/tests/src/test/java/PowerTests.java b/tests/src/test/java/PowerTests.java index 096e2d126a..6d6655a5b1 100644 --- a/tests/src/test/java/PowerTests.java +++ b/tests/src/test/java/PowerTests.java @@ -101,5 +101,38 @@ public class PowerTests extends PowerTestFixture{ powerGraph.update(); assertEquals(expectedSatisfaction, bufferedConsumerTile.entity.power.satisfaction, MathUtils.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. + */ + @TestFactory + DynamicTest[] testDirectConsumptionWithBattery(){ + return new DynamicTest[]{ + dynamicTest("1", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 0.0f, 10.0f, 0.0f, "Empty battery, no consumer")), + dynamicTest("2", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 90.0f, 100.0f, 0.0f, "Battery full after update, no consumer")), + dynamicTest("3", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 100.0f, 100.0f, 0.0f, "Full battery, no consumer")), + dynamicTest("4", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, "No producer, no consumer, empty battery")), + dynamicTest("5", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 100.0f, 100.0f, 0.0f, "No producer, no consumer, full battery")), + dynamicTest("6", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 0.0f, 0.0f, 0.0f, "No producer, empty battery")), + dynamicTest("7", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 100.0f, 90.0f, 1.0f, "No producer, full battery")), + dynamicTest("8", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 5.0f, 0.0f, 0.5f, "No producer, low battery")) + }; + } + void test_directConsumptionWithBattery(float producedPower, float requestedPower, float initialBatteryCapacity, float expectedBatteryCapacity, float expectedSatisfaction, String parameterDescription){ + Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); + Tile directConsumerTile = createFakeTile(0, 1, createFakeDirectConsumer(requestedPower, 0.6f)); + float maxCapacity = 100f; + Tile batteryTile = createFakeTile(0, 2, createFakeBattery(maxCapacity, 10 )); + batteryTile.entity.power.satisfaction = initialBatteryCapacity / maxCapacity; + + PowerGraph powerGraph = new PowerGraph(); + powerGraph.add(producerTile); + powerGraph.add(directConsumerTile); + powerGraph.add(batteryTile); + + powerGraph.update(); + assertEquals(expectedBatteryCapacity, batteryTile.entity.power.satisfaction * maxCapacity, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Expected battery capacity did not match"); + assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); + } } } From 77b9feb76554dc3ef45559d7b40e597fae033b4f Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Mon, 26 Nov 2018 09:24:08 +0100 Subject: [PATCH 18/67] More Tests and fixes - Batteries will now get charged with no consumers - Fixed stat display of power generators --- .../world/blocks/power/PowerGenerator.java | 2 +- .../world/blocks/power/PowerGraph.java | 2 +- tests/src/test/java/PowerTests.java | 37 ++++++++++++------- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index b1ee6f73ce..b489e827da 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -22,7 +22,7 @@ public class PowerGenerator extends PowerDistributor{ @Override public void setStats(){ super.setStats(); - stats.add(generationType, powerProduction, StatUnit.powerSecond); + stats.add(generationType, powerProduction * 60.0f, StatUnit.powerSecond); } @Override 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 8c07c31580..c8f5cfe846 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -132,7 +132,7 @@ public class PowerGraph{ } public void update(){ - if(threads.getFrameID() == lastFrameUpdated || consumers.size == 0 || producers.size == 0){ + if(threads.getFrameID() == lastFrameUpdated || consumers.size == 0 && producers.size == 0 && batteries.size == 0){ return; } diff --git a/tests/src/test/java/PowerTests.java b/tests/src/test/java/PowerTests.java index 6d6655a5b1..3b93652c0e 100644 --- a/tests/src/test/java/PowerTests.java +++ b/tests/src/test/java/PowerTests.java @@ -108,31 +108,40 @@ public class PowerTests extends PowerTestFixture{ @TestFactory DynamicTest[] testDirectConsumptionWithBattery(){ return new DynamicTest[]{ - dynamicTest("1", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 0.0f, 10.0f, 0.0f, "Empty battery, no consumer")), - dynamicTest("2", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 90.0f, 100.0f, 0.0f, "Battery full after update, no consumer")), - dynamicTest("3", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 100.0f, 100.0f, 0.0f, "Full battery, no consumer")), - dynamicTest("4", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, "No producer, no consumer, empty battery")), - dynamicTest("5", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 100.0f, 100.0f, 0.0f, "No producer, no consumer, full battery")), - dynamicTest("6", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 0.0f, 0.0f, 0.0f, "No producer, empty battery")), - dynamicTest("7", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 100.0f, 90.0f, 1.0f, "No producer, full battery")), - dynamicTest("8", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 5.0f, 0.0f, 0.5f, "No producer, low battery")) + dynamicTest("01", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 0.0f, 10.0f, 0.0f, "Empty battery, no consumer")), + dynamicTest("02", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 90.0f, 100.0f, 0.0f, "Battery full after update, no consumer")), + dynamicTest("03", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 100.0f, 100.0f, 0.0f, "Full battery, no consumer")), + dynamicTest("04", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, "No producer, no consumer, empty battery")), + dynamicTest("05", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 100.0f, 100.0f, 0.0f, "No producer, no consumer, full battery")), + dynamicTest("06", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 0.0f, 0.0f, 0.0f, "No producer, empty battery")), + dynamicTest("07", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 100.0f, 90.0f, 1.0f, "No producer, full battery")), + dynamicTest("08", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 5.0f, 0.0f, 0.5f, "No producer, low battery")), + dynamicTest("09", () -> test_directConsumptionWithBattery(5.0f, 10.0f, 5.0f, 0.0f, 1.0f, "Producer + Battery = Consumed")), }; } void test_directConsumptionWithBattery(float producedPower, float requestedPower, float initialBatteryCapacity, float expectedBatteryCapacity, float expectedSatisfaction, String parameterDescription){ - Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); - Tile directConsumerTile = createFakeTile(0, 1, createFakeDirectConsumer(requestedPower, 0.6f)); + PowerGraph powerGraph = new PowerGraph(); + + if(producedPower > 0.0f){ + Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); + powerGraph.add(producerTile); + } + Tile directConsumerTile = null; + if(requestedPower > 0.0f){ + directConsumerTile = createFakeTile(0, 1, createFakeDirectConsumer(requestedPower, 0.6f)); + powerGraph.add(directConsumerTile); + } float maxCapacity = 100f; Tile batteryTile = createFakeTile(0, 2, createFakeBattery(maxCapacity, 10 )); batteryTile.entity.power.satisfaction = initialBatteryCapacity / maxCapacity; - PowerGraph powerGraph = new PowerGraph(); - powerGraph.add(producerTile); - powerGraph.add(directConsumerTile); powerGraph.add(batteryTile); powerGraph.update(); assertEquals(expectedBatteryCapacity, batteryTile.entity.power.satisfaction * maxCapacity, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Expected battery capacity did not match"); - assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); + if(directConsumerTile != null){ + assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); + } } } } From f640494c1ff1680607677eff83ccf549d42e9fb5 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Mon, 26 Nov 2018 10:26:49 +0100 Subject: [PATCH 19/67] PowerGraph now handles delta Also power bars are displayed for non-buffered consumers as well --- core/src/io/anuke/mindustry/world/Block.java | 2 +- .../mindustry/world/blocks/power/PowerGenerator.java | 2 +- .../io/anuke/mindustry/world/blocks/power/PowerGraph.java | 8 +++++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 8eebd822cc..e00bd316d4 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -338,7 +338,7 @@ public class Block extends BaseBlock { //TODO make this easier to config. public void setBars(){ - if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ + if(consumes.has(ConsumePower.class)){ bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); } if(hasLiquids) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index b489e827da..915ef48021 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -27,7 +27,7 @@ public class PowerGenerator extends PowerDistributor{ @Override public float getPowerProduction(Tile tile){ - return powerProduction * tile.entity().productionEfficiency * tile.entity.delta(); + return powerProduction * tile.entity().productionEfficiency; } @Override 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 c8f5cfe846..8439d79c11 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -37,7 +37,7 @@ public class PowerGraph{ public float getPowerProduced(){ float powerProduced = 0f; for(Tile producer : producers){ - powerProduced += producer.block().getPowerProduction(producer); + powerProduced += producer.block().getPowerProduction(producer) * producer.entity.delta(); } return powerProduced; } @@ -47,7 +47,7 @@ public class PowerGraph{ for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; if(consumes.has(ConsumePower.class)){ - powerNeeded += consumes.get(ConsumePower.class).requestedPower(consumer.block(), consumer.entity); + powerNeeded += consumes.get(ConsumePower.class).requestedPower(consumer.block(), consumer.entity) * consumer.entity.delta(); } } return powerNeeded; @@ -69,7 +69,7 @@ public class PowerGraph{ for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; if(consumes.has(ConsumePower.class)){ - totalCapacity += consumes.get(ConsumePower.class).requestedPower(battery.block(), battery.entity); + totalCapacity += consumes.get(ConsumePower.class).requestedPower(battery.block(), battery.entity) * battery.entity.delta(); } } return totalCapacity; @@ -218,6 +218,8 @@ public class PowerGraph{ } } } + // Update the graph once so direct consumers without any connected producer lose their power + graph.update(); } } From 11e071289bb60f0f00a5b9010b2a1a5bf2a77f0f Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Tue, 27 Nov 2018 08:51:19 +0100 Subject: [PATCH 20/67] Reorganized power tests ... ... and changed creation of fake tiles so update() implementations have all required dependencies --- .../java/{ => power}/FakeThreadHandler.java | 3 +- .../java/power/ItemLiquidGeneratorTests.java | 37 +++++++++++++++++ .../java/{ => power}/PowerTestFixture.java | 29 +++++++++++--- .../src/test/java/{ => power}/PowerTests.java | 40 ++++++++++++++----- 4 files changed, 92 insertions(+), 17 deletions(-) rename tests/src/test/java/{ => power}/FakeThreadHandler.java (94%) create mode 100644 tests/src/test/java/power/ItemLiquidGeneratorTests.java rename tests/src/test/java/{ => power}/PowerTestFixture.java (67%) rename tests/src/test/java/{ => power}/PowerTests.java (88%) diff --git a/tests/src/test/java/FakeThreadHandler.java b/tests/src/test/java/power/FakeThreadHandler.java similarity index 94% rename from tests/src/test/java/FakeThreadHandler.java rename to tests/src/test/java/power/FakeThreadHandler.java index 3588205e98..9b2676c49b 100644 --- a/tests/src/test/java/FakeThreadHandler.java +++ b/tests/src/test/java/power/FakeThreadHandler.java @@ -1,4 +1,5 @@ -import com.badlogic.gdx.Gdx; +package power; + import io.anuke.mindustry.core.ThreadHandler; import io.anuke.ucore.core.Timers; diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java new file mode 100644 index 0000000000..930f257cd9 --- /dev/null +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -0,0 +1,37 @@ +package power; + +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.Liquid; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.power.ItemLiquidGenerator; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** This class tests the abstract ItemLiquidGenerator class and maybe some of its dependencies. */ +public class ItemLiquidGeneratorTests extends PowerTestFixture{ + + private ItemLiquidGenerator sut; // system under test (https://en.wikipedia.org/wiki/System_under_test) + private Tile tile; + + @BeforeEach + public void createItemLiquidGenerator(){ + sut = new ItemLiquidGenerator("fakegen"){ + @Override + protected float getLiquidEfficiency(Liquid liquid){ + return liquid.flammability; + } + + @Override + protected float getItemEfficiency(Item item){ + return item.flammability; + } + }; + tile = createFakeTile(0, 0, sut); + } + + @Test + void detectCrashes(){ + sut.update(tile); + } +} diff --git a/tests/src/test/java/PowerTestFixture.java b/tests/src/test/java/power/PowerTestFixture.java similarity index 67% rename from tests/src/test/java/PowerTestFixture.java rename to tests/src/test/java/power/PowerTestFixture.java index 2ecd5a7dc7..e2bc95ec4e 100644 --- a/tests/src/test/java/PowerTestFixture.java +++ b/tests/src/test/java/power/PowerTestFixture.java @@ -1,18 +1,31 @@ +package power; + +import io.anuke.mindustry.Vars; import io.anuke.mindustry.content.blocks.Blocks; +import io.anuke.mindustry.core.ContentLoader; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Floor; +import io.anuke.mindustry.world.blocks.PowerBlock; import io.anuke.mindustry.world.blocks.power.Battery; import io.anuke.mindustry.world.blocks.power.PowerGenerator; import io.anuke.mindustry.world.modules.PowerModule; +import org.junit.jupiter.api.BeforeAll; import java.lang.reflect.Field; +import java.lang.reflect.Method; /** This class provides objects commonly used by power related unit tests. * For now, this is a helper with static methods, but this might change. * */ public class PowerTestFixture{ + @BeforeAll + static void initializeDependencies(){ + Vars.content = new ContentLoader(); + Vars.content.load(); + Vars.threads = new FakeThreadHandler(); + } + protected static PowerGenerator createFakeProducerBlock(float producedPower){ return new PowerGenerator("fakegen"){{ powerProduction = producedPower; @@ -26,13 +39,13 @@ public class PowerTestFixture{ } protected static Block createFakeDirectConsumer(float powerPerTick, float minimumSatisfaction){ - return new Block("fakedirectconsumer"){{ + return new PowerBlock("fakedirectconsumer"){{ consumes.powerDirect(powerPerTick, minimumSatisfaction); }}; } protected static Block createFakeBufferedConsumer(float capacity, float ticksToFill){ - return new Block("fakebufferedconsumer"){{ + return new PowerBlock("fakebufferedconsumer"){{ consumes.powerBuffered(capacity, ticksToFill); }}; } @@ -47,6 +60,10 @@ public class PowerTestFixture{ try{ Tile tile = new Tile(x, y); + // Using the Tile(int, int, byte, byte) constructor would require us to register any fake block or tile we create + // Since this part shall not be part of the test and would require more work anyway, we manually set the block and floor + // and call the private changed() method through reflections. + Field field = Tile.class.getDeclaredField("wall"); field.setAccessible(true); field.set(tile, block); @@ -55,8 +72,10 @@ public class PowerTestFixture{ field.setAccessible(true); field.set(tile, Blocks.sand); - tile.entity = block.newEntity(); - tile.entity.power = new PowerModule(); + Method method = Tile.class.getDeclaredMethod("changed"); + method.setAccessible(true); + method.invoke(tile); + return tile; }catch(Exception ex){ return null; diff --git a/tests/src/test/java/PowerTests.java b/tests/src/test/java/power/PowerTests.java similarity index 88% rename from tests/src/test/java/PowerTests.java rename to tests/src/test/java/power/PowerTests.java index 3b93652c0e..48b5cf8c6f 100644 --- a/tests/src/test/java/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -1,28 +1,22 @@ +package power; + import com.badlogic.gdx.math.MathUtils; import io.anuke.mindustry.Vars; -import io.anuke.mindustry.content.blocks.PowerBlocks; -import io.anuke.mindustry.content.blocks.ProductionBlocks; import io.anuke.mindustry.core.ContentLoader; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.power.PowerGraph; +import io.anuke.mindustry.world.consumers.ConsumePower; import org.junit.jupiter.api.*; -import org.junit.jupiter.params.provider.ValueSource; -import org.junit.jupiter.params.ParameterizedTest; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.DynamicTest.dynamicTest; public class PowerTests extends PowerTestFixture{ - @BeforeAll - static void initializeDependencies(){ - Vars.content = new ContentLoader(); - Vars.content.load(); - Vars.threads = new FakeThreadHandler(); - } - @BeforeEach void initTest(){ } @@ -143,5 +137,29 @@ public class PowerTests extends PowerTestFixture{ assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); } } + + /** Makes sure a direct consumer stops working after power production is set to zero. */ + @Test + void testDirectConsumptionStopsWithNoPower(){ + Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(10.0f)); + Tile consumerTile = createFakeTile(0, 1, createFakeDirectConsumer(5.0f, 0.6f)); + + PowerGraph powerGraph = new PowerGraph(); + powerGraph.add(producerTile); + powerGraph.add(consumerTile); + powerGraph.update(); + + assertEquals(1.0f, consumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR); + + powerGraph.remove(producerTile); + powerGraph.add(consumerTile); + powerGraph.update(); + + assertEquals(0.0f, consumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR); + if(consumerTile.block().consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumerTile.block().consumes.get(ConsumePower.class); + assertFalse(consumePower.valid(consumerTile.block(), consumerTile.entity())); + } + } } } From f56e1933a66c92d9f7cefe58b68c77ce5d48f6ce Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Tue, 27 Nov 2018 21:27:35 +0100 Subject: [PATCH 21/67] Fixed occassional crashes of Power Tests and continued TDD --- .../mindustry/content/blocks/PowerBlocks.java | 10 ++-- .../blocks/power/ItemLiquidGenerator.java | 15 +++--- .../world/blocks/power/LiquidGenerator.java | 5 +- .../blocks/power/LiquidHeatGenerator.java | 4 +- .../world/blocks/power/PowerGenerator.java | 2 +- .../java/power/ItemLiquidGeneratorTests.java | 50 +++++++++++++------ .../src/test/java/power/PowerTestFixture.java | 23 +++++++-- 7 files changed, 72 insertions(+), 37 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index 6b47d31a67..db497f5043 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -19,17 +19,17 @@ public class PowerBlocks extends BlockList implements ContentList{ thermalGenerator = new LiquidHeatGenerator("thermal-generator"){{ maxLiquidGenerate = 4f; - // TODO: Adapt to new power system - powerProduction = -1; - powerPerLiquid = 0.1f; + // TODO: Balance + powerProduction = 0.17f; + liquidPowerMultiplier = 0.1f; generateEffect = BlockFx.redgeneratespark; size = 2; }}; turbineGenerator = new TurbineGenerator("turbine-generator"){{ - // TODO: Adapt to new power system + // TODO: Balance powerProduction = 0.28f; - powerPerLiquid = 0.1f; + liquidPowerMultiplier = 0.3f; itemDuration = 30f; consumes.liquid(Liquids.water, 0.05f); size = 2; diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index 07f16b8767..83031719d2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -14,7 +14,7 @@ import static io.anuke.mindustry.Vars.tilesize; public abstract class ItemLiquidGenerator extends ItemGenerator{ protected float minLiquidEfficiency = 0.2f; - protected float powerPerLiquid = 0.13f; + protected float liquidPowerMultiplier = 1.3f; // A liquid with 100% flammability will be 30% more efficient than an item with 100% flammability. /**Maximum liquid used per frame.*/ protected float maxLiquidGenerate = 0.4f; @@ -46,15 +46,16 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ } //liquid takes priority over solids + float calculationDelta = entity.delta(); if(liquid != null && entity.liquids.get(liquid) >= 0.001f && entity.cons.valid()){ - float powerPerLiquid = getLiquidEfficiency(liquid) * this.powerPerLiquid; - float used = Math.min(entity.liquids.get(liquid), maxLiquidGenerate * entity.delta()); - // TODO: Adapt to new power system - //used = Math.min(used, (powerCapacity - entity.power.amount) / powerPerLiquid); + float baseLiquidEfficiency = getLiquidEfficiency(liquid) * this.liquidPowerMultiplier; + float maximumPossible = maxLiquidGenerate * calculationDelta; + float used = Math.min(entity.liquids.get(liquid), maximumPossible); entity.liquids.remove(liquid, used); - // TODO: Adapt to new power system - //entity.power.amount += used * powerPerLiquid; + + // Note: 1 Item with 100% Flammability = 100% efficiency. This means 100% is not max but rather a reference point for this generator. + entity.productionEfficiency = baseLiquidEfficiency * used / maximumPossible; if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java index 0b19f94fc0..7314c56a0d 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java @@ -13,7 +13,7 @@ import io.anuke.ucore.util.Mathf; public abstract class LiquidGenerator extends PowerGenerator{ protected float minEfficiency = 0.2f; - protected float powerPerLiquid; + protected float liquidPowerMultiplier; /**Maximum liquid used per frame.*/ protected float maxLiquidGenerate; protected Effect generateEffect = BlockFx.generatespark; @@ -50,8 +50,9 @@ public abstract class LiquidGenerator extends PowerGenerator{ public void update(Tile tile){ TileEntity entity = tile.entity(); + // TODO Code duplication with ItemLiquidGenerator if(entity.liquids.get(entity.liquids.current()) >= 0.001f){ - float powerPerLiquid = getEfficiency(entity.liquids.current()) * this.powerPerLiquid; + //float powerPerLiquid = getEfficiency(entity.liquids.current()) * this.powerPerLiquid; float used = Math.min(entity.liquids.currentAmount(), maxLiquidGenerate * entity.delta()); // TODO Adapt to new power system //used = Math.min(used, (powerCapacity - entity.power.amount) / powerPerLiquid); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java index 8b5665b4c5..9129e884e9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java @@ -14,9 +14,9 @@ public class LiquidHeatGenerator extends LiquidGenerator{ public void setStats(){ super.setStats(); - // TODO Verify for new power system stats.remove(BlockStat.basePowerGeneration); - stats.add(BlockStat.basePowerGeneration, maxLiquidGenerate * powerPerLiquid * 60f, StatUnit.powerSecond); + // TODO Adapt to new new power system. Maybe this override can be removed. + //stats.add(BlockStat.basePowerGeneration, * 60f, StatUnit.powerSecond); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index 915ef48021..48d8bd0106 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -10,7 +10,7 @@ import io.anuke.mindustry.world.meta.BlockStat; public class PowerGenerator extends PowerDistributor{ /** The amount of power produced per tick. */ - public float powerProduction; + protected float powerProduction; public BlockStat generationType = BlockStat.basePowerGeneration; public PowerGenerator(String name){ diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java index 930f257cd9..cf54743faf 100644 --- a/tests/src/test/java/power/ItemLiquidGeneratorTests.java +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -1,37 +1,57 @@ package power; +import io.anuke.mindustry.content.Liquids; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.power.BurnerGenerator; +import io.anuke.mindustry.world.blocks.power.ItemGenerator; import io.anuke.mindustry.world.blocks.power.ItemLiquidGenerator; +import io.anuke.mindustry.world.blocks.power.PowerGenerator; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + /** This class tests the abstract ItemLiquidGenerator class and maybe some of its dependencies. */ public class ItemLiquidGeneratorTests extends PowerTestFixture{ - private ItemLiquidGenerator sut; // system under test (https://en.wikipedia.org/wiki/System_under_test) + private ItemLiquidGenerator generator; private Tile tile; + private ItemGenerator.ItemGeneratorEntity entity; + private final float fakeLiquidPowerMultiplier = 2.0f; + private final float fakeMaxLiquidGenerate = 0.5f; @BeforeEach - public void createItemLiquidGenerator(){ - sut = new ItemLiquidGenerator("fakegen"){ - @Override - protected float getLiquidEfficiency(Liquid liquid){ - return liquid.flammability; - } + public void createBurnerGenerator(){ + // Use a burner generator instead of a custom ItemLiquidGenerator subclass since we would implement abstract methods the same way. + generator = new BurnerGenerator("fakegen"){{ + powerProduction = 0.1f; + itemDuration = 60f; + liquidPowerMultiplier = fakeLiquidPowerMultiplier; + maxLiquidGenerate = fakeMaxLiquidGenerate; + }}; - @Override - protected float getItemEfficiency(Item item){ - return item.flammability; - } - }; - tile = createFakeTile(0, 0, sut); + tile = createFakeTile(0, 0, generator); + entity = tile.entity(); } @Test - void detectCrashes(){ - sut.update(tile); + void testLiquidConsumption(){ + final float providedUsage = 0.1f; + final float expectedEfficiency = providedUsage / fakeMaxLiquidGenerate * fakeLiquidPowerMultiplier * Liquids.oil.flammability; + + entity.liquids.add(Liquids.oil, providedUsage); + entity.cons.update(tile.entity); + assumeTrue(entity.cons.valid()); + + // Perform an update on the generator once - This should use up all oil and produce a fraction of what's possible + generator.update(tile); + + assertEquals(0.0f, entity.liquids.get(Liquids.oil)); + assertEquals(expectedEfficiency, entity.productionEfficiency); } } diff --git a/tests/src/test/java/power/PowerTestFixture.java b/tests/src/test/java/power/PowerTestFixture.java index e2bc95ec4e..1588f54188 100644 --- a/tests/src/test/java/power/PowerTestFixture.java +++ b/tests/src/test/java/power/PowerTestFixture.java @@ -3,17 +3,25 @@ package power; import io.anuke.mindustry.Vars; import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.core.ContentLoader; +import io.anuke.mindustry.core.World; +import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.PowerBlock; import io.anuke.mindustry.world.blocks.power.Battery; import io.anuke.mindustry.world.blocks.power.PowerGenerator; +import io.anuke.mindustry.world.modules.ConsumeModule; +import io.anuke.mindustry.world.modules.ItemModule; +import io.anuke.mindustry.world.modules.LiquidModule; import io.anuke.mindustry.world.modules.PowerModule; +import io.anuke.ucore.entities.Entities; import org.junit.jupiter.api.BeforeAll; import java.lang.reflect.Field; import java.lang.reflect.Method; +import static io.anuke.mindustry.Vars.world; + /** This class provides objects commonly used by power related unit tests. * For now, this is a helper with static methods, but this might change. * */ @@ -62,7 +70,7 @@ public class PowerTestFixture{ // Using the Tile(int, int, byte, byte) constructor would require us to register any fake block or tile we create // Since this part shall not be part of the test and would require more work anyway, we manually set the block and floor - // and call the private changed() method through reflections. + // through reflections and then simulate part of what the changed() method does. Field field = Tile.class.getDeclaredField("wall"); field.setAccessible(true); @@ -72,10 +80,15 @@ public class PowerTestFixture{ field.setAccessible(true); field.set(tile, Blocks.sand); - Method method = Tile.class.getDeclaredMethod("changed"); - method.setAccessible(true); - method.invoke(tile); - + // Simulate the "changed" method. Calling it through reflections would require half the game to be initialized. + tile.entity = block.newEntity().init(tile, false); + tile.entity.cons = new ConsumeModule(); + if(block.hasItems) tile.entity.items = new ItemModule(); + if(block.hasLiquids) tile.entity.liquids = new LiquidModule(); + if(block.hasPower){ + tile.entity.power = new PowerModule(); + tile.entity.power.graph.add(tile); + } return tile; }catch(Exception ex){ return null; From 739219c57b7c703a6ed7ed9549030c8c45b8c949 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Tue, 27 Nov 2018 22:28:41 +0100 Subject: [PATCH 22/67] Finished first draft of ItemLiquidGenerator through TDD --- .../blocks/power/ItemLiquidGenerator.java | 14 ++- .../java/power/ItemLiquidGeneratorTests.java | 87 +++++++++++++++---- 2 files changed, 78 insertions(+), 23 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index 83031719d2..83d433b8c6 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -45,8 +45,12 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ } } - //liquid takes priority over solids + entity.productionEfficiency = 0.0f; + // Note: Do not use this delta when calculating the amount of power or the power efficiency, but use it for resource consumption if necessary. + // Power amount is delta'd by PowerGraph class already. float calculationDelta = entity.delta(); + + //liquid takes priority over solids if(liquid != null && entity.liquids.get(liquid) >= 0.001f && entity.cons.valid()){ float baseLiquidEfficiency = getLiquidEfficiency(liquid) * this.liquidPowerMultiplier; float maximumPossible = maxLiquidGenerate * calculationDelta; @@ -62,22 +66,16 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ } }else if(entity.cons.valid()){ - // TODO: Adapt to new power system - //float maxPower = Math.min(powerCapacity - entity.power.amount, powerOutput * entity.delta()) * entity.efficiency; - if(entity.generateTime <= 0f && entity.items.total() > 0){ Effects.effect(generateEffect, tile.worldx() + Mathf.range(3f), tile.worldy() + Mathf.range(3f)); Item item = entity.items.take(); - // TODO: Adapt to new power system - //entity.efficiency = getItemEfficiency(item); + entity.productionEfficiency = getItemEfficiency(item); entity.explosiveness = item.explosiveness; entity.generateTime = 1f; } if(entity.generateTime > 0f){ entity.generateTime -= 1f / itemDuration * entity.delta(); - // TODO: Adapt to new power system - //entity.power.amount += maxPower; entity.generateTime = Mathf.clamp(entity.generateTime); if(Mathf.chance(entity.delta() * 0.06 * Mathf.clamp(entity.explosiveness - 0.25f))){ diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java index cf54743faf..f8685709c7 100644 --- a/tests/src/test/java/power/ItemLiquidGeneratorTests.java +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -1,5 +1,7 @@ package power; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Items; import io.anuke.mindustry.content.Liquids; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Liquid; @@ -8,22 +10,25 @@ import io.anuke.mindustry.world.blocks.power.BurnerGenerator; import io.anuke.mindustry.world.blocks.power.ItemGenerator; import io.anuke.mindustry.world.blocks.power.ItemLiquidGenerator; import io.anuke.mindustry.world.blocks.power.PowerGenerator; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.DynamicTest.dynamicTest; -/** This class tests the abstract ItemLiquidGenerator class and maybe some of its dependencies. */ +/** + * This class tests ItemLiquidGenerators. Currently, testing is only performed on the BurnerGenerator subclass, + * which means only power calculations based on flammability are tested. + */ public class ItemLiquidGeneratorTests extends PowerTestFixture{ private ItemLiquidGenerator generator; private Tile tile; private ItemGenerator.ItemGeneratorEntity entity; private final float fakeLiquidPowerMultiplier = 2.0f; - private final float fakeMaxLiquidGenerate = 0.5f; + private final float fakeItemDuration = 0.5f; + private final float maximumLiquidUsage = 0.5f; @BeforeEach public void createBurnerGenerator(){ @@ -32,26 +37,78 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ powerProduction = 0.1f; itemDuration = 60f; liquidPowerMultiplier = fakeLiquidPowerMultiplier; - maxLiquidGenerate = fakeMaxLiquidGenerate; + itemDuration = fakeItemDuration; + maxLiquidGenerate = maximumLiquidUsage; }}; tile = createFakeTile(0, 0, generator); entity = tile.entity(); } - @Test - void testLiquidConsumption(){ - final float providedUsage = 0.1f; - final float expectedEfficiency = providedUsage / fakeMaxLiquidGenerate * fakeLiquidPowerMultiplier * Liquids.oil.flammability; + /** Tests the consumption and efficiency when being supplied with liquids. */ + @TestFactory + DynamicTest[] testLiquidConsumption(){ + return new DynamicTest[]{ + dynamicTest("01", () -> test_liquidConsumption(Liquids.oil, 0.0f, "No liquids provided")), + dynamicTest("02", () -> test_liquidConsumption(Liquids.oil, maximumLiquidUsage / 4.0f, "Low oil provided")), + dynamicTest("03", () -> test_liquidConsumption(Liquids.oil, maximumLiquidUsage * 1.0f, "Sufficient oil provided")), + dynamicTest("04", () -> test_liquidConsumption(Liquids.oil, maximumLiquidUsage * 2.0f, "Excess oil provided")) + // Note: The generator will decline any other liquid since it's not flammable + }; + } - entity.liquids.add(Liquids.oil, providedUsage); + void test_liquidConsumption(Liquid liquid, float availableLiquidAmount, String parameterDescription){ + final float expectedEfficiency = Math.min(1.0f, availableLiquidAmount / maximumLiquidUsage) * fakeLiquidPowerMultiplier * liquid.flammability; + final float expectedRemainingLiquidAmount = liquid.flammability > 0f ? Math.max(0.0f, availableLiquidAmount - maximumLiquidUsage) : availableLiquidAmount; + assertTrue(generator.acceptLiquid(tile, null, liquid, availableLiquidAmount), parameterDescription + ": Liquids which will be declined by the generator don't need to be tested - The code won't be called for those cases."); + + // Reset liquids since BeforeEach will not be called between dynamic tests + for(Liquid tmpLiquid : Vars.content.liquids()){ + entity.liquids.reset(tmpLiquid, 0.0f); + } + entity.liquids.add(liquid, availableLiquidAmount); entity.cons.update(tile.entity); - assumeTrue(entity.cons.valid()); + assertTrue(entity.cons.valid()); - // Perform an update on the generator once - This should use up all oil and produce a fraction of what's possible + // Perform an update on the generator once - This should use up any resource up to the maximum liquid usage generator.update(tile); - assertEquals(0.0f, entity.liquids.get(Liquids.oil)); - assertEquals(expectedEfficiency, entity.productionEfficiency); + assertEquals(expectedRemainingLiquidAmount, entity.liquids.get(liquid), parameterDescription + ": Remaining liquid amount mismatch."); + assertEquals(expectedEfficiency, entity.productionEfficiency, parameterDescription + ": Efficiency mismatch."); + } + + /** Tests the consumption and efficiency when being supplied with items. */ + @TestFactory + DynamicTest[] testItemConsumption(){ + return new DynamicTest[]{ + dynamicTest("01", () -> test_itemConsumption(Items.coal, 0, "No items provided")), + dynamicTest("02", () -> test_itemConsumption(Items.coal, 1, "Sufficient coal provided")), + dynamicTest("03", () -> test_itemConsumption(Items.coal, 10, "Excess coal provided")), + dynamicTest("04", () -> test_itemConsumption(Items.blastCompound, 1, "Blast compound provided")), + //dynamicTest("03", () -> test_itemConsumption(Items.plastanium, 1, "Plastanium provided")), // Not accepted by generator due to low flammability + dynamicTest("05", () -> test_itemConsumption(Items.biomatter, 1, "Biomatter provided")), + dynamicTest("06", () -> test_itemConsumption(Items.pyratite, 1, "Pyratite provided")) + }; + } + + void test_itemConsumption(Item item, int amount, String parameterDescription){ + final float expectedEfficiency = Math.min(1.0f, amount > 0 ? item.flammability : 0f); + final float expectedRemainingItemAmount = Math.max(0, amount - 1); + assertTrue(generator.acceptItem(item, tile, null), parameterDescription + ": Items which will be declined by the generator don't need to be tested - The code won't be called for those cases."); + + + // Reset items since BeforeEach will not be called between dynamic tests + entity.items.clear(); + if(amount > 0){ + entity.items.add(item, amount); + } + entity.cons.update(tile.entity); + assertTrue(entity.cons.valid()); + + // Perform an update on the generator once - This should use up one or zero items - dependent on if the item is accepted and available or not. + generator.update(tile); + + assertEquals(expectedRemainingItemAmount, entity.items.get(item), parameterDescription + ": Remaining item amount mismatch."); + assertEquals(expectedEfficiency, entity.productionEfficiency, parameterDescription + ": Efficiency mismatch."); } } From 1f8751054ceee612728f3731fa8e2f470d47cf8a Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 28 Nov 2018 11:04:08 +0100 Subject: [PATCH 23/67] Tested/fixed delta calculations Tests now use a fixed delta of 0.5 to make sure calculations work with deltas different from 1.0 --- .../blocks/power/ItemLiquidGenerator.java | 2 +- .../world/blocks/power/PowerGraph.java | 2 +- .../test/java/power/FakeThreadHandler.java | 3 +- .../java/power/ItemLiquidGeneratorTests.java | 13 ++-- .../src/test/java/power/PowerTestFixture.java | 7 +++ tests/src/test/java/power/PowerTests.java | 59 +++++++++++-------- 6 files changed, 53 insertions(+), 33 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index 83d433b8c6..c3e19143e2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -54,7 +54,7 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ if(liquid != null && entity.liquids.get(liquid) >= 0.001f && entity.cons.valid()){ float baseLiquidEfficiency = getLiquidEfficiency(liquid) * this.liquidPowerMultiplier; float maximumPossible = maxLiquidGenerate * calculationDelta; - float used = Math.min(entity.liquids.get(liquid), maximumPossible); + float used = Math.min(entity.liquids.get(liquid) * calculationDelta, maximumPossible); entity.liquids.remove(liquid, used); 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 8439d79c11..e9e693755a 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -121,7 +121,7 @@ public class PowerGraph{ if(consumePower.isBuffered){ // Add a percentage of the requested amount, but limit it to the mission amount. // TODO This can maybe be calculated without converting to absolute values first - float maximumRate = consumePower.requestedPower(consumer.block(), consumer.entity()) * coverage; + float maximumRate = consumePower.requestedPower(consumer.block(), consumer.entity()) * coverage * consumer.entity.delta(); float missingAmount = consumePower.powerCapacity * (1 - consumer.entity.power.satisfaction); consumer.entity.power.satisfaction += Math.min(missingAmount, maximumRate) / consumePower.powerCapacity; }else{ diff --git a/tests/src/test/java/power/FakeThreadHandler.java b/tests/src/test/java/power/FakeThreadHandler.java index 9b2676c49b..9767268df3 100644 --- a/tests/src/test/java/power/FakeThreadHandler.java +++ b/tests/src/test/java/power/FakeThreadHandler.java @@ -6,11 +6,12 @@ import io.anuke.ucore.core.Timers; /** Fake thread handler which produces a new frame each time getFrameID is called and always provides a delta of 1. */ public class FakeThreadHandler extends ThreadHandler{ private int fakeFrameId = 0; + public static final float fakeDelta = 0.5f; FakeThreadHandler(){ super(); - Timers.setDeltaProvider(() -> 1.0f); + Timers.setDeltaProvider(() -> fakeDelta); } @Override public long getFrameID(){ diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java index f8685709c7..471b59bdf6 100644 --- a/tests/src/test/java/power/ItemLiquidGeneratorTests.java +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -9,10 +9,8 @@ import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.power.BurnerGenerator; import io.anuke.mindustry.world.blocks.power.ItemGenerator; import io.anuke.mindustry.world.blocks.power.ItemLiquidGenerator; -import io.anuke.mindustry.world.blocks.power.PowerGenerator; import org.junit.jupiter.api.*; -import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.DynamicTest.dynamicTest; @@ -20,6 +18,10 @@ import static org.junit.jupiter.api.DynamicTest.dynamicTest; /** * This class tests ItemLiquidGenerators. Currently, testing is only performed on the BurnerGenerator subclass, * which means only power calculations based on flammability are tested. + * All tests are run with a fixed delta of 0.5 so delta considerations can be tested as well. + * Additionally, each PowerGraph::update() call will have its own thread frame, i.e. the method will never be called twice within the same frame. + * Both of these constraints are handled by FakeThreadHandler within PowerTestFixture. + * Any power amount (produced, consumed, buffered) should be affected by FakeThreadHandler.fakeDelta but satisfaction should not! */ public class ItemLiquidGeneratorTests extends PowerTestFixture{ @@ -58,8 +60,11 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ } void test_liquidConsumption(Liquid liquid, float availableLiquidAmount, String parameterDescription){ - final float expectedEfficiency = Math.min(1.0f, availableLiquidAmount / maximumLiquidUsage) * fakeLiquidPowerMultiplier * liquid.flammability; - final float expectedRemainingLiquidAmount = liquid.flammability > 0f ? Math.max(0.0f, availableLiquidAmount - maximumLiquidUsage) : availableLiquidAmount; + final float baseEfficiency = fakeLiquidPowerMultiplier * liquid.flammability; + final float expectedEfficiency = Math.min(1.0f, availableLiquidAmount / maximumLiquidUsage) * baseEfficiency; + final float expectedConsumptionPerTick = Math.min(maximumLiquidUsage, availableLiquidAmount); + final float expectedRemainingLiquidAmount = Math.max(0.0f, availableLiquidAmount - expectedConsumptionPerTick * FakeThreadHandler.fakeDelta); + assertTrue(generator.acceptLiquid(tile, null, liquid, availableLiquidAmount), parameterDescription + ": Liquids which will be declined by the generator don't need to be tested - The code won't be called for those cases."); // Reset liquids since BeforeEach will not be called between dynamic tests diff --git a/tests/src/test/java/power/PowerTestFixture.java b/tests/src/test/java/power/PowerTestFixture.java index 1588f54188..1e46d1e287 100644 --- a/tests/src/test/java/power/PowerTestFixture.java +++ b/tests/src/test/java/power/PowerTestFixture.java @@ -1,5 +1,6 @@ package power; +import com.badlogic.gdx.math.MathUtils; import io.anuke.mindustry.Vars; import io.anuke.mindustry.content.blocks.Blocks; import io.anuke.mindustry.core.ContentLoader; @@ -24,9 +25,15 @@ import static io.anuke.mindustry.Vars.world; /** This class provides objects commonly used by power related unit tests. * For now, this is a helper with static methods, but this might change. + * + * Note: All tests which subclass this will run with a fixed delta of 0.5! * */ public class PowerTestFixture{ + public static final float smallRoundingTolerance = MathUtils.FLOAT_ROUNDING_ERROR; + public static final float mediumRoundingTolerance = MathUtils.FLOAT_ROUNDING_ERROR * 10; + public static final float highRoundingTolerance = MathUtils.FLOAT_ROUNDING_ERROR * 100; + @BeforeAll static void initializeDependencies(){ Vars.content = new ContentLoader(); diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index 48b5cf8c6f..36d4e258dc 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -1,20 +1,23 @@ package power; import com.badlogic.gdx.math.MathUtils; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.core.ContentLoader; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.power.PowerGraph; import io.anuke.mindustry.world.consumers.ConsumePower; import org.junit.jupiter.api.*; -import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assumptions.assumeTrue; import static org.junit.jupiter.api.DynamicTest.dynamicTest; +/** + * Tests code related to the power system in general, but not specific blocks. + * All tests are run with a fixed delta of 0.5 so delta considerations can be tested as well. + * Additionally, each PowerGraph::update() call will have its own thread frame, i.e. the method will never be called twice within the same frame. + * Both of these constraints are handled by FakeThreadHandler within PowerTestFixture. + * Any power amount (produced, consumed, buffered) should be affected by FakeThreadHandler.fakeDelta but satisfaction should not! + */ public class PowerTests extends PowerTestFixture{ @BeforeEach @@ -33,6 +36,7 @@ public class PowerTests extends PowerTestFixture{ return new DynamicTest[]{ // Note: Unfortunately, the display names are not yet output through gradle. See https://github.com/gradle/gradle/issues/5975 // That's why we inject the description into the test method for now. + // Additional Note: If you don't see any labels in front of the values supplied as function parameters, use a better IDE like IntelliJ IDEA. dynamicTest("01", () -> test_directConsumptionCalculation(0.0f, 1.0f, 0.0f, "0.0 produced, 1.0 consumed (no power available)")), dynamicTest("02", () -> test_directConsumptionCalculation(0.0f, 0.0f, 0.0f, "0.0 produced, 0.0 consumed (no power anywhere)")), dynamicTest("03", () -> test_directConsumptionCalculation(1.0f, 0.0f, 0.0f, "1.0 produced, 0.0 consumed (no power requested)")), @@ -50,8 +54,8 @@ public class PowerTests extends PowerTestFixture{ powerGraph.add(producerTile); powerGraph.add(directConsumerTile); - assumeTrue(MathUtils.isEqual(producedPower, powerGraph.getPowerProduced())); - assumeTrue(MathUtils.isEqual(requiredPower, powerGraph.getPowerNeeded())); + assertEquals(producedPower * FakeThreadHandler.fakeDelta, powerGraph.getPowerProduced(), MathUtils.FLOAT_ROUNDING_ERROR); + assertEquals(requiredPower * FakeThreadHandler.fakeDelta, powerGraph.getPowerNeeded(), MathUtils.FLOAT_ROUNDING_ERROR); // Update and check for the expected power satisfaction of the consumer powerGraph.update(); @@ -63,33 +67,35 @@ public class PowerTests extends PowerTestFixture{ DynamicTest[] testBufferedConsumption(){ 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", () -> test_bufferedConsumptionCalculation(0.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power anywhere")), dynamicTest("02", () -> test_bufferedConsumptionCalculation(0.0f, 1.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power provided")), dynamicTest("03", () -> test_bufferedConsumptionCalculation(1.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power requested")), - dynamicTest("04", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 1.0f, 0.0f, 1.0f, "Empty Buffer, Stable Power, One tick to fill")), - dynamicTest("05", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.1f, 0.0f, 0.1f, "Empty Buffer, Stable Power, multiple ticks to fill")), - dynamicTest("06", () -> test_bufferedConsumptionCalculation(1.0f, 0.5f, 0.5f, 0.0f, 1.0f, "Empty Buffer, Power excess, one tick to fill")), - dynamicTest("07", () -> test_bufferedConsumptionCalculation(1.0f, 0.5f, 0.1f, 0.0f, 0.2f, "Empty Buffer, Power excess, multiple ticks to fill")), - dynamicTest("08", () -> test_bufferedConsumptionCalculation(0.5f, 1.0f, 1.0f, 0.0f, 0.5f, "Empty Buffer, Power shortage, one tick to fill")), - dynamicTest("09", () -> test_bufferedConsumptionCalculation(0.5f, 1.0f, 0.1f, 0.0f, 0.1f, "Empty Buffer, Power shortage, multiple ticks to fill")), - dynamicTest("10", () -> test_bufferedConsumptionCalculation(0.0f, 1.0f, 0.1f, 0.5f, 0.5f, "Unchanged buffer with no power produced")), - dynamicTest("11", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.1f, 1.0f, 1.0f, "Unchanged buffer when already full")), - dynamicTest("12", () -> test_bufferedConsumptionCalculation(0.2f, 1.0f, 0.5f, 0.5f, 0.7f, "Half buffer, power shortage")), - dynamicTest("13", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.5f, 0.7f, 1.0f, "Buffer does not get exceeded")), - dynamicTest("14", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.5f, 0.5f, 1.0f, "Half buffer, filled with excess")) + dynamicTest("04", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 1.0f, 0.0f, 0.5f, "Empty Buffer, Stable Power, One tick to fill")), + dynamicTest("05", () -> test_bufferedConsumptionCalculation(2.0f, 1.0f, 2.0f, 0.0f, 1.0f, "Empty Buffer, Stable Power, One delta to fill")), + dynamicTest("06", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.1f, 0.0f, 0.05f, "Empty Buffer, Stable Power, multiple ticks to fill")), + dynamicTest("07", () -> test_bufferedConsumptionCalculation(1.2f, 0.5f, 1.0f, 0.0f, 1.0f, "Empty Buffer, Power excess, one delta to fill")), + dynamicTest("08", () -> test_bufferedConsumptionCalculation(1.0f, 0.5f, 0.1f, 0.0f, 0.1f, "Empty Buffer, Power excess, multiple ticks to fill")), + dynamicTest("09", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 2.0f, 0.0f, 0.5f, "Empty Buffer, Power shortage, one delta to fill")), + dynamicTest("10", () -> test_bufferedConsumptionCalculation(0.5f, 1.0f, 0.1f, 0.0f, 0.05f, "Empty Buffer, Power shortage, multiple ticks to fill")), + dynamicTest("11", () -> test_bufferedConsumptionCalculation(0.0f, 1.0f, 0.1f, 0.5f, 0.5f, "Unchanged buffer with no power produced")), + dynamicTest("12", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.1f, 1.0f, 1.0f, "Unchanged buffer when already full")), + dynamicTest("13", () -> test_bufferedConsumptionCalculation(0.2f, 1.0f, 0.5f, 0.5f, 0.6f, "Half buffer, power shortage")), + dynamicTest("14", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.5f, 0.9f, 1.0f, "Buffer does not get exceeded")), + dynamicTest("15", () -> test_bufferedConsumptionCalculation(2.0f, 1.0f, 1.0f, 0.5f, 1.0f, "Half buffer, filled with excess")) }; } - void test_bufferedConsumptionCalculation(float producedPower, float maxBuffer, float powerPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){ + void test_bufferedConsumptionCalculation(float producedPower, float maxBuffer, float powerConsumedPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); - Tile bufferedConsumerTile = createFakeTile(0, 1, createFakeBufferedConsumer(maxBuffer, maxBuffer > 0.0f ? maxBuffer/powerPerTick : 1.0f)); + 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); - assumeTrue(MathUtils.isEqual(producedPower, powerGraph.getPowerProduced())); - //assumeTrue(MathUtils.isEqual(Math.min(maxBuffer, powerPerTick), powerGraph.getPowerNeeded())); + assertEquals(producedPower * FakeThreadHandler.fakeDelta, powerGraph.getPowerProduced(), MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Produced power did not match"); + assertEquals(Math.min(maxBuffer, powerConsumedPerTick * FakeThreadHandler.fakeDelta), powerGraph.getPowerNeeded(), MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": ConsumedPower did not match"); // Update and check for the expected power satisfaction of the consumer powerGraph.update(); @@ -102,14 +108,15 @@ public class PowerTests extends PowerTestFixture{ @TestFactory DynamicTest[] testDirectConsumptionWithBattery(){ return new DynamicTest[]{ - dynamicTest("01", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 0.0f, 10.0f, 0.0f, "Empty battery, no consumer")), - dynamicTest("02", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 90.0f, 100.0f, 0.0f, "Battery full after update, no consumer")), + // Note: expectedBatteryCapacity is currently adjusted to a delta of 0.5! (FakeThreadHandler sets it to that) + dynamicTest("01", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 0.0f, 5.0f, 0.0f, "Empty battery, no consumer")), + dynamicTest("02", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 94.999f, 99.999f, 0.0f, "Battery almost full after update, no consumer")), dynamicTest("03", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 100.0f, 100.0f, 0.0f, "Full battery, no consumer")), dynamicTest("04", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, "No producer, no consumer, empty battery")), dynamicTest("05", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 100.0f, 100.0f, 0.0f, "No producer, no consumer, full battery")), dynamicTest("06", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 0.0f, 0.0f, 0.0f, "No producer, empty battery")), - dynamicTest("07", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 100.0f, 90.0f, 1.0f, "No producer, full battery")), - dynamicTest("08", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 5.0f, 0.0f, 0.5f, "No producer, low battery")), + dynamicTest("07", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 100.0f, 95.0f, 1.0f, "No producer, full battery")), + dynamicTest("08", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 2.5f, 0.0f, 0.5f, "No producer, low battery")), dynamicTest("09", () -> test_directConsumptionWithBattery(5.0f, 10.0f, 5.0f, 0.0f, 1.0f, "Producer + Battery = Consumed")), }; } @@ -132,7 +139,7 @@ public class PowerTests extends PowerTestFixture{ powerGraph.add(batteryTile); powerGraph.update(); - assertEquals(expectedBatteryCapacity, batteryTile.entity.power.satisfaction * maxCapacity, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Expected battery capacity did not match"); + assertEquals(expectedBatteryCapacity / maxCapacity, batteryTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Expected battery satisfaction did not match"); if(directConsumerTile != null){ assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); } From cf3d2c3def279031e104b48953c24c2d19c4d262 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 28 Nov 2018 11:59:58 +0100 Subject: [PATCH 24/67] Fixed handling of item duration --- .../blocks/power/ItemLiquidGenerator.java | 11 +++++--- .../java/power/ItemLiquidGeneratorTests.java | 27 +++++++++++++++++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index c3e19143e2..c41f8d1c39 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -45,13 +45,16 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ } } - entity.productionEfficiency = 0.0f; // Note: Do not use this delta when calculating the amount of power or the power efficiency, but use it for resource consumption if necessary. // Power amount is delta'd by PowerGraph class already. float calculationDelta = entity.delta(); + if(!entity.cons.valid()){ + entity.productionEfficiency = 0.0f; + return; + } //liquid takes priority over solids - if(liquid != null && entity.liquids.get(liquid) >= 0.001f && entity.cons.valid()){ + if(liquid != null && entity.liquids.get(liquid) >= 0.001f){ float baseLiquidEfficiency = getLiquidEfficiency(liquid) * this.liquidPowerMultiplier; float maximumPossible = maxLiquidGenerate * calculationDelta; float used = Math.min(entity.liquids.get(liquid) * calculationDelta, maximumPossible); @@ -64,7 +67,7 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); } - }else if(entity.cons.valid()){ + }else{ if(entity.generateTime <= 0f && entity.items.total() > 0){ Effects.effect(generateEffect, tile.worldx() + Mathf.range(3f), tile.worldy() + Mathf.range(3f)); @@ -82,6 +85,8 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ entity.damage(Mathf.random(8f)); Effects.effect(explodeEffect, tile.worldx() + Mathf.range(size * tilesize / 2f), tile.worldy() + Mathf.range(size * tilesize / 2f)); } + }else{ + entity.productionEfficiency = 0.0f; } } } diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java index 471b59bdf6..c1b1ff6bf8 100644 --- a/tests/src/test/java/power/ItemLiquidGeneratorTests.java +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -29,7 +29,7 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ private Tile tile; private ItemGenerator.ItemGeneratorEntity entity; private final float fakeLiquidPowerMultiplier = 2.0f; - private final float fakeItemDuration = 0.5f; + private final float fakeItemDuration = 60f; // 60 ticks private final float maximumLiquidUsage = 0.5f; @BeforeEach @@ -102,8 +102,11 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ assertTrue(generator.acceptItem(item, tile, null), parameterDescription + ": Items which will be declined by the generator don't need to be tested - The code won't be called for those cases."); - // Reset items since BeforeEach will not be called between dynamic tests + // Clean up manually since BeforeEach will not be called between dynamic tests entity.items.clear(); + entity.generateTime = 0.0f; + entity.productionEfficiency = 0.0f; + if(amount > 0){ entity.items.add(item, amount); } @@ -116,4 +119,24 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ assertEquals(expectedRemainingItemAmount, entity.items.get(item), parameterDescription + ": Remaining item amount mismatch."); assertEquals(expectedEfficiency, entity.productionEfficiency, parameterDescription + ": Efficiency mismatch."); } + + /** Makes sure the efficiency stays equal during the item duration. */ + @Test + void test_efficiencyConstantDuringItemDuration(){ + + // Burn a single coal and test for the duration + entity.items.add(Items.coal, 1); + entity.cons.update(tile.entity); + generator.update(tile); + + float expectedEfficiency = entity.productionEfficiency; + + float currentDuration = 0.0f; + while((currentDuration += FakeThreadHandler.fakeDelta) <= fakeItemDuration){ + generator.update(tile); + assertEquals(expectedEfficiency, entity.productionEfficiency, "Duration: " + String.valueOf(currentDuration)); + } + generator.update(tile); + assertEquals(0.0f, entity.productionEfficiency, "Duration: " + String.valueOf(currentDuration)); + } } From 2972780bed875a0129829d358699b13f1e903ac7 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 28 Nov 2018 13:19:52 +0100 Subject: [PATCH 25/67] Initial Efficiency is now zero. Blocks display efficiency. --- .../mindustry/content/blocks/PowerBlocks.java | 27 ++++++++++++++----- core/src/io/anuke/mindustry/world/Block.java | 3 +-- .../world/blocks/power/PowerGenerator.java | 17 +++++++++++- .../world/consumers/ConsumePower.java | 4 ++- tests/src/test/java/power/PowerTests.java | 5 ++++ 5 files changed, 45 insertions(+), 11 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index db497f5043..cf59a636d1 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -4,6 +4,7 @@ import io.anuke.mindustry.content.Liquids; import io.anuke.mindustry.content.fx.BlockFx; import io.anuke.mindustry.game.ContentList; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.power.*; public class PowerBlocks extends BlockList implements ContentList{ @@ -41,14 +42,26 @@ public class PowerBlocks extends BlockList implements ContentList{ itemDuration = 220f; }}; - solarPanel = new PowerGenerator("solar-panel"){{ - powerProduction = 0.0045f; - }}; + // TODO: Maybe reintroduce a class for the initial production efficiency + solarPanel = new PowerGenerator("solar-panel"){ + { + powerProduction = 0.0045f; + } + @Override + public void update(Tile tile){ + tile.entity().productionEfficiency = 1.0f; + } + }; - largeSolarPanel = new PowerGenerator("solar-panel-large"){{ - powerProduction = 0.055f; - size = 3; - }}; + largeSolarPanel = new PowerGenerator("solar-panel-large"){ + { + powerProduction = 0.055f; + } + @Override + public void update(Tile tile){ + tile.entity().productionEfficiency = 1.0f; + } + }; thoriumReactor = new NuclearReactor("thorium-reactor"){{ size = 3; diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 21ee395822..4e6caa78d2 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -337,9 +337,8 @@ public class Block extends BaseBlock { } public void setBars(){ - if(consumes.has(ConsumePower.class)){ + if(consumes.has(ConsumePower.class)) bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); - } if(hasLiquids) bars.add(new BlockBar(BarType.liquid, true, tile -> tile.entity.liquids.total() / liquidCapacity)); if(hasItems) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index 48d8bd0106..8a3e59dd9d 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -1,5 +1,7 @@ package io.anuke.mindustry.world.blocks.power; +import io.anuke.mindustry.world.BarType; +import io.anuke.mindustry.world.meta.BlockBar; import io.anuke.mindustry.world.meta.StatUnit; import io.anuke.ucore.util.EnumSet; @@ -11,6 +13,11 @@ import io.anuke.mindustry.world.meta.BlockStat; public class PowerGenerator extends PowerDistributor{ /** The amount of power produced per tick. */ protected float powerProduction; + /** The maximum possible efficiency for this generator. Supply values larger than 1.0f if more than 100% is possible. + * This could be the case when e.g. an item with 100% flammability is the reference point, but a more effective liquid + * can be supplied as an alternative. + */ + protected float maxEfficiency = 1.0f; public BlockStat generationType = BlockStat.basePowerGeneration; public PowerGenerator(String name){ @@ -40,8 +47,16 @@ public class PowerGenerator extends PowerDistributor{ return new GeneratorEntity(); } + @Override + public void setBars(){ + super.setBars(); + if(hasPower){ + bars.add(new BlockBar(BarType.power, true, tile -> tile.entity().productionEfficiency / maxEfficiency)); + } + } + public static class GeneratorEntity extends TileEntity{ public float generateTime; - public float productionEfficiency = 1; + public float productionEfficiency = 0.0f; } } diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java index 2fd27ace54..d4d9a3dffb 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -86,7 +86,9 @@ public class ConsumePower extends Consume{ * @return The amount of power which is requested per tick. */ public float requestedPower(Block block, TileEntity entity){ - // TODO Is it possible to make the block not consume power while items/liquids are missing? + // TODO Make the block not consume power on the following conditions, either here or in PowerGraph: + // - Other consumers are not valid, e.g. additional input items/liquids are missing + // - Buffer is full return powerPerTick; } diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index 36d4e258dc..25a85c58d2 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -2,6 +2,7 @@ package power; import com.badlogic.gdx.math.MathUtils; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.power.PowerGenerator; import io.anuke.mindustry.world.blocks.power.PowerGraph; import io.anuke.mindustry.world.consumers.ConsumePower; import org.junit.jupiter.api.*; @@ -48,6 +49,7 @@ public class PowerTests extends PowerTestFixture{ } void test_directConsumptionCalculation(float producedPower, float requiredPower, float expectedSatisfaction, String parameterDescription){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); + producerTile.entity().productionEfficiency = 1.0f; Tile directConsumerTile = createFakeTile(0, 1, createFakeDirectConsumer(requiredPower, 0.6f)); PowerGraph powerGraph = new PowerGraph(); @@ -87,6 +89,7 @@ public class PowerTests extends PowerTestFixture{ } void test_bufferedConsumptionCalculation(float producedPower, float maxBuffer, float powerConsumedPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); + producerTile.entity().productionEfficiency = 1.0f; Tile bufferedConsumerTile = createFakeTile(0, 1, createFakeBufferedConsumer(maxBuffer, maxBuffer > 0.0f ? maxBuffer/powerConsumedPerTick : 1.0f)); bufferedConsumerTile.entity.power.satisfaction = initialSatisfaction; @@ -125,6 +128,7 @@ public class PowerTests extends PowerTestFixture{ if(producedPower > 0.0f){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); + producerTile.entity().productionEfficiency = 1.0f; powerGraph.add(producerTile); } Tile directConsumerTile = null; @@ -149,6 +153,7 @@ public class PowerTests extends PowerTestFixture{ @Test void testDirectConsumptionStopsWithNoPower(){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(10.0f)); + producerTile.entity().productionEfficiency = 1.0f; Tile consumerTile = createFakeTile(0, 1, createFakeDirectConsumer(5.0f, 0.6f)); PowerGraph powerGraph = new PowerGraph(); From ec631f34995f174c5514ad83f33475fbd52364bf Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 28 Nov 2018 23:54:26 +0100 Subject: [PATCH 26/67] Changed test naming conventions as requested by Anuke --- .../java/power/ItemLiquidGeneratorTests.java | 32 ++++---- tests/src/test/java/power/PowerTests.java | 76 +++++++++---------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java index c1b1ff6bf8..9511059fda 100644 --- a/tests/src/test/java/power/ItemLiquidGeneratorTests.java +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -49,17 +49,17 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ /** Tests the consumption and efficiency when being supplied with liquids. */ @TestFactory - DynamicTest[] testLiquidConsumption(){ + DynamicTest[] generatorWorksProperlyWithLiquidInput(){ return new DynamicTest[]{ - dynamicTest("01", () -> test_liquidConsumption(Liquids.oil, 0.0f, "No liquids provided")), - dynamicTest("02", () -> test_liquidConsumption(Liquids.oil, maximumLiquidUsage / 4.0f, "Low oil provided")), - dynamicTest("03", () -> test_liquidConsumption(Liquids.oil, maximumLiquidUsage * 1.0f, "Sufficient oil provided")), - dynamicTest("04", () -> test_liquidConsumption(Liquids.oil, maximumLiquidUsage * 2.0f, "Excess oil provided")) + dynamicTest("01", () -> simulateLiquidConsumption(Liquids.oil, 0.0f, "No liquids provided")), + dynamicTest("02", () -> simulateLiquidConsumption(Liquids.oil, maximumLiquidUsage / 4.0f, "Low oil provided")), + dynamicTest("03", () -> simulateLiquidConsumption(Liquids.oil, maximumLiquidUsage * 1.0f, "Sufficient oil provided")), + dynamicTest("04", () -> simulateLiquidConsumption(Liquids.oil, maximumLiquidUsage * 2.0f, "Excess oil provided")) // Note: The generator will decline any other liquid since it's not flammable }; } - void test_liquidConsumption(Liquid liquid, float availableLiquidAmount, String parameterDescription){ + void simulateLiquidConsumption(Liquid liquid, float availableLiquidAmount, String parameterDescription){ final float baseEfficiency = fakeLiquidPowerMultiplier * liquid.flammability; final float expectedEfficiency = Math.min(1.0f, availableLiquidAmount / maximumLiquidUsage) * baseEfficiency; final float expectedConsumptionPerTick = Math.min(maximumLiquidUsage, availableLiquidAmount); @@ -84,19 +84,19 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ /** Tests the consumption and efficiency when being supplied with items. */ @TestFactory - DynamicTest[] testItemConsumption(){ + DynamicTest[] generatorWorksProperlyWithItemInput(){ return new DynamicTest[]{ - dynamicTest("01", () -> test_itemConsumption(Items.coal, 0, "No items provided")), - dynamicTest("02", () -> test_itemConsumption(Items.coal, 1, "Sufficient coal provided")), - dynamicTest("03", () -> test_itemConsumption(Items.coal, 10, "Excess coal provided")), - dynamicTest("04", () -> test_itemConsumption(Items.blastCompound, 1, "Blast compound provided")), - //dynamicTest("03", () -> test_itemConsumption(Items.plastanium, 1, "Plastanium provided")), // Not accepted by generator due to low flammability - dynamicTest("05", () -> test_itemConsumption(Items.biomatter, 1, "Biomatter provided")), - dynamicTest("06", () -> test_itemConsumption(Items.pyratite, 1, "Pyratite provided")) + dynamicTest("01", () -> simulateItemConsumption(Items.coal, 0, "No items provided")), + dynamicTest("02", () -> simulateItemConsumption(Items.coal, 1, "Sufficient coal provided")), + dynamicTest("03", () -> simulateItemConsumption(Items.coal, 10, "Excess coal provided")), + dynamicTest("04", () -> simulateItemConsumption(Items.blastCompound, 1, "Blast compound provided")), + //dynamicTest("03", () -> simulateItemConsumption(Items.plastanium, 1, "Plastanium provided")), // Not accepted by generator due to low flammability + dynamicTest("05", () -> simulateItemConsumption(Items.biomatter, 1, "Biomatter provided")), + dynamicTest("06", () -> simulateItemConsumption(Items.pyratite, 1, "Pyratite provided")) }; } - void test_itemConsumption(Item item, int amount, String parameterDescription){ + void simulateItemConsumption(Item item, int amount, String parameterDescription){ final float expectedEfficiency = Math.min(1.0f, amount > 0 ? item.flammability : 0f); final float expectedRemainingItemAmount = Math.max(0, amount - 1); assertTrue(generator.acceptItem(item, tile, null), parameterDescription + ": Items which will be declined by the generator don't need to be tested - The code won't be called for those cases."); @@ -122,7 +122,7 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ /** Makes sure the efficiency stays equal during the item duration. */ @Test - void test_efficiencyConstantDuringItemDuration(){ + void efficiencyRemainsConstantWithinItemDuration(){ // Burn a single coal and test for the duration entity.items.add(Items.coal, 1); diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index 25a85c58d2..5c1bea7d0c 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -33,21 +33,21 @@ public class PowerTests extends PowerTestFixture{ * Assumption: When the consumer requests zero power, satisfaction does not change. Default is 0.0f. */ @TestFactory - DynamicTest[] testDirectConsumption(){ + DynamicTest[] directConsumerSatisfactionIsAsExpected(){ return new DynamicTest[]{ // Note: Unfortunately, the display names are not yet output through gradle. See https://github.com/gradle/gradle/issues/5975 // That's why we inject the description into the test method for now. // Additional Note: If you don't see any labels in front of the values supplied as function parameters, use a better IDE like IntelliJ IDEA. - dynamicTest("01", () -> test_directConsumptionCalculation(0.0f, 1.0f, 0.0f, "0.0 produced, 1.0 consumed (no power available)")), - dynamicTest("02", () -> test_directConsumptionCalculation(0.0f, 0.0f, 0.0f, "0.0 produced, 0.0 consumed (no power anywhere)")), - dynamicTest("03", () -> test_directConsumptionCalculation(1.0f, 0.0f, 0.0f, "1.0 produced, 0.0 consumed (no power requested)")), - dynamicTest("04", () -> test_directConsumptionCalculation(1.0f, 1.0f, 1.0f, "1.0 produced, 1.0 consumed (stable consumption)")), - dynamicTest("05", () -> test_directConsumptionCalculation(0.5f, 1.0f, 0.5f, "0.5 produced, 1.0 consumed (power shortage)")), - dynamicTest("06", () -> test_directConsumptionCalculation(1.0f, 0.5f, 1.0f, "1.0 produced, 0.5 consumed (power excess)")), - dynamicTest("07", () -> test_directConsumptionCalculation(0.09f, 0.09f - MathUtils.FLOAT_ROUNDING_ERROR / 10.0f, 1.0f, "floating point inaccuracy (stable consumption)")) + dynamicTest("01", () -> simulateDirectConsumption(0.0f, 1.0f, 0.0f, "0.0 produced, 1.0 consumed (no power available)")), + dynamicTest("02", () -> simulateDirectConsumption(0.0f, 0.0f, 0.0f, "0.0 produced, 0.0 consumed (no power anywhere)")), + dynamicTest("03", () -> simulateDirectConsumption(1.0f, 0.0f, 0.0f, "1.0 produced, 0.0 consumed (no power requested)")), + dynamicTest("04", () -> simulateDirectConsumption(1.0f, 1.0f, 1.0f, "1.0 produced, 1.0 consumed (stable consumption)")), + dynamicTest("05", () -> simulateDirectConsumption(0.5f, 1.0f, 0.5f, "0.5 produced, 1.0 consumed (power shortage)")), + dynamicTest("06", () -> simulateDirectConsumption(1.0f, 0.5f, 1.0f, "1.0 produced, 0.5 consumed (power excess)")), + dynamicTest("07", () -> simulateDirectConsumption(0.09f, 0.09f - MathUtils.FLOAT_ROUNDING_ERROR / 10.0f, 1.0f, "floating point inaccuracy (stable consumption)")) }; } - void test_directConsumptionCalculation(float producedPower, float requiredPower, float expectedSatisfaction, String parameterDescription){ + void simulateDirectConsumption(float producedPower, float requiredPower, float expectedSatisfaction, String parameterDescription){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); producerTile.entity().productionEfficiency = 1.0f; Tile directConsumerTile = createFakeTile(0, 1, createFakeDirectConsumer(requiredPower, 0.6f)); @@ -66,28 +66,28 @@ public class PowerTests extends PowerTestFixture{ /** Tests the satisfaction of a single buffered consumer after a single update of the power graph which contains a single producer. */ @TestFactory - DynamicTest[] testBufferedConsumption(){ + 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", () -> test_bufferedConsumptionCalculation(0.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power anywhere")), - dynamicTest("02", () -> test_bufferedConsumptionCalculation(0.0f, 1.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power provided")), - dynamicTest("03", () -> test_bufferedConsumptionCalculation(1.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power requested")), - dynamicTest("04", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 1.0f, 0.0f, 0.5f, "Empty Buffer, Stable Power, One tick to fill")), - dynamicTest("05", () -> test_bufferedConsumptionCalculation(2.0f, 1.0f, 2.0f, 0.0f, 1.0f, "Empty Buffer, Stable Power, One delta to fill")), - dynamicTest("06", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.1f, 0.0f, 0.05f, "Empty Buffer, Stable Power, multiple ticks to fill")), - dynamicTest("07", () -> test_bufferedConsumptionCalculation(1.2f, 0.5f, 1.0f, 0.0f, 1.0f, "Empty Buffer, Power excess, one delta to fill")), - dynamicTest("08", () -> test_bufferedConsumptionCalculation(1.0f, 0.5f, 0.1f, 0.0f, 0.1f, "Empty Buffer, Power excess, multiple ticks to fill")), - dynamicTest("09", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 2.0f, 0.0f, 0.5f, "Empty Buffer, Power shortage, one delta to fill")), - dynamicTest("10", () -> test_bufferedConsumptionCalculation(0.5f, 1.0f, 0.1f, 0.0f, 0.05f, "Empty Buffer, Power shortage, multiple ticks to fill")), - dynamicTest("11", () -> test_bufferedConsumptionCalculation(0.0f, 1.0f, 0.1f, 0.5f, 0.5f, "Unchanged buffer with no power produced")), - dynamicTest("12", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.1f, 1.0f, 1.0f, "Unchanged buffer when already full")), - dynamicTest("13", () -> test_bufferedConsumptionCalculation(0.2f, 1.0f, 0.5f, 0.5f, 0.6f, "Half buffer, power shortage")), - dynamicTest("14", () -> test_bufferedConsumptionCalculation(1.0f, 1.0f, 0.5f, 0.9f, 1.0f, "Buffer does not get exceeded")), - dynamicTest("15", () -> test_bufferedConsumptionCalculation(2.0f, 1.0f, 1.0f, 0.5f, 1.0f, "Half buffer, filled with excess")) + 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 test_bufferedConsumptionCalculation(float producedPower, float maxBuffer, float powerConsumedPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){ + void simulateBufferedConsumption(float producedPower, float maxBuffer, float powerConsumedPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); producerTile.entity().productionEfficiency = 1.0f; Tile bufferedConsumerTile = createFakeTile(0, 1, createFakeBufferedConsumer(maxBuffer, maxBuffer > 0.0f ? maxBuffer/powerConsumedPerTick : 1.0f)); @@ -109,21 +109,21 @@ public class PowerTests extends PowerTestFixture{ * The used battery is created with a maximum capacity of 100 and receives ten power per tick. */ @TestFactory - DynamicTest[] testDirectConsumptionWithBattery(){ + DynamicTest[] batteryCapacityIsAsExpected(){ return new DynamicTest[]{ // Note: expectedBatteryCapacity is currently adjusted to a delta of 0.5! (FakeThreadHandler sets it to that) - dynamicTest("01", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 0.0f, 5.0f, 0.0f, "Empty battery, no consumer")), - dynamicTest("02", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 94.999f, 99.999f, 0.0f, "Battery almost full after update, no consumer")), - dynamicTest("03", () -> test_directConsumptionWithBattery(10.0f, 0.0f, 100.0f, 100.0f, 0.0f, "Full battery, no consumer")), - dynamicTest("04", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, "No producer, no consumer, empty battery")), - dynamicTest("05", () -> test_directConsumptionWithBattery(0.0f, 0.0f, 100.0f, 100.0f, 0.0f, "No producer, no consumer, full battery")), - dynamicTest("06", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 0.0f, 0.0f, 0.0f, "No producer, empty battery")), - dynamicTest("07", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 100.0f, 95.0f, 1.0f, "No producer, full battery")), - dynamicTest("08", () -> test_directConsumptionWithBattery(0.0f, 10.0f, 2.5f, 0.0f, 0.5f, "No producer, low battery")), - dynamicTest("09", () -> test_directConsumptionWithBattery(5.0f, 10.0f, 5.0f, 0.0f, 1.0f, "Producer + Battery = Consumed")), + dynamicTest("01", () -> simulateDirectConsumptionWithBattery(10.0f, 0.0f, 0.0f, 5.0f, 0.0f, "Empty battery, no consumer")), + dynamicTest("02", () -> simulateDirectConsumptionWithBattery(10.0f, 0.0f, 94.999f, 99.999f, 0.0f, "Battery almost full after update, no consumer")), + dynamicTest("03", () -> simulateDirectConsumptionWithBattery(10.0f, 0.0f, 100.0f, 100.0f, 0.0f, "Full battery, no consumer")), + dynamicTest("04", () -> simulateDirectConsumptionWithBattery(0.0f, 0.0f, 0.0f, 0.0f, 0.0f, "No producer, no consumer, empty battery")), + dynamicTest("05", () -> simulateDirectConsumptionWithBattery(0.0f, 0.0f, 100.0f, 100.0f, 0.0f, "No producer, no consumer, full battery")), + dynamicTest("06", () -> simulateDirectConsumptionWithBattery(0.0f, 10.0f, 0.0f, 0.0f, 0.0f, "No producer, empty battery")), + dynamicTest("07", () -> simulateDirectConsumptionWithBattery(0.0f, 10.0f, 100.0f, 95.0f, 1.0f, "No producer, full battery")), + dynamicTest("08", () -> simulateDirectConsumptionWithBattery(0.0f, 10.0f, 2.5f, 0.0f, 0.5f, "No producer, low battery")), + dynamicTest("09", () -> simulateDirectConsumptionWithBattery(5.0f, 10.0f, 5.0f, 0.0f, 1.0f, "Producer + Battery = Consumed")), }; } - void test_directConsumptionWithBattery(float producedPower, float requestedPower, float initialBatteryCapacity, float expectedBatteryCapacity, float expectedSatisfaction, String parameterDescription){ + void simulateDirectConsumptionWithBattery(float producedPower, float requestedPower, float initialBatteryCapacity, float expectedBatteryCapacity, float expectedSatisfaction, String parameterDescription){ PowerGraph powerGraph = new PowerGraph(); if(producedPower > 0.0f){ @@ -151,7 +151,7 @@ public class PowerTests extends PowerTestFixture{ /** Makes sure a direct consumer stops working after power production is set to zero. */ @Test - void testDirectConsumptionStopsWithNoPower(){ + void directConsumptionStopsWithNoPower(){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(10.0f)); producerTile.entity().productionEfficiency = 1.0f; Tile consumerTile = createFakeTile(0, 1, createFakeDirectConsumer(5.0f, 0.6f)); From 12ccc10e83fedde8398bb662cb6c96df8873eed2 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 28 Nov 2018 23:56:11 +0100 Subject: [PATCH 27/67] Restored SolarGenerator and adapted to new power system. --- .../mindustry/content/blocks/PowerBlocks.java | 25 +++++------------ .../maps/generation/FortressGenerator.java | 4 +-- .../world/blocks/power/SolarGenerator.java | 27 +++++++++++++++++++ 3 files changed, 35 insertions(+), 21 deletions(-) create mode 100644 core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index cf59a636d1..c2f2e24596 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -42,26 +42,13 @@ public class PowerBlocks extends BlockList implements ContentList{ itemDuration = 220f; }}; - // TODO: Maybe reintroduce a class for the initial production efficiency - solarPanel = new PowerGenerator("solar-panel"){ - { - powerProduction = 0.0045f; - } - @Override - public void update(Tile tile){ - tile.entity().productionEfficiency = 1.0f; - } - }; + solarPanel = new SolarGenerator("solar-panel"){{ + powerProduction = 0.0045f; + }}; - largeSolarPanel = new PowerGenerator("solar-panel-large"){ - { - powerProduction = 0.055f; - } - @Override - public void update(Tile tile){ - tile.entity().productionEfficiency = 1.0f; - } - }; + largeSolarPanel = new PowerGenerator("solar-panel-large"){{ + powerProduction = 0.055f; + }}; thoriumReactor = new NuclearReactor("thorium-reactor"){{ size = 3; diff --git a/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java b/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java index 7cde4bc33c..f2ea2cb06a 100644 --- a/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java +++ b/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java @@ -25,6 +25,7 @@ import io.anuke.mindustry.world.blocks.defense.turrets.PowerTurret; import io.anuke.mindustry.world.blocks.defense.turrets.Turret; import io.anuke.mindustry.world.blocks.power.NuclearReactor; import io.anuke.mindustry.world.blocks.power.PowerGenerator; +import io.anuke.mindustry.world.blocks.power.SolarGenerator; import io.anuke.mindustry.world.blocks.storage.CoreBlock; import io.anuke.mindustry.world.blocks.storage.StorageBlock; import io.anuke.mindustry.world.blocks.units.UnitFactory; @@ -114,8 +115,7 @@ public class FortressGenerator{ seeder.get(PowerBlocks.solarPanel, tile -> tile.block() == PowerBlocks.largeSolarPanel && gen.random.chance(0.3)), //coal gens - // TODO Verify - This used to be solar panel - seeder.get(PowerBlocks.combustionGenerator, tile -> tile.block() instanceof PowerGenerator && gen.random.chance(0.2)), + seeder.get(PowerBlocks.combustionGenerator, tile -> tile.block() instanceof SolarGenerator && gen.random.chance(0.2)), //water extractors seeder.get(ProductionBlocks.waterExtractor, tile -> tile.block() instanceof NuclearReactor && gen.random.chance(0.5)), diff --git a/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java new file mode 100644 index 0000000000..ebbc88b35f --- /dev/null +++ b/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java @@ -0,0 +1,27 @@ +package io.anuke.mindustry.world.blocks.power; + +import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.world.BarType; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.meta.BlockBar; +import io.anuke.mindustry.world.meta.BlockStat; +import io.anuke.mindustry.world.meta.StatUnit; +import io.anuke.ucore.core.Timers; +import io.anuke.ucore.util.EnumSet; + +public class SolarGenerator extends PowerGenerator{ + + public SolarGenerator(String name){ + super(name); + // Remove the BlockFlag.producer flag to make this a lower priority target than other generators. + flags = EnumSet.of(); + } + + @Override + public TileEntity newEntity(){ + return new PowerGenerator.GeneratorEntity(){{ + productionEfficiency = 1.0f; + }}; + } + +} From 4cb72c19982198b1f540948e2e30741a702eff7a Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Thu, 29 Nov 2018 00:38:53 +0100 Subject: [PATCH 28/67] Resolved some of the power-related TODOs --- .../mindustry/content/blocks/DebugBlocks.java | 5 +-- .../content/blocks/LiquidBlocks.java | 4 -- .../world/blocks/power/FusionReactor.java | 44 ++++++++++--------- .../world/blocks/power/PowerGenerator.java | 14 ++++++ 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java index a2fff36077..f1655fb23d 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DebugBlocks.java @@ -40,7 +40,6 @@ public class DebugBlocks extends BlockList implements ContentList{ public void load(){ powerVoid = new PowerBlock("powervoid"){ { - // TODO Adapt to new power system if necessary consumes.powerDirect(Float.MAX_VALUE); shadow = "shadow-round-1"; } @@ -54,8 +53,7 @@ public class DebugBlocks extends BlockList implements ContentList{ @Override public void init(){ super.init(); - // TODO Adapt to new power system if necessary - //stats.remove(BlockStat.powerCapacity); + stats.remove(BlockStat.powerUse); } }; @@ -67,7 +65,6 @@ public class DebugBlocks extends BlockList implements ContentList{ shadow = "shadow-round-1"; } - // TODO Adapt to new power system if necessary @Override public float getPowerProduction(Tile tile){ return 10000f; diff --git a/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java b/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java index 1e3b9933fb..8be6774a72 100644 --- a/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java @@ -22,8 +22,6 @@ public class LiquidBlocks extends BlockList implements ContentList{ pumpAmount = 0.2f; consumes.powerDirect(0.015f); liquidCapacity = 30f; - // TODO Verify: No longer buffered - consumes.powerDirect(20f / 60f); hasPower = true; size = 2; tier = 1; @@ -35,8 +33,6 @@ public class LiquidBlocks extends BlockList implements ContentList{ consumes.powerDirect(0.03f); liquidCapacity = 40f; hasPower = true; - // TODO Verify: No longer buffered - consumes.powerDirect(20f / 60f); size = 2; tier = 2; }}; diff --git a/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java b/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java index 632a291b3e..0194cfb81d 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java @@ -4,17 +4,17 @@ import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.g2d.TextureRegion; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.production.GenericCrafter.GenericCrafterEntity; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.StatUnit; import io.anuke.ucore.core.Graphics; import io.anuke.ucore.core.Timers; import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.util.Mathf; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + public class FusionReactor extends PowerGenerator{ protected int plasmas = 4; - protected float maxPowerProduced = 2f; protected float warmupSpeed = 0.001f; protected Color plasma1 = Color.valueOf("ffd06b"), plasma2 = Color.valueOf("ff361b"); @@ -24,35 +24,25 @@ public class FusionReactor extends PowerGenerator{ super(name); hasPower = true; hasLiquids = true; - // TODO Adapt to new power system - //powerCapacity = 100f; + powerProduction = 2.0f; liquidCapacity = 30f; hasItems = true; } - @Override - public void setStats(){ - super.setStats(); - - // TODO Verify for new power system - stats.remove(BlockStat.basePowerGeneration); - stats.add(BlockStat.basePowerGeneration, maxPowerProduced * 60f, StatUnit.powerSecond); - } - @Override public void update(Tile tile){ FusionReactorEntity entity = tile.entity(); + float increaseOrDecrease = 1.0f; if(entity.cons.valid()){ entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, warmupSpeed); }else{ entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.01f); + increaseOrDecrease = -1.0f; } - // TODO Adapt to new power system - //float powerAdded = Math.min(powerCapacity - entity.power.amount, maxPowerProduced * Mathf.pow(entity.warmup, 4f) * Timers.delta()); - //entity.power.amount += powerAdded; - entity.totalProgress += entity.warmup * Timers.delta(); + float efficiencyAdded = Mathf.pow(entity.warmup, 4f) * Timers.delta(); + entity.productionEfficiency = Mathf.clamp(entity.productionEfficiency + efficiencyAdded * increaseOrDecrease); tile.entity.power.graph.update(); } @@ -100,7 +90,7 @@ public class FusionReactor extends PowerGenerator{ Draw.rect(name + "-top", tile.drawx(), tile.drawy()); - Draw.color(ind1, ind2, entity.warmup + Mathf.absin(entity.totalProgress, 3f, entity.warmup * 0.5f)); + Draw.color(ind1, ind2, entity.warmup + Mathf.absin(entity.productionEfficiency, 3f, entity.warmup * 0.5f)); Draw.rect(name + "-light", tile.drawx(), tile.drawy()); Draw.color(); @@ -127,7 +117,19 @@ public class FusionReactor extends PowerGenerator{ //TODO catastrophic failure } - public static class FusionReactorEntity extends GenericCrafterEntity{ + public static class FusionReactorEntity extends GeneratorEntity{ + public float warmup; + @Override + public void write(DataOutput stream) throws IOException{ + super.write(stream); + stream.writeFloat(warmup); + } + + @Override + public void read(DataInput stream) throws IOException{ + super.read(stream); + warmup = stream.readFloat(); + } } } diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index 8a3e59dd9d..49da784b2a 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -10,6 +10,10 @@ import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.meta.BlockFlag; import io.anuke.mindustry.world.meta.BlockStat; +import java.io.DataInput; +import java.io.DataOutput; +import java.io.IOException; + public class PowerGenerator extends PowerDistributor{ /** The amount of power produced per tick. */ protected float powerProduction; @@ -58,5 +62,15 @@ public class PowerGenerator extends PowerDistributor{ public static class GeneratorEntity extends TileEntity{ public float generateTime; public float productionEfficiency = 0.0f; + + @Override + public void write(DataOutput stream) throws IOException{ + stream.writeFloat(productionEfficiency); + } + + @Override + public void read(DataInput stream) throws IOException{ + productionEfficiency = stream.readFloat(); + } } } From 1d57568322cbf7d1bb0a84215949b50ffd907f4d Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Thu, 29 Nov 2018 23:35:56 +0100 Subject: [PATCH 29/67] All power distributors now update power graph Replaced manual power graph update by base implementation call so future changes in base class get applied to all subclasses automatically. Added the call where missing. --- .../io/anuke/mindustry/world/blocks/power/FusionReactor.java | 2 +- .../io/anuke/mindustry/world/blocks/power/ItemGenerator.java | 2 ++ .../anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java | 2 ++ .../io/anuke/mindustry/world/blocks/power/LiquidGenerator.java | 2 +- .../io/anuke/mindustry/world/blocks/power/NuclearReactor.java | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java b/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java index 0194cfb81d..047825375c 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/FusionReactor.java @@ -44,7 +44,7 @@ public class FusionReactor extends PowerGenerator{ float efficiencyAdded = Mathf.pow(entity.warmup, 4f) * Timers.delta(); entity.productionEfficiency = Mathf.clamp(entity.productionEfficiency + efficiencyAdded * increaseOrDecrease); - tile.entity.power.graph.update(); + super.update(tile); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java index a8d8da9699..3a16d3df04 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java @@ -93,6 +93,8 @@ public abstract class ItemGenerator extends PowerGenerator{ Effects.effect(explodeEffect, tile.worldx() + Mathf.range(size * tilesize / 2f), tile.worldy() + Mathf.range(size * tilesize / 2f)); } } + + super.update(tile); } protected abstract float getItemEfficiency(Item item); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index c41f8d1c39..974ade4db5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -89,6 +89,8 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ entity.productionEfficiency = 0.0f; } } + + super.update(tile); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java index 7314c56a0d..fd909bae76 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java @@ -66,7 +66,7 @@ public abstract class LiquidGenerator extends PowerGenerator{ } } - tile.entity.power.graph.update(); + super.update(tile); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java b/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java index 2edeaa6f09..f2abdbc304 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java @@ -128,7 +128,7 @@ public class NuclearReactor extends PowerGenerator{ if(entity.heat >= 0.999f){ entity.kill(); }else{ - tile.entity.power.graph.update(); + super.update(tile); } } From bcb9cfd2fe2808e6907644f06bcab47710ddf232 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Fri, 30 Nov 2018 00:03:53 +0100 Subject: [PATCH 30/67] Synchronized Item/Liquid generator code --- .../world/blocks/power/ItemGenerator.java | 7 +++++ .../blocks/power/ItemLiquidGenerator.java | 29 ++++--------------- .../world/blocks/power/LiquidGenerator.java | 19 +++++++----- .../mindustry/world/blocks/units/MechPad.java | 7 ----- .../java/power/ItemLiquidGeneratorTests.java | 1 + 5 files changed, 24 insertions(+), 39 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java index 3a16d3df04..b18092c111 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java @@ -76,6 +76,11 @@ public abstract class ItemGenerator extends PowerGenerator{ public void update(Tile tile){ ItemGeneratorEntity entity = tile.entity(); + if(!entity.cons.valid()){ + entity.productionEfficiency = 0.0f; + return; + } + if(entity.generateTime <= 0f && entity.items.total() > 0){ Effects.effect(generateEffect, tile.worldx() + Mathf.range(3f), tile.worldy() + Mathf.range(3f)); Item item = entity.items.take(); @@ -92,6 +97,8 @@ public abstract class ItemGenerator extends PowerGenerator{ entity.damage(Mathf.random(8f)); Effects.effect(explodeEffect, tile.worldx() + Mathf.range(size * tilesize / 2f), tile.worldy() + Mathf.range(size * tilesize / 2f)); } + }else{ + entity.productionEfficiency = 0.0f; } super.update(tile); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index 974ade4db5..c8af36a3ff 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -35,8 +35,6 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ public void update(Tile tile){ ItemGeneratorEntity entity = tile.entity(); - entity.power.graph.update(); - Liquid liquid = null; for(Liquid other : content.liquids()){ if(entity.liquids.get(other) >= 0.001f && getLiquidEfficiency(other) >= minLiquidEfficiency){ @@ -67,30 +65,13 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); } + + // Update the graph manually instead of letting the base class do it since that would consume items + entity.power.graph.update(); }else{ - - if(entity.generateTime <= 0f && entity.items.total() > 0){ - Effects.effect(generateEffect, tile.worldx() + Mathf.range(3f), tile.worldy() + Mathf.range(3f)); - Item item = entity.items.take(); - entity.productionEfficiency = getItemEfficiency(item); - entity.explosiveness = item.explosiveness; - entity.generateTime = 1f; - } - - if(entity.generateTime > 0f){ - entity.generateTime -= 1f / itemDuration * entity.delta(); - entity.generateTime = Mathf.clamp(entity.generateTime); - - if(Mathf.chance(entity.delta() * 0.06 * Mathf.clamp(entity.explosiveness - 0.25f))){ - entity.damage(Mathf.random(8f)); - Effects.effect(explodeEffect, tile.worldx() + Mathf.range(size * tilesize / 2f), tile.worldy() + Mathf.range(size * tilesize / 2f)); - } - }else{ - entity.productionEfficiency = 0.0f; - } + // No liquids accepted, act like a default ItemGenerator + super.update(tile); } - - super.update(tile); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java index fd909bae76..67701ec58d 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java @@ -48,18 +48,21 @@ public abstract class LiquidGenerator extends PowerGenerator{ @Override public void update(Tile tile){ - TileEntity entity = tile.entity(); + ItemGeneratorEntity entity = tile.entity(); + + // Note: Do not use this delta when calculating the amount of power or the power efficiency, but use it for resource consumption if necessary. + // Power amount is delta'd by PowerGraph class already. + float calculationDelta = entity.delta(); - // TODO Code duplication with ItemLiquidGenerator if(entity.liquids.get(entity.liquids.current()) >= 0.001f){ - //float powerPerLiquid = getEfficiency(entity.liquids.current()) * this.powerPerLiquid; - float used = Math.min(entity.liquids.currentAmount(), maxLiquidGenerate * entity.delta()); - // TODO Adapt to new power system - //used = Math.min(used, (powerCapacity - entity.power.amount) / powerPerLiquid); + float baseLiquidEfficiency = getEfficiency(entity.liquids.current()) * this.liquidPowerMultiplier; + float maximumPossible = maxLiquidGenerate * calculationDelta; + float used = Math.min(entity.liquids.currentAmount() * calculationDelta, maximumPossible); entity.liquids.remove(entity.liquids.current(), used); - // TODO Adapt to new power system - //entity.power.amount += used * powerPerLiquid; + + // Note: 1 Item with 100% Flammability = 100% efficiency. This means 100% is not max but rather a reference point for this generator. + entity.productionEfficiency = baseLiquidEfficiency * used / maximumPossible; if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); 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 a1fa070367..6eafeb2a1d 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/MechPad.java @@ -52,13 +52,6 @@ public class MechPad extends Block{ super.init(); } - @Override - public void setStats(){ - super.setStats(); - // TODO Verify for new power system - //stats.remove(BlockStat.powerUse); - } - @Override public boolean shouldConsume(Tile tile){ return false; diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java index 9511059fda..aae193543d 100644 --- a/tests/src/test/java/power/ItemLiquidGeneratorTests.java +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -22,6 +22,7 @@ import static org.junit.jupiter.api.DynamicTest.dynamicTest; * Additionally, each PowerGraph::update() call will have its own thread frame, i.e. the method will never be called twice within the same frame. * Both of these constraints are handled by FakeThreadHandler within PowerTestFixture. * Any power amount (produced, consumed, buffered) should be affected by FakeThreadHandler.fakeDelta but satisfaction should not! + * TODO Find a way to reuse these tests for LiquidGenerator and ItemGenerator */ public class ItemLiquidGeneratorTests extends PowerTestFixture{ From 28f73d0a76f70fa27824092e8b4ab4818905c12e Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Fri, 30 Nov 2018 18:37:51 +0100 Subject: [PATCH 31/67] Merged ItemGenerator, LiquidGenerator and ItemLiquidGenerator into a single class. Unit tests now test the ItemsOnly, LiquidsOnly and LiquidsAndItems cases --- .../world/blocks/power/BurnerGenerator.java | 2 +- .../world/blocks/power/DecayGenerator.java | 5 +- .../world/blocks/power/ItemGenerator.java | 117 ------------- .../blocks/power/ItemLiquidGenerator.java | 165 ++++++++++++++---- .../world/blocks/power/LiquidGenerator.java | 91 ---------- .../blocks/power/LiquidHeatGenerator.java | 7 +- .../java/power/ItemLiquidGeneratorTests.java | 133 ++++++++------ 7 files changed, 222 insertions(+), 298 deletions(-) delete mode 100644 core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java delete mode 100644 core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java diff --git a/core/src/io/anuke/mindustry/world/blocks/power/BurnerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/BurnerGenerator.java index 91051e8a85..0441f2a876 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/BurnerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/BurnerGenerator.java @@ -6,7 +6,7 @@ import io.anuke.mindustry.type.Liquid; public class BurnerGenerator extends ItemLiquidGenerator{ public BurnerGenerator(String name){ - super(name); + super(InputType.LiquidsAndItems, name); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/power/DecayGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/DecayGenerator.java index 225efd98c5..678da93587 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/DecayGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/DecayGenerator.java @@ -1,11 +1,12 @@ package io.anuke.mindustry.world.blocks.power; import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.Liquid; -public class DecayGenerator extends ItemGenerator{ +public class DecayGenerator extends ItemLiquidGenerator{ public DecayGenerator(String name){ - super(name); + super(InputType.ItemsOnly, name); hasItems = true; hasLiquids = false; } diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java deleted file mode 100644 index b18092c111..0000000000 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemGenerator.java +++ /dev/null @@ -1,117 +0,0 @@ -package io.anuke.mindustry.world.blocks.power; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import io.anuke.mindustry.content.fx.BlockFx; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.type.Item; -import io.anuke.mindustry.world.BarType; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.consumers.ConsumeItemFilter; -import io.anuke.mindustry.world.meta.BlockBar; -import io.anuke.mindustry.world.meta.BlockStat; -import io.anuke.mindustry.world.meta.StatUnit; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Effects.Effect; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.util.Mathf; - -import java.io.DataInput; -import java.io.DataOutput; -import java.io.IOException; - -import static io.anuke.mindustry.Vars.tilesize; - -public abstract class ItemGenerator extends PowerGenerator{ - protected float minItemEfficiency = 0.2f; - protected float itemDuration = 70f; - protected Effect generateEffect = BlockFx.generatespark, explodeEffect = - BlockFx.generatespark; - protected Color heatColor = Color.valueOf("ff9b59"); - protected TextureRegion topRegion; - - public ItemGenerator(String name){ - super(name); - itemCapacity = 20; - hasItems = true; - - consumes.add(new ConsumeItemFilter(item -> getItemEfficiency(item) >= minItemEfficiency)).update(false).optional(true); - } - - @Override - public void load(){ - super.load(); - topRegion = Draw.region(name + "-top"); - } - - @Override - public void setBars(){ - super.setBars(); - bars.replace(new BlockBar(BarType.inventory, true, tile -> (float) tile.entity.items.total() / itemCapacity)); - } - - @Override - public void draw(Tile tile){ - super.draw(tile); - - GeneratorEntity entity = tile.entity(); - - if(entity.generateTime > 0){ - Draw.color(heatColor); - float alpha = (entity.items.total() > 0 ? 1f : Mathf.clamp(entity.generateTime)); - alpha = alpha * 0.7f + Mathf.absin(Timers.time(), 12f, 0.3f) * alpha; - Draw.alpha(alpha); - Draw.rect(topRegion, tile.drawx(), tile.drawy()); - Draw.reset(); - } - } - - @Override - public boolean acceptItem(Item item, Tile tile, Tile source){ - return getItemEfficiency(item) >= minItemEfficiency && tile.entity.items.total() < itemCapacity; - } - - @Override - public void update(Tile tile){ - ItemGeneratorEntity entity = tile.entity(); - - if(!entity.cons.valid()){ - entity.productionEfficiency = 0.0f; - return; - } - - if(entity.generateTime <= 0f && entity.items.total() > 0){ - Effects.effect(generateEffect, tile.worldx() + Mathf.range(3f), tile.worldy() + Mathf.range(3f)); - Item item = entity.items.take(); - entity.productionEfficiency = getItemEfficiency(item); - entity.explosiveness = item.explosiveness; - entity.generateTime = 1f; - } - - if(entity.generateTime > 0f){ - entity.generateTime -= Math.min(1f / itemDuration * entity.delta(), entity.generateTime); - - if(Mathf.chance(entity.delta() * 0.06 * Mathf.clamp(entity.explosiveness - 0.25f))){ - //this block is run last so that in the event of a block destruction, no code relies on the block type - entity.damage(Mathf.random(8f)); - Effects.effect(explodeEffect, tile.worldx() + Mathf.range(size * tilesize / 2f), tile.worldy() + Mathf.range(size * tilesize / 2f)); - } - }else{ - entity.productionEfficiency = 0.0f; - } - - super.update(tile); - } - - protected abstract float getItemEfficiency(Item item); - - @Override - public TileEntity newEntity(){ - return new ItemGeneratorEntity(); - } - - public static class ItemGeneratorEntity extends GeneratorEntity{ - public float explosiveness; - } -} diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index c8af36a3ff..7fbc233533 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -1,47 +1,86 @@ package io.anuke.mindustry.world.blocks.power; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import io.anuke.mindustry.content.fx.BlockFx; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Liquid; +import io.anuke.mindustry.world.BarType; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.consumers.ConsumeItemFilter; import io.anuke.mindustry.world.consumers.ConsumeLiquidFilter; +import io.anuke.mindustry.world.meta.BlockBar; import io.anuke.ucore.core.Effects; +import io.anuke.ucore.core.Timers; import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.util.Mathf; import static io.anuke.mindustry.Vars.content; import static io.anuke.mindustry.Vars.tilesize; -public abstract class ItemLiquidGenerator extends ItemGenerator{ +/** + * Power generation block which can use items, liquids or both as input sources for power production. + * Liquids will take priority over items. + */ +public class ItemLiquidGenerator extends PowerGenerator{ + + protected float minItemEfficiency = 0.2f; + /** The time in number of ticks during which a single item will produce power. */ + protected float itemDuration = 70f; + protected float minLiquidEfficiency = 0.2f; protected float liquidPowerMultiplier = 1.3f; // A liquid with 100% flammability will be 30% more efficient than an item with 100% flammability. - /**Maximum liquid used per frame.*/ + /** Maximum liquid used per frame. */ protected float maxLiquidGenerate = 0.4f; - public ItemLiquidGenerator(String name){ - super(name); - hasLiquids = true; - liquidCapacity = 10f; + protected Effects.Effect generateEffect = BlockFx.generatespark; + protected Effects.Effect explodeEffect = BlockFx.generatespark; + protected Color heatColor = Color.valueOf("ff9b59"); + protected TextureRegion topRegion; - consumes.add(new ConsumeLiquidFilter(liquid -> getLiquidEfficiency(liquid) >= minLiquidEfficiency, 0.001f, true)).update(false).optional(true); + public enum InputType{ + ItemsOnly, + LiquidsOnly, + LiquidsAndItems + } + + public ItemLiquidGenerator(InputType inputType, String name){ + super(name); + this.hasItems = inputType != InputType.LiquidsOnly; + this.hasLiquids = inputType != InputType.ItemsOnly; + + if(hasItems){ + itemCapacity = 20; + consumes.add(new ConsumeItemFilter(item -> getItemEfficiency(item) >= minItemEfficiency)).update(false).optional(true); + } + + if(hasLiquids){ + liquidCapacity = 10f; + consumes.add(new ConsumeLiquidFilter(liquid -> getLiquidEfficiency(liquid) >= minLiquidEfficiency, 0.001f, true)).update(false).optional(true); + } } @Override - public void init(){ - super.init(); + public void load(){ + super.load(); + if(hasItems){ + topRegion = Draw.region(name + "-top"); + } } + @Override + public void setBars(){ + super.setBars(); + if(hasItems){ + bars.replace(new BlockBar(BarType.inventory, true, tile -> (float) tile.entity.items.total() / itemCapacity)); + } + } + + @Override public void update(Tile tile){ - ItemGeneratorEntity entity = tile.entity(); - - Liquid liquid = null; - for(Liquid other : content.liquids()){ - if(entity.liquids.get(other) >= 0.001f && getLiquidEfficiency(other) >= minLiquidEfficiency){ - liquid = other; - break; - } - } + ItemLiquidGeneratorEntity entity = tile.entity(); // Note: Do not use this delta when calculating the amount of power or the power efficiency, but use it for resource consumption if necessary. // Power amount is delta'd by PowerGraph class already. @@ -51,8 +90,16 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ entity.productionEfficiency = 0.0f; return; } + + Liquid liquid = null; + for(Liquid other : content.liquids()){ + if(hasLiquids && entity.liquids.get(other) >= 0.001f && getLiquidEfficiency(other) >= minLiquidEfficiency){ + liquid = other; + break; + } + } //liquid takes priority over solids - if(liquid != null && entity.liquids.get(liquid) >= 0.001f){ + if(hasLiquids && liquid != null && entity.liquids.get(liquid) >= 0.001f){ float baseLiquidEfficiency = getLiquidEfficiency(liquid) * this.liquidPowerMultiplier; float maximumPossible = maxLiquidGenerate * calculationDelta; float used = Math.min(entity.liquids.get(liquid) * calculationDelta, maximumPossible); @@ -65,35 +112,85 @@ public abstract class ItemLiquidGenerator extends ItemGenerator{ if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); } + }else if(hasItems){ + // No liquids accepted or none supplied, try using items if accepted + if(entity.generateTime <= 0f && entity.items.total() > 0){ + Effects.effect(generateEffect, tile.worldx() + Mathf.range(3f), tile.worldy() + Mathf.range(3f)); + Item item = entity.items.take(); + entity.productionEfficiency = getItemEfficiency(item); + entity.explosiveness = item.explosiveness; + entity.generateTime = 1f; + } - // Update the graph manually instead of letting the base class do it since that would consume items - entity.power.graph.update(); - }else{ - // No liquids accepted, act like a default ItemGenerator - super.update(tile); + if(entity.generateTime > 0f){ + entity.generateTime -= Math.min(1f / itemDuration * entity.delta(), entity.generateTime); + + if(Mathf.chance(entity.delta() * 0.06 * Mathf.clamp(entity.explosiveness - 0.25f))){ + //this block is run last so that in the event of a block destruction, no code relies on the block type + entity.damage(Mathf.random(8f)); + Effects.effect(explodeEffect, tile.worldx() + Mathf.range(size * tilesize / 2f), tile.worldy() + Mathf.range(size * tilesize / 2f)); + } + }else{ + entity.productionEfficiency = 0.0f; + } } + + super.update(tile); + } + + @Override + public boolean acceptItem(Item item, Tile tile, Tile source){ + return hasItems && getItemEfficiency(item) >= minItemEfficiency && tile.entity.items.total() < itemCapacity; + } + + @Override + public boolean acceptLiquid(Tile tile, Tile source, Liquid liquid, float amount){ + return hasLiquids && getLiquidEfficiency(liquid) >= minLiquidEfficiency && tile.entity.liquids.get(liquid) < liquidCapacity; } @Override public void draw(Tile tile){ super.draw(tile); - TileEntity entity = tile.entity(); + GeneratorEntity entity = tile.entity(); - Draw.color(entity.liquids.current().color); - Draw.alpha(entity.liquids.currentAmount() / liquidCapacity); - drawLiquidCenter(tile); - Draw.color(); - } + if(hasItems){ + if(entity.generateTime > 0){ + Draw.color(heatColor); + float alpha = (entity.items.total() > 0 ? 1f : Mathf.clamp(entity.generateTime)); + alpha = alpha * 0.7f + Mathf.absin(Timers.time(), 12f, 0.3f) * alpha; + Draw.alpha(alpha); + Draw.rect(topRegion, tile.drawx(), tile.drawy()); + Draw.reset(); + } + } - @Override - public boolean acceptLiquid(Tile tile, Tile source, Liquid liquid, float amount){ - return getLiquidEfficiency(liquid) >= minLiquidEfficiency && tile.entity.liquids.get(liquid) < liquidCapacity; + if(hasLiquids){ + Draw.color(entity.liquids.current().color); + Draw.alpha(entity.liquids.currentAmount() / liquidCapacity); + drawLiquidCenter(tile); + Draw.color(); + } } public void drawLiquidCenter(Tile tile){ Draw.rect("blank", tile.drawx(), tile.drawy(), 2, 2); } - protected abstract float getLiquidEfficiency(Liquid liquid); + protected float getItemEfficiency(Item item){ + return 0.0f; + } + + protected float getLiquidEfficiency(Liquid liquid){ + return 0.0f; + } + + @Override + public TileEntity newEntity(){ + return new ItemLiquidGeneratorEntity(); + } + + public static class ItemLiquidGeneratorEntity extends GeneratorEntity{ + public float explosiveness; + } } diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java deleted file mode 100644 index 67701ec58d..0000000000 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidGenerator.java +++ /dev/null @@ -1,91 +0,0 @@ -package io.anuke.mindustry.world.blocks.power; - -import io.anuke.mindustry.content.fx.BlockFx; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.type.Liquid; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.power.ItemGenerator.ItemGeneratorEntity; -import io.anuke.mindustry.world.consumers.ConsumeLiquidFilter; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Effects.Effect; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.util.Mathf; - -public abstract class LiquidGenerator extends PowerGenerator{ - protected float minEfficiency = 0.2f; - protected float liquidPowerMultiplier; - /**Maximum liquid used per frame.*/ - protected float maxLiquidGenerate; - protected Effect generateEffect = BlockFx.generatespark; - - public LiquidGenerator(String name){ - super(name); - liquidCapacity = 30f; - hasLiquids = true; - } - - @Override - public void setStats(){ - consumes.add(new ConsumeLiquidFilter(liquid -> getEfficiency(liquid) >= minEfficiency, maxLiquidGenerate)).update(false); - super.setStats(); - } - - @Override - public void draw(Tile tile){ - super.draw(tile); - - TileEntity entity = tile.entity(); - - Draw.color(entity.liquids.current().color); - Draw.alpha(entity.liquids.total() / liquidCapacity); - drawLiquidCenter(tile); - Draw.color(); - } - - public void drawLiquidCenter(Tile tile){ - Draw.rect("blank", tile.drawx(), tile.drawy(), 2, 2); - } - - @Override - public void update(Tile tile){ - ItemGeneratorEntity entity = tile.entity(); - - // Note: Do not use this delta when calculating the amount of power or the power efficiency, but use it for resource consumption if necessary. - // Power amount is delta'd by PowerGraph class already. - float calculationDelta = entity.delta(); - - if(entity.liquids.get(entity.liquids.current()) >= 0.001f){ - float baseLiquidEfficiency = getEfficiency(entity.liquids.current()) * this.liquidPowerMultiplier; - float maximumPossible = maxLiquidGenerate * calculationDelta; - float used = Math.min(entity.liquids.currentAmount() * calculationDelta, maximumPossible); - - entity.liquids.remove(entity.liquids.current(), used); - - // Note: 1 Item with 100% Flammability = 100% efficiency. This means 100% is not max but rather a reference point for this generator. - entity.productionEfficiency = baseLiquidEfficiency * used / maximumPossible; - - if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ - Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); - } - } - - super.update(tile); - } - - @Override - public boolean acceptLiquid(Tile tile, Tile source, Liquid liquid, float amount){ - return getEfficiency(liquid) >= minEfficiency && super.acceptLiquid(tile, source, liquid, amount); - } - - @Override - public TileEntity newEntity(){ - return new ItemGeneratorEntity(); - } - - /** - * Returns an efficiency value for the specified liquid. - * Greater efficiency means more power generation. - * If a liquid's efficiency is below {@link #minEfficiency}, it is not accepted. - */ - protected abstract float getEfficiency(Liquid liquid); -} diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java index 9129e884e9..c1d95d9ff1 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java @@ -1,13 +1,14 @@ package io.anuke.mindustry.world.blocks.power; +import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.StatUnit; -public class LiquidHeatGenerator extends LiquidGenerator{ +public class LiquidHeatGenerator extends ItemLiquidGenerator{ public LiquidHeatGenerator(String name){ - super(name); + super(InputType.LiquidsOnly, name); } @Override @@ -20,7 +21,7 @@ public class LiquidHeatGenerator extends LiquidGenerator{ } @Override - protected float getEfficiency(Liquid liquid){ + protected float getLiquidEfficiency(Liquid liquid){ return liquid.temperature - 0.5f; } } diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java index aae193543d..8a2fa7ff18 100644 --- a/tests/src/test/java/power/ItemLiquidGeneratorTests.java +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -1,48 +1,55 @@ package power; -import io.anuke.mindustry.Vars; import io.anuke.mindustry.content.Items; import io.anuke.mindustry.content.Liquids; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.power.BurnerGenerator; -import io.anuke.mindustry.world.blocks.power.ItemGenerator; import io.anuke.mindustry.world.blocks.power.ItemLiquidGenerator; import org.junit.jupiter.api.*; +import java.util.ArrayList; + import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.DynamicTest.dynamicTest; /** - * This class tests ItemLiquidGenerators. Currently, testing is only performed on the BurnerGenerator subclass, - * which means only power calculations based on flammability are tested. + * This class tests generators which can process items, liquids or both. * All tests are run with a fixed delta of 0.5 so delta considerations can be tested as well. * Additionally, each PowerGraph::update() call will have its own thread frame, i.e. the method will never be called twice within the same frame. * Both of these constraints are handled by FakeThreadHandler within PowerTestFixture. - * Any power amount (produced, consumed, buffered) should be affected by FakeThreadHandler.fakeDelta but satisfaction should not! - * TODO Find a way to reuse these tests for LiquidGenerator and ItemGenerator + * Any expected power amount (produced, consumed, buffered) should be affected by FakeThreadHandler.fakeDelta but satisfaction should not! */ public class ItemLiquidGeneratorTests extends PowerTestFixture{ private ItemLiquidGenerator generator; private Tile tile; - private ItemGenerator.ItemGeneratorEntity entity; + private ItemLiquidGenerator.ItemLiquidGeneratorEntity entity; private final float fakeLiquidPowerMultiplier = 2.0f; private final float fakeItemDuration = 60f; // 60 ticks private final float maximumLiquidUsage = 0.5f; - @BeforeEach - public void createBurnerGenerator(){ - // Use a burner generator instead of a custom ItemLiquidGenerator subclass since we would implement abstract methods the same way. - generator = new BurnerGenerator("fakegen"){{ - powerProduction = 0.1f; - itemDuration = 60f; - liquidPowerMultiplier = fakeLiquidPowerMultiplier; - itemDuration = fakeItemDuration; - maxLiquidGenerate = maximumLiquidUsage; - }}; + public void createGenerator(ItemLiquidGenerator.InputType inputType){ + generator = new ItemLiquidGenerator(inputType, "fakegen"){ + { + powerProduction = 0.1f; + itemDuration = 60f; + liquidPowerMultiplier = fakeLiquidPowerMultiplier; + itemDuration = fakeItemDuration; + maxLiquidGenerate = maximumLiquidUsage; + } + + @Override + public float getItemEfficiency(Item item){ + return item.flammability; + } + + @Override + public float getLiquidEfficiency(Liquid liquid){ + return liquid.flammability; + } + }; tile = createFakeTile(0, 0, generator); entity = tile.entity(); @@ -51,27 +58,35 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ /** Tests the consumption and efficiency when being supplied with liquids. */ @TestFactory DynamicTest[] generatorWorksProperlyWithLiquidInput(){ - return new DynamicTest[]{ - dynamicTest("01", () -> simulateLiquidConsumption(Liquids.oil, 0.0f, "No liquids provided")), - dynamicTest("02", () -> simulateLiquidConsumption(Liquids.oil, maximumLiquidUsage / 4.0f, "Low oil provided")), - dynamicTest("03", () -> simulateLiquidConsumption(Liquids.oil, maximumLiquidUsage * 1.0f, "Sufficient oil provided")), - dynamicTest("04", () -> simulateLiquidConsumption(Liquids.oil, maximumLiquidUsage * 2.0f, "Excess oil provided")) - // Note: The generator will decline any other liquid since it's not flammable + + // Execute all tests for the case where only liquids are accepted and for the case where liquids and items are accepted (but supply only liquids) + ItemLiquidGenerator.InputType[] inputTypesToBeTested = new ItemLiquidGenerator.InputType[]{ + ItemLiquidGenerator.InputType.LiquidsOnly, + ItemLiquidGenerator.InputType.LiquidsAndItems }; + + ArrayList tests = new ArrayList<>(); + for(ItemLiquidGenerator.InputType inputType : inputTypesToBeTested){ + tests.add(dynamicTest("01", () -> simulateLiquidConsumption(inputType, Liquids.oil, 0.0f, "No liquids provided"))); + tests.add(dynamicTest("02", () -> simulateLiquidConsumption(inputType, Liquids.oil, maximumLiquidUsage / 4.0f, "Low oil provided"))); + tests.add(dynamicTest("03", () -> simulateLiquidConsumption(inputType, Liquids.oil, maximumLiquidUsage * 1.0f, "Sufficient oil provided"))); + tests.add(dynamicTest("04", () -> simulateLiquidConsumption(inputType, Liquids.oil, maximumLiquidUsage * 2.0f, "Excess oil provided"))); + // Note: The generator will decline any other liquid since it's not flammable + } + DynamicTest[] testArray = new DynamicTest[tests.size()]; + testArray = tests.toArray(testArray); + return testArray; } - void simulateLiquidConsumption(Liquid liquid, float availableLiquidAmount, String parameterDescription){ + void simulateLiquidConsumption(ItemLiquidGenerator.InputType inputType, Liquid liquid, float availableLiquidAmount, String parameterDescription){ final float baseEfficiency = fakeLiquidPowerMultiplier * liquid.flammability; final float expectedEfficiency = Math.min(1.0f, availableLiquidAmount / maximumLiquidUsage) * baseEfficiency; final float expectedConsumptionPerTick = Math.min(maximumLiquidUsage, availableLiquidAmount); final float expectedRemainingLiquidAmount = Math.max(0.0f, availableLiquidAmount - expectedConsumptionPerTick * FakeThreadHandler.fakeDelta); - assertTrue(generator.acceptLiquid(tile, null, liquid, availableLiquidAmount), parameterDescription + ": Liquids which will be declined by the generator don't need to be tested - The code won't be called for those cases."); + createGenerator(inputType); + assertTrue(generator.acceptLiquid(tile, null, liquid, availableLiquidAmount), inputType + " | " + parameterDescription + ": Liquids which will be declined by the generator don't need to be tested - The code won't be called for those cases."); - // Reset liquids since BeforeEach will not be called between dynamic tests - for(Liquid tmpLiquid : Vars.content.liquids()){ - entity.liquids.reset(tmpLiquid, 0.0f); - } entity.liquids.add(liquid, availableLiquidAmount); entity.cons.update(tile.entity); assertTrue(entity.cons.valid()); @@ -79,34 +94,41 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ // Perform an update on the generator once - This should use up any resource up to the maximum liquid usage generator.update(tile); - assertEquals(expectedRemainingLiquidAmount, entity.liquids.get(liquid), parameterDescription + ": Remaining liquid amount mismatch."); - assertEquals(expectedEfficiency, entity.productionEfficiency, parameterDescription + ": Efficiency mismatch."); + assertEquals(expectedRemainingLiquidAmount, entity.liquids.get(liquid), inputType + " | " + parameterDescription + ": Remaining liquid amount mismatch."); + assertEquals(expectedEfficiency, entity.productionEfficiency, inputType + " | " + parameterDescription + ": Efficiency mismatch."); } /** Tests the consumption and efficiency when being supplied with items. */ @TestFactory DynamicTest[] generatorWorksProperlyWithItemInput(){ - return new DynamicTest[]{ - dynamicTest("01", () -> simulateItemConsumption(Items.coal, 0, "No items provided")), - dynamicTest("02", () -> simulateItemConsumption(Items.coal, 1, "Sufficient coal provided")), - dynamicTest("03", () -> simulateItemConsumption(Items.coal, 10, "Excess coal provided")), - dynamicTest("04", () -> simulateItemConsumption(Items.blastCompound, 1, "Blast compound provided")), - //dynamicTest("03", () -> simulateItemConsumption(Items.plastanium, 1, "Plastanium provided")), // Not accepted by generator due to low flammability - dynamicTest("05", () -> simulateItemConsumption(Items.biomatter, 1, "Biomatter provided")), - dynamicTest("06", () -> simulateItemConsumption(Items.pyratite, 1, "Pyratite provided")) + + // Execute all tests for the case where only items are accepted and for the case where liquids and items are accepted (but supply only items) + ItemLiquidGenerator.InputType[] inputTypesToBeTested = new ItemLiquidGenerator.InputType[]{ + ItemLiquidGenerator.InputType.ItemsOnly, + ItemLiquidGenerator.InputType.LiquidsAndItems }; + + ArrayList tests = new ArrayList<>(); + for(ItemLiquidGenerator.InputType inputType : inputTypesToBeTested){ + tests.add(dynamicTest("01", () -> simulateItemConsumption(inputType, Items.coal, 0, "No items provided"))); + tests.add(dynamicTest("02", () -> simulateItemConsumption(inputType, Items.coal, 1, "Sufficient coal provided"))); + tests.add(dynamicTest("03", () -> simulateItemConsumption(inputType, Items.coal, 10, "Excess coal provided"))); + tests.add(dynamicTest("04", () -> simulateItemConsumption(inputType, Items.blastCompound, 1, "Blast compound provided"))); + //dynamicTest("03", () -> simulateItemConsumption(inputType, Items.plastanium, 1, "Plastanium provided")), // Not accepted by generator due to low flammability + tests.add(dynamicTest("05", () -> simulateItemConsumption(inputType, Items.biomatter, 1, "Biomatter provided"))); + tests.add(dynamicTest("06", () -> simulateItemConsumption(inputType, Items.pyratite, 1, "Pyratite provided"))); + } + DynamicTest[] testArray = new DynamicTest[tests.size()]; + testArray = tests.toArray(testArray); + return testArray; } - void simulateItemConsumption(Item item, int amount, String parameterDescription){ + void simulateItemConsumption(ItemLiquidGenerator.InputType inputType, Item item, int amount, String parameterDescription){ final float expectedEfficiency = Math.min(1.0f, amount > 0 ? item.flammability : 0f); final float expectedRemainingItemAmount = Math.max(0, amount - 1); - assertTrue(generator.acceptItem(item, tile, null), parameterDescription + ": Items which will be declined by the generator don't need to be tested - The code won't be called for those cases."); - - // Clean up manually since BeforeEach will not be called between dynamic tests - entity.items.clear(); - entity.generateTime = 0.0f; - entity.productionEfficiency = 0.0f; + createGenerator(inputType); + assertTrue(generator.acceptItem(item, tile, null), inputType + " | " + parameterDescription + ": Items which will be declined by the generator don't need to be tested - The code won't be called for those cases."); if(amount > 0){ entity.items.add(item, amount); @@ -117,13 +139,24 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ // Perform an update on the generator once - This should use up one or zero items - dependent on if the item is accepted and available or not. generator.update(tile); - assertEquals(expectedRemainingItemAmount, entity.items.get(item), parameterDescription + ": Remaining item amount mismatch."); - assertEquals(expectedEfficiency, entity.productionEfficiency, parameterDescription + ": Efficiency mismatch."); + assertEquals(expectedRemainingItemAmount, entity.items.get(item), inputType + " | " + parameterDescription + ": Remaining item amount mismatch."); + assertEquals(expectedEfficiency, entity.productionEfficiency, inputType + " | " + parameterDescription + ": Efficiency mismatch."); } /** Makes sure the efficiency stays equal during the item duration. */ @Test - void efficiencyRemainsConstantWithinItemDuration(){ + void efficiencyRemainsConstantWithinItemDuration_ItemsOnly(){ + testItemDuration(ItemLiquidGenerator.InputType.ItemsOnly); + } + + /** Makes sure the efficiency stays equal during the item duration. */ + @Test + void efficiencyRemainsConstantWithinItemDuration_ItemsAndLiquids(){ + testItemDuration(ItemLiquidGenerator.InputType.LiquidsAndItems); + } + + void testItemDuration(ItemLiquidGenerator.InputType inputType){ + createGenerator(inputType); // Burn a single coal and test for the duration entity.items.add(Items.coal, 1); From 61210955bd5b4e4264f8fb2d40befcc6fff7d8ae Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Fri, 30 Nov 2018 18:45:40 +0100 Subject: [PATCH 32/67] Optimized power distribution implementation for buffered consumers --- .../io/anuke/mindustry/world/blocks/power/PowerGraph.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) 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 e9e693755a..ea2e8abb26 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -8,6 +8,7 @@ import com.badlogic.gdx.utils.Queue; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.consumers.ConsumePower; import io.anuke.mindustry.world.consumers.Consumers; +import io.anuke.ucore.util.Mathf; import static io.anuke.mindustry.Vars.threads; @@ -119,11 +120,9 @@ public class PowerGraph{ if(consumes.has(ConsumePower.class)){ ConsumePower consumePower = consumes.get(ConsumePower.class); if(consumePower.isBuffered){ - // Add a percentage of the requested amount, but limit it to the mission amount. - // TODO This can maybe be calculated without converting to absolute values first + // 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(); - float missingAmount = consumePower.powerCapacity * (1 - consumer.entity.power.satisfaction); - consumer.entity.power.satisfaction += Math.min(missingAmount, maximumRate) / consumePower.powerCapacity; + consumer.entity.power.satisfaction = Mathf.clamp(consumer.entity.power.satisfaction + maximumRate / consumePower.powerCapacity); }else{ consumer.entity.power.satisfaction = coverage; } From 965fec5927bdce68a9e39f0ed649a6f0a93a2f8c Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Fri, 30 Nov 2018 21:08:33 +0100 Subject: [PATCH 33/67] Continued work on power generators --- .../mindustry/content/blocks/PowerBlocks.java | 6 +- .../blocks/power/ItemLiquidGenerator.java | 7 +- .../blocks/power/LiquidHeatGenerator.java | 6 +- .../world/blocks/power/PowerGenerator.java | 6 +- .../java/power/ItemLiquidGeneratorTests.java | 4 +- .../test/java/power/PowerBalancingTests.java | 74 +++++++++++++++++++ tests/src/test/java/power/PowerTests.java | 6 +- 7 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 tests/src/test/java/power/PowerBalancingTests.java diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index c2f2e24596..dbd70cbeca 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -20,17 +20,13 @@ public class PowerBlocks extends BlockList implements ContentList{ thermalGenerator = new LiquidHeatGenerator("thermal-generator"){{ maxLiquidGenerate = 4f; - // TODO: Balance - powerProduction = 0.17f; - liquidPowerMultiplier = 0.1f; + powerProduction = 8f; generateEffect = BlockFx.redgeneratespark; size = 2; }}; turbineGenerator = new TurbineGenerator("turbine-generator"){{ - // TODO: Balance powerProduction = 0.28f; - liquidPowerMultiplier = 0.3f; itemDuration = 30f; consumes.liquid(Liquids.water, 0.05f); size = 2; diff --git a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java index 7fbc233533..0fab57f16d 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -30,7 +30,6 @@ public class ItemLiquidGenerator extends PowerGenerator{ protected float itemDuration = 70f; protected float minLiquidEfficiency = 0.2f; - protected float liquidPowerMultiplier = 1.3f; // A liquid with 100% flammability will be 30% more efficient than an item with 100% flammability. /** Maximum liquid used per frame. */ protected float maxLiquidGenerate = 0.4f; @@ -100,14 +99,14 @@ public class ItemLiquidGenerator extends PowerGenerator{ } //liquid takes priority over solids if(hasLiquids && liquid != null && entity.liquids.get(liquid) >= 0.001f){ - float baseLiquidEfficiency = getLiquidEfficiency(liquid) * this.liquidPowerMultiplier; + float baseLiquidEfficiency = getLiquidEfficiency(liquid); float maximumPossible = maxLiquidGenerate * calculationDelta; float used = Math.min(entity.liquids.get(liquid) * calculationDelta, maximumPossible); entity.liquids.remove(liquid, used); - // Note: 1 Item with 100% Flammability = 100% efficiency. This means 100% is not max but rather a reference point for this generator. - entity.productionEfficiency = baseLiquidEfficiency * used / maximumPossible; + // Note: 0.5 = 100%. PowerGraph will multiply this efficiency by two on its own. + entity.productionEfficiency = Mathf.clamp(baseLiquidEfficiency * used / maximumPossible); if(used > 0.001f && Mathf.chance(0.05 * entity.delta())){ Effects.effect(generateEffect, tile.drawx() + Mathf.range(3f), tile.drawy() + Mathf.range(3f)); diff --git a/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java index c1d95d9ff1..bf65ea8bd5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/LiquidHeatGenerator.java @@ -1,6 +1,6 @@ package io.anuke.mindustry.world.blocks.power; -import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.content.Liquids; import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.StatUnit; @@ -16,8 +16,8 @@ public class LiquidHeatGenerator extends ItemLiquidGenerator{ super.setStats(); stats.remove(BlockStat.basePowerGeneration); - // TODO Adapt to new new power system. Maybe this override can be removed. - //stats.add(BlockStat.basePowerGeneration, * 60f, StatUnit.powerSecond); + // Right now, Lava is the only thing that can be used. + stats.add(BlockStat.basePowerGeneration, powerProduction * getLiquidEfficiency(Liquids.lava) / maxLiquidGenerate * 60f, StatUnit.powerSecond); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index 49da784b2a..84b2b6c841 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -33,12 +33,14 @@ public class PowerGenerator extends PowerDistributor{ @Override public void setStats(){ super.setStats(); - stats.add(generationType, powerProduction * 60.0f, StatUnit.powerSecond); + // Divide power production by two since that is what is produced at an efficiency of 0.5, which currently represents 100% + stats.add(generationType, powerProduction * 60.0f / 2.0f, StatUnit.powerSecond); } @Override public float getPowerProduction(Tile tile){ - return powerProduction * tile.entity().productionEfficiency; + // Multiply all efficiencies by two since 0.5 = 100% efficiency + return powerProduction * tile.entity().productionEfficiency * 2.0f; } @Override diff --git a/tests/src/test/java/power/ItemLiquidGeneratorTests.java b/tests/src/test/java/power/ItemLiquidGeneratorTests.java index 8a2fa7ff18..dcde4df3e1 100644 --- a/tests/src/test/java/power/ItemLiquidGeneratorTests.java +++ b/tests/src/test/java/power/ItemLiquidGeneratorTests.java @@ -26,7 +26,6 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ private ItemLiquidGenerator generator; private Tile tile; private ItemLiquidGenerator.ItemLiquidGeneratorEntity entity; - private final float fakeLiquidPowerMultiplier = 2.0f; private final float fakeItemDuration = 60f; // 60 ticks private final float maximumLiquidUsage = 0.5f; @@ -35,7 +34,6 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ { powerProduction = 0.1f; itemDuration = 60f; - liquidPowerMultiplier = fakeLiquidPowerMultiplier; itemDuration = fakeItemDuration; maxLiquidGenerate = maximumLiquidUsage; } @@ -79,7 +77,7 @@ public class ItemLiquidGeneratorTests extends PowerTestFixture{ } void simulateLiquidConsumption(ItemLiquidGenerator.InputType inputType, Liquid liquid, float availableLiquidAmount, String parameterDescription){ - final float baseEfficiency = fakeLiquidPowerMultiplier * liquid.flammability; + final float baseEfficiency = liquid.flammability; final float expectedEfficiency = Math.min(1.0f, availableLiquidAmount / maximumLiquidUsage) * baseEfficiency; final float expectedConsumptionPerTick = Math.min(maximumLiquidUsage, availableLiquidAmount); final float expectedRemainingLiquidAmount = Math.max(0.0f, availableLiquidAmount - expectedConsumptionPerTick * FakeThreadHandler.fakeDelta); diff --git a/tests/src/test/java/power/PowerBalancingTests.java b/tests/src/test/java/power/PowerBalancingTests.java new file mode 100644 index 0000000000..72a7811e73 --- /dev/null +++ b/tests/src/test/java/power/PowerBalancingTests.java @@ -0,0 +1,74 @@ +package power; + +import com.badlogic.gdx.math.MathUtils; +import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.content.Liquids; +import io.anuke.mindustry.content.blocks.PowerBlocks; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.Liquid; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.power.BurnerGenerator; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** Sets expectations to specific production blocks using specific inputs. */ + +public class PowerBalancingTests extends PowerTestFixture{ +// Last updated to values of: v63 + + /** + * Tests the produced power of a power block with items or liquids. + * @apiNote Tests only a single tick with a fixed delta and interpolates that to match one second. + * @param powerBlock The block to be tested. + * @param inputItem The item to be supplied (may be null). + * @param inputLiquid The liquid to be supplied (may be null). + * @param expectedPowerPerSecond The amount of power which should be produced per second. + */ + public void testPowerGenerator(Block powerBlock, Item inputItem, Liquid inputLiquid, float expectedPowerPerSecond){ + Tile fakeTile = createFakeTile(0, 0, powerBlock); + if(inputItem != null){ + fakeTile.entity.items.add(inputItem, 1); + } + if(inputLiquid != null){ + fakeTile.entity.liquids.add(inputLiquid, 1); + } + fakeTile.entity.cons.update(fakeTile.entity); + powerBlock.update(fakeTile); + + assertEquals(expectedPowerPerSecond, fakeTile.entity.power.graph.getPowerProduced() * 60f, MathUtils.FLOAT_ROUNDING_ERROR * 10.0f); + } + + @Test + public void testCombustionWithCoal(){ + testPowerGenerator(PowerBlocks.combustionGenerator, Items.coal, null, 2.7f); // 100% flammability + } + + @Test + public void testCombustionWithOil(){ + testPowerGenerator(PowerBlocks.combustionGenerator, null, Liquids.oil, 2.7f * 1.2f); // 120% flammability + } + + @Test + public void testCombustionWithBlastCompound(){ + testPowerGenerator(PowerBlocks.combustionGenerator, Items.blastCompound, null, 2.7f * 0.4f); // 40% flammability + } + + @Test + public void testTurbineWithCoal(){ + testPowerGenerator(PowerBlocks.turbineGenerator, Items.coal, Liquids.water, 8.4f); // 100% flammability + } + + @Test + public void testTurbineWithBiomatter(){ + testPowerGenerator(PowerBlocks.turbineGenerator, Items.biomatter, Liquids.water, 8.4f * 0.8f); // 100% flammability + } + + @Test + public void testThermalWithLava(){ + testPowerGenerator(PowerBlocks.thermalGenerator, null, Liquids.lava, 36f); // 100% flammability + } +} diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index 5c1bea7d0c..e8167a4aec 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -49,7 +49,7 @@ public class PowerTests extends PowerTestFixture{ } void simulateDirectConsumption(float producedPower, float requiredPower, float expectedSatisfaction, String parameterDescription){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); - producerTile.entity().productionEfficiency = 1.0f; + producerTile.entity().productionEfficiency = 0.5f; // Currently, 0.5f = 100% Tile directConsumerTile = createFakeTile(0, 1, createFakeDirectConsumer(requiredPower, 0.6f)); PowerGraph powerGraph = new PowerGraph(); @@ -89,7 +89,7 @@ public class PowerTests extends PowerTestFixture{ } void simulateBufferedConsumption(float producedPower, float maxBuffer, float powerConsumedPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); - producerTile.entity().productionEfficiency = 1.0f; + producerTile.entity().productionEfficiency = 0.5f; // Currently, 0.5 = 100% Tile bufferedConsumerTile = createFakeTile(0, 1, createFakeBufferedConsumer(maxBuffer, maxBuffer > 0.0f ? maxBuffer/powerConsumedPerTick : 1.0f)); bufferedConsumerTile.entity.power.satisfaction = initialSatisfaction; @@ -128,7 +128,7 @@ public class PowerTests extends PowerTestFixture{ if(producedPower > 0.0f){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); - producerTile.entity().productionEfficiency = 1.0f; + producerTile.entity().productionEfficiency = 0.5f; // Currently, 0.5f = 100% powerGraph.add(producerTile); } Tile directConsumerTile = null; From 074a6bfa87e813ac636c58a68ccfdbff4ca039d5 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Fri, 30 Nov 2018 21:10:31 +0100 Subject: [PATCH 34/67] Reverted accidental file push --- .../test/java/power/PowerBalancingTests.java | 74 ------------------- 1 file changed, 74 deletions(-) delete mode 100644 tests/src/test/java/power/PowerBalancingTests.java diff --git a/tests/src/test/java/power/PowerBalancingTests.java b/tests/src/test/java/power/PowerBalancingTests.java deleted file mode 100644 index 72a7811e73..0000000000 --- a/tests/src/test/java/power/PowerBalancingTests.java +++ /dev/null @@ -1,74 +0,0 @@ -package power; - -import com.badlogic.gdx.math.MathUtils; -import io.anuke.mindustry.content.Items; -import io.anuke.mindustry.content.Liquids; -import io.anuke.mindustry.content.blocks.PowerBlocks; -import io.anuke.mindustry.type.Item; -import io.anuke.mindustry.type.Liquid; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.power.BurnerGenerator; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** Sets expectations to specific production blocks using specific inputs. */ - -public class PowerBalancingTests extends PowerTestFixture{ -// Last updated to values of: v63 - - /** - * Tests the produced power of a power block with items or liquids. - * @apiNote Tests only a single tick with a fixed delta and interpolates that to match one second. - * @param powerBlock The block to be tested. - * @param inputItem The item to be supplied (may be null). - * @param inputLiquid The liquid to be supplied (may be null). - * @param expectedPowerPerSecond The amount of power which should be produced per second. - */ - public void testPowerGenerator(Block powerBlock, Item inputItem, Liquid inputLiquid, float expectedPowerPerSecond){ - Tile fakeTile = createFakeTile(0, 0, powerBlock); - if(inputItem != null){ - fakeTile.entity.items.add(inputItem, 1); - } - if(inputLiquid != null){ - fakeTile.entity.liquids.add(inputLiquid, 1); - } - fakeTile.entity.cons.update(fakeTile.entity); - powerBlock.update(fakeTile); - - assertEquals(expectedPowerPerSecond, fakeTile.entity.power.graph.getPowerProduced() * 60f, MathUtils.FLOAT_ROUNDING_ERROR * 10.0f); - } - - @Test - public void testCombustionWithCoal(){ - testPowerGenerator(PowerBlocks.combustionGenerator, Items.coal, null, 2.7f); // 100% flammability - } - - @Test - public void testCombustionWithOil(){ - testPowerGenerator(PowerBlocks.combustionGenerator, null, Liquids.oil, 2.7f * 1.2f); // 120% flammability - } - - @Test - public void testCombustionWithBlastCompound(){ - testPowerGenerator(PowerBlocks.combustionGenerator, Items.blastCompound, null, 2.7f * 0.4f); // 40% flammability - } - - @Test - public void testTurbineWithCoal(){ - testPowerGenerator(PowerBlocks.turbineGenerator, Items.coal, Liquids.water, 8.4f); // 100% flammability - } - - @Test - public void testTurbineWithBiomatter(){ - testPowerGenerator(PowerBlocks.turbineGenerator, Items.biomatter, Liquids.water, 8.4f * 0.8f); // 100% flammability - } - - @Test - public void testThermalWithLava(){ - testPowerGenerator(PowerBlocks.thermalGenerator, null, Liquids.lava, 36f); // 100% flammability - } -} From b4aba3d263e32b5c7924e0ff7d1428bf19a6ee95 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Sat, 1 Dec 2018 13:41:14 +0100 Subject: [PATCH 35/67] All power blocks now produce power as stated in v63 tool tips --- .../anuke/mindustry/content/blocks/PowerBlocks.java | 6 +++--- .../mindustry/world/blocks/power/NuclearReactor.java | 12 ++++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index 5336edede3..aa06fd58a1 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -20,7 +20,7 @@ public class PowerBlocks extends BlockList implements ContentList{ thermalGenerator = new LiquidHeatGenerator("thermal-generator"){{ maxLiquidGenerate = 2f; - powerProduction = 4f; + powerProduction = 2f; generateEffect = BlockFx.redgeneratespark; size = 2; }}; @@ -42,14 +42,14 @@ public class PowerBlocks extends BlockList implements ContentList{ powerProduction = 0.0045f; }}; - largeSolarPanel = new PowerGenerator("solar-panel-large"){{ + largeSolarPanel = new SolarGenerator("solar-panel-large"){{ powerProduction = 0.055f; }}; thoriumReactor = new NuclearReactor("thorium-reactor"){{ size = 3; health = 700; - powerMultiplier = 1.1f; + powerProduction = 1.1f; }}; fusionReactor = new FusionReactor("fusion-reactor"){{ diff --git a/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java b/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java index f2abdbc304..f9e1966fd5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/NuclearReactor.java @@ -34,7 +34,6 @@ public class NuclearReactor extends PowerGenerator{ protected Color coolColor = new Color(1, 1, 1, 0f); protected Color hotColor = Color.valueOf("ff9575a3"); protected int fuelUseTime = 120; //time to consume 1 fuel - protected float powerMultiplier = 0.45f; //power per frame, depends on full capacity protected float heating = 0.013f; //heating per frame protected float coolantPower = 0.015f; //how much heat decreases per coolant unit protected float smokeThreshold = 0.3f; //threshold at which block starts smoking @@ -49,8 +48,6 @@ public class NuclearReactor extends PowerGenerator{ super(name); itemCapacity = 30; liquidCapacity = 50; - // TODO Adapt to new power system - //powerCapacity = 80f; hasItems = true; hasLiquids = true; @@ -77,9 +74,9 @@ public class NuclearReactor extends PowerGenerator{ super.setStats(); stats.add(BlockStat.inputLiquid, new LiquidFilterValue(liquid -> liquid.temperature <= 0.5f)); - // TODO Verify for new power system stats.remove(BlockStat.basePowerGeneration); - stats.add(BlockStat.basePowerGeneration, powerMultiplier * 60f * 0.5f, StatUnit.powerSecond); + // Display the power which will be produced at 50% efficiency + stats.add(BlockStat.basePowerGeneration, powerProduction * 60f * 0.5f, StatUnit.powerSecond); } @Override @@ -88,12 +85,11 @@ public class NuclearReactor extends PowerGenerator{ int fuel = entity.items.get(consumes.item()); float fullness = (float) fuel / itemCapacity; + entity.productionEfficiency = fullness / 2.0f; // Currently, efficiency of 0.5 = 100% if(fuel > 0){ entity.heat += fullness * heating * Math.min(entity.delta(), 4f); - // TODO Adapt to new power system - //entity.power.amount += powerMultiplier * fullness * entity.delta(); - //entity.power.amount = Mathf.clamp(entity.power.amount, 0f, powerCapacity); + if(entity.timer.get(timerFuel, fuelUseTime)){ entity.items.remove(consumes.item(), 1); } From 982c9bf964e2bd0641924f341879c5aa13d6eec4 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Sat, 1 Dec 2018 14:31:01 +0100 Subject: [PATCH 36/67] - Fixed double power production - Buffered consumers no longer request power when full --- .../world/blocks/power/PowerGenerator.java | 15 ++++++--------- .../mindustry/world/consumers/ConsumePower.java | 11 +++++++---- tests/src/test/java/power/PowerTestFixture.java | 3 ++- tests/src/test/java/power/PowerTests.java | 10 ++++++++-- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index 84b2b6c841..d165925c07 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -15,13 +15,8 @@ import java.io.DataOutput; import java.io.IOException; public class PowerGenerator extends PowerDistributor{ - /** The amount of power produced per tick. */ + /** The amount of power produced per tick in case of an efficiency of 1.0, which currently represents 200%. */ protected float powerProduction; - /** The maximum possible efficiency for this generator. Supply values larger than 1.0f if more than 100% is possible. - * This could be the case when e.g. an item with 100% flammability is the reference point, but a more effective liquid - * can be supplied as an alternative. - */ - protected float maxEfficiency = 1.0f; public BlockStat generationType = BlockStat.basePowerGeneration; public PowerGenerator(String name){ @@ -39,8 +34,9 @@ public class PowerGenerator extends PowerDistributor{ @Override public float getPowerProduction(Tile tile){ - // Multiply all efficiencies by two since 0.5 = 100% efficiency - return powerProduction * tile.entity().productionEfficiency * 2.0f; + // While 0.5 efficiency currently reflects 100%, we do not need to multiply by any factor since powerProduction states the + // power which would be produced at 1.0 efficiency + return powerProduction * tile.entity().productionEfficiency; } @Override @@ -57,12 +53,13 @@ public class PowerGenerator extends PowerDistributor{ public void setBars(){ super.setBars(); if(hasPower){ - bars.add(new BlockBar(BarType.power, true, tile -> tile.entity().productionEfficiency / maxEfficiency)); + bars.add(new BlockBar(BarType.power, true, tile -> tile.entity().productionEfficiency)); } } public static class GeneratorEntity extends TileEntity{ public float generateTime; + /** The efficiency of the producer. Currently, an efficiency of 0.5 means 100% */ public float productionEfficiency = 0.0f; @Override diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java index d4d9a3dffb..28d3d4ab66 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -1,5 +1,6 @@ package io.anuke.mindustry.world.consumers; +import com.badlogic.gdx.math.MathUtils; import io.anuke.ucore.scene.ui.layout.Table; import io.anuke.mindustry.entities.TileEntity; @@ -86,10 +87,12 @@ public class ConsumePower extends Consume{ * @return The amount of power which is requested per tick. */ public float requestedPower(Block block, TileEntity entity){ - // TODO Make the block not consume power on the following conditions, either here or in PowerGraph: - // - Other consumers are not valid, e.g. additional input items/liquids are missing - // - Buffer is full - return powerPerTick; + if(isBuffered){ + // Stop requesting power once the buffer is full. + return MathUtils.isEqual(entity.power.satisfaction, 1.0f) ? 0.0f : powerPerTick; + }else{ + return powerPerTick; + } } diff --git a/tests/src/test/java/power/PowerTestFixture.java b/tests/src/test/java/power/PowerTestFixture.java index 34d88bd62e..5728f6bc8e 100644 --- a/tests/src/test/java/power/PowerTestFixture.java +++ b/tests/src/test/java/power/PowerTestFixture.java @@ -42,8 +42,9 @@ public class PowerTestFixture{ } protected static PowerGenerator createFakeProducerBlock(float producedPower){ + // Multiply produced power by 2 since production efficiency is defined to be 0.5 = 100% return new PowerGenerator("fakegen"){{ - powerProduction = producedPower; + powerProduction = producedPower * 2.0f; }}; } diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index e8167a4aec..a79089ab91 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -98,7 +98,13 @@ public class PowerTests extends PowerTestFixture{ powerGraph.add(bufferedConsumerTile); assertEquals(producedPower * FakeThreadHandler.fakeDelta, powerGraph.getPowerProduced(), MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Produced power did not match"); - assertEquals(Math.min(maxBuffer, powerConsumedPerTick * FakeThreadHandler.fakeDelta), powerGraph.getPowerNeeded(), MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": ConsumedPower did not match"); + float expectedPowerUsage; + if(initialSatisfaction == 1.0f){ + expectedPowerUsage = 0f; + }else{ + expectedPowerUsage = Math.min(maxBuffer, powerConsumedPerTick * FakeThreadHandler.fakeDelta); + } + assertEquals(expectedPowerUsage, powerGraph.getPowerNeeded(), MathUtils.FLOAT_ROUNDING_ERROR, parameterDescription + ": Consumed power did not match"); // Update and check for the expected power satisfaction of the consumer powerGraph.update(); @@ -128,7 +134,7 @@ public class PowerTests extends PowerTestFixture{ if(producedPower > 0.0f){ Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower)); - producerTile.entity().productionEfficiency = 0.5f; // Currently, 0.5f = 100% + producerTile.entity().productionEfficiency = 0.5f; powerGraph.add(producerTile); } Tile directConsumerTile = null; From 14e057cf056f7caf79c8f14ebf09976574efa211 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Sat, 1 Dec 2018 16:13:03 +0100 Subject: [PATCH 37/67] - Resolved all remaining TODOs --- .../content/blocks/DefenseBlocks.java | 1 - .../mindustry/content/blocks/PowerBlocks.java | 1 + .../world/blocks/defense/ForceProjector.java | 30 +++++++++++-------- .../world/blocks/distribution/MassDriver.java | 7 ++--- .../world/blocks/units/RepairPoint.java | 29 +++++++++++++----- .../world/consumers/ConsumePower.java | 3 +- .../mindustry/world/consumers/Consumers.java | 2 +- 7 files changed, 46 insertions(+), 27 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java index e3038d569e..be7c016886 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java @@ -83,7 +83,6 @@ public class DefenseBlocks extends BlockList implements ContentList{ }}; forceProjector = new ForceProjector("force-projector"){{ - consumes.powerDirect(0.2f); size = 3; consumes.item(Items.phasefabric).optional(true); }}; diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index aa06fd58a1..cabd8bfe92 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -43,6 +43,7 @@ public class PowerBlocks extends BlockList implements ContentList{ }}; largeSolarPanel = new SolarGenerator("solar-panel-large"){{ + size = 3; powerProduction = 0.055f; }}; 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 9af93e6fa4..d80eb9546a 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java @@ -11,6 +11,7 @@ import io.anuke.mindustry.world.BarType; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.consumers.ConsumeLiquidFilter; +import io.anuke.mindustry.world.consumers.ConsumePower; import io.anuke.mindustry.world.meta.BlockBar; import io.anuke.mindustry.world.meta.BlockStat; import io.anuke.mindustry.world.meta.StatUnit; @@ -41,9 +42,12 @@ public class ForceProjector extends Block { protected float cooldownNormal = 1.75f; protected float cooldownLiquid = 1.5f; protected float cooldownBrokenBase = 0.35f; + protected float basePowerDraw = 0.2f; protected float powerDamage = 0.1f; + protected final ConsumePower consumePower; protected TextureRegion topRegion; + public ForceProjector(String name) { super(name); update = true; @@ -54,7 +58,7 @@ public class ForceProjector extends Block { hasItems = true; itemCapacity = 10; consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).optional(true).update(false); - consumes.powerBuffered(60f); + consumePower = consumes.powerBuffered(60f); } @Override @@ -67,6 +71,7 @@ public class ForceProjector extends Block { public void setStats(){ super.setStats(); + stats.add(BlockStat.powerUse, basePowerDraw * 60f, StatUnit.powerSecond); stats.add(BlockStat.powerDamage, powerDamage, StatUnit.powerUnits); } @@ -99,16 +104,18 @@ public class ForceProjector extends Block { Effects.effect(BlockFx.reactorsmoke, tile.drawx() + Mathf.range(tilesize/2f), tile.drawy() + Mathf.range(tilesize/2f)); } - if(!entity.cons.valid() && !cheat){ + // Draw base power from buffer manually (ConsumePower doesn't support both filling a buffer and drawing power additionally so we have to do it here) + entity.power.satisfaction -= Math.min(entity.power.satisfaction, basePowerDraw / consumePower.powerCapacity); + + if(entity.power.satisfaction == 0.0f && !cheat){ entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f); if(entity.warmup <= 0.09f){ entity.broken = true; } }else{ entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.1f); - // TODO Adapt power calculations to new power system -// float powerUse = Math.min(powerDamage * entity.delta() * (1f + entity.buildup / breakage), powerCapacity); -// entity.power.amount -= powerUse; + float relativePowerDraw = powerDamage * entity.delta() * (1f + entity.buildup / breakage) / consumePower.powerCapacity; + entity.power.satisfaction -= Math.min(relativePowerDraw, entity.power.satisfaction); } if(entity.buildup > 0){ @@ -143,14 +150,13 @@ public class ForceProjector extends Block { if(trait.canBeAbsorbed() && trait.getTeam() != tile.getTeam() && isInsideHexagon(trait.getX(), trait.getY(), realRadius * 2f, tile.drawx(), tile.drawy())){ trait.absorb(); Effects.effect(BulletFx.absorb, trait); - float hit = trait.getShieldDamage()*powerDamage; + float relativePowerDraw = trait.getShieldDamage() * powerDamage / consumePower.powerCapacity; entity.hit = 1f; - // TODO Adapt power calculations to new power system -// entity.power.amount -= Math.min(hit, entity.power.amount); -// -// if(entity.power.amount <= 0.0001f){ -// entity.buildup += trait.getShieldDamage() * entity.warmup*2f; -// } + + entity.power.satisfaction -= Math.min(relativePowerDraw, entity.power.satisfaction); + if(entity.power.satisfaction <= 0.0001f){ + entity.buildup += trait.getShieldDamage() * entity.warmup * 2f; + } entity.buildup += trait.getShieldDamage() * entity.warmup; } }); 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 f81c344278..6b8f05554b 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -37,7 +37,6 @@ import java.io.IOException; import static io.anuke.mindustry.Vars.*; -// TODO Adapt whole class to new power system public class MassDriver extends Block{ protected float range; protected float rotateSpeed = 0.04f; @@ -49,7 +48,7 @@ public class MassDriver extends Block{ protected Effect smokeEffect = ShootFx.shootBigSmoke2; protected Effect recieveEffect = BlockFx.mineBig; protected float shake = 3f; - protected final static float powerPercentageUsed = 0.8f; + protected final static float powerPercentageUsed = 1.0f; protected TextureRegion turretRegion; public MassDriver(String name){ @@ -171,8 +170,8 @@ public class MassDriver extends Block{ entity.rotation = Mathf.slerpDelta(entity.rotation, tile.angleTo(waiter), rotateSpeed); }else if(tile.entity.items.total() >= minDistribute && - linkValid(tile) && //only fire when at least at 80% power capacity - tile.entity.power.satisfaction > powerPercentageUsed && + linkValid(tile) && //only fire when at 100% power capacity + tile.entity.power.satisfaction >= powerPercentageUsed && link.block().itemCapacity - link.entity.items.total() >= minDistribute && entity.reload <= 0.0001f){ MassDriverEntity other = link.entity(); 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 6d0192634b..c9c86aefe5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/RepairPoint.java @@ -10,7 +10,10 @@ import io.anuke.mindustry.graphics.Layer; import io.anuke.mindustry.graphics.Palette; 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.BlockFlag; +import io.anuke.mindustry.world.meta.BlockStat; +import io.anuke.mindustry.world.meta.StatUnit; import io.anuke.ucore.core.Timers; import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Lines; @@ -26,6 +29,8 @@ public class RepairPoint extends Block{ protected float repairRadius = 50f; protected float repairSpeed = 0.3f; + protected float powerPerEvent = 0.06f; + protected ConsumePower consumePower; protected TextureRegion topRegion; @@ -37,8 +42,7 @@ public class RepairPoint extends Block{ layer = Layer.turret; layer2 = Layer.laser; hasPower = true; - // TODO Adapt to new power system - Make it use power while repairing - consumes.powerBuffered(20f); + consumePower = consumes.powerBuffered(20f); } @Override @@ -48,6 +52,12 @@ public class RepairPoint extends Block{ topRegion = Draw.region(name + "-turret"); } + @Override + public void setStats(){ + super.setStats(); + stats.add(BlockStat.powerUse, powerPerEvent * 60f, StatUnit.powerSecond); + } + @Override public void drawSelect(Tile tile){ Draw.color(Palette.accent); @@ -83,17 +93,22 @@ public class RepairPoint extends Block{ public void update(Tile tile){ RepairPointEntity entity = tile.entity(); + boolean targetIsBeingRepaired = false; if(entity.target != null && (entity.target.isDead() || entity.target.distanceTo(tile) > repairRadius || entity.target.health >= entity.target.maxHealth())){ entity.target = null; }else if(entity.target != null){ - entity.target.health += repairSpeed * Timers.delta() * entity.strength; - entity.target.clampHealth(); - // TODO: Make it use power here and reset power once target goes null - entity.rotation = Mathf.slerpDelta(entity.rotation, entity.angleTo(entity.target), 0.5f); + float relativeConsumption = powerPerEvent / consumePower.powerCapacity; + if(entity.power.satisfaction > 0.0f){ + entity.target.health += repairSpeed * Timers.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; + } } - if(entity.target != null && entity.cons.valid()){ + if(entity.target != null && targetIsBeingRepaired){ entity.strength = Mathf.lerpDelta(entity.strength, 1f, 0.08f * Timers.delta()); }else{ entity.strength = Mathf.lerpDelta(entity.strength, 0f, 0.07f * Timers.delta()); diff --git a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java index 28d3d4ab66..38cb91aa36 100644 --- a/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -14,7 +14,7 @@ public class ConsumePower extends Consume{ /** The maximum amount of power which can be processed per tick. This might influence efficiency or load a buffer. */ protected final float powerPerTick; /** The minimum power satisfaction (fraction of powerPerTick) which must be achieved before the module may work. */ - protected final float minimumSatisfaction; + public final float minimumSatisfaction; /** The maximum power capacity in power units. */ public final float powerCapacity; /** True if the module can store power. */ @@ -64,7 +64,6 @@ public class ConsumePower extends Consume{ @Override public boolean valid(Block block, TileEntity entity){ if(isBuffered){ - // TODO - Verify: It might be necessary to know about the power required per shot/event here. return true; }else{ return entity.power.satisfaction >= minimumSatisfaction; diff --git a/core/src/io/anuke/mindustry/world/consumers/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index 6d602da3e3..8b0f3c7861 100644 --- a/core/src/io/anuke/mindustry/world/consumers/Consumers.java +++ b/core/src/io/anuke/mindustry/world/consumers/Consumers.java @@ -63,7 +63,7 @@ public class Consumers{ */ public ConsumePower powerBuffered(float powerCapacity){ // TODO Balance: How long should it take to fill a buffer? The lower this value, the more power will be "stolen" from direct consumers. - return powerBuffered(powerCapacity, 60); + return powerBuffered(powerCapacity, 60f); } /** From 62c6d8e42d595f5d568e11c1b6e8fc731b6556a9 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Sat, 1 Dec 2018 16:49:30 +0100 Subject: [PATCH 38/67] Fixed Arcs not firing --- core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java b/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java index 4655f8fa12..48359ac757 100644 --- a/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/TurretBlocks.java @@ -110,7 +110,7 @@ public class TurretBlocks extends BlockList implements ContentList{ shootShake = 1f; shootCone = 40f; rotatespeed = 8f; - powerUsed = 10f; + powerUsed = 1f / 3f; consumes.powerBuffered(30f); range = 150f; shootEffect = ShootFx.lightningShoot; From 034fab480d8b16c46091546fc006a8900c97eccc Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Sat, 1 Dec 2018 18:32:17 +0100 Subject: [PATCH 39/67] Most direct consumers now operate slower when not supplied with full power --- core/src/io/anuke/mindustry/world/Block.java | 8 ++++++++ .../mindustry/world/blocks/defense/MendProjector.java | 2 +- .../world/blocks/defense/OverdriveProjector.java | 2 +- .../mindustry/world/blocks/distribution/LiquidBridge.java | 6 +++++- .../io/anuke/mindustry/world/blocks/production/Drill.java | 3 +++ .../anuke/mindustry/world/blocks/production/Fracker.java | 2 +- .../mindustry/world/blocks/production/GenericCrafter.java | 2 +- .../mindustry/world/blocks/production/Incinerator.java | 3 ++- .../mindustry/world/blocks/production/LiquidMixer.java | 3 +++ .../mindustry/world/blocks/production/PowerCrafter.java | 2 +- .../mindustry/world/blocks/production/PowerSmelter.java | 2 +- .../io/anuke/mindustry/world/blocks/production/Pump.java | 3 +++ .../mindustry/world/blocks/production/Separator.java | 2 +- .../mindustry/world/blocks/production/SolidPump.java | 2 +- .../anuke/mindustry/world/blocks/units/UnitFactory.java | 2 +- 15 files changed, 33 insertions(+), 11 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 4394277321..fe7376d91c 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -186,6 +186,14 @@ public class Block extends BaseBlock { return out; } + protected float getProgressIncrease(TileEntity entity, float baseTime){ + float progressIncrease = 1f / baseTime * entity.delta(); + if(hasPower){ + progressIncrease *= entity.power.satisfaction; // Reduced increase in case of low power + } + return progressIncrease; + } + public boolean isLayer(Tile tile){ return true; } diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/MendProjector.java b/core/src/io/anuke/mindustry/world/blocks/defense/MendProjector.java index 997c821de9..9b6ea039a7 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/MendProjector.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/MendProjector.java @@ -84,7 +84,7 @@ public class MendProjector extends Block{ other = other.target(); if(other.getTeamID() == tile.getTeamID() && !healed.contains(other.packedPosition()) && other.entity != null && other.entity.health < other.entity.maxHealth()){ - other.entity.healBy(other.entity.maxHealth() * (healPercent + entity.phaseHeat*phaseBoost)/100f); + other.entity.healBy(other.entity.maxHealth() * (healPercent + entity.phaseHeat*phaseBoost)/100f * entity.power.satisfaction); Effects.effect(BlockFx.healBlockFull, Hue.mix(color, phase, entity.phaseHeat), other.drawx(), other.drawy(), other.block().size); healed.add(other.packedPosition()); } diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/OverdriveProjector.java b/core/src/io/anuke/mindustry/world/blocks/defense/OverdriveProjector.java index 615a6fa5a8..80acda8888 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/OverdriveProjector.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/OverdriveProjector.java @@ -68,7 +68,7 @@ public class OverdriveProjector extends Block{ if(entity.charge >= reload){ float realRange = range + entity.phaseHeat * phaseRangeBoost; - float realBoost = speedBoost + entity.phaseHeat*speedBoostPhase; + float realBoost = (speedBoost + entity.phaseHeat*speedBoostPhase) * entity.power.satisfaction; Effects.effect(BlockFx.overdriveWave, Hue.mix(color, phase, entity.phaseHeat), tile.drawx(), tile.drawy(), realRange); entity.charge = 0f; diff --git a/core/src/io/anuke/mindustry/world/blocks/distribution/LiquidBridge.java b/core/src/io/anuke/mindustry/world/blocks/distribution/LiquidBridge.java index 97a495cc54..3a0124fbc9 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/LiquidBridge.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/LiquidBridge.java @@ -31,7 +31,11 @@ public class LiquidBridge extends ItemBridge{ tryDumpLiquid(tile, entity.liquids.current()); }else{ if(entity.cons.valid()){ - entity.uptime = Mathf.lerpDelta(entity.uptime, 1f, 0.04f); + float alpha = 0.04f; + if(hasPower){ + alpha *= entity.power.satisfaction; // Exceed boot time unless power is at max. + } + entity.uptime = Mathf.lerpDelta(entity.uptime, 1f, alpha); }else{ entity.uptime = Mathf.lerpDelta(entity.uptime, 0f, 0.02f); } 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 212a71168b..ccd016f292 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Drill.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Drill.java @@ -188,6 +188,9 @@ public class Drill extends Block{ if(entity.consumed(ConsumeLiquid.class) && !liquidRequired){ speed = liquidBoostIntensity; } + if(hasPower){ + speed *= entity.power.satisfaction; // Drill slower when not at full power + } entity.warmup = Mathf.lerpDelta(entity.warmup, speed, warmupSpeed); entity.progress += entity.delta() diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Fracker.java b/core/src/io/anuke/mindustry/world/blocks/production/Fracker.java index 4c8fd24934..0c809a732e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Fracker.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Fracker.java @@ -64,7 +64,7 @@ public class Fracker extends SolidPump{ if(entity.cons.valid() && entity.accumulator < itemUseTime){ super.update(tile); - entity.accumulator += entity.delta(); + entity.accumulator += entity.delta() * entity.power.satisfaction; }else{ tryDumpLiquid(tile, result); } 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 a0e08537f3..7f7a3c69d7 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/GenericCrafter.java @@ -83,7 +83,7 @@ public class GenericCrafter extends Block{ if(entity.cons.valid() && tile.entity.items.get(output) < itemCapacity){ - entity.progress += 1f / craftTime * entity.delta(); + entity.progress += getProgressIncrease(entity, craftTime); entity.totalProgress += entity.delta(); entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f); diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java b/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java index 5fa8d18eff..e3fd63ad9e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Incinerator.java @@ -26,7 +26,8 @@ public class Incinerator extends Block{ update = true; solid = true; - consumes.powerDirect(0.05f); + // Incinerator has no speed which could be adjusted, so it will only operate fully powered for now + consumes.powerDirect(0.05f, 1.0f); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/production/LiquidMixer.java b/core/src/io/anuke/mindustry/world/blocks/production/LiquidMixer.java index 01e96fb5c3..4d00df4f3e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/LiquidMixer.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/LiquidMixer.java @@ -60,6 +60,9 @@ public class LiquidMixer extends LiquidBlock{ if(tile.entity.cons.valid()){ float use = Math.min(consumes.get(ConsumeLiquid.class).used() * entity.delta(), liquidCapacity - entity.liquids.get(outputLiquid)); + if(hasPower){ + use *= entity.power.satisfaction; // Produce less liquid if power is not maxed + } entity.accumulator += use; entity.liquids.add(outputLiquid, use); for(int i = 0; i < (int) (entity.accumulator / liquidPerItem); i++){ diff --git a/core/src/io/anuke/mindustry/world/blocks/production/PowerCrafter.java b/core/src/io/anuke/mindustry/world/blocks/production/PowerCrafter.java index 15fb12218b..4f3889eca5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/PowerCrafter.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/PowerCrafter.java @@ -65,7 +65,7 @@ public class PowerCrafter extends Block{ GenericCrafterEntity entity = tile.entity(); if(entity.cons.valid()){ - entity.progress += 1f / craftTime * entity.delta(); + entity.progress += getProgressIncrease(entity, craftTime); entity.totalProgress += entity.delta(); } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/PowerSmelter.java b/core/src/io/anuke/mindustry/world/blocks/production/PowerSmelter.java index a1e587315a..dd9daed408 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/PowerSmelter.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/PowerSmelter.java @@ -122,7 +122,7 @@ public class PowerSmelter extends PowerBlock{ } } - entity.craftTime += entity.delta(); + entity.craftTime += entity.delta() * entity.power.satisfaction; if(entity.items.get(result) >= itemCapacity //output full || entity.heat <= minHeat //not burning diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Pump.java b/core/src/io/anuke/mindustry/world/blocks/production/Pump.java index 939587fa60..188fa00cea 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Pump.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Pump.java @@ -95,6 +95,9 @@ public class Pump extends LiquidBlock{ if(tile.entity.cons.valid() && liquidDrop != null){ float maxPump = Math.min(liquidCapacity - tile.entity.liquids.total(), tiles * pumpAmount * tile.entity.delta()); + if(hasPower){ + maxPump *= tile.entity.power.satisfaction; // Produce slower if not at full power + } tile.entity.liquids.add(liquidDrop, maxPump); } diff --git a/core/src/io/anuke/mindustry/world/blocks/production/Separator.java b/core/src/io/anuke/mindustry/world/blocks/production/Separator.java index 50ab3fa2cb..514236578b 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/Separator.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/Separator.java @@ -87,7 +87,7 @@ public class Separator extends Block{ entity.totalProgress += entity.warmup * entity.delta(); if(entity.cons.valid()){ - entity.progress += 1f / filterTime*entity.delta(); + entity.progress += getProgressIncrease(entity, filterTime); entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f); }else{ entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.02f); diff --git a/core/src/io/anuke/mindustry/world/blocks/production/SolidPump.java b/core/src/io/anuke/mindustry/world/blocks/production/SolidPump.java index 46a30bca31..a0c2b860a6 100644 --- a/core/src/io/anuke/mindustry/world/blocks/production/SolidPump.java +++ b/core/src/io/anuke/mindustry/world/blocks/production/SolidPump.java @@ -76,7 +76,7 @@ public class SolidPump extends Pump{ } if(tile.entity.cons.valid() && typeLiquid(tile) < liquidCapacity - 0.001f){ - float maxPump = Math.min(liquidCapacity - typeLiquid(tile), pumpAmount * entity.delta() * fraction); + float maxPump = Math.min(liquidCapacity - typeLiquid(tile), pumpAmount * entity.delta() * fraction * entity.power.satisfaction); tile.entity.liquids.add(result, maxPump); entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.02f); if(Mathf.chance(entity.delta() * updateEffectChance)) 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 d7856bf1b3..d08ec4294f 100644 --- a/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/io/anuke/mindustry/world/blocks/units/UnitFactory.java @@ -159,7 +159,7 @@ public class UnitFactory extends Block{ if(hasRequirements(entity.items, entity.buildTime / produceTime) && entity.cons.valid()){ - entity.buildTime += entity.delta(); + entity.buildTime += entity.delta() * entity.power.satisfaction; entity.speedScl = Mathf.lerpDelta(entity.speedScl, 1f, 0.05f); }else{ entity.speedScl = Mathf.lerpDelta(entity.speedScl, 0f, 0.05f); From f8cf185db931a1447fc7fcac13cf01be6867709f Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Sun, 2 Dec 2018 12:43:18 +0100 Subject: [PATCH 40/67] Fixed display of solar panel power generation --- .../mindustry/world/blocks/power/SolarGenerator.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java index ebbc88b35f..097bc2183e 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/SolarGenerator.java @@ -17,6 +17,14 @@ public class SolarGenerator extends PowerGenerator{ flags = EnumSet.of(); } + @Override + public void setStats(){ + super.setStats(); + // Solar Generators don't really have an efficiency (yet), so for them 100% = 1.0f + stats.remove(generationType); + stats.add(generationType, powerProduction * 60.0f, StatUnit.powerSecond); + } + @Override public TileEntity newEntity(){ return new PowerGenerator.GeneratorEntity(){{ From c75c22955c6bda167296c3778b47d98ea9660865 Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Sun, 2 Dec 2018 22:01:42 +0200 Subject: [PATCH 41/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 6bc5700d53..463be9e68a 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -1,6 +1,6 @@ text.credits.text = Створив [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\nЄ питання по грі або проблеми с перекладом? Іди в офіційний сервер discord Mindustry в канал #український. text.credits = Автори -text.contributors = Перекладачі та Контриб'ютори +text.contributors = Перекладачі та Помічники text.discord = Приєднуйтесь до нашого Discord! text.link.discord.description = Офіційний discord-сервер Mindustry text.link.github.description = Код гри @@ -10,6 +10,7 @@ text.link.itch.io.description = Itch.io сторінка з веб-версіє text.link.google-play.description = Скачати з Google Play для Android text.link.wiki.description = Офіційна Mindustry вікі (англ.) text.linkfail = Не вдалося відкрити посилання!\nURL-адреса скопійовано у ваш буфер обміну. +text.screenshot = Скріншот збережено в {0} text.gameover = Гру закінчено text.gameover.pvp = [accent] {0}[] команда перемогла! text.sector.gameover = Цей сектор було втрачено. Повторно висадитися? @@ -72,6 +73,7 @@ text.nextmission = Наступна місія text.maps.none = [LIGHT_GRAY]Карт не знайдено! text.about.button = Про гру text.name = Нік: +text.noname = Спочатку придумайте[accent] собі нікнейм[]. text.filename = Ім'я файлу: text.unlocked = Новий блок розблоковано! text.unlocked.plural = Нові блоки розблоковано! From 787a9c00837b16246adc393db18c3f091713cebc Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Sun, 2 Dec 2018 22:12:39 +0200 Subject: [PATCH 42/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 28 ++++++++++++--------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 463be9e68a..8d9e17050a 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -86,6 +86,7 @@ text.server.kicked.sectorComplete = Сектор завойовано. text.server.kicked.sectorComplete.text = Ваша місія завершена. \nСервер продовжить роботу і висадить Вас в наступному секторі. text.server.kicked.clientOutdated = Застарілий клієнт! Оновіть свою гру! text.server.kicked.serverOutdated = Застарілий сервер! Попросіть адміністратора серверу оновити сервер/гру! +text.server.kicked.commitMismatch = Номер збірки серверу не співпадає з номером вашої збірки клієнта. Скачайте підходящу версію. text.server.kicked.banned = Ви були заблоковані на цьому сервері. text.server.kicked.recentKick = Нещодавно Вас вигнали(кікнули). \nПочекайте трохи перед наступним підключенням. text.server.kicked.nameInUse = На цьому сервері є хтось \nз таким ніком. @@ -268,6 +269,7 @@ text.error.invalidaddress = Некоректна адреса. text.error.timedout = Час очікувування вийшов.\nПереконайтеся, що адреса коректна і що власник сервера налаштував переадресацію порту! text.error.mismatch = Ошибка пакету:\nможливе невідповідність версії клієнта / сервера.\nПереконайтеся, що у Вас та у володара сервера встановлена остання версія Mindustry! text.error.alreadyconnected = Ви вже підключилися. +text.error.mapnotfound = Файл мапи не знайдено text.error.any = Невідома мережева помилка text.settings.language = Мова text.settings.reset = Скинути за замовчуванням @@ -292,7 +294,7 @@ text.blocks.unknown = [LIGHT_GRAY]??? text.blocks.blockinfo = Інформація про блок text.blocks.powercapacity = Місткість енергії text.blocks.powershot = Енергія/постріл -text.blocks.targetsair = Атакуе повітряних ворогів? +text.blocks.targetsair = Атакуе повітряних ворогів text.blocks.itemspeed = Швидкість переміщення ресурсів text.blocks.shootrange = Діапазон дії text.blocks.size = Розмір @@ -348,6 +350,7 @@ text.category.items = Предмети text.category.crafting = Створення text.category.shooting = Стрільба text.category.optional = Додаткові поліпшення +setting.indicators.name = Показувати у сторону союзників setting.autotarget.name = Авто-ціль setting.fpscap.name = Макс. FPS setting.fpscap.none = Необмежений @@ -382,6 +385,7 @@ command.retreat = Відступити command.patrol = Патрулювати keybind.press = Натисніть клавішу... keybind.press.axis = Натисніть клавішу... +keybind.screenshot.name = Скріншот мапи keybind.move_x.name = Рух по осі x keybind.move_y.name = Рух по осі x keybind.select.name = ВибратиПостріл @@ -426,7 +430,7 @@ item.lead.description = Базовий стартовий матеріал. Ши item.coal.name = Вугілля item.coal.description = Загальне та легкодоступне паливо. item.dense-alloy.name = Щільний сплав -item.dense-alloy.description = Жорсткий сплав вироблений зі свинця та міді. Використовується в передових транспортних блоках та високорівневих свердлах. +item.dense-alloy.description = Сплав, котрий вироблений зі свинця та міді. Використовується в передових транспортних блоках та високорівневих свердлах. item.titanium.name = Титан item.titanium.description = Рідкий суперлегкий метал широко використовується в рідкому транспорті, свердлах та літальних апаратах. item.thorium.name = Торій @@ -452,7 +456,7 @@ liquid.lava.name = Лава liquid.oil.name = Нафта liquid.cryofluid.name = Кріогенна рідина mech.alpha-mech.name = Альфа -mech.alpha-mech.weapon = Звичайний кулемет +mech.alpha-mech.weapon = Тяжкий кулемет mech.alpha-mech.ability = Виклик дронів mech.alpha-mech.description = Стандартний мех для настільних пристроїв. Має пристойну швидкість і урон; може створити до 3-х дронів для збільшення можливості перемоги. mech.delta-mech.name = Дельта @@ -464,8 +468,8 @@ mech.tau-mech.weapon = Відновлювальний лазер mech.tau-mech.ability = Відновлювальний спалах mech.tau-mech.description = Мех підтримки. Зцілює союзницькі блоки, стріляючи в них. Може зцілити союзників у радіусі зі своєю здатністю для ремонту. mech.omega-mech.name = Омега -mech.omega-mech.weapon = Купа ракет -mech.omega-mech.ability = Захисна конфігурація +mech.omega-mech.weapon = Ракометна конфігурація +mech.omega-mech.ability = Поглинання урона mech.omega-mech.description = Громіздкий і добре броньований мех, зроблений для фронтових нападів. Його здатність може блокувати до 90% вхідного урона. mech.dart-ship.name = Дротик mech.dart-ship.weapon = Ретранслятор @@ -476,7 +480,7 @@ mech.javelin-ship.weapon = Вибухові ракети mech.javelin-ship.ability = Генератор дуг mech.trident-ship.name = Тризубець mech.trident-ship.description = Важкий бомбардувальник. Досить добре броньований. -mech.trident-ship.weapon = Вантажний відсік з бомбами +mech.trident-ship.weapon = Бомби mech.glaive-ship.name = Спис mech.glaive-ship.description = Великий, добре броньований бойовий корабель. Оснащений запальним ретранслятором. Гарне прискорення і максимальна швидкість. mech.glaive-ship.weapon = Вогняний кулемет @@ -495,7 +499,7 @@ text.mech.ability = [LIGHT_GRAY]Здібність: {0} text.liquid.heatcapacity = [LIGHT_GRAY]Теплоємність: {0} text.liquid.viscosity = [LIGHT_GRAY]В'язкість: {0} text.liquid.temperature = [LIGHT_GRAY]Температура: {0} -block.constructing = {0}[LIGHT_GRAY](В процесі) +block.constructing = {0}\n[LIGHT_GRAY](В процесі) block.spawn.name = Місце появи ворога block.core.name = Ядро block.space.name = Пустота @@ -648,7 +652,7 @@ unit.wraith.description = Швидка бойова одиниця, котрий unit.fortress.name = Фортеця unit.fortress.description = Тяжка артилерійна наземна бойова одиниця. unit.revenant.name = Потойбічний вбивця -unit.revenant.description = Бойова одиниця з важкою лазерною зброєю. +unit.revenant.description = Важка бойова одиниця з лазерною зброєю. tutorial.begin = Ваша місія тут полягає в ліквідації[LIGHT_GRAY] противника[].\n\nПочнімо з[accent] видобутку міді[]. Щоб зробити це, торкніться мідної рудної жили біля вашого ядра. tutorial.drill = Ручна робота не ефективна\n[accent]Бури []можуть копати автоматично.\nПоставте один на мідній жилі. tutorial.conveyor = [accent]Конвейери[] використовуються для транспортування предметів в ядра.\nЗробіть лінію конвейерів від бурів до ядра. @@ -777,7 +781,7 @@ block.liquidsource.description = Безліченно виводить ріди block.itemvoid.description = Знищує будь-які предмети, які входять, без використання енергії. Працює тільки в пісочниці. block.powerinfinite.description = Нескінченність не межа. Безмежно виводить енергію. Лише пісочниця. block.powervoid.description = Енергія просто йде в порожнечу. Лише пісочниця. -liquid.water.description = Зазвичай використовується для охолодження машин та переробки відходів. -liquid.lava.description = Можна перетворити в[LIGHT_GRAY] камінь[], який використовується для генерації енергії або використовуати як боєприпаси для певних турелей. -liquid.oil.description = Можна спалити, взірвати або використовувати як теплоносій. -liquid.cryofluid.description = Найефективніша рідина для охолодження. Рідина з температурою нижче ніж -273 градусів за Цельсієм. Може бути використана для прискорення стрільби турелей або для охолодження чогось. +liquid.water.description = Цю рідину можно підвести до бурів для прискорення швидкості видобутку або к турелям для прискорення стрілянини. +liquid.lava.description = Можна перетворити в[LIGHT_GRAY] камінь[]. +liquid.oil.description = Можна спалити, взірвати або використовувати для охолодження. +liquid.cryofluid.description = Рідина з температурою нижче ніж -273 градусів за Цельсієм. Може бути використана для прискорення стрільби турелей або для охолодження чогось. From e5dabc63c26efc733ca0850c1dbc01dcfbba8975 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 5 Dec 2018 18:03:50 +0100 Subject: [PATCH 43/67] Removed power bars from producers --- .../world/blocks/power/PowerGenerator.java | 8 -- .../test/java/power/PowerBalancingTests.java | 94 +++++++++++++++++++ 2 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 tests/src/test/java/power/PowerBalancingTests.java diff --git a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java index d165925c07..5045f83c5f 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGenerator.java @@ -49,14 +49,6 @@ public class PowerGenerator extends PowerDistributor{ return new GeneratorEntity(); } - @Override - public void setBars(){ - super.setBars(); - if(hasPower){ - bars.add(new BlockBar(BarType.power, true, tile -> tile.entity().productionEfficiency)); - } - } - public static class GeneratorEntity extends TileEntity{ public float generateTime; /** The efficiency of the producer. Currently, an efficiency of 0.5 means 100% */ diff --git a/tests/src/test/java/power/PowerBalancingTests.java b/tests/src/test/java/power/PowerBalancingTests.java new file mode 100644 index 0000000000..2555285985 --- /dev/null +++ b/tests/src/test/java/power/PowerBalancingTests.java @@ -0,0 +1,94 @@ +package power; + +import com.badlogic.gdx.math.MathUtils; +import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.content.Liquids; +import io.anuke.mindustry.content.blocks.PowerBlocks; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.Liquid; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.power.BurnerGenerator; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** Sets expectations to specific production blocks using specific inputs. */ + +public class PowerBalancingTests extends PowerTestFixture{ +// Last updated to values of: v63 + + /** + * Tests the produced power of a power block with a full stack of items or liquids. + * @apiNote Tests only a single tick with a fixed delta and interpolates that to match one second. + * @param powerBlock The block to be tested. + * @param inputItem The item to be supplied (may be null). + * @param inputLiquid The liquid to be supplied (may be null). + * @param expectedPowerPerSecond The amount of power which should be produced per second. + */ + public void testPowerGenerator(Block powerBlock, Item inputItem, Liquid inputLiquid, float expectedPowerPerSecond){ + Tile fakeTile = createFakeTile(0, 0, powerBlock); + if(inputItem != null){ + fakeTile.entity.items.add(inputItem, powerBlock.itemCapacity); + } + if(inputLiquid != null){ + fakeTile.entity.liquids.add(inputLiquid, powerBlock.liquidCapacity); + } + fakeTile.entity.cons.update(fakeTile.entity); + powerBlock.update(fakeTile); + + assertEquals(expectedPowerPerSecond, fakeTile.entity.power.graph.getPowerProduced() * 60f / FakeThreadHandler.fakeDelta, MathUtils.FLOAT_ROUNDING_ERROR * 10.0f); + } + + @Test + public void testCombustionWithCoal(){ + testPowerGenerator(PowerBlocks.combustionGenerator, Items.coal, null, 2.7f); // 100% flammability + } + + @Test + public void testCombustionWithOil(){ + testPowerGenerator(PowerBlocks.combustionGenerator, null, Liquids.oil, 2.7f * 1.2f); // 120% flammability + } + + @Test + public void testCombustionWithBlastCompound(){ + testPowerGenerator(PowerBlocks.combustionGenerator, Items.blastCompound, null, 2.7f * 0.4f); // 40% flammability + } + + @Test + public void testTurbineWithCoal(){ + testPowerGenerator(PowerBlocks.turbineGenerator, Items.coal, Liquids.water, 8.4f); // 100% flammability + } + + @Test + public void testTurbineWithBiomatter(){ + testPowerGenerator(PowerBlocks.turbineGenerator, Items.biomatter, Liquids.water, 8.4f * 0.8f); // 100% flammability + } + + @Test + public void testThermalWithLava(){ + testPowerGenerator(PowerBlocks.thermalGenerator, null, Liquids.lava, 36f); // 100% flammability + } + + @Test + public void testSolarPanel(){ + testPowerGenerator(PowerBlocks.solarPanel, null, null, 0.27f); + } + + @Test + public void testLargeSolarPanel(){ + testPowerGenerator(PowerBlocks.largeSolarPanel, null, null, 3.3f); + } + + @Test + public void testThoriumReactor(){ + testPowerGenerator(PowerBlocks.thoriumReactor, Items.thorium, Liquids.cryofluid, 33.0f); + } + + @Test + public void testRadioIsotopeGenerator(){ + testPowerGenerator(PowerBlocks.rtgGenerator, Items.thorium, null, 9.0f); + } +} From 329dac77eefe5ffb8704e2e0a168200ae9e1b8c2 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 5 Dec 2018 18:56:07 +0100 Subject: [PATCH 44/67] Consumers only draw power when all other required inputs are valid --- .../world/blocks/power/PowerGraph.java | 29 ++++++++-- .../test/java/power/DirectConsumerTests.java | 57 +++++++++++++++++++ .../src/test/java/power/PowerTestFixture.java | 3 +- 3 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 tests/src/test/java/power/DirectConsumerTests.java 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 ea2e8abb26..f9e5efdadd 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -6,6 +6,7 @@ import com.badlogic.gdx.utils.IntSet; import com.badlogic.gdx.utils.ObjectSet; import com.badlogic.gdx.utils.Queue; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.consumers.Consume; import io.anuke.mindustry.world.consumers.ConsumePower; import io.anuke.mindustry.world.consumers.Consumers; import io.anuke.ucore.util.Mathf; @@ -48,7 +49,10 @@ public class PowerGraph{ for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; if(consumes.has(ConsumePower.class)){ - powerNeeded += consumes.get(ConsumePower.class).requestedPower(consumer.block(), consumer.entity) * consumer.entity.delta(); + ConsumePower consumePower = consumes.get(ConsumePower.class); + if(otherConsumersAreValid(consumer, consumePower)){ + powerNeeded += consumePower.requestedPower(consumer.block(), consumer.entity) * consumer.entity.delta(); + } } } return powerNeeded; @@ -119,12 +123,16 @@ public class PowerGraph{ Consumers consumes = consumer.block().consumes; if(consumes.has(ConsumePower.class)){ ConsumePower consumePower = consumes.get(ConsumePower.class); - if(consumePower.isBuffered){ - // 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); + if(!otherConsumersAreValid(consumer, consumePower)){ + consumer.entity.power.satisfaction = 0.0f; // Only supply power if the consumer would get valid that way }else{ - consumer.entity.power.satisfaction = coverage; + if(consumePower.isBuffered){ + // 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); + }else{ + consumer.entity.power.satisfaction = coverage; + } } } } @@ -222,6 +230,15 @@ public class PowerGraph{ } } + private boolean otherConsumersAreValid(Tile tile, Consume consumePower){ + for(Consume cons : tile.block().consumes.all()){ + if(cons != consumePower && !cons.isOptional() && !cons.valid(tile.block(), tile.entity())){ + return false; + } + } + return true; + } + @Override public String toString(){ return "PowerGraph{" + diff --git a/tests/src/test/java/power/DirectConsumerTests.java b/tests/src/test/java/power/DirectConsumerTests.java new file mode 100644 index 0000000000..92812f8079 --- /dev/null +++ b/tests/src/test/java/power/DirectConsumerTests.java @@ -0,0 +1,57 @@ +package power; + +import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.content.UnitTypes; +import io.anuke.mindustry.type.ItemStack; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.power.PowerGenerator; +import io.anuke.mindustry.world.blocks.power.PowerGraph; +import io.anuke.mindustry.world.blocks.units.UnitFactory; +import org.junit.jupiter.api.Test; +import sun.nio.cs.Surrogate; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** Tests for direct power consumers. */ +public class DirectConsumerTests extends PowerTestFixture{ + + @Test + void noPowerRequestedWithNoItems(){ + testUnitFactory(0, 0, 0.08f, 0.08f, 0.0f); + } + + @Test + void noPowerRequestedWithInsufficientItems(){ + testUnitFactory(30, 0, 0.08f, 0.08f, 0.0f); + testUnitFactory(0, 30, 0.08f, 0.08f, 0.0f); + } + + @Test + void powerRequestedWithSufficientItems(){ + testUnitFactory(30, 30, 0.08f, 0.08f, 1.0f); + } + + void testUnitFactory(int siliconAmount, int leadAmount, float producedPower, float requestedPower, float expectedSatisfaction){ + Tile consumerTile = createFakeTile(0, 0, new UnitFactory("fakefactory"){{ + type = UnitTypes.spirit; + produceTime = 60; + consumes.powerDirect(requestedPower); + consumes.items(new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 30)); + }}); + consumerTile.entity.items.add(Items.silicon, siliconAmount); + consumerTile.entity.items.add(Items.lead, leadAmount); + + Tile producerTile = createFakeTile(2, 0, createFakeProducerBlock(producedPower)); + producerTile.entity().productionEfficiency = 0.5f; // 100% + + PowerGraph graph = new PowerGraph(); + graph.add(producerTile); + graph.add(consumerTile); + + consumerTile.entity.update(); + graph.update(); + + assertEquals(expectedSatisfaction, consumerTile.entity.power.satisfaction); + } +} diff --git a/tests/src/test/java/power/PowerTestFixture.java b/tests/src/test/java/power/PowerTestFixture.java index 5728f6bc8e..92d195399b 100644 --- a/tests/src/test/java/power/PowerTestFixture.java +++ b/tests/src/test/java/power/PowerTestFixture.java @@ -99,7 +99,8 @@ public class PowerTestFixture{ } // Assign incredibly high health so the block does not get destroyed on e.g. burning Blast Compound - block.health *= 100.0f; + block.health = 100000; + tile.entity.health = 100000.0f; return tile; }catch(Exception ex){ From 57bebd3fb6800e54d8d870b1da2f960f6ee5dea9 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Wed, 5 Dec 2018 18:57:52 +0100 Subject: [PATCH 45/67] Removed file which was not supposed to be commited --- .../test/java/power/PowerBalancingTests.java | 94 ------------------- 1 file changed, 94 deletions(-) delete mode 100644 tests/src/test/java/power/PowerBalancingTests.java diff --git a/tests/src/test/java/power/PowerBalancingTests.java b/tests/src/test/java/power/PowerBalancingTests.java deleted file mode 100644 index 2555285985..0000000000 --- a/tests/src/test/java/power/PowerBalancingTests.java +++ /dev/null @@ -1,94 +0,0 @@ -package power; - -import com.badlogic.gdx.math.MathUtils; -import io.anuke.mindustry.content.Items; -import io.anuke.mindustry.content.Liquids; -import io.anuke.mindustry.content.blocks.PowerBlocks; -import io.anuke.mindustry.type.Item; -import io.anuke.mindustry.type.Liquid; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.power.BurnerGenerator; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** Sets expectations to specific production blocks using specific inputs. */ - -public class PowerBalancingTests extends PowerTestFixture{ -// Last updated to values of: v63 - - /** - * Tests the produced power of a power block with a full stack of items or liquids. - * @apiNote Tests only a single tick with a fixed delta and interpolates that to match one second. - * @param powerBlock The block to be tested. - * @param inputItem The item to be supplied (may be null). - * @param inputLiquid The liquid to be supplied (may be null). - * @param expectedPowerPerSecond The amount of power which should be produced per second. - */ - public void testPowerGenerator(Block powerBlock, Item inputItem, Liquid inputLiquid, float expectedPowerPerSecond){ - Tile fakeTile = createFakeTile(0, 0, powerBlock); - if(inputItem != null){ - fakeTile.entity.items.add(inputItem, powerBlock.itemCapacity); - } - if(inputLiquid != null){ - fakeTile.entity.liquids.add(inputLiquid, powerBlock.liquidCapacity); - } - fakeTile.entity.cons.update(fakeTile.entity); - powerBlock.update(fakeTile); - - assertEquals(expectedPowerPerSecond, fakeTile.entity.power.graph.getPowerProduced() * 60f / FakeThreadHandler.fakeDelta, MathUtils.FLOAT_ROUNDING_ERROR * 10.0f); - } - - @Test - public void testCombustionWithCoal(){ - testPowerGenerator(PowerBlocks.combustionGenerator, Items.coal, null, 2.7f); // 100% flammability - } - - @Test - public void testCombustionWithOil(){ - testPowerGenerator(PowerBlocks.combustionGenerator, null, Liquids.oil, 2.7f * 1.2f); // 120% flammability - } - - @Test - public void testCombustionWithBlastCompound(){ - testPowerGenerator(PowerBlocks.combustionGenerator, Items.blastCompound, null, 2.7f * 0.4f); // 40% flammability - } - - @Test - public void testTurbineWithCoal(){ - testPowerGenerator(PowerBlocks.turbineGenerator, Items.coal, Liquids.water, 8.4f); // 100% flammability - } - - @Test - public void testTurbineWithBiomatter(){ - testPowerGenerator(PowerBlocks.turbineGenerator, Items.biomatter, Liquids.water, 8.4f * 0.8f); // 100% flammability - } - - @Test - public void testThermalWithLava(){ - testPowerGenerator(PowerBlocks.thermalGenerator, null, Liquids.lava, 36f); // 100% flammability - } - - @Test - public void testSolarPanel(){ - testPowerGenerator(PowerBlocks.solarPanel, null, null, 0.27f); - } - - @Test - public void testLargeSolarPanel(){ - testPowerGenerator(PowerBlocks.largeSolarPanel, null, null, 3.3f); - } - - @Test - public void testThoriumReactor(){ - testPowerGenerator(PowerBlocks.thoriumReactor, Items.thorium, Liquids.cryofluid, 33.0f); - } - - @Test - public void testRadioIsotopeGenerator(){ - testPowerGenerator(PowerBlocks.rtgGenerator, Items.thorium, null, 9.0f); - } -} From 26e972314ee8a4c3a37cdeb65d2dd1ff4f7cbe7f Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Wed, 5 Dec 2018 22:44:03 +0200 Subject: [PATCH 46/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 8d9e17050a..a1cfb9721e 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -416,6 +416,8 @@ mode.freebuild.name = Вільне\nбудівництво mode.freebuild.description = В режимі "Пісочниця" треба самим добувати ресурси та хвилі йдуть за вашим бажанням. mode.pvp.name = PVP mode.pvp.description = боріться проти інших гравців. +mode.attack.name = Атака +mode.attack.descrption = Немає хвиль, мета - знищити базу противника. content.item.name = Предмети content.liquid.name = Рідини content.unit.name = Бойові одиниці From e726d160941af0be40a964dea9b8f2d821401259 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Thu, 6 Dec 2018 19:49:49 +0100 Subject: [PATCH 47/67] Removed power bars from direct consumers --- core/src/io/anuke/mindustry/world/Block.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index b16d6a2616..839d0af6bd 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -345,7 +345,9 @@ public class Block extends BaseBlock { } public void setBars(){ - if(consumes.has(ConsumePower.class)) bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); + if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ + bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); + } if(hasLiquids) bars.add(new BlockBar(BarType.liquid, true, tile -> tile.entity.liquids.total() / liquidCapacity)); if(hasItems) bars.add(new BlockBar(BarType.inventory, true, tile -> (float) tile.entity.items.total() / itemCapacity)); } From bb30876557c4c0cc0913db6bc67f8b76d8f5b86f Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Fri, 7 Dec 2018 10:13:22 +0200 Subject: [PATCH 48/67] From Ber and --- core/assets/bundles/bundle_uk_UA.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index a1cfb9721e..e76f71cc47 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -468,7 +468,7 @@ mech.delta-mech.description = Швидкий, легкоброньований mech.tau-mech.name = Тау mech.tau-mech.weapon = Відновлювальний лазер mech.tau-mech.ability = Відновлювальний спалах -mech.tau-mech.description = Мех підтримки. Зцілює союзницькі блоки, стріляючи в них. Може зцілити союзників у радіусі зі своєю здатністю для ремонту. +mech.tau-mech.description = Мех підтримки. Лагодить союзницькі блоки, стріляючи в них. Може зцілити союзників у радіусі зі своєю здатністю для ремонту. mech.omega-mech.name = Омега mech.omega-mech.weapon = Ракометна конфігурація mech.omega-mech.ability = Поглинання урона @@ -505,7 +505,7 @@ block.constructing = {0}\n[LIGHT_GRAY](В процесі) block.spawn.name = Місце появи ворога block.core.name = Ядро block.space.name = Пустота -block.metalfloor.name = Металічна підлога +block.metalfloor.name = Металева плитка block.deepwater.name = Глибоководдя block.water.name = Вода block.lava.name = Лава From 10c41bf443fc30c99b4788a4f6b679f0cc256ba9 Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Fri, 7 Dec 2018 10:16:21 +0200 Subject: [PATCH 49/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index e76f71cc47..735f8e878f 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -711,7 +711,7 @@ block.phase-conveyor.description = Поки гра знаходиться в 2D, block.junction.description = Назва говорить сама за себе. За допомогою нього можна зробити дві конвеєрні стрічки, які проходять через один одного і не змішуються. block.mass-driver.description = При наявності енергії передають ресурси на відстань 100 блоків, стріляючи в один-одного. block.smelter.description = Виробляє щільний сплав з міді і свинцю. Можна підвести пісок для прискорення виробництва. -block.arc-smelter.description = Покращена версія плавильного заводу. Вимагає енергію. Виробляє щільний сплав зміді і свинця.\nМожно підвести пісок для прискорення виробництва. +block.arc-smelter.description = Покращена версія плавильного заводу. Вимагає енергію. Виробляє щільний сплав з міді і свинця.\nМожно підвести пісок для прискорення виробництва. block.silicon-smelter.description = За допомогою піску, вугілля і енергії виробляє кремній. block.plastanium-compressor.description = Створює пластинійи з титану і нафти. Вимагає енергії. Для прискорення виробництва можна додати в компресор пісок. block.phase-weaver.description = Виробляє фазову тканину торію і піску. Вимагає багато енергії. From 988df8742c8f4b59e9fc0baf0219db03e84d6e70 Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Fri, 7 Dec 2018 10:27:11 +0200 Subject: [PATCH 50/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 735f8e878f..6267474ac3 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -442,7 +442,7 @@ item.silicon.description = Надзвичайно корисний напівп item.plastanium.name = Пластиній item.plastanium.description = Легкий, пластичний матеріал, що використовується в сучасних літальних апаратах та у боєприпасах для фрагментації. item.phase-fabric.name = Фазова тканина -item.phase-fabric.description = Невагоме речовина, що використовується в сучасній електроніці і технології самовідновлення. +item.phase-fabric.description = Невагоме речовина, що використовується в сучасній електроніці і технології самовідновлення. Непридатна для вишивання. item.surge-alloy.name = Високоміцний сплав item.surge-alloy.description = Передовий сплав з унікальними електричними властивостями. item.biomatter.name = Біоматерія From 787706b1f7284ae729a7b0eba888b9a53caa898c Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Fri, 7 Dec 2018 19:37:51 +0100 Subject: [PATCH 51/67] Set default minimum power to 50% and set four blocks to 100% --- .../content/blocks/DefenseBlocks.java | 4 ++-- .../content/blocks/DistributionBlocks.java | 2 +- .../content/blocks/LiquidBlocks.java | 2 +- .../mindustry/world/consumers/Consumers.java | 22 +++++++++++++++++-- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java index be7c016886..fb7987ca8b 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DefenseBlocks.java @@ -71,13 +71,13 @@ public class DefenseBlocks extends BlockList implements ContentList{ }}; mendProjector = new MendProjector("mend-projector"){{ - consumes.powerDirect(0.2f); + consumes.powerDirect(0.2f, 1.0f); size = 2; consumes.item(Items.phasefabric).optional(true); }}; overdriveProjector = new OverdriveProjector("overdrive-projector"){{ - consumes.powerDirect(0.35f); + consumes.powerDirect(0.35f, 1.0f); size = 2; consumes.item(Items.phasefabric).optional(true); }}; diff --git a/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java b/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java index 4739347791..d0d1621cc5 100644 --- a/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/DistributionBlocks.java @@ -35,7 +35,7 @@ public class DistributionBlocks extends BlockList implements ContentList{ phaseConveyor = new ItemBridge("phase-conveyor"){{ range = 12; hasPower = true; - consumes.powerDirect(0.03f); + consumes.powerDirect(0.03f, 1.0f); }}; sorter = new Sorter("sorter"); diff --git a/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java b/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java index 8be6774a72..1f60004c20 100644 --- a/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/LiquidBlocks.java @@ -67,7 +67,7 @@ public class LiquidBlocks extends BlockList implements ContentList{ phaseConduit = new LiquidBridge("phase-conduit"){{ range = 12; hasPower = true; - consumes.powerDirect(0.03f); + consumes.powerDirect(0.03f, 1.0f); }}; } } diff --git a/core/src/io/anuke/mindustry/world/consumers/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index 8b0f3c7861..a3d5027188 100644 --- a/core/src/io/anuke/mindustry/world/consumers/Consumers.java +++ b/core/src/io/anuke/mindustry/world/consumers/Consumers.java @@ -37,12 +37,12 @@ public class Consumers{ } /** - * Creates a consumer which directly uses power without buffering it. The module will work while at least 60% of power is supplied. + * Creates a consumer which directly uses power without buffering it. The module will work while at least 50% of power is supplied. * @param powerPerTick The amount of power which is required each tick for 100% efficiency. * @return the created consumer object. */ public ConsumePower powerDirect(float powerPerTick){ - return powerDirect(powerPerTick, 0.6f); + return powerDirect(powerPerTick, 0.5f); } /** @@ -122,6 +122,15 @@ public class Consumers{ return map.containsKey(type); } + public boolean hasSubtypeOf(Class type){ + for(Consume consume : all()){ + if(type.isAssignableFrom(consume.getClass())){ + return true; + } + } + return false; + } + public T get(Class type){ if(!map.containsKey(type)){ throw new IllegalArgumentException("Block does not contain consumer of type '" + type + "'!"); @@ -129,6 +138,15 @@ public class Consumers{ return (T) map.get(type); } + public T getSubtypeOf(Class type){ + for(Consume consume : all()){ + if(type.isAssignableFrom(consume.getClass())){ + return (T)consume; + } + } + throw new IllegalArgumentException("Block does not contain consumer of type '" + type + "' or any of its subtypes!"); + } + public Iterable all(){ return map.values(); } From 0a76647175e819aa55608cf02dc8d1b8fed8dc5b Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Fri, 7 Dec 2018 19:38:14 +0100 Subject: [PATCH 52/67] ForceProjector should now work properly --- core/src/io/anuke/mindustry/world/Block.java | 6 +-- .../world/blocks/defense/ForceProjector.java | 38 ++++++++++++++----- .../world/blocks/distribution/MassDriver.java | 2 +- .../world/blocks/power/PowerGraph.java | 29 +++++++------- tests/src/test/java/power/PowerTests.java | 4 +- 5 files changed, 51 insertions(+), 28 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 839d0af6bd..534ee13a52 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -345,7 +345,7 @@ public class Block extends BaseBlock { } public void setBars(){ - if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ + if(consumes.hasSubtypeOf(ConsumePower.class) && consumes.getSubtypeOf(ConsumePower.class).isBuffered){ bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); } if(hasLiquids) bars.add(new BlockBar(BarType.liquid, true, tile -> tile.entity.liquids.total() / liquidCapacity)); @@ -412,8 +412,8 @@ public class Block extends BaseBlock { explosiveness += tile.entity.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2f); } - if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ - power += tile.entity.power.satisfaction * consumes.get(ConsumePower.class).powerCapacity; + if(consumes.hasSubtypeOf(ConsumePower.class) && consumes.getSubtypeOf(ConsumePower.class).isBuffered){ + power += tile.entity.power.satisfaction * consumes.getSubtypeOf(ConsumePower.class).powerCapacity; } tempColor.mul(1f / units); 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 d80eb9546a..79732764a0 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/ForceProjector.java @@ -44,7 +44,7 @@ public class ForceProjector extends Block { protected float cooldownBrokenBase = 0.35f; protected float basePowerDraw = 0.2f; protected float powerDamage = 0.1f; - protected final ConsumePower consumePower; + protected final ConsumeForceProjectorPower consumePower; protected TextureRegion topRegion; @@ -58,7 +58,8 @@ public class ForceProjector extends Block { hasItems = true; itemCapacity = 10; consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).optional(true).update(false); - consumePower = consumes.powerBuffered(60f); + consumePower = new ConsumeForceProjectorPower(60f, 60f); + consumes.add(consumePower); } @Override @@ -104,18 +105,27 @@ public class ForceProjector extends Block { Effects.effect(BlockFx.reactorsmoke, tile.drawx() + Mathf.range(tilesize/2f), tile.drawy() + Mathf.range(tilesize/2f)); } - // Draw base power from buffer manually (ConsumePower doesn't support both filling a buffer and drawing power additionally so we have to do it here) - entity.power.satisfaction -= Math.min(entity.power.satisfaction, basePowerDraw / consumePower.powerCapacity); + // Use Cases: + // - There is enough power in the buffer, and there are no shots fired => Draw base power and keep shield up + // - There is enough power in the buffer, but not enough power to cope for shots being fired => Draw all power and break shield + // - There is enough power in the buffer and enough power to cope for shots being fired => Draw base power + additional power based on shots absorbed + // - 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 - if(entity.power.satisfaction == 0.0f && !cheat){ + float relativePowerDraw = 0.0f; + if(!cheat){ + relativePowerDraw = basePowerDraw / consumePower.powerCapacity; + } + + if(entity.power.satisfaction < relativePowerDraw){ entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f); + entity.power.satisfaction = .0f; if(entity.warmup <= 0.09f){ entity.broken = true; } }else{ entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, 0.1f); - float relativePowerDraw = powerDamage * entity.delta() * (1f + entity.buildup / breakage) / consumePower.powerCapacity; - entity.power.satisfaction -= Math.min(relativePowerDraw, entity.power.satisfaction); + entity.power.satisfaction -= Math.min(entity.power.satisfaction, relativePowerDraw); } if(entity.buildup > 0){ @@ -150,10 +160,10 @@ public class ForceProjector extends Block { if(trait.canBeAbsorbed() && trait.getTeam() != tile.getTeam() && isInsideHexagon(trait.getX(), trait.getY(), realRadius * 2f, tile.drawx(), tile.drawy())){ trait.absorb(); Effects.effect(BulletFx.absorb, trait); - float relativePowerDraw = trait.getShieldDamage() * powerDamage / consumePower.powerCapacity; + float relativeDamagePowerDraw = trait.getShieldDamage() * powerDamage / consumePower.powerCapacity; entity.hit = 1f; - entity.power.satisfaction -= Math.min(relativePowerDraw, entity.power.satisfaction); + entity.power.satisfaction -= Math.min(relativeDamagePowerDraw, entity.power.satisfaction); if(entity.power.satisfaction <= 0.0001f){ entity.buildup += trait.getShieldDamage() * entity.warmup * 2f; } @@ -264,4 +274,14 @@ public class ForceProjector extends Block { return shieldGroup; } } + + public class ConsumeForceProjectorPower extends ConsumePower{ + public ConsumeForceProjectorPower(float powerCapacity, float ticksToFill){ + super(powerCapacity / ticksToFill, 0.0f, powerCapacity, true); + } + @Override + public boolean valid(Block block, TileEntity entity){ + return entity.power.satisfaction >= basePowerDraw / powerCapacity && super.valid(block, entity); + } + } } 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 390f13f46b..80eea77ef8 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -131,7 +131,7 @@ public class MassDriver extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.powerShot, consumes.get(ConsumePower.class).powerCapacity * powerPercentageUsed, StatUnit.powerUnits); + stats.add(BlockStat.powerShot, consumes.getSubtypeOf(ConsumePower.class).powerCapacity * powerPercentageUsed, StatUnit.powerUnits); } @Override 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 a83f210c7b..45311afcd5 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -48,8 +48,8 @@ public class PowerGraph{ float powerNeeded = 0f; for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; - if(consumes.has(ConsumePower.class)){ - ConsumePower consumePower = consumes.get(ConsumePower.class); + if(consumes.hasSubtypeOf(ConsumePower.class)){ + ConsumePower consumePower = consumes.getSubtypeOf(ConsumePower.class); if(otherConsumersAreValid(consumer, consumePower)){ powerNeeded += consumePower.requestedPower(consumer.block(), consumer.entity) * consumer.entity.delta(); } @@ -62,8 +62,8 @@ public class PowerGraph{ float totalAccumulator = 0f; for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.has(ConsumePower.class)){ - totalAccumulator += battery.entity.power.satisfaction * consumes.get(ConsumePower.class).powerCapacity; + if(consumes.hasSubtypeOf(ConsumePower.class)){ + totalAccumulator += battery.entity.power.satisfaction * consumes.getSubtypeOf(ConsumePower.class).powerCapacity; } } return totalAccumulator; @@ -73,8 +73,8 @@ public class PowerGraph{ float totalCapacity = 0f; for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.has(ConsumePower.class)){ - totalCapacity += consumes.get(ConsumePower.class).requestedPower(battery.block(), battery.entity) * battery.entity.delta(); + if(consumes.hasSubtypeOf(ConsumePower.class)){ + totalCapacity += consumes.getSubtypeOf(ConsumePower.class).requestedPower(battery.block(), battery.entity) * battery.entity.delta(); } } return totalCapacity; @@ -88,8 +88,8 @@ public class PowerGraph{ float consumedPowerPercentage = Math.min(1.0f, needed / stored); for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.has(ConsumePower.class)){ - ConsumePower consumePower = consumes.get(ConsumePower.class); + if(consumes.hasSubtypeOf(ConsumePower.class)){ + ConsumePower consumePower = consumes.getSubtypeOf(ConsumePower.class); if(consumePower.powerCapacity > 0f){ battery.entity.power.satisfaction = Math.max(0.0f, battery.entity.power.satisfaction - consumedPowerPercentage); } @@ -104,8 +104,8 @@ public class PowerGraph{ for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.has(ConsumePower.class)){ - ConsumePower consumePower = consumes.get(ConsumePower.class); + if(consumes.hasSubtypeOf(ConsumePower.class)){ + ConsumePower consumePower = consumes.getSubtypeOf(ConsumePower.class); 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); @@ -121,8 +121,8 @@ public class PowerGraph{ float coverage = Math.min(1, produced / needed); for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; - if(consumes.has(ConsumePower.class)){ - ConsumePower consumePower = consumes.get(ConsumePower.class); + if(consumes.hasSubtypeOf(ConsumePower.class)){ + ConsumePower consumePower = consumes.getSubtypeOf(ConsumePower.class); if(!otherConsumersAreValid(consumer, consumePower)){ consumer.entity.power.satisfaction = 0.0f; // Only supply power if the consumer would get valid that way }else{ @@ -180,7 +180,10 @@ public class PowerGraph{ public void clear(){ for(Tile other : all){ - if(other.entity != null && other.entity.power != null){ other.entity.power.graph = null; } + if(other.entity != null && other.entity.power != null){ + other.entity.power.satisfaction = 0.0f; + other.entity.power.graph = null; + } } all.clear(); producers.clear(); diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index a79089ab91..64bfa2e451 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -174,8 +174,8 @@ public class PowerTests extends PowerTestFixture{ powerGraph.update(); assertEquals(0.0f, consumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR); - if(consumerTile.block().consumes.has(ConsumePower.class)){ - ConsumePower consumePower = consumerTile.block().consumes.get(ConsumePower.class); + if(consumerTile.block().consumes.hasSubtypeOf(ConsumePower.class)){ + ConsumePower consumePower = consumerTile.block().consumes.getSubtypeOf(ConsumePower.class); assertFalse(consumePower.valid(consumerTile.block(), consumerTile.entity())); } } From a292ff4287144cf78ed56c4cbf8ceac1c4cfcfa3 Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Fri, 14 Dec 2018 18:24:06 +0200 Subject: [PATCH 53/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 24 ++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 6267474ac3..96a12886a4 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -24,9 +24,9 @@ text.level.select = Вибір мапи text.level.mode = Режим гри: text.construction.desktop = Щоб скасувати вибір блоку або припинити будівництво, [accent] скористайтеся пробілом[]. text.construction.title = Інструкція з будівництва блоків -text.construction = Ви тільки що перейшли в режим будівництва[accent] блоків[].\n\nЩоб розпочати розміщення, просто торкніться підходящого місця поруч із вашим кораблем.\nПісля вибору деяких блоків натисніть прапорець, щоб підтвердити, і ваш корабель почне будувати їх.\n\n- [accent]Вилучіть блоки[] з вашого вибору, торкнувшись їх.\n- [accent]Перемістіть виділення[] утримуючи та перетягнувши будь-який блок у виділенні.\n- [accent]Розташуйте блоки у лінію[], торкнувшись і утримуючи порожнє місце, а потім перетягуючи в потрібному напрямку.\n- [accent]Скасуйте розміщення блоків[] натиснувши X внизу ліворуч. +text.construction = Ви тільки що перейшли в режим будівництва[accent] блоків[].\n\nЩоб розпочати розміщення, просто торкніться підходящого місця поруч із вашим кораблем.\nПісля вибору деяких блоків натисніть прапорець, щоб підтвердити, і ваш корабель почне будувати їх.\n\n- [accent]Вилучіть блоки[] з вашого вибору, торкнувшись їх.\n- [accent]Перемістіть виділення[] утримуючи та перетягнувши будь-який блок у виділенні.\n- [accent]Розташуйте блоки у лінію[], торкнувшись і утримуючи порожнє місце, а потім перетягуючи в потрібному напрямку.\n- [accent]Скасуйте розміщення блоків[] натиснувши X внизу праворуч. text.deconstruction.title = Інструкція з деконструкції блоків -text.deconstruction = Ви тільки що перешли в [accent] режим деконструкції блоків[].\n\nЩоб почати руйнувати, просто торкніться блоку поруч із вашим кораблем.\nПісля вибору деяких блоків натисніть прапорець, щоб підтвердити, і ваш корабель почне їх деконструювати.\n\n- [accent]Вилучіть блоки[] з вашого вибору, торкнувшись їх.\n- [accent]Вилучіть блоки в зоні[] , торкнувшись і утримуючи порожнє місце, потім перетягніть у потрібному напрямку.\n- [accent]Скасуйте деконструкцію або виділення[] натиснувши X внизу ліворуч. +text.deconstruction = Ви тільки що перешли в [accent] режим деконструкції блоків[].\n\nЩоб почати руйнувати, просто торкніться блоку поруч із вашим кораблем.\nПісля вибору деяких блоків натисніть прапорець, щоб підтвердити, і ваш корабель почне їх деконструювати.\n\n- [accent]Вилучіть блоки[] з вашого вибору, торкнувшись їх.\n- [accent]Вилучіть блоки в зоні[] , торкнувшись і утримуючи порожнє місце, потім перетягніть у потрібному напрямку.\n- [accent]Скасуйте деконструкцію або виділення[] натиснувши X внизу праворуч. text.showagain = Не показувати знову до наступного сеансу text.coreattack = < Ядро під атакою! > text.unlocks = Розблоковане @@ -40,7 +40,7 @@ text.sector = Обраний сектор: [LIGHT_GRAY]{0} text.sector.time = Час: [LIGHT_GRAY]{0} text.sector.deploy = Висадитися text.sector.abandon = Відступити -text.sector.abandon.confirm = Ви впевнені, що хочете відступити?\nЦе не може бути скасовано! +text.sector.abandon.confirm = Ви впевнені, що хочете відступити?\nПрогрес в секторі не можна відновити! text.sector.resume = Продовжити text.sector.locked = [scarlet][[Незавершений] text.sector.unexplored = [accent][[Недосліджений] @@ -437,12 +437,12 @@ item.titanium.name = Титан item.titanium.description = Рідкий суперлегкий метал широко використовується в рідкому транспорті, свердлах та літальних апаратах. item.thorium.name = Торій item.thorium.description = Густий, радіоактивний метал, що використовується як структурна підтримка та ядерне паливо. -item.silicon.name = Кремень +item.silicon.name = Кремній item.silicon.description = Надзвичайно корисний напівпровідник з застосуванням в сонячних батареях та складній електроніці. item.plastanium.name = Пластиній item.plastanium.description = Легкий, пластичний матеріал, що використовується в сучасних літальних апаратах та у боєприпасах для фрагментації. item.phase-fabric.name = Фазова тканина -item.phase-fabric.description = Невагоме речовина, що використовується в сучасній електроніці і технології самовідновлення. Непридатна для вишивання. +item.phase-fabric.description = Невагоме речовина, що використовується в сучасній електроніці і технології самовідновлення. Не для вишивання. item.surge-alloy.name = Високоміцний сплав item.surge-alloy.description = Передовий сплав з унікальними електричними властивостями. item.biomatter.name = Біоматерія @@ -470,7 +470,7 @@ mech.tau-mech.weapon = Відновлювальний лазер mech.tau-mech.ability = Відновлювальний спалах mech.tau-mech.description = Мех підтримки. Лагодить союзницькі блоки, стріляючи в них. Може зцілити союзників у радіусі зі своєю здатністю для ремонту. mech.omega-mech.name = Омега -mech.omega-mech.weapon = Ракометна конфігурація +mech.omega-mech.weapon = Ракометний пулемет mech.omega-mech.ability = Поглинання урона mech.omega-mech.description = Громіздкий і добре броньований мех, зроблений для фронтових нападів. Його здатність може блокувати до 90% вхідного урона. mech.dart-ship.name = Дротик @@ -670,7 +670,7 @@ tutorial.silicondrill = Кремній потребує[accent] вугілляl[ tutorial.generator = Ця технологія потребує енергії.\nЗробіть[accent] генератор внутрішнього згорання[] для цього. tutorial.generatordrill = Генератор потребує вугілля.\nПобудуйте бур на вугільній жилі. tutorial.node = Енергії потребує транспортування\nСоздайте[accent] силовий вузол[] поруч з вашим генератором згорання, щоб передавати його енергію. -tutorial.nodelink = Енергія може бути передана через контактні енергетичні блоки та генератори, або з'єднані силові вузли.\n\nЗ'єднайте живлення, торкнувшись вузла та вибравши генератор і кремнієвий завод. +tutorial.nodelink = Енергія може бути передана через контактні енергетичні блоки та генератори, або з'єднані силові вузли.\n\nЗ'єднайте їх, торкнувшись вузла та вибравши генератор і кремнієвий завод. tutorial.silicon = Кремній почався створюватися. Отримайте трохи.\n\nРекомендується вдосконалити виробничу систему. tutorial.daggerfactory = Побудуйте[accent] завод "Розвідник".[]\n\nЦе буде використано для створення мехів атаки. tutorial.router = Фабрики потребують ресурсів для функціонування.\nСтворіть маршрутизатор для розподілення ресурсів з конвейера. @@ -778,11 +778,11 @@ block.router.description = Приймає елементи з одного на block.distributor.description = Розширений маршрутизатор, який рівномірно розбиває елементи на 7 різних напрямків. block.bridge-conveyor.description = Покращений блок транспортування предметів. Дозволяє транспортувати предмети понад 3 блоки над будь-якої місцевостю або будівлеє. block.alpha-mech-pad.description = Коли ви отримаєте достатньо енергії, перебудовує ваш корабель у [accent] Альфа[] мех. -block.itemsource.description = Безліченно виводить предмети. Лише пісочниця. -block.liquidsource.description = Безліченно виводить рідини. Лише пісочниця. -block.itemvoid.description = Знищує будь-які предмети, які входять, без використання енергії. Працює тільки в пісочниці. -block.powerinfinite.description = Нескінченність не межа. Безмежно виводить енергію. Лише пісочниця. -block.powervoid.description = Енергія просто йде в порожнечу. Лише пісочниця. +block.itemsource.description = Безліченно виводить предмети. +block.liquidsource.description = Безліченно виводить рідини. +block.itemvoid.description = Знищує будь-які предмети, які входять, без використання енергії. +block.powerinfinite.description = Нескінченність не межа. Безмежно виводить енергію. +block.powervoid.description = Енергія просто йде в порожнечу. liquid.water.description = Цю рідину можно підвести до бурів для прискорення швидкості видобутку або к турелям для прискорення стрілянини. liquid.lava.description = Можна перетворити в[LIGHT_GRAY] камінь[]. liquid.oil.description = Можна спалити, взірвати або використовувати для охолодження. From 7a533f60535d9e38513a79b8fbb64c00648521ba Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Fri, 14 Dec 2018 18:28:40 +0200 Subject: [PATCH 54/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 96a12886a4..f48ed703a1 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -383,6 +383,8 @@ category.multiplayer.name = Мультиплеєр command.attack = Атакувати command.retreat = Відступити command.patrol = Патрулювати +keybind.gridMode.name = Вибрати блок +keybind.gridModeShift.name = Вибрати категорію keybind.press = Натисніть клавішу... keybind.press.axis = Натисніть клавішу... keybind.screenshot.name = Скріншот мапи From 23fb15fe78ded180d20f13221d54b2432b739428 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Fri, 14 Dec 2018 21:14:19 +0100 Subject: [PATCH 55/67] Buffered consumers will no longer have their buffer reset when being unlinked --- .../io/anuke/mindustry/world/blocks/power/PowerGraph.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 45311afcd5..3cbf21c096 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -181,7 +181,10 @@ public class PowerGraph{ public void clear(){ for(Tile other : all){ if(other.entity != null && other.entity.power != null){ - other.entity.power.satisfaction = 0.0f; + if(other.block().consumes.hasSubtypeOf(ConsumePower.class) && !other.block().consumes.getSubtypeOf(ConsumePower.class).isBuffered){ + // Reset satisfaction to zero in case of direct consumer. There is no reason to clear power from buffered consumers. + other.entity.power.satisfaction = 0.0f; + } other.entity.power.graph = null; } } From fed11db92e4e6d3022ebf21c231a8725669448e7 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Sat, 15 Dec 2018 00:47:47 +0100 Subject: [PATCH 56/67] Buffered consumers now fill instantly --- core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java | 4 ++-- core/src/io/anuke/mindustry/world/consumers/Consumers.java | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java index cabd8bfe92..624900db5a 100644 --- a/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/PowerBlocks.java @@ -59,12 +59,12 @@ public class PowerBlocks extends BlockList implements ContentList{ }}; battery = new Battery("battery"){{ - consumes.powerBuffered(320f, 120f); + consumes.powerBuffered(320f, 1f); }}; batteryLarge = new Battery("battery-large"){{ size = 3; - consumes.powerBuffered(2000f, 600f); + consumes.powerBuffered(2000f, 1f); }}; powerNode = new PowerNode("power-node"){{ diff --git a/core/src/io/anuke/mindustry/world/consumers/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index a3d5027188..eff6a77107 100644 --- a/core/src/io/anuke/mindustry/world/consumers/Consumers.java +++ b/core/src/io/anuke/mindustry/world/consumers/Consumers.java @@ -58,12 +58,11 @@ public class Consumers{ /** * Creates a consumer which stores power and uses it only in case of certain events (e.g. a turret firing). - * It will take 60 ticks (one second) to fill the buffer, given enough power supplied. + * 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){ - // TODO Balance: How long should it take to fill a buffer? The lower this value, the more power will be "stolen" from direct consumers. - return powerBuffered(powerCapacity, 60f); + return powerBuffered(powerCapacity, 1f); } /** From 828661e9dedf4b27166ba40cd6b1e17e77ff6781 Mon Sep 17 00:00:00 2001 From: Timmeey86 Date: Sat, 15 Dec 2018 23:49:46 +0100 Subject: [PATCH 57/67] More efficient way of retrieving ConsumePower subclasses --- core/src/io/anuke/mindustry/world/Block.java | 6 ++--- .../world/blocks/distribution/MassDriver.java | 2 +- .../world/blocks/power/PowerGraph.java | 26 +++++++++---------- .../mindustry/world/consumers/Consumers.java | 23 ++-------------- tests/src/test/java/power/PowerTests.java | 4 +-- 5 files changed, 21 insertions(+), 40 deletions(-) diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index 534ee13a52..839d0af6bd 100644 --- a/core/src/io/anuke/mindustry/world/Block.java +++ b/core/src/io/anuke/mindustry/world/Block.java @@ -345,7 +345,7 @@ public class Block extends BaseBlock { } public void setBars(){ - if(consumes.hasSubtypeOf(ConsumePower.class) && consumes.getSubtypeOf(ConsumePower.class).isBuffered){ + if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ bars.add(new BlockBar(BarType.power, true, tile -> tile.entity.power.satisfaction)); } if(hasLiquids) bars.add(new BlockBar(BarType.liquid, true, tile -> tile.entity.liquids.total() / liquidCapacity)); @@ -412,8 +412,8 @@ public class Block extends BaseBlock { explosiveness += tile.entity.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2f); } - if(consumes.hasSubtypeOf(ConsumePower.class) && consumes.getSubtypeOf(ConsumePower.class).isBuffered){ - power += tile.entity.power.satisfaction * consumes.getSubtypeOf(ConsumePower.class).powerCapacity; + if(consumes.has(ConsumePower.class) && consumes.get(ConsumePower.class).isBuffered){ + power += tile.entity.power.satisfaction * consumes.get(ConsumePower.class).powerCapacity; } tempColor.mul(1f / units); 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 80eea77ef8..390f13f46b 100644 --- a/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/io/anuke/mindustry/world/blocks/distribution/MassDriver.java @@ -131,7 +131,7 @@ public class MassDriver extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.powerShot, consumes.getSubtypeOf(ConsumePower.class).powerCapacity * powerPercentageUsed, StatUnit.powerUnits); + stats.add(BlockStat.powerShot, consumes.get(ConsumePower.class).powerCapacity * powerPercentageUsed, StatUnit.powerUnits); } @Override 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 3cbf21c096..b9d36c2894 100644 --- a/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java +++ b/core/src/io/anuke/mindustry/world/blocks/power/PowerGraph.java @@ -48,8 +48,8 @@ public class PowerGraph{ float powerNeeded = 0f; for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; - if(consumes.hasSubtypeOf(ConsumePower.class)){ - ConsumePower consumePower = consumes.getSubtypeOf(ConsumePower.class); + if(consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumes.get(ConsumePower.class); if(otherConsumersAreValid(consumer, consumePower)){ powerNeeded += consumePower.requestedPower(consumer.block(), consumer.entity) * consumer.entity.delta(); } @@ -62,8 +62,8 @@ public class PowerGraph{ float totalAccumulator = 0f; for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.hasSubtypeOf(ConsumePower.class)){ - totalAccumulator += battery.entity.power.satisfaction * consumes.getSubtypeOf(ConsumePower.class).powerCapacity; + if(consumes.has(ConsumePower.class)){ + totalAccumulator += battery.entity.power.satisfaction * consumes.get(ConsumePower.class).powerCapacity; } } return totalAccumulator; @@ -73,8 +73,8 @@ public class PowerGraph{ float totalCapacity = 0f; for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.hasSubtypeOf(ConsumePower.class)){ - totalCapacity += consumes.getSubtypeOf(ConsumePower.class).requestedPower(battery.block(), battery.entity) * battery.entity.delta(); + if(consumes.has(ConsumePower.class)){ + totalCapacity += consumes.get(ConsumePower.class).requestedPower(battery.block(), battery.entity) * battery.entity.delta(); } } return totalCapacity; @@ -88,8 +88,8 @@ public class PowerGraph{ float consumedPowerPercentage = Math.min(1.0f, needed / stored); for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.hasSubtypeOf(ConsumePower.class)){ - ConsumePower consumePower = consumes.getSubtypeOf(ConsumePower.class); + if(consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumes.get(ConsumePower.class); if(consumePower.powerCapacity > 0f){ battery.entity.power.satisfaction = Math.max(0.0f, battery.entity.power.satisfaction - consumedPowerPercentage); } @@ -104,8 +104,8 @@ public class PowerGraph{ for(Tile battery : batteries){ Consumers consumes = battery.block().consumes; - if(consumes.hasSubtypeOf(ConsumePower.class)){ - ConsumePower consumePower = consumes.getSubtypeOf(ConsumePower.class); + if(consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumes.get(ConsumePower.class); 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); @@ -121,8 +121,8 @@ public class PowerGraph{ float coverage = Math.min(1, produced / needed); for(Tile consumer : consumers){ Consumers consumes = consumer.block().consumes; - if(consumes.hasSubtypeOf(ConsumePower.class)){ - ConsumePower consumePower = consumes.getSubtypeOf(ConsumePower.class); + if(consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumes.get(ConsumePower.class); if(!otherConsumersAreValid(consumer, consumePower)){ consumer.entity.power.satisfaction = 0.0f; // Only supply power if the consumer would get valid that way }else{ @@ -181,7 +181,7 @@ public class PowerGraph{ public void clear(){ for(Tile other : all){ if(other.entity != null && other.entity.power != null){ - if(other.block().consumes.hasSubtypeOf(ConsumePower.class) && !other.block().consumes.getSubtypeOf(ConsumePower.class).isBuffered){ + if(other.block().consumes.has(ConsumePower.class) && !other.block().consumes.get(ConsumePower.class).isBuffered){ // Reset satisfaction to zero in case of direct consumer. There is no reason to clear power from buffered consumers. other.entity.power.satisfaction = 0.0f; } diff --git a/core/src/io/anuke/mindustry/world/consumers/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index c647b233e0..9391503750 100644 --- a/core/src/io/anuke/mindustry/world/consumers/Consumers.java +++ b/core/src/io/anuke/mindustry/world/consumers/Consumers.java @@ -109,7 +109,7 @@ public class Consumers{ } public Consume add(Consume consume){ - map.put(consume.getClass(), consume); + map.put((consume instanceof ConsumePower ? ConsumePower.class : consume.getClass()), consume); return consume; } @@ -121,32 +121,13 @@ public class Consumers{ return map.containsKey(type); } - public boolean hasSubtypeOf(Class type){ - for(Consume consume : all()){ - if(type.isAssignableFrom(consume.getClass())){ - return true; - } - } - return false; - } - @SuppressWarnings("unchecked") public T get(Class type){ if(!map.containsKey(type)){ throw new IllegalArgumentException("Block does not contain consumer of type '" + type + "'!"); } return (T) map.get(type); - } - - @SuppressWarnings("unchecked") - public T getSubtypeOf(Class type){ - for(Consume consume : all()){ - if(type.isAssignableFrom(consume.getClass())){ - return (T)consume; - } - } - throw new IllegalArgumentException("Block does not contain consumer of type '" + type + "' or any of its subtypes!"); - } + }< public Iterable all(){ return map.values(); diff --git a/tests/src/test/java/power/PowerTests.java b/tests/src/test/java/power/PowerTests.java index 64bfa2e451..a79089ab91 100644 --- a/tests/src/test/java/power/PowerTests.java +++ b/tests/src/test/java/power/PowerTests.java @@ -174,8 +174,8 @@ public class PowerTests extends PowerTestFixture{ powerGraph.update(); assertEquals(0.0f, consumerTile.entity.power.satisfaction, MathUtils.FLOAT_ROUNDING_ERROR); - if(consumerTile.block().consumes.hasSubtypeOf(ConsumePower.class)){ - ConsumePower consumePower = consumerTile.block().consumes.getSubtypeOf(ConsumePower.class); + if(consumerTile.block().consumes.has(ConsumePower.class)){ + ConsumePower consumePower = consumerTile.block().consumes.get(ConsumePower.class); assertFalse(consumePower.valid(consumerTile.block(), consumerTile.entity())); } } From 46df7726c3b2a1346f13fb4c01def046435845fd Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Sat, 22 Dec 2018 11:38:01 +0200 Subject: [PATCH 58/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index f48ed703a1..8347257f85 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -267,7 +267,7 @@ text.connectfail = [crimson]Не вдалося підключитися до с text.error.unreachable = Сервер не доступний. text.error.invalidaddress = Некоректна адреса. text.error.timedout = Час очікувування вийшов.\nПереконайтеся, що адреса коректна і що власник сервера налаштував переадресацію порту! -text.error.mismatch = Ошибка пакету:\nможливе невідповідність версії клієнта / сервера.\nПереконайтеся, що у Вас та у володара сервера встановлена остання версія Mindustry! +text.error.mismatch = Помилка пакету:\nможливе невідповідність версії клієнта / сервера.\nПереконайтеся, що у Вас та у володара сервера встановлена остання версія Mindustry! text.error.alreadyconnected = Ви вже підключилися. text.error.mapnotfound = Файл мапи не знайдено text.error.any = Невідома мережева помилка From 572f358bdaeb78656903b3b3fa1400b84528970a Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Sat, 22 Dec 2018 16:47:57 +0200 Subject: [PATCH 59/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 8347257f85..39196864ff 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -445,7 +445,7 @@ item.plastanium.name = Пластиній item.plastanium.description = Легкий, пластичний матеріал, що використовується в сучасних літальних апаратах та у боєприпасах для фрагментації. item.phase-fabric.name = Фазова тканина item.phase-fabric.description = Невагоме речовина, що використовується в сучасній електроніці і технології самовідновлення. Не для вишивання. -item.surge-alloy.name = Високоміцний сплав +item.surge-alloy.name = Кінетичний сплав item.surge-alloy.description = Передовий сплав з унікальними електричними властивостями. item.biomatter.name = Біоматерія item.biomatter.description = Скупчення органічної муси; використовується для перетворення в нафту або як паливо. @@ -717,7 +717,7 @@ block.arc-smelter.description = Покращена версія плавильн block.silicon-smelter.description = За допомогою піску, вугілля і енергії виробляє кремній. block.plastanium-compressor.description = Створює пластинійи з титану і нафти. Вимагає енергії. Для прискорення виробництва можна додати в компресор пісок. block.phase-weaver.description = Виробляє фазову тканину торію і піску. Вимагає багато енергії. -block.alloy-smelter.description = Створює високоміцний сплав з титану, кременя, міді і свинця. Вимагає енергію. +block.alloy-smelter.description = Створює кінетичний сплав з титану, кременя, міді і свинця. Вимагає енергію. block.pulverizer.description = Подрібнює камінь в пісок. Вимагає енергії. block.pyratite-mixer.description = Створює піротит з вугілля, свинцю і піску. Вимагає енергії. block.blast-mixer.description = Створює вибухонебезпечне з'єднання з нафти і піротіта. Для прискорення виробництва можна додати в мішалку пісок. From 84f60f63b85236c5cab52369dcb054b3830449f7 Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Sat, 22 Dec 2018 17:11:52 +0200 Subject: [PATCH 60/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 39196864ff..b5993e0227 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -185,7 +185,7 @@ text.quit.confirm = Ви впевнені що хочете вийти? text.changelog.title = Журнал змін text.changelog.loading = Отримання журналу змін... text.changelog.error.android = [accent]Зверніть увагу, що іноді журнал змін не працює на ОС Android 4.4 або на нижчій версії!\nЦе пов'язано з внутрішньою помилкою Android. -text.changelog.error.ios = [accent]В настоящее время журнал изменений не поддерживается iOS. +text.changelog.error.ios = [accent]Журнал змін наразі не підтримується iOS. text.changelog.error = [scarlet]Помилка отримання журналу змін!\nПеревірте підключення до Інтернету. text.changelog.current = [yellow][[Поточна версія] text.changelog.latest = [accent][[Остання версія] From 1f4e611629c87b1dba2c12b3ec271bcbbdcaad62 Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Wed, 26 Dec 2018 22:45:13 +0200 Subject: [PATCH 61/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index b5993e0227..a1d38f12d0 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -32,7 +32,7 @@ text.coreattack = < Ядро під атакою! > text.unlocks = Розблоковане text.savegame = Зберегти гру text.loadgame = Завантажити гру -text.joingame = Приєднатися +text.joingame = Мережева гра text.addplayers = Дод/Видалити гравців text.customgame = Користувальницька гра text.sectors = Сектори @@ -379,7 +379,7 @@ setting.crashreport.name = Надіслати анонімні звіти про text.keybind.title = Налаштування управління category.general.name = Основне category.view.name = Перегляд -category.multiplayer.name = Мультиплеєр +category.multiplayer.name = Мережева гра command.attack = Атакувати command.retreat = Відступити command.patrol = Патрулювати From f22caed0888c7f03f693be6b42415393bf7cc3bd Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Wed, 26 Dec 2018 22:49:18 +0200 Subject: [PATCH 62/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index a1d38f12d0..dd8c00c64d 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -601,10 +601,10 @@ block.oil-extractor.name = Екстрактор нафти block.spirit-factory.name = Завод дронов "Призрак" block.phantom-factory.name = Завод дронов "Фантом" block.wraith-factory.name = Завод винищувачів "Примара" -block.ghoul-factory.name = Завод бомбардувальників "Ґуль" -block.dagger-factory.name = Завод мехов "Разведчик" -block.titan-factory.name = Завод мехов "Титан" -block.fortress-factory.name = Завод мехов "Крепость" +block.ghoul-factory.name = Завод бомбардувальників-винищувачів "Ґуль" +block.dagger-factory.name = Завод мехів "Кинджал" +block.titan-factory.name = Завод мехів "Титан" +block.fortress-factory.name = Завод мехів "Крепость" block.revenant-factory.name = Завод бомбардировщиков "Потусторонний убийца" block.repair-point.name = Ремонтний пункт block.pulse-conduit.name = Імпульсний водовід @@ -645,7 +645,7 @@ unit.spirit.name = Дрон-привид unit.spirit.description = Початковий дрон. З'являється в ядрі за замовчуванням. Автоматично добуває руди та ремонтує блоки. unit.phantom.name = Фантом unit.phantom.description = Покращений дрон. Автоматично добуває руди та ремонтує блоки. -unit.dagger.name = Розвідник +unit.dagger.name = Кинджал unit.dagger.description = Базова наземна бойова одиниця. Корисен у купі. unit.titan.name = Титан unit.titan.description = Улучшенная бронированная наземная боевая единица. Атакует наземные и воздушные цели. @@ -674,7 +674,7 @@ tutorial.generatordrill = Генератор потребує вугілля.\n tutorial.node = Енергії потребує транспортування\nСоздайте[accent] силовий вузол[] поруч з вашим генератором згорання, щоб передавати його енергію. tutorial.nodelink = Енергія може бути передана через контактні енергетичні блоки та генератори, або з'єднані силові вузли.\n\nЗ'єднайте їх, торкнувшись вузла та вибравши генератор і кремнієвий завод. tutorial.silicon = Кремній почався створюватися. Отримайте трохи.\n\nРекомендується вдосконалити виробничу систему. -tutorial.daggerfactory = Побудуйте[accent] завод "Розвідник".[]\n\nЦе буде використано для створення мехів атаки. +tutorial.daggerfactory = Побудуйте[accent] завод "Кинджал".[]\n\nЦе буде використано для створення мехів атаки. tutorial.router = Фабрики потребують ресурсів для функціонування.\nСтворіть маршрутизатор для розподілення ресурсів з конвейера. tutorial.dagger = Зв'яжіть силовий вузол з заводом.\nЯк тільки вимоги будуть виконані, буде створено мех.\n\nЯкщо необхідно, то створіть ще бурів, генераторів та конвейерів tutorial.battle = [LIGHT_GRAY] Супротивник[] показав своє ядро.\nЗнищьте його з вашим мехом та бойовою одиницею. From 197b97d0db3c588a4f5f357b3451625d04788be8 Mon Sep 17 00:00:00 2001 From: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com> Date: Wed, 2 Jan 2019 13:26:36 +0200 Subject: [PATCH 63/67] Update bundle_uk_UA.properties --- core/assets/bundles/bundle_uk_UA.properties | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index dd8c00c64d..c78427623f 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -93,6 +93,7 @@ text.server.kicked.nameInUse = На цьому сервері є хтось \nз text.server.kicked.nameEmpty = Ваш нікнейм має містити принаймні один символ або цифру. text.server.kicked.idInUse = Ви вже на цьому сервері! Підключення двох облікових записів не допускається. text.server.kicked.customClient = Цей сервер не підтримує користувальницькі збірки. Завантажте офіційну версію. +text.server.kicked.gameover = Гра завершена! text.host.info = Кнопка [accent]Сервер[] розміщує сервер на порті [scarlet]6567[]. \nКористувачі, які знаходяться у тій же [LIGHT_GRAY] WiFi або локальній мережі[] повинні бачити ваш сервер у своєму списку серверів.\n\nЯкщо ви хочете, щоб люди могли приєднуватися з будь-якої точки через IP, то [accent] переадресація порту [] обов'язкова.\n\n[LIGHT_GRAY] Примітка. Якщо у вас виникли проблеми з підключенням до вашої локальної гри, переконайтеся, що ви дозволили Mindustry доступ до вашої локальної мережі в налаштуваннях брандмауера. text.join.info = Тут ви можете ввести [accent]IP серверу[] для підключення або знайти сервери у [accent]локальній мережі[] для підключення до них.\nПідтримується локальна мережа(LAN) і широкосмугова мережа(WAN).\n\n[LIGHT_GRAY] Примітка. Тут немає автоматичного глобального списку серверів; якщо ви хочете підключитися до когось через IP, вам доведеться попросити створювача серверу дати свій ip. text.hostserver = Запустити сервер @@ -270,6 +271,7 @@ text.error.timedout = Час очікувування вийшов.\nПерек text.error.mismatch = Помилка пакету:\nможливе невідповідність версії клієнта / сервера.\nПереконайтеся, що у Вас та у володара сервера встановлена остання версія Mindustry! text.error.alreadyconnected = Ви вже підключилися. text.error.mapnotfound = Файл мапи не знайдено +text.error.io = Мережева помилка введення-виведення text.error.any = Невідома мережева помилка text.settings.language = Мова text.settings.reset = Скинути за замовчуванням From e24d96da6fc1f932e9b787ea00763122d61686ae Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 3 Jan 2019 18:35:48 -0500 Subject: [PATCH 64/67] Note --- core/src/io/anuke/mindustry/io/SaveIO.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java index 45f1e24303..2248d056c4 100644 --- a/core/src/io/anuke/mindustry/io/SaveIO.java +++ b/core/src/io/anuke/mindustry/io/SaveIO.java @@ -13,6 +13,7 @@ import java.util.zip.InflaterInputStream; import static io.anuke.mindustry.Vars.*; +//TODO load backup meta if possible public class SaveIO{ public static final IntArray breakingVersions = IntArray.with(47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 63); public static final IntMap versions = new IntMap<>(); @@ -65,9 +66,7 @@ public class SaveIO{ public static boolean isSaveValid(DataInputStream stream){ try{ - int version = stream.readInt(); - SaveFileVersion ver = versions.get(version); - ver.getData(stream); + getData(stream); return true; }catch(Exception e){ e.printStackTrace(); From f61f3af687dfcaa27435a6f966bd5fcfcaf7fe8d Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 4 Jan 2019 11:39:49 -0500 Subject: [PATCH 65/67] Zoom fixes --- core/src/io/anuke/mindustry/core/Renderer.java | 2 +- core/src/io/anuke/mindustry/input/MobileInput.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/io/anuke/mindustry/core/Renderer.java b/core/src/io/anuke/mindustry/core/Renderer.java index 756ed9945c..cff7944281 100644 --- a/core/src/io/anuke/mindustry/core/Renderer.java +++ b/core/src/io/anuke/mindustry/core/Renderer.java @@ -297,7 +297,7 @@ public class Renderer implements ApplicationListener{ public void clampScale(){ float s = io.anuke.arc.scene.ui.layout.Unit.dp.scl(1f); - targetscale = Mathf.clamp(targetscale, Math.round(s * 1), Math.round(s * 6)); + targetscale = Mathf.clamp(targetscale, s * 2.5f, Math.round(s * 7)); } public void takeMapScreenshot(){ diff --git a/core/src/io/anuke/mindustry/input/MobileInput.java b/core/src/io/anuke/mindustry/input/MobileInput.java index b8fc6c00cd..a1d95f6b46 100644 --- a/core/src/io/anuke/mindustry/input/MobileInput.java +++ b/core/src/io/anuke/mindustry/input/MobileInput.java @@ -691,7 +691,7 @@ public class MobileInput extends InputHandler implements GestureListener{ @Override public boolean zoom(float initialDistance, float distance){ - float amount = (distance > initialDistance ? 0.1f : -0.1f) * Time.delta(); + float amount = (distance > initialDistance ? 0.1f : -0.1f); renderer.scaleCamera(io.anuke.arc.scene.ui.layout.Unit.dp.scl(amount)); return true; } From 3b41ded04502acf1182b6d822faf342b72b8c8e5 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 4 Jan 2019 12:18:26 -0500 Subject: [PATCH 66/67] More zoom fixes --- core/src/io/anuke/mindustry/input/MobileInput.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/io/anuke/mindustry/input/MobileInput.java b/core/src/io/anuke/mindustry/input/MobileInput.java index a1d95f6b46..4ac8d2f761 100644 --- a/core/src/io/anuke/mindustry/input/MobileInput.java +++ b/core/src/io/anuke/mindustry/input/MobileInput.java @@ -47,6 +47,7 @@ public class MobileInput extends InputHandler implements GestureListener{ //gesture data private Vector2 vector = new Vector2(); + private float lastDistance = -1f; private boolean canPan; /** Set of completed guides. */ private ObjectSet guides = new ObjectSet<>(); @@ -691,8 +692,11 @@ public class MobileInput extends InputHandler implements GestureListener{ @Override public boolean zoom(float initialDistance, float distance){ - float amount = (distance > initialDistance ? 0.1f : -0.1f); + if(lastDistance == -1) lastDistance = initialDistance; + + float amount = (distance > lastDistance ? 0.07f : -0.07f) * Time.delta(); renderer.scaleCamera(io.anuke.arc.scene.ui.layout.Unit.dp.scl(amount)); + lastDistance = distance; return true; } From d5626bfe526b56ca14a30b9373bf4bdb3327212f Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 4 Jan 2019 15:12:34 -0500 Subject: [PATCH 67/67] Updated to next version of kryonet --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 88ff2ba2c4..f4ff0f9303 100644 --- a/build.gradle +++ b/build.gradle @@ -251,7 +251,7 @@ project(":net"){ dependencies{ compile project(":core") compile "org.lz4:lz4-java:1.4.1" - compile 'com.github.Anuken:kryonet:38ca8d51b5763ebe463ed973a63b64390ff51416' + compile 'com.github.Anuken:kryonet:a64d2280880e80566ca1bdaffa55de43e51cad38' compile 'com.github.Anuken:WaifUPnP:05eb46bc577fd7674596946ba288c96c0cedd893' } }