commit
e07c4465ba
98 changed files with 1721 additions and 1090 deletions
|
@ -15,7 +15,7 @@ public interface ADHexHolder {
|
|||
@Nullable
|
||||
List<Iota> getHex(ServerLevel level);
|
||||
|
||||
void writeHex(List<Iota> patterns, int media);
|
||||
void writeHex(List<Iota> patterns, long media);
|
||||
|
||||
void clearHex();
|
||||
}
|
||||
|
|
|
@ -7,28 +7,28 @@ public interface ADMediaHolder {
|
|||
/**
|
||||
* Use {@code withdrawMedia(-1, true)}
|
||||
*
|
||||
* @see ADMediaHolder#withdrawMedia(int, boolean)
|
||||
* @see ADMediaHolder#withdrawMedia(long, boolean)
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
int getMedia();
|
||||
long getMedia();
|
||||
|
||||
/**
|
||||
* Use {@code withdrawMedia(-1, true) + insertMedia(-1, true)} where possible
|
||||
*
|
||||
* @see ADMediaHolder#insertMedia(int, boolean)
|
||||
* @see ADMediaHolder#withdrawMedia(int, boolean)
|
||||
* @see ADMediaHolder#insertMedia(long, boolean)
|
||||
* @see ADMediaHolder#withdrawMedia(long, boolean)
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
int getMaxMedia();
|
||||
long getMaxMedia();
|
||||
|
||||
/**
|
||||
* Use {@code insertMedia(media - withdrawMedia(-1, true), false)} where possible
|
||||
*
|
||||
* @see ADMediaHolder#insertMedia(int, boolean)
|
||||
* @see ADMediaHolder#withdrawMedia(int, boolean)
|
||||
* @see ADMediaHolder#insertMedia(long, boolean)
|
||||
* @see ADMediaHolder#withdrawMedia(long, boolean)
|
||||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
void setMedia(int media);
|
||||
void setMedia(long media);
|
||||
|
||||
/**
|
||||
* Whether this media holder can have media inserted into it.
|
||||
|
@ -63,7 +63,7 @@ public interface ADMediaHolder {
|
|||
* <p>
|
||||
* Withdrawing a negative amount will act as though you attempted to withdraw as much media as the holder contains.
|
||||
*/
|
||||
default int withdrawMedia(int cost, boolean simulate) {
|
||||
default long withdrawMedia(long cost, boolean simulate) {
|
||||
var mediaHere = getMedia();
|
||||
if (cost < 0) {
|
||||
cost = mediaHere;
|
||||
|
@ -83,9 +83,9 @@ public interface ADMediaHolder {
|
|||
* Inserting a negative amount will act as though you attempted to insert exactly as much media as the holder was
|
||||
* missing.
|
||||
*/
|
||||
default int insertMedia(int amount, boolean simulate) {
|
||||
default long insertMedia(long amount, boolean simulate) {
|
||||
var mediaHere = getMedia();
|
||||
int emptySpace = getMaxMedia() - mediaHere;
|
||||
long emptySpace = getMaxMedia() - mediaHere;
|
||||
if (emptySpace <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ public interface ADMediaHolder {
|
|||
amount = emptySpace;
|
||||
}
|
||||
|
||||
int inserting = Math.min(amount, emptySpace);
|
||||
long inserting = Math.min(amount, emptySpace);
|
||||
|
||||
if (!simulate) {
|
||||
var newMedia = mediaHere + inserting;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
@ -15,9 +17,9 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
// Facing dir is the direction it starts searching for slates in to start
|
||||
public abstract class BlockAbstractImpetus extends BlockCircleComponent implements EntityBlock {
|
||||
|
@ -30,21 +32,20 @@ public abstract class BlockAbstractImpetus extends BlockCircleComponent implemen
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level world) {
|
||||
public ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
|
||||
return new ControlFlow.Stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
|
||||
return enterDir != bs.getValue(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> exitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
public EnumSet<Direction> possibleExitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
return EnumSet.of(bs.getValue(FACING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable HexPattern getPattern(BlockPos pos, BlockState bs, Level world) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction normalDir(BlockPos pos, BlockState bs, Level world, int recursionLeft) {
|
||||
return normalDirOfOther(pos.relative(bs.getValue(FACING)), world, recursionLeft);
|
||||
|
@ -58,7 +59,7 @@ public abstract class BlockAbstractImpetus extends BlockCircleComponent implemen
|
|||
@Override
|
||||
public void tick(BlockState pState, ServerLevel pLevel, BlockPos pPos, RandomSource pRandom) {
|
||||
if (pLevel.getBlockEntity(pPos) instanceof BlockEntityAbstractImpetus tile && pState.getValue(ENERGIZED)) {
|
||||
tile.stepCircle();
|
||||
tile.tickExecution();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +67,8 @@ public abstract class BlockAbstractImpetus extends BlockCircleComponent implemen
|
|||
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
|
||||
if (!pNewState.is(pState.getBlock())
|
||||
&& pLevel.getBlockEntity(pPos) instanceof BlockEntityAbstractImpetus impetus) {
|
||||
impetus.stopCasting();
|
||||
impetus.endExecution(); // TODO: Determine if this was important
|
||||
// TODO: Fix this, it should make all the glowy circle components stop glowing.
|
||||
}
|
||||
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
|
||||
}
|
||||
|
|
|
@ -1,38 +1,47 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.api.casting.circles.ICircleComponent;
|
||||
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.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public abstract class BlockCircleComponent extends Block {
|
||||
public abstract class BlockCircleComponent extends Block implements ICircleComponent {
|
||||
public static final BooleanProperty ENERGIZED = BooleanProperty.create("energized");
|
||||
|
||||
public BlockCircleComponent(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can this component get transferred to from a block coming in from that direction, with the given normal?
|
||||
*/
|
||||
abstract public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos,
|
||||
BlockState bs, Level world);
|
||||
@Override
|
||||
public BlockState startEnergized(BlockPos pos, BlockState bs, Level world) {
|
||||
var newState = bs.setValue(ENERGIZED, true);
|
||||
world.setBlockAndUpdate(pos, newState);
|
||||
|
||||
abstract public EnumSet<Direction> exitDirections(BlockPos pos, BlockState bs, Level world);
|
||||
return newState;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
abstract public HexPattern getPattern(BlockPos pos, BlockState bs, Level world);
|
||||
@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 BlockCircleComponent#canEnterFromDirection(Direction, Direction, BlockPos, BlockState, Level)}
|
||||
* This is used for {@link ICircleComponent#canEnterFromDirection(Direction, BlockPos, BlockState, ServerLevel)}
|
||||
* as well as particles.
|
||||
*/
|
||||
public Direction normalDir(BlockPos pos, BlockState bs, Level world) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -232,7 +232,7 @@ fun List<Iota>.getIntBetween(idx: Int, min: Int, max: Int, argc: Int = 0): Int {
|
|||
return rounded
|
||||
}
|
||||
}
|
||||
throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "double.between", min, max)
|
||||
throw MishapInvalidIota.of(x, if (argc == 0) idx else argc - (idx + 1), "int.between", min, max)
|
||||
}
|
||||
|
||||
fun List<Iota>.getBlockPos(idx: Int, argc: Int = 0): BlockPos {
|
||||
|
|
|
@ -0,0 +1,401 @@
|
|||
package at.petrak.hexcasting.api.casting.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
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.server.level.ServerPlayer;
|
||||
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 long MAX_CAPACITY = 9_000_000_000_000_000_000L;
|
||||
|
||||
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 long 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() {
|
||||
if (this.level == null)
|
||||
return;
|
||||
|
||||
this.setChanged();
|
||||
|
||||
var state = this.getExecutionState();
|
||||
if (state == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var shouldContinue = state.tick(this);
|
||||
|
||||
if (!shouldContinue) {
|
||||
this.endExecution();
|
||||
this.executionState = null;
|
||||
}
|
||||
else
|
||||
this.level.scheduleTick(this.getBlockPos(), this.getBlockState().getBlock(), state.getTickSpeed());
|
||||
}
|
||||
|
||||
public void endExecution() {
|
||||
if (this.executionState == null)
|
||||
return;
|
||||
|
||||
this.executionState.endExecution(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.executionState != null)
|
||||
return this.executionState;
|
||||
|
||||
if (this.lazyExecutionState != null)
|
||||
this.executionState = CircleExecutionState.load(this.lazyExecutionState, (ServerLevel) this.level);
|
||||
|
||||
return this.executionState;
|
||||
}
|
||||
|
||||
public void startExecution(@Nullable ServerPlayer player) {
|
||||
if (this.level == null)
|
||||
return; // TODO: error here?
|
||||
if (this.level.isClientSide)
|
||||
return; // TODO: error here?
|
||||
|
||||
if (this.executionState != null) {
|
||||
return;
|
||||
}
|
||||
this.executionState = CircleExecutionState.createNew(this, player);
|
||||
|
||||
if (this.executionState == null)
|
||||
return;
|
||||
|
||||
var serverLevel = (ServerLevel) this.level;
|
||||
|
||||
serverLevel.scheduleTick(this.getBlockPos(), this.getBlockState().getBlock(), this.executionState.getTickSpeed());
|
||||
|
||||
serverLevel.setBlockAndUpdate(this.getBlockPos(), this.getBlockState().setValue(BlockCircleComponent.ENERGIZED, true));
|
||||
}
|
||||
|
||||
@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 long getMedia() {
|
||||
return this.media;
|
||||
}
|
||||
|
||||
public void setMedia(long media) {
|
||||
this.media = media;
|
||||
}
|
||||
|
||||
public long 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 long 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.putLong(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;
|
||||
}
|
||||
|
||||
if (tag.contains(TAG_MEDIA, Tag.TAG_INT)) {
|
||||
this.media = tag.getInt(TAG_MEDIA);
|
||||
} else if (tag.contains(TAG_MEDIA, Tag.TAG_LONG)) {
|
||||
this.media = tag.getLong(TAG_MEDIA);
|
||||
}
|
||||
}
|
||||
|
||||
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,271 @@
|
|||
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;
|
||||
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.world.item.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* See {@link BlockEntityAbstractImpetus}, this is what's stored in it
|
||||
*/
|
||||
public class CircleExecutionState {
|
||||
public static final String
|
||||
TAG_KNOWN_POSITIONS = "known_positions",
|
||||
TAG_REACHED_POSITIONS = "reached_positions",
|
||||
TAG_CURRENT_POS = "current_pos",
|
||||
TAG_ENTERED_FROM = "entered_from",
|
||||
TAG_IMAGE = "image",
|
||||
TAG_CASTER = "caster",
|
||||
TAG_COLORIZER = "colorizer";
|
||||
|
||||
// Does contain the starting impetus
|
||||
public final Set<BlockPos> knownPositions;
|
||||
public final List<BlockPos> reachedPositions;
|
||||
public BlockPos currentPos;
|
||||
public Direction enteredFrom;
|
||||
public CastingImage currentImage;
|
||||
public @Nullable UUID caster;
|
||||
public FrozenColorizer colorizer;
|
||||
|
||||
public final AABB bounds;
|
||||
|
||||
|
||||
protected CircleExecutionState(Set<BlockPos> knownPositions, List<BlockPos> reachedPositions,
|
||||
BlockPos currentPos, Direction enteredFrom,
|
||||
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));
|
||||
}
|
||||
|
||||
// Return null if the circle does not close.
|
||||
public static @Nullable CircleExecutionState createNew(BlockEntityAbstractImpetus impetus, @Nullable ServerPlayer caster) {
|
||||
var level = (ServerLevel) impetus.getLevel();
|
||||
|
||||
if (level == null)
|
||||
return null;
|
||||
|
||||
// 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 knownPositions = new HashSet<>(seenGoodPositions);
|
||||
var reachedPositions = new ArrayList<BlockPos>();
|
||||
reachedPositions.add(impetus.getBlockPos());
|
||||
var start = seenGoodPositions.get(0);
|
||||
|
||||
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() {
|
||||
var out = new CompoundTag();
|
||||
|
||||
var knownTag = new ListTag();
|
||||
for (var bp : this.knownPositions) {
|
||||
knownTag.add(NbtUtils.writeBlockPos(bp));
|
||||
}
|
||||
out.put(TAG_KNOWN_POSITIONS, knownTag);
|
||||
|
||||
var reachedTag = new ListTag();
|
||||
for (var bp : this.reachedPositions) {
|
||||
reachedTag.add(NbtUtils.writeBlockPos(bp));
|
||||
}
|
||||
out.put(TAG_REACHED_POSITIONS, reachedTag);
|
||||
|
||||
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());
|
||||
|
||||
if (this.caster != null)
|
||||
out.putUUID(TAG_CASTER, this.caster);
|
||||
|
||||
out.put(TAG_COLORIZER, this.colorizer.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 reachedPositions = new ArrayList<BlockPos>();
|
||||
var reachedTag = nbt.getList(TAG_REACHED_POSITIONS, Tag.TAG_COMPOUND);
|
||||
for (var tag : reachedTag) {
|
||||
reachedPositions.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);
|
||||
|
||||
UUID caster = null;
|
||||
if (nbt.hasUUID(TAG_CASTER))
|
||||
caster = nbt.getUUID(TAG_CASTER);
|
||||
|
||||
FrozenColorizer colorizer = FrozenColorizer.fromNBT(nbt.getCompound(TAG_COLORIZER));
|
||||
|
||||
return new CircleExecutionState(knownPositions, reachedPositions, currentPos, enteredFrom, image, caster, colorizer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update this, also mutates the impetus.
|
||||
* <p>
|
||||
* Returns whether to continue.
|
||||
*/
|
||||
public boolean tick(BlockEntityAbstractImpetus impetus) {
|
||||
var world = (ServerLevel) impetus.getLevel();
|
||||
|
||||
if (world == null)
|
||||
return true; // if the world is null, try again next tick.
|
||||
|
||||
ServerPlayer caster = null;
|
||||
if (this.caster != null && world.getEntity(this.caster) instanceof ServerPlayer player)
|
||||
caster = player;
|
||||
|
||||
var env = new CircleCastEnv(world, impetus.getBlockPos(), impetus.getStartDirection(), caster, this.bounds);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
executorBlockState = executor.startEnergized(this.currentPos, executorBlockState, world);
|
||||
this.reachedPositions.add(this.currentPos);
|
||||
|
||||
boolean halt = false;
|
||||
var ctrl = executor.acceptControlFlow(this.currentImage, env, this.enteredFrom, this.currentPos,
|
||||
executorBlockState, world);
|
||||
if (ctrl instanceof ICircleComponent.ControlFlow.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.getBlock() 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));
|
||||
ICircleComponent.sfx(this.currentPos, executorBlockState, world, Objects.requireNonNull(env.getImpetus()), false);
|
||||
halt = true;
|
||||
break;
|
||||
} else {
|
||||
found = exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (found == null) {
|
||||
// will never enter here if there were too many 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));
|
||||
ICircleComponent.sfx(this.currentPos, executorBlockState, world, Objects.requireNonNull(env.getImpetus()), false);
|
||||
halt = true;
|
||||
} else {
|
||||
// A single valid exit position has been found.
|
||||
ICircleComponent.sfx(this.currentPos, executorBlockState, world, Objects.requireNonNull(env.getImpetus()), true);
|
||||
currentPos = found.getFirst();
|
||||
enteredFrom = found.getSecond();
|
||||
currentImage = cont.update;
|
||||
}
|
||||
}
|
||||
|
||||
return !halt;
|
||||
}
|
||||
|
||||
/**
|
||||
* How many ticks should pass between activations, given the number of blocks encountered so far.
|
||||
*/
|
||||
protected int getTickSpeed() {
|
||||
return Math.max(2, 10 - (this.reachedPositions.size() - 1) / 3);
|
||||
}
|
||||
|
||||
public void endExecution(BlockEntityAbstractImpetus impetus) {
|
||||
var world = (ServerLevel) impetus.getLevel();
|
||||
|
||||
if (world == null)
|
||||
return; // TODO: error here?
|
||||
|
||||
for (var pos : this.reachedPositions) {
|
||||
var there = world.getBlockState(pos);
|
||||
if (there.getBlock() instanceof ICircleComponent cc) {
|
||||
cc.endEnergized(pos, there, world);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
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.
|
||||
* <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);
|
||||
|
||||
/**
|
||||
* Given the current position and a direction, return a pair of the new position after a step
|
||||
* in that direction, along with the direction (this is a helper function for creating
|
||||
* {@link ICircleComponent.ControlFlow}s.
|
||||
*/
|
||||
@Contract(pure = true)
|
||||
default Pair<BlockPos, Direction> exitPositionFromDirection(BlockPos pos, Direction dir) {
|
||||
return Pair.of(pos.offset(dir.getStepX(), dir.getStepY(), dir.getStepZ()), dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the {@link ICircleComponent} at the given position glowing. Returns the new state
|
||||
* of the given block.
|
||||
* // 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
|
||||
* the given block.
|
||||
*/
|
||||
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;
|
||||
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() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -104,6 +104,11 @@ public abstract class CastingEnvironment {
|
|||
*/
|
||||
public abstract boolean isVecInRange(Vec3 vec);
|
||||
|
||||
/**
|
||||
* Return whether the caster can edit blocks at the given permission (i.e. not adventure mode, etc.)
|
||||
*/
|
||||
public abstract boolean hasEditPermissionsAt(BlockPos vec);
|
||||
|
||||
public final boolean isVecInWorld(Vec3 vec) {
|
||||
return this.world.isInWorldBounds(new BlockPos(vec))
|
||||
&& this.world.getWorldBorder().isWithinBounds(vec.x, vec.z, 0.5);
|
||||
|
@ -122,7 +127,7 @@ public abstract class CastingEnvironment {
|
|||
*/
|
||||
public final void assertVecInRange(Vec3 vec) throws MishapLocationTooFarAway {
|
||||
this.assertVecInWorld(vec);
|
||||
if (this.isVecInRange(vec)) {
|
||||
if (!this.isVecInRange(vec)) {
|
||||
throw new MishapLocationTooFarAway(vec, "too_far");
|
||||
}
|
||||
}
|
||||
|
@ -132,8 +137,7 @@ public abstract class CastingEnvironment {
|
|||
}
|
||||
|
||||
public final boolean canEditBlockAt(BlockPos vec) {
|
||||
// TODO winfy: fill this in
|
||||
return false;
|
||||
return this.isVecInRange(Vec3.atCenterOf(vec)) && this.hasEditPermissionsAt(vec);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -143,7 +147,7 @@ public abstract class CastingEnvironment {
|
|||
if (!this.isVecInWorld(e.position())) {
|
||||
throw new MishapEntityTooFarAway(e);
|
||||
}
|
||||
if (this.isVecInRange(e.position())) {
|
||||
if (!this.isVecInRange(e.position())) {
|
||||
throw new MishapEntityTooFarAway(e);
|
||||
}
|
||||
}
|
||||
|
@ -157,10 +161,10 @@ public abstract class CastingEnvironment {
|
|||
}
|
||||
}
|
||||
|
||||
public abstract InteractionHand castingHand();
|
||||
public abstract InteractionHand getCastingHand();
|
||||
|
||||
public InteractionHand otherHand() {
|
||||
return HexUtils.otherHand(this.castingHand());
|
||||
public InteractionHand getOtherHand() {
|
||||
return HexUtils.otherHand(this.getCastingHand());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -176,6 +180,11 @@ public abstract class CastingEnvironment {
|
|||
*/
|
||||
protected abstract List<ItemStack> getUsableStacks(StackDiscoveryMode mode);
|
||||
|
||||
/**
|
||||
* Get the primary/secondary item stacks this env can use (i.e. main hand and offhand for the player).
|
||||
*/
|
||||
protected abstract List<HeldItemInfo> getPrimaryStacks();
|
||||
|
||||
/**
|
||||
* Return the slot from which to take blocks and items.
|
||||
*/
|
||||
|
@ -191,12 +200,12 @@ public abstract class CastingEnvironment {
|
|||
return null;
|
||||
}
|
||||
|
||||
public static record HeldItemInfo(ItemStack stack, InteractionHand hand) {
|
||||
public static record HeldItemInfo(ItemStack stack, @Nullable InteractionHand hand) {
|
||||
public ItemStack component1() {
|
||||
return stack;
|
||||
}
|
||||
|
||||
public InteractionHand component2() {
|
||||
public @Nullable InteractionHand component2() {
|
||||
return hand;
|
||||
}
|
||||
}
|
||||
|
@ -205,9 +214,14 @@ public abstract class CastingEnvironment {
|
|||
* Return the slot from which to take blocks and items.
|
||||
*/
|
||||
// TODO winfy: resolve the null here
|
||||
// @Nullable
|
||||
public HeldItemInfo getHeldItemToOperateOn(Predicate<ItemStack> stackOk) {
|
||||
// TODO winfy: return something properly
|
||||
public @Nullable HeldItemInfo getHeldItemToOperateOn(Predicate<ItemStack> stackOk) {
|
||||
var stacks = this.getPrimaryStacks();
|
||||
for (HeldItemInfo stack : stacks) {
|
||||
if (stackOk.test(stack.stack)) {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
169
Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java
vendored
Normal file
169
Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java
vendored
Normal file
|
@ -0,0 +1,169 @@
|
|||
package at.petrak.hexcasting.api.casting.eval.env;
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray;
|
||||
import at.petrak.hexcasting.api.casting.circles.BlockEntityAbstractImpetus;
|
||||
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 at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
|
||||
import net.minecraft.Util;
|
||||
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.DyeColor;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static at.petrak.hexcasting.api.casting.eval.env.PlayerBasedCastEnv.SENTINEL_RADIUS;
|
||||
|
||||
public class CircleCastEnv extends CastingEnvironment {
|
||||
protected EvalSound sound = HexEvalSounds.NOTHING;
|
||||
|
||||
protected final BlockPos impetusLoc;
|
||||
protected final Direction startDir;
|
||||
protected final @Nullable ServerPlayer caster;
|
||||
protected final AABB bounds;
|
||||
|
||||
public CircleCastEnv(ServerLevel world, BlockPos impetusLoc, Direction startDir, @Nullable ServerPlayer caster, AABB bounds) {
|
||||
super(world);
|
||||
this.impetusLoc = impetusLoc;
|
||||
this.startDir = startDir;
|
||||
this.caster = caster;
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ServerPlayer getCaster() {
|
||||
return this.caster;
|
||||
}
|
||||
|
||||
public @Nullable BlockEntityAbstractImpetus getImpetus() {
|
||||
var entity = this.world.getBlockEntity(impetusLoc);
|
||||
|
||||
if (entity instanceof BlockEntityAbstractImpetus)
|
||||
return (BlockEntityAbstractImpetus) entity;
|
||||
return null;
|
||||
}
|
||||
|
||||
public BlockPos getImpetusLoc() {
|
||||
return impetusLoc;
|
||||
}
|
||||
|
||||
public Direction getStartDir() {
|
||||
return startDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MishapEnvironment getMishapEnvironment() {
|
||||
return new CircleMishapEnv(this.world, this.impetusLoc, this.startDir, this.caster, this.bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvalSound getSoundType() {
|
||||
return sound;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postExecution(CastResult result) {
|
||||
this.sound = this.sound.greaterOf(result.getSound());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3 mishapSprayPos() {
|
||||
return Vec3.atCenterOf(impetusLoc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long extractMedia(long cost) {
|
||||
var entity = this.getImpetus();
|
||||
if (entity == null)
|
||||
return cost;
|
||||
|
||||
var mediaAvailable = entity.getMedia();
|
||||
if (mediaAvailable < 0)
|
||||
return 0;
|
||||
|
||||
long mediaToTake = Math.min(cost, mediaAvailable);
|
||||
cost -= mediaToTake;
|
||||
entity.setMedia(mediaAvailable - mediaToTake);
|
||||
|
||||
return cost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVecInRange(Vec3 vec) {
|
||||
if (this.caster != null) {
|
||||
var sentinel = HexAPI.instance().getSentinel(this.caster);
|
||||
if (sentinel != null
|
||||
&& sentinel.extendsRange()
|
||||
&& this.caster.getLevel().dimension() == sentinel.dimension()
|
||||
&& vec.distanceToSqr(sentinel.position()) <= SENTINEL_RADIUS * SENTINEL_RADIUS
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return this.bounds.contains(vec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEditPermissionsAt(BlockPos vec) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionHand getCastingHand() {
|
||||
return InteractionHand.MAIN_HAND;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getAlternateItem() {
|
||||
return ItemStack.EMPTY.copy(); // TODO: adjacent inventory/item frame?
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<ItemStack> getUsableStacks(StackDiscoveryMode mode) {
|
||||
return new ArrayList<>(); // TODO: Could do something like get items in inventories adjacent to the circle?
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<HeldItemInfo> getPrimaryStacks() {
|
||||
return List.of(); // TODO: Adjacent inv!
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrozenColorizer getColorizer() {
|
||||
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);
|
||||
}
|
||||
}
|
55
Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleMishapEnv.java
vendored
Normal file
55
Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleMishapEnv.java
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package at.petrak.hexcasting.api.casting.eval.env;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.MishapEnvironment;
|
||||
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.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class CircleMishapEnv extends MishapEnvironment {
|
||||
protected final BlockPos impetusLoc;
|
||||
protected final Direction startDir;
|
||||
protected final @Nullable ServerPlayer caster;
|
||||
protected final AABB bounds;
|
||||
|
||||
protected CircleMishapEnv(ServerLevel world, BlockPos impetusLoc, Direction startDir, @Nullable ServerPlayer caster, AABB bounds) {
|
||||
super(world, null);
|
||||
this.impetusLoc = impetusLoc;
|
||||
this.startDir = startDir;
|
||||
this.caster = caster;
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void yeetHeldItemsTowards(Vec3 targetPos) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dropHeldItems() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drown() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void damage(float healthProportion) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeXp(int amount) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void blind(int ticks) {
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
@ -34,6 +34,9 @@ public class PackagedItemCastEnv extends PlayerBasedCastEnv {
|
|||
|
||||
@Override
|
||||
public long extractMedia(long costLeft) {
|
||||
if (this.caster.isCreative())
|
||||
return 0;
|
||||
|
||||
var casterStack = this.caster.getItemInHand(this.castingHand);
|
||||
var casterHexHolder = IXplatAbstractions.INSTANCE.findHexHolder(casterStack);
|
||||
var canCastFromInv = casterHexHolder.canDrawMediaFromInventory();
|
||||
|
@ -54,7 +57,7 @@ public class PackagedItemCastEnv extends PlayerBasedCastEnv {
|
|||
}
|
||||
|
||||
@Override
|
||||
public InteractionHand castingHand() {
|
||||
public InteractionHand getCastingHand() {
|
||||
return this.castingHand;
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
@ -16,11 +16,13 @@ import at.petrak.hexcasting.api.mod.HexStatistics;
|
|||
import at.petrak.hexcasting.api.utils.HexUtils;
|
||||
import at.petrak.hexcasting.api.utils.MediaHelper;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.GameType;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
|
@ -102,6 +104,16 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment {
|
|||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<HeldItemInfo> getPrimaryStacks() {
|
||||
var primaryItem = this.caster.getItemInHand(this.castingHand);
|
||||
|
||||
if (primaryItem.isEmpty())
|
||||
primaryItem = ItemStack.EMPTY.copy();
|
||||
|
||||
return List.of(new HeldItemInfo(getAlternateItem(), this.getOtherHand()), new HeldItemInfo(primaryItem, this.castingHand));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVecInRange(Vec3 vec) {
|
||||
var sentinel = HexAPI.instance().getSentinel(this.caster);
|
||||
|
@ -116,6 +128,11 @@ public abstract class PlayerBasedCastEnv extends CastingEnvironment {
|
|||
return vec.distanceToSqr(this.caster.position()) <= AMBIT_RADIUS * AMBIT_RADIUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEditPermissionsAt(BlockPos vec) {
|
||||
return this.caster.gameMode.getGameModeForPlayer() != GameType.ADVENTURE && this.world.mayInteract(this.caster, vec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getAlternateItem() {
|
||||
var otherHand = HexUtils.otherHand(this.castingHand);
|
|
@ -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;
|
||||
|
@ -20,8 +20,13 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
|
||||
public class StaffCastEnv extends PlayerBasedCastEnv {
|
||||
private final InteractionHand castingHand;
|
||||
|
||||
|
||||
public StaffCastEnv(ServerPlayer caster, InteractionHand castingHand) {
|
||||
super(caster, castingHand);
|
||||
|
||||
this.castingHand = castingHand;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -35,6 +40,9 @@ public class StaffCastEnv extends PlayerBasedCastEnv {
|
|||
|
||||
@Override
|
||||
public long extractMedia(long cost) {
|
||||
if (this.caster.isCreative())
|
||||
return 0;
|
||||
|
||||
var canOvercast = this.canOvercast();
|
||||
var remaining = this.extractMediaFromInventory(cost, canOvercast);
|
||||
if (remaining > 0 && !canOvercast) {
|
||||
|
@ -43,6 +51,11 @@ public class StaffCastEnv extends PlayerBasedCastEnv {
|
|||
return remaining;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionHand getCastingHand() {
|
||||
return castingHand;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FrozenColorizer getColorizer() {
|
||||
return HexAPI.instance().getColorizer(this.caster);
|
||||
|
@ -73,18 +86,19 @@ public class StaffCastEnv extends PlayerBasedCastEnv {
|
|||
sender.awardStat(HexStatistics.PATTERNS_DRAWN);
|
||||
|
||||
var vm = IXplatAbstractions.INSTANCE.getStaffcastVM(sender, msg.handUsed());
|
||||
|
||||
// every time we send a new pattern it'll be happening in a different tick, so reset here
|
||||
// i don't think we can do this in the casting vm itself because it doesn't know if `queueAndExecuteIotas`
|
||||
// is being called from the top level or not
|
||||
vm.getImage().getUserData().remove(HexAPI.OP_COUNT_USERDATA);
|
||||
|
||||
ExecutionClientView clientInfo = vm.queueAndExecuteIota(new PatternIota(msg.pattern()), sender.getLevel());
|
||||
ExecutionClientView clientInfo = vm.queueExecuteAndWrapIota(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, vm);
|
||||
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;
|
|
@ -8,7 +8,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
*
|
||||
* @param sound the actual sound file
|
||||
* @param priority the priority of this sound. the sound with the highest priority in a given cast will be
|
||||
* playd.
|
||||
* played.
|
||||
* shortcutMetacasting takes precedence over this.
|
||||
*/
|
||||
public record EvalSound(@Nullable SoundEvent sound, int priority) {
|
||||
|
|
|
@ -39,7 +39,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,7 +1,6 @@
|
|||
package at.petrak.hexcasting.api.casting.eval.vm
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI
|
||||
import at.petrak.hexcasting.api.casting.PatternShapeMatch
|
||||
import at.petrak.hexcasting.api.casting.PatternShapeMatch.*
|
||||
import at.petrak.hexcasting.api.casting.SpellList
|
||||
import at.petrak.hexcasting.api.casting.eval.*
|
||||
|
@ -13,14 +12,9 @@ import at.petrak.hexcasting.api.casting.iota.PatternIota
|
|||
import at.petrak.hexcasting.api.casting.math.HexDir
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern
|
||||
import at.petrak.hexcasting.api.casting.mishaps.*
|
||||
import at.petrak.hexcasting.api.mod.HexConfig
|
||||
import at.petrak.hexcasting.api.mod.HexTags
|
||||
import at.petrak.hexcasting.api.utils.*
|
||||
import at.petrak.hexcasting.common.casting.PatternRegistryManifest
|
||||
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
|
||||
/**
|
||||
|
@ -31,14 +25,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
|
||||
|
@ -105,22 +100,7 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
|
|||
)
|
||||
}
|
||||
|
||||
if (iota is PatternIota) {
|
||||
return executePattern(iota.pattern, world, continuation)
|
||||
} else {
|
||||
return CastResult(
|
||||
continuation,
|
||||
null,
|
||||
listOf(
|
||||
OperatorSideEffect.DoMishap(
|
||||
MishapUnescapedValue(iota),
|
||||
Mishap.Context(HexPattern(HexDir.WEST), null)
|
||||
)
|
||||
), // Should never matter
|
||||
ResolvedPatternType.INVALID,
|
||||
HexEvalSounds.MISHAP
|
||||
)
|
||||
}
|
||||
return iota.execute(this, world, continuation)
|
||||
} catch (exception: Exception) {
|
||||
// This means something very bad has happened
|
||||
exception.printStackTrace()
|
||||
|
@ -142,93 +122,6 @@ class CastingVM(var image: CastingImage, val env: CastingEnvironment) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When the server gets a packet from the client with a new pattern,
|
||||
* handle it functionally.
|
||||
*/
|
||||
private fun executePattern(newPat: HexPattern, world: ServerLevel, continuation: SpellContinuation): CastResult {
|
||||
var castedName: Component? = null
|
||||
try {
|
||||
|
||||
val lookup = PatternRegistryManifest.matchPattern(newPat, world, false)
|
||||
this.env.precheckAction(lookup)
|
||||
|
||||
val action = if (lookup is Normal || lookup is PerWorld) {
|
||||
val key = when (lookup) {
|
||||
is Normal -> lookup.key
|
||||
is PerWorld -> lookup.key
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
|
||||
val reqsEnlightenment = isOfTag(IXplatAbstractions.INSTANCE.actionRegistry, key, HexTags.Actions.REQUIRES_ENLIGHTENMENT)
|
||||
|
||||
castedName = HexAPI.instance().getActionI18n(key, reqsEnlightenment)
|
||||
|
||||
IXplatAbstractions.INSTANCE.actionRegistry.get(key)!!.action
|
||||
} else if (lookup is Special) {
|
||||
castedName = lookup.handler.name
|
||||
lookup.handler.act()
|
||||
} else if (lookup is PatternShapeMatch.Nothing) {
|
||||
throw MishapInvalidPattern()
|
||||
} else throw IllegalStateException()
|
||||
|
||||
val opCount = if (this.image.userData.contains(HexAPI.OP_COUNT_USERDATA)) {
|
||||
this.image.userData.getInt(HexAPI.OP_COUNT_USERDATA)
|
||||
} else {
|
||||
this.image.userData.putInt(HexAPI.OP_COUNT_USERDATA, 0)
|
||||
0
|
||||
}
|
||||
if (opCount + 1 > HexConfig.server().maxOpCount()) {
|
||||
throw MishapEvalTooMuch()
|
||||
}
|
||||
this.image.userData.putInt(HexAPI.OP_COUNT_USERDATA, opCount + 1)
|
||||
|
||||
val sideEffects = mutableListOf<OperatorSideEffect>()
|
||||
var stack2: List<Iota>? = null
|
||||
var cont2 = continuation
|
||||
var userData2: CompoundTag? = null
|
||||
|
||||
val result = action.operate(
|
||||
this.env,
|
||||
this.image.stack.toMutableList(),
|
||||
this.image.userData.copy(),
|
||||
continuation
|
||||
)
|
||||
cont2 = result.newContinuation
|
||||
stack2 = result.newStack
|
||||
userData2 = result.newUserdata
|
||||
// TODO parens also break prescience
|
||||
sideEffects.addAll(result.sideEffects)
|
||||
|
||||
val hereFd = this.image
|
||||
val fd = if (stack2 != null) {
|
||||
hereFd.copy(
|
||||
stack = stack2,
|
||||
userData = userData2,
|
||||
)
|
||||
} else {
|
||||
hereFd
|
||||
}
|
||||
|
||||
return CastResult(
|
||||
cont2,
|
||||
fd,
|
||||
sideEffects,
|
||||
ResolvedPatternType.EVALUATED,
|
||||
env.soundType,
|
||||
)
|
||||
|
||||
} catch (mishap: Mishap) {
|
||||
return CastResult(
|
||||
continuation,
|
||||
null,
|
||||
listOf(OperatorSideEffect.DoMishap(mishap, Mishap.Context(newPat, castedName))),
|
||||
mishap.resolutionType(env),
|
||||
HexEvalSounds.MISHAP
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the side effects of a pattern, updating our aggregated info.
|
||||
*/
|
||||
|
|
|
@ -54,7 +54,7 @@ data class FrameForEach(
|
|||
val (stackTop, newImage, newCont) = if (data.nonEmpty) {
|
||||
// Increment the evaluation depth,
|
||||
// push the next datum to the top of the stack,
|
||||
Triple(data.car, harness.image.incDepth(), continuation
|
||||
Triple(data.car, harness.image, continuation
|
||||
// put the next Thoth object back on the stack for the next Thoth cycle,
|
||||
.pushFrame(FrameForEach(data.cdr, code, stack, acc))
|
||||
// and prep the Thoth'd code block for evaluation.
|
||||
|
|
|
@ -1,9 +1,23 @@
|
|||
package at.petrak.hexcasting.api.casting.iota;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult;
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType;
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation;
|
||||
import at.petrak.hexcasting.api.casting.math.HexDir;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapUnescapedValue;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public abstract class Iota {
|
||||
@NotNull
|
||||
|
@ -34,6 +48,34 @@ public abstract class Iota {
|
|||
*/
|
||||
abstract public @NotNull Tag serialize();
|
||||
|
||||
/**
|
||||
* This method is called when this iota is executed (i.e. Hermes is run on a list containing it, unescaped).
|
||||
* By default it will return a {@link CastResult} indicating an error has occurred.
|
||||
*/
|
||||
public @NotNull CastResult execute(CastingVM vm, ServerLevel world, SpellContinuation continuation) {
|
||||
return new CastResult(
|
||||
continuation,
|
||||
null,
|
||||
List.of(
|
||||
new OperatorSideEffect.DoMishap(
|
||||
new MishapUnescapedValue(this),
|
||||
new Mishap.Context(new HexPattern(HexDir.WEST, List.of()), null)
|
||||
)
|
||||
), // Should never matter
|
||||
ResolvedPatternType.INVALID,
|
||||
HexEvalSounds.MISHAP
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called to determine whether the iota is above the max serialisation depth/serialisation count limits. It should return every "iota" that is a subelement of this iota.
|
||||
* For example, if you implemented a Map<Iota, Iota>, then it should be an iterable over the keys *and* values of the map. If you implemented a typed List<Double> iota for some reason, it would
|
||||
* probably be a good idea to supply an iterable over those doubles mapped to double iotas.
|
||||
*/
|
||||
public @Nullable Iterable<Iota> subIotas() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Component display() {
|
||||
return this.type.display(this.serialize());
|
||||
}
|
||||
|
|
|
@ -91,11 +91,13 @@ public abstract class IotaType<T extends Iota> {
|
|||
if (totalEltsFound >= HexIotaTypes.MAX_SERIALIZATION_TOTAL) {
|
||||
return true; // too bad
|
||||
}
|
||||
if (iota instanceof ListIota subsublist) {
|
||||
var subIotas = iota.subIotas();
|
||||
if (subIotas != null) {
|
||||
if (depth + 1 >= HexIotaTypes.MAX_SERIALIZATION_DEPTH) {
|
||||
return true;
|
||||
}
|
||||
listsToExamine.addLast(new Pair<>(subsublist.getList(), depth + 1));
|
||||
|
||||
listsToExamine.addLast(new Pair<>(subIotas, depth + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +107,7 @@ public abstract class IotaType<T extends Iota> {
|
|||
|
||||
/**
|
||||
* This method attempts to find the type from the {@code type} key.
|
||||
* See {@link HexIotaTypes#serialize(Iota)} for the storage format.
|
||||
* See {@link IotaType#serialize(Iota)} for the storage format.
|
||||
*
|
||||
* @return {@code null} if it cannot get the type.
|
||||
*/
|
||||
|
|
|
@ -73,6 +73,11 @@ public class ListIota extends Iota {
|
|||
return out;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Iterable<Iota> subIotas() {
|
||||
return this.getList();
|
||||
}
|
||||
|
||||
public static IotaType<ListIota> TYPE = new IotaType<>() {
|
||||
@Nullable
|
||||
@Override
|
||||
|
|
|
@ -1,16 +1,40 @@
|
|||
package at.petrak.hexcasting.api.casting.iota;
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
|
||||
import at.petrak.hexcasting.api.casting.PatternShapeMatch;
|
||||
import at.petrak.hexcasting.api.casting.castables.Action;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastResult;
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPatternType;
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.OperatorSideEffect;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.Mishap;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapEvalTooMuch;
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapInvalidPattern;
|
||||
import at.petrak.hexcasting.api.mod.HexConfig;
|
||||
import at.petrak.hexcasting.api.mod.HexTags;
|
||||
import at.petrak.hexcasting.api.utils.HexUtils;
|
||||
import at.petrak.hexcasting.common.casting.PatternRegistryManifest;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexEvalSounds;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static at.petrak.hexcasting.api.utils.HexUtils.isOfTag;
|
||||
|
||||
public class PatternIota extends Iota {
|
||||
public PatternIota(@NotNull HexPattern pattern) {
|
||||
super(HexIotaTypes.PATTERN, pattern);
|
||||
|
@ -41,9 +65,82 @@ public class PatternIota extends Iota {
|
|||
return this.getPattern().serializeToNBT();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull CastResult execute(CastingVM vm, ServerLevel world, SpellContinuation continuation) {
|
||||
@Nullable Component castedName = null;
|
||||
try {
|
||||
var lookup = PatternRegistryManifest.matchPattern(this.getPattern(), world, false);
|
||||
vm.getEnv().precheckAction(lookup);
|
||||
|
||||
Action action;
|
||||
if (lookup instanceof PatternShapeMatch.Normal || lookup instanceof PatternShapeMatch.PerWorld) {
|
||||
ResourceKey<ActionRegistryEntry> key;
|
||||
if (lookup instanceof PatternShapeMatch.Normal normal) {
|
||||
key = normal.key;
|
||||
} else {
|
||||
PatternShapeMatch.PerWorld perWorld = (PatternShapeMatch.PerWorld) lookup;
|
||||
key = perWorld.key;
|
||||
}
|
||||
|
||||
var reqsEnlightenment = isOfTag(IXplatAbstractions.INSTANCE.getActionRegistry(), key, HexTags.Actions.REQUIRES_ENLIGHTENMENT);
|
||||
|
||||
castedName = HexAPI.instance().getActionI18n(key, reqsEnlightenment);
|
||||
|
||||
action = Objects.requireNonNull(IXplatAbstractions.INSTANCE.getActionRegistry().get(key)).action();
|
||||
} else if (lookup instanceof PatternShapeMatch.Special special) {
|
||||
castedName = special.handler.getName();
|
||||
action = special.handler.act();
|
||||
} else if (lookup instanceof PatternShapeMatch.Nothing) {
|
||||
throw new MishapInvalidPattern();
|
||||
} else throw new IllegalStateException();
|
||||
|
||||
var opCount = 0;
|
||||
if (vm.getImage().getUserData().contains(HexAPI.OP_COUNT_USERDATA)) {
|
||||
opCount = vm.getImage().getUserData().getInt(HexAPI.OP_COUNT_USERDATA);
|
||||
} else
|
||||
vm.getImage().getUserData().putInt(HexAPI.OP_COUNT_USERDATA, 0);
|
||||
|
||||
if (opCount + 1 > HexConfig.server().maxOpCount()) {
|
||||
throw new MishapEvalTooMuch();
|
||||
}
|
||||
vm.getImage().getUserData().putInt(HexAPI.OP_COUNT_USERDATA, opCount + 1);
|
||||
|
||||
var result = action.operate(
|
||||
vm.getEnv(),
|
||||
new ArrayList<>(vm.getImage().getStack()),
|
||||
vm.getImage().getUserData().copy(),
|
||||
continuation
|
||||
);
|
||||
|
||||
var cont2 = result.getNewContinuation();
|
||||
var stack2 = result.getNewStack();
|
||||
var userData2 = result.getNewUserdata();
|
||||
// TODO parens also break prescience
|
||||
var sideEffects = result.getSideEffects();
|
||||
|
||||
var hereFd = vm.getImage();
|
||||
hereFd = hereFd.copy(stack2, hereFd.getParenCount(), hereFd.getParenthesized(), hereFd.getEscapeNext(), userData2);
|
||||
|
||||
return new CastResult(
|
||||
cont2,
|
||||
hereFd,
|
||||
sideEffects,
|
||||
ResolvedPatternType.EVALUATED,
|
||||
vm.getEnv().getSoundType()
|
||||
);
|
||||
|
||||
} catch (Mishap mishap) {
|
||||
return new CastResult(
|
||||
continuation,
|
||||
null,
|
||||
List.of(new OperatorSideEffect.DoMishap(mishap, new Mishap.Context(this.getPattern(), castedName))),
|
||||
mishap.resolutionType(vm.getEnv()),
|
||||
HexEvalSounds.MISHAP
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public static IotaType<PatternIota> TYPE = new IotaType<>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public PatternIota deserialize(Tag tag, ServerLevel world) throws IllegalArgumentException {
|
||||
return PatternIota.deserialize(tag);
|
||||
|
|
|
@ -9,7 +9,7 @@ import net.minecraft.world.InteractionHand
|
|||
import net.minecraft.world.item.DyeColor
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
class MishapBadOffhandItem(val item: ItemStack, val hand: InteractionHand, val wanted: Component) : Mishap() {
|
||||
class MishapBadOffhandItem(val item: ItemStack, val hand: InteractionHand?, val wanted: Component) : Mishap() {
|
||||
override fun accentColor(ctx: CastingEnvironment, errorCtx: Context): FrozenColorizer =
|
||||
dyeColor(DyeColor.BROWN)
|
||||
|
||||
|
@ -24,7 +24,7 @@ class MishapBadOffhandItem(val item: ItemStack, val hand: InteractionHand, val w
|
|||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun of(item: ItemStack, hand: InteractionHand, stub: String, vararg args: Any): MishapBadOffhandItem {
|
||||
fun of(item: ItemStack, hand: InteractionHand?, stub: String, vararg args: Any): MishapBadOffhandItem {
|
||||
return MishapBadOffhandItem(item, hand, "hexcasting.mishap.bad_item.$stub".asTranslatedComponent(*args))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public interface HexHolderItem extends MediaHolderItem {
|
|||
@Nullable
|
||||
List<Iota> getHex(ItemStack stack, ServerLevel level);
|
||||
|
||||
void writeHex(ItemStack stack, List<Iota> program, int media);
|
||||
void writeHex(ItemStack stack, List<Iota> program, long media);
|
||||
|
||||
void clearHex(ItemStack stack);
|
||||
}
|
||||
|
|
|
@ -11,25 +11,25 @@ import org.jetbrains.annotations.ApiStatus;
|
|||
*/
|
||||
@ApiStatus.OverrideOnly
|
||||
public interface MediaHolderItem {
|
||||
int getMedia(ItemStack stack);
|
||||
long getMedia(ItemStack stack);
|
||||
|
||||
int getMaxMedia(ItemStack stack);
|
||||
long getMaxMedia(ItemStack stack);
|
||||
|
||||
void setMedia(ItemStack stack, int media);
|
||||
void setMedia(ItemStack stack, long media);
|
||||
|
||||
boolean canProvideMedia(ItemStack stack);
|
||||
|
||||
boolean canRecharge(ItemStack stack);
|
||||
|
||||
default float getMediaFullness(ItemStack stack) {
|
||||
int max = getMaxMedia(stack);
|
||||
long max = getMaxMedia(stack);
|
||||
if (max == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (float) getMedia(stack) / (float) max;
|
||||
}
|
||||
|
||||
default int withdrawMedia(ItemStack stack, int cost, boolean simulate) {
|
||||
default long withdrawMedia(ItemStack stack, long cost, boolean simulate) {
|
||||
var mediaHere = getMedia(stack);
|
||||
if (cost < 0) {
|
||||
cost = mediaHere;
|
||||
|
@ -41,9 +41,9 @@ public interface MediaHolderItem {
|
|||
return Math.min(cost, mediaHere);
|
||||
}
|
||||
|
||||
default int insertMedia(ItemStack stack, int amount, boolean simulate) {
|
||||
default long insertMedia(ItemStack stack, long amount, boolean simulate) {
|
||||
var mediaHere = getMedia(stack);
|
||||
int emptySpace = getMaxMedia(stack) - mediaHere;
|
||||
long emptySpace = getMaxMedia(stack) - mediaHere;
|
||||
if (emptySpace <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ public interface MediaHolderItem {
|
|||
amount = emptySpace;
|
||||
}
|
||||
|
||||
int inserting = Math.min(amount, emptySpace);
|
||||
long inserting = Math.min(amount, emptySpace);
|
||||
|
||||
if (!simulate) {
|
||||
var newMedia = mediaHere + inserting;
|
||||
|
|
|
@ -28,10 +28,10 @@ fun isMediaItem(stack: ItemStack): Boolean {
|
|||
@JvmOverloads
|
||||
fun extractMedia(
|
||||
stack: ItemStack,
|
||||
cost: Int = -1,
|
||||
cost: Long = -1,
|
||||
drainForBatteries: Boolean = false,
|
||||
simulate: Boolean = false
|
||||
): Int {
|
||||
): Long {
|
||||
val mediaHolder = IXplatAbstractions.INSTANCE.findMediaHolder(stack) ?: return 0
|
||||
|
||||
return extractMedia(mediaHolder, cost, drainForBatteries, simulate)
|
||||
|
@ -47,10 +47,10 @@ fun extractMedia(
|
|||
*/
|
||||
fun extractMedia(
|
||||
holder: ADMediaHolder,
|
||||
cost: Int = -1,
|
||||
cost: Long = -1,
|
||||
drainForBatteries: Boolean = false,
|
||||
simulate: Boolean = false
|
||||
): Int {
|
||||
): Long {
|
||||
if (drainForBatteries && !holder.canConstructBattery())
|
||||
return 0
|
||||
|
||||
|
@ -84,11 +84,12 @@ fun compareMediaItem(aMedia: ADMediaHolder, bMedia: ADMediaHolder): Int {
|
|||
if (priority != 0)
|
||||
return priority
|
||||
|
||||
return aMedia.withdrawMedia(-1, true) - bMedia.withdrawMedia(-1, true)
|
||||
return (aMedia.withdrawMedia(-1, true) - bMedia.withdrawMedia(-1, true))
|
||||
.coerceIn(Int.MIN_VALUE.toLong(), Int.MAX_VALUE.toLong()).toInt()
|
||||
}
|
||||
|
||||
fun mediaBarColor(media: Int, maxMedia: Int): Int {
|
||||
val amt = if (maxMedia == 0) {
|
||||
fun mediaBarColor(media: Long, maxMedia: Long): Int {
|
||||
val amt = if (maxMedia == 0L) {
|
||||
0f
|
||||
} else {
|
||||
media.toFloat() / maxMedia.toFloat()
|
||||
|
@ -100,8 +101,8 @@ fun mediaBarColor(media: Int, maxMedia: Int): Int {
|
|||
return Mth.color(r / 255f, g / 255f, b / 255f)
|
||||
}
|
||||
|
||||
fun mediaBarWidth(media: Int, maxMedia: Int): Int {
|
||||
val amt = if (maxMedia == 0) {
|
||||
fun mediaBarWidth(media: Long, maxMedia: Long): Int {
|
||||
val amt = if (maxMedia == 0L) {
|
||||
0f
|
||||
} else {
|
||||
media.toFloat() / maxMedia.toFloat()
|
||||
|
|
|
@ -36,7 +36,7 @@ public class HexAdditionalRenderers {
|
|||
var player = Minecraft.getInstance().player;
|
||||
if (player != null) {
|
||||
var sentinel = IXplatAbstractions.INSTANCE.getSentinel(player);
|
||||
if (sentinel.hasSentinel() && player.getLevel().dimension().equals(sentinel.dimension())) {
|
||||
if (sentinel != null && player.getLevel().dimension().equals(sentinel.dimension())) {
|
||||
renderSentinel(sentinel, player, ps, partialTick);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
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.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.client.ScryingLensOverlayRegistry;
|
||||
import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicBookshelf;
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks;
|
||||
|
@ -63,7 +64,7 @@ public class ScryingLensOverlays {
|
|||
if (world.getBlockEntity(pos) instanceof BlockEntityAkashicBookshelf tile) {
|
||||
var iotaTag = tile.getIotaTag();
|
||||
if (iotaTag != null) {
|
||||
var display = HexIotaTypes.getDisplay(iotaTag);
|
||||
var display = IotaType.getDisplay(iotaTag);
|
||||
lines.add(new Pair<>(new ItemStack(Items.BOOK), display));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
@ -14,9 +16,9 @@ import net.minecraft.world.level.block.state.StateDefinition;
|
|||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
// As it turns out, not actually an impetus
|
||||
public class BlockEmptyImpetus extends BlockCircleComponent {
|
||||
|
@ -29,21 +31,20 @@ public class BlockEmptyImpetus extends BlockCircleComponent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level world) {
|
||||
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))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
|
||||
return enterDir != bs.getValue(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> exitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
public EnumSet<Direction> possibleExitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
return EnumSet.of(bs.getValue(FACING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable HexPattern getPattern(BlockPos pos, BlockState bs, Level world) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction normalDir(BlockPos pos, BlockState bs, Level world, int recursionLeft) {
|
||||
return normalDirOfOther(pos.relative(bs.getValue(FACING)), world, recursionLeft);
|
||||
|
|
|
@ -2,11 +2,15 @@ package at.petrak.hexcasting.common.blocks.circles;
|
|||
|
||||
import at.petrak.hexcasting.annotations.SoftImplement;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.PatternIota;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
@ -31,7 +35,10 @@ import net.minecraft.world.phys.shapes.VoxelShape;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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").
|
||||
|
@ -70,14 +77,37 @@ public class BlockSlate extends BlockCircleComponent implements EntityBlock, Sim
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level world) {
|
||||
var thisNormal = this.normalDir(pos, bs, world);
|
||||
return enterDir != thisNormal && normalDir == thisNormal;
|
||||
public ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
|
||||
HexPattern pattern;
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntitySlate tile) {
|
||||
pattern = tile.pattern;
|
||||
} else {
|
||||
return new ControlFlow.Stop();
|
||||
}
|
||||
|
||||
var exitDirsSet = this.possibleExitDirections(pos, bs, world);
|
||||
exitDirsSet.remove(enterDir.getOpposite());
|
||||
|
||||
var exitDirs = exitDirsSet.stream().map((dir) -> this.exitPositionFromDirection(pos, dir));
|
||||
|
||||
if (pattern == null)
|
||||
return new ControlFlow.Continue(imageIn, exitDirs.toList());
|
||||
|
||||
var vm = new CastingVM(imageIn, env);
|
||||
|
||||
vm.queueExecuteAndWrapIota(new PatternIota(pattern), world);
|
||||
|
||||
return new ControlFlow.Continue(vm.getImage(), exitDirs.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> exitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
public boolean canEnterFromDirection(Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
|
||||
var thisNormal = this.normalDir(pos, bs, world);
|
||||
return enterDir != thisNormal && enterDir != thisNormal.getOpposite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> possibleExitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
var allDirs = EnumSet.allOf(Direction.class);
|
||||
var normal = this.normalDir(pos, bs, world);
|
||||
allDirs.remove(normal);
|
||||
|
@ -85,16 +115,6 @@ public class BlockSlate extends BlockCircleComponent implements EntityBlock, Sim
|
|||
return allDirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable
|
||||
HexPattern getPattern(BlockPos pos, BlockState bs, Level world) {
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntitySlate tile) {
|
||||
return tile.pattern;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SoftImplement("forge")
|
||||
public ItemStack getCloneItemStack(BlockState state, HitResult target, BlockGetter level, BlockPos pos,
|
||||
Player player) {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles.directrix;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
|
@ -13,9 +15,9 @@ import net.minecraft.world.level.block.state.StateDefinition;
|
|||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockEmptyDirectrix extends BlockCircleComponent {
|
||||
public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.AXIS;
|
||||
|
@ -28,20 +30,21 @@ public class BlockEmptyDirectrix extends BlockCircleComponent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level world) {
|
||||
public ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
|
||||
var sign = world.random.nextBoolean() ? Direction.AxisDirection.POSITIVE : Direction.AxisDirection.NEGATIVE;
|
||||
return new ControlFlow.Continue(imageIn, List.of(this.exitPositionFromDirection(pos, Direction.fromAxisAndDirection(bs.getValue(AXIS), sign))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> exitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
var sign = world.random.nextBoolean() ? Direction.AxisDirection.POSITIVE : Direction.AxisDirection.NEGATIVE;
|
||||
return EnumSet.of(Direction.fromAxisAndDirection(bs.getValue(AXIS), sign));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable HexPattern getPattern(BlockPos pos, BlockState bs, Level world) {
|
||||
return null;
|
||||
public EnumSet<Direction> possibleExitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
return EnumSet.of(
|
||||
Direction.fromAxisAndDirection(bs.getValue(AXIS), Direction.AxisDirection.NEGATIVE),
|
||||
Direction.fromAxisAndDirection(bs.getValue(AXIS), Direction.AxisDirection.POSITIVE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles.directrix;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.casting.math.HexPattern;
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.particles.DustParticleOptions;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.RandomSource;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
@ -18,9 +20,9 @@ import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
|||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockRedstoneDirectrix extends BlockCircleComponent {
|
||||
public static final DirectionProperty FACING = BlockStateProperties.FACING;
|
||||
|
@ -35,19 +37,18 @@ public class BlockRedstoneDirectrix extends BlockCircleComponent {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level world) {
|
||||
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, getRealFacing(bs))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
|
||||
return enterDir != getRealFacing(bs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> exitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
return EnumSet.of(getRealFacing(bs));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable HexPattern getPattern(BlockPos pos, BlockState bs, Level world) {
|
||||
return null;
|
||||
public EnumSet<Direction> possibleExitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
return EnumSet.of(bs.getValue(FACING), bs.getValue(FACING).getOpposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -31,7 +31,8 @@ public class BlockRightClickImpetus extends BlockAbstractImpetus {
|
|||
var tile = pLevel.getBlockEntity(pPos);
|
||||
if (tile instanceof BlockEntityRightClickImpetus impetus) {
|
||||
if (pPlayer instanceof ServerPlayer serverPlayer) {
|
||||
impetus.activateSpellCircle(serverPlayer);
|
||||
// impetus.activateSpellCircle(serverPlayer);
|
||||
impetus.startExecution(serverPlayer);
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
|
|
@ -92,9 +92,11 @@ public class BlockStoredPlayerImpetus extends BlockAbstractImpetus {
|
|||
|
||||
if (isPowered && pLevel.getBlockEntity(pPos) instanceof BlockEntityStoredPlayerImpetus tile) {
|
||||
var player = tile.getStoredPlayer();
|
||||
if (player instanceof ServerPlayer splayer) {
|
||||
if (player == null) {
|
||||
tile.startExecution(null);
|
||||
} else if (player instanceof ServerPlayer splayer) {
|
||||
// phew
|
||||
tile.activateSpellCircle(splayer);
|
||||
tile.startExecution(splayer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -27,11 +27,6 @@ public class BlockEntityLookingImpetus extends BlockEntityAbstractImpetus {
|
|||
super(HexBlockEntities.IMPETUS_LOOK_TILE, pWorldPosition, pBlockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activatorAlwaysInRange() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// https://github.com/VazkiiMods/Botania/blob/2607bcd31c4eaeb617f7d1b3ec1c1db08f59add4/Common/src/main/java/vazkii/botania/common/block/tile/TileEnderEye.java#L27
|
||||
public static void serverTick(Level level, BlockPos pos, BlockState bs, BlockEntityLookingImpetus self) {
|
||||
if (bs.getValue(BlockCircleComponent.ENERGIZED)) {
|
||||
|
@ -74,7 +69,7 @@ public class BlockEntityLookingImpetus extends BlockEntityAbstractImpetus {
|
|||
if (newLook != prevLookAmt) {
|
||||
if (newLook == MAX_LOOK_AMOUNT) {
|
||||
self.lookAmount = 0;
|
||||
self.activateSpellCircle(looker);
|
||||
self.startExecution(looker);
|
||||
} else {
|
||||
if (newLook % 5 == 1) {
|
||||
var t = (float) newLook / MAX_LOOK_AMOUNT;
|
||||
|
|
|
@ -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;
|
||||
|
@ -9,9 +9,4 @@ public class BlockEntityRightClickImpetus extends BlockEntityAbstractImpetus {
|
|||
public BlockEntityRightClickImpetus(BlockPos pWorldPosition, BlockState pBlockState) {
|
||||
super(HexBlockEntities.IMPETUS_RIGHTCLICK_TILE, pWorldPosition, pBlockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activatorAlwaysInRange() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -35,17 +35,6 @@ public class BlockEntityStoredPlayerImpetus extends BlockEntityAbstractImpetus {
|
|||
super(HexBlockEntities.IMPETUS_STOREDPLAYER_TILE, pWorldPosition, pBlockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activatorAlwaysInRange() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable
|
||||
Player getPlayer() {
|
||||
return this.storedPlayer == null ? null : this.level.getPlayerByUUID(this.storedPlayer);
|
||||
}
|
||||
|
||||
protected @Nullable
|
||||
GameProfile getPlayerName() {
|
||||
Player player = getStoredPlayer();
|
||||
|
@ -76,13 +65,13 @@ public class BlockEntityStoredPlayerImpetus extends BlockEntityAbstractImpetus {
|
|||
// just feels wrong to use the protected method
|
||||
public @Nullable
|
||||
Player getStoredPlayer() {
|
||||
return this.getPlayer();
|
||||
return null; // TODO: Fix
|
||||
}
|
||||
|
||||
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();
|
||||
|
|
|
@ -3,6 +3,7 @@ package at.petrak.hexcasting.common.casting.operators.circles
|
|||
import at.petrak.hexcasting.api.casting.castables.ConstMediaAction
|
||||
import at.petrak.hexcasting.api.casting.asActionResult
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapNoSpellCircle
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
@ -11,11 +12,11 @@ class OpCircleBounds(val max: Boolean) : ConstMediaAction {
|
|||
override val argc = 0
|
||||
|
||||
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> {
|
||||
val circle = ctx.spellCircle
|
||||
if (circle == null)
|
||||
if (ctx !is CircleCastEnv)
|
||||
throw MishapNoSpellCircle()
|
||||
val circle = ctx.impetus ?: throw MishapNoSpellCircle()
|
||||
|
||||
val aabb = circle.aabb
|
||||
val aabb = circle.executionState!!.bounds // the circle should have an execution state since it's executing this.
|
||||
|
||||
return if (max)
|
||||
Vec3(aabb.maxX - 0.5, aabb.maxY - 0.5, aabb.maxZ - 0.5).asActionResult
|
||||
|
|
|
@ -4,6 +4,7 @@ import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus
|
|||
import at.petrak.hexcasting.api.casting.castables.ConstMediaAction
|
||||
import at.petrak.hexcasting.api.casting.asActionResult
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapNoSpellCircle
|
||||
|
||||
|
@ -11,13 +12,9 @@ object OpImpetusDir : ConstMediaAction {
|
|||
override val argc = 0
|
||||
|
||||
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> {
|
||||
val circle = ctx.spellCircle
|
||||
if (circle == null)
|
||||
if (ctx !is CircleCastEnv)
|
||||
throw MishapNoSpellCircle()
|
||||
|
||||
val pos = circle.impetusPos
|
||||
val bs = ctx.world.getBlockState(pos)
|
||||
val dir = bs.getValue(BlockAbstractImpetus.FACING)
|
||||
return dir.step().asActionResult
|
||||
return ctx.startDir.step().asActionResult
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package at.petrak.hexcasting.common.casting.operators.circles
|
|||
import at.petrak.hexcasting.api.casting.castables.ConstMediaAction
|
||||
import at.petrak.hexcasting.api.casting.asActionResult
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
|
||||
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapNoSpellCircle
|
||||
|
||||
|
@ -10,10 +11,9 @@ object OpImpetusPos : ConstMediaAction {
|
|||
override val argc = 0
|
||||
|
||||
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> {
|
||||
val circle = ctx.spellCircle
|
||||
if (circle == null)
|
||||
if (ctx !is CircleCastEnv)
|
||||
throw MishapNoSpellCircle()
|
||||
|
||||
return circle.impetusPos.asActionResult
|
||||
return ctx.impetusLoc.asActionResult
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
package at.petrak.hexcasting.common.casting.operators.eval
|
||||
|
||||
import at.petrak.hexcasting.api.casting.SpellList
|
||||
import at.petrak.hexcasting.api.casting.asActionResult
|
||||
import at.petrak.hexcasting.api.casting.castables.Action
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
|
||||
import at.petrak.hexcasting.api.casting.eval.OperationResult
|
||||
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.FrameFinishEval
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
|
||||
|
@ -25,11 +23,6 @@ object OpEval : Action {
|
|||
val datum = stack.removeLastOrNull() ?: throw MishapNotEnoughArgs(1, 0)
|
||||
val instrs = evaluatable(datum, 0)
|
||||
|
||||
instrs.ifRight {
|
||||
CastingImage.incDepth(userData)
|
||||
it.asActionResult
|
||||
}
|
||||
|
||||
// if not installed already...
|
||||
// also, never make a break boundary when evaluating just one pattern
|
||||
val newCont =
|
||||
|
@ -41,6 +34,6 @@ object OpEval : Action {
|
|||
|
||||
val instrsList = instrs.map({ SpellList.LList(0, listOf(PatternIota(it))) }, { it })
|
||||
val frame = FrameEvaluate(instrsList, true)
|
||||
return OperationResult(listOf(), userData, listOf(), newCont.pushFrame(frame))
|
||||
return OperationResult(stack, userData, listOf(), newCont.pushFrame(frame))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package at.petrak.hexcasting.common.casting.operators.rw
|
|||
import at.petrak.hexcasting.api.casting.castables.ConstMediaAction
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.casting.iota.NullIota
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapBadOffhandItem
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
|
||||
|
@ -13,7 +14,7 @@ object OpRead : ConstMediaAction {
|
|||
val (handStack, hand) = ctx.getHeldItemToOperateOn {
|
||||
val dataHolder = IXplatAbstractions.INSTANCE.findDataHolder(it)
|
||||
dataHolder != null && (dataHolder.readIota(ctx.world) != null || dataHolder.emptyIota() != null)
|
||||
}
|
||||
} ?: return listOf(NullIota())
|
||||
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(handStack)
|
||||
?: throw MishapBadOffhandItem.of(handStack, hand, "iota.read")
|
||||
|
|
|
@ -12,7 +12,7 @@ object OpReadable : ConstMediaAction {
|
|||
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> {
|
||||
val (handStack) = ctx.getHeldItemToOperateOn {
|
||||
IXplatAbstractions.INSTANCE.findDataHolder(it) != null
|
||||
}
|
||||
} ?: return false.asActionResult
|
||||
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(handStack)
|
||||
?: return false.asActionResult
|
||||
|
|
|
@ -15,7 +15,7 @@ object OpWritable : ConstMediaAction {
|
|||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(it)
|
||||
|
||||
datumHolder != null
|
||||
}
|
||||
} ?: return false.asActionResult
|
||||
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(handStack) ?: return false.asActionResult
|
||||
val success = datumHolder.writeIota(NullIota(), true)
|
||||
|
|
|
@ -9,6 +9,7 @@ import at.petrak.hexcasting.api.casting.iota.Iota
|
|||
import at.petrak.hexcasting.api.casting.mishaps.MishapBadOffhandItem
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapOthersName
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
// we make this a spell cause imo it's a little ... anticlimactic for it to just make no noise
|
||||
object OpWrite : SpellAction {
|
||||
|
@ -23,7 +24,7 @@ object OpWrite : SpellAction {
|
|||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(it)
|
||||
|
||||
datumHolder != null && datumHolder.writeIota(datum, true)
|
||||
}
|
||||
} ?: throw MishapBadOffhandItem.of(ItemStack.EMPTY.copy(), null, "iota.write") // TODO: hack
|
||||
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(handStack)
|
||||
?: throw MishapBadOffhandItem.of(handStack, hand, "iota.write")
|
||||
|
|
|
@ -9,6 +9,9 @@ object OpGetCaster : ConstMediaAction {
|
|||
override val argc = 0
|
||||
|
||||
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> {
|
||||
if (ctx.caster == null)
|
||||
return null.asActionResult
|
||||
|
||||
ctx.assertEntityInRange(ctx.caster)
|
||||
return ctx.caster.asActionResult
|
||||
}
|
||||
|
|
|
@ -19,6 +19,8 @@ object OpColorize : SpellAction {
|
|||
ctx: CastingEnvironment
|
||||
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
|
||||
val (handStack, hand) = ctx.getHeldItemToOperateOn(IXplatAbstractions.INSTANCE::isColorizer)
|
||||
?: throw MishapBadOffhandItem.of(ItemStack.EMPTY, null, "colorizer") // TODO: hack
|
||||
|
||||
if (!IXplatAbstractions.INSTANCE.isColorizer(handStack)) {
|
||||
throw MishapBadOffhandItem.of(
|
||||
handStack,
|
||||
|
@ -26,6 +28,7 @@ object OpColorize : SpellAction {
|
|||
"colorizer"
|
||||
)
|
||||
}
|
||||
|
||||
return Triple(
|
||||
Spell(handStack),
|
||||
MediaConstants.DUST_UNIT,
|
||||
|
|
|
@ -53,14 +53,14 @@ class OpConjureBlock(val light: Boolean) : SpellAction {
|
|||
if (worldState.canBeReplaced(placeContext)) {
|
||||
val block = if (this.light) HexBlocks.CONJURED_LIGHT else HexBlocks.CONJURED_BLOCK
|
||||
|
||||
if (!IXplatAbstractions.INSTANCE.isPlacingAllowed(ctx.world, pos, ItemStack(block), ctx.caster))
|
||||
if (ctx.caster != null && !IXplatAbstractions.INSTANCE.isPlacingAllowed(ctx.world, pos, ItemStack(block), ctx.caster))
|
||||
return
|
||||
|
||||
val state = block.getStateForPlacement(placeContext)
|
||||
if (state != null) {
|
||||
ctx.world.setBlock(pos, state, 5)
|
||||
|
||||
val colorizer = IXplatAbstractions.INSTANCE.getColorizer(ctx.caster)
|
||||
val colorizer = ctx.colorizer
|
||||
|
||||
if (ctx.world.getBlockState(pos).block is BlockConjured) {
|
||||
BlockConjured.setColor(ctx.world, pos, colorizer)
|
||||
|
|
|
@ -49,7 +49,7 @@ class OpCreateFluid(val cost: Int, val bucket: Item, val cauldron: BlockState, v
|
|||
ctx.world.setBlock(pos, cauldron, 3)
|
||||
else if (!IXplatAbstractions.INSTANCE.tryPlaceFluid(
|
||||
ctx.world,
|
||||
ctx.castingHand(),
|
||||
ctx.getCastingHand(),
|
||||
pos,
|
||||
fluid
|
||||
) && bucket is BucketItem) {
|
||||
|
|
|
@ -23,7 +23,8 @@ object OpErase : SpellAction {
|
|||
|
||||
(hexHolder?.hasHex() == true) ||
|
||||
(datumHolder?.writeIota(null, true) == true)
|
||||
}
|
||||
} ?: throw MishapBadOffhandItem.of(ItemStack.EMPTY.copy(), null, "eraseable") // TODO: hack
|
||||
|
||||
val hexHolder = IXplatAbstractions.INSTANCE.findHexHolder(handStack)
|
||||
val datumHolder = IXplatAbstractions.INSTANCE.findDataHolder(handStack)
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@ import net.minecraft.world.InteractionHand
|
|||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
// TODO: how to handle in cirles
|
||||
object OpMakeBattery : SpellAction {
|
||||
override val argc = 1
|
||||
|
||||
|
@ -28,6 +29,10 @@ object OpMakeBattery : SpellAction {
|
|||
val entity = args.getItemEntity(0, argc)
|
||||
|
||||
val (handStack, hand) = ctx.getHeldItemToOperateOn { it.`is`(HexTags.Items.PHIAL_BASE) }
|
||||
?: throw MishapBadOffhandItem.of(ItemStack.EMPTY.copy(), null, "bottle") // TODO: hack
|
||||
|
||||
if (hand == null)
|
||||
throw MishapBadOffhandItem.of(handStack, null, "havent_handled_null_hand_yet") // TODO: hack!
|
||||
|
||||
if (!handStack.`is`(HexTags.Items.PHIAL_BASE)) {
|
||||
throw MishapBadOffhandItem.of(
|
||||
|
@ -68,10 +73,10 @@ object OpMakeBattery : SpellAction {
|
|||
val entityStack = itemEntity.item.copy()
|
||||
val mediamount = extractMedia(entityStack, drainForBatteries = true)
|
||||
if (mediamount > 0) {
|
||||
ctx.caster.setItemInHand(
|
||||
ctx.caster?.setItemInHand(
|
||||
hand,
|
||||
ItemMediaHolder.withMedia(ItemStack(HexItems.BATTERY), mediamount, mediamount)
|
||||
)
|
||||
) ?: return
|
||||
}
|
||||
|
||||
itemEntity.item = entityStack
|
||||
|
|
|
@ -14,6 +14,7 @@ import at.petrak.hexcasting.xplat.IXplatAbstractions
|
|||
import net.minecraft.world.entity.item.ItemEntity
|
||||
import net.minecraft.world.item.ItemStack
|
||||
|
||||
// TODO: How to handle in circles
|
||||
class OpMakePackagedSpell<T : ItemPackagedHex>(val itemType: T, val cost: Int) : SpellAction {
|
||||
override val argc = 2
|
||||
override fun execute(
|
||||
|
@ -27,6 +28,8 @@ class OpMakePackagedSpell<T : ItemPackagedHex>(val itemType: T, val cost: Int) :
|
|||
val hexHolder = IXplatAbstractions.INSTANCE.findHexHolder(it)
|
||||
it.`is`(itemType) && hexHolder != null && !hexHolder.hasHex()
|
||||
}
|
||||
?: throw MishapBadOffhandItem(ItemStack.EMPTY.copy(), null, itemType.description) // TODO: hack
|
||||
|
||||
val hexHolder = IXplatAbstractions.INSTANCE.findHexHolder(handStack)
|
||||
if (!handStack.`is`(itemType)) {
|
||||
throw MishapBadOffhandItem(handStack, hand, itemType.description)
|
||||
|
@ -47,7 +50,7 @@ class OpMakePackagedSpell<T : ItemPackagedHex>(val itemType: T, val cost: Int) :
|
|||
)
|
||||
}
|
||||
|
||||
val trueName = MishapOthersName.getTrueNameFromArgs(patterns, ctx.caster)
|
||||
val trueName = ctx.caster?.let { MishapOthersName.getTrueNameFromArgs(patterns, it) }
|
||||
if (trueName != null)
|
||||
throw MishapOthersName(trueName)
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ import at.petrak.hexcasting.api.casting.iota.Iota
|
|||
import at.petrak.hexcasting.api.casting.mishaps.MishapBadBlock
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.core.particles.BlockParticleOption
|
||||
import net.minecraft.core.particles.ParticleTypes
|
||||
import net.minecraft.sounds.SoundSource
|
||||
|
@ -20,6 +21,7 @@ import net.minecraft.world.item.context.UseOnContext
|
|||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
// TODO: how to handle in cirles
|
||||
object OpPlaceBlock : SpellAction {
|
||||
override val argc: Int
|
||||
get() = 1
|
||||
|
@ -32,9 +34,9 @@ object OpPlaceBlock : SpellAction {
|
|||
ctx.assertVecInRange(pos)
|
||||
|
||||
val blockHit = BlockHitResult(
|
||||
Vec3.atCenterOf(pos), ctx.caster.direction, pos, false
|
||||
Vec3.atCenterOf(pos), ctx.caster?.direction ?: Direction.NORTH, pos, false
|
||||
)
|
||||
val itemUseCtx = UseOnContext(ctx.caster, ctx.castingHand, blockHit)
|
||||
val itemUseCtx = ctx.caster?.let { UseOnContext(it, ctx.castingHand, blockHit) } ?: return null
|
||||
val placeContext = BlockPlaceContext(itemUseCtx)
|
||||
|
||||
val worldState = ctx.world.getBlockState(pos)
|
||||
|
@ -53,34 +55,36 @@ object OpPlaceBlock : SpellAction {
|
|||
if (!ctx.canEditBlockAt(pos))
|
||||
return
|
||||
|
||||
val caster = ctx.caster ?: return // TODO: Fix!
|
||||
|
||||
val blockHit = BlockHitResult(
|
||||
Vec3.atCenterOf(pos), ctx.caster.direction, pos, false
|
||||
Vec3.atCenterOf(pos), caster.direction, pos, false
|
||||
)
|
||||
|
||||
val bstate = ctx.world.getBlockState(pos)
|
||||
val placeeStack = ctx.getOperativeSlot { it.item is BlockItem }?.copy()
|
||||
val placeeStack = ctx.getHeldItemToOperateOn { it.item is BlockItem }?.stack
|
||||
if (placeeStack != null) {
|
||||
if (!IXplatAbstractions.INSTANCE.isPlacingAllowed(ctx.world, pos, placeeStack, ctx.caster))
|
||||
return
|
||||
|
||||
if (!placeeStack.isEmpty) {
|
||||
// https://github.com/VazkiiMods/Psi/blob/master/src/main/java/vazkii/psi/common/spell/trick/block/PieceTrickPlaceBlock.java#L143
|
||||
val oldStack = ctx.caster.getItemInHand(ctx.castingHand)
|
||||
val oldStack = caster.getItemInHand(ctx.castingHand)
|
||||
val spoofedStack = placeeStack.copy()
|
||||
|
||||
// we temporarily give the player the stack, place it using mc code, then give them the old stack back.
|
||||
spoofedStack.count = 1
|
||||
ctx.caster.setItemInHand(ctx.castingHand, spoofedStack)
|
||||
caster.setItemInHand(ctx.castingHand, spoofedStack)
|
||||
|
||||
val itemUseCtx = UseOnContext(ctx.caster, ctx.castingHand, blockHit)
|
||||
val itemUseCtx = UseOnContext(caster, ctx.castingHand, blockHit)
|
||||
val placeContext = BlockPlaceContext(itemUseCtx)
|
||||
if (bstate.canBeReplaced(placeContext)) {
|
||||
if (ctx.withdrawItem(placeeStack, 1, false)) {
|
||||
if (ctx.withdrawItem({ it == placeeStack }, 1, false)) {
|
||||
val res = spoofedStack.useOn(placeContext)
|
||||
|
||||
ctx.caster.setItemInHand(ctx.castingHand, oldStack)
|
||||
caster.setItemInHand(ctx.castingHand, oldStack)
|
||||
if (res != InteractionResult.FAIL) {
|
||||
ctx.withdrawItem(placeeStack, 1, true)
|
||||
ctx.withdrawItem({ it == placeeStack }, 1, true)
|
||||
|
||||
ctx.world.playSound(
|
||||
ctx.caster,
|
||||
|
@ -95,10 +99,10 @@ object OpPlaceBlock : SpellAction {
|
|||
)
|
||||
}
|
||||
} else {
|
||||
ctx.caster.setItemInHand(ctx.castingHand, oldStack)
|
||||
caster.setItemInHand(ctx.castingHand, oldStack)
|
||||
}
|
||||
} else {
|
||||
ctx.caster.setItemInHand(ctx.castingHand, oldStack)
|
||||
caster.setItemInHand(ctx.castingHand, oldStack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ object OpPrint : Action {
|
|||
|
||||
private data class Spell(val datum: Iota) : RenderedSpell {
|
||||
override fun cast(ctx: CastingEnvironment) {
|
||||
ctx.caster.sendSystemMessage(datum.display())
|
||||
ctx.caster?.sendSystemMessage(datum.display()) // TODO: how to handle in cirles
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,8 +25,9 @@ object OpRecharge : SpellAction {
|
|||
|
||||
val (handStack, hand) = ctx.getHeldItemToOperateOn {
|
||||
val media = IXplatAbstractions.INSTANCE.findMediaHolder(it)
|
||||
media != null && media.canRecharge() && media.insertMedia(-1, true) != 0
|
||||
media != null && media.canRecharge() && media.insertMedia(-1, true) != 0L
|
||||
}
|
||||
?: throw MishapBadOffhandItem.of(ItemStack.EMPTY.copy(), null, "rechargable") // TODO: hack
|
||||
|
||||
val media = IXplatAbstractions.INSTANCE.findMediaHolder(handStack)
|
||||
|
||||
|
@ -46,7 +47,7 @@ object OpRecharge : SpellAction {
|
|||
)
|
||||
}
|
||||
|
||||
if (media.insertMedia(-1, true) == 0)
|
||||
if (media.insertMedia(-1, true) == 0L)
|
||||
return null
|
||||
|
||||
return Triple(
|
||||
|
|
|
@ -34,15 +34,17 @@ object OpTheOnlyReasonAnyoneDownloadedPsi : SpellAction {
|
|||
|
||||
private data class Spell(val pos: BlockPos) : RenderedSpell {
|
||||
override fun cast(ctx: CastingEnvironment) {
|
||||
val caster = ctx.caster ?: return // TODO: fix!
|
||||
|
||||
// https://github.com/VazkiiMods/Psi/blob/master/src/main/java/vazkii/psi/common/spell/trick/PieceTrickOvergrow.java
|
||||
if (!ctx.world.mayInteract(ctx.caster, pos))
|
||||
if (!ctx.world.mayInteract(caster, pos))
|
||||
return
|
||||
|
||||
val hit = BlockHitResult(Vec3.ZERO, Direction.UP, pos, false)
|
||||
val save: ItemStack = ctx.caster.getItemInHand(InteractionHand.MAIN_HAND)
|
||||
ctx.caster.setItemInHand(InteractionHand.MAIN_HAND, ItemStack(Items.BONE_MEAL))
|
||||
val fakeContext = UseOnContext(ctx.caster, InteractionHand.MAIN_HAND, hit)
|
||||
ctx.caster.setItemInHand(InteractionHand.MAIN_HAND, save)
|
||||
val save: ItemStack = caster.getItemInHand(InteractionHand.MAIN_HAND)
|
||||
caster.setItemInHand(InteractionHand.MAIN_HAND, ItemStack(Items.BONE_MEAL))
|
||||
val fakeContext = UseOnContext(caster, InteractionHand.MAIN_HAND, hit)
|
||||
caster.setItemInHand(InteractionHand.MAIN_HAND, save)
|
||||
Items.BONE_MEAL.useOn(fakeContext)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ object OpBrainsweep : SpellAction {
|
|||
|
||||
IXplatAbstractions.INSTANCE.brainsweep(sacrifice)
|
||||
if (sacrifice is Villager && HexConfig.server().doVillagersTakeOffenseAtMindMurder()) {
|
||||
sacrifice.tellWitnessesThatIWasMurdered(ctx.caster)
|
||||
ctx.caster?.let { sacrifice.tellWitnessesThatIWasMurdered(it) }
|
||||
}
|
||||
|
||||
val sound = (sacrifice as AccessorLivingEntity).`hex$getDeathSound`()
|
||||
|
|
|
@ -33,7 +33,6 @@ class OpCreateSentinel(val extendsRange: Boolean) : SpellAction {
|
|||
IXplatAbstractions.INSTANCE.setSentinel(
|
||||
ctx.caster,
|
||||
Sentinel(
|
||||
true,
|
||||
extendsRange,
|
||||
target,
|
||||
ctx.world.dimension()
|
||||
|
|
|
@ -2,7 +2,6 @@ package at.petrak.hexcasting.common.casting.operators.spells.sentinel
|
|||
|
||||
import at.petrak.hexcasting.api.misc.MediaConstants
|
||||
|
||||
import at.petrak.hexcasting.api.player.Sentinel
|
||||
import at.petrak.hexcasting.api.casting.ParticleSpray
|
||||
import at.petrak.hexcasting.api.casting.RenderedSpell
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
|
@ -16,9 +15,9 @@ object OpDestroySentinel : SpellAction {
|
|||
override fun execute(
|
||||
args: List<Iota>,
|
||||
ctx: CastingEnvironment
|
||||
): Triple<RenderedSpell, Int, List<ParticleSpray>> {
|
||||
): Triple<RenderedSpell, Int, List<ParticleSpray>>? {
|
||||
val particles = mutableListOf<ParticleSpray>()
|
||||
val sentinel = IXplatAbstractions.INSTANCE.getSentinel(ctx.caster)
|
||||
val sentinel = IXplatAbstractions.INSTANCE.getSentinel(ctx.caster) ?: return null
|
||||
// TODO why can't you remove things from other dimensions?
|
||||
if (sentinel.dimension != ctx.world.dimension())
|
||||
throw MishapLocationInWrongDimension(sentinel.dimension.location())
|
||||
|
@ -33,7 +32,7 @@ object OpDestroySentinel : SpellAction {
|
|||
|
||||
private object Spell : RenderedSpell {
|
||||
override fun cast(ctx: CastingEnvironment) {
|
||||
IXplatAbstractions.INSTANCE.setSentinel(ctx.caster, Sentinel.none())
|
||||
IXplatAbstractions.INSTANCE.setSentinel(ctx.caster, null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,12 +13,9 @@ object OpGetSentinelPos : ConstMediaAction {
|
|||
override val argc = 0
|
||||
override val mediaCost = MediaConstants.DUST_UNIT / 10
|
||||
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> {
|
||||
val sentinel = IXplatAbstractions.INSTANCE.getSentinel(ctx.caster)
|
||||
val sentinel = IXplatAbstractions.INSTANCE.getSentinel(ctx.caster) ?: return listOf(NullIota())
|
||||
if (sentinel.dimension != ctx.world.dimension())
|
||||
throw MishapLocationInWrongDimension(sentinel.dimension.location())
|
||||
return if (sentinel.hasSentinel)
|
||||
sentinel.position.asActionResult
|
||||
else
|
||||
listOf(NullIota())
|
||||
return sentinel.position.asActionResult
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,14 +18,11 @@ object OpGetSentinelWayfind : ConstMediaAction {
|
|||
override fun execute(args: List<Iota>, ctx: CastingEnvironment): List<Iota> {
|
||||
val from = args.getVec3(0, argc)
|
||||
|
||||
val sentinel = IXplatAbstractions.INSTANCE.getSentinel(ctx.caster)
|
||||
val sentinel = IXplatAbstractions.INSTANCE.getSentinel(ctx.caster) ?: return listOf(NullIota())
|
||||
|
||||
if (sentinel.dimension != ctx.world.dimension())
|
||||
throw MishapLocationInWrongDimension(sentinel.dimension.location())
|
||||
|
||||
return if (!sentinel.hasSentinel)
|
||||
listOf(NullIota())
|
||||
else
|
||||
sentinel.position.subtract(from).normalize().asActionResult
|
||||
return sentinel.position.subtract(from).normalize().asActionResult
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,20 +24,26 @@ object OpFisherman : Action {
|
|||
|
||||
val depth = let {
|
||||
val x = stack.last()
|
||||
stack.removeLast()
|
||||
val maxIdx = stack.size - 1
|
||||
if (x is DoubleIota) {
|
||||
val double = x.double
|
||||
val rounded = double.roundToInt()
|
||||
if (abs(double - rounded) <= DoubleIota.TOLERANCE && rounded in 1..maxIdx) {
|
||||
if (abs(double - rounded) <= DoubleIota.TOLERANCE && rounded in -maxIdx..maxIdx) {
|
||||
return@let rounded
|
||||
}
|
||||
}
|
||||
throw MishapInvalidIota.of(x, 0, "double.between", 1, maxIdx)
|
||||
throw MishapInvalidIota.of(x, 0, "int.between", -maxIdx, maxIdx)
|
||||
}
|
||||
|
||||
if (depth >= 0) {
|
||||
val fish = stack.removeAt(stack.size - 1 - depth)
|
||||
stack.add(fish)
|
||||
} else {
|
||||
val lure = stack.removeLast()
|
||||
stack.add(stack.size + depth, lure)
|
||||
}
|
||||
|
||||
stack.removeLast()
|
||||
val fish = stack.removeAt(stack.size - depth)
|
||||
stack.add(fish)
|
||||
|
||||
return OperationResult(stack, userData, listOf(), continuation)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ import at.petrak.hexcasting.api.casting.castables.Action
|
|||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment
|
||||
import at.petrak.hexcasting.api.casting.eval.OperationResult
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.SpellContinuation
|
||||
import at.petrak.hexcasting.api.casting.getPositiveIntUnderInclusive
|
||||
import at.petrak.hexcasting.api.casting.getIntBetween
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota
|
||||
import at.petrak.hexcasting.api.casting.mishaps.MishapNotEnoughArgs
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
|
@ -19,10 +19,16 @@ object OpFishermanButItCopies : Action {
|
|||
if (stack.size < 2)
|
||||
throw MishapNotEnoughArgs(2, stack.size)
|
||||
|
||||
val depth = stack.getPositiveIntUnderInclusive(stack.lastIndex, stack.size - 2)
|
||||
val depth = stack.getIntBetween(stack.lastIndex, -(stack.size - 2), stack.size - 2)
|
||||
stack.removeLast()
|
||||
val fish = stack.get(stack.size - 1 - depth)
|
||||
stack.add(fish)
|
||||
|
||||
if (depth >= 0) {
|
||||
val fish = stack[stack.size - 1 - depth]
|
||||
stack.add(fish)
|
||||
} else {
|
||||
val lure = stack.last()
|
||||
stack.add(stack.size - 1 + depth, lure)
|
||||
}
|
||||
|
||||
return OperationResult(stack, userData, listOf(), continuation)
|
||||
}
|
||||
|
|
|
@ -38,8 +38,8 @@ public class ItemStaff extends Item {
|
|||
var descs = harness.generateDescs();
|
||||
|
||||
IXplatAbstractions.INSTANCE.sendPacketToPlayer(serverPlayer,
|
||||
new MsgOpenSpellGuiAck(hand, patterns, descs.getFirst(), descs.getSecond(), descs.getThird(),
|
||||
harness.getParenCount()));
|
||||
new MsgOpenSpellGuiAck(hand, patterns, descs.getFirst(), descs.getSecond(),
|
||||
0)); // TODO: Fix!
|
||||
}
|
||||
|
||||
player.awardStat(Stats.ITEM_USED.get(this));
|
||||
|
|
|
@ -5,17 +5,17 @@ import net.minecraft.world.item.ItemStack;
|
|||
|
||||
public record DebugUnlockerHolder(ItemStack creativeUnlocker) implements ADMediaHolder {
|
||||
@Override
|
||||
public int getMedia() {
|
||||
public long getMedia() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMedia() {
|
||||
public long getMaxMedia() {
|
||||
return Integer.MAX_VALUE - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMedia(int media) {
|
||||
public void setMedia(long media) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
|
@ -40,15 +40,15 @@ public record DebugUnlockerHolder(ItemStack creativeUnlocker) implements ADMedia
|
|||
}
|
||||
|
||||
@Override
|
||||
public int withdrawMedia(int cost, boolean simulate) {
|
||||
ItemCreativeUnlocker.addToIntArray(creativeUnlocker, ItemCreativeUnlocker.TAG_EXTRACTIONS, cost);
|
||||
public long withdrawMedia(long cost, boolean simulate) {
|
||||
ItemCreativeUnlocker.addToLongArray(creativeUnlocker, ItemCreativeUnlocker.TAG_EXTRACTIONS, cost);
|
||||
|
||||
return cost < 0 ? getMedia() : cost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertMedia(int amount, boolean simulate) {
|
||||
ItemCreativeUnlocker.addToIntArray(creativeUnlocker, ItemCreativeUnlocker.TAG_INSERTIONS, amount);
|
||||
public long insertMedia(long amount, boolean simulate) {
|
||||
ItemCreativeUnlocker.addToLongArray(creativeUnlocker, ItemCreativeUnlocker.TAG_INSERTIONS, amount);
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -65,7 +65,10 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
});
|
||||
|
||||
DiscoveryHandlers.addMediaHolderDiscoverer(harness -> {
|
||||
var player = harness.getCtx().getCaster();
|
||||
var player = harness.getEnv().getCaster();
|
||||
if (player == null)
|
||||
return List.of();
|
||||
|
||||
if (!player.isCreative())
|
||||
return List.of();
|
||||
|
||||
|
@ -112,17 +115,17 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getMedia(ItemStack stack) {
|
||||
return Integer.MAX_VALUE;
|
||||
public long getMedia(ItemStack stack) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMedia(ItemStack stack) {
|
||||
return Integer.MAX_VALUE - 1;
|
||||
public long getMaxMedia(ItemStack stack) {
|
||||
return Long.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMedia(ItemStack stack, int media) {
|
||||
public void setMedia(ItemStack stack, long media) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
|
@ -146,21 +149,31 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
NBTHelper.putIntArray(stack, tag, newArr);
|
||||
}
|
||||
|
||||
public static void addToLongArray(ItemStack stack, String tag, long n) {
|
||||
long[] arr = NBTHelper.getLongArray(stack, tag);
|
||||
if (arr == null) {
|
||||
arr = new long[0];
|
||||
}
|
||||
long[] newArr = Arrays.copyOf(arr, arr.length + 1);
|
||||
newArr[newArr.length - 1] = n;
|
||||
NBTHelper.putLongArray(stack, tag, newArr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int withdrawMedia(ItemStack stack, int cost, boolean simulate) {
|
||||
public long withdrawMedia(ItemStack stack, long cost, boolean simulate) {
|
||||
// In case it's withdrawn through other means
|
||||
if (!simulate && isDebug(stack, DISPLAY_MEDIA)) {
|
||||
addToIntArray(stack, TAG_EXTRACTIONS, cost);
|
||||
addToLongArray(stack, TAG_EXTRACTIONS, cost);
|
||||
}
|
||||
|
||||
return cost < 0 ? getMedia(stack) : cost;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertMedia(ItemStack stack, int amount, boolean simulate) {
|
||||
public long insertMedia(ItemStack stack, long amount, boolean simulate) {
|
||||
// In case it's inserted through other means
|
||||
if (!simulate && isDebug(stack, DISPLAY_MEDIA)) {
|
||||
addToIntArray(stack, TAG_INSERTIONS, amount);
|
||||
addToLongArray(stack, TAG_INSERTIONS, amount);
|
||||
}
|
||||
|
||||
return amount < 0 ? getMaxMedia(stack) : amount;
|
||||
|
@ -180,21 +193,21 @@ public class ItemCreativeUnlocker extends Item implements MediaHolderItem {
|
|||
}
|
||||
|
||||
private void debugDisplay(ItemStack stack, String tag, String langKey, String allKey, Entity entity) {
|
||||
int[] arr = NBTHelper.getIntArray(stack, tag);
|
||||
long[] arr = NBTHelper.getLongArray(stack, tag);
|
||||
if (arr != null) {
|
||||
NBTHelper.remove(stack, tag);
|
||||
for (int i : arr) {
|
||||
for (long 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 +219,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;
|
||||
|
|
|
@ -35,29 +35,35 @@ public abstract class ItemMediaHolder extends Item implements MediaHolderItem {
|
|||
super(pProperties);
|
||||
}
|
||||
|
||||
public static ItemStack withMedia(ItemStack stack, int media, int maxMedia) {
|
||||
public static ItemStack withMedia(ItemStack stack, long media, long maxMedia) {
|
||||
Item item = stack.getItem();
|
||||
if (item instanceof ItemMediaHolder) {
|
||||
NBTHelper.putInt(stack, TAG_MEDIA, media);
|
||||
NBTHelper.putInt(stack, TAG_MAX_MEDIA, maxMedia);
|
||||
NBTHelper.putLong(stack, TAG_MEDIA, media);
|
||||
NBTHelper.putLong(stack, TAG_MAX_MEDIA, maxMedia);
|
||||
}
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMedia(ItemStack stack) {
|
||||
return NBTHelper.getInt(stack, TAG_MEDIA);
|
||||
public long getMedia(ItemStack stack) {
|
||||
if (NBTHelper.hasInt(stack, TAG_MEDIA))
|
||||
return NBTHelper.getInt(stack, TAG_MEDIA);
|
||||
|
||||
return NBTHelper.getLong(stack, TAG_MEDIA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMedia(ItemStack stack) {
|
||||
return NBTHelper.getInt(stack, TAG_MAX_MEDIA);
|
||||
public long getMaxMedia(ItemStack stack) {
|
||||
if (NBTHelper.hasInt(stack, TAG_MAX_MEDIA))
|
||||
return NBTHelper.getInt(stack, TAG_MAX_MEDIA);
|
||||
|
||||
return NBTHelper.getLong(stack, TAG_MAX_MEDIA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMedia(ItemStack stack, int media) {
|
||||
NBTHelper.putInt(stack, TAG_MEDIA, Mth.clamp(media, 0, getMaxMedia(stack)));
|
||||
public void setMedia(ItemStack stack, long media) {
|
||||
NBTHelper.putLong(stack, TAG_MEDIA, Mth.clamp(media, 0, getMaxMedia(stack)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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;
|
||||
|
@ -74,7 +74,7 @@ public abstract class ItemPackagedHex extends ItemMediaHolder implements HexHold
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeHex(ItemStack stack, List<Iota> program, int media) {
|
||||
public void writeHex(ItemStack stack, List<Iota> program, long media) {
|
||||
ListTag patsTag = new ListTag();
|
||||
for (Iota pat : program) {
|
||||
patsTag.add(IotaType.serialize(pat));
|
||||
|
@ -110,7 +110,7 @@ public abstract class ItemPackagedHex extends ItemMediaHolder implements HexHold
|
|||
var sPlayer = (ServerPlayer) player;
|
||||
var ctx = new PackagedItemCastEnv(sPlayer, usedHand);
|
||||
var harness = CastingVM.empty(ctx);
|
||||
harness.queueAndExecuteIotas(instrs, sPlayer.getLevel());
|
||||
harness.queueExecuteAndWrapIotas(instrs, sPlayer.getLevel());
|
||||
|
||||
boolean broken = breakAfterDepletion() && getMedia(stack) == 0;
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package at.petrak.hexcasting.common.items.storage;
|
||||
|
||||
import at.petrak.hexcasting.api.casting.iota.Iota;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
import at.petrak.hexcasting.api.utils.NBTHelper;
|
||||
import at.petrak.hexcasting.common.lib.hex.HexIotaTypes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -40,7 +40,7 @@ public class ItemThoughtKnot extends Item implements IotaHolderItem {
|
|||
@Override
|
||||
public void writeDatum(ItemStack stack, @Nullable Iota iota) {
|
||||
if (iota != null) {
|
||||
NBTHelper.putCompound(stack, TAG_DATA, HexIotaTypes.serialize(iota));
|
||||
NBTHelper.putCompound(stack, TAG_DATA, IotaType.serialize(iota));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,13 +32,10 @@ public record MsgNewSpellPatternAck(ExecutionClientView info, int index) impleme
|
|||
var index = buf.readInt();
|
||||
|
||||
var stack = buf.readList(FriendlyByteBuf::readNbt);
|
||||
var parens = buf.readList(FriendlyByteBuf::readNbt);
|
||||
var raven = buf.readOptional(FriendlyByteBuf::readNbt).orElse(null);
|
||||
|
||||
var parenCount = buf.readVarInt();
|
||||
|
||||
return new MsgNewSpellPatternAck(
|
||||
new ExecutionClientView(isStackEmpty, resolutionType, stack, parens, raven, parenCount), index
|
||||
new ExecutionClientView(isStackEmpty, resolutionType, stack, raven), index
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -48,29 +45,23 @@ public record MsgNewSpellPatternAck(ExecutionClientView info, int index) impleme
|
|||
buf.writeEnum(this.info.getResolutionType());
|
||||
buf.writeInt(this.index);
|
||||
|
||||
buf.writeCollection(this.info.getStack(), FriendlyByteBuf::writeNbt);
|
||||
buf.writeCollection(this.info.getParenthesized(), FriendlyByteBuf::writeNbt);
|
||||
buf.writeCollection(this.info.getStackDescs(), FriendlyByteBuf::writeNbt);
|
||||
buf.writeOptional(Optional.ofNullable(this.info.getRavenmind()), FriendlyByteBuf::writeNbt);
|
||||
|
||||
buf.writeVarInt(this.info.getParenCount());
|
||||
}
|
||||
|
||||
public static void handle(MsgNewSpellPatternAck self) {
|
||||
Minecraft.getInstance().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
var mc = Minecraft.getInstance();
|
||||
if (self.info().isStackClear()) {
|
||||
// don't pay attention to the screen, so it also stops when we die
|
||||
mc.getSoundManager().stop(HexSounds.CASTING_AMBIANCE.getLocation(), null);
|
||||
}
|
||||
var screen = Minecraft.getInstance().screen;
|
||||
if (screen instanceof GuiSpellcasting spellGui) {
|
||||
if (self.info().isStackClear() && self.info.getRavenmind() == null) {
|
||||
mc.setScreen(null);
|
||||
} else {
|
||||
spellGui.recvServerUpdate(self.info(), self.index());
|
||||
}
|
||||
Minecraft.getInstance().execute(() -> {
|
||||
var mc = Minecraft.getInstance();
|
||||
if (self.info().isStackClear()) {
|
||||
// don't pay attention to the screen, so it also stops when we die
|
||||
mc.getSoundManager().stop(HexSounds.CASTING_AMBIANCE.getLocation(), null);
|
||||
}
|
||||
var screen = Minecraft.getInstance().screen;
|
||||
if (screen instanceof GuiSpellcasting spellGui) {
|
||||
if (self.info().isStackClear() && self.info.getRavenmind() == null) {
|
||||
mc.setScreen(null);
|
||||
} else {
|
||||
spellGui.recvServerUpdate(self.info(), self.index());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -18,7 +18,6 @@ import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
|||
*/
|
||||
public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> patterns,
|
||||
List<CompoundTag> stack,
|
||||
List<CompoundTag> parenthesized,
|
||||
CompoundTag ravenmind,
|
||||
int parenCount
|
||||
)
|
||||
|
@ -38,12 +37,11 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
|
|||
var patterns = buf.readList(fbb -> ResolvedPattern.fromNBT(fbb.readAnySizeNbt()));
|
||||
|
||||
var stack = buf.readList(FriendlyByteBuf::readNbt);
|
||||
var parens = buf.readList(FriendlyByteBuf::readNbt);
|
||||
var raven = buf.readAnySizeNbt();
|
||||
|
||||
var parenCount = buf.readVarInt();
|
||||
|
||||
return new MsgOpenSpellGuiAck(hand, patterns, stack, parens, raven, parenCount);
|
||||
return new MsgOpenSpellGuiAck(hand, patterns, stack, raven, parenCount);
|
||||
}
|
||||
|
||||
public void serialize(FriendlyByteBuf buf) {
|
||||
|
@ -52,21 +50,17 @@ public record MsgOpenSpellGuiAck(InteractionHand hand, List<ResolvedPattern> pat
|
|||
buf.writeCollection(this.patterns, (fbb, pat) -> fbb.writeNbt(pat.serializeToNBT()));
|
||||
|
||||
buf.writeCollection(this.stack, FriendlyByteBuf::writeNbt);
|
||||
buf.writeCollection(this.parenthesized, FriendlyByteBuf::writeNbt);
|
||||
buf.writeNbt(this.ravenmind);
|
||||
|
||||
buf.writeVarInt(this.parenCount);
|
||||
}
|
||||
|
||||
public static void handle(MsgOpenSpellGuiAck msg) {
|
||||
Minecraft.getInstance().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
var mc = Minecraft.getInstance();
|
||||
mc.setScreen(
|
||||
new GuiSpellcasting(msg.hand(), msg.patterns(), msg.stack, msg.parenthesized, msg.ravenmind,
|
||||
msg.parenCount));
|
||||
}
|
||||
Minecraft.getInstance().execute(() -> {
|
||||
var mc = Minecraft.getInstance();
|
||||
mc.setScreen(
|
||||
new GuiSpellcasting(msg.hand(), msg.patterns(), msg.stack, msg.ravenmind,
|
||||
msg.parenCount));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,13 +3,13 @@ package at.petrak.hexcasting.common.recipe.ingredient.brainsweep;
|
|||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
|
@ -37,7 +37,7 @@ public abstract class BrainsweepeeIngredient {
|
|||
* Can return null in case someone did something stupid with a recipe
|
||||
*/
|
||||
@Nullable
|
||||
public abstract Entity exampleEntity(ClientLevel level);
|
||||
public abstract Entity exampleEntity(Level level);
|
||||
|
||||
public abstract Type ingrType();
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@ package at.petrak.hexcasting.common.recipe.ingredient.brainsweep;
|
|||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.resources.language.I18n;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
@ -13,6 +12,7 @@ import net.minecraft.tags.TagKey;
|
|||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -43,7 +43,7 @@ public class EntityTagIngredient extends BrainsweepeeIngredient {
|
|||
boolean moddersDidAGoodJob = I18n.exists(key);
|
||||
return moddersDidAGoodJob
|
||||
? Component.translatable(key)
|
||||
: Component.literal("#" + this.entityTypeTag.location().toString());
|
||||
: Component.literal("#" + this.entityTypeTag.location());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -55,10 +55,10 @@ public class EntityTagIngredient extends BrainsweepeeIngredient {
|
|||
var out = new ArrayList<Component>();
|
||||
out.add(moddersDidAGoodJob
|
||||
? Component.translatable(key)
|
||||
: Component.literal("#" + loc.toString()));
|
||||
: Component.literal("#" + loc));
|
||||
if (advanced && moddersDidAGoodJob) {
|
||||
// Print it anyways
|
||||
out.add(Component.literal("#" + loc.toString()).withStyle(ChatFormatting.DARK_GRAY));
|
||||
out.add(Component.literal("#" + loc).withStyle(ChatFormatting.DARK_GRAY));
|
||||
}
|
||||
|
||||
out.add(BrainsweepeeIngredient.getModNameComponent(loc.getNamespace()));
|
||||
|
@ -67,7 +67,7 @@ public class EntityTagIngredient extends BrainsweepeeIngredient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Entity exampleEntity(ClientLevel level) {
|
||||
public Entity exampleEntity(Level level) {
|
||||
var someEntityTys = Registry.ENTITY_TYPE.getTagOrEmpty(this.entityTypeTag).iterator();
|
||||
if (someEntityTys.hasNext()) {
|
||||
var someTy = someEntityTys.next();
|
||||
|
@ -96,6 +96,9 @@ public class EntityTagIngredient extends BrainsweepeeIngredient {
|
|||
|
||||
public static EntityTagIngredient deserialize(JsonObject obj) {
|
||||
var tagLoc = ResourceLocation.tryParse(GsonHelper.getAsString(obj, "tag"));
|
||||
if (tagLoc == null) {
|
||||
throw new IllegalArgumentException("unknown tag " + obj);
|
||||
}
|
||||
var type = TagKey.create(Registry.ENTITY_TYPE_REGISTRY, tagLoc);
|
||||
return new EntityTagIngredient(type);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package at.petrak.hexcasting.common.recipe.ingredient.brainsweep;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
@ -10,6 +9,7 @@ import net.minecraft.server.level.ServerLevel;
|
|||
import net.minecraft.util.GsonHelper;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.Level;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
@ -41,7 +41,7 @@ public class EntityTypeIngredient extends BrainsweepeeIngredient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Entity exampleEntity(ClientLevel level) {
|
||||
public Entity exampleEntity(Level level) {
|
||||
return this.entityType.create(level);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ public class EntityTypeIngredient extends BrainsweepeeIngredient {
|
|||
|
||||
public static EntityTypeIngredient deserialize(JsonObject obj) {
|
||||
var typeLoc = ResourceLocation.tryParse(GsonHelper.getAsString(obj, "entityType"));
|
||||
if (!Registry.ENTITY_TYPE.containsKey(typeLoc)) {
|
||||
if (typeLoc == null || !Registry.ENTITY_TYPE.containsKey(typeLoc)) {
|
||||
throw new IllegalArgumentException("unknown entity type " + typeLoc);
|
||||
}
|
||||
return new EntityTypeIngredient(Registry.ENTITY_TYPE.get(typeLoc));
|
||||
|
|
|
@ -2,7 +2,6 @@ package at.petrak.hexcasting.common.recipe.ingredient.brainsweep;
|
|||
|
||||
import com.google.gson.JsonObject;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
|
@ -16,6 +15,7 @@ import net.minecraft.world.entity.EntityType;
|
|||
import net.minecraft.world.entity.npc.Villager;
|
||||
import net.minecraft.world.entity.npc.VillagerProfession;
|
||||
import net.minecraft.world.entity.npc.VillagerType;
|
||||
import net.minecraft.world.level.Level;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -52,7 +52,7 @@ public class VillagerIngredient extends BrainsweepeeIngredient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Entity exampleEntity(ClientLevel level) {
|
||||
public Entity exampleEntity(Level level) {
|
||||
var biome = Objects.requireNonNullElse(this.biome, VillagerType.PLAINS);
|
||||
var profession = Objects.requireNonNullElse(this.profession, VillagerProfession.TOOLSMITH);
|
||||
var tradeLevel = Math.min(this.minLevel, 1);
|
||||
|
|
|
@ -77,7 +77,7 @@ public interface IXplatAbstractions {
|
|||
|
||||
void setColorizer(Player target, FrozenColorizer colorizer);
|
||||
|
||||
void setSentinel(Player target, Sentinel sentinel);
|
||||
void setSentinel(Player target, @Nullable Sentinel sentinel);
|
||||
|
||||
void setFlight(ServerPlayer target, @Nullable FlightAbility flight);
|
||||
|
||||
|
@ -95,7 +95,7 @@ public interface IXplatAbstractions {
|
|||
|
||||
FrozenColorizer getColorizer(Player player);
|
||||
|
||||
Sentinel getSentinel(Player player);
|
||||
@Nullable Sentinel getSentinel(Player player);
|
||||
|
||||
CastingVM getStaffcastVM(ServerPlayer player, InteractionHand hand);
|
||||
|
||||
|
|
|
@ -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/$",
|
||||
|
@ -989,7 +992,7 @@
|
|||
"hexcasting.page.stackmanip.2dup": "Copy the top two iotas of the stack. [0, 1] becomes [0, 1, 0, 1].",
|
||||
"hexcasting.page.stackmanip.stack_len": "Pushes the size of the stack as a number to the top of the stack. (For example, a stack of [0, 1] will become [0, 1, 2].)",
|
||||
"hexcasting.page.stackmanip.duplicate_n": "Removes the number at the top of the stack, then copies the top iota of the stack that number of times. (A count of 2 results in two of the iota on the stack, not three.)",
|
||||
"hexcasting.page.stackmanip.fisherman": "Grabs the element in the stack indexed by the number and brings it to the top.",
|
||||
"hexcasting.page.stackmanip.fisherman": "Grabs the element in the stack indexed by the number and brings it to the top. If the number is negative, instead moves the top element of the stack down that many elements.",
|
||||
"hexcasting.page.stackmanip.fisherman/copy": "Like $(action)Fisherman's Gambit/$, but instead of moving the iota, copies it.",
|
||||
"hexcasting.page.stackmanip.mask.1": "An infinite family of actions that keep or remove elements at the top of the stack based on the sequence of dips and lines.",
|
||||
"hexcasting.page.stackmanip.mask.2": "Assuming that I draw a Bookkeeper's Gambit pattern left-to-right, the number of iotas the action will require is determined by the horizontal distance covered by the pattern. From deepest in the stack to shallowest, a flat line will keep the iota, whereas a triangle dipping down will remove it.$(br2)If my stack contains $(italic)0, 1, 2/$ from deepest to shallowest, drawing the first pattern opposite will give me $(italic)1/$, the second will give me $(italic)0/$, and the third will give me $(italic)0, 2/$ (the 0 at the bottom is left untouched).",
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
"op_id": "hexcasting:get_caster",
|
||||
"anchor": "hexcasting:get_caster",
|
||||
"input": "",
|
||||
"output": "entity",
|
||||
"output": "entity | null",
|
||||
"text": "hexcasting.page.basics_pattern.get_caster"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -10,6 +10,8 @@ import net.minecraft.resources.ResourceKey;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class CCSentinel implements Component, AutoSyncedComponent {
|
||||
public static final String
|
||||
TAG_HAS_SENTINEL = "has_sentinel",
|
||||
|
@ -18,13 +20,13 @@ public class CCSentinel implements Component, AutoSyncedComponent {
|
|||
TAG_DIMENSION = "dimension";
|
||||
|
||||
private final Player owner;
|
||||
private Sentinel sentinel = Sentinel.none();
|
||||
private @Nullable Sentinel sentinel = null;
|
||||
|
||||
public CCSentinel(Player owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public Sentinel getSentinel() {
|
||||
public @Nullable Sentinel getSentinel() {
|
||||
return sentinel;
|
||||
}
|
||||
|
||||
|
@ -41,16 +43,16 @@ public class CCSentinel implements Component, AutoSyncedComponent {
|
|||
var position = HexUtils.vecFromNBT(tag.getLongArray(TAG_POSITION));
|
||||
var dim = ResourceKey.create(Registry.DIMENSION_REGISTRY,
|
||||
new ResourceLocation(tag.getString(TAG_DIMENSION)));
|
||||
this.sentinel = new Sentinel(true, extendsRange, position, dim);
|
||||
this.sentinel = new Sentinel(extendsRange, position, dim);
|
||||
} else {
|
||||
this.sentinel = Sentinel.none();
|
||||
this.sentinel = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToNbt(CompoundTag tag) {
|
||||
tag.putBoolean(TAG_HAS_SENTINEL, this.sentinel.hasSentinel());
|
||||
if (this.sentinel.hasSentinel()) {
|
||||
tag.putBoolean(TAG_HAS_SENTINEL, this.sentinel != null);
|
||||
if (this.sentinel != null) {
|
||||
tag.putBoolean(TAG_EXTENDS_RANGE, this.sentinel.extendsRange());
|
||||
tag.put(TAG_POSITION, HexUtils.serializeToNBT(this.sentinel.position()));
|
||||
tag.putString(TAG_DIMENSION, this.sentinel.dimension().location().toString());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -45,7 +45,7 @@ public abstract class CCHexHolder extends ItemComponent implements ADHexHolder {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeHex(List<Iota> patterns, int media) {
|
||||
public void writeHex(List<Iota> patterns, long media) {
|
||||
this.hexHolder.writeHex(this.stack, patterns, media);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,17 +25,17 @@ public abstract class CCMediaHolder extends ItemComponent implements ADMediaHold
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getMedia() {
|
||||
public long getMedia() {
|
||||
return this.mediaHolder.getMedia(this.stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMedia() {
|
||||
public long getMaxMedia() {
|
||||
return this.mediaHolder.getMaxMedia(this.stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMedia(int media) {
|
||||
public void setMedia(long media) {
|
||||
this.mediaHolder.setMedia(this.stack, media);
|
||||
}
|
||||
|
||||
|
@ -60,12 +60,12 @@ public abstract class CCMediaHolder extends ItemComponent implements ADMediaHold
|
|||
}
|
||||
|
||||
@Override
|
||||
public int withdrawMedia(int cost, boolean simulate) {
|
||||
public long withdrawMedia(long cost, boolean simulate) {
|
||||
return this.mediaHolder.withdrawMedia(this.stack, cost, simulate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertMedia(int amount, boolean simulate) {
|
||||
public long insertMedia(long amount, boolean simulate) {
|
||||
return this.mediaHolder.insertMedia(this.stack, amount, simulate);
|
||||
}
|
||||
}
|
||||
|
@ -81,17 +81,17 @@ public abstract class CCMediaHolder extends ItemComponent implements ADMediaHold
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getMedia() {
|
||||
return baseWorth.get() * stack.getCount();
|
||||
public long getMedia() {
|
||||
return (long) baseWorth.get() * stack.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMedia() {
|
||||
public long getMaxMedia() {
|
||||
return getMedia();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMedia(int media) {
|
||||
public void setMedia(long media) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
|
@ -116,8 +116,8 @@ public abstract class CCMediaHolder extends ItemComponent implements ADMediaHold
|
|||
}
|
||||
|
||||
@Override
|
||||
public int withdrawMedia(int cost, boolean simulate) {
|
||||
int worth = baseWorth.get();
|
||||
public long withdrawMedia(long cost, boolean simulate) {
|
||||
long worth = baseWorth.get();
|
||||
if (cost < 0) {
|
||||
cost = worth * stack.getCount();
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public class TrinketsApiInterop {
|
|||
});
|
||||
|
||||
DiscoveryHandlers.addMediaHolderDiscoverer(harness -> {
|
||||
Optional<TrinketComponent> optional = TrinketsApi.getTrinketComponent(harness.getCtx().getCaster());
|
||||
Optional<TrinketComponent> optional = TrinketsApi.getTrinketComponent(harness.getEnv().getCaster());
|
||||
if (optional.isPresent()) {
|
||||
TrinketComponent component = optional.get();
|
||||
return component.getEquipped(MediaHelper::isMediaItem).stream()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -155,7 +155,7 @@ public class FabricXplatImpl implements IXplatAbstractions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setSentinel(Player target, Sentinel sentinel) {
|
||||
public void setSentinel(Player target, @Nullable Sentinel sentinel) {
|
||||
var cc = HexCardinalComponents.SENTINEL.get(target);
|
||||
cc.setSentinel(sentinel);
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ public class CapSyncers {
|
|||
x.setAltiora(player, x.getAltiora(proto));
|
||||
x.setSentinel(player, x.getSentinel(proto));
|
||||
x.setColorizer(player, x.getColorizer(proto));
|
||||
x.setStaffcastImage(player, x.getStaffcastVM(proto, InteractionHand.MAIN_HAND));
|
||||
x.setStaffcastImage(player, x.getStaffcastVM(proto, InteractionHand.MAIN_HAND).getImage());
|
||||
x.setPatterns(player, x.getPatternsSavedInUi(proto));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -28,7 +28,7 @@ public record CapItemHexHolder(HexHolderItem holder,
|
|||
}
|
||||
|
||||
@Override
|
||||
public void writeHex(List<Iota> patterns, int media) {
|
||||
public void writeHex(List<Iota> patterns, long media) {
|
||||
holder.writeHex(stack, patterns, media);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,17 +11,17 @@ public record CapItemMediaHolder(MediaHolderItem holder,
|
|||
ItemStack stack) implements ADMediaHolder {
|
||||
|
||||
@Override
|
||||
public int getMedia() {
|
||||
public long getMedia() {
|
||||
return holder.getMedia(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMedia() {
|
||||
public long getMaxMedia() {
|
||||
return holder.getMaxMedia(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMedia(int media) {
|
||||
public void setMedia(long media) {
|
||||
holder.setMedia(stack, media);
|
||||
}
|
||||
|
||||
|
@ -46,12 +46,12 @@ public record CapItemMediaHolder(MediaHolderItem holder,
|
|||
}
|
||||
|
||||
@Override
|
||||
public int withdrawMedia(int cost, boolean simulate) {
|
||||
public long withdrawMedia(long cost, boolean simulate) {
|
||||
return holder.withdrawMedia(stack, cost, simulate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int insertMedia(int amount, boolean simulate) {
|
||||
public long insertMedia(long amount, boolean simulate) {
|
||||
return holder.insertMedia(stack, amount, simulate);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,17 +12,17 @@ public record CapStaticMediaHolder(Supplier<Integer> baseWorth,
|
|||
int consumptionPriority,
|
||||
ItemStack stack) implements ADMediaHolder {
|
||||
@Override
|
||||
public int getMedia() {
|
||||
return baseWorth.get() * stack.getCount();
|
||||
public long getMedia() {
|
||||
return (long) baseWorth.get() * stack.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxMedia() {
|
||||
public long getMaxMedia() {
|
||||
return getMedia();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMedia(int media) {
|
||||
public void setMedia(long media) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
|
@ -47,8 +47,8 @@ public record CapStaticMediaHolder(Supplier<Integer> baseWorth,
|
|||
}
|
||||
|
||||
@Override
|
||||
public int withdrawMedia(int cost, boolean simulate) {
|
||||
int worth = baseWorth.get();
|
||||
public long withdrawMedia(long cost, boolean simulate) {
|
||||
long worth = baseWorth.get();
|
||||
if (cost < 0) {
|
||||
cost = worth * stack.getCount();
|
||||
}
|
||||
|
|
|
@ -44,7 +44,12 @@ public class CuriosApiInterop {
|
|||
|
||||
DiscoveryHandlers.addMediaHolderDiscoverer(harness -> {
|
||||
List<ADMediaHolder> holders = Lists.newArrayList();
|
||||
harness.getCtx().getCaster().getCapability(CuriosCapability.INVENTORY).ifPresent(handler -> {
|
||||
var caster = harness.getEnv().getCaster();
|
||||
|
||||
if (caster == null)
|
||||
return holders;
|
||||
|
||||
caster.getCapability(CuriosCapability.INVENTORY).ifPresent(handler -> {
|
||||
for (var stacksHandler : handler.getCurios().values()) {
|
||||
var stacks = stacksHandler.getStacks();
|
||||
for (int i = 0; i < stacks.getSlots(); i++) {
|
||||
|
|
|
@ -11,12 +11,14 @@ import net.minecraft.resources.ResourceKey;
|
|||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
||||
|
||||
/**
|
||||
* Sent server->client to synchronize the status of the sentinel.
|
||||
*/
|
||||
public record MsgSentinelStatusUpdateAck(Sentinel update) implements IMessage {
|
||||
public record MsgSentinelStatusUpdateAck(@Nullable Sentinel update) implements IMessage {
|
||||
public static final ResourceLocation ID = modLoc("sntnl");
|
||||
|
||||
@Override
|
||||
|
@ -28,16 +30,25 @@ public record MsgSentinelStatusUpdateAck(Sentinel update) implements IMessage {
|
|||
var buf = new FriendlyByteBuf(buffer);
|
||||
|
||||
var exists = buf.readBoolean();
|
||||
if (!exists) {
|
||||
return new MsgSentinelStatusUpdateAck(null);
|
||||
}
|
||||
|
||||
var greater = buf.readBoolean();
|
||||
var origin = new Vec3(buf.readDouble(), buf.readDouble(), buf.readDouble());
|
||||
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY, buf.readResourceLocation());
|
||||
|
||||
var sentinel = new Sentinel(exists, greater, origin, dimension);
|
||||
var sentinel = new Sentinel(greater, origin, dimension);
|
||||
return new MsgSentinelStatusUpdateAck(sentinel);
|
||||
}
|
||||
|
||||
public void serialize(FriendlyByteBuf buf) {
|
||||
buf.writeBoolean(update.hasSentinel());
|
||||
if (update == null) {
|
||||
buf.writeBoolean(false);
|
||||
return;
|
||||
}
|
||||
|
||||
buf.writeBoolean(true);
|
||||
buf.writeBoolean(update.extendsRange());
|
||||
buf.writeDouble(update.position().x);
|
||||
buf.writeDouble(update.position().y);
|
||||
|
@ -46,13 +57,10 @@ public record MsgSentinelStatusUpdateAck(Sentinel update) implements IMessage {
|
|||
}
|
||||
|
||||
public static void handle(MsgSentinelStatusUpdateAck self) {
|
||||
Minecraft.getInstance().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
var player = Minecraft.getInstance().player;
|
||||
if (player != null) {
|
||||
IXplatAbstractions.INSTANCE.setSentinel(player, self.update());
|
||||
}
|
||||
Minecraft.getInstance().execute(() -> {
|
||||
var player = Minecraft.getInstance().player;
|
||||
if (player != null) {
|
||||
IXplatAbstractions.INSTANCE.setSentinel(player, self.update());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -7,9 +7,10 @@ import at.petrak.hexcasting.api.addldata.ADIotaHolder;
|
|||
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
||||
import at.petrak.hexcasting.api.casting.ActionRegistryEntry;
|
||||
import at.petrak.hexcasting.api.casting.castables.SpecialHandler;
|
||||
import at.petrak.hexcasting.api.casting.eval.CastingEnvironment;
|
||||
import at.petrak.hexcasting.api.casting.eval.ResolvedPattern;
|
||||
import at.petrak.hexcasting.api.casting.eval.env.StaffCastEnv;
|
||||
import at.petrak.hexcasting.api.casting.eval.sideeffects.EvalSound;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
|
||||
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
|
||||
import at.petrak.hexcasting.api.casting.iota.IotaType;
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
|
@ -195,10 +196,10 @@ public class ForgeXplatImpl implements IXplatAbstractions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setSentinel(Player player, Sentinel sentinel) {
|
||||
public void setSentinel(Player player, @Nullable Sentinel sentinel) {
|
||||
CompoundTag tag = player.getPersistentData();
|
||||
tag.putBoolean(TAG_SENTINEL_EXISTS, sentinel.hasSentinel());
|
||||
if (sentinel.hasSentinel()) {
|
||||
tag.putBoolean(TAG_SENTINEL_EXISTS, sentinel == null);
|
||||
if (sentinel != null) {
|
||||
tag.putBoolean(TAG_SENTINEL_GREATER, sentinel.extendsRange());
|
||||
tag.put(TAG_SENTINEL_POSITION, HexUtils.serializeToNBT(sentinel.position()));
|
||||
tag.putString(TAG_SENTINEL_DIMENSION, sentinel.dimension().location().toString());
|
||||
|
@ -214,8 +215,8 @@ public class ForgeXplatImpl implements IXplatAbstractions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setStaffcastImage(ServerPlayer player, CastingVM harness) {
|
||||
player.getPersistentData().put(TAG_HARNESS, harness == null ? new CompoundTag() : harness.serializeToNBT());
|
||||
public void setStaffcastImage(ServerPlayer player, @Nullable CastingImage image) {
|
||||
player.getPersistentData().put(TAG_HARNESS, image == null ? new CompoundTag() : image.serializeToNbt());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -275,14 +276,14 @@ public class ForgeXplatImpl implements IXplatAbstractions {
|
|||
var dimension = ResourceKey.create(Registry.DIMENSION_REGISTRY,
|
||||
new ResourceLocation(tag.getString(TAG_SENTINEL_DIMENSION)));
|
||||
|
||||
return new Sentinel(true, extendsRange, position, dimension);
|
||||
return new Sentinel(extendsRange, position, dimension);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CastingVM getStaffcastVM(ServerPlayer player, InteractionHand hand) {
|
||||
// This is always from a staff because we don't need to load the harness when casting from item
|
||||
var ctx = new CastingEnvironment(player, hand, CastingEnvironment.CastSource.STAFF);
|
||||
return CastingVM.fromNBT(player.getPersistentData().getCompound(TAG_HARNESS), ctx);
|
||||
var ctx = new StaffCastEnv(player, hand);
|
||||
return new CastingVM(CastingImage.loadFromNbt(player.getPersistentData().getCompound(TAG_HARNESS), player.getLevel()), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -403,7 +404,7 @@ public class ForgeXplatImpl implements IXplatAbstractions {
|
|||
return ForgeUnsealedIngredient.of(stack);
|
||||
}
|
||||
|
||||
private static Supplier<CreativeModeTab> TAB = Suppliers.memoize(() ->
|
||||
private final static Supplier<CreativeModeTab> TAB = Suppliers.memoize(() ->
|
||||
new CreativeModeTab(HexAPI.MOD_ID) {
|
||||
@Override
|
||||
public ItemStack makeIcon() {
|
||||
|
@ -411,8 +412,8 @@ public class ForgeXplatImpl implements IXplatAbstractions {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void fillItemList(NonNullList<ItemStack> p_40778_) {
|
||||
super.fillItemList(p_40778_);
|
||||
public void fillItemList(NonNullList<ItemStack> items) {
|
||||
super.fillItemList(items);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in a new issue