block out the spell circle executor, help me winfy-won kenobi
This commit is contained in:
parent
9a231a634b
commit
bdbb15ab51
25 changed files with 738 additions and 612 deletions
|
@ -1,5 +1,6 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
|
|
|
@ -1,578 +0,0 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.SpellCircleContext;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.PatternIota;
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
import at.petrak.hexcasting.api.misc.MediaConstants;
|
||||
import at.petrak.hexcasting.api.mod.HexConfig;
|
||||
import at.petrak.hexcasting.api.utils.MediaHelper;
|
||||
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
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.sounds.SoundSource;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.WorldlyContainer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implements WorldlyContainer {
|
||||
public static final String
|
||||
TAG_ACTIVATOR = "activator",
|
||||
TAG_COLORIZER = "colorizer",
|
||||
TAG_NEXT_BLOCK = "next_block",
|
||||
TAG_TRACKED_BLOCKS = "tracked_blocks",
|
||||
TAG_FOUND_ALL = "found_all",
|
||||
TAG_MEDIA = "media",
|
||||
TAG_LAST_MISHAP = "last_mishap";
|
||||
|
||||
private static final DecimalFormat DUST_AMOUNT = new DecimalFormat("###,###.##");
|
||||
|
||||
@Nullable
|
||||
private UUID activator = null;
|
||||
@Nullable
|
||||
private FrozenColorizer colorizer = null;
|
||||
@Nullable
|
||||
private BlockPos nextBlock = null;
|
||||
@Nullable
|
||||
private List<BlockPos> trackedBlocks = null;
|
||||
private transient Set<BlockPos> knownBlocks = null;
|
||||
private boolean foundAll = false;
|
||||
@Nullable
|
||||
private Component lastMishap = null;
|
||||
|
||||
private static final int MAX_CAPACITY = 2_000_000_000;
|
||||
|
||||
private int media = 0;
|
||||
|
||||
public BlockEntityAbstractImpetus(BlockEntityType<?> pType, BlockPos pWorldPosition, BlockState pBlockState) {
|
||||
super(pType, pWorldPosition, pBlockState);
|
||||
}
|
||||
|
||||
abstract public boolean activatorAlwaysInRange();
|
||||
|
||||
public int getMedia() {
|
||||
return this.media;
|
||||
}
|
||||
|
||||
public void setMedia(int media) {
|
||||
this.media = media;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Component getLastMishap() {
|
||||
return lastMishap;
|
||||
}
|
||||
|
||||
public void setLastMishap(@Nullable Component lastMishap) {
|
||||
this.lastMishap = lastMishap;
|
||||
}
|
||||
|
||||
public void activateSpellCircle(ServerPlayer activator) {
|
||||
if (this.nextBlock != null) {
|
||||
return;
|
||||
}
|
||||
this.level.scheduleTick(this.getBlockPos(), this.getBlockState().getBlock(), this.getTickSpeed());
|
||||
|
||||
this.activator = activator.getUUID();
|
||||
this.nextBlock = this.getBlockPos();
|
||||
this.trackedBlocks = new ArrayList<>();
|
||||
this.knownBlocks = new HashSet<>();
|
||||
this.colorizer = IXplatAbstractions.INSTANCE.getColorizer(activator);
|
||||
|
||||
this.level.setBlockAndUpdate(this.getBlockPos(),
|
||||
this.getBlockState().setValue(BlockAbstractImpetus.ENERGIZED, true));
|
||||
this.stepCircle();
|
||||
}
|
||||
|
||||
public void applyScryingLensOverlay(List<Pair<ItemStack, Component>> lines,
|
||||
BlockState state, BlockPos pos,
|
||||
Player observer, Level world,
|
||||
Direction hitFace) {
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntityAbstractImpetus beai) {
|
||||
if (beai.getMedia() < 0) {
|
||||
lines.add(new Pair<>(new ItemStack(HexItems.AMETHYST_DUST), ItemCreativeUnlocker.infiniteMedia(world)));
|
||||
} else {
|
||||
var dustCount = (float) beai.getMedia() / (float) MediaConstants.DUST_UNIT;
|
||||
var dustCmp = Component.translatable("hexcasting.tooltip.media",
|
||||
DUST_AMOUNT.format(dustCount));
|
||||
lines.add(new Pair<>(new ItemStack(HexItems.AMETHYST_DUST), dustCmp));
|
||||
}
|
||||
|
||||
var mishap = this.getLastMishap();
|
||||
if (mishap != null) {
|
||||
lines.add(new Pair<>(new ItemStack(Items.MUSIC_DISC_11), mishap));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveModData(CompoundTag tag) {
|
||||
if (this.activator != null && this.colorizer != null && this.nextBlock != null && this.trackedBlocks != null) {
|
||||
tag.putUUID(TAG_ACTIVATOR, this.activator);
|
||||
tag.put(TAG_NEXT_BLOCK, NbtUtils.writeBlockPos(this.nextBlock));
|
||||
tag.put(TAG_COLORIZER, this.colorizer.serializeToNBT());
|
||||
tag.putBoolean(TAG_FOUND_ALL, this.foundAll);
|
||||
|
||||
var trackeds = new ListTag();
|
||||
for (var tracked : this.trackedBlocks) {
|
||||
trackeds.add(NbtUtils.writeBlockPos(tracked));
|
||||
}
|
||||
tag.put(TAG_TRACKED_BLOCKS, trackeds);
|
||||
}
|
||||
|
||||
tag.putInt(TAG_MEDIA, this.media);
|
||||
if (this.lastMishap != null) {
|
||||
tag.putString(TAG_LAST_MISHAP, Component.Serializer.toJson(this.lastMishap));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadModData(CompoundTag tag) {
|
||||
if (tag.contains(TAG_ACTIVATOR, Tag.TAG_INT_ARRAY) &&
|
||||
tag.contains(TAG_COLORIZER, Tag.TAG_COMPOUND) &&
|
||||
tag.contains(TAG_NEXT_BLOCK, Tag.TAG_COMPOUND) &&
|
||||
tag.contains(TAG_TRACKED_BLOCKS, Tag.TAG_LIST)) {
|
||||
this.activator = tag.getUUID(TAG_ACTIVATOR);
|
||||
this.colorizer = FrozenColorizer.fromNBT(tag.getCompound(TAG_COLORIZER));
|
||||
this.nextBlock = NbtUtils.readBlockPos(tag.getCompound(TAG_NEXT_BLOCK));
|
||||
this.foundAll = tag.getBoolean(TAG_FOUND_ALL);
|
||||
var trackeds = tag.getList(TAG_TRACKED_BLOCKS, Tag.TAG_COMPOUND);
|
||||
this.trackedBlocks = new ArrayList<>(trackeds.size());
|
||||
this.knownBlocks = new HashSet<>();
|
||||
for (var tracked : trackeds) {
|
||||
var pos = NbtUtils.readBlockPos((CompoundTag) tracked);
|
||||
this.trackedBlocks.add(pos);
|
||||
this.knownBlocks.add(pos);
|
||||
}
|
||||
} else {
|
||||
this.activator = null;
|
||||
this.colorizer = null;
|
||||
this.nextBlock = null;
|
||||
this.foundAll = false;
|
||||
this.trackedBlocks = new ArrayList<>();
|
||||
this.knownBlocks = new HashSet<>();
|
||||
}
|
||||
|
||||
this.media = tag.getInt(TAG_MEDIA);
|
||||
if (tag.contains(TAG_LAST_MISHAP, Tag.TAG_STRING)) {
|
||||
this.lastMishap = Component.Serializer.fromJson(tag.getString(TAG_LAST_MISHAP));
|
||||
} else {
|
||||
this.lastMishap = null;
|
||||
}
|
||||
}
|
||||
|
||||
void stepCircle() {
|
||||
this.setChanged();
|
||||
|
||||
// haha which silly idiot would have done something like this
|
||||
if (this.activator == null || this.colorizer == null || this.nextBlock == null || this.trackedBlocks == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var possibleErrorPos = this.checkEverythingOk();
|
||||
if (possibleErrorPos != null) {
|
||||
this.sfx(possibleErrorPos, false);
|
||||
this.stopCasting();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.foundAll) {
|
||||
this.clearEnergized();
|
||||
this.castSpell();
|
||||
this.stopCasting();
|
||||
return;
|
||||
}
|
||||
|
||||
// This should only fail if we remove blocks halfway through casting
|
||||
var bsHere = this.level.getBlockState(this.nextBlock);
|
||||
if (!this.trackedBlocks.isEmpty() && bsHere.getBlock() instanceof BlockAbstractImpetus) {
|
||||
// no two impetuses!
|
||||
this.sfx(this.nextBlock, false);
|
||||
this.stopCasting();
|
||||
return;
|
||||
}
|
||||
var blockHere = bsHere.getBlock();
|
||||
if (!(blockHere instanceof BlockCircleComponent cc)) {
|
||||
this.sfx(this.nextBlock, false);
|
||||
this.stopCasting();
|
||||
return;
|
||||
}
|
||||
// Awesome we know this block is OK
|
||||
var thisNormal = cc.normalDir(this.nextBlock, bsHere, this.level);
|
||||
var possibleExits = cc.exitDirections(this.nextBlock, bsHere, this.level);
|
||||
BlockPos foundPos = null;
|
||||
for (var exit : possibleExits) {
|
||||
var neighborPos = this.nextBlock.relative(exit);
|
||||
var blockThere = this.level.getBlockState(neighborPos);
|
||||
// at this point, we haven't actually added nextBlock to trackedBlocks
|
||||
// so, in the smallest circle case (a 2x2), this will have a size of 3 (with this block being the 4th).
|
||||
var closedLoop = (this.trackedBlocks.size() >= 3 && this.trackedBlocks.get(0).equals(neighborPos));
|
||||
var mightBeOkThere = closedLoop
|
||||
|| this.trackedBlocks.isEmpty()
|
||||
|| !this.trackedBlocks.get(this.trackedBlocks.size() - 1).equals(neighborPos);
|
||||
if (mightBeOkThere
|
||||
&& blockThere.getBlock() instanceof BlockCircleComponent cc2
|
||||
&& cc2.canEnterFromDirection(exit.getOpposite(), thisNormal, neighborPos, blockThere, this.level)
|
||||
// another good use for the implies operator 😩
|
||||
&& (!blockThere.getValue(BlockCircleComponent.ENERGIZED) || this.knownBlocks.contains(neighborPos))) {
|
||||
if (foundPos == null) {
|
||||
foundPos = neighborPos;
|
||||
this.foundAll |= closedLoop;
|
||||
} else {
|
||||
// uh oh, fork in the road
|
||||
this.sfx(this.nextBlock, false);
|
||||
this.stopCasting();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (foundPos != null) {
|
||||
// pog
|
||||
this.trackedBlocks.add(this.nextBlock);
|
||||
this.knownBlocks.add(this.nextBlock);
|
||||
this.nextBlock = foundPos;
|
||||
} else {
|
||||
// end of the line
|
||||
this.sfx(this.nextBlock, false);
|
||||
this.stopCasting();
|
||||
return;
|
||||
}
|
||||
|
||||
var lastPos = this.trackedBlocks.get(this.trackedBlocks.size() - 1);
|
||||
var justTrackedBlock = this.level.getBlockState(lastPos);
|
||||
this.level.setBlockAndUpdate(lastPos, justTrackedBlock.setValue(BlockCircleComponent.ENERGIZED, true));
|
||||
this.sfx(lastPos, true);
|
||||
|
||||
this.level.scheduleTick(this.getBlockPos(), this.getBlockState().getBlock(), this.getTickSpeed());
|
||||
}
|
||||
|
||||
private void castSpell() {
|
||||
var player = this.getPlayer();
|
||||
|
||||
if (player instanceof ServerPlayer splayer) {
|
||||
var bounds = getBounds(this.trackedBlocks);
|
||||
|
||||
var ctx = new CastingEnvironment(splayer, InteractionHand.MAIN_HAND,
|
||||
new SpellCircleContext(this.getBlockPos(), bounds, this.activatorAlwaysInRange()));
|
||||
var harness = new CastingVM(ctx);
|
||||
|
||||
BlockPos erroredPos = null;
|
||||
for (var tracked : this.trackedBlocks) {
|
||||
var bs = this.level.getBlockState(tracked);
|
||||
if (bs.getBlock() instanceof BlockCircleComponent cc) {
|
||||
var newPattern = cc.getPattern(tracked, bs, this.level);
|
||||
if (newPattern != null) {
|
||||
var info = harness.queueAndExecuteIota(new PatternIota(newPattern), splayer.getLevel());
|
||||
if (!info.getResolutionType().getSuccess()) {
|
||||
erroredPos = tracked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (erroredPos != null) {
|
||||
this.sfx(erroredPos, false);
|
||||
} else {
|
||||
this.setLastMishap(null);
|
||||
}
|
||||
|
||||
this.setChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private static AABB getBounds(List<BlockPos> poses) {
|
||||
int minX = Integer.MAX_VALUE;
|
||||
int minY = Integer.MAX_VALUE;
|
||||
int minZ = Integer.MAX_VALUE;
|
||||
int maxX = Integer.MIN_VALUE;
|
||||
int maxY = Integer.MIN_VALUE;
|
||||
int maxZ = Integer.MIN_VALUE;
|
||||
|
||||
for (var pos : poses) {
|
||||
if (pos.getX() < minX) {
|
||||
minX = pos.getX();
|
||||
}
|
||||
if (pos.getY() < minY) {
|
||||
minY = pos.getY();
|
||||
}
|
||||
if (pos.getZ() < minZ) {
|
||||
minZ = pos.getZ();
|
||||
}
|
||||
if (pos.getX() > maxX) {
|
||||
maxX = pos.getX();
|
||||
}
|
||||
if (pos.getY() > maxY) {
|
||||
maxY = pos.getY();
|
||||
}
|
||||
if (pos.getZ() > maxZ) {
|
||||
maxZ = pos.getZ();
|
||||
}
|
||||
}
|
||||
|
||||
return new AABB(minX, minY, minZ, maxX + 1, maxY + 1, maxZ + 1);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private BlockPos checkEverythingOk() {
|
||||
// if they logged out or changed dimensions or something
|
||||
if (this.getPlayer() == null) {
|
||||
return this.getBlockPos();
|
||||
}
|
||||
|
||||
for (var pos : this.trackedBlocks) {
|
||||
if (!(this.level.getBlockState(pos).getBlock() instanceof BlockCircleComponent)) {
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.trackedBlocks.size() > HexConfig.server().maxSpellCircleLength()) {
|
||||
return this.trackedBlocks.get(this.trackedBlocks.size() - 1);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void sfx(BlockPos pos, boolean success) {
|
||||
Vec3 vpos;
|
||||
Vec3 vecOutDir;
|
||||
|
||||
var bs = this.level.getBlockState(pos);
|
||||
if (bs.getBlock() instanceof BlockCircleComponent bcc) {
|
||||
var outDir = bcc.normalDir(pos, bs, this.level);
|
||||
var height = bcc.particleHeight(pos, bs, this.level);
|
||||
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 (this.level 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 ? this.colorizer : new FrozenColorizer(new ItemStack(HexItems.DYE_COLORIZERS.get(DyeColor.RED)),
|
||||
this.activator));
|
||||
}
|
||||
|
||||
var pitch = 1f;
|
||||
var sound = HexSounds.SPELL_CIRCLE_FAIL;
|
||||
if (success) {
|
||||
sound = HexSounds.SPELL_CIRCLE_FIND_BLOCK;
|
||||
// This is a good use of my time
|
||||
var note = this.trackedBlocks.size() - 1;
|
||||
var semitone = this.semitoneFromScale(note);
|
||||
pitch = (float) Math.pow(2.0, (semitone - 8) / 12d);
|
||||
}
|
||||
level.playSound(null, vpos.x, vpos.y, vpos.z, sound, SoundSource.BLOCKS, 1f, pitch);
|
||||
}
|
||||
|
||||
protected void clearEnergized() {
|
||||
if (this.trackedBlocks != null) {
|
||||
for (var tracked : this.trackedBlocks) {
|
||||
var bs = this.level.getBlockState(tracked);
|
||||
if (bs.getBlock() instanceof BlockCircleComponent) {
|
||||
this.level.setBlockAndUpdate(tracked, bs.setValue(BlockCircleComponent.ENERGIZED, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void stopCasting() {
|
||||
clearEnergized();
|
||||
|
||||
this.activator = null;
|
||||
this.nextBlock = null;
|
||||
this.trackedBlocks = null;
|
||||
this.foundAll = false;
|
||||
|
||||
// without this check, breaking the block will just immediately replace it with
|
||||
// the new unenergized state
|
||||
if (this.level.getBlockState(this.getBlockPos()).getBlock() instanceof BlockAbstractImpetus) {
|
||||
this.level.setBlockAndUpdate(this.getBlockPos(),
|
||||
this.getBlockState().setValue(BlockCircleComponent.ENERGIZED, false));
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected Player getPlayer() {
|
||||
return this.level.getPlayerByUUID(this.activator);
|
||||
}
|
||||
|
||||
protected int getTickSpeed() {
|
||||
if (this.trackedBlocks == null) {
|
||||
return 10;
|
||||
} else {
|
||||
return Math.max(2, 10 - trackedBlocks.size() / 3);
|
||||
}
|
||||
}
|
||||
|
||||
protected int semitoneFromScale(int note) {
|
||||
var blockBelow = this.level.getBlockState(this.getBlockPos().below());
|
||||
var scale = MAJOR_SCALE;
|
||||
if (blockBelow.is(Blocks.CRYING_OBSIDIAN)) {
|
||||
scale = MINOR_SCALE;
|
||||
} else if (blockBelow.is(BlockTags.DOORS) || blockBelow.is(BlockTags.TRAPDOORS)) {
|
||||
scale = DORIAN_SCALE;
|
||||
} else if (blockBelow.is(Blocks.PISTON) || blockBelow.is(Blocks.STICKY_PISTON)) {
|
||||
scale = MIXOLYDIAN_SCALE;
|
||||
} else if (blockBelow.is(Blocks.BLUE_WOOL)
|
||||
|| blockBelow.is(Blocks.BLUE_CONCRETE) || blockBelow.is(Blocks.BLUE_CONCRETE_POWDER)
|
||||
|| blockBelow.is(Blocks.BLUE_TERRACOTTA) || blockBelow.is(Blocks.BLUE_GLAZED_TERRACOTTA)
|
||||
|| blockBelow.is(Blocks.BLUE_STAINED_GLASS) || blockBelow.is(Blocks.BLUE_STAINED_GLASS_PANE)) {
|
||||
scale = BLUES_SCALE;
|
||||
} else if (blockBelow.is(Blocks.BONE_BLOCK)) {
|
||||
scale = BAD_TIME;
|
||||
} else if (blockBelow.is(Blocks.COMPOSTER)) {
|
||||
scale = SUSSY_BAKA;
|
||||
}
|
||||
|
||||
note = Mth.clamp(note, 0, scale.length - 1);
|
||||
return scale[note];
|
||||
}
|
||||
|
||||
// this is a good use of my time
|
||||
private static final int[] MAJOR_SCALE = {0, 2, 4, 5, 7, 9, 11, 12};
|
||||
private static final int[] MINOR_SCALE = {0, 2, 3, 5, 7, 8, 11, 12};
|
||||
private static final int[] DORIAN_SCALE = {0, 2, 3, 5, 7, 9, 10, 12};
|
||||
private static final int[] MIXOLYDIAN_SCALE = {0, 2, 4, 5, 7, 9, 10, 12};
|
||||
private static final int[] BLUES_SCALE = {0, 3, 5, 6, 7, 10, 12};
|
||||
private static final int[] BAD_TIME = {0, 0, 12, 7, 6, 5, 3, 0, 3, 5};
|
||||
private static final int[] SUSSY_BAKA = {5, 8, 10, 11, 10, 8, 5, 3, 7, 5};
|
||||
|
||||
private static final int[] SLOTS = {0};
|
||||
|
||||
@Override
|
||||
public int[] getSlotsForFace(Direction var1) {
|
||||
return SLOTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItemThroughFace(int index, ItemStack stack, @Nullable Direction dir) {
|
||||
return this.canPlaceItem(index, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTakeItemThroughFace(int var1, ItemStack var2, Direction var3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return ItemStack.EMPTY.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int count) {
|
||||
return ItemStack.EMPTY.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return ItemStack.EMPTY.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack stack) {
|
||||
insertMedia(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int index, ItemStack stack) {
|
||||
if (remainingMediaCapacity() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stack.is(HexItems.CREATIVE_UNLOCKER)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var mediamount = extractMediaFromItem(stack, true);
|
||||
return mediamount > 0;
|
||||
}
|
||||
|
||||
public int extractMediaFromItem(ItemStack stack, boolean simulate) {
|
||||
if (this.media < 0) {
|
||||
return 0;
|
||||
}
|
||||
return MediaHelper.extractMedia(stack, remainingMediaCapacity(), true, simulate);
|
||||
}
|
||||
|
||||
public void insertMedia(ItemStack stack) {
|
||||
if (getMedia() >= 0 && !stack.isEmpty() && stack.getItem() == HexItems.CREATIVE_UNLOCKER) {
|
||||
setInfiniteMedia();
|
||||
stack.shrink(1);
|
||||
} else {
|
||||
var mediamount = extractMediaFromItem(stack, false);
|
||||
if (mediamount > 0) {
|
||||
this.media = Math.min(mediamount + media, MAX_CAPACITY);
|
||||
this.sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setInfiniteMedia() {
|
||||
this.media = -1;
|
||||
this.sync();
|
||||
}
|
||||
|
||||
public int remainingMediaCapacity() {
|
||||
if (this.media < 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(0, MAX_CAPACITY - this.media);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,352 @@
|
|||
package at.petrak.hexcasting.api.casting.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||||
import at.petrak.hexcasting.api.misc.MediaConstants;
|
||||
import at.petrak.hexcasting.api.utils.MediaHelper;
|
||||
import at.petrak.hexcasting.common.items.magic.ItemCreativeUnlocker;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.BlockTags;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.WorldlyContainer;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Default impl for an impetus, not tecnically necessary but I'm exposing it for ease of use
|
||||
* <p>
|
||||
* This does assume a great deal so you might have to re-implement a lot of this yourself if you
|
||||
* wanna do something wild and new
|
||||
*/
|
||||
public abstract class BlockEntityAbstractImpetus extends HexBlockEntity implements WorldlyContainer {
|
||||
private static final DecimalFormat DUST_AMOUNT = new DecimalFormat("###,###.##");
|
||||
private static final int MAX_CAPACITY = 2_000_000_000;
|
||||
|
||||
public static final String
|
||||
TAG_EXECUTION_STATE = "executor",
|
||||
TAG_MEDIA = "media",
|
||||
TAG_ERROR_MSG = "errorMsg",
|
||||
TAG_ERROR_DISPLAY = "errorDisplay";
|
||||
|
||||
// We might try to load the executor in loadModData when the level doesn't exist yet,
|
||||
// so save the tag and load it lazy
|
||||
@Nullable CompoundTag lazyExecutionState;
|
||||
@Nullable
|
||||
protected CircleExecutionState executionState;
|
||||
|
||||
protected int media = 0;
|
||||
|
||||
// these are null together
|
||||
@Nullable
|
||||
protected Component errorMsg = null;
|
||||
@Nullable
|
||||
protected ItemStack errorDisplay = null;
|
||||
|
||||
|
||||
public BlockEntityAbstractImpetus(BlockEntityType<?> pType, BlockPos pWorldPosition, BlockState pBlockState) {
|
||||
super(pType, pWorldPosition, pBlockState);
|
||||
}
|
||||
|
||||
public Direction getStartDirection() {
|
||||
return this.getBlockState().getValue(BlockStateProperties.FACING);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Component getErrorMsg() {
|
||||
return errorMsg;
|
||||
}
|
||||
|
||||
public void clearError() {
|
||||
this.errorMsg = null;
|
||||
this.errorDisplay = null;
|
||||
}
|
||||
|
||||
public void postError(Component error, ItemStack display) {
|
||||
this.errorMsg = error;
|
||||
this.errorDisplay = display;
|
||||
}
|
||||
|
||||
public void postMishap(Component mishapDisplay) {
|
||||
this.errorMsg = mishapDisplay;
|
||||
this.errorDisplay = new ItemStack(Items.MUSIC_DISC_11);
|
||||
}
|
||||
|
||||
//region execution
|
||||
|
||||
public void tickExecution() {
|
||||
var state = this.getExecutionState();
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO winfy 🥺 pls help
|
||||
}
|
||||
|
||||
/**
|
||||
* ONLY CALL THIS WHEN YOU KNOW THE WORLD EXISTS AND ON THE SERVER, lazy-loads it
|
||||
*/
|
||||
public @Nullable CircleExecutionState getExecutionState() {
|
||||
if (this.level == null) {
|
||||
throw new IllegalStateException("didn't you read the doc comment, don't call this if the level is null");
|
||||
}
|
||||
|
||||
if (this.lazyExecutionState != null) {
|
||||
this.executionState = CircleExecutionState.load(this.lazyExecutionState, (ServerLevel) this.level);
|
||||
} else {
|
||||
this.executionState = null;
|
||||
}
|
||||
|
||||
return this.executionState;
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
protected static AABB getBounds(List<BlockPos> poses) {
|
||||
int minX = Integer.MAX_VALUE;
|
||||
int minY = Integer.MAX_VALUE;
|
||||
int minZ = Integer.MAX_VALUE;
|
||||
int maxX = Integer.MIN_VALUE;
|
||||
int maxY = Integer.MIN_VALUE;
|
||||
int maxZ = Integer.MIN_VALUE;
|
||||
|
||||
for (var pos : poses) {
|
||||
if (pos.getX() < minX) {
|
||||
minX = pos.getX();
|
||||
}
|
||||
if (pos.getY() < minY) {
|
||||
minY = pos.getY();
|
||||
}
|
||||
if (pos.getZ() < minZ) {
|
||||
minZ = pos.getZ();
|
||||
}
|
||||
if (pos.getX() > maxX) {
|
||||
maxX = pos.getX();
|
||||
}
|
||||
if (pos.getY() > maxY) {
|
||||
maxY = pos.getY();
|
||||
}
|
||||
if (pos.getZ() > maxZ) {
|
||||
maxZ = pos.getZ();
|
||||
}
|
||||
}
|
||||
|
||||
return new AABB(minX, minY, minZ, maxX + 1, maxY + 1, maxZ + 1);
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
//region media handling
|
||||
|
||||
public int getMedia() {
|
||||
return this.media;
|
||||
}
|
||||
|
||||
public void setMedia(int media) {
|
||||
this.media = media;
|
||||
}
|
||||
|
||||
public int extractMediaFromInsertedItem(ItemStack stack, boolean simulate) {
|
||||
if (this.media < 0) {
|
||||
return 0;
|
||||
}
|
||||
return MediaHelper.extractMedia(stack, remainingMediaCapacity(), true, simulate);
|
||||
}
|
||||
|
||||
public void insertMedia(ItemStack stack) {
|
||||
if (getMedia() >= 0 && !stack.isEmpty() && stack.getItem() == HexItems.CREATIVE_UNLOCKER) {
|
||||
setInfiniteMedia();
|
||||
stack.shrink(1);
|
||||
} else {
|
||||
var mediamount = extractMediaFromInsertedItem(stack, false);
|
||||
if (mediamount > 0) {
|
||||
this.media = Math.min(mediamount + media, MAX_CAPACITY);
|
||||
this.sync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setInfiniteMedia() {
|
||||
this.media = -1;
|
||||
this.sync();
|
||||
}
|
||||
|
||||
public int remainingMediaCapacity() {
|
||||
if (this.media < 0) {
|
||||
return 0;
|
||||
}
|
||||
return Math.max(0, MAX_CAPACITY - this.media);
|
||||
}
|
||||
|
||||
//endregion
|
||||
|
||||
@Override
|
||||
protected void saveModData(CompoundTag tag) {
|
||||
if (this.executionState != null) {
|
||||
tag.put(TAG_EXECUTION_STATE, this.executionState.save());
|
||||
}
|
||||
|
||||
tag.putInt(TAG_MEDIA, this.media);
|
||||
if (this.errorMsg != null && this.errorDisplay != null) {
|
||||
tag.putString(TAG_ERROR_MSG, Component.Serializer.toJson(this.errorMsg));
|
||||
var itemTag = new CompoundTag();
|
||||
this.errorDisplay.save(itemTag);
|
||||
tag.put(TAG_ERROR_DISPLAY, itemTag);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadModData(CompoundTag tag) {
|
||||
this.executionState = null;
|
||||
if (tag.contains(TAG_EXECUTION_STATE, Tag.TAG_COMPOUND)) {
|
||||
this.lazyExecutionState = tag.getCompound(TAG_EXECUTION_STATE);
|
||||
} else {
|
||||
this.lazyExecutionState = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void applyScryingLensOverlay(List<Pair<ItemStack, Component>> lines,
|
||||
BlockState state, BlockPos pos, Player observer, Level world, Direction hitFace) {
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntityAbstractImpetus beai) {
|
||||
if (beai.getMedia() < 0) {
|
||||
lines.add(new Pair<>(new ItemStack(HexItems.AMETHYST_DUST), ItemCreativeUnlocker.infiniteMedia(world)));
|
||||
} else {
|
||||
var dustCount = (float) beai.getMedia() / (float) MediaConstants.DUST_UNIT;
|
||||
var dustCmp = Component.translatable("hexcasting.tooltip.media",
|
||||
DUST_AMOUNT.format(dustCount));
|
||||
lines.add(new Pair<>(new ItemStack(HexItems.AMETHYST_DUST), dustCmp));
|
||||
}
|
||||
|
||||
if (this.errorMsg != null && this.errorDisplay != null) {
|
||||
lines.add(new Pair<>(this.errorDisplay, this.errorMsg));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//region music
|
||||
|
||||
protected int semitoneFromScale(int note) {
|
||||
var blockBelow = this.level.getBlockState(this.getBlockPos().below());
|
||||
var scale = MAJOR_SCALE;
|
||||
if (blockBelow.is(Blocks.CRYING_OBSIDIAN)) {
|
||||
scale = MINOR_SCALE;
|
||||
} else if (blockBelow.is(BlockTags.DOORS) || blockBelow.is(BlockTags.TRAPDOORS)) {
|
||||
scale = DORIAN_SCALE;
|
||||
} else if (blockBelow.is(Blocks.PISTON) || blockBelow.is(Blocks.STICKY_PISTON)) {
|
||||
scale = MIXOLYDIAN_SCALE;
|
||||
} else if (blockBelow.is(Blocks.BLUE_WOOL)
|
||||
|| blockBelow.is(Blocks.BLUE_CONCRETE) || blockBelow.is(Blocks.BLUE_CONCRETE_POWDER)
|
||||
|| blockBelow.is(Blocks.BLUE_TERRACOTTA) || blockBelow.is(Blocks.BLUE_GLAZED_TERRACOTTA)
|
||||
|| blockBelow.is(Blocks.BLUE_STAINED_GLASS) || blockBelow.is(Blocks.BLUE_STAINED_GLASS_PANE)) {
|
||||
scale = BLUES_SCALE;
|
||||
} else if (blockBelow.is(Blocks.BONE_BLOCK)) {
|
||||
scale = BAD_TIME;
|
||||
} else if (blockBelow.is(Blocks.COMPOSTER)) {
|
||||
scale = SUSSY_BAKA;
|
||||
}
|
||||
|
||||
note = Mth.clamp(note, 0, scale.length - 1);
|
||||
return scale[note];
|
||||
}
|
||||
|
||||
// this is a good use of my time
|
||||
private static final int[] MAJOR_SCALE = {0, 2, 4, 5, 7, 9, 11, 12};
|
||||
private static final int[] MINOR_SCALE = {0, 2, 3, 5, 7, 8, 11, 12};
|
||||
private static final int[] DORIAN_SCALE = {0, 2, 3, 5, 7, 9, 10, 12};
|
||||
private static final int[] MIXOLYDIAN_SCALE = {0, 2, 4, 5, 7, 9, 10, 12};
|
||||
private static final int[] BLUES_SCALE = {0, 3, 5, 6, 7, 10, 12};
|
||||
private static final int[] BAD_TIME = {0, 0, 12, 7, 6, 5, 3, 0, 3, 5};
|
||||
private static final int[] SUSSY_BAKA = {5, 8, 10, 11, 10, 8, 5, 3, 7, 5};
|
||||
|
||||
//endregion
|
||||
|
||||
//region item handler contract stuff
|
||||
private static final int[] SLOTS = {0};
|
||||
|
||||
@Override
|
||||
public int[] getSlotsForFace(Direction var1) {
|
||||
return SLOTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItemThroughFace(int index, ItemStack stack, @Nullable Direction dir) {
|
||||
return this.canPlaceItem(index, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTakeItemThroughFace(int var1, ItemStack var2, Direction var3) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int index) {
|
||||
return ItemStack.EMPTY.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int index, int count) {
|
||||
return ItemStack.EMPTY.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int index) {
|
||||
return ItemStack.EMPTY.copy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int index, ItemStack stack) {
|
||||
insertMedia(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int index, ItemStack stack) {
|
||||
if (remainingMediaCapacity() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (stack.is(HexItems.CREATIVE_UNLOCKER)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var mediamount = extractMediaFromInsertedItem(stack, true);
|
||||
return mediamount > 0;
|
||||
}
|
||||
|
||||
//endregion
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
package at.petrak.hexcasting.api.casting.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import at.petrak.hexcasting.api.utils.HexUtils;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtUtils;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* See {@link BlockEntityAbstractImpetus}, this is what's stored in it
|
||||
*/
|
||||
public class CircleExecutionState {
|
||||
public static final String
|
||||
TAG_KNOWN_POSITIONS = "known_positions",
|
||||
TAG_CURRENT_POS = "current_pos",
|
||||
TAG_ENTERED_FROM = "entered_from",
|
||||
TAG_IMAGE = "image";
|
||||
|
||||
// Does contain the starting impetus
|
||||
public final Set<BlockPos> knownPositions;
|
||||
public BlockPos currentPos;
|
||||
public Direction enteredFrom;
|
||||
public CastingImage currentImage;
|
||||
|
||||
|
||||
protected CircleExecutionState(Set<BlockPos> knownPositions, BlockPos currentPos, Direction enteredFrom,
|
||||
CastingImage currentImage) {
|
||||
this.knownPositions = knownPositions;
|
||||
this.currentPos = currentPos;
|
||||
this.enteredFrom = enteredFrom;
|
||||
this.currentImage = currentImage;
|
||||
}
|
||||
|
||||
// Return null if the circle does not close.
|
||||
public static @Nullable CircleExecutionState createNew(BlockEntityAbstractImpetus impetus) {
|
||||
var level = (ServerLevel) impetus.getLevel();
|
||||
|
||||
// Flood fill! Just like VCC all over again.
|
||||
// this contains tentative positions and directions entered from
|
||||
var todo = new Stack<Pair<Direction, BlockPos>>();
|
||||
todo.add(Pair.of(impetus.getStartDirection(), impetus.getBlockPos().relative(impetus.getStartDirection())));
|
||||
var seenPositions = new HashSet<BlockPos>();
|
||||
var seenGoodPositions = new ArrayList<BlockPos>();
|
||||
|
||||
while (!todo.isEmpty()) {
|
||||
var pair = todo.pop();
|
||||
var enterDir = pair.getFirst();
|
||||
var herePos = pair.getSecond();
|
||||
|
||||
if (!seenPositions.add(herePos)) {
|
||||
// it's new
|
||||
var hereBs = level.getBlockState(herePos);
|
||||
if (!(hereBs.getBlock() instanceof ICircleComponent cmp)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cmp.canEnterFromDirection(enterDir, herePos, hereBs, level)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
seenGoodPositions.add(herePos);
|
||||
var outs = cmp.possibleExitDirections(herePos, hereBs, level);
|
||||
for (var out : outs) {
|
||||
todo.add(Pair.of(out, herePos.relative(out)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!seenPositions.contains(impetus.getBlockPos()) || seenGoodPositions.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var start = seenGoodPositions.get(0);
|
||||
return new CircleExecutionState(new HashSet<>(seenGoodPositions), start, impetus.getStartDirection(),
|
||||
new CastingImage());
|
||||
}
|
||||
|
||||
public CompoundTag save() {
|
||||
var out = new CompoundTag();
|
||||
|
||||
var knownTag = new ListTag();
|
||||
for (var bp : this.knownPositions) {
|
||||
knownTag.add(NbtUtils.writeBlockPos(bp));
|
||||
}
|
||||
out.put(TAG_KNOWN_POSITIONS, knownTag);
|
||||
|
||||
out.put(TAG_CURRENT_POS, NbtUtils.writeBlockPos(this.currentPos));
|
||||
out.putByte(TAG_ENTERED_FROM, (byte) this.enteredFrom.ordinal());
|
||||
out.put(TAG_IMAGE, this.currentImage.serializeToNbt());
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public static CircleExecutionState load(CompoundTag nbt, ServerLevel world) {
|
||||
var knownPositions = new HashSet<BlockPos>();
|
||||
var knownTag = nbt.getList(TAG_KNOWN_POSITIONS, Tag.TAG_COMPOUND);
|
||||
for (var tag : knownTag) {
|
||||
knownPositions.add(NbtUtils.readBlockPos(HexUtils.downcast(tag, CompoundTag.TYPE)));
|
||||
}
|
||||
|
||||
var currentPos = NbtUtils.readBlockPos(nbt.getCompound(TAG_CURRENT_POS));
|
||||
var enteredFrom = Direction.values()[nbt.getByte(TAG_ENTERED_FROM)];
|
||||
var image = CastingImage.loadFromNbt(nbt.getCompound(TAG_IMAGE), world);
|
||||
|
||||
return new CircleExecutionState(knownPositions, currentPos, enteredFrom, image);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this, also mutates the impetus.
|
||||
* <p>
|
||||
* Returns whether to continue.
|
||||
*/
|
||||
public boolean tick(BlockEntityAbstractImpetus impetus) {
|
||||
var world = (ServerLevel) impetus.getLevel();
|
||||
|
||||
var env = new CircleCastEnv(world, impetus.getBlockPos(), impetus.getStartDirection());
|
||||
|
||||
var executorBlock = world.getBlockState(this.currentPos);
|
||||
if (!(executorBlock instanceof ICircleComponent executor)) {
|
||||
// TODO: notification of the error?
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean halt = false;
|
||||
var ctrl = executor.acceptControlFlow(this.currentImage, env, this.enteredFrom, this.currentPos,
|
||||
executorBlock, world);
|
||||
if (ctrl instanceof ICircleComponent.ControlFlow.Stop stop) {
|
||||
// acceptControlFlow should have already posted the error
|
||||
halt = true;
|
||||
} else if (ctrl instanceof ICircleComponent.ControlFlow.Continue cont) {
|
||||
Pair<BlockPos, Direction> found = null;
|
||||
|
||||
for (var exit : cont.exits) {
|
||||
var there = world.getBlockState(exit.getFirst());
|
||||
if (there instanceof ICircleComponent cc
|
||||
&& cc.canEnterFromDirection(exit.getSecond(), exit.getFirst(), there, world)) {
|
||||
if (found != null) {
|
||||
// oh no!
|
||||
impetus.postError(
|
||||
Component.translatable("hexcasting.circles.many_exits",
|
||||
Component.literal(this.currentPos.toShortString()).withStyle(ChatFormatting.RED)),
|
||||
new ItemStack(Items.COMPASS));
|
||||
halt = true;
|
||||
break;
|
||||
} else {
|
||||
found = exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found == null) {
|
||||
// will never enter here if there were too manu because found will have been set
|
||||
impetus.postError(
|
||||
Component.translatable("hexcasting.circles.no_exits",
|
||||
Component.literal(this.currentPos.toShortString()).withStyle(ChatFormatting.RED)),
|
||||
new ItemStack(Items.OAK_SIGN));
|
||||
halt = true;
|
||||
}
|
||||
}
|
||||
|
||||
return !halt;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package at.petrak.hexcasting.api.casting.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implement this on a block to make circles interact with it.
|
||||
* <p>
|
||||
* This is its own interface so you can have your blocks subclass something else, and to avoid enormous
|
||||
* files. The mod doesn't check for the interface on anything but blocks.
|
||||
*/
|
||||
public interface ICircleComponent {
|
||||
/**
|
||||
* The heart of the interface! Functionally modify the casting environment.
|
||||
* <p>
|
||||
* With the new update you can have the side effects happen inline. In fact, you have to have the side effects
|
||||
* happen inline.
|
||||
* <p>
|
||||
* Also, return a list of directions that the control flow can exit this block in.
|
||||
* The circle environment will mishap if not exactly 1 of the returned directions can be accepted from.
|
||||
*/
|
||||
ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Direction enterDir, BlockPos pos,
|
||||
BlockState bs, ServerLevel world);
|
||||
|
||||
/**
|
||||
* Can this component get transferred to from a block coming in from that direction, with the given normal?
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
boolean canEnterFromDirection(Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world);
|
||||
|
||||
/**
|
||||
* This determines the directions the control flow <em>can</em> exit from. It's called at the beginning of execution
|
||||
* to see if the circle actually forms a loop.
|
||||
* <p>
|
||||
* For most blocks, this should be the same as returned from {@link ICircleComponent#acceptControlFlow}
|
||||
* Things like directrices might return otherwise. Whatever is returned when controlling flow must be a subset of
|
||||
* this set.
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
EnumSet<Direction> possibleExitDirections(BlockPos pos, BlockState bs, Level world);
|
||||
|
||||
abstract sealed class ControlFlow {
|
||||
public static final class Continue extends ControlFlow {
|
||||
public final CastingImage update;
|
||||
public final List<Pair<BlockPos, Direction>> exits;
|
||||
|
||||
public Continue(CastingImage update, List<Pair<BlockPos, Direction>> exits) {
|
||||
this.update = update;
|
||||
this.exits = exits;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class Stop extends ControlFlow {
|
||||
public Stop() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
89
Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java
vendored
Normal file
89
Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
package at.petrak.hexcasting.api.casting.eval.env;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.MishapEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class CircleCastEnv extends CastingEnvironment {
|
||||
protected final BlockPos impetusLoc;
|
||||
protected final Direction startDir;
|
||||
|
||||
public CircleCastEnv(ServerLevel world, BlockPos impetusLoc, Direction startDir) {
|
||||
super(world);
|
||||
this.impetusLoc = impetusLoc;
|
||||
this.startDir = startDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ServerPlayer getCaster() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MishapEnvironment getMishapEnvironment() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvalSound getSoundType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postExecution(CastResult result) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 mishapSprayPos() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long extractMedia(long cost) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVecInRange(Vec3 vec) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionHand castingHand() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getAlternateItem() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ItemStack> getUsableStacks(StackDiscoveryMode mode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrozenColorizer getColorizer() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void produceParticles(ParticleSpray particles, FrozenColorizer colorizer) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hexcasting.common.casting.env;
|
||||
package at.petrak.hexcasting.api.casting.eval.env;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult;
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hexcasting.common.casting.env;
|
||||
package at.petrak.hexcasting.api.casting.eval.env;
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hexcasting.common.casting.env;
|
||||
package at.petrak.hexcasting.api.casting.eval.env;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.MishapEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hexcasting.common.casting.env;
|
||||
package at.petrak.hexcasting.api.casting.eval.env;
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult;
|
||||
|
@ -72,15 +72,15 @@ public class StaffCastEnv extends PlayerBasedCastEnv {
|
|||
|
||||
sender.awardStat(HexStatistics.PATTERNS_DRAWN);
|
||||
|
||||
var harness = IXplatAbstractions.INSTANCE.getStaffcastVM(sender, msg.handUsed());
|
||||
var vm = IXplatAbstractions.INSTANCE.getStaffcastVM(sender, msg.handUsed());
|
||||
|
||||
ExecutionClientView clientInfo = harness.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
|
||||
ExecutionClientView clientInfo = vm.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
|
||||
|
||||
if (clientInfo.isStackClear()) {
|
||||
IXplatAbstractions.INSTANCE.setStaffcastImage(sender, null);
|
||||
IXplatAbstractions.INSTANCE.setPatterns(sender, List.of());
|
||||
} else {
|
||||
IXplatAbstractions.INSTANCE.setStaffcastImage(sender, harness);
|
||||
IXplatAbstractions.INSTANCE.setStaffcastImage(sender, vm.getImage());
|
||||
if (!resolvedPatterns.isEmpty()) {
|
||||
resolvedPatterns.get(resolvedPatterns.size() - 1).setType(clientInfo.getResolutionType());
|
||||
}
|
5
Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/package-info.java
vendored
Normal file
5
Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/package-info.java
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* Default impls for some casting and mishap envs for your convenience and also so i can impl
|
||||
* BlockEntityAbstractImpetus in api guilt-free
|
||||
*/
|
||||
package at.petrak.hexcasting.api.casting.eval.env;
|
|
@ -41,7 +41,6 @@ data class CastingImage private constructor(
|
|||
const val TAG_PARENTHESIZED = "parenthesized"
|
||||
const val TAG_ESCAPE_NEXT = "escape_next"
|
||||
const val TAG_USERDATA = "userdata"
|
||||
const val TAG_RAVENMIND = "ravenmind"
|
||||
|
||||
@JvmStatic
|
||||
public fun loadFromNbt(tag: CompoundTag, world: ServerLevel): CastingImage {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package at.petrak.hexcasting.api.casting.eval.vm
|
||||
package at.petrak.hexcasting.api.casting.eval.vharnessm
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI
|
||||
import at.petrak.hexcasting.api.casting.PatternShapeMatch
|
||||
|
@ -6,6 +6,9 @@ import at.petrak.hexcasting.api.casting.PatternShapeMatch.*
|
|||
import at.petrak.hexcasting.api.casting.SpellList
|
||||
import at.petrak.hexcasting.api.casting.eval.*
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.FrameEvaluate
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType
|
||||
import at.petrak.hexcasting.api.casting.iota.ListIota
|
||||
|
@ -30,13 +33,15 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
|
|||
/**
|
||||
* Execute a single iota.
|
||||
*/
|
||||
fun queueAndExecuteIota(iota: Iota, world: ServerLevel): ExecutionClientView = queueAndExecuteIotas(listOf(iota), world)
|
||||
fun queueExecuteAndWrapIota(iota: Iota, world: ServerLevel): ExecutionClientView = queueExecuteAndWrapIotas(listOf(iota), world)
|
||||
|
||||
/**
|
||||
* The main entrypoint to the VM. Given a list of iotas, execute them in sequence, and return whatever the client
|
||||
* needs to see.
|
||||
*
|
||||
* Mutates this
|
||||
*/
|
||||
fun queueAndExecuteIotas(iotas: List<Iota>, world: ServerLevel): ExecutionClientView {
|
||||
fun queueExecuteAndWrapIotas(iotas: List<Iota>, world: ServerLevel): ExecutionClientView {
|
||||
// Initialize the continuation stack to a single top-level eval for all iotas.
|
||||
var continuation = SpellContinuation.Done.pushFrame(FrameEvaluate(SpellList.LList(0, iotas), false))
|
||||
// Begin aggregating info
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package at.petrak.hexcasting.client.render;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
|
||||
import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicBookshelf;
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package at.petrak.hexcasting.common.blocks.entity;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hexcasting.common.blocks.entity;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hexcasting.common.blocks.entity;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
@ -80,9 +80,9 @@ public class BlockEntityStoredPlayerImpetus extends BlockEntityAbstractImpetus {
|
|||
}
|
||||
|
||||
public void applyScryingLensOverlay(List<Pair<ItemStack, Component>> lines,
|
||||
BlockState state, BlockPos pos, Player observer,
|
||||
Level world,
|
||||
Direction hitFace) {
|
||||
BlockState state, BlockPos pos, Player observer,
|
||||
Level world,
|
||||
Direction hitFace) {
|
||||
super.applyScryingLensOverlay(lines, state, pos, observer, world, hitFace);
|
||||
|
||||
var name = this.getPlayerName();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hexcasting.common.items.magic;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.item.MediaHolderItem;
|
||||
import at.petrak.hexcasting.api.misc.DiscoveryHandlers;
|
||||
import at.petrak.hexcasting.api.misc.MediaConstants;
|
||||
|
@ -186,15 +186,15 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
for (int i : arr) {
|
||||
if (i < 0) {
|
||||
entity.sendSystemMessage(Component.translatable("hexcasting.debug.media_" + langKey,
|
||||
stack.getDisplayName(),
|
||||
Component.translatable("hexcasting.debug." + allKey).withStyle(ChatFormatting.GRAY))
|
||||
stack.getDisplayName(),
|
||||
Component.translatable("hexcasting.debug." + allKey).withStyle(ChatFormatting.GRAY))
|
||||
.withStyle(ChatFormatting.LIGHT_PURPLE));
|
||||
} else {
|
||||
entity.sendSystemMessage(Component.translatable("hexcasting.debug.media_" + langKey + ".with_dust",
|
||||
stack.getDisplayName(),
|
||||
Component.literal("" + i).withStyle(ChatFormatting.WHITE),
|
||||
Component.literal(String.format("%.2f", i * 1.0 / MediaConstants.DUST_UNIT)).withStyle(
|
||||
ChatFormatting.WHITE))
|
||||
stack.getDisplayName(),
|
||||
Component.literal("" + i).withStyle(ChatFormatting.WHITE),
|
||||
Component.literal(String.format("%.2f", i * 1.0 / MediaConstants.DUST_UNIT)).withStyle(
|
||||
ChatFormatting.WHITE))
|
||||
.withStyle(ChatFormatting.LIGHT_PURPLE));
|
||||
}
|
||||
}
|
||||
|
@ -206,7 +206,8 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
BlockEntity be = context.getLevel().getBlockEntity(context.getClickedPos());
|
||||
if (be instanceof BlockEntityAbstractImpetus impetus) {
|
||||
impetus.setInfiniteMedia();
|
||||
context.getLevel().playSound(null, context.getClickedPos(), HexSounds.SPELL_CIRCLE_FIND_BLOCK, SoundSource.PLAYERS, 1f, 1f);
|
||||
context.getLevel().playSound(null, context.getClickedPos(), HexSounds.SPELL_CIRCLE_FIND_BLOCK,
|
||||
SoundSource.PLAYERS, 1f, 1f);
|
||||
return InteractionResult.sidedSuccess(context.getLevel().isClientSide());
|
||||
}
|
||||
return InteractionResult.PASS;
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
package at.petrak.hexcasting.common.items.magic;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.env.PackagedItemCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.item.HexHolderItem;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.casting.env.PackagedItemCastEnv;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package at.petrak.hexcasting.common.network;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
|
||||
import at.petrak.hexcasting.api.casting.eval.env.StaffCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.common.casting.env.StaffCastEnv;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
|
|
@ -607,6 +607,9 @@
|
|||
"hexcasting.mishap.unknown": "threw an exception (%s). This is a bug in the mod.",
|
||||
"hexcasting.mishap.shame": "Shame on you!",
|
||||
|
||||
"hexcasting.circles.no_exit": "The flow of media at %s could not find an exit",
|
||||
"hexcasting.circles.many_exits": "The flow of media at %s had too many exits",
|
||||
|
||||
"_comment": "Patchi stuff",
|
||||
|
||||
"hexcasting.landing": "I seem to have discovered a new method of magical arts, in which one draws patterns strange and wild onto a hexagonal grid. It fascinates me. I've decided to start a journal of my thoughts and findings.$(br2)$(l:https://discord.gg/4xxHGYteWk)Discord Server Link/$",
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package at.petrak.hexcasting.fabric.cc;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.env.StaffCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.common.casting.env.StaffCastEnv;
|
||||
import dev.onyxstudios.cca.api.v3.component.Component;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hexcasting.fabric.storage
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant
|
||||
|
@ -27,7 +27,7 @@ class FabricImpetusStorage(val impetus: BlockEntityAbstractImpetus) : SingleSlot
|
|||
fun insertStack(stack: ItemStack, transaction: TransactionContext) {
|
||||
val copied = stack.copy()
|
||||
val size = stack.count
|
||||
val extractable = impetus.extractMediaFromItem(stack, false)
|
||||
val extractable = impetus.extractMediaFromInsertedItem(stack, false)
|
||||
mediaToTake -= extractable
|
||||
val taken = size - stack.count
|
||||
itemsConsumed += taken.toLong()
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package at.petrak.hexcasting.forge.cap;
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.*;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.iota.DoubleIota;
|
||||
import at.petrak.hexcasting.api.item.ColorizerItem;
|
||||
import at.petrak.hexcasting.api.item.HexHolderItem;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package at.petrak.hexcasting.forge.cap;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
@ -29,7 +29,8 @@ public record ForgeImpetusCapability(BlockEntityAbstractImpetus impetus) impleme
|
|||
if (!simulate) {
|
||||
impetus.insertMedia(stack);
|
||||
} else {
|
||||
impetus.extractMediaFromItem(stack, false); // Media goes nowhere, since nothing is actually being done
|
||||
impetus.extractMediaFromInsertedItem(stack, false); // Media goes nowhere, since nothing is actually
|
||||
// being done
|
||||
}
|
||||
|
||||
return stack;
|
||||
|
|
Loading…
Reference in a new issue