mirror of
https://github.com/Creators-of-Create/Create.git
synced 2025-02-05 01:45:00 +01:00
Curse of the Speed Controller
This commit is contained in:
parent
1fed398e8f
commit
f4514b3e5e
9 changed files with 216 additions and 76 deletions
|
@ -1,5 +1,7 @@
|
||||||
package com.simibubi.create.content.contraptions.base;
|
package com.simibubi.create.content.contraptions.base;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.contraptions.solver.AllConnections;
|
||||||
|
import com.simibubi.create.content.contraptions.solver.KineticConnections;
|
||||||
import com.simibubi.create.foundation.utility.Iterate;
|
import com.simibubi.create.foundation.utility.Iterate;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
@ -61,6 +63,11 @@ public abstract class HorizontalAxisKineticBlock extends KineticBlock {
|
||||||
return state.getValue(HORIZONTAL_AXIS);
|
return state.getValue(HORIZONTAL_AXIS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KineticConnections getInitialConnections(BlockState state) {
|
||||||
|
return AllConnections.FULL_SHAFT.apply(state.getValue(HORIZONTAL_AXIS));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasShaftTowards(LevelReader world, BlockPos pos, BlockState state, Direction face) {
|
public boolean hasShaftTowards(LevelReader world, BlockPos pos, BlockState state, Direction face) {
|
||||||
return face.getAxis() == state.getValue(HORIZONTAL_AXIS);
|
return face.getAxis() == state.getValue(HORIZONTAL_AXIS);
|
||||||
|
|
|
@ -10,6 +10,8 @@ import com.simibubi.create.AllTileEntities;
|
||||||
import com.simibubi.create.content.contraptions.base.HorizontalAxisKineticBlock;
|
import com.simibubi.create.content.contraptions.base.HorizontalAxisKineticBlock;
|
||||||
import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock;
|
import com.simibubi.create.content.contraptions.relays.elementary.CogWheelBlock;
|
||||||
import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel;
|
import com.simibubi.create.content.contraptions.relays.elementary.ICogWheel;
|
||||||
|
import com.simibubi.create.content.contraptions.solver.AllConnections;
|
||||||
|
import com.simibubi.create.content.contraptions.solver.KineticConnections;
|
||||||
import com.simibubi.create.foundation.block.ITE;
|
import com.simibubi.create.foundation.block.ITE;
|
||||||
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
|
import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
|
||||||
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
|
import com.simibubi.create.foundation.utility.placement.PlacementHelpers;
|
||||||
|
@ -43,6 +45,11 @@ public class SpeedControllerBlock extends HorizontalAxisKineticBlock implements
|
||||||
super(properties);
|
super(properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KineticConnections getInitialConnections(BlockState state) {
|
||||||
|
return AllConnections.SPEED_CONTROLLER.apply(state.getValue(HORIZONTAL_AXIS));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
public BlockState getStateForPlacement(BlockPlaceContext context) {
|
||||||
BlockState above = context.getLevel()
|
BlockState above = context.getLevel()
|
||||||
|
|
|
@ -49,11 +49,14 @@ public class SpeedControllerTileEntity extends KineticTileEntity {
|
||||||
targetSpeed.value = DEFAULT_SPEED;
|
targetSpeed.value = DEFAULT_SPEED;
|
||||||
targetSpeed.moveText(new Vec3(9, 0, 10));
|
targetSpeed.moveText(new Vec3(9, 0, 10));
|
||||||
targetSpeed.withUnit(i -> Lang.translate("generic.unit.rpm"));
|
targetSpeed.withUnit(i -> Lang.translate("generic.unit.rpm"));
|
||||||
targetSpeed.withCallback(i -> this.updateTargetRotation());
|
|
||||||
targetSpeed.withStepFunction(CreativeMotorTileEntity::step);
|
targetSpeed.withStepFunction(CreativeMotorTileEntity::step);
|
||||||
behaviours.add(targetSpeed);
|
behaviours.add(targetSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getTargetSpeed() {
|
||||||
|
return targetSpeed.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
private void updateTargetRotation() {
|
private void updateTargetRotation() {
|
||||||
if (hasNetwork())
|
if (hasNetwork())
|
||||||
getOrCreateNetwork().remove(this);
|
getOrCreateNetwork().remove(this);
|
||||||
|
@ -123,7 +126,7 @@ public class SpeedControllerTileEntity extends KineticTileEntity {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ControllerValueBoxTransform extends ValueBoxTransform.Sided {
|
private static class ControllerValueBoxTransform extends ValueBoxTransform.Sided {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Vec3 getSouthLocation() {
|
protected Vec3 getSouthLocation() {
|
||||||
|
|
|
@ -2,7 +2,11 @@ package com.simibubi.create.content.contraptions.relays.elementary;
|
||||||
|
|
||||||
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
|
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerTileEntity;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
|
@ -23,4 +27,13 @@ public class SimpleKineticTileEntity extends KineticTileEntity {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getGeneratedSpeed() {
|
||||||
|
Block block = getBlockState().getBlock();
|
||||||
|
BlockEntity below = level.getBlockEntity(getBlockPos().below());
|
||||||
|
if (block instanceof ICogWheel cog && cog.isLargeCog()
|
||||||
|
&& below instanceof SpeedControllerTileEntity controller && controller.getSpeed() != 0)
|
||||||
|
return controller.getTargetSpeed();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import net.minecraft.core.Vec3i;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.AXIS;
|
import static net.minecraft.world.level.block.state.properties.BlockStateProperties.AXIS;
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ public class AllConnections {
|
||||||
public static final LazyMap<Axis, Type>
|
public static final LazyMap<Axis, Type>
|
||||||
TYPE_SHAFT = ValueType.map(),
|
TYPE_SHAFT = ValueType.map(),
|
||||||
TYPE_LARGE_COG = ValueType.map(),
|
TYPE_LARGE_COG = ValueType.map(),
|
||||||
TYPE_SMALL_COG = ValueType.map();
|
TYPE_SMALL_COG = ValueType.map(),
|
||||||
|
TYPE_SPEED_CONTROLLER_TOP = ValueType.map();
|
||||||
|
|
||||||
|
|
||||||
private static Direction pos(Axis axis) {
|
private static Direction pos(Axis axis) {
|
||||||
|
@ -48,6 +50,14 @@ public class AllConnections {
|
||||||
return new Entry(diff, TYPE_LARGE_COG.apply(from), TYPE_LARGE_COG.apply(to), ratio);
|
return new Entry(diff, TYPE_LARGE_COG.apply(from), TYPE_LARGE_COG.apply(to), ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Optional<Axis> oppAxis(Axis axis) {
|
||||||
|
return switch (axis) {
|
||||||
|
case X -> Optional.of(Axis.Z);
|
||||||
|
case Z -> Optional.of(Axis.X);
|
||||||
|
default -> Optional.empty();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final KineticConnections EMPTY = new KineticConnections();
|
public static final KineticConnections EMPTY = new KineticConnections();
|
||||||
|
|
||||||
|
@ -61,6 +71,7 @@ public class AllConnections {
|
||||||
LARGE_COG = new LazyMap<>(axis -> {
|
LARGE_COG = new LazyMap<>(axis -> {
|
||||||
Type large = TYPE_LARGE_COG.apply(axis);
|
Type large = TYPE_LARGE_COG.apply(axis);
|
||||||
Type small = TYPE_SMALL_COG.apply(axis);
|
Type small = TYPE_SMALL_COG.apply(axis);
|
||||||
|
|
||||||
List<Entry> out = new LinkedList<>();
|
List<Entry> out = new LinkedList<>();
|
||||||
Direction cur = DirectionHelper.getPositivePerpendicular(axis);
|
Direction cur = DirectionHelper.getPositivePerpendicular(axis);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
|
@ -70,12 +81,18 @@ public class AllConnections {
|
||||||
out.add(new Entry(cur.getNormal().relative(next), large, small, -2));
|
out.add(new Entry(cur.getNormal().relative(next), large, small, -2));
|
||||||
cur = next;
|
cur = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oppAxis(axis).ifPresent(opp -> {
|
||||||
|
Type sc = TYPE_SPEED_CONTROLLER_TOP.apply(opp);
|
||||||
|
out.add(new Entry(Direction.DOWN.getNormal(), large, sc).stressOnly());
|
||||||
|
});
|
||||||
return new KineticConnections(out);
|
return new KineticConnections(out);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
SMALL_COG = new LazyMap<>(axis -> {
|
SMALL_COG = new LazyMap<>(axis -> {
|
||||||
Type large = TYPE_LARGE_COG.apply(axis);
|
Type large = TYPE_LARGE_COG.apply(axis);
|
||||||
Type small = TYPE_SMALL_COG.apply(axis);
|
Type small = TYPE_SMALL_COG.apply(axis);
|
||||||
|
|
||||||
List<Entry> out = new LinkedList<>();
|
List<Entry> out = new LinkedList<>();
|
||||||
Direction cur = DirectionHelper.getPositivePerpendicular(axis);
|
Direction cur = DirectionHelper.getPositivePerpendicular(axis);
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
|
@ -84,11 +101,19 @@ public class AllConnections {
|
||||||
out.add(new Entry(cur.getNormal().relative(next), small, large, -0.5f));
|
out.add(new Entry(cur.getNormal().relative(next), small, large, -0.5f));
|
||||||
cur = next;
|
cur = next;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new KineticConnections(out);
|
return new KineticConnections(out);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
LARGE_COG_FULL_SHAFT = new LazyMap<>(axis -> LARGE_COG.apply(axis).merge(FULL_SHAFT.apply(axis))),
|
LARGE_COG_FULL_SHAFT = new LazyMap<>(axis -> LARGE_COG.apply(axis).merge(FULL_SHAFT.apply(axis))),
|
||||||
|
|
||||||
SMALL_COG_FULL_SHAFT = new LazyMap<>(axis -> SMALL_COG.apply(axis).merge(FULL_SHAFT.apply(axis)));
|
SMALL_COG_FULL_SHAFT = new LazyMap<>(axis -> SMALL_COG.apply(axis).merge(FULL_SHAFT.apply(axis))),
|
||||||
|
|
||||||
|
SPEED_CONTROLLER = new LazyMap<>(axis -> {
|
||||||
|
Type sc = TYPE_SPEED_CONTROLLER_TOP.apply(axis);
|
||||||
|
Type large = TYPE_LARGE_COG.apply(oppAxis(axis).get());
|
||||||
|
Vec3i up = Direction.UP.getNormal();
|
||||||
|
return new KineticConnections(new Entry(up, sc, large).stressOnly()).merge(FULL_SHAFT.apply(axis));
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,9 +32,16 @@ public class KineticConnections {
|
||||||
public Entry(Vec3i offset, Type type) {
|
public Entry(Vec3i offset, Type type) {
|
||||||
this(offset, type, type, 1);
|
this(offset, type, type, 1);
|
||||||
}
|
}
|
||||||
|
public Entry stressOnly() {
|
||||||
|
return new Entry(offset, new Value(value.from, value.to, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static record Value(Type from, Type to, float ratio) { }
|
private static record Value(Type from, Type to, float ratio) {
|
||||||
|
public boolean isStressOnly() {
|
||||||
|
return ratio == 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final Map<Vec3i, Value> connections;
|
private final Map<Vec3i, Value> connections;
|
||||||
|
|
||||||
|
@ -65,12 +72,26 @@ public class KineticConnections {
|
||||||
Value toValue = to.connections.get(offset.multiply(-1));
|
Value toValue = to.connections.get(offset.multiply(-1));
|
||||||
if (toValue == null) return Optional.empty();
|
if (toValue == null) return Optional.empty();
|
||||||
|
|
||||||
|
if (fromValue.isStressOnly() || toValue.isStressOnly()) return Optional.empty();
|
||||||
|
|
||||||
if (fromValue.from.compatible(toValue.to) && fromValue.to.compatible(toValue.from)
|
if (fromValue.from.compatible(toValue.to) && fromValue.to.compatible(toValue.from)
|
||||||
&& (Mth.equal(fromValue.ratio, 1/toValue.ratio) || (Mth.equal(toValue.ratio, 1/fromValue.ratio))))
|
&& (Mth.equal(fromValue.ratio, 1/toValue.ratio) || (Mth.equal(toValue.ratio, 1/fromValue.ratio))))
|
||||||
return Optional.of(fromValue.ratio);
|
return Optional.of(fromValue.ratio);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean checkStressOnlyConnection(KineticConnections to, Vec3i offset) {
|
||||||
|
Value fromValue = connections.get(offset);
|
||||||
|
if (fromValue == null) return false;
|
||||||
|
|
||||||
|
Value toValue = to.connections.get(offset.multiply(-1));
|
||||||
|
if (toValue == null) return false;
|
||||||
|
|
||||||
|
if (!fromValue.isStressOnly() || !toValue.isStressOnly()) return false;
|
||||||
|
|
||||||
|
return fromValue.from.compatible(toValue.to) && fromValue.to.compatible(toValue.from);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
if (this == o) return true;
|
if (this == o) return true;
|
||||||
|
@ -91,5 +112,9 @@ public class KineticConnections {
|
||||||
return new KineticConnections(out);
|
return new KineticConnections(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasStressOnlyConnections() {
|
||||||
|
return connections.values().stream().anyMatch(Value::isStressOnly);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ package com.simibubi.create.content.contraptions.solver;
|
||||||
|
|
||||||
import com.simibubi.create.foundation.utility.Pair;
|
import com.simibubi.create.foundation.utility.Pair;
|
||||||
|
|
||||||
|
import com.simibubi.create.foundation.utility.ResetableLazy;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -15,46 +17,71 @@ public class KineticNetwork {
|
||||||
private final Set<KineticNode> members = new HashSet<>();
|
private final Set<KineticNode> members = new HashSet<>();
|
||||||
private final Set<KineticNode> generators = new HashSet<>();
|
private final Set<KineticNode> generators = new HashSet<>();
|
||||||
private final Set<Pair<KineticNode, KineticNode>> conflictingCycles = new HashSet<>();
|
private final Set<Pair<KineticNode, KineticNode>> conflictingCycles = new HashSet<>();
|
||||||
|
|
||||||
private float rootTheoreticalSpeed;
|
private float rootTheoreticalSpeed;
|
||||||
private @Nullable KineticNode mainGenerator;
|
|
||||||
private boolean rootSpeedDirty;
|
private boolean rootSpeedDirty;
|
||||||
|
private @Nullable KineticNode mainGenerator;
|
||||||
|
|
||||||
private boolean overstressed;
|
private boolean overstressed;
|
||||||
|
|
||||||
private float rootSpeedCur;
|
private boolean rootSpeedChanged;
|
||||||
private float rootSpeedPrev;
|
|
||||||
private final Set<KineticNode> potentialNewBranches = new HashSet<>();
|
private final Set<KineticNode> potentialNewBranches = new HashSet<>();
|
||||||
|
|
||||||
|
private final ResetableLazy<Float> totalStressImpact = ResetableLazy.of(() ->
|
||||||
|
(float) members.stream().mapToDouble(n -> n.getTotalStressImpact(rootTheoreticalSpeed)).sum());
|
||||||
|
private final ResetableLazy<Float> totalStressCapacity = ResetableLazy.of(() ->
|
||||||
|
(float) members.stream().mapToDouble(KineticNode::getStressCapacity).sum());
|
||||||
|
|
||||||
|
private boolean ticked;
|
||||||
|
private final Set<KineticNode> stressConnectors = new HashSet<>();
|
||||||
|
|
||||||
public KineticNetwork(KineticNode root) {
|
public KineticNetwork(KineticNode root) {
|
||||||
addMember(root);
|
addMember(root);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addMember(KineticNode node) {
|
public void addMember(KineticNode node) {
|
||||||
members.add(node);
|
members.add(node);
|
||||||
|
potentialNewBranches.add(node);
|
||||||
|
if (node.getConnections().hasStressOnlyConnections()) stressConnectors.add(node);
|
||||||
|
|
||||||
if (node.isGenerator() && !generators.contains(node)) {
|
if (node.isGenerator() && !generators.contains(node)) {
|
||||||
generators.add(node);
|
generators.add(node);
|
||||||
rootSpeedDirty = true;
|
rootSpeedDirty = true;
|
||||||
|
rootSpeedChanged = true;
|
||||||
|
}
|
||||||
|
if (node.hasStressImpact()) onMemberStressImpactUpdated();
|
||||||
|
if (node.hasStressCapacity()) onMemberStressCapacityUpdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
potentialNewBranches.add(node);
|
public void onMemberGeneratedSpeedUpdated(KineticNode node) {
|
||||||
}
|
|
||||||
|
|
||||||
public void updateMember(KineticNode node) {
|
|
||||||
if (!members.contains(node)) throw new IllegalArgumentException();
|
|
||||||
|
|
||||||
if (node.isGenerator()) {
|
if (node.isGenerator()) {
|
||||||
generators.add(node);
|
generators.add(node);
|
||||||
} else {
|
} else {
|
||||||
generators.remove(node);
|
generators.remove(node);
|
||||||
}
|
}
|
||||||
rootSpeedDirty = true;
|
rootSpeedDirty = true;
|
||||||
|
rootSpeedChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMemberStressImpactUpdated() {
|
||||||
|
totalStressImpact.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onMemberStressCapacityUpdated() {
|
||||||
|
totalStressCapacity.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeMember(KineticNode node) {
|
public void removeMember(KineticNode node) {
|
||||||
members.remove(node);
|
|
||||||
if (node.isGenerator() && generators.contains(node)) {
|
if (node.isGenerator() && generators.contains(node)) {
|
||||||
generators.remove(node);
|
generators.remove(node);
|
||||||
rootSpeedDirty = true;
|
rootSpeedDirty = true;
|
||||||
|
rootSpeedChanged = true;
|
||||||
}
|
}
|
||||||
|
if (node.hasStressImpact()) onMemberStressImpactUpdated();
|
||||||
|
if (node.hasStressCapacity()) onMemberStressCapacityUpdated();
|
||||||
|
|
||||||
|
members.remove(node);
|
||||||
|
stressConnectors.remove(node);
|
||||||
conflictingCycles.removeIf(p -> p.getFirst() == node || p.getSecond() == node);
|
conflictingCycles.removeIf(p -> p.getFirst() == node || p.getSecond() == node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,73 +132,88 @@ public class KineticNetwork {
|
||||||
}
|
}
|
||||||
|
|
||||||
rootTheoreticalSpeed = newSpeed * sign;
|
rootTheoreticalSpeed = newSpeed * sign;
|
||||||
if (!overstressed) {
|
|
||||||
rootSpeedCur = rootTheoreticalSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
mainGenerator = newGenerator;
|
mainGenerator = newGenerator;
|
||||||
rootSpeedDirty = false;
|
rootSpeedDirty = false;
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public float getTotalStressImpact() {
|
||||||
* @return a List of new networks created during this function call
|
return totalStressImpact.get();
|
||||||
*/
|
|
||||||
public List<KineticNetwork> tick() {
|
|
||||||
List<KineticNetwork> newNetworks = updateMemberSpeeds();
|
|
||||||
|
|
||||||
if (generators.isEmpty()) {
|
|
||||||
overstressed = false;
|
|
||||||
members.forEach(KineticNode::stop);
|
|
||||||
members.forEach(KineticNode::flushChangedSpeed);
|
|
||||||
return newNetworks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float stressImpact = (float) members.stream().mapToDouble(n -> n.getTotalStressImpact(rootTheoreticalSpeed)).sum();
|
public float getTotalStressCapacity() {
|
||||||
float stressCapacity = (float) members.stream().mapToDouble(KineticNode::getStressCapacity).sum();
|
return totalStressCapacity.get();
|
||||||
|
}
|
||||||
|
|
||||||
if (stressImpact > stressCapacity) {
|
private float getRootSpeed() {
|
||||||
if (!overstressed) {
|
return isStopped() ? 0 : rootTheoreticalSpeed;
|
||||||
overstressed = true;
|
}
|
||||||
rootSpeedCur = 0;
|
|
||||||
members.forEach(KineticNode::stop);
|
public void untick() {
|
||||||
|
ticked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick(List<KineticNetwork> newNetworks) {
|
||||||
|
if (ticked) return;
|
||||||
|
|
||||||
|
Set<KineticNetwork> stressConnected = stressConnectors.stream()
|
||||||
|
.flatMap(KineticNode::getActiveStressOnlyConnections)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
stressConnected.add(this);
|
||||||
|
|
||||||
|
float stressImpact = 0;
|
||||||
|
float stressCapacity = 0;
|
||||||
|
|
||||||
|
for (KineticNetwork cur : stressConnected) {
|
||||||
|
cur.ticked = true;
|
||||||
|
cur.updateMemberSpeeds(newNetworks);
|
||||||
|
stressImpact += cur.getTotalStressImpact();
|
||||||
|
stressCapacity += cur.getTotalStressCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean nowOverstressed = stressImpact > stressCapacity;
|
||||||
|
|
||||||
|
for (KineticNetwork cur : stressConnected) {
|
||||||
|
if (cur.generators.isEmpty()) {
|
||||||
|
cur.overstressed = false;
|
||||||
|
} else if (nowOverstressed) {
|
||||||
|
if (!cur.overstressed) {
|
||||||
|
cur.overstressed = true;
|
||||||
|
rootSpeedChanged = true;
|
||||||
|
cur.members.forEach(KineticNode::stop);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (overstressed) {
|
if (cur.overstressed) {
|
||||||
overstressed = false;
|
cur.overstressed = false;
|
||||||
rootSpeedCur = rootTheoreticalSpeed;
|
rootSpeedChanged = true;
|
||||||
newNetworks.addAll(bulldozeContradictingMembers());
|
cur.bulldozeContradictingMembers(newNetworks);
|
||||||
newNetworks.addAll(updateMemberSpeeds());
|
cur.updateMemberSpeeds(newNetworks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
members.forEach(KineticNode::flushChangedSpeed);
|
cur.members.forEach(KineticNode::flushChangedSpeed);
|
||||||
return newNetworks;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the speed of every member, starting from the main generator and popping off speeding nodes along the way
|
* Update the speed of every member, starting from the main generator and popping off speeding nodes along the way
|
||||||
* @return a List of new networks created during this function call
|
* @param newNetworks a List that any new networks created during this call will be added to
|
||||||
*/
|
*/
|
||||||
private List<KineticNetwork> updateMemberSpeeds() {
|
private void updateMemberSpeeds(List<KineticNetwork> newNetworks) {
|
||||||
boolean rootSpeedChanged = rootSpeedPrev != rootSpeedCur;
|
|
||||||
rootSpeedPrev = rootSpeedCur;
|
|
||||||
|
|
||||||
// if we're stopped, then all members' speeds will be 0, so no need to check for speeding nodes
|
// if we're stopped, then all members' speeds will be 0, so no need to check for speeding nodes
|
||||||
if (isStopped()) {
|
if (isStopped()) {
|
||||||
members.forEach(KineticNode::stop);
|
members.forEach(KineticNode::stop);
|
||||||
return new LinkedList<>();
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SolveResult recalculateSpeedResult = tryRecalculateSpeed();
|
SolveResult recalculateSpeedResult = tryRecalculateSpeed();
|
||||||
// generators should not be turning against each other or have conflicting cycles by now
|
// generators should not be turning against each other or have conflicting cycles by now
|
||||||
assert(recalculateSpeedResult.isOk());
|
assert(recalculateSpeedResult.isOk());
|
||||||
|
|
||||||
List<KineticNetwork> newNetworks = new LinkedList<>();
|
|
||||||
|
|
||||||
if (rootSpeedChanged) {
|
if (rootSpeedChanged) {
|
||||||
// root speed changed, update all nodes starting from the main generator
|
// root speed changed, update all nodes starting from the main generator
|
||||||
|
rootSpeedChanged = false;
|
||||||
bfs(mainGenerator, newNetworks, false);
|
bfs(mainGenerator, newNetworks, false);
|
||||||
} else if (!potentialNewBranches.isEmpty()) {
|
} else if (!potentialNewBranches.isEmpty()) {
|
||||||
// new nodes added, update only the new network branches
|
// new nodes added, update only the new network branches
|
||||||
|
@ -180,8 +222,6 @@ public class KineticNetwork {
|
||||||
.forEach(n -> bfs(n, newNetworks, true));
|
.forEach(n -> bfs(n, newNetworks, true));
|
||||||
potentialNewBranches.clear();
|
potentialNewBranches.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
return newNetworks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bfs(KineticNode root, List<KineticNetwork> newNetworks, boolean followSource) {
|
private void bfs(KineticNode root, List<KineticNetwork> newNetworks, boolean followSource) {
|
||||||
|
@ -195,7 +235,7 @@ public class KineticNetwork {
|
||||||
if (!members.contains(cur) || visited.contains(cur)) continue;
|
if (!members.contains(cur) || visited.contains(cur)) continue;
|
||||||
visited.add(cur);
|
visited.add(cur);
|
||||||
|
|
||||||
if (cur.tryUpdateSpeed(rootSpeedCur).isOk()) {
|
if (cur.tryUpdateSpeed(getRootSpeed()).isOk()) {
|
||||||
cur.getActiveConnections()
|
cur.getActiveConnections()
|
||||||
.map(Pair::getFirst)
|
.map(Pair::getFirst)
|
||||||
.filter(n -> !followSource || n.getSource() == cur)
|
.filter(n -> !followSource || n.getSource() == cur)
|
||||||
|
@ -208,7 +248,7 @@ public class KineticNetwork {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<KineticNetwork> bulldozeContradictingMembers() {
|
private void bulldozeContradictingMembers(List<KineticNetwork> newNetworks) {
|
||||||
/*
|
/*
|
||||||
This method is necessary to handle the edge case where contradicting nodes have been added to the network while
|
This method is necessary to handle the edge case where contradicting nodes have been added to the network while
|
||||||
it was overstressed and now that it's moving again we need to pop them. Here we can't just stop following a
|
it was overstressed and now that it's moving again we need to pop them. Here we can't just stop following a
|
||||||
|
@ -216,10 +256,8 @@ public class KineticNetwork {
|
||||||
just pop all potentially contradicting nodes off and hope no one cares
|
just pop all potentially contradicting nodes off and hope no one cares
|
||||||
*/
|
*/
|
||||||
|
|
||||||
List<KineticNetwork> newNetworks = new LinkedList<>();
|
|
||||||
|
|
||||||
// generators running against network
|
// generators running against network
|
||||||
float sign = Math.signum(rootSpeedCur);
|
float sign = Math.signum(rootTheoreticalSpeed);
|
||||||
List<KineticNode> runningAgainst = generators.stream()
|
List<KineticNode> runningAgainst = generators.stream()
|
||||||
.filter(n -> Math.signum(n.getGeneratedSpeedAtRoot()) != sign)
|
.filter(n -> Math.signum(n.getGeneratedSpeedAtRoot()) != sign)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
@ -230,8 +268,6 @@ public class KineticNetwork {
|
||||||
.map(Pair::getFirst)
|
.map(Pair::getFirst)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
cycles.forEach(n -> { n.popBlock(); newNetworks.add(n.getNetwork()); });
|
cycles.forEach(n -> { n.popBlock(); newNetworks.add(n.getNetwork()); });
|
||||||
|
|
||||||
return newNetworks;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,8 @@ public class KineticNode {
|
||||||
|
|
||||||
this.connections = entity.getConnections();
|
this.connections = entity.getConnections();
|
||||||
this.generatedSpeed = entity.getGeneratedSpeed();
|
this.generatedSpeed = entity.getGeneratedSpeed();
|
||||||
this.stressCapacity = entity.getStressCapacity();
|
|
||||||
this.stressImpact = entity.getStressImpact();
|
this.stressImpact = entity.getStressImpact();
|
||||||
|
this.stressCapacity = entity.getStressCapacity();
|
||||||
|
|
||||||
this.network = new KineticNetwork(this);
|
this.network = new KineticNetwork(this);
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,14 @@ public class KineticNode {
|
||||||
return getActiveConnections().collect(Collectors.toList());
|
return getActiveConnections().collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Stream<KineticNetwork> getActiveStressOnlyConnections() {
|
||||||
|
return connections.getDirections().stream()
|
||||||
|
.map(d -> nodeAccessor.apply(entity.getBlockPos().offset(d))
|
||||||
|
.filter(n -> connections.checkStressOnlyConnection(n.connections, d)))
|
||||||
|
.flatMap(Optional::stream)
|
||||||
|
.map(KineticNode::getNetwork);
|
||||||
|
}
|
||||||
|
|
||||||
public float getGeneratedSpeedAtRoot() {
|
public float getGeneratedSpeedAtRoot() {
|
||||||
return generatedSpeed / speedRatio;
|
return generatedSpeed / speedRatio;
|
||||||
}
|
}
|
||||||
|
@ -79,17 +87,34 @@ public class KineticNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUpdated() {
|
public void onUpdated() {
|
||||||
float newSpeed = entity.getGeneratedSpeed();
|
float generatedSpeedNew = entity.getGeneratedSpeed();
|
||||||
if (generatedSpeed != newSpeed) {
|
if (this.generatedSpeed != generatedSpeedNew) {
|
||||||
generatedSpeed = newSpeed;
|
this.generatedSpeed = generatedSpeedNew;
|
||||||
network.updateMember(this);
|
network.onMemberGeneratedSpeedUpdated(this);
|
||||||
if (network.tryRecalculateSpeed().isContradiction()) {
|
if (network.tryRecalculateSpeed().isContradiction()) {
|
||||||
popBlock();
|
popBlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stressImpact = entity.getStressImpact();
|
float stressImpactNew = entity.getStressImpact();
|
||||||
stressCapacity = entity.getStressCapacity();
|
if (this.stressImpact != stressImpactNew) {
|
||||||
|
this.stressImpact = stressImpactNew;
|
||||||
|
network.onMemberStressImpactUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
float stressCapacityNew = entity.getStressCapacity();
|
||||||
|
if (this.stressCapacity != stressCapacityNew) {
|
||||||
|
this.stressCapacity = stressCapacityNew;
|
||||||
|
network.onMemberStressCapacityUpdated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasStressCapacity() {
|
||||||
|
return stressCapacity != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasStressImpact() {
|
||||||
|
return stressImpact != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getTheoreticalSpeed(float speedAtRoot) {
|
public float getTheoreticalSpeed(float speedAtRoot) {
|
||||||
|
@ -111,7 +136,7 @@ public class KineticNode {
|
||||||
return network.tryRecalculateSpeed();
|
return network.tryRecalculateSpeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KineticNode getSource() {
|
public @Nullable KineticNode getSource() {
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -55,17 +55,16 @@ public class KineticSolver {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tick() {
|
public void tick() {
|
||||||
Set<KineticNetwork> visited = new HashSet<>();
|
Set<KineticNetwork> networks = nodes.values().stream().map(KineticNode::getNetwork).collect(Collectors.toSet());
|
||||||
|
networks.forEach(KineticNetwork::untick);
|
||||||
|
|
||||||
List<KineticNetwork> frontier = new LinkedList<>();
|
List<KineticNetwork> frontier = new LinkedList<>();
|
||||||
|
|
||||||
Set<KineticNetwork> networks = nodes.values().stream().map(KineticNode::getNetwork).collect(Collectors.toSet());
|
|
||||||
for (KineticNetwork network : networks) {
|
for (KineticNetwork network : networks) {
|
||||||
frontier.add(network);
|
frontier.add(network);
|
||||||
while (!frontier.isEmpty()) {
|
while (!frontier.isEmpty()) {
|
||||||
KineticNetwork cur = frontier.remove(0);
|
KineticNetwork cur = frontier.remove(0);
|
||||||
if (visited.contains(cur)) continue;
|
cur.tick(frontier);
|
||||||
visited.add(cur);
|
|
||||||
frontier.addAll(cur.tick());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue