Declutter and distribute for easier new additions

This commit is contained in:
grimmauld 2021-04-20 21:47:26 +02:00
parent 4fa551b452
commit 838eeeee10
6 changed files with 232 additions and 188 deletions

View file

@ -1,11 +1,5 @@
package com.simibubi.create.content.optics; package com.simibubi.create.content.optics;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.Nullable;
import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.matrix.MatrixStack;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
@ -14,6 +8,12 @@ import net.minecraft.item.DyeColor;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
public class Beam extends ArrayList<BeamSegment> { public class Beam extends ArrayList<BeamSegment> {
private final Set<ILightHandler> lightEventListeners; private final Set<ILightHandler> lightEventListeners;
@Nullable @Nullable
@ -68,10 +68,16 @@ public class Beam extends ArrayList<BeamSegment> {
} }
public boolean isRemoved() { public boolean isRemoved() {
// || !get(0).getHandler().getOutBeams().contains(this)
ILightHandler handler = getHandler(); ILightHandler handler = getHandler();
removed = removed || isEmpty() || handler == null || handler.getTile() removed = removed || isEmpty() || handler == null || handler.getTile()
.isRemoved() || (parent != null && parent.isRemoved()); .isRemoved() || (parent != null && parent.isRemovedSimple());
return removed;
}
private boolean isRemovedSimple() {
ILightHandler handler = getHandler();
removed = removed || isEmpty() || handler == null || handler.getTile()
.isRemoved();
return removed; return removed;
} }
@ -88,4 +94,10 @@ public class Beam extends ArrayList<BeamSegment> {
return out; return out;
} }
@Nullable
public Beam getParent() {
return parent;
}
} }

View file

@ -0,0 +1,119 @@
package com.simibubi.create.content.optics;
import com.google.common.collect.Iterators;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.BeaconHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.tileentity.BeaconTileEntity;
import net.minecraft.tileentity.TileEntity;
import javax.annotation.Nullable;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
public abstract class LightHandlingBehaviour<T extends SmartTileEntity & ILightHandlerProvider> extends TileEntityBehaviour implements ILightHandler {
protected final T handler;
public Set<Beam> beams;
private boolean isUpdating;
@Nullable
private BeaconTileEntity beacon;
private Beam beaconBeam = null;
public LightHandlingBehaviour(T te) {
super(te);
handler = te;
isUpdating = false;
beams = new HashSet<>();
}
@Override
public void tick() {
super.tick();
if (beacon != null && beacon.isRemoved())
updateBeaconState();
}
protected void updateBeaconState() {
BeaconTileEntity beaconBefore = beacon;
beacon = BeaconHelper.getBeaconTE(getBlockPos(), getHandlerWorld())
.orElse(null);
if (beaconBefore != null) {
beaconBeam.clear();
beaconBeam = null;
updateBeams();
}
if (beacon != null) {
beaconBeam = constructOutBeam(null, VecHelper.UP, beacon.getPos());
if (beaconBeam != null && !beaconBeam.isEmpty()) {
beaconBeam.addListener(this);
beaconBeam.onCreated();
}
}
}
@Override
public TileEntity getTile() {
return tileEntity;
}
@Override
public void lazyTick() {
super.lazyTick();
updateBeaconState();
updateBeams();
}
@Override
public void updateBeams() {
if (isUpdating)
return;
isUpdating = true;
Set<Beam> newBeams = new HashSet<>();
for (Beam child : new HashSet<>(beams)) {
Beam parent = child.getParent();
child.onRemoved();
if (parent == null || parent.isRemoved())
continue;
constructSubBeams(parent, newBeams).forEach(Beam::onCreated);
}
beams = newBeams;
isUpdating = false;
}
@Override
public Stream<Beam> constructSubBeams(Beam beam) {
if (beams.stream()
.map(Beam::getParent)
.filter(Objects::nonNull)
.filter(((Predicate<Beam>) Beam::isRemoved).negate())
.map(Beam::getDirection)
.filter(Objects::nonNull)
.anyMatch(b -> b.equals(beam.getDirection())))
return Stream.empty();
return constructSubBeams(beam, beams);
}
public Stream<Beam> constructSubBeams(Beam beam, Set<Beam> beamListing) {
return safeConstructSubBeamsFor(beam)
.filter(Objects::nonNull)
.filter(((Predicate<Beam>) Beam::isEmpty).negate())
.peek(beamListing::add);
}
protected abstract Stream<Beam> safeConstructSubBeamsFor(Beam beam);
@Override
public Iterator<Beam> getRenderBeams() {
Iterator<Beam> beaconIter = beaconBeam == null ? Collections.emptyIterator() : Collections.singleton(beaconBeam)
.iterator();
return Iterators.concat(beams.iterator(), beaconIter);
}
}

