diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 549dceed33..ab90b86c56 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1089,7 +1089,7 @@ stat.buildspeedmultiplier = Build Speed Multiplier stat.reactive = Reacts stat.immunities = Immunities stat.healing = Healing -stat.efficiency = [stat]{0}% Efficiency +stat.efficiency = {0}% Efficiency ability.forcefield = Force Field ability.forcefield.description = Projects a force shield that absorbs bullets @@ -1147,6 +1147,7 @@ bar.cargounitcap = Cargo Unit Cap Reached bar.drillspeed = Drill Speed: {0}/s bar.pumpspeed = Pump Speed: {0}/s bar.efficiency = Efficiency: {0}% +bar.yield = Yield: +{0}% bar.boost = Boost: +{0}% bar.powerbuffer = Batteries: {0}/{1} bar.powerbalance = Power: {0}/s diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 93338bddf1..dc314076cf 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -1090,6 +1090,7 @@ public class Blocks{ hasLiquids = false; itemCapacity = 30; boostScale = 0.15f; + outputScale = 0.15f; drawer = new DrawMulti(new DrawDefault(), new DrawFlame(Color.valueOf("ffef99"))); ambientSound = Sounds.loopSmelter; ambientSoundVolume = 0.07f; diff --git a/core/src/mindustry/world/blocks/production/AttributeCrafter.java b/core/src/mindustry/world/blocks/production/AttributeCrafter.java index 0cff4eb1b7..f001390537 100644 --- a/core/src/mindustry/world/blocks/production/AttributeCrafter.java +++ b/core/src/mindustry/world/blocks/production/AttributeCrafter.java @@ -1,6 +1,7 @@ package mindustry.world.blocks.production; import arc.*; +import arc.struct.*; import mindustry.game.*; import mindustry.graphics.*; import mindustry.ui.*; @@ -11,12 +12,15 @@ import mindustry.world.meta.*; public class AttributeCrafter extends GenericCrafter{ public Attribute attribute = Attribute.heat; public float baseEfficiency = 1f; - public float boostScale = 1f; public float maxBoost = 1f; public float minEfficiency = -1f; - public float displayEfficiencyScale = 1f; public boolean displayEfficiency = true; + public boolean displayScaledOutput = true; public boolean scaleLiquidConsumption = false; + /** Scaled output (yield) multiplier, scales with attribute. <=0 to disable. */ + public float outputScale = 0f; + /** Scaled efficiency (speed) multiplier, scales with attribute. <=0 to disable. */ + public float boostScale = 1f; public AttributeCrafter(String name){ super(name); @@ -26,23 +30,39 @@ public class AttributeCrafter extends GenericCrafter{ public void drawPlace(int x, int y, int rotation, boolean valid){ super.drawPlace(x, y, rotation, valid); - if(!displayEfficiency) return; + if((!displayEfficiency || boostScale <= 0f) && (!displayScaledOutput || outputScale <= 0f)) return; - drawPlaceText(Core.bundle.format("bar.efficiency", - (int)((baseEfficiency + Math.min(maxBoost, boostScale * sumAttribute(attribute, x, y))) * 100f)), x, y, valid); + drawPlaceText( + (displayEfficiency && boostScale > 0f ? + Core.bundle.format("bar.efficiency", + (int)((baseEfficiency + Math.min(maxBoost, boostScale * sumAttribute(attribute, x, y))) * 100f)) + : "") + + (displayScaledOutput && outputScale > 0f ? "\n" + + Core.bundle.format("bar.yield", + (int)(Math.min(maxBoost, outputScale * sumAttribute(attribute, x, y)) * 100f)) + : ""), x, y, valid); } @Override public void setBars(){ super.setBars(); - if(!displayEfficiency) return; + if((!displayEfficiency || boostScale <= 0f) && (!displayScaledOutput || outputScale <= 0f)) return; - addBar("efficiency", (AttributeCrafterBuild entity) -> + if(displayEfficiency && boostScale > 0f){ + addBar("efficiency", (AttributeCrafterBuild entity) -> new Bar( - () -> Core.bundle.format("bar.efficiency", (int)(entity.efficiencyMultiplier() * 100 * displayEfficiencyScale)), + () -> Core.bundle.format("bar.efficiency", (int)(entity.efficiencyMultiplier() * 100)), () -> Pal.lightOrange, entity::efficiencyMultiplier)); + } + if(displayScaledOutput && outputScale > 0f){ + addBar("yield", (AttributeCrafterBuild entity) -> + new Bar( + () -> Core.bundle.format("bar.yield", (int)((entity.outputMultiplier() - baseEfficiency) * 100)), + () -> Pal.lightOrange, + entity::outputMultiplier)); + } } @Override @@ -54,18 +74,29 @@ public class AttributeCrafter extends GenericCrafter{ @Override public void setStats(){ super.setStats(); - - stats.add(baseEfficiency <= 0.0001f ? Stat.tiles : Stat.affinities, attribute, floating, boostScale * size * size, !displayEfficiency); + if(outputScale > 0f){ + stats.add(baseEfficiency <= 0.0001f ? Stat.tiles : + Stat.affinities, attribute, floating, boostScale * size * size, outputScale * size * size, Seq.with(outputItems), craftTime, !displayEfficiency); + }else{ + stats.add(baseEfficiency <= 0.0001f ? Stat.tiles : Stat.affinities, attribute, floating, boostScale * size * size, !displayEfficiency); + } } public class AttributeCrafterBuild extends GenericCrafterBuild{ public float attrsum; + public float accumulator; + public float scaledOutput; + public int scaledInt; @Override public float getProgressIncrease(float base){ return super.getProgressIncrease(base) * efficiencyMultiplier(); } + public float outputMultiplier(){ + return baseEfficiency + Math.min(maxBoost, outputScale * attrsum) + attribute.env(); + } + public float efficiencyMultiplier(){ return baseEfficiency + Math.min(maxBoost, boostScale * attrsum) + attribute.env(); } @@ -75,6 +106,23 @@ public class AttributeCrafter extends GenericCrafter{ return scaleLiquidConsumption ? efficiencyMultiplier() : super.efficiencyScale(); } + @Override + public float scaleOutput(float amount, boolean item, boolean accumulate){ + scaledOutput = amount * outputMultiplier(); + + if(item){ + if(accumulate){ + accumulator += scaledOutput; + scaledInt = (int)(accumulator); + accumulator -= scaledInt; + }else{ + scaledInt = (int)(accumulator + scaledOutput); + } + } + + return outputScale > 0f ? Math.min(itemCapacity, scaledInt) : amount; + } + @Override public void pickedUp(){ attrsum = 0f; diff --git a/core/src/mindustry/world/blocks/production/GenericCrafter.java b/core/src/mindustry/world/blocks/production/GenericCrafter.java index 8599a7881b..d1323bf7c3 100644 --- a/core/src/mindustry/world/blocks/production/GenericCrafter.java +++ b/core/src/mindustry/world/blocks/production/GenericCrafter.java @@ -193,7 +193,7 @@ public class GenericCrafter extends Block{ public boolean shouldConsume(){ if(outputItems != null){ for(var output : outputItems){ - if(items.get(output.item) + output.amount > itemCapacity){ + if(items.get(output.item) + scaleOutput(output.amount, true, false) > itemCapacity){ return false; } } @@ -263,7 +263,7 @@ public class GenericCrafter extends Block{ if(outputLiquids != null){ max = 0f; for(var s : outputLiquids){ - float value = (liquidCapacity - liquids.get(s.liquid)) / (s.amount * edelta()); + float value = (liquidCapacity - liquids.get(s.liquid)) / (scaleOutput(s.amount) * edelta()); scaling = Math.min(scaling, value); max = Math.max(max, value); } @@ -287,12 +287,21 @@ public class GenericCrafter extends Block{ return totalProgress; } + public float scaleOutput(float amount){ + return scaleOutput(amount, false, false); + } + + public float scaleOutput(float amount, boolean item, boolean accumulate){ + return amount; + } + public void craft(){ consume(); if(outputItems != null){ for(var output : outputItems){ - for(int i = 0; i < output.amount; i++){ + float maxOutput = scaleOutput(output.amount, true, true); + for(int i = 0; i < maxOutput; i++){ offload(output.item); } } diff --git a/core/src/mindustry/world/meta/StatValues.java b/core/src/mindustry/world/meta/StatValues.java index 8556ef3adc..db55a3969b 100644 --- a/core/src/mindustry/world/meta/StatValues.java +++ b/core/src/mindustry/world/meta/StatValues.java @@ -225,7 +225,7 @@ public class StatValues{ } /** Displays an item with a specified amount. */ - private static Stack stack(TextureRegion region, int amount, @Nullable UnlockableContent content, boolean tooltip){ + private static Stack stack(TextureRegion region, float amount, @Nullable UnlockableContent content, boolean tooltip){ Stack stack = new Stack(); stack.add(new Table(o -> { @@ -236,7 +236,7 @@ public class StatValues{ if(amount != 0){ stack.add(new Table(t -> { t.left().bottom(); - t.add(amount >= 1000 ? UI.formatAmount(amount) : amount + "").style(Styles.outlineLabel); + t.add(amount >= 1000 ? UI.formatAmount((int)amount) : fixValue(amount)).style(Styles.outlineLabel); t.pack(); })); } @@ -259,7 +259,7 @@ public class StatValues{ return stack(item.uiIcon, amount, item); } - public static Stack stack(UnlockableContent item, int amount, boolean tooltip){ + public static Stack stack(UnlockableContent item, float amount, boolean tooltip){ return stack(item.uiIcon, amount, item, tooltip); } @@ -294,6 +294,13 @@ public class StatValues{ return t; } + public static Table displayItem(Item item, float amount, float timePeriod, boolean showName){ + Table t = new Table(); + t.add(stack(item, amount, !showName)); + t.add((showName ? item.localizedName + "\n" : "") + "[lightgray]" + Strings.autoFixed(amount / (timePeriod / 60f), 3) + StatUnit.perSecond.localized()).padLeft(8).padRight(5).style(Styles.outlineLabel); + return t; + } + /** Displays the item with a "/sec" qualifier based on the time period, in ticks. */ public static Table displayItemPercent(Item item, int percent, boolean showName){ Table t = new Table(); @@ -320,6 +327,10 @@ public class StatValues{ return blocks(attr, floating, scale, startZero, true); } + public static StatValue blocks(Attribute attr, boolean floating, float scale1, float scale2, @Nullable Seq outputs, float timePeriod, boolean startZero){ + return blocks(attr, floating, scale1, scale2, outputs, timePeriod, startZero, true); + } + public static StatValue blocks(Attribute attr, boolean floating, float scale, boolean startZero, boolean checkFloors){ return table -> table.table(c -> { Runnable[] rebuild = {null}; @@ -364,6 +375,74 @@ public class StatValues{ }); }); } + + public static StatValue blocks(Attribute attr, boolean floating, float scaleEff, float scaleAmount, @Nullable Seq outputs, float timePeriod, boolean startZero, boolean checkFloors) { + return table -> { + if(table.getCells().size > 0) table.getCells().peek().growX(); + table.row(); + + table.table(c -> { + Runnable[] rebuild = {null}; + Map[] lastMap = {null}; + + rebuild[0] = () -> { + c.clearChildren(); + c.left(); + + if(state.isGame()){ + var blocks = Vars.content.blocks() + .select(block -> (!checkFloors || block instanceof Floor) && indexer.isBlockPresent(block) && block.attributes.get(attr) != 0 && !((block instanceof Floor f && f.isDeep()) && !floating)) + .with(s -> s.sort(f -> f.attributes.get(attr))); + + if(blocks.any()){ + for(var block : blocks){ + c.table(Styles.grayPanel, b -> { + float effiency = 1f + block.attributes.get(attr) * scaleEff; + + b.image(block.uiIcon).size(40f).pad(10f).left().scaling(Scaling.fit); + b.table(center -> { + center.left(); + + if(outputs != null && outputs.any()){ + for(ItemStack output : outputs){ + float scaled = output.amount * (1f + block.attributes.get(attr) * scaleAmount); + + center.table(it -> { + it.left(); + it.add(displayItem(output.item, scaled, timePeriod / effiency , true)).left().padLeft(6f); + }).padRight(8f); + } + }else{ + center.add("@none"); + } + }).left().grow(); + b.add((effiency < 1f ? "[negstat]" : "[stat]") + Core.bundle.format("stat.efficiency", fixValue(effiency * 100f))).right().pad(10f).padRight(15f); + + }).growX().pad(5).row(); + } + }else{ + c.add("@none.inmap"); + } + }else{ + c.add("@stat.showinmap"); + } + }; + + rebuild[0].run(); + + //rebuild when map changes. + c.update(() -> { + Map current = state.isGame() ? state.map : null; + + if(current != lastMap[0]){ + rebuild[0].run(); + lastMap[0] = current; + } + }); + }).growX().colspan(table.getColumns()).row(); + }; +} + public static StatValue content(Seq list){ return content(list, i -> true); } @@ -479,7 +558,7 @@ public class StatValues{ c.table(Styles.grayPanel, b -> { b.image(item.uiIcon).size(40).pad(10f).left().scaling(Scaling.fit); b.add(item.localizedName + (timePeriod > 0 ? "\n[lightgray]" + Strings.autoFixed(time, time < 0.01f ? 4 : 2) + StatUnit.perSecond.localized() : "")).left().grow(); - b.add(Core.bundle.format("stat.efficiency", fixValue(efficiency.get(item) * 100f))).right().pad(10f).padRight(15f); + b.add("[stat]" + Core.bundle.format("stat.efficiency", fixValue(efficiency.get(item) * 100f))).right().pad(10f).padRight(15f); }).growX().pad(5).row(); } }).growX().colspan(table.getColumns()).row(); @@ -494,7 +573,7 @@ public class StatValues{ for(Liquid liquid : content.liquids().select(l -> filter.get(l) && l.unlockedNow() && !l.isHidden())){ c.table(Styles.grayPanel, b -> { b.add(displayLiquid(liquid, amount, true)).pad(10f).left().grow(); - b.add(Core.bundle.format("stat.efficiency", fixValue(efficiency.get(liquid) * 100f))).right().pad(10f).padRight(15f); + b.add("[stat]" + Core.bundle.format("stat.efficiency", fixValue(efficiency.get(liquid) * 100f))).right().pad(10f).padRight(15f); }).growX().pad(5).row(); } }).growX().colspan(table.getColumns()).row(); diff --git a/core/src/mindustry/world/meta/Stats.java b/core/src/mindustry/world/meta/Stats.java index d9651e9b6d..d132a41169 100644 --- a/core/src/mindustry/world/meta/Stats.java +++ b/core/src/mindustry/world/meta/Stats.java @@ -80,6 +80,10 @@ public class Stats{ add(stat, StatValues.blocks(attr, floating, scale, startZero)); } + public void add(Stat stat, Attribute attr, boolean floating, float scale1, float scale2, @Nullable Seq outputs, float timePeriod, boolean startZero){ + add(stat, StatValues.blocks(attr, floating, scale1, scale2, outputs, timePeriod, startZero)); + } + /** Adds a single string value with this stat. */ public void add(Stat stat, String format, Object... args){ add(stat, StatValues.string(format, args));