lots of little changes to get circles rendering and sounding right again.

This commit is contained in:
Talia-12 2023-04-03 22:17:32 +10:00
parent 335f360f55
commit 5c8f63beb2
8 changed files with 151 additions and 38 deletions

View file

@ -33,7 +33,7 @@ public abstract class BlockAbstractImpetus extends BlockCircleComponent implemen
@Override
public ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
return new ControlFlow.Continue(imageIn, List.of(this.exitPositionFromDirection(pos, bs.getValue(FACING))));
return new ControlFlow.Stop();
}
@Override

View file

@ -19,6 +19,26 @@ public abstract class BlockCircleComponent extends Block implements ICircleCompo
super(p_49795_);
}
@Override
public BlockState startEnergized(BlockPos pos, BlockState bs, Level world) {
var newState = bs.setValue(ENERGIZED, true);
world.setBlockAndUpdate(pos, bs);
return newState;
}
@Override
public boolean isEnergized(BlockPos pos, BlockState bs, Level world) {
return bs.getValue(ENERGIZED);
}
@Override
public BlockState endEnergized(BlockPos pos, BlockState bs, Level world) {
var newState = bs.setValue(ENERGIZED, false);
world.setBlockAndUpdate(pos, newState);
return newState;
}
/**
* Which direction points "up" or "out" for this block?
* This is used for {@link ICircleComponent#canEnterFromDirection(Direction, BlockPos, BlockState, ServerLevel)}

View file

@ -96,6 +96,8 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
public void tickExecution() {
if (this.level == null)
return;
this.setChanged();
var state = this.getExecutionState();
if (state == null) {
@ -127,11 +129,11 @@ public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implemen
throw new IllegalStateException("didn't you read the doc comment, don't call this if the level is null");
}
if (this.lazyExecutionState != null) {
if (this.executionState != null)
return this.executionState;
if (this.lazyExecutionState != null)
this.executionState = CircleExecutionState.load(this.lazyExecutionState, (ServerLevel) this.level);
} else {
this.executionState = null;
}
return this.executionState;
}

View file

@ -1,10 +1,14 @@
package at.petrak.hexcasting.api.casting.circles;
import at.petrak.hexcasting.api.HexAPI;
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.api.utils.HexUtils;
import at.petrak.hexcasting.common.lib.HexItems;
import com.mojang.datafixers.util.Pair;
import net.minecraft.ChatFormatting;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
@ -14,6 +18,7 @@ import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.phys.AABB;
@ -31,7 +36,8 @@ public class CircleExecutionState {
TAG_CURRENT_POS = "current_pos",
TAG_ENTERED_FROM = "entered_from",
TAG_IMAGE = "image",
TAG_CASTER = "caster";
TAG_CASTER = "caster",
TAG_COLORIZER = "colorizer";
// Does contain the starting impetus
public final Set<BlockPos> knownPositions;
@ -39,21 +45,22 @@ public class CircleExecutionState {
public BlockPos currentPos;
public Direction enteredFrom;
public CastingImage currentImage;
public @Nullable UUID caster;
public FrozenColorizer colorizer;
public final AABB bounds;
public @Nullable UUID caster;
protected CircleExecutionState(Set<BlockPos> knownPositions, List<BlockPos> reachedPositions,
BlockPos currentPos, Direction enteredFrom,
CastingImage currentImage, @Nullable UUID caster) {
CastingImage currentImage, @Nullable UUID caster, FrozenColorizer colorizer) {
this.knownPositions = knownPositions;
this.reachedPositions = reachedPositions;
this.currentPos = currentPos;
this.enteredFrom = enteredFrom;
this.currentImage = currentImage;
this.caster = caster;
this.colorizer = colorizer;
this.bounds = BlockEntityAbstractImpetus.getBounds(new ArrayList<>(this.knownPositions));
}
@ -100,12 +107,18 @@ public class CircleExecutionState {
return null;
}
var knownPositions = new HashSet<>(seenGoodPositions);
var reachedPositions = new ArrayList<BlockPos>();
reachedPositions.add(impetus.getBlockPos());
var start = seenGoodPositions.get(0);
if (caster == null)
return new CircleExecutionState(new HashSet<>(seenGoodPositions), List.of(impetus.getBlockPos()), start, impetus.getStartDirection(), new CastingImage(), null);
else
return new CircleExecutionState(new HashSet<>(seenGoodPositions), List.of(impetus.getBlockPos()), start, impetus.getStartDirection(), new CastingImage(), caster.getUUID());
if (caster == null) {
var colorizer = new FrozenColorizer(new ItemStack(HexItems.DYE_COLORIZERS.get(DyeColor.PURPLE)), Util.NIL_UUID);
return new CircleExecutionState(knownPositions, reachedPositions, start, impetus.getStartDirection(), new CastingImage(), null, colorizer);
} else {
var colorizer = HexAPI.instance().getColorizer(caster);
return new CircleExecutionState(knownPositions, reachedPositions, start, impetus.getStartDirection(), new CastingImage(), caster.getUUID(), colorizer);
}
}
public CompoundTag save() {
@ -130,6 +143,8 @@ public class CircleExecutionState {
if (this.caster != null)
out.putUUID(TAG_CASTER, this.caster);
out.put(TAG_COLORIZER, this.colorizer.serializeToNBT());
return out;
}
@ -153,7 +168,9 @@ public class CircleExecutionState {
if (nbt.hasUUID(TAG_CASTER))
caster = nbt.getUUID(TAG_CASTER);
return new CircleExecutionState(knownPositions, reachedPositions, currentPos, enteredFrom, image, caster);
FrozenColorizer colorizer = FrozenColorizer.fromNBT(nbt.getCompound(TAG_COLORIZER));
return new CircleExecutionState(knownPositions, reachedPositions, currentPos, enteredFrom, image, caster, colorizer);
}
/**
@ -173,17 +190,18 @@ public class CircleExecutionState {
var env = new CircleCastEnv(world, impetus.getBlockPos(), impetus.getStartDirection(), caster, this.bounds);
var executorBlock = world.getBlockState(this.currentPos);
if (!(executorBlock instanceof ICircleComponent executor)) {
var executorBlockState = world.getBlockState(this.currentPos);
if (!(executorBlockState.getBlock() instanceof ICircleComponent executor)) {
// TODO: notification of the error?
ICircleComponent.sfx(this.currentPos, executorBlockState, world, Objects.requireNonNull(env.getImpetus()), false);
return false;
}
executorBlock = executor.startEnergized(this.currentPos, executorBlock, world);
executorBlockState = executor.startEnergized(this.currentPos, executorBlockState, world);
boolean halt = false;
var ctrl = executor.acceptControlFlow(this.currentImage, env, this.enteredFrom, this.currentPos,
executorBlock, world);
executorBlockState, world);
if (ctrl instanceof ICircleComponent.ControlFlow.Stop) {
// acceptControlFlow should have already posted the error
halt = true;
@ -192,7 +210,7 @@ public class CircleExecutionState {
for (var exit : cont.exits) {
var there = world.getBlockState(exit.getFirst());
if (there instanceof ICircleComponent cc
if (there.getBlock() instanceof ICircleComponent cc
&& cc.canEnterFromDirection(exit.getSecond(), exit.getFirst(), there, world)) {
if (found != null) {
// oh no!
@ -200,6 +218,7 @@ public class CircleExecutionState {
Component.translatable("hexcasting.circles.many_exits",
Component.literal(this.currentPos.toShortString()).withStyle(ChatFormatting.RED)),
new ItemStack(Items.COMPASS));
ICircleComponent.sfx(this.currentPos, executorBlockState, world, Objects.requireNonNull(env.getImpetus()), false);
halt = true;
break;
} else {
@ -214,20 +233,16 @@ public class CircleExecutionState {
Component.translatable("hexcasting.circles.no_exits",
Component.literal(this.currentPos.toShortString()).withStyle(ChatFormatting.RED)),
new ItemStack(Items.OAK_SIGN));
ICircleComponent.sfx(this.currentPos, executorBlockState, world, Objects.requireNonNull(env.getImpetus()), false);
halt = true;
} else {
// A single valid exit position has been found.
knownPositions.add(found.getFirst());
reachedPositions.add(found.getFirst());
if (found.getFirst() != impetus.getBlockPos()) {
currentPos = found.getFirst();
enteredFrom = found.getSecond();
currentImage = cont.update;
} else {
// The wave has reached the impetus, end the execution.
// TODO: this should maybe just be in the execution code for
halt = true;
}
ICircleComponent.sfx(this.currentPos, executorBlockState, world, Objects.requireNonNull(env.getImpetus()), true);
currentPos = found.getFirst();
enteredFrom = found.getSecond();
currentImage = cont.update;
}
}
@ -249,7 +264,7 @@ public class CircleExecutionState {
for (var pos : this.reachedPositions) {
var there = world.getBlockState(pos);
if (there instanceof ICircleComponent cc) {
if (there.getBlock() instanceof ICircleComponent cc) {
cc.endEnergized(pos, there, world);
}
}

View file

@ -1,17 +1,29 @@
package at.petrak.hexcasting.api.casting.circles;
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
import at.petrak.hexcasting.api.casting.ParticleSpray;
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
import at.petrak.hexcasting.api.misc.FrozenColorizer;
import at.petrak.hexcasting.common.lib.HexItems;
import at.petrak.hexcasting.common.lib.HexSounds;
import com.mojang.datafixers.util.Pair;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Contract;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
/**
* Implement this on a block to make circles interact with it.
@ -56,7 +68,7 @@ public interface ICircleComponent {
*/
@Contract(pure = true)
default Pair<BlockPos, Direction> exitPositionFromDirection(BlockPos pos, Direction dir) {
return Pair.of(new BlockPos(dir.getStepX(), dir.getStepY(), dir.getStepZ()), dir);
return Pair.of(pos.offset(dir.getStepX(), dir.getStepY(), dir.getStepZ()), dir);
}
/**
@ -65,6 +77,11 @@ public interface ICircleComponent {
* // TODO: determine if this should just be in {@link ICircleComponent#acceptControlFlow(CastingImage, CircleCastEnv, Direction, BlockPos, BlockState, ServerLevel)}.
*/
BlockState startEnergized(BlockPos pos, BlockState bs, Level world);
/**
* Returns whether the {@link ICircleComponent} at the given position is energized.
*/
boolean isEnergized(BlockPos pos, BlockState bs, Level world);
/**
* End the {@link ICircleComponent} at the given position glowing. Returns the new state of
@ -72,6 +89,53 @@ public interface ICircleComponent {
*/
BlockState endEnergized(BlockPos pos, BlockState bs, Level world);
static void sfx(BlockPos pos, BlockState bs, Level world, BlockEntityAbstractImpetus impetus, boolean success) {
Vec3 vpos;
Vec3 vecOutDir;
FrozenColorizer colorizer;
UUID activator = Util.NIL_UUID;
if (impetus.getExecutionState() != null && impetus.getExecutionState().caster != null)
activator = impetus.getExecutionState().caster;
if (impetus.getExecutionState() == null)
colorizer = new FrozenColorizer(new ItemStack(HexItems.DYE_COLORIZERS.get(DyeColor.RED)), activator);
else
colorizer = impetus.getExecutionState().colorizer;
if (bs.getBlock() instanceof BlockCircleComponent bcc) {
var outDir = bcc.normalDir(pos, bs, world);
var height = bcc.particleHeight(pos, bs, world);
vecOutDir = new Vec3(outDir.step());
vpos = Vec3.atCenterOf(pos).add(vecOutDir.scale(height));
} else {
// we probably are doing this because it's an error and we removed a block
vpos = Vec3.atCenterOf(pos);
vecOutDir = new Vec3(0, 0, 0);
}
if (world instanceof ServerLevel serverLevel) {
var spray = new ParticleSpray(vpos, vecOutDir.scale(success ? 1.0 : 1.5), success ? 0.1 : 0.5,
Mth.PI / (success ? 4 : 2), success ? 30 : 100);
spray.sprayParticles(serverLevel,
success ? colorizer : new FrozenColorizer(new ItemStack(HexItems.DYE_COLORIZERS.get(DyeColor.RED)),
activator));
}
var pitch = 1f;
var sound = HexSounds.SPELL_CIRCLE_FAIL;
if (success) {
sound = HexSounds.SPELL_CIRCLE_FIND_BLOCK;
var state = impetus.getExecutionState();
// This is a good use of my time
var note = state.reachedPositions.size() - 1;
var semitone = impetus.semitoneFromScale(note);
pitch = (float) Math.pow(2.0, (semitone - 8) / 12d);
}
world.playSound(null, vpos.x, vpos.y, vpos.z, sound, SoundSource.BLOCKS, 1f, pitch);
}
abstract sealed class ControlFlow {
public static final class Continue extends ControlFlow {
public final CastingImage update;

View file

@ -48,7 +48,7 @@ public class CircleCastEnv extends CastingEnvironment {
return this.caster;
}
public @Nullable BlockEntityAbstractImpetus getCircle() {
public @Nullable BlockEntityAbstractImpetus getImpetus() {
var entity = this.world.getBlockEntity(impetusLoc);
if (entity instanceof BlockEntityAbstractImpetus)
@ -86,7 +86,7 @@ public class CircleCastEnv extends CastingEnvironment {
@Override
public long extractMedia(long cost) {
var entity = this.getCircle();
var entity = this.getImpetus();
if (entity == null)
return cost;
@ -144,13 +144,24 @@ public class CircleCastEnv extends CastingEnvironment {
@Override
public FrozenColorizer getColorizer() {
if (this.caster != null)
return HexAPI.instance().getColorizer(this.caster);
var out = this.getColorizerFromImpetus();
if (out != null)
return out;
// TODO: colouriser from an adjacent inventory also?
return new FrozenColorizer(new ItemStack(HexItems.DYE_COLORIZERS.get(DyeColor.PURPLE)), Util.NIL_UUID);
}
private @Nullable FrozenColorizer getColorizerFromImpetus() {
var impetus = this.getImpetus();
if (impetus == null)
return null;
var state = impetus.getExecutionState();
if (state == null)
return null;
return state.colorizer;
}
@Override
public void produceParticles(ParticleSpray particles, FrozenColorizer colorizer) {
particles.sprayParticles(this.world, colorizer);

View file

@ -37,6 +37,7 @@ import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
// When on the floor or ceiling FACING is the direction the *bottom* of the pattern points
// (or which way is "down").
@ -79,10 +80,10 @@ public class BlockSlate extends BlockCircleComponent implements EntityBlock, Sim
// TODO: Do something here actually with imageIn
var exitDirsSet = this.possibleExitDirections(pos, bs, world);
exitDirsSet.remove(enterDir);
exitDirsSet.remove(enterDir.getOpposite());
var exitDirs = exitDirsSet.stream().map((dir) -> this.exitPositionFromDirection(pos, dir));
return new ControlFlow.Continue(imageIn, exitDirs.toList());
}

View file

@ -14,7 +14,7 @@ class OpCircleBounds(val max: Boolean) : ConstMediaAction {
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> {
if (ctx !is CircleCastEnv)
throw MishapNoSpellCircle()
val circle = ctx.circle ?: throw MishapNoSpellCircle()
val circle = ctx.impetus ?: throw MishapNoSpellCircle()
val aabb = circle.executionState!!.bounds // the circle should have an execution state since it's executing this.