View file

@ -0,0 +1,72 @@
package com.simibubi.create.content.optics;
import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.optics.mirror.RotationMode;
import com.simibubi.create.foundation.utility.ServerSpeedProvider;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.math.MathHelper;
public abstract class RotatedLightHandlingBehaviour<T extends KineticTileEntity & ILightHandlerProvider & RotationMode.RotationModeProvider> extends LightHandlingBehaviour<T> {
protected float angle;
protected float clientAngleDiff;
private float prevAngle;
public RotatedLightHandlingBehaviour(T te) {
super(te);
}
@Override
public void write(CompoundNBT nbt, boolean clientPacket) {
nbt.putFloat("Angle", angle);
super.write(nbt, clientPacket);
}
@Override
public void read(CompoundNBT nbt, boolean clientPacket) {
angle = nbt.getFloat("Angle");
super.read(nbt, clientPacket);
}
public float getInterpolatedAngle(float partialTicks) {
if (tileEntity.isVirtual())
return MathHelper.lerp(partialTicks + .5f, prevAngle, angle);
if (handler.getMode() == RotationMode.ROTATE_LIMITED && Math.abs(angle) == 90)
return angle;
return MathHelper.lerp(partialTicks, angle, angle + getAngularSpeed());
}
public float getAngularSpeed() {
float speed = handler.getSpeed() * 3 / 10f;
if (handler.getSpeed() == 0)
speed = 0;
if (getHandlerWorld() != null && getHandlerWorld().isRemote) {
speed *= ServerSpeedProvider.get();
speed += clientAngleDiff / 3f;
}
return speed;
}
@Override
public void tick() {
super.tick();
prevAngle = angle;
if (getHandlerWorld() != null && getHandlerWorld().isRemote)
clientAngleDiff /= 2;
float angularSpeed = getAngularSpeed();
float newAngle = angle + angularSpeed;
angle = newAngle % 360;
if (handler.getMode() == RotationMode.ROTATE_LIMITED)
angle = MathHelper.clamp(angle, -90, 90);
if (handler.getMode() == RotationMode.ROTATE_45 && angle == prevAngle) // don't snap while still rotating
angle = 45F * Math.round(Math.round(angle) / 45F);
if (angle != prevAngle) {
updateBeams();
}
}
}

View file

@ -1,160 +1,29 @@
package com.simibubi.create.content.optics.mirror; package com.simibubi.create.content.optics.mirror;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.google.common.collect.Iterators;
import com.simibubi.create.content.optics.Beam; import com.simibubi.create.content.optics.Beam;
import com.simibubi.create.content.optics.ILightHandler; import com.simibubi.create.content.optics.RotatedLightHandlingBehaviour;
import com.simibubi.create.foundation.collision.Matrix3d; import com.simibubi.create.foundation.collision.Matrix3d;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType; import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.BeaconHelper;
import com.simibubi.create.foundation.utility.ServerSpeedProvider;
import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.BeaconTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.Direction; import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.vector.Vector3d; import net.minecraft.util.math.vector.Vector3d;
public class MirrorBehaviour extends TileEntityBehaviour implements ILightHandler { import javax.annotation.Nonnull;
public static final BehaviourType<MirrorBehaviour> TYPE = new BehaviourType<>();
private final MirrorTileEntity mirror;
protected float angle;
protected float clientAngleDiff;
Map<Beam, Beam> beams;
private float prevAngle;
@Nullable
private BeaconTileEntity beacon;
private Beam beaconBeam = null;
private boolean isUpdating = false;
import java.util.stream.Stream;
public class MirrorBehaviour extends RotatedLightHandlingBehaviour<MirrorTileEntity> {
public static final BehaviourType<MirrorBehaviour> TYPE = new BehaviourType<>();
public MirrorBehaviour(MirrorTileEntity te) { public MirrorBehaviour(MirrorTileEntity te) {
super(te); super(te);
mirror = te;
beacon = null;
beams = new HashMap<>();
}
@Override
public void write(CompoundNBT nbt, boolean clientPacket) {
nbt.putFloat("Angle", angle);
super.write(nbt, clientPacket);
}
@Override
public void read(CompoundNBT nbt, boolean clientPacket) {
angle = nbt.getFloat("Angle");
super.read(nbt, clientPacket);
}
public float getInterpolatedAngle(float partialTicks) {
if (tileEntity.isVirtual())
return MathHelper.lerp(partialTicks + .5f, prevAngle, angle);
if (mirror.movementMode.get() == RotationMode.ROTATE_LIMITED && Math.abs(angle) == 90)
return angle;
return MathHelper.lerp(partialTicks, angle, angle + getAngularSpeed());
}
public float getAngularSpeed() {
float speed = mirror.getSpeed() * 3 / 10f;
if (mirror.getSpeed() == 0)
speed = 0;
if (getHandlerWorld() != null && getHandlerWorld().isRemote) {
speed *= ServerSpeedProvider.get();
speed += clientAngleDiff / 3f;
}
return speed;
}
@Override
public void tick() {
super.tick();
prevAngle = angle;
if (getHandlerWorld() != null && getHandlerWorld().isRemote)
clientAngleDiff /= 2;
float angularSpeed = getAngularSpeed();
float newAngle = angle + angularSpeed;
angle = newAngle % 360;
if (mirror.movementMode.get() == RotationMode.ROTATE_LIMITED)
angle = MathHelper.clamp(angle, -90, 90);
if (mirror.movementMode.get() == RotationMode.ROTATE_45 && angle == prevAngle) // don't snap while still rotating
angle = 45F * Math.round(Math.round(angle) / 45F);
if (angle != prevAngle) {
updateBeams();
}
if (beacon != null && beacon.isRemoved())
updateBeaconState();
}
private void updateBeaconState() {
BeaconTileEntity beaconBefore = beacon;
beacon = BeaconHelper.getBeaconTE(getBlockPos(), getHandlerWorld())
.orElse(null);
if (beaconBefore != null) {
beaconBeam.clear();
beaconBeam = null;
updateBeams();
}
if (beacon != null) {
beaconBeam = constructOutBeam(null, VecHelper.UP, beacon.getPos());
if (beaconBeam != null && !beaconBeam.isEmpty()) {
beaconBeam.addListener(this);
beaconBeam.onCreated();
}
}
}
@Override
public void updateBeams() {
if (isUpdating)
return;
isUpdating = true;
Map<Beam, Beam> newBeams = new HashMap<>();
for (Map.Entry<Beam, Beam> entry : new HashSet<>(beams.entrySet())) {
entry.getValue()
.onRemoved();
if (entry.getKey()
.isRemoved())
continue;
Beam reflected = reflectBeam(entry.getKey());
if (reflected != null && !reflected.isEmpty()) {
newBeams.put(entry.getKey(), reflected);
reflected.onCreated();
entry.getKey()
.addListener(this);
}
}
beams = newBeams;
isUpdating = false;
} }
private Vector3d getReflectionAngle(Vector3d inputAngle) { private Vector3d getReflectionAngle(Vector3d inputAngle) {
inputAngle = inputAngle.normalize(); inputAngle = inputAngle.normalize();
Direction.Axis axis = mirror.getAxis(); Direction.Axis axis = handler.getAxis();
Vector3d normal; Vector3d normal;
if (axis.isHorizontal()) if (axis.isHorizontal())
normal = new Matrix3d().asIdentity() normal = new Matrix3d().asIdentity()
@ -168,22 +37,11 @@ public class MirrorBehaviour extends TileEntityBehaviour implements ILightHandle
return inputAngle.subtract(normal.scale(2 * inputAngle.dotProduct(normal))); return inputAngle.subtract(normal.scale(2 * inputAngle.dotProduct(normal)));
} }
@Override
public void lazyTick() {
super.lazyTick();
updateBeaconState();
updateBeams();
}
@Override
public TileEntity getTile() {
return tileEntity;
}
@Nonnull @Nonnull
@Override @Override
public Direction getBeamRotationAround() { public Direction getBeamRotationAround() {
return Direction.getFacingFromAxisDirection(mirror.getAxis(), Direction.AxisDirection.POSITIVE); return Direction.getFacingFromAxisDirection(handler.getAxis(), Direction.AxisDirection.POSITIVE);
} }
@Override @Override
@ -192,37 +50,11 @@ public class MirrorBehaviour extends TileEntityBehaviour implements ILightHandle
} }
@Override @Override
public Iterator<Beam> getRenderBeams() { protected Stream<Beam> safeConstructSubBeamsFor(Beam beam) {
Iterator<Beam> beaconIter = beaconBeam == null ? Collections.emptyIterator() : Collections.singleton(beaconBeam)
.iterator();
return Iterators.concat(beams.values()
.iterator(), beaconIter);
}
@Override
public Stream<Beam> constructSubBeams(Beam beam) {
if (beams.keySet()
.stream()
.filter(((Predicate<Beam>) Beam::isRemoved).negate())
.map(Beam::getDirection)
.filter(Objects::nonNull)
.anyMatch(b -> b.equals(beam.getDirection())))
return Stream.empty();
Beam reflected = reflectBeam(beam);
if (reflected != null && !reflected.isEmpty()) {
beams.put(beam, reflected);
beam.addListener(this);
return Stream.of(reflected);
}
return Stream.empty();
}
@Nullable
private Beam reflectBeam(Beam beam) {
Vector3d inDir = beam.getDirection(); Vector3d inDir = beam.getDirection();
if (inDir == null) if (inDir == null)
return null; return Stream.empty();
return constructOutBeam(beam, getReflectionAngle(inDir).normalize()); return Stream.of(constructOutBeam(beam, getReflectionAngle(inDir).normalize()));
} }
} }

View file

@ -17,7 +17,7 @@ import net.minecraft.util.math.AxisAlignedBB;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
public class MirrorTileEntity extends KineticTileEntity implements ILightHandlerProvider { public class MirrorTileEntity extends KineticTileEntity implements ILightHandlerProvider, RotationMode.RotationModeProvider {
protected ScrollOptionBehaviour<RotationMode> movementMode; protected ScrollOptionBehaviour<RotationMode> movementMode;
protected MirrorBehaviour mirror; protected MirrorBehaviour mirror;
@ -63,4 +63,9 @@ public class MirrorTileEntity extends KineticTileEntity implements ILightHandler
public ILightHandler getHandler() { public ILightHandler getHandler() {
return mirror; return mirror;
} }
@Override
public RotationMode getMode() {
return movementMode.get();
}
} }

View file

@ -30,4 +30,8 @@ public enum RotationMode implements INamedIconOptions {
return translationKey; return translationKey;
} }
@FunctionalInterface
public interface RotationModeProvider {
RotationMode getMode();
}
} }