Better multi-liquid support / Reinforced pump progress

This commit is contained in:
Anuken 2021-11-16 10:25:59 -05:00
parent bf1909295e
commit eefcce5cf1
16 changed files with 182 additions and 109 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 493 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

View File

@ -443,3 +443,4 @@
63263=hydrogen|liquid-hydrogen-ui
63262=electrolyzer|block-electrolyzer-ui
63261=ozone|liquid-ozone-ui
63260=reinforced-liquid-junction|block-reinforced-liquid-junction-ui

Binary file not shown.

View File

@ -60,7 +60,7 @@ public class Blocks implements ContentList{
//crafting
siliconSmelter, siliconCrucible, siliconArcFurnace, kiln, graphitePress, plastaniumCompressor, multiPress, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer,
melter, separator, disassembler, sporePress, pulverizer, incinerator, coalCentrifuge,
electrolyzer, oxidizer, heatReactor, carbideCrucible,
electrolyzer, oxidationChamber, heatReactor, carbideCrucible,
cellSynthesisChamber,
//sandbox
@ -84,7 +84,7 @@ public class Blocks implements ContentList{
mechanicalPump, rotaryPump, impulsePump, conduit, pulseConduit, platedConduit, liquidRouter, liquidContainer, liquidTank, liquidJunction, bridgeConduit, phaseConduit,
//liquid - reinforced
reinforcedPump, reinforcedConduit, reinforcedBridgeConduit, reinforcedLiquidRouter, reinforcedLiquidContainer, reinforcedLiquidTank,
reinforcedPump, reinforcedConduit, reinforcedLiquidJunction, reinforcedBridgeConduit, reinforcedLiquidRouter, reinforcedLiquidContainer, reinforcedLiquidTank,
//power
combustionGenerator, thermalGenerator, steamGenerator, differentialGenerator, rtgGenerator, solarPanel, largeSolarPanel, thoriumReactor,
@ -980,17 +980,28 @@ public class Blocks implements ContentList{
glowScale = 6f;
}}
);
iconOverride = new String[]{"-bottom", "", "-top"};
iconOverride = new String[]{"-bottom", "", "-top"};
outputLiquids = LiquidStack.with(Liquids.ozone, 2f * craftTime / 60, Liquids.hydrogen, 3f * craftTime / 60);
liquidOutputDirections = new int[]{1, 3};
}};
oxidizer = new HeatProducer("oxidizer"){{
//TODO sprite
//TODO 'oxidation chamber'
if(false)
oxidationChamber = new HeatProducer("oxidation-chamber"){{
requirements(Category.crafting, with(Items.tungsten, 60, Items.graphite, 30));
size = 3;
//converts oxygen (?) + beryllium into heat + oxide
outputItem = new ItemStack(Items.oxide, 1);
consumes.liquid(Liquids.ozone, 2f / 60f);
consumes.item(Items.beryllium);
consumes.power(1f);
craftTime = 60f * 3f;
liquidCapacity = 30f;
heatOutput = 8f;
}};
heatReactor = new HeatProducer("heat-reactor"){{
@ -1495,13 +1506,12 @@ public class Blocks implements ContentList{
//TODO different name
reinforcedPump = new Pump("reinforced-pump"){{
requirements(Category.liquid, with(Items.beryllium, 70, Items.tungsten, 20, Items.silicon, 20));
//TODO perhaps something else?
consumes.item(Items.beryllium);
//TODO CUSTOM DRAW ANIMATION + 3x3?
//TODO balance consumption
consumes.liquid(Liquids.hydrogen, 1.5f / 60f);
pumpAmount = 0.4f;
consumes.power(0.5f);
liquidCapacity = 40f;
hasPower = true;
size = 2;
}};
@ -1514,6 +1524,13 @@ public class Blocks implements ContentList{
health = 250;
}};
//TODO is this necessary? junctions are not good design
reinforcedLiquidJunction = new LiquidJunction("reinforced-liquid-junction"){{
requirements(Category.liquid, with(Items.graphite, 3, Items.beryllium, 3));
health = 260;
((Conduit)reinforcedConduit).junctionReplacement = this;
}};
reinforcedBridgeConduit = new DirectionLiquidBridge("reinforced-bridge-conduit"){{
requirements(Category.liquid, with(Items.graphite, 4, Items.beryllium, 8));
range = 4;
@ -2742,23 +2759,6 @@ public class Blocks implements ContentList{
health = size * size * 80;
}};
nuclearWarhead = new NuclearWarhead("nuclear-warhead"){{
requirements(Category.crafting, BuildVisibility.debugOnly, with(Items.thorium, 40));
size = 2;
}};
warheadAssembler = new SingleBlockProducer("warhead-assembler"){{
requirements(Category.crafting, BuildVisibility.debugOnly, with(Items.thorium, 100));
result = nuclearWarhead;
size = 3;
buildSpeed = 0.3f;
}};
ballisticSilo = new BallisticSilo("ballistic-silo"){{
requirements(Category.crafting, BuildVisibility.debugOnly, with(Items.thorium, 100));
size = 5;
}};
//endregion campaign
//region logic

View File

@ -66,9 +66,9 @@ public class Liquids implements ContentList{
}};
//TODO combustion
hydrogen = new Liquid("hydrogen", Color.valueOf("97a5f7")){{
hydrogen = new Liquid("hydrogen", Color.valueOf("b8c2fc")){{
gas = true;
barColor = Color.valueOf("7d8be0");
barColor = Color.valueOf("a3b0ff");
flammability = 1f;
}};

View File

@ -645,6 +645,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
liquids.remove(liquid, flow);
return flow;
}else if(next.liquids.currentAmount() / next.block.liquidCapacity > 0.1f && fract > 0.1f){
//TODO !IMPORTANT! uses current(), which is 1) wrong for multi-liquid blocks and 2) causes unwanted reactions, e.g. hydrogen + slag in pump
//TODO these are incorrect effect positions
float fx = (x + next.x) / 2f, fy = (y + next.y) / 2f;
@ -1181,21 +1182,30 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
if(liquids != null){
table.row();
table.left();
table.table(l -> {
boolean[] had = {false};
Bits current = new Bits();
Runnable rebuild = () -> {
l.clearChildren();
l.left();
l.image(() -> liquids.current().uiIcon).padRight(3f);
l.label(() -> liquids.getFlowRate() < 0 ? "..." : Strings.fixed(liquids.getFlowRate(), 2) + ps).color(Color.lightGray);
for(var liquid : content.liquids()){
if(liquids.hasFlowLiquid(liquid)){
l.image(liquid.uiIcon).padRight(3f);
l.label(() -> liquids.getFlowRate(liquid) < 0 ? "..." : Strings.fixed(liquids.getFlowRate(liquid), 1) + ps).color(Color.lightGray);
l.row();
}
}
};
rebuild.run();
l.update(() -> {
if(!had[0] && liquids.hadFlow()){
had[0] = true;
rebuild.run();
}
for(var liquid : content.liquids()){
if(liquids.hasFlowLiquid(liquid) && !current.get(liquid.id)){
current.set(liquid.id);
rebuild.run();
}
}
});
}).left();
}

View File

@ -447,25 +447,26 @@ public class Block extends UnlockableContent{
if(hasItems && itemCapacity > 0) stats.add(Stat.itemCapacity, itemCapacity, StatUnit.items);
}
public void addLiquidBar(Liquid liq){
bars.add("liquid-" + liq.name, entity -> new Bar(
() -> liq.localizedName,
liq::barColor,
() -> entity.liquids.get(liq) / liquidCapacity)
);
}
/** Adds a liquid bar that dynamically displays a liquid type. */
public <T extends Building> void addLiquidBar(Func<T, Liquid> current){
bars.add("liquid", entity -> new Bar(
() -> current.get((T)entity) == null || entity.liquids.get(current.get((T)entity)) <= 0.001f ? Core.bundle.get("bar.liquid") : current.get((T)entity).localizedName,
() -> current.get((T)entity) == null ? Color.clear : current.get((T)entity).barColor(),
() -> current.get((T)entity) == null ? 0f : entity.liquids.get(current.get((T)entity)) / liquidCapacity)
);
}
public void setBars(){
bars.add("health", entity -> new Bar("stat.health", Pal.health, entity::healthf).blink(Color.white));
if(hasLiquids){
Func<Building, Liquid> current;
if(consumes.has(ConsumeType.liquid) && consumes.get(ConsumeType.liquid) instanceof ConsumeLiquid){
Liquid liquid = consumes.<ConsumeLiquid>get(ConsumeType.liquid).liquid;
current = entity -> liquid;
}else{
current = entity -> entity.liquids.current();
}
bars.add("liquid", entity -> new Bar(
() -> entity.liquids.get(current.get(entity)) <= 0.001f ? Core.bundle.get("bar.liquid") : current.get(entity).localizedName,
() -> current.get(entity).barColor(),
() -> entity.liquids.get(current.get(entity)) / liquidCapacity)
);
}
if(hasPower && consumes.hasPower()){
ConsumePower cons = consumes.getPower();
boolean buffered = cons.buffered;
@ -490,6 +491,38 @@ public class Block extends UnlockableContent{
if(unitCapModifier != 0){
stats.add(Stat.maxUnits, (unitCapModifier < 0 ? "-" : "+") + Math.abs(unitCapModifier));
}
//liquids added last
if(hasLiquids){
//TODO liquids need to be handled VERY carefully. there are several potential possibilities:
//1. no consumption or output (conduit/tank)
// - display current(), 1 bar
//2. static set of inputs and outputs
// - create bars for each input/output, straightforward
//3. TODO dynamic input/output combo???
// - confusion
boolean added = false;
//add bars for *specific* consumed liquids
if(consumes.has(ConsumeType.liquid)){
var consl = consumes.get(ConsumeType.liquid);
if(consl instanceof ConsumeLiquid liq){
added = true;
addLiquidBar(liq.liquid);
}else if(consl instanceof ConsumeLiquids multi){
added = true;
for(var stack : multi.liquids){
addLiquidBar(stack.liquid);
}
}
}
//nothing was added, so it's safe to add a dynamic liquid bar (probably?)
if(!added){
addLiquidBar(build -> build.liquids.current());
}
}
}
public boolean canReplace(Block other){

View File

@ -4,7 +4,6 @@ import arc.math.*;
import arc.util.io.*;
import mindustry.graphics.*;
import mindustry.ui.*;
import mindustry.world.blocks.power.NuclearReactor.*;
import mindustry.world.blocks.production.*;
import mindustry.world.draw.*;
@ -30,7 +29,7 @@ public class HeatProducer extends GenericCrafter{
public void setBars(){
super.setBars();
bars.add("heat", (NuclearReactorBuild entity) -> new Bar("bar.heat", Pal.lightOrange, () -> entity.heat));
bars.add("heat", (HeatProducerBuild entity) -> new Bar("bar.heat", Pal.lightOrange, () -> entity.heat));
}
public class HeatProducerBuild extends GenericCrafterBuild implements HeatBlock{

View File

@ -5,6 +5,7 @@ import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.meta.*;
//TODO should leak!
public class LiquidJunction extends LiquidBlock{
public LiquidJunction(String name){

View File

@ -12,9 +12,7 @@ import mindustry.entities.units.*;
import mindustry.gen.*;
import mindustry.logic.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.consumers.*;
import mindustry.world.draw.*;
import mindustry.world.meta.*;
@ -74,41 +72,14 @@ public class GenericCrafter extends Block{
public void setBars(){
super.setBars();
//set up liquid bars for multiple liquid outputs
//TODO this will currently screw up input display if input liquids are filters - no good way to fix that yet
//set up liquid bars for liquid outputs
if(outputLiquids != null && outputLiquids.length > 0){
//no need for dynamic liquid bar
bars.remove("liquid");
Seq<Liquid> consumed = new Seq<>();
//find list of liquids consumed
if(consumes.has(ConsumeType.liquid)){
var consl = consumes.get(ConsumeType.liquid);
if(consl instanceof ConsumeLiquid liq){
consumed.add(liq.liquid);
}else if(consl instanceof ConsumeLiquids multi){
for(var stack : multi.liquids){
consumed.add(stack.liquid);
}
}
}
//display consumed first
for(var liq : consumed){
bars.add("liquid-consume-" + liq.name, entity -> new Bar(
() -> liq.localizedName,
liq::barColor,
() -> entity.liquids.get(liq) / liquidCapacity)
);
}
//then display output buffer
for(var stack : outputLiquids){
bars.add("liquid-output-" + stack.liquid.name, entity -> new Bar(
() -> stack.liquid.localizedName,
() -> stack.liquid.barColor(),
() -> entity.liquids.get(stack.liquid) / liquidCapacity)
);
addLiquidBar(stack.liquid);
}
}
}

View File

@ -3,6 +3,7 @@ package mindustry.world.blocks.production;
import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.util.*;
import mindustry.game.*;
import mindustry.graphics.*;
import mindustry.type.*;
@ -82,6 +83,14 @@ public class Pump extends LiquidBlock{
}
}
@Override
public void setBars(){
super.setBars();
//replace dynamic output bar with own custom bar
addLiquidBar((PumpBuild build) -> build.liquidDrop);
}
protected boolean canPump(Tile tile){
return tile != null && tile.floor().liquidDrop != null;
}
@ -89,13 +98,15 @@ public class Pump extends LiquidBlock{
public class PumpBuild extends LiquidBuild{
public float consTimer;
public float amount = 0f;
public Liquid liquidDrop = null;
public @Nullable Liquid liquidDrop = null;
@Override
public void draw(){
Draw.rect(name, x, y);
Draw.rect(region, x, y);
Drawf.liquid(liquidRegion, x, y, liquids.currentAmount() / liquidCapacity, liquids.current().color);
if(liquidDrop == null) return;
Drawf.liquid(liquidRegion, x, y, liquids.get(liquidDrop) / liquidCapacity, liquidDrop.color);
}
@Override
@ -126,17 +137,19 @@ public class Pump extends LiquidBlock{
@Override
public void updateTile(){
if(consValid() && liquidDrop != null){
float maxPump = Math.min(liquidCapacity - liquids.total(), amount * pumpAmount * edelta());
float maxPump = Math.min(liquidCapacity - liquids.get(liquidDrop), amount * pumpAmount * edelta());
liquids.add(liquidDrop, maxPump);
//does nothing for most pumps, as those do not require items.
if((consTimer += delta()) >= consumeTime){
consume();
consumeTime = 0f;
consTimer = 0f;
}
}
dumpLiquid(liquids.current());
if(liquidDrop != null){
dumpLiquid(liquidDrop);
}
}
}

View File

@ -30,6 +30,7 @@ public class LiquidVoid extends Block{
@Override
public void handleLiquid(Building source, Liquid liquid, float amount){
liquids.handleFlow(liquid, amount);
}
}

View File

@ -13,8 +13,6 @@ public class BlockBars{
}
public void remove(String name){
if(!bars.containsKey(name))
throw new RuntimeException("No bar with name '" + name + "' found; current bars: " + bars.keys().toSeq());
bars.remove(name);
}

View File

@ -90,15 +90,11 @@ public class ItemModule extends BlockModule{
/** @return a specific item's flow rate in items/s; any value < 0 means not ready.*/
public float getFlowRate(Item item){
if(flow == null) return -1f;
return displayFlow[item.id] * 60;
return flow == null ? -1f : displayFlow[item.id] * 60;
}
public boolean hasFlowItem(Item item){
if(flow == null) return false;
return cacheBits.get(item.id);
return flow != null && cacheBits.get(item.id);
}
public void each(ItemConsumer cons){

View File

@ -1,6 +1,7 @@
package mindustry.world.modules;
import arc.math.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.type.*;
@ -14,18 +15,25 @@ public class LiquidModule extends BlockModule{
private static final Interval flowTimer = new Interval(2);
private static final float pollScl = 20f;
private static WindowedMean[] cacheFlow;
private static float[] cacheSums;
private static float[] displayFlow;
private static final Bits cacheBits = new Bits();
private float[] liquids = new float[content.liquids().size];
private float total;
private Liquid current = content.liquid(0);
private float smoothLiquid;
private boolean hadFlow;
private @Nullable WindowedMean flow;
private float lastAdded, currentFlowRate;
//TODO broken for multi flow
private @Nullable WindowedMean[] flow;
public void update(boolean showFlow){
smoothLiquid = Mathf.lerpDelta(smoothLiquid, currentAmount(), 0.1f);
if(showFlow){
/*
if(flowTimer.get(1, pollScl)){
if(flow == null) flow = new WindowedMean(windowSize);
@ -36,21 +44,57 @@ public class LiquidModule extends BlockModule{
if(currentFlowRate < 0 || flowTimer.get(updateInterval)){
currentFlowRate = flow.hasEnoughData() ? flow.mean() / pollScl : -1f;
}
}*/
if(flowTimer.get(1, pollScl)){
if(flow == null){
if(cacheFlow == null || cacheFlow.length != liquids.length){
cacheFlow = new WindowedMean[liquids.length];
for(int i = 0; i < liquids.length; i++){
cacheFlow[i] = new WindowedMean(windowSize);
}
cacheSums = new float[liquids.length];
displayFlow = new float[liquids.length];
}else{
for(int i = 0; i < liquids.length; i++){
cacheFlow[i].reset();
}
Arrays.fill(cacheSums, 0);
cacheBits.clear();
}
Arrays.fill(displayFlow, -1);
flow = cacheFlow;
}
boolean updateFlow = flowTimer.get(30);
for(int i = 0; i < liquids.length; i++){
flow[i].add(cacheSums[i]);
if(cacheSums[i] > 0){
cacheBits.set(i);
}
cacheSums[i] = 0;
if(updateFlow){
displayFlow[i] = flow[i].hasEnoughData() ? flow[i].mean() / pollScl : -1;
}
}
}
}else{
currentFlowRate = -1f;
flow = null;
hadFlow = false;
}
}
/** @return current liquid's flow rate in u/s; any value < 0 means 'not ready'. */
public float getFlowRate(){
return currentFlowRate * 60;
public float getFlowRate(Liquid liquid){
return flow == null ? -1f : displayFlow[liquid.id] * 60;
}
public boolean hadFlow(){
return hadFlow;
public boolean hasFlowLiquid(Liquid liquid){
return flow != null && cacheBits.get(liquid.id);
}
public float smoothAmount(){
@ -93,7 +137,13 @@ public class LiquidModule extends BlockModule{
current = liquid;
if(flow != null){
lastAdded += Math.max(amount, 0);
cacheSums[liquid.id] += Math.max(amount, 0);
}
}
public void handleFlow(Liquid liquid, float amount){
if(flow != null){
cacheSums[liquid.id] += Math.max(amount, 0);
}
}