Rotation validation

- Contraptions can no longer speed up themselves when connected in a loop
- Components now validate from time to time if they still have a source
- Some minor refactoring in KineticTileEntity
- Empty networks are now being removed from the network space (fixes memory leak)
- Waterwheels no longer differentiate between a slight side-ways flow and a straight flow of water
This commit is contained in:
simibubi 2020-03-06 17:22:02 +01:00
parent fed4df515a
commit b89db104b8
20 changed files with 460 additions and 367 deletions

View file

@ -10,6 +10,7 @@ public class CKinetics extends ConfigBase {
public ConfigInt maxRotationSpeed = i(256, 64, "maxRotationSpeed", Comments.rpm, Comments.maxRotationSpeed);
public ConfigEnum<DeployerAggroSetting> ignoreDeployerAttacks =
e(DeployerAggroSetting.CREEPERS, "ignoreDeployerAttacks", Comments.ignoreDeployerAttacks);
public ConfigInt kineticValidationFrequency = i(60, 5, "kineticValidationFrequency", Comments.kineticValidationFrequency);
public ConfigGroup fan = group(0, "encasedFan", "Encased Fan");
public ConfigInt fanPushDistance = i(20, 5, "fanPushDistance", Comments.fanPushDistance);
@ -71,6 +72,7 @@ public class CKinetics extends ConfigBase {
static String ignoreDeployerAttacks = "Select what mobs should ignore Deployers when attacked by them.";
static String waterWheelSpeed = "Rotation speed gained by a water wheel for each side with running water. (halved if not against blades)";
static String disableStress = "Disable the Stress mechanic altogether.";
static String kineticValidationFrequency = "Game ticks between Kinetic Blocks checking whether their source is still valid.";
}
public static enum DeployerAggroSetting {

View file

@ -25,9 +25,9 @@ public class KineticNetwork {
members = new HashMap<>();
}
public void initFromTE(KineticTileEntity te) {
unloadedStressCapacity = te.maxStress;
unloadedStress = te.currentStress;
public void initFromTE(float maxStress, float currentStress) {
unloadedStressCapacity = maxStress;
unloadedStress = currentStress;
initialized = true;
updateStress();
updateStressCapacity();
@ -42,16 +42,13 @@ public class KineticNetwork {
sources.put(te, capacity);
}
float stressApplied = te.getStressApplied();
unloadedStress -= stressApplied * getStressMultiplierForSpeed(te.speed);
unloadedStress -= stressApplied * getStressMultiplierForSpeed(te.getTheoreticalSpeed());
members.put(te, stressApplied);
}
public void add(KineticTileEntity te) {
if (members.containsKey(te))
return;
// Debug.debugChatAndShowStack(te.getType().getRegistryName().getPath() + " added to Network", 5);
if (te.isSource()) {
sources.put(te, te.getAddedStressCapacity());
updateStressCapacity();
@ -75,9 +72,6 @@ public class KineticNetwork {
public void remove(KineticTileEntity te) {
if (!members.containsKey(te))
return;
// Debug.debugChat(te.getType().getRegistryName().getPath() + " removed from Network");
if (te.isSource()) {
sources.remove(te);
updateStressCapacity();
@ -86,6 +80,9 @@ public class KineticNetwork {
members.remove(te);
updateStress();
sync();
if (members.isEmpty())
TorquePropagator.networks.get(te.getWorld()).remove(this.id);
}
public void sync() {
@ -101,21 +98,17 @@ public class KineticNetwork {
if (maxStress != newMaxStress) {
maxStress = newMaxStress;
sync();
// Debug.debugChatAndShowStack("Current Stress level: " + currentStress + "/" + maxStress, 5);
}
}
public void updateStress() {
float presentStress = 0;
for (KineticTileEntity te : members.keySet())
presentStress += members.get(te) * getStressMultiplierForSpeed(te.speed);
presentStress += members.get(te) * getStressMultiplierForSpeed(te.getTheoreticalSpeed());
float newStress = presentStress + unloadedStress;
if (currentStress != newStress) {
currentStress = newStress;
sync();
// Debug.debugChatAndShowStack("Current Stress level: " + currentStress + "/" + maxStress, 5);
}
}

View file

@ -14,6 +14,7 @@ import com.simibubi.create.modules.contraptions.base.IRotate;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.relays.advanced.SpeedControllerTileEntity;
import com.simibubi.create.modules.contraptions.relays.belt.BeltTileEntity;
import com.simibubi.create.modules.contraptions.relays.encased.DirectionalShaftHalvesTileEntity;
import com.simibubi.create.modules.contraptions.relays.encased.EncasedBeltBlock;
import com.simibubi.create.modules.contraptions.relays.encased.SplitShaftTileEntity;
import com.simibubi.create.modules.contraptions.relays.gearbox.GearboxTileEntity;
@ -52,8 +53,8 @@ public class RotationPropagator {
if (axis.getCoordinate(diff.getX(), diff.getY(), diff.getZ()) != 0)
alignedAxes = false;
boolean connectedByAxis = alignedAxes
&& definitionFrom.hasShaftTowards(world, from.getPos(), stateFrom, direction)
boolean connectedByAxis =
alignedAxes && definitionFrom.hasShaftTowards(world, from.getPos(), stateFrom, direction)
&& definitionTo.hasShaftTowards(world, to.getPos(), stateTo, direction.getOpposite());
boolean connectedByGears = definitionFrom.hasCogsTowards(world, from.getPos(), stateFrom, direction)
@ -130,9 +131,9 @@ public class RotationPropagator {
}
private static float getAxisModifier(KineticTileEntity te, Direction direction) {
if (!te.hasSource())
if (!te.hasSource() || !(te instanceof DirectionalShaftHalvesTileEntity))
return 1;
Direction source = te.getSourceFacing();
Direction source = ((DirectionalShaftHalvesTileEntity) te).getSourceFacing();
if (te instanceof GearboxTileEntity)
return direction.getAxis() == source.getAxis() ? direction == source ? 1 : -1
@ -180,7 +181,7 @@ public class RotationPropagator {
return;
if (!worldIn.isBlockPresent(pos))
return;
if (addedTE.speed != 0) {
if (addedTE.getTheoreticalSpeed() != 0) {
propagateNewSource(addedTE);
return;
}
@ -188,16 +189,17 @@ public class RotationPropagator {
for (KineticTileEntity neighbourTE : getConnectedNeighbours(addedTE)) {
final float speedModifier = getRotationSpeedModifier(neighbourTE, addedTE);
if (neighbourTE.speed == 0)
float neighbourSpeed = neighbourTE.getTheoreticalSpeed();
if (neighbourSpeed == 0)
continue;
if (neighbourTE.hasSource() && neighbourTE.getSource().equals(addedTE.getPos())) {
addedTE.setSpeed(neighbourTE.speed * speedModifier);
addedTE.setSpeed(neighbourSpeed * speedModifier);
addedTE.onSpeedChanged(0);
addedTE.sendData();
continue;
}
addedTE.setSpeed(neighbourTE.speed * speedModifier);
addedTE.setSpeed(neighbourSpeed * speedModifier);
addedTE.setSource(neighbourTE.getPos());
addedTE.onSpeedChanged(0);
addedTE.sendData();
@ -209,23 +211,25 @@ public class RotationPropagator {
/**
* Search for sourceless networks attached to the given entity and update them.
*
* @param updateTE
* @param currentTE
*/
private static void propagateNewSource(KineticTileEntity updateTE) {
BlockPos pos = updateTE.getPos();
World world = updateTE.getWorld();
private static void propagateNewSource(KineticTileEntity currentTE) {
BlockPos pos = currentTE.getPos();
World world = currentTE.getWorld();
for (KineticTileEntity neighbourTE : getConnectedNeighbours(updateTE)) {
float modFromTo = getRotationSpeedModifier(updateTE, neighbourTE);
float modToFrom = getRotationSpeedModifier(neighbourTE, updateTE);
final float newSpeed = updateTE.speed * modFromTo;
float oppositeSpeed = neighbourTE.speed * modToFrom;
for (KineticTileEntity neighbourTE : getConnectedNeighbours(currentTE)) {
float modFromTo = getRotationSpeedModifier(currentTE, neighbourTE);
float modToFrom = getRotationSpeedModifier(neighbourTE, currentTE);
float speedOfCurrent = currentTE.getTheoreticalSpeed();
float speedOfNeighbour = neighbourTE.getTheoreticalSpeed();
float newSpeed = speedOfCurrent * modFromTo;
float oppositeSpeed = speedOfNeighbour * modToFrom;
boolean incompatible = Math.signum(newSpeed) != Math.signum(neighbourTE.speed)
&& (newSpeed != 0 && neighbourTE.speed != 0);
boolean incompatible =
Math.signum(newSpeed) != Math.signum(speedOfNeighbour) && (newSpeed != 0 && speedOfNeighbour != 0);
boolean tooFast = Math.abs(newSpeed) > AllConfigs.SERVER.kinetics.maxRotationSpeed.get();
boolean speedChangedTooOften = updateTE.speedChangeCounter > MAX_FLICKER_SCORE;
boolean speedChangedTooOften = currentTE.getFlickerScore() > MAX_FLICKER_SCORE;
if (tooFast || speedChangedTooOften) {
world.destroyBlock(pos, true);
return;
@ -238,43 +242,45 @@ public class RotationPropagator {
} else {
// Same direction: overpower the slower speed
if (Math.abs(oppositeSpeed) > Math.abs(updateTE.speed)) {
if (Math.abs(oppositeSpeed) > Math.abs(speedOfCurrent)) {
// Neighbour faster, overpower the incoming tree
updateTE.setSource(neighbourTE.getPos());
float prevSpeed = updateTE.getSpeed();
updateTE.setSpeed(neighbourTE.speed * getRotationSpeedModifier(neighbourTE, updateTE));
updateTE.onSpeedChanged(prevSpeed);
updateTE.sendData();
currentTE.setSource(neighbourTE.getPos());
float prevSpeed = currentTE.getSpeed();
currentTE.setSpeed(speedOfNeighbour * getRotationSpeedModifier(neighbourTE, currentTE));
currentTE.onSpeedChanged(prevSpeed);
currentTE.sendData();
propagateNewSource(updateTE);
propagateNewSource(currentTE);
return;
}
if (Math.abs(newSpeed) >= Math.abs(neighbourTE.speed)) {
if (Math.abs(newSpeed) > Math.abs(neighbourTE.speed) || updateTE.newNetworkID != null
&& !updateTE.newNetworkID.equals(neighbourTE.newNetworkID)) {
// Current faster, overpower the neighbours' tree
if (updateTE.hasSource() && updateTE.getSource().equals(neighbourTE.getPos())) {
updateTE.removeSource();
// Current faster, overpower the neighbours' tree
if (Math.abs(newSpeed) >= Math.abs(speedOfNeighbour)) {
if (!currentTE.canOverPower(neighbourTE)) {
if (Math.abs(newSpeed) > Math.abs(speedOfNeighbour))
world.destroyBlock(pos, true);
continue;
}
neighbourTE.setSource(updateTE.getPos());
if (currentTE.hasSource() && currentTE.getSource().equals(neighbourTE.getPos()))
currentTE.removeSource();
neighbourTE.setSource(currentTE.getPos());
float prevSpeed = neighbourTE.getSpeed();
neighbourTE.setSpeed(updateTE.speed * getRotationSpeedModifier(updateTE, neighbourTE));
neighbourTE.setSpeed(speedOfCurrent * getRotationSpeedModifier(currentTE, neighbourTE));
neighbourTE.onSpeedChanged(prevSpeed);
neighbourTE.sendData();
propagateNewSource(neighbourTE);
}
continue;
}
}
if (neighbourTE.speed == newSpeed)
if (neighbourTE.getTheoreticalSpeed() == newSpeed)
continue;
float prevSpeed = neighbourTE.getSpeed();
neighbourTE.setSpeed(newSpeed);
neighbourTE.setSource(updateTE.getPos());
neighbourTE.setSource(currentTE.getPos());
neighbourTE.onSpeedChanged(prevSpeed);
neighbourTE.sendData();
propagateNewSource(neighbourTE);
@ -294,7 +300,7 @@ public class RotationPropagator {
return;
if (removedTE == null)
return;
if (removedTE.speed == 0)
if (removedTE.getTheoreticalSpeed() == 0)
return;
for (BlockPos neighbourPos : getPotentialNeighbourLocations(removedTE)) {

View file

@ -27,22 +27,15 @@ public class TorquePropagator {
UUID id = te.getNetworkID();
KineticNetwork network;
Map<UUID, KineticNetwork> map = networks.get(te.getWorld());
if (id == null) {
network = new KineticNetwork();
if (id == null)
return null;
// Debug.debugChatAndShowStack(te.getType().getRegistryName().getPath() + " created new Network", 5);
te.newNetworkID = network.id;
te.updateNetwork = true;
map.put(id, network);
} else {
if (!map.containsKey(id)) {
network = new KineticNetwork();
network.id = te.getNetworkID();
map.put(id, network);
}
network = map.get(id);
}
return network;
}

View file

@ -1,14 +1,17 @@
package com.simibubi.create.modules.contraptions.base;
import java.util.Optional;
import java.util.UUID;
import com.simibubi.create.modules.contraptions.KineticNetwork;
import com.simibubi.create.modules.contraptions.base.IRotate.SpeedLevel;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.math.BlockPos;
public abstract class GeneratingKineticTileEntity extends KineticTileEntity {
public boolean reActivateSource;
public GeneratingKineticTileEntity(TileEntityType<?> typeIn) {
super(typeIn);
}
@ -18,68 +21,108 @@ public abstract class GeneratingKineticTileEntity extends KineticTileEntity {
}
@Override
public void reActivateSource() {
public void removeSource() {
if (hasSource() && isSource())
reActivateSource = true;
super.removeSource();
}
@Override
public void setSource(BlockPos source) {
super.setSource(source);
KineticTileEntity sourceTe = (KineticTileEntity) world.getTileEntity(source);
if (reActivateSource && sourceTe != null && Math.abs(sourceTe.getSpeed()) >= Math.abs(getGeneratedSpeed()))
reActivateSource = false;
}
@Override
public void tick() {
super.tick();
if (reActivateSource) {
updateGeneratedRotation();
reActivateSource = false;
}
}
public void updateGeneratedRotation() {
float speed = getGeneratedSpeed();
float prevSpeed = this.speed;
if (this.speed != speed) {
if (world.isRemote)
return;
if (!world.isRemote) {
if (prevSpeed != speed) {
if (!hasSource()) {
SpeedLevel levelBefore = SpeedLevel.of(this.speed);
SpeedLevel levelafter = SpeedLevel.of(speed);
if (levelBefore != levelafter)
queueRotationIndicators();
effects.queueRotationIndicators();
}
if (speed == 0) {
if (hasSource())
notifyStressCapacityChange(0);
else {
detachKinetics();
setSpeed(speed);
newNetworkID = null;
updateNetwork = true;
}
} else if (this.speed == 0) {
setSpeed(speed);
newNetworkID = UUID.randomUUID();
updateNetwork = true;
attachKinetics();
} else {
if (hasSource()) {
if (Math.abs(this.speed) >= Math.abs(speed)) {
if (Math.signum(this.speed) == Math.signum(speed))
notifyStressCapacityChange(getAddedStressCapacity());
else
world.destroyBlock(pos, true);
} else {
detachKinetics();
setSpeed(speed);
source = Optional.empty();
newNetworkID = UUID.randomUUID();
updateNetwork = true;
attachKinetics();
}
} else {
detachKinetics();
setSpeed(speed);
attachKinetics();
}
}
applyNewSpeed(prevSpeed, speed);
}
if (hasNetwork() && speed != 0) {
getNetwork().updateCapacityFor(this, getAddedStressCapacity());
getNetwork().updateStressCapacity();
getNetwork().updateStress();
KineticNetwork network = getNetwork();
network.updateCapacityFor(this, getAddedStressCapacity());
network.updateStress();
}
onSpeedChanged(prevSpeed);
sendData();
}
public void applyNewSpeed(float prevSpeed, float speed) {
// Speed changed to 0
if (speed == 0) {
if (hasSource() && hasNetwork()) {
getNetwork().updateCapacityFor(this, 0);
return;
}
detachKinetics();
setSpeed(speed);
newNetworkID = null;
updateNetwork = true;
return;
}
// Now turning - create a new Network
if (prevSpeed == 0) {
setSpeed(speed);
newNetworkID = UUID.randomUUID();
updateNetwork = true;
attachKinetics();
return;
}
// Change speed when overpowered by other generator
if (hasSource() && hasNetwork()) {
// Staying below Overpowered speed
if (Math.abs(prevSpeed) >= Math.abs(speed)) {
if (Math.signum(prevSpeed) != Math.signum(speed)) {
world.destroyBlock(pos, true);
return;
}
getNetwork().updateCapacityFor(this, getAddedStressCapacity());
return;
}
// Faster than attached network -> become the new source
detachKinetics();
setSpeed(speed);
source = null;
newNetworkID = UUID.randomUUID();
updateNetwork = true;
attachKinetics();
return;
}
// Reapply source
detachKinetics();
setSpeed(speed);
attachKinetics();
}
}

View file

@ -112,7 +112,7 @@ public abstract class KineticBlock extends Block implements IRotate {
return;
KineticTileEntity kte = (KineticTileEntity) tileEntity;
kte.queueRotationIndicators();
kte.effects.queueRotationIndicators();
}
public float getParticleTargetRadius() {

View file

@ -0,0 +1,116 @@
package com.simibubi.create.modules.contraptions.base;
import java.util.Random;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.base.IRotate.SpeedLevel;
import com.simibubi.create.modules.contraptions.particle.RotationIndicatorParticleData;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
public class KineticEffectHandler {
int overStressedTime;
float overStressedEffect;
int particleSpawnCountdown;
KineticTileEntity kte;
public KineticEffectHandler(KineticTileEntity kte) {
this.kte = kte;
}
public void tick() {
World world = kte.getWorld();
if (world.isRemote) {
if (overStressedTime > 0)
if (--overStressedTime == 0)
if (kte.overStressed) {
overStressedEffect = 1;
spawnEffect(ParticleTypes.SMOKE, 0.2f, 5);
} else {
overStressedEffect = -1;
spawnEffect(ParticleTypes.CLOUD, .075f, 2);
}
if (overStressedEffect != 0) {
overStressedEffect -= overStressedEffect * .1f;
if (Math.abs(overStressedEffect) < 1 / 128f)
overStressedEffect = 0;
}
} else if (particleSpawnCountdown > 0) {
if (--particleSpawnCountdown == 0)
spawnRotationIndicators();
}
}
public void queueRotationIndicators() {
particleSpawnCountdown = 2;
}
public void spawnEffect(IParticleData particle, float maxMotion, int amount) {
World world = kte.getWorld();
if (world == null)
return;
if (!world.isRemote)
return;
Random r = world.rand;
for (int i = 0; i < amount; i++) {
Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, r, maxMotion);
Vec3d position = VecHelper.getCenterOf(kte.getPos());
world.addParticle(particle, position.x, position.y, position.z, motion.x, motion.y, motion.z);
}
}
public void spawnRotationIndicators() {
float speed = kte.getSpeed();
if (speed == 0)
return;
BlockState state = kte.getBlockState();
Block block = state.getBlock();
if (!(block instanceof KineticBlock))
return;
KineticBlock kb = (KineticBlock) block;
float radius1 = kb.getParticleInitialRadius();
float radius2 = kb.getParticleTargetRadius();
Axis axis = kb.getRotationAxis(state);
BlockPos pos = kte.getPos();
World world = kte.getWorld();
if (axis == null)
return;
if (world == null)
return;
char axisChar = axis.name().charAt(0);
Vec3d vec = VecHelper.getCenterOf(pos);
SpeedLevel speedLevel = SpeedLevel.of(speed);
int color = speedLevel.getColor();
int particleSpeed = speedLevel.getParticleSpeed();
particleSpeed *= Math.signum(speed);
if (world instanceof ServerWorld) {
AllTriggers.triggerForNearbyPlayers(AllTriggers.ROTATION, world, pos, 5);
RotationIndicatorParticleData particleData =
new RotationIndicatorParticleData(color, particleSpeed, radius1, radius2, 10, axisChar);
((ServerWorld) world).spawnParticle(particleData, vec.x, vec.y, vec.z, 20, 0, 0, 0, 1);
}
}
public void triggerOverStressedEffect() {
overStressedTime = overStressedTime == 0 ? 2 : 0;
}
}

View file

@ -1,12 +1,7 @@
package com.simibubi.create.modules.contraptions.base;
import static net.minecraft.util.text.TextFormatting.GREEN;
import static net.minecraft.util.text.TextFormatting.WHITE;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
import com.simibubi.create.Create;
@ -14,60 +9,113 @@ import com.simibubi.create.config.AllConfigs;
import com.simibubi.create.foundation.advancement.AllTriggers;
import com.simibubi.create.foundation.behaviour.base.SmartTileEntity;
import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.modules.contraptions.KineticNetwork;
import com.simibubi.create.modules.contraptions.RotationPropagator;
import com.simibubi.create.modules.contraptions.base.IRotate.SpeedLevel;
import com.simibubi.create.modules.contraptions.base.IRotate.StressImpact;
import com.simibubi.create.modules.contraptions.particle.RotationIndicatorParticleData;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.NBTUtil;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.tileentity.ITickableTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.Direction.Axis;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.server.ServerWorld;
import net.minecraftforge.common.ForgeConfigSpec.ConfigValue;
public abstract class KineticTileEntity extends SmartTileEntity implements ITickableTileEntity {
int particleSpawnCountdown;
protected UUID networkID;
protected UUID newNetworkID;
protected float maxStress;
protected float currentStress;
protected boolean updateNetwork;
// Speed related
public float speed;
protected Optional<BlockPos> source;
public boolean reActivateSource;
public int speedChangeCounter;
// Torque related
public float maxStress;
public float currentStress;
protected KineticEffectHandler effects;
protected BlockPos source;
protected float speed;
protected boolean overStressed;
public UUID networkID;
public UUID newNetworkID;
public boolean updateNetwork;
protected boolean initNetwork;
// Client
int overStressedTime;
float overStressedEffect;
private int flickerTally;
private int validationCountdown;
public KineticTileEntity(TileEntityType<?> typeIn) {
super(typeIn);
speed = 0;
source = Optional.empty();
effects = new KineticEffectHandler(this);
}
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
public void tick() {
super.tick();
effects.tick();
if (world.isRemote)
return;
if (validationCountdown-- <= 0) {
validationCountdown = AllConfigs.SERVER.kinetics.kineticValidationFrequency.get();
validateKinetics();
}
if (getFlickerScore() > 0)
flickerTally = getFlickerScore() - 1;
if (initNetwork) {
initNetwork = false;
KineticNetwork network = getNetwork();
if (!network.initialized)
network.initFromTE(maxStress, currentStress);
network.addSilently(this);
}
if (updateNetwork) {
updateNetwork = false;
if (hasNetwork() && !networkID.equals(newNetworkID)) {
getNetwork().remove(this);
networkID = null;
maxStress = currentStress = 0;
overStressed = false;
}
if (newNetworkID != null) {
networkID = newNetworkID;
KineticNetwork network = getNetwork();
network.initialized = true;
network.add(this);
}
sendData();
}
}
private void validateKinetics() {
if (hasSource()) {
if (!world.isBlockPresent(source))
return;
KineticTileEntity sourceTe = (KineticTileEntity) world.getTileEntity(source);
if (sourceTe == null || sourceTe.speed == 0) {
removeSource();
detachKinetics();
return;
}
if (hasNetwork() && maxStress == 0) {
for (KineticTileEntity kineticTileEntity : getNetwork().members.keySet())
kineticTileEntity.removeSource();
return;
}
return;
}
if (speed != 0) {
if (getGeneratedSpeed() == 0)
setSpeed(0);
}
}
public void sync(float maxStress, float currentStress) {
@ -100,46 +148,35 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
return stressEntries.get(path).get().floatValue();
}
protected void notifyStressChange(float stress) {
getNetwork().updateStressFor(this, stress);
}
@Override
public boolean hasFastRenderer() {
return true;
}
public void onSpeedChanged(float previousSpeed) {
boolean fromOrToZero = (previousSpeed == 0) != (getSpeed() == 0);
boolean directionSwap = !fromOrToZero && Math.signum(previousSpeed) != Math.signum(getSpeed());
if (fromOrToZero || directionSwap) {
speedChangeCounter += 5;
flickerTally = getFlickerScore() + 5;
}
}
@Override
public void remove() {
if (world.isRemote) {
super.remove();
return;
}
if (hasNetwork()) {
if (!world.isRemote) {
if (hasNetwork())
getNetwork().remove(this);
detachKinetics();
}
RotationPropagator.handleRemoved(getWorld(), getPos(), this);
super.remove();
}
@Override
public CompoundNBT write(CompoundNBT compound) {
compound.putFloat("Speed", speed);
if (hasSource())
compound.put("Source", NBTUtil.writeBlockPos(getSource()));
compound.put("Source", NBTUtil.writeBlockPos(source));
if (hasNetwork()) {
compound.putFloat("MaxStress", maxStress);
compound.putFloat("Stress", currentStress);
compound.put("Id", NBTUtil.writeUniqueId(getNetworkID()));
compound.put("Id", NBTUtil.writeUniqueId(networkID));
}
return super.write(compound);
@ -147,23 +184,21 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
@Override
public void read(CompoundNBT compound) {
setSpeed(compound.getFloat("Speed"));
setSource(null);
if (compound.contains("Source")) {
CompoundNBT tagSource = compound.getCompound("Source");
setSource(NBTUtil.readBlockPos(tagSource));
}
speed = compound.getFloat("Speed");
source = null;
networkID = newNetworkID = null;
overStressed = false;
if (compound.contains("Source"))
source = NBTUtil.readBlockPos(compound.getCompound("Source"));
if (compound.contains("Id")) {
maxStress = compound.getFloat("MaxStress");
currentStress = compound.getFloat("Stress");
overStressed = maxStress < currentStress && StressImpact.isEnabled();
setNetworkID(NBTUtil.readUniqueId(compound.getCompound("Id")));
networkID = NBTUtil.readUniqueId(compound.getCompound("Id"));
newNetworkID = networkID;
initNetwork = true;
} else {
networkID = newNetworkID = null;
overStressed = false;
}
super.read(compound);
@ -174,7 +209,7 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
boolean overStressedBefore = overStressed;
super.readClientUpdate(tag);
if (overStressedBefore != overStressed && speed != 0)
overStressedTime = overStressedTime == 0 ? 2 : 0;
effects.triggerOverStressedEffect();
}
public boolean isSource() {
@ -184,6 +219,10 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
public float getSpeed() {
if (overStressed)
return 0;
return getTheoreticalSpeed();
}
public float getTheoreticalSpeed() {
return speed;
}
@ -196,31 +235,22 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
}
public boolean hasSource() {
return source.isPresent();
return source != null;
}
public BlockPos getSource() {
return source.get();
}
public Direction getSourceFacing() {
BlockPos source = getSource().subtract(getPos());
return Direction.getFacingFromVector(source.getX(), source.getY(), source.getZ());
return source;
}
public void setSource(BlockPos source) {
this.source = Optional.ofNullable(source);
this.source = source;
if (world == null || world.isRemote)
return;
if (source == null)
return;
KineticTileEntity sourceTe = (KineticTileEntity) world.getTileEntity(source);
if (sourceTe == null)
return;
if (reActivateSource && Math.abs(sourceTe.getSpeed()) >= Math.abs(getGeneratedSpeed())) {
reActivateSource = false;
KineticTileEntity sourceTe = (KineticTileEntity) world.getTileEntity(source);
if (sourceTe == null) {
removeSource();
return;
}
newNetworkID = sourceTe.newNetworkID;
@ -228,10 +258,7 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
}
public void removeSource() {
if (hasSource() && isSource())
reActivateSource = true;
this.source = Optional.empty();
source = null;
newNetworkID = null;
updateNetwork = true;
float prevSpeed = getSpeed();
@ -247,10 +274,8 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
return networkID != null;
}
public void applyNewSpeed(float speed) {
detachKinetics();
this.speed = speed;
attachKinetics();
public boolean canOverPower(KineticTileEntity other) {
return newNetworkID != null && !newNetworkID.equals(other.newNetworkID);
}
public void attachKinetics() {
@ -269,79 +294,6 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
this.networkID = networkID;
}
/**
* Callback for source blocks to re-apply their speed when an overpowering
* source is removed
*/
public void reActivateSource() {
}
@Override
public void tick() {
super.tick();
if (world.isRemote) {
if (overStressedTime > 0)
if (--overStressedTime == 0)
if (overStressed) {
overStressedEffect = 1;
spawnEffect(ParticleTypes.SMOKE, 0.2f, 5);
} else {
overStressedEffect = -1;
spawnEffect(ParticleTypes.CLOUD, .075f, 2);
}
if (overStressedEffect != 0) {
overStressedEffect -= overStressedEffect * .1f;
if (Math.abs(overStressedEffect) < 1 / 128f)
overStressedEffect = 0;
}
return;
}
if (speedChangeCounter > 0)
speedChangeCounter--;
if (particleSpawnCountdown > 0)
if (--particleSpawnCountdown == 0)
spawnRotationIndicators();
if (initNetwork) {
initNetwork = false;
KineticNetwork network = getNetwork();
if (!network.initialized)
network.initFromTE(this);
network.addSilently(this);
}
if (updateNetwork) {
updateNetwork = false;
if (hasNetwork() && !networkID.equals(newNetworkID)) {
getNetwork().remove(this);
networkID = null;
maxStress = currentStress = 0;
overStressed = false;
}
if (newNetworkID != null) {
networkID = newNetworkID;
KineticNetwork network = getNetwork();
network.initialized = true;
network.add(this);
}
sendData();
}
if (reActivateSource) {
reActivateSource();
reActivateSource = false;
}
}
public boolean isSpeedRequirementFulfilled() {
BlockState state = getBlockState();
if (!(getBlockState().getBlock() instanceof IRotate))
@ -357,60 +309,17 @@ public abstract class KineticTileEntity extends SmartTileEntity implements ITick
return true;
}
public void addDebugInformation(List<String> lines) {
lines.add("Speed: " + GREEN + speed);
lines.add("Cost: " + GREEN + getStressApplied() + WHITE + "/" + GREEN + getAddedStressCapacity());
lines.add("Stress: " + GREEN + currentStress + WHITE + "/" + GREEN + maxStress);
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) {
}
public void queueRotationIndicators() {
// wait a few ticks for network jamming etc
particleSpawnCountdown = 2;
@Override
public boolean hasFastRenderer() {
return true;
}
protected void spawnEffect(IParticleData particle, float maxMotion, int amount) {
if (!hasWorld())
return;
if (!world.isRemote)
return;
Random r = getWorld().rand;
for (int i = 0; i < amount; i++) {
Vec3d motion = VecHelper.offsetRandomly(Vec3d.ZERO, r, maxMotion);
Vec3d position = VecHelper.getCenterOf(pos);
this.getWorld().addParticle(particle, position.x, position.y, position.z, motion.x, motion.y, motion.z);
}
}
protected void spawnRotationIndicators() {
if (getSpeed() == 0)
return;
BlockState state = getBlockState();
Block block = state.getBlock();
if (!(block instanceof KineticBlock))
return;
KineticBlock kb = (KineticBlock) block;
float radius1 = kb.getParticleInitialRadius();
float radius2 = kb.getParticleTargetRadius();
Axis axis = kb.getRotationAxis(state);
if (axis == null)
return;
char axisChar = axis.name().charAt(0);
Vec3d vec = VecHelper.getCenterOf(pos);
SpeedLevel speedLevel = SpeedLevel.of(getSpeed());
int color = speedLevel.getColor();
int particleSpeed = speedLevel.getParticleSpeed();
particleSpeed *= Math.signum(getSpeed());
if (getWorld() instanceof ServerWorld) {
AllTriggers.triggerForNearbyPlayers(AllTriggers.ROTATION, world, pos, 5);
RotationIndicatorParticleData particleData =
new RotationIndicatorParticleData(color, particleSpeed, radius1, radius2, 10, axisChar);
((ServerWorld) getWorld()).spawnParticle(particleData, vec.x, vec.y, vec.z, 20, 0, 0, 0, 1);
}
public int getFlickerScore() {
return flickerTally;
}
}

View file

@ -24,8 +24,8 @@ public class KineticTileEntityRenderer extends SafeTileEntityRendererFast<Kineti
public static boolean rainbowMode = false;
@Override
public void renderFast(KineticTileEntity te, double x, double y, double z, float partialTicks,
int destroyStage, BufferBuilder buffer) {
public void renderFast(KineticTileEntity te, double x, double y, double z, float partialTicks, int destroyStage,
BufferBuilder buffer) {
renderRotatingBuffer(te, getWorld(), getRotatedModel(te), x, y, z, buffer);
}
@ -68,11 +68,12 @@ public class KineticTileEntityRenderer extends SafeTileEntityRendererFast<Kineti
else
buffer.color(white);
} else {
if (te.overStressedEffect != 0)
if (te.overStressedEffect > 0)
buffer.color(ColorHelper.mixColors(white, 0xFF0000, te.overStressedEffect));
float overStressedEffect = te.effects.overStressedEffect;
if (overStressedEffect != 0)
if (overStressedEffect > 0)
buffer.color(ColorHelper.mixColors(white, 0xFF0000, overStressedEffect));
else
buffer.color(ColorHelper.mixColors(white, 0x00FFBB, -te.overStressedEffect));
buffer.color(ColorHelper.mixColors(white, 0x00FFBB, -overStressedEffect));
else
buffer.color(white);
}

View file

@ -63,7 +63,7 @@ public class DeployerTileEntityRenderer extends SafeTileEntityRenderer<DeployerT
float yRot = AngleHelper.horizontalAngle(facing) + 180;
float zRot = facing == Direction.UP ? 90 : facing == Direction.DOWN ? 270 : 0;
boolean displayMode = facing == Direction.UP && te.speed == 0 && !punching;
boolean displayMode = facing == Direction.UP && te.getSpeed() == 0 && !punching;
GlStateManager.rotatef(yRot, 0, 1, 0);
if (!displayMode) {

View file

@ -86,7 +86,7 @@ public class SawBlock extends DirectionalAxisKineticBlock implements IWithTileEn
withTileEntityDo(worldIn, pos, te -> {
if (te.getSpeed() == 0)
return;
entityIn.attackEntityFrom(damageSourceSaw, MathHelper.clamp(Math.abs(te.speed / 512f) + 1, 0, 20));
entityIn.attackEntityFrom(damageSourceSaw, MathHelper.clamp(Math.abs(te.getSpeed() / 512f) + 1, 0, 20));
});
}

View file

@ -89,6 +89,7 @@ public class WaterWheelBlock extends HorizontalKineticBlock {
flowVec = flowVec.scale(f.getAxisDirection().getOffset());
boolean clockwise = wf.getAxisDirection() == AxisDirection.POSITIVE;
int clockwiseMultiplier = 2;
flowVec = new Vec3d(Math.signum(flowVec.x), Math.signum(flowVec.y), Math.signum(flowVec.z));
if (wf.getAxis() == Axis.Z) {
if (f.getAxis() == Axis.Y)

View file

@ -65,12 +65,13 @@ public class SpeedControllerTileEntity extends KineticTileEntity {
if (targetSpeed == 0)
return 0;
if (targetingController && cogWheel.speed == 0)
float wheelSpeed = cogWheel.getTheoreticalSpeed();
if (targetingController && wheelSpeed == 0)
return 1;
if (!speedController.hasSource()) {
if (targetingController)
return targetSpeed / cogWheel.speed;
return targetSpeed / wheelSpeed;
return 1;
}
@ -78,8 +79,8 @@ public class SpeedControllerTileEntity extends KineticTileEntity {
if (wheelPowersController) {
if (targetingController)
return targetSpeed / cogWheel.speed;
return cogWheel.speed / targetSpeed;
return targetSpeed / wheelSpeed;
return wheelSpeed / targetSpeed;
}
if (targetingController)

View file

@ -347,7 +347,7 @@ public class BeltBlock extends HorizontalKineticBlock implements IHaveNoBlockIte
return;
BeltTileEntity beltEntity = (BeltTileEntity) tileEntity;
BlockPos controller = beltEntity.getController();
beltEntity.setSource(null);
beltEntity.removeSource();
beltEntity.remove();
int limit = 1000;
@ -368,7 +368,7 @@ public class BeltBlock extends HorizontalKineticBlock implements IHaveNoBlockIte
inv.eject(stack);
}
te.setSource(null);
te.removeSource();
te.remove();
if (destroyedBlock.get(CASING))

View file

@ -194,8 +194,8 @@ public class BeltConnectorItem extends BlockItem implements IAddedByOther {
if (axis != world.getBlockState(second).get(BlockStateProperties.AXIS))
return false;
float speed1 = ((KineticTileEntity) world.getTileEntity(first)).speed;
float speed2 = ((KineticTileEntity) world.getTileEntity(second)).speed;
float speed1 = ((KineticTileEntity) world.getTileEntity(first)).getTheoreticalSpeed();
float speed2 = ((KineticTileEntity) world.getTileEntity(second)).getTheoreticalSpeed();
if (Math.signum(speed1) != Math.signum(speed2) && speed1 != 0 && speed2 != 0)
return false;

View file

@ -0,0 +1,20 @@
package com.simibubi.create.modules.contraptions.relays.encased;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
public class DirectionalShaftHalvesTileEntity extends KineticTileEntity {
public DirectionalShaftHalvesTileEntity(TileEntityType<?> typeIn) {
super(typeIn);
}
public Direction getSourceFacing() {
BlockPos source = getSource().subtract(getPos());
return Direction.getFacingFromVector(source.getX(), source.getY(), source.getZ());
}
}

View file

@ -1,11 +1,9 @@
package com.simibubi.create.modules.contraptions.relays.encased;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
public abstract class SplitShaftTileEntity extends KineticTileEntity {
public abstract class SplitShaftTileEntity extends DirectionalShaftHalvesTileEntity {
public SplitShaftTileEntity(TileEntityType<?> typeIn) {
super(typeIn);

View file

@ -133,13 +133,14 @@ public class GaugeInformationRenderer {
String _generatorStatsTitle = Lang.translate("gui.goggles.generator_stats");
String _capacityProvided = Lang.translate("tooltip.capacityProvided");
if (te.speed != te.getGeneratedSpeed() && te.speed != 0)
addedStressCapacity *= (te.getGeneratedSpeed() / te.speed);
float speed = te.getTheoreticalSpeed();
if (speed != te.getGeneratedSpeed() && speed != 0)
addedStressCapacity *= (te.getGeneratedSpeed() / speed);
tooltip.add(spacing + _generatorStatsTitle);
tooltip.add(spacing + GRAY + _capacityProvided);
float actualSpeed = Math.abs(te.speed);
float actualSpeed = Math.abs(speed);
float relativeCap = 0;
if (actualSpeed != 0)
relativeCap = addedStressCapacity * actualSpeed;
@ -176,8 +177,8 @@ public class GaugeInformationRenderer {
}
StressGaugeTileEntity stressGauge = (StressGaugeTileEntity) te;
List<String> stressLevels = Lang.translatedOptions("tooltip.stressImpact", "low", "medium", "high");
double stress = stressGauge.currentStress;
double cap = stressGauge.maxStress;
double stress = stressGauge.getNetworkStress();
double cap = stressGauge.getNetworkCapacity();
double relStress = stress / (cap == 0 ? 1 : cap);
StressImpact impactId = relStress > 1 ? null
: (relStress > .75f) ? StressImpact.HIGH
@ -199,17 +200,17 @@ public class GaugeInformationRenderer {
level += " (" + (int) (relStress * 100) + "%)";
float actualSpeed = stressGauge.speed;
if (actualSpeed == 0)
float theoreticalSpeed = stressGauge.getTheoreticalSpeed();
if (theoreticalSpeed == 0)
level = DARK_GRAY + ItemDescription.makeProgressBar(3, -1) + _noRotation;
tooltip.add(spacing + GRAY + _stressGaugeTitle);
tooltip.add(spacing + level);
if (actualSpeed != 0) {
if (theoreticalSpeed != 0) {
tooltip.add(spacing + GRAY + _capacity);
String capacity = color + "" + format((cap - stress) / Math.abs(actualSpeed)) + _stressUnit + " "
String capacity = color + "" + format((cap - stress) / Math.abs(theoreticalSpeed)) + _stressUnit + " "
+ DARK_GRAY + _atCurrentSpeed;
String capacityAtBase = GRAY + "" + format(cap - stress) + _stressUnit + " " + DARK_GRAY + _baseValue;
tooltip.add(spacing + " " + capacity);
@ -221,9 +222,10 @@ public class GaugeInformationRenderer {
if (!(te instanceof SpeedGaugeTileEntity))
return;
SpeedGaugeTileEntity speedGauge = (SpeedGaugeTileEntity) te;
boolean overstressed = speedGauge.currentStress > speedGauge.maxStress && speedGauge.speed != 0;
float speed = speedGauge.getTheoreticalSpeed();
boolean overstressed = speedGauge.getSpeed() == 0 && speed != 0;
SpeedLevel speedLevel = SpeedLevel.of(speedGauge.speed);
SpeedLevel speedLevel = SpeedLevel.of(speed);
String color = speedLevel.getTextColor() + "";
if (overstressed)
color = DARK_GRAY + "" + TextFormatting.STRIKETHROUGH;
@ -232,7 +234,7 @@ public class GaugeInformationRenderer {
int index = speedLevel.ordinal();
String level = color + ItemDescription.makeProgressBar(3, index)
+ (speedLevel != SpeedLevel.NONE ? speedLevels.get(index) : "");
level += " (" + format(Math.abs(speedGauge.speed)) + "" + _rpmUnit + ") ";
level += " (" + format(Math.abs(speed)) + "" + _rpmUnit + ") ";
tooltip.add(spacing + GRAY + _speedGaugeTitle);
tooltip.add(spacing + level);

View file

@ -44,4 +44,12 @@ public class StressGaugeTileEntity extends GaugeTileEntity {
sync(maxStress, currentStress);
}
public float getNetworkStress() {
return currentStress;
}
public float getNetworkCapacity() {
return maxStress;
}
}

View file

@ -1,9 +1,9 @@
package com.simibubi.create.modules.contraptions.relays.gearbox;
import com.simibubi.create.AllTileEntities;
import com.simibubi.create.modules.contraptions.base.KineticTileEntity;
import com.simibubi.create.modules.contraptions.relays.encased.DirectionalShaftHalvesTileEntity;
public class GearboxTileEntity extends KineticTileEntity {
public class GearboxTileEntity extends DirectionalShaftHalvesTileEntity {
public GearboxTileEntity() {
super(AllTileEntities.GEARBOX.type);