lots of little changes to get circles rendering and sounding right again.
This commit is contained in:
parent
335f360f55
commit
5c8f63beb2
8 changed files with 151 additions and 38 deletions
|
@ -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
|
||||
|
|
|
@ -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)}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
Loading…
Reference in a new issue