Semi-functioning implementation

This commit is contained in:
Anuken
2023-05-09 15:00:26 -04:00
parent a32462971b
commit 5ccadcf38f
5 changed files with 219 additions and 47 deletions

View File

@@ -99,7 +99,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
private transient boolean sleeping;
private transient float sleepTime;
private transient boolean initialized;
private transient boolean initialized, wasAdded;
/** Sets this tile entity data to this and adds it if necessary. */
public Building init(Tile tile, Team team, boolean shouldAdd, int rotation){
@@ -174,6 +174,11 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
//endregion
//region io
/** certain blocks merge liquid graphs, so it is necessary to provide a fake one at write-time */
public LiquidModule writeLiquids(){
return liquids;
}
public final void writeBase(Writes write){
boolean writeVisibility = state.rules.fog && visibleFlags != 0;
@@ -186,7 +191,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
write.b(moduleBitmask());
if(items != null) items.write(write);
if(power != null) power.write(write);
if(liquids != null) liquids.write(write);
if(liquids != null) writeLiquids().write(write);
//efficiency is written as two bytes to save space
write.b((byte)(Mathf.clamp(efficiency) * 255f));
@@ -820,6 +825,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
if(!net.client() && state.isCampaign() && team == state.rules.defaultTeam) liquid.unlock();
float selfCapacity = liquidCapacity();
for(int i = 0; i < proximity.size; i++){
incrementDump(proximity.size);
@@ -829,10 +836,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
other = other.getLiquidDestination(self(), liquid);
if(other != null && other.team == team && other.block.hasLiquids && canDumpLiquid(other, liquid) && other.liquids != null){
float ofract = other.liquids.get(liquid) / other.block.liquidCapacity;
float fract = liquids.get(liquid) / block.liquidCapacity;
float ofract = other.liquids.get(liquid) / other.liquidCapacity();
float fract = liquids.get(liquid) / selfCapacity;
if(ofract < fract) transferLiquid(other, (fract - ofract) * block.liquidCapacity / scaling, liquid);
if(ofract < fract) transferLiquid(other, (fract - ofract) * selfCapacity / scaling, liquid);
}
}
}
@@ -842,7 +849,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
}
public void transferLiquid(Building next, float amount, Liquid liquid){
float flow = Math.min(next.block.liquidCapacity - next.liquids.get(liquid), amount);
float flow = Math.min(next.liquidCapacity() - next.liquids.get(liquid), amount);
if(next.acceptLiquid(self(), liquid)){
next.handleLiquid(self(), liquid, flow);
@@ -869,19 +876,20 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
if(next == null) return 0;
next = next.getLiquidDestination(self(), liquid);
float selfCapacity = liquidCapacity(), nextCapacity = next.liquidCapacity();
if(next.team == team && next.block.hasLiquids && liquids.get(liquid) > 0f){
float ofract = next.liquids.get(liquid) / next.block.liquidCapacity;
float fract = liquids.get(liquid) / block.liquidCapacity * block.liquidPressure;
float flow = Math.min(Mathf.clamp((fract - ofract)) * (block.liquidCapacity), liquids.get(liquid));
flow = Math.min(flow, next.block.liquidCapacity - next.liquids.get(liquid));
float ofract = next.liquids.get(liquid) / nextCapacity;
float fract = liquids.get(liquid) / selfCapacity * block.liquidPressure;
float flow = Math.min(Mathf.clamp((fract - ofract)) * selfCapacity, liquids.get(liquid));
flow = Math.min(flow, nextCapacity - next.liquids.get(liquid));
if(flow > 0f && ofract <= fract && next.acceptLiquid(self(), liquid)){
next.handleLiquid(self(), liquid, flow);
liquids.remove(liquid, flow);
return flow;
//handle reactions between different liquid types ▼
}else if(!next.block.consumesLiquid(liquid) && next.liquids.currentAmount() / next.block.liquidCapacity > 0.1f && fract > 0.1f){
}else if(!next.block.consumesLiquid(liquid) && next.liquids.currentAmount() / nextCapacity > 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;
@@ -907,6 +915,11 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
return 0;
}
/** Override to set a custom liquid capacity, e.g. for blocks with shared inventories. */
public float liquidCapacity(){
return block.liquidCapacity;
}
public Building getLiquidDestination(Building from, Liquid liquid){
return self();
}
@@ -1074,6 +1087,9 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
noSleep();
}
/** Like onProximityAdded, but only called once. Essentially does what onProximityAdded was supposed to. */
public void onAdded(){}
public void updatePowerGraph(){
for(Building other : getPowerConnections(tempBuilds)){
if(other.power != null){
@@ -1679,6 +1695,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
other.onProximityUpdate();
}
proximity.clear();
wasAdded = false;
}
public void rotated(int prevRotation, int newRotation){
@@ -1705,6 +1722,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
proximity.add(tile);
}
if(!wasAdded){
onAdded();
wasAdded = true;
}
onProximityAdded();
onProximityUpdate();

View File

@@ -558,7 +558,7 @@ public class Block extends UnlockableContent implements Senseable{
addBar("liquid-" + liq.name, entity -> !liq.unlockedNow() ? null : new Bar(
() -> liq.localizedName,
liq::barColor,
() -> entity.liquids.get(liq) / liquidCapacity
() -> entity.liquids.get(liq) / entity.liquidCapacity()
));
}
@@ -567,7 +567,7 @@ public class Block extends UnlockableContent implements Senseable{
addBar("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)
() -> current.get((T)entity) == null ? 0f : entity.liquids.get(current.get((T)entity)) / entity.liquidCapacity())
);
}

View File

@@ -8,6 +8,7 @@ import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
@@ -15,17 +16,21 @@ import mindustry.entities.units.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.input.*;
import mindustry.logic.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.blocks.distribution.*;
import mindustry.world.modules.*;
import static mindustry.Vars.*;
import static mindustry.type.Liquid.*;
public class Conduit extends LiquidBlock implements Autotiler{
static final boolean debugGraphs = false;
static final float rotatePad = 6, hpad = rotatePad / 2f / 4f;
static final float[][] rotateOffsets = {{hpad, hpad}, {-hpad, hpad}, {-hpad, -hpad}, {hpad, -hpad}};
static final LiquidModule tempLiquids = new LiquidModule();
public final int timerFlow = timers++;
@@ -51,6 +56,10 @@ public class Conduit extends LiquidBlock implements Autotiler{
noUpdateDisabled = true;
canOverdrive = false;
priority = TargetPriority.transport;
//conduits don't need to update
update = false;
destructible = true;
}
@Override
@@ -155,7 +164,6 @@ public class Conduit extends LiquidBlock implements Autotiler{
public boolean capped, backCapped = false;
protected void addGraphs(){
graph = null;
//connect self to every nearby graph
getConnections(other -> {
if(other.graph != null){
@@ -163,6 +171,7 @@ public class Conduit extends LiquidBlock implements Autotiler{
}
});
//nothing to connect to
if(graph == null){
new ConduitGraph().merge(this);
}
@@ -172,15 +181,35 @@ public class Conduit extends LiquidBlock implements Autotiler{
//graph is getting recalculated, no longer valid
if(graph != null){
graph.checkRemove();
graph.remove(this);
graph = null; //TODO ?????
}
getConnections(other -> new ConduitGraph().reflow(this, other));
}
@Override
public void onProximityAdded(){
super.onProximityAdded();
public void control(LAccess type, double p1, double p2, double p3, double p4){
if(type == LAccess.enabled){
boolean shouldEnable = !Mathf.zero((float)p1);
if(enabled != shouldEnable){
if(graph != null){
//keep track of how many conduits are disabled, so the graph can stop working
if(shouldEnable){
graph.disabledConduits --;
}else{
graph.disabledConduits ++;
}
}
enabled = shouldEnable;
}
}
}
@Override
public void onAdded(){
addGraphs();
}
@@ -222,14 +251,20 @@ public class Conduit extends LiquidBlock implements Autotiler{
if(capped && capRegion.found()) Draw.rect(capRegion, x, y, rotdeg());
if(backCapped && capRegion.found()) Draw.rect(capRegion, x, y, rotdeg() + 180);
//TODO this is for debuggig only
Mathf.rand.setSeed(graph == null ? -1 : graph.id);
Draw.color(Tmp.c1.rand());
Draw.alpha(0.4f);
if(debugGraphs){
//simple visualization that assigns random color to each graph
Mathf.rand.setSeed(graph == null ? -1 : graph.id);
Draw.color(Tmp.c1.rand());
Fill.square(x, y, 4f);
Drawf.selected(tileX(), tileY(), block, Tmp.c1);
Draw.color(Pal.accent);
Draw.color();
if(this == graph.head){
Fill.poly(x, y, 3, 2f, rotdeg());
}
Draw.color();
}
}
protected void drawAt(float x, float y, int bits, int rotation, SliceMode slice){
@@ -253,7 +288,7 @@ public class Conduit extends LiquidBlock implements Autotiler{
//the drawing state machine sure was a great design choice with no downsides or hidden behavior!!!
float xscl = Draw.xscl, yscl = Draw.yscl;
Draw.scl(1f, 1f);
Drawf.liquid(sliced(liquidr, slice), x + ox, y + oy, smoothLiquid, liquids.current().color.write(Tmp.c1).a(1f));
Drawf.liquid(sliced(liquidr, slice), x + ox, y + oy, graph == null ? smoothLiquid : graph.smoothLiquid, liquids.current().color.write(Tmp.c1).a(1f));
Draw.scl(xscl, yscl);
Draw.rect(sliced(topRegions[bits], slice), x, y, angle);
@@ -282,15 +317,15 @@ public class Conduit extends LiquidBlock implements Autotiler{
}
@Override
public void updateTile(){
smoothLiquid = Mathf.lerpDelta(smoothLiquid, liquids.currentAmount() / liquidCapacity, 0.05f);
public LiquidModule writeLiquids(){
//"saved" liquids are based on a fraction, essentially splitting apart and re-joining
tempLiquids.set(liquids, graph == null ? 1f : block.liquidCapacity / graph.totalCapacity);
return tempLiquids;
}
if(liquids.currentAmount() > 0.0001f && timer(timerFlow, 1)){
moveLiquidForward(leaks, liquids.current());
noSleep();
}else{
sleep();
}
@Override
public float liquidCapacity(){
return graph == null ? block.liquidCapacity : graph.totalCapacity;
}
@Nullable
@@ -303,10 +338,15 @@ public class Conduit extends LiquidBlock implements Autotiler{
return null;
}
@Override
public void writeAll(Writes write){
super.writeAll(write);
}
/** Calls callback with every conduit that transfers fluids to this one. */
public void getConnections(Cons<ConduitBuild> cons){
for(var other : proximity){
if(other instanceof ConduitBuild conduit){
if(other instanceof ConduitBuild conduit && other.team == team){
if(
front() == conduit ||
other.front() == this
@@ -320,25 +360,34 @@ public class Conduit extends LiquidBlock implements Autotiler{
/*
TODO:
- [ ] liquids shared as one inventory
- [ ] liquids merged when placing
- [ ] liquids split when breaking
- [ ] liquids saved
- [ ] liquids accept input
- [ ] liquids transfer forward
- [ ] liquids leak
- [ ] liquids display properly (including flow rate)
- [X] liquids shared as one inventory
- [X] liquids merged when placing
- [X] liquids split when breaking
- [X] liquids saved
- [X] liquids accept input
- [X] liquids transfer forward
- [X] liquids leak
- [X] liquids display properly (including flow rate)
- [ ] liquids merge different types correctly - ?????
- [X] conduits can (or can't) be disabled
*/
public static class ConduitGraph{
private static final IntSet closedSet = new IntSet();
private static final IntSet closedSet = new IntSet(), headSet = new IntSet();
private static final Queue<ConduitBuild> queue = new Queue<>();
static int lastId = -1;
static int lastId = 0;
public final int id = lastId ++;
public float smoothLiquid;
/** if any are disabled, does not update */
private int disabledConduits;
private Seq<ConduitBuild> conduits = new Seq<>();
private final @Nullable ConduitGraphUpdater entity;
private LiquidModule liquids = new LiquidModule();
private float totalCapacity;
public @Nullable Building head;
public ConduitGraph(){
entity = ConduitGraphUpdater.create();
@@ -346,7 +395,17 @@ public class Conduit extends LiquidBlock implements Autotiler{
}
public void update(){
//TODO
smoothLiquid = Mathf.lerpDelta(smoothLiquid, liquids.currentAmount() / totalCapacity, 0.05f);
if(disabledConduits > 0) return;
if(head != null){
//move forward as the head
if(liquids.currentAmount() > 0.0001f && head.timer(((Conduit)head.block).timerFlow, 1)){
head.moveLiquidForward(((Conduit)head.block).leaks, liquids.current());
}
}
}
public void checkAdd(){
@@ -357,6 +416,15 @@ public class Conduit extends LiquidBlock implements Autotiler{
if(entity != null) entity.remove();
}
public void remove(ConduitBuild build){
float fraction = build.block.liquidCapacity / totalCapacity;
//remove fraction of liquids based on what part this conduit constituted
//e.g. 70% of capacity was made up by this conduit = multiply liquids by 0.3 (remove 70%)
liquids.mul(1f - fraction);
totalCapacity -= build.block.liquidCapacity;
}
public void reflow(@Nullable ConduitBuild ignore, ConduitBuild conduit){
closedSet.clear();
queue.clear();
@@ -367,9 +435,10 @@ public class Conduit extends LiquidBlock implements Autotiler{
closedSet.add(conduit.id);
queue.add(conduit);
while(queue.size > 0){
var parent = queue.removeFirst();
assign(parent);
assign(parent, ignore);
parent.getConnections(child -> {
if(closedSet.add(child.id)){
@@ -387,7 +456,7 @@ public class Conduit extends LiquidBlock implements Autotiler{
if(other.graph != null){
//merge graphs - TODO - flip if it is larger
//merge graphs - TODO - flip if it is larger, like power graphs?
for(var cond : other.graph.conduits){
assign(cond);
}
@@ -397,16 +466,57 @@ public class Conduit extends LiquidBlock implements Autotiler{
}
protected void assign(ConduitBuild build){
assign(build, null);
}
protected void assign(ConduitBuild build, @Nullable Building ignore){
if(build.graph != this){
//invalidate older graph
//merge graph liquids - TODO - how does this react to different types
if(build.graph != null){
build.graph.checkRemove();
//add liquids based on what fraction it made up
liquids.add(build.liquids, build.block.liquidCapacity / build.graph.totalCapacity);
}else{
//simple direct liquid merge
liquids.add(build.liquids);
}
totalCapacity += build.block.liquidCapacity;
build.graph = this;
build.liquids = liquids;
conduits.add(build);
checkAdd();
//re-validate head
if(head == null){
head = build;
}
//find the best head block
headSet.clear();
headSet.add(head.id);
while(true){
var next = head.front();
if(next instanceof ConduitBuild cond && cond.team == head.team && next != ignore){
if(!headSet.add(next.id)){
//there's a loop, which means a head does not exist
head = null;
break;
}else{
head = next;
}
}else{
//found the end
break;
}
}
//snap smoothLiquid so it doesn't start at 0
smoothLiquid = liquids.currentAmount() / totalCapacity;
}
}

View File

@@ -24,7 +24,7 @@ public class DrawLiquidRegion extends DrawBlock{
public void draw(Building build){
Liquid drawn = drawLiquid != null ? drawLiquid : build.liquids.current();
Drawf.liquid(liquid, build.x, build.y,
build.liquids.get(drawn) / build.block.liquidCapacity * alpha,
build.liquids.get(drawn) / build.liquidCapacity() * alpha,
drawn.color
);
}

View File

@@ -64,6 +64,12 @@ public class LiquidModule extends BlockModule{
}
}
public void mul(float amount){
for(int i = 0; i < liquids.length; i ++){
liquids[i] *= amount;
}
}
public void stopFlow(){
flow = null;
}
@@ -107,6 +113,27 @@ public class LiquidModule extends BlockModule{
Arrays.fill(liquids, 0);
}
public void add(LiquidModule other){
add(other, 1f);
}
public void add(LiquidModule other, float mul){
for(int i = 0; i < liquids.length; i ++){
liquids[i] += other.liquids[i] * mul;
if(liquids[i] > liquids[current.id]){
current = content.liquid(i);
}
}
}
public void set(LiquidModule other, float mul){
current = other.current;
for(int i = 0; i < liquids.length; i ++){
liquids[i] = other.liquids[i] * mul;
}
}
public void add(Liquid liquid, float amount){
liquids[liquid.id] += amount;
current = liquid;
@@ -180,6 +207,20 @@ public class LiquidModule extends BlockModule{
}
}
@Override
public String toString(){
var res = new StringBuilder();
res.append("LiquidModule{ current=").append(current).append(", ");
for(int i = 0; i < liquids.length; i++){
if(liquids[i] > 0){
res.append(content.liquid(i).name).append(":").append(liquids[i]).append(", ");
}
}
res.setLength(res.length() - 2);
res.append("}");
return res.toString();
}
public interface LiquidConsumer{
void accept(Liquid liquid, float amount);
}