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 6a729435ce..9a1b7dbfb1 100644 --- a/core/src/io/anuke/mindustry/world/BaseBlock.java +++ b/core/src/io/anuke/mindustry/world/BaseBlock.java @@ -25,14 +25,8 @@ public abstract class BaseBlock extends MappableContent{ public boolean outputsLiquid = false; public boolean singleLiquid = true; - public boolean consumesPower; - 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 boolean consumesPower = true; + public boolean outputsPower = false; public int itemCapacity; public float liquidCapacity = 10f; diff --git a/core/src/io/anuke/mindustry/world/Block.java b/core/src/io/anuke/mindustry/world/Block.java index c04987bdd1..8eebd822cc 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.ConsumePower; 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(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) @@ -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(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/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..f81c344278 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.ConsumePower; 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,8 @@ public class MassDriver extends Block{ hasItems = true; layer = Layer.turret; hasPower = true; + consumes.powerBuffered(30f); + consumes.require(ConsumePower.class); } @Remote(targets = Loc.both, called = Loc.server, forward = true) @@ -80,7 +84,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 +132,7 @@ public class MassDriver extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.powerShot, basePowerUse, StatUnit.powerUnits); + stats.add(BlockStat.powerShot, consumes.get(ConsumePower.class).powerCapacity * powerPercentageUsed, StatUnit.powerUnits); } @Override @@ -167,9 +171,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 68afdec034..0afbe9d2da 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,13 @@ 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.ConsumePower; +import io.anuke.mindustry.world.consumers.Consumers; import static io.anuke.mindustry.Vars.threads; @@ -42,10 +45,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.has(ConsumePower.class)){ + powerNeeded += consumes.get(ConsumePower.class).requestedPower(consumer.block(), consumer.entity); } } return powerNeeded; @@ -54,7 +56,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.has(ConsumePower.class)){ + totalAccumulator += battery.entity.power.satisfaction * consumes.get(ConsumePower.class).powerCapacity; + } } return totalAccumulator; } @@ -62,23 +67,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.has(ConsumePower.class)){ + totalCapacity += consumes.get(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 +99,15 @@ 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; + 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 = satisfaction; + consumer.entity.power.satisfaction = coverage; } } } @@ -109,10 +122,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(!MathUtils.isEqual(powerNeeded, powerProduced)){ + if(powerNeeded > powerProduced){ + powerProduced += useBatteries(powerNeeded - powerProduced); + }else if(powerProduced > powerNeeded){ + powerProduced -= chargeBatteries(powerProduced - powerNeeded); + } } distributePower(powerNeeded, powerProduced); @@ -139,7 +154,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(); @@ -169,7 +184,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); @@ -190,12 +205,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/ConsumePower.java b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java new file mode 100644 index 0000000000..9aec5498de --- /dev/null +++ b/core/src/io/anuke/mindustry/world/consumers/ConsumePower.java @@ -0,0 +1,94 @@ +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 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; + + 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 consumePowerDirect(float powerPerTick, float minimumSatisfaction){ + return new ConsumePower(powerPerTick, minimumSatisfaction, 0.0f, false); + } + + /** + * 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 consumePowerBuffered(float powerCapacity, float ticksToFill){ + return new ConsumePower(powerCapacity / ticksToFill, 0.0f, powerCapacity, true); + } + + @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 + } + + @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. + * @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/Consumers.java b/core/src/io/anuke/mindustry/world/consumers/Consumers.java index e38607ad33..75b91a98e4 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 = ConsumePower.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 = ConsumePower.consumePowerBuffered(powerCapacity, ticksToFill); + add(c); + return c; + } + public ConsumeItem item(Item item){ return item(item, 1); } diff --git a/tests/src/test/java/PowerTests.java b/tests/src/test/java/PowerTests.java new file mode 100644 index 0000000000..07f7e6e2ba --- /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.assertEquals; +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/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/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); + 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 + 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) + + // Distribute power and make sure the water extractor is powered + powerGraph.distributePower(powerNeeded, powerProduced); + assertEquals(waterExtractorTile.entity.power.satisfaction, 1.0f, epsilon); + } +}