Compare commits
6 commits
main
...
spell-circ
Author | SHA1 | Date | |
---|---|---|---|
f4cfbb0e48 | |||
a03f80de2e | |||
46cc0b55cc | |||
600e622d7b | |||
129fbcbb53 | |||
d4df3e613c |
|
@ -0,0 +1,47 @@
|
|||
package at.petrak.hexcasting.api.addldata;
|
||||
|
||||
import at.petrak.hexcasting.api.circles.BlockEdge;
|
||||
import at.petrak.hexcasting.api.circles.FlowUpdate;
|
||||
import at.petrak.hexcasting.api.circles.ICircleState;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Additional data attached to block entities that make them work as parts in a Spell Circle.
|
||||
* <p>
|
||||
* Yes, sadly they do actually have to be block entities. Thank Forge.
|
||||
* <p>
|
||||
* I would love to call this something less silly than "widget" but the word "component" is already taken by
|
||||
* Fabric.
|
||||
* <p>
|
||||
* See also {@link at.petrak.hexcasting.api.circles api.circles}.
|
||||
*/
|
||||
public interface ADCircleWidget {
|
||||
/**
|
||||
* Whether this block accepts flow from the given edge.
|
||||
* <p>
|
||||
* This should be immutably based on the block's state. A single slate would output {@code true} for
|
||||
* all the edges it is connected to, for example.
|
||||
*/
|
||||
boolean acceptsFlow(BlockEdge edge);
|
||||
|
||||
/**
|
||||
* Directions this block <i>can</i> disperse flow to.
|
||||
* <p>
|
||||
* This should be immutably based on the block's state. A directrix would output both of the edges it can
|
||||
* exit from, for example.
|
||||
*/
|
||||
EnumSet<BlockEdge> possibleExitDirs(BlockEdge inputEdge);
|
||||
|
||||
/**
|
||||
* What this block should do upon receiving the flow.
|
||||
* <p>
|
||||
* Returning {@code null} signals there was a problem somehow, like if it got an input from an edge it didn't
|
||||
* expect. In that case the circle will throw a {@link at.petrak.hexcasting.api.spell.mishaps.MishapMessedUpSpellCircle
|
||||
* MishapMessedUpSpellCircle}
|
||||
*/
|
||||
@Nullable
|
||||
FlowUpdate onReceiveFlow(BlockEdge inputEdge, BlockEntity sender, ICircleState state);
|
||||
}
|
|
@ -2,9 +2,11 @@
|
|||
* An "Additional Data," or AD, is what I am calling the abstraction over capabilities on Forge and
|
||||
* cardinal components on Fabric.
|
||||
* <p>
|
||||
* An {@code ADFooBar} in this package will be implemented by a {@code CCFooBar} on Fabric.
|
||||
* On Forge, there are a set of private records that implement them.
|
||||
* For each AD in here, the mod provides an interface or abstract class that mirrors it.
|
||||
* Implementing the given interface on whatever type that AD attaches to (items, blocks) will automatically attach an
|
||||
* appropriate instance of an AD to it as a capability/CC by scanning the registry.
|
||||
* For example, {@link at.petrak.hexcasting.api.item.ColorizerItem ColorizerItem}.
|
||||
* <p>
|
||||
* The point is, this provides an interface for interacting with however whatever platform sticks extra info on stuff.
|
||||
* I do not know why we don't just implement the AD interface directly. Ask Wire.
|
||||
*/
|
||||
package at.petrak.hexcasting.api.addldata;
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern;
|
||||
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;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
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.Random;
|
||||
|
||||
// Facing dir is the direction it starts searching for slates in to start
|
||||
public abstract class BlockAbstractImpetus extends BlockCircleComponent implements EntityBlock {
|
||||
public static final DirectionProperty FACING = BlockStateProperties.FACING;
|
||||
|
||||
public BlockAbstractImpetus(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
this.registerDefaultState(
|
||||
this.stateDefinition.any().setValue(ENERGIZED, false).setValue(FACING, Direction.NORTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level world) {
|
||||
return enterDir != bs.getValue(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> exitDirections(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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float particleHeight(BlockPos pos, BlockState bs, Level world) {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(BlockState pState, ServerLevel pLevel, BlockPos pPos, Random pRandom) {
|
||||
if (pLevel.getBlockEntity(pPos) instanceof BlockEntityAbstractImpetus tile && pState.getValue(ENERGIZED)) {
|
||||
tile.stepCircle();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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();
|
||||
}
|
||||
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
|
||||
return this.defaultBlockState().setValue(FACING, pContext.getNearestLookingDirection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState rotate(BlockState pState, Rotation pRot) {
|
||||
return pState.setValue(FACING, pRot.rotate(pState.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState mirror(BlockState pState, Mirror pMirror) {
|
||||
return pState.rotate(pMirror.getRotation(pState.getValue(FACING)));
|
||||
}
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
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 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);
|
||||
|
||||
abstract public EnumSet<Direction> exitDirections(BlockPos pos, BlockState bs, Level world);
|
||||
|
||||
@Nullable
|
||||
abstract public HexPattern getPattern(BlockPos pos, BlockState bs, Level world);
|
||||
|
||||
/**
|
||||
* Which direction points "up" or "out" for this block?
|
||||
* This is used for {@link BlockCircleComponent#canEnterFromDirection(Direction, Direction, BlockPos, BlockState, Level)}
|
||||
* as well as particles.
|
||||
*/
|
||||
public Direction normalDir(BlockPos pos, BlockState bs, Level world) {
|
||||
return normalDir(pos, bs, world, 16);
|
||||
}
|
||||
|
||||
abstract public Direction normalDir(BlockPos pos, BlockState bs, Level world, int recursionLeft);
|
||||
|
||||
public static Direction normalDirOfOther(BlockPos other, Level world, int recursionLeft) {
|
||||
if (recursionLeft <= 0) {
|
||||
return Direction.UP;
|
||||
}
|
||||
|
||||
var stateThere = world.getBlockState(other);
|
||||
if (stateThere.getBlock() instanceof BlockCircleComponent bcc) {
|
||||
return bcc.normalDir(other, stateThere, world, recursionLeft - 1);
|
||||
} else {
|
||||
return Direction.UP;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How many blocks in the {@link BlockCircleComponent#normalDir(BlockPos, BlockState, Level)} from the center
|
||||
* particles should be spawned in
|
||||
*/
|
||||
abstract public float particleHeight(BlockPos pos, BlockState bs, Level world);
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> pBuilder) {
|
||||
pBuilder.add(ENERGIZED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAnalogOutputSignal(BlockState pState) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAnalogOutputSignal(BlockState pState, Level pLevel, BlockPos pPos) {
|
||||
return pState.getValue(ENERGIZED) ? 15 : 0;
|
||||
}
|
||||
}
|
|
@ -1,559 +0,0 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer;
|
||||
import at.petrak.hexcasting.api.misc.ManaConstants;
|
||||
import at.petrak.hexcasting.api.mod.HexConfig;
|
||||
import at.petrak.hexcasting.api.spell.ParticleSpray;
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext;
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingHarness;
|
||||
import at.petrak.hexcasting.api.spell.casting.SpellCircleContext;
|
||||
import at.petrak.hexcasting.api.spell.iota.PatternIota;
|
||||
import at.petrak.hexcasting.api.utils.ManaHelper;
|
||||
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.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
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.network.chat.TranslatableComponent;
|
||||
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.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.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_MANA = "mana",
|
||||
TAG_LAST_MISHAP = "last_mishap";
|
||||
|
||||
@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 mana = 0;
|
||||
|
||||
public BlockEntityAbstractImpetus(BlockEntityType<?> pType, BlockPos pWorldPosition, BlockState pBlockState) {
|
||||
super(pType, pWorldPosition, pBlockState);
|
||||
}
|
||||
|
||||
abstract public boolean activatorAlwaysInRange();
|
||||
|
||||
public int getMana() {
|
||||
return this.mana;
|
||||
}
|
||||
|
||||
public void setMana(int mana) {
|
||||
this.mana = mana;
|
||||
}
|
||||
|
||||
@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,
|
||||
LocalPlayer observer, ClientLevel world,
|
||||
Direction hitFace, InteractionHand lensHand) {
|
||||
if (world.getBlockEntity(pos) instanceof BlockEntityAbstractImpetus beai) {
|
||||
var dustCount = (float) beai.getMana() / (float) ManaConstants.DUST_UNIT;
|
||||
var dustCmp = new TranslatableComponent("hexcasting.tooltip.lens.impetus.mana",
|
||||
String.format("%.2f", 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_MANA, this.mana);
|
||||
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.mana = tag.getInt(TAG_MANA);
|
||||
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 CastingContext(splayer, InteractionHand.MAIN_HAND,
|
||||
new SpellCircleContext(this.getBlockPos(), bounds, this.activatorAlwaysInRange()));
|
||||
var harness = new CastingHarness(ctx);
|
||||
|
||||
var makeSound = false;
|
||||
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.executeIota(new PatternIota(newPattern), splayer.getLevel());
|
||||
if (info.getMakesCastSound()) {
|
||||
makeSound = true;
|
||||
}
|
||||
if (!info.getResolutionType().getSuccess()) {
|
||||
erroredPos = tracked;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (makeSound) {
|
||||
this.level.playSound(null, this.getBlockPos(), HexSounds.SPELL_CIRCLE_CAST, SoundSource.BLOCKS,
|
||||
2f, 1f);
|
||||
}
|
||||
|
||||
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) {
|
||||
insertMana(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPlaceItem(int index, ItemStack stack) {
|
||||
var manamount = extractMana(stack, true);
|
||||
return manamount > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
this.mana = 0;
|
||||
this.stopCasting();
|
||||
this.sync();
|
||||
}
|
||||
|
||||
public int extractMana(ItemStack stack, boolean simulate) {
|
||||
return ManaHelper.extractMana(stack, remainingManaCapacity(), true, simulate);
|
||||
}
|
||||
|
||||
public void insertMana(ItemStack stack) {
|
||||
var manamount = extractMana(stack, false);
|
||||
if (manamount > 0) {
|
||||
this.mana += manamount;
|
||||
this.sync();
|
||||
}
|
||||
}
|
||||
|
||||
public int remainingManaCapacity() {
|
||||
return MAX_CAPACITY - this.mana;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||||
import at.petrak.hexcasting.api.circles.BlockEdge;
|
||||
import at.petrak.hexcasting.api.circles.FlowUpdate;
|
||||
import at.petrak.hexcasting.api.circles.ICircleState;
|
||||
import at.petrak.hexcasting.api.circles.Margin;
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Helper class for halfslab-sized blocks that sit on the sides of blocks.
|
||||
* <p>
|
||||
* On both the Forge and Fabric sides, the registry will be scanned for all blocks which extend this,
|
||||
* and the appropriate cap/CC will be attached.
|
||||
* <p>
|
||||
* No, there isn't a more generic version for you to use. Why? In case you don't want to be locked into
|
||||
* using {@link HexBlockEntity}.
|
||||
*/
|
||||
@MethodsReturnNonnullByDefault
|
||||
@ParametersAreNonnullByDefault
|
||||
public abstract class BlockEntitySidedCircleWidget extends HexBlockEntity {
|
||||
public BlockEntitySidedCircleWidget(BlockEntityType<?> type, BlockPos worldPosition, BlockState blockState) {
|
||||
super(type, worldPosition, blockState);
|
||||
}
|
||||
|
||||
// "Overrides."
|
||||
// The BlockEdge is transformed into a Margin in the forge/fabric implers
|
||||
public abstract boolean acceptsFlow(Margin margin);
|
||||
|
||||
public abstract EnumSet<BlockEdge> possibleExitDirs(Margin inputMargin);
|
||||
|
||||
public abstract FlowUpdate onReceiveFlow(Margin inputMargin, BlockEntity sender, ICircleState state);
|
||||
|
||||
/**
|
||||
* Convert the given edge to a margin in the block's local marginspace (ie, post-transformation),
|
||||
* or {@code null} if it's not an edge this block touches.
|
||||
*/
|
||||
@Nullable
|
||||
public Margin getMargin(BlockEdge edge) {
|
||||
var bs = this.getBlockState();
|
||||
var hereNormal = bs.getValue(BlockSidedCircleWidget.FACING);
|
||||
var otherNormal = edge.getOtherNormal(hereNormal);
|
||||
if (otherNormal == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var absoluteMargin = Margin.fromNormalAndDir(hereNormal, otherNormal);
|
||||
var topMargin = bs.getValue(BlockSidedCircleWidget.TOP_MARGIN);
|
||||
return absoluteMargin.transform(topMargin);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
package at.petrak.hexcasting.api.block.circle;
|
||||
|
||||
import at.petrak.hexcasting.api.circles.Margin;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
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.BooleanProperty;
|
||||
import net.minecraft.world.level.block.state.properties.DirectionProperty;
|
||||
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Block holder of a {@link BlockEntitySidedCircleWidget}.
|
||||
*/
|
||||
abstract public class BlockSidedCircleWidget extends Block implements EntityBlock {
|
||||
public static final double THICKNESS = 8;
|
||||
public static final VoxelShape AABB_XP = Block.box(16 - THICKNESS, 0, 0, 16, 16, 16);
|
||||
public static final VoxelShape AABB_XN = Block.box(0, 0, 0, THICKNESS, 16, 16);
|
||||
public static final VoxelShape AABB_YP = Block.box(0, 0, 0, 16, THICKNESS, 16);
|
||||
public static final VoxelShape AABB_YN = Block.box(0, 16 - THICKNESS, 0, 16, 16, 16);
|
||||
public static final VoxelShape AABB_ZP = Block.box(0, 0, 16 - THICKNESS, 16, 16, 16);
|
||||
public static final VoxelShape AABB_ZN = Block.box(0, 0, 0, 16, 16, THICKNESS);
|
||||
|
||||
/**
|
||||
* If this has been activated yet by the circle.
|
||||
*/
|
||||
public static final BooleanProperty ENERGIZED = BooleanProperty.create("energized");
|
||||
/**
|
||||
* The normal vector of this block
|
||||
*/
|
||||
public static final DirectionProperty FACING = BlockStateProperties.FACING;
|
||||
/**
|
||||
* Which margin this block considers to be its "top." AKA, its local facing dir.
|
||||
*/
|
||||
public static final EnumProperty<Margin> TOP_MARGIN = EnumProperty.create("top_margin", Margin.class);
|
||||
// storing these 4 properties raises the property count to 384, but minecraft noteblocks have like 768
|
||||
// I'll be fiiiiiiiiiiiiine
|
||||
public static final BooleanProperty TOP_CONNECTS = BooleanProperty.create("top_connects");
|
||||
public static final BooleanProperty RIGHT_CONNECTS = BooleanProperty.create("right_connects");
|
||||
public static final BooleanProperty BOTTOM_CONNECTS = BooleanProperty.create("bottom_connects");
|
||||
public static final BooleanProperty LEFT_CONNECTS = BooleanProperty.create("left_connects");
|
||||
|
||||
public BlockSidedCircleWidget(Properties properties) {
|
||||
super(properties);
|
||||
var statedef = this.getStateDefinition().any()
|
||||
.setValue(ENERGIZED, false)
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(TOP_MARGIN, Margin.TOP);
|
||||
for (var margin : Margin.values()) {
|
||||
statedef = statedef.setValue(getConnectorProp(margin), false);
|
||||
}
|
||||
this.registerDefaultState(statedef);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
builder.add(ENERGIZED, FACING, TOP_MARGIN);
|
||||
for (var margin : Margin.values()) {
|
||||
builder.add(getConnectorProp(margin));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
|
||||
return switch (state.getValue(FACING)) {
|
||||
case DOWN -> AABB_YN;
|
||||
case UP -> AABB_YP;
|
||||
case NORTH -> AABB_ZN;
|
||||
case SOUTH -> AABB_ZP;
|
||||
case WEST -> AABB_XN;
|
||||
case EAST -> AABB_XP;
|
||||
};
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
|
||||
var normal = ctx.getClickedFace();
|
||||
if (ctx.isSecondaryUseActive()) {
|
||||
normal = normal.getOpposite();
|
||||
}
|
||||
|
||||
var margin = getHoveredMargin(ctx.getClickLocation(), normal);
|
||||
|
||||
return this.defaultBlockState()
|
||||
.setValue(FACING, normal)
|
||||
.setValue(TOP_MARGIN, margin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the little triangle overlay thing IE does.
|
||||
*/
|
||||
public static Margin getHoveredMargin(Vec3 clickPos, Direction normal) {
|
||||
// uv cause textures
|
||||
double u, v;
|
||||
switch (normal.getAxis()) {
|
||||
case X -> {
|
||||
u = Mth.positiveModulo(clickPos.z * -1 * normal.getAxisDirection().getStep(), 1);
|
||||
v = Mth.positiveModulo(clickPos.y, 1);
|
||||
}
|
||||
case Y -> {
|
||||
// North is "up", so +v here ...
|
||||
// and u is a quarter-turn to the right, so east.
|
||||
u = Mth.positiveModulo(clickPos.x * normal.getAxisDirection().getStep(), 1);
|
||||
v = Mth.positiveModulo(clickPos.z * -1 * normal.getAxisDirection().getStep(), 1);
|
||||
}
|
||||
case Z -> {
|
||||
u = Mth.positiveModulo(clickPos.x * normal.getAxisDirection().getStep(), 1);
|
||||
v = Mth.positiveModulo(clickPos.y, 1);
|
||||
}
|
||||
default -> throw new IllegalStateException();
|
||||
}
|
||||
u -= 0.5;
|
||||
v -= 0.5;
|
||||
|
||||
// i have done some futzing around with desmos
|
||||
// also i know this is shoddy code, but i've never found a good way to do this matrix of boolean thing,
|
||||
// especially when I cant `match` on a tuple of booleans. so i'll just comment.
|
||||
// this is true when the point is in an "hourglass", within two triangles radiating up and down...
|
||||
var flag1 = Math.abs(u) < Math.abs(v) ? 1 : 0;
|
||||
// ... and this is true in the upper-left diagonal half of the coordinate plane
|
||||
var flag2 = u < v ? 1 : 0;
|
||||
var id = (flag1 << 1) | flag2;
|
||||
return switch (id) {
|
||||
case 0b00 -> Margin.RIGHT;
|
||||
case 0b01 -> Margin.LEFT;
|
||||
case 0b10 -> Margin.BOTTOM;
|
||||
case 0b11 -> Margin.TOP;
|
||||
default -> throw new IllegalStateException();
|
||||
};
|
||||
}
|
||||
|
||||
public static BooleanProperty getConnectorProp(Margin margin) {
|
||||
return switch (margin) {
|
||||
case TOP -> TOP_CONNECTS;
|
||||
case LEFT -> LEFT_CONNECTS;
|
||||
case BOTTOM -> BOTTOM_CONNECTS;
|
||||
case RIGHT -> RIGHT_CONNECTS;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
package at.petrak.hexcasting.api.circles;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.MethodsReturnNonnullByDefault;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.Mth;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.ParametersAreNonnullByDefault;
|
||||
|
||||
/**
|
||||
* Edges are named after their <i>normal vectors</i>. For example, {@code XP_YP} is the top-east
|
||||
* corner of the block (positive x, positive y).
|
||||
*/
|
||||
@ParametersAreNonnullByDefault
|
||||
@MethodsReturnNonnullByDefault
|
||||
public enum BlockEdge {
|
||||
XP_YP(Direction.EAST, Direction.UP),
|
||||
ZP_YP(Direction.UP, Direction.SOUTH),
|
||||
XN_YP(Direction.WEST, Direction.UP),
|
||||
ZN_YP(Direction.UP, Direction.NORTH),
|
||||
XP_ZP(Direction.EAST, Direction.SOUTH),
|
||||
XN_ZP(Direction.WEST, Direction.SOUTH),
|
||||
XP_ZN(Direction.EAST, Direction.NORTH),
|
||||
XN_ZN(Direction.WEST, Direction.NORTH),
|
||||
XP_YN(Direction.EAST, Direction.DOWN),
|
||||
ZP_YN(Direction.DOWN, Direction.SOUTH),
|
||||
XN_YN(Direction.WEST, Direction.DOWN),
|
||||
ZN_YN(Direction.DOWN, Direction.SOUTH);
|
||||
|
||||
private final Direction norm1, norm2;
|
||||
|
||||
BlockEdge(Direction norm1, Direction norm2) {
|
||||
this.norm1 = norm1;
|
||||
this.norm2 = norm2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the edge from two normals. You can provide them in any order.
|
||||
*/
|
||||
public static BlockEdge fromNormals(Direction norm1, Direction norm2) {
|
||||
for (var edge : values()) {
|
||||
if (edge.norm1 == norm1 && edge.norm2 == norm2
|
||||
|| edge.norm1 == norm2 && edge.norm2 == edge.norm1) {
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Couldn't find " + norm1 + " & " + norm2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the normals of this edge. They will be returned in order {@code X, Y, Z}.
|
||||
*/
|
||||
public Pair<Direction, Direction> getNormals() {
|
||||
return Pair.of(this.norm1, this.norm2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate this many quarters of a block counter-clockwise around the given axis.
|
||||
* "Counter-clockwise" is from the point of view of the axis, facing inwards. So, rotating once
|
||||
* about the {@code Y} axis brings {@code XP_ZP} to {@code XP_ZN}.
|
||||
*/
|
||||
public BlockEdge rotateAbout(Direction.Axis axis, int quarters) {
|
||||
quarters = Mth.positiveModulo(quarters, 4);
|
||||
if (quarters == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Oh lord, group theory that minecraft has fortunately already done for me.
|
||||
Direction norm1$, norm2$;
|
||||
switch (quarters) {
|
||||
case 1 -> {
|
||||
norm1$ = this.norm1.getCounterClockWise(axis);
|
||||
norm2$ = this.norm2.getCounterClockWise(axis);
|
||||
}
|
||||
case 2 -> {
|
||||
norm1$ = this.norm1.getAxis() == axis ? this.norm1 : this.norm1.getOpposite();
|
||||
norm2$ = this.norm2.getAxis() == axis ? this.norm2 : this.norm2.getOpposite();
|
||||
}
|
||||
case 3 -> {
|
||||
norm1$ = this.norm1.getClockWise(axis);
|
||||
norm2$ = this.norm2.getClockWise(axis);
|
||||
}
|
||||
default -> throw new IllegalStateException();
|
||||
}
|
||||
|
||||
return fromNormals(norm1$, norm2$);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotate this many quarters of a block counter-clockwise around the given normal.
|
||||
* "Counter-clockwise" is from the point of view of the normal, facing outwards. So, rotating once
|
||||
* about the {@code NORTH} axis (negative Z) brings {@code XP_ZP} to {@code YP_ZP}.
|
||||
*/
|
||||
public BlockEdge rotateAbout(Direction direction, int quarters) {
|
||||
if (quarters % 4 == 0) {
|
||||
return this;
|
||||
}
|
||||
return rotateAbout(direction.getAxis(),
|
||||
direction.getAxisDirection() == Direction.AxisDirection.NEGATIVE ? -quarters : quarters);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get if the given direction is one of this block's normals, and if so, return the other normal.
|
||||
*/
|
||||
@Nullable
|
||||
public Direction getOtherNormal(Direction norm) {
|
||||
if (this.norm1 == norm) {
|
||||
return this.norm2;
|
||||
} else if (this.norm2 == norm) {
|
||||
return this.norm1;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package at.petrak.hexcasting.api.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.spell.casting.FunctionalData;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
/**
|
||||
* What this block passes onto the next block: what block it tries to exit to and where on that block it enters.
|
||||
*/
|
||||
public record FlowUpdate(FunctionalData newData, BlockPos nextPos, BlockEdge entryEdge) {
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
package at.petrak.hexcasting.api.circles;
|
||||
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Getter for lots of information about the currently operating circle's state.
|
||||
*/
|
||||
public interface ICircleState {
|
||||
BlockPos impetusPos();
|
||||
|
||||
Pair<BlockPos, BlockPos> circleBounds();
|
||||
|
||||
/**
|
||||
* Block positions the circle has floodfilled for ahead of time, when starting the circle.
|
||||
* <p>
|
||||
* See {@link ICircleState#isPosValid} for more info.
|
||||
*/
|
||||
Set<BlockPos> allScannedPositions();
|
||||
|
||||
/**
|
||||
* Whether this block position has been considered for being in the circle.
|
||||
* <p>
|
||||
* Anything this returns {@code false} for will fail the circle if it tries to output there.
|
||||
* This will also confuse the player, a lot, as the intention is that once a ritual starts successfully either
|
||||
* it encounters a normal mishap or finishes. So please code your circle components well.
|
||||
*/
|
||||
default boolean isPosValid(BlockPos pos) {
|
||||
return allScannedPositions().contains(pos);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package at.petrak.hexcasting.api.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntitySidedCircleWidget;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.util.StringRepresentable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A helper for {@link BlockEntitySidedCircleWidget BlockSidedCircleComponent}:
|
||||
* an abstraction over direction to only the 4 edges relevant to it.
|
||||
* <p>
|
||||
* When the normal dir is horizontal, {@code TOP} corresponds to {@code Direction.UP}, and {@code LEFT} and
|
||||
* {@code RIGHT} are to the left and to the right from the POV of <i>facing</i> that normal.
|
||||
* So, {@code Direction.NORTH}'s {@code RIGHT} is {@code Direction.WEST}.
|
||||
* <p>
|
||||
* When the normal dir is {@code Direction.UP} or {@code Direction.DOWN}, {@code TOP} corresponds to
|
||||
* {@code Direction.EAST}, cause we gotta pick something and +X seems reasonable. {@code Margin} always proceeds
|
||||
* counter-clockwise, so {@code Direction.UP}'s {@code RIGHT} is {@code Direction.EAST} and {@code Direction.DOWN}'s
|
||||
* {@code RIGHT} is {@code Direction.WEST}.
|
||||
* <p>
|
||||
* Things are additionally complicated because a {@code BlockSidedCircleComponent} can <i>also</i> have its TOP
|
||||
* dir facing any of the 4 margins, but that's a whole nother can of worms and I'm tired of typing doc comments.
|
||||
* Javac can yell a warning for not writing a doc comment, but it can't warn me for writing a <b>bad</b> one!
|
||||
*/
|
||||
public enum Margin implements StringRepresentable {
|
||||
TOP("top"),
|
||||
LEFT("left"),
|
||||
BOTTOM("bottom"),
|
||||
RIGHT("right");
|
||||
|
||||
private final String serializedName;
|
||||
|
||||
Margin(String serializedName) {
|
||||
this.serializedName = serializedName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return which margin the given direction points as, given a normal vector.
|
||||
* <p>
|
||||
* Throws an exception if the two directions are on the same axis.
|
||||
*/
|
||||
public static Margin fromNormalAndDir(Direction normal, Direction dir) throws IllegalArgumentException {
|
||||
var normalAxis = normal.getAxis();
|
||||
|
||||
var cursor = switch (normalAxis) {
|
||||
case X, Z -> Direction.UP;
|
||||
case Y -> Direction.NORTH;
|
||||
};
|
||||
for (var margin : Margin.values()) {
|
||||
if (cursor == dir) {
|
||||
return margin;
|
||||
}
|
||||
cursor = normal.getAxisDirection() == Direction.AxisDirection.NEGATIVE
|
||||
? cursor.getClockWise(normalAxis)
|
||||
: cursor.getCounterClockWise(normalAxis);
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Two directions with equal axes. Normal: " + normal + "; dir: " + dir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert this direction to the (absolute) edge given the normal vector of the block it's on.
|
||||
*/
|
||||
public BlockEdge toEdge(Direction normal) {
|
||||
var topDir = switch (normal.getAxis()) {
|
||||
case X, Z -> Direction.UP;
|
||||
case Y -> Direction.NORTH;
|
||||
};
|
||||
var topEdge = BlockEdge.fromNormals(normal.getOpposite(), topDir);
|
||||
return topEdge.rotateAbout(normal, this.ordinal());
|
||||
}
|
||||
|
||||
/**
|
||||
* If we consider {@code this} to be the "local {@code TOP}", transform {@code that} to face in the
|
||||
* appropriate direction.
|
||||
* <p>
|
||||
* Actually, I'm pretty sure this is transitive, so it doesn't matter which one you put as the receiver or argument.
|
||||
*/
|
||||
public Margin transform(Margin that) {
|
||||
return Margin.values()[(this.ordinal() + that.ordinal()) % Margin.values().length];
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getSerializedName() {
|
||||
return this.serializedName;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package at.petrak.hexcasting.api.spell.mishaps
|
||||
|
||||
import at.petrak.hexcasting.api.misc.FrozenColorizer
|
||||
import at.petrak.hexcasting.api.spell.casting.CastingContext
|
||||
import at.petrak.hexcasting.api.spell.iota.Iota
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
/**
|
||||
* Probably should never be thrown, but happens if a spell circle's flow is messed up during execution somehow.
|
||||
*/
|
||||
class MishapMessedUpSpellCircle : Mishap() {
|
||||
override fun accentColor(ctx: CastingContext, errorCtx: Context): FrozenColorizer {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun execute(ctx: CastingContext, errorCtx: Context, stack: MutableList<Iota>) {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
override fun errorMessage(ctx: CastingContext, errorCtx: Context): Component {
|
||||
TODO("Not yet implemented")
|
||||
}
|
||||
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
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 net.minecraft.world.level.material.PushReaction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
// As it turns out, not actually an impetus
|
||||
public class BlockEmptyImpetus extends BlockCircleComponent {
|
||||
public static final DirectionProperty FACING = BlockStateProperties.FACING;
|
||||
|
||||
public BlockEmptyImpetus(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
this.registerDefaultState(this.stateDefinition.any()
|
||||
.setValue(ENERGIZED, false).setValue(FACING, Direction.NORTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level world) {
|
||||
return enterDir != bs.getValue(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> exitDirections(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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float particleHeight(BlockPos pos, BlockState bs, Level world) {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushReaction getPistonPushReaction(BlockState pState) {
|
||||
return PushReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
|
||||
return this.defaultBlockState().setValue(FACING, pContext.getNearestLookingDirection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState rotate(BlockState pState, Rotation pRot) {
|
||||
return pState.setValue(FACING, pRot.rotate(pState.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState mirror(BlockState pState, Mirror pMirror) {
|
||||
return pState.rotate(pMirror.getRotation(pState.getValue(FACING)));
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles;
|
||||
|
||||
import at.petrak.hexcasting.api.block.HexBlockEntity;
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern;
|
||||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class BlockEntitySlate extends HexBlockEntity {
|
||||
public static final String TAG_PATTERN = "pattern";
|
||||
|
||||
@Nullable
|
||||
public HexPattern pattern;
|
||||
|
||||
public BlockEntitySlate(BlockPos pos, BlockState state) {
|
||||
super(HexBlockEntities.SLATE_TILE, pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveModData(CompoundTag tag) {
|
||||
if (this.pattern != null) {
|
||||
tag.put(TAG_PATTERN, this.pattern.serializeToNBT());
|
||||
} else {
|
||||
tag.put(TAG_PATTERN, new CompoundTag());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadModData(CompoundTag tag) {
|
||||
if (tag.contains(TAG_PATTERN, Tag.TAG_COMPOUND)) {
|
||||
CompoundTag patternTag = tag.getCompound(TAG_PATTERN);
|
||||
if (HexPattern.isPattern(patternTag)) {
|
||||
this.pattern = HexPattern.fromNBT(patternTag);
|
||||
} else {
|
||||
this.pattern = null;
|
||||
}
|
||||
} else {
|
||||
this.pattern = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,217 +0,0 @@
|
|||
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.spell.iota.PatternIota;
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.BlockGetter;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.LevelAccessor;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.StateDefinition;
|
||||
import net.minecraft.world.level.block.state.properties.*;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import net.minecraft.world.phys.HitResult;
|
||||
import net.minecraft.world.phys.shapes.CollisionContext;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.EnumSet;
|
||||
|
||||
// When on the floor or ceiling FACING is the direction the *bottom* of the pattern points
|
||||
// (or which way is "down").
|
||||
// When on the wall FACING is the direction of the *front* of the block
|
||||
public class BlockSlate extends BlockCircleComponent implements EntityBlock, SimpleWaterloggedBlock {
|
||||
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
|
||||
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||
public static final EnumProperty<AttachFace> ATTACH_FACE = BlockStateProperties.ATTACH_FACE;
|
||||
|
||||
public static final double THICKNESS = 1;
|
||||
public static final VoxelShape AABB_FLOOR = Block.box(0, 0, 0, 16, THICKNESS, 16);
|
||||
public static final VoxelShape AABB_CEILING = Block.box(0, 16 - THICKNESS, 0, 16, 16, 16);
|
||||
public static final VoxelShape AABB_EAST_WALL = Block.box(0, 0, 0, THICKNESS, 16, 16);
|
||||
public static final VoxelShape AABB_WEST_WALL = Block.box(16 - THICKNESS, 0, 0, 16, 16, 16);
|
||||
public static final VoxelShape AABB_SOUTH_WALL = Block.box(0, 0, 0, 16, 16, THICKNESS);
|
||||
public static final VoxelShape AABB_NORTH_WALL = Block.box(0, 0, 16 - THICKNESS, 16, 16, 16);
|
||||
|
||||
public BlockSlate(Properties p_53182_) {
|
||||
super(p_53182_);
|
||||
this.registerDefaultState(
|
||||
this.stateDefinition.any()
|
||||
.setValue(ENERGIZED, false)
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(WATERLOGGED, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean propagatesSkylightDown(BlockState state, @Nonnull BlockGetter reader, @Nonnull BlockPos pos) {
|
||||
return !state.getValue(WATERLOGGED);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public FluidState getFluidState(BlockState state) {
|
||||
return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<Direction> exitDirections(BlockPos pos, BlockState bs, Level world) {
|
||||
var allDirs = EnumSet.allOf(Direction.class);
|
||||
var normal = this.normalDir(pos, bs, world);
|
||||
allDirs.remove(normal);
|
||||
allDirs.remove(normal.getOpposite());
|
||||
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) {
|
||||
BlockEntity be = level.getBlockEntity(pos);
|
||||
if (be instanceof BlockEntitySlate slate) {
|
||||
ItemStack stack = new ItemStack(HexItems.SLATE);
|
||||
if (slate.pattern != null) {
|
||||
HexItems.SLATE.writeDatum(stack, new PatternIota(slate.pattern));
|
||||
}
|
||||
return stack;
|
||||
}
|
||||
|
||||
return new ItemStack(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction normalDir(BlockPos pos, BlockState bs, Level world, int recursionLeft) {
|
||||
return switch (bs.getValue(ATTACH_FACE)) {
|
||||
case FLOOR -> Direction.UP;
|
||||
case CEILING -> Direction.DOWN;
|
||||
case WALL -> bs.getValue(FACING);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public float particleHeight(BlockPos pos, BlockState bs, Level world) {
|
||||
return 0.5f - 15f / 16f;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
|
||||
return new BlockEntitySlate(pPos, pState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
|
||||
return switch (pState.getValue(ATTACH_FACE)) {
|
||||
case FLOOR -> AABB_FLOOR;
|
||||
case CEILING -> AABB_CEILING;
|
||||
case WALL -> switch (pState.getValue(FACING)) {
|
||||
case NORTH -> AABB_NORTH_WALL;
|
||||
case EAST -> AABB_EAST_WALL;
|
||||
case SOUTH -> AABB_SOUTH_WALL;
|
||||
// NORTH; up and down don't happen (but we need branches for them)
|
||||
default -> AABB_WEST_WALL;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushReaction getPistonPushReaction(BlockState pState) {
|
||||
return PushReaction.DESTROY;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(FACING, ATTACH_FACE, WATERLOGGED);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
|
||||
FluidState fluidState = pContext.getLevel().getFluidState(pContext.getClickedPos());
|
||||
|
||||
for (Direction direction : pContext.getNearestLookingDirections()) {
|
||||
BlockState blockstate;
|
||||
if (direction.getAxis() == Direction.Axis.Y) {
|
||||
blockstate = this.defaultBlockState()
|
||||
.setValue(ATTACH_FACE, direction == Direction.UP ? AttachFace.CEILING : AttachFace.FLOOR)
|
||||
.setValue(FACING, pContext.getHorizontalDirection().getOpposite());
|
||||
} else {
|
||||
blockstate = this.defaultBlockState()
|
||||
.setValue(ATTACH_FACE, AttachFace.WALL)
|
||||
.setValue(FACING, direction.getOpposite());
|
||||
}
|
||||
blockstate = blockstate.setValue(WATERLOGGED,
|
||||
fluidState.is(FluidTags.WATER) && fluidState.getAmount() == 8);
|
||||
|
||||
if (blockstate.canSurvive(pContext.getLevel(), pContext.getClickedPos())) {
|
||||
return blockstate;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// i do as the FaceAttachedHorizontalDirectionBlock.java guides
|
||||
@Override
|
||||
public boolean canSurvive(BlockState pState, LevelReader pLevel, BlockPos pPos) {
|
||||
return canAttach(pLevel, pPos, getConnectedDirection(pState).getOpposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState updateShape(BlockState pState, Direction pFacing, BlockState pFacingState, LevelAccessor pLevel,
|
||||
BlockPos pCurrentPos, BlockPos pFacingPos) {
|
||||
if (pState.getValue(WATERLOGGED)) {
|
||||
pLevel.scheduleTick(pCurrentPos, Fluids.WATER, Fluids.WATER.getTickDelay(pLevel));
|
||||
}
|
||||
|
||||
return getConnectedDirection(pState).getOpposite() == pFacing
|
||||
&& !pState.canSurvive(pLevel, pCurrentPos) ?
|
||||
pState.getFluidState().createLegacyBlock()
|
||||
: super.updateShape(pState, pFacing, pFacingState, pLevel, pCurrentPos, pFacingPos);
|
||||
}
|
||||
|
||||
public static boolean canAttach(LevelReader pReader, BlockPos pPos, Direction pDirection) {
|
||||
BlockPos blockpos = pPos.relative(pDirection);
|
||||
return pReader.getBlockState(blockpos).isFaceSturdy(pReader, blockpos, pDirection.getOpposite());
|
||||
}
|
||||
|
||||
protected static Direction getConnectedDirection(BlockState pState) {
|
||||
return switch (pState.getValue(ATTACH_FACE)) {
|
||||
case CEILING -> Direction.DOWN;
|
||||
case FLOOR -> Direction.UP;
|
||||
default -> pState.getValue(FACING);
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,79 +0,0 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles.directrix;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
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.EnumProperty;
|
||||
import net.minecraft.world.level.material.PushReaction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public class BlockEmptyDirectrix extends BlockCircleComponent {
|
||||
public static final EnumProperty<Direction.Axis> AXIS = BlockStateProperties.AXIS;
|
||||
|
||||
public BlockEmptyDirectrix(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
this.registerDefaultState(this.stateDefinition.any()
|
||||
.setValue(ENERGIZED, false)
|
||||
.setValue(AXIS, Direction.Axis.X));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction normalDir(BlockPos pos, BlockState bs, Level world, int recursionLeft) {
|
||||
return Direction.UP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float particleHeight(BlockPos pos, BlockState bs, Level world) {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushReaction getPistonPushReaction(BlockState pState) {
|
||||
return PushReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(AXIS);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
|
||||
return this.defaultBlockState().setValue(AXIS, pContext.getNearestLookingDirection().getAxis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState rotate(BlockState pState, Rotation pRot) {
|
||||
return pState.setValue(AXIS,
|
||||
pRot.rotate(Direction.get(Direction.AxisDirection.POSITIVE, pState.getValue(AXIS))).getAxis());
|
||||
}
|
||||
}
|
|
@ -1,127 +0,0 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles.directrix;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.spell.math.HexPattern;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.core.particles.DustParticleOptions;
|
||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.Mirror;
|
||||
import net.minecraft.world.level.block.Rotation;
|
||||
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.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.Random;
|
||||
|
||||
public class BlockRedstoneDirectrix extends BlockCircleComponent {
|
||||
public static final DirectionProperty FACING = BlockStateProperties.FACING;
|
||||
public static final BooleanProperty REDSTONE_POWERED = BlockStateProperties.POWERED;
|
||||
|
||||
public BlockRedstoneDirectrix(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
this.registerDefaultState(this.stateDefinition.any()
|
||||
.setValue(REDSTONE_POWERED, false)
|
||||
.setValue(ENERGIZED, false)
|
||||
.setValue(FACING, Direction.NORTH));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canEnterFromDirection(Direction enterDir, Direction normalDir, BlockPos pos, BlockState bs,
|
||||
Level 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction normalDir(BlockPos pos, BlockState bs, Level world, int recursionLeft) {
|
||||
return normalDirOfOther(pos.relative(getRealFacing(bs)), world, recursionLeft);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float particleHeight(BlockPos pos, BlockState bs, Level world) {
|
||||
return 0.5f;
|
||||
}
|
||||
|
||||
protected Direction getRealFacing(BlockState bs) {
|
||||
var facing = bs.getValue(FACING);
|
||||
if (bs.getValue(REDSTONE_POWERED)) {
|
||||
return facing.getOpposite();
|
||||
} else {
|
||||
return facing;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void neighborChanged(BlockState pState, Level pLevel, BlockPos pPos, Block pBlock, BlockPos pFromPos,
|
||||
boolean pIsMoving) {
|
||||
super.neighborChanged(pState, pLevel, pPos, pBlock, pFromPos, pIsMoving);
|
||||
|
||||
if (!pLevel.isClientSide) {
|
||||
boolean currentlyPowered = pState.getValue(REDSTONE_POWERED);
|
||||
if (currentlyPowered != pLevel.hasNeighborSignal(pPos)) {
|
||||
pLevel.setBlock(pPos, pState.setValue(REDSTONE_POWERED, !currentlyPowered), 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void animateTick(BlockState bs, Level pLevel, BlockPos pos, Random rand) {
|
||||
if (bs.getValue(REDSTONE_POWERED)) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
var step = bs.getValue(FACING).getOpposite().step();
|
||||
var center = Vec3.atCenterOf(pos).add(step.x() * 0.5, step.y() * 0.5, step.z() * 0.5);
|
||||
double x = center.x + (rand.nextDouble() - 0.5) * 0.5D;
|
||||
double y = center.y + (rand.nextDouble() - 0.5) * 0.5D;
|
||||
double z = center.z + (rand.nextDouble() - 0.5) * 0.5D;
|
||||
pLevel.addParticle(DustParticleOptions.REDSTONE, x, y, z,
|
||||
step.x() * 0.1, step.y() * 0.1, step.z() * 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PushReaction getPistonPushReaction(BlockState pState) {
|
||||
return PushReaction.BLOCK;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(REDSTONE_POWERED, FACING);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
|
||||
return this.defaultBlockState().setValue(FACING, pContext.getNearestLookingDirection());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState rotate(BlockState pState, Rotation pRot) {
|
||||
return pState.setValue(FACING, pRot.rotate(pState.getValue(FACING)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState mirror(BlockState pState, Mirror pMirror) {
|
||||
return pState.rotate(pMirror.getRotation(pState.getValue(FACING)));
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles.impetuses;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
|
||||
import at.petrak.hexcasting.common.blocks.entity.BlockEntityLookingImpetus;
|
||||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class BlockLookingImpetus extends BlockAbstractImpetus {
|
||||
|
||||
public BlockLookingImpetus(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
|
||||
return new BlockEntityLookingImpetus(pPos, pState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level pLevel, BlockState pState,
|
||||
BlockEntityType<T> type) {
|
||||
if (!pLevel.isClientSide) {
|
||||
return createTickerHelper(type, HexBlockEntities.IMPETUS_LOOK_TILE,
|
||||
BlockEntityLookingImpetus::serverTick);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// uegh
|
||||
@Nullable
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static <E extends BlockEntity, A extends BlockEntity> BlockEntityTicker<A> createTickerHelper(
|
||||
BlockEntityType<A> type, BlockEntityType<E> targetType, BlockEntityTicker<? super E> ticker) {
|
||||
return targetType == type ? (BlockEntityTicker<A>) ticker : null;
|
||||
}
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles.impetuses;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
|
||||
import at.petrak.hexcasting.common.blocks.entity.BlockEntityRightClickImpetus;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class BlockRightClickImpetus extends BlockAbstractImpetus {
|
||||
public BlockRightClickImpetus(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
|
||||
return new BlockEntityRightClickImpetus(pPos, pState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand,
|
||||
BlockHitResult pHit) {
|
||||
if (!pPlayer.isShiftKeyDown()) {
|
||||
var tile = pLevel.getBlockEntity(pPos);
|
||||
if (tile instanceof BlockEntityRightClickImpetus impetus) {
|
||||
if (pPlayer instanceof ServerPlayer serverPlayer) {
|
||||
impetus.activateSpellCircle(serverPlayer);
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
}
|
|
@ -1,104 +0,0 @@
|
|||
package at.petrak.hexcasting.common.blocks.circles.impetuses;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.spell.iota.EntityIota;
|
||||
import at.petrak.hexcasting.common.blocks.entity.BlockEntityStoredPlayerImpetus;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import at.petrak.hexcasting.xplat.IXplatAbstractions;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.sounds.SoundSource;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.InteractionResult;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
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.BooleanProperty;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class BlockStoredPlayerImpetus extends BlockAbstractImpetus {
|
||||
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
|
||||
|
||||
public BlockStoredPlayerImpetus(Properties p_49795_) {
|
||||
super(p_49795_);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
|
||||
return new BlockEntityStoredPlayerImpetus(pPos, pState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
||||
super.createBlockStateDefinition(builder);
|
||||
builder.add(POWERED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult use(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand,
|
||||
BlockHitResult pHit) {
|
||||
if (pLevel.getBlockEntity(pPos) instanceof BlockEntityStoredPlayerImpetus tile) {
|
||||
var usedStack = pPlayer.getItemInHand(pHand);
|
||||
var datumContainer = IXplatAbstractions.INSTANCE.findDataHolder(usedStack);
|
||||
if (datumContainer != null) {
|
||||
if (pLevel instanceof ServerLevel level) {
|
||||
var stored = datumContainer.readIota(level);
|
||||
if (stored instanceof EntityIota eieio) {
|
||||
var entity = eieio.getEntity();
|
||||
if (entity instanceof Player player) {
|
||||
// phew, we got something
|
||||
tile.setPlayer(player.getGameProfile(), entity.getUUID());
|
||||
level.sendBlockUpdated(pPos, pState, pState, Block.UPDATE_CLIENTS);
|
||||
|
||||
pLevel.playSound(pPlayer, pPos, HexSounds.IMPETUS_STOREDPLAYER_DING,
|
||||
SoundSource.BLOCKS, 1f, 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick(BlockState pState, ServerLevel pLevel, BlockPos pPos, Random pRandom) {
|
||||
super.tick(pState, pLevel, pPos, pRandom);
|
||||
if (pLevel.getBlockEntity(pPos) instanceof BlockEntityStoredPlayerImpetus tile) {
|
||||
tile.updatePlayerProfile();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void neighborChanged(BlockState pState, Level pLevel, BlockPos pPos, Block pBlock, BlockPos pFromPos,
|
||||
boolean pIsMoving) {
|
||||
super.neighborChanged(pState, pLevel, pPos, pBlock, pFromPos, pIsMoving);
|
||||
|
||||
if (!pLevel.isClientSide()) {
|
||||
boolean prevPowered = pState.getValue(POWERED);
|
||||
boolean isPowered = pLevel.hasNeighborSignal(pPos);
|
||||
|
||||
if (prevPowered != isPowered) {
|
||||
pLevel.setBlockAndUpdate(pPos, pState.setValue(POWERED, isPowered));
|
||||
|
||||
if (isPowered && pLevel.getBlockEntity(pPos) instanceof BlockEntityStoredPlayerImpetus tile) {
|
||||
var player = tile.getStoredPlayer();
|
||||
if (player instanceof ServerPlayer splayer) {
|
||||
// phew
|
||||
tile.activateSpellCircle(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.block.circle.BlockEntityCircleWidget;
|
||||
import at.petrak.hexcasting.common.lib.HexBlockEntities;
|
||||
import at.petrak.hexcasting.common.lib.HexSounds;
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
@ -34,7 +34,7 @@ public class BlockEntityLookingImpetus extends BlockEntityAbstractImpetus {
|
|||
|
||||
// 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)) {
|
||||
if (bs.getValue(BlockEntityCircleWidget.ENERGIZED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,11 @@
|
|||
package at.petrak.hexcasting.common.lib;
|
||||
|
||||
import at.petrak.hexcasting.api.block.circle.BlockAbstractImpetus;
|
||||
import at.petrak.hexcasting.common.blocks.BlockConjured;
|
||||
import at.petrak.hexcasting.common.blocks.BlockConjuredLight;
|
||||
import at.petrak.hexcasting.common.blocks.BlockFlammable;
|
||||
import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicBookshelf;
|
||||
import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicLigature;
|
||||
import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicRecord;
|
||||
import at.petrak.hexcasting.common.blocks.circles.BlockEmptyImpetus;
|
||||
import at.petrak.hexcasting.common.blocks.circles.BlockSlate;
|
||||
import at.petrak.hexcasting.common.blocks.circles.directrix.BlockEmptyDirectrix;
|
||||
import at.petrak.hexcasting.common.blocks.circles.directrix.BlockRedstoneDirectrix;
|
||||
import at.petrak.hexcasting.common.blocks.circles.impetuses.BlockLookingImpetus;
|
||||
import at.petrak.hexcasting.common.blocks.circles.impetuses.BlockRightClickImpetus;
|
||||
import at.petrak.hexcasting.common.blocks.circles.impetuses.BlockStoredPlayerImpetus;
|
||||
import at.petrak.hexcasting.common.blocks.decoration.*;
|
||||
import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
@ -118,6 +110,7 @@ public class HexBlocks {
|
|||
.isViewBlocking(HexBlocks::never)),
|
||||
new Item.Properties());
|
||||
|
||||
/*
|
||||
// "no" item because we add it manually
|
||||
public static final BlockSlate SLATE = blockNoItem("slate", new BlockSlate(slateish()));
|
||||
|
||||
|
@ -137,6 +130,7 @@ public class HexBlocks {
|
|||
new BlockEmptyDirectrix(slateish()));
|
||||
public static final BlockRedstoneDirectrix DIRECTRIX_REDSTONE = blockItem("directrix_redstone",
|
||||
new BlockRedstoneDirectrix(slateish()));
|
||||
*/
|
||||
|
||||
public static final BlockAkashicRecord AKASHIC_RECORD = blockItem("akashic_record",
|
||||
new BlockAkashicRecord(akashicWoodyHard().lightLevel(bs -> 15)));
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [32, 32],
|
||||
"textures": {
|
||||
"particle": "hexcasting:block/circle/common",
|
||||
"common": "hexcasting:block/circle/common",
|
||||
"core": "hexcasting:block/circle/impetus"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "body",
|
||||
"from": [2, 0, 2],
|
||||
"to": [14, 4, 14],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 1, 6, 3], "texture": "#common"},
|
||||
"east": {"uv": [0, 1, 6, 3], "texture": "#common"},
|
||||
"south": {"uv": [0, 1, 6, 3], "texture": "#common"},
|
||||
"west": {"uv": [0, 1, 6, 3], "texture": "#common"},
|
||||
"up": {"uv": [2, 2, 14, 14], "texture": "#core"},
|
||||
"down": {"uv": [9, 1, 15, 7], "rotation": 270, "texture": "#common"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, 225, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, 3, 0],
|
||||
"scale": [0.25, 0.25, 0.25]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [30, 225, 0],
|
||||
"scale": [0.625, 0.625, 0.625]
|
||||
},
|
||||
"fixed": {
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [32, 32],
|
||||
"textures": {
|
||||
"particle": "hexcasting:block/circle/common",
|
||||
"common": "hexcasting:block/circle/common",
|
||||
"core": "hexcasting:block/circle/impetus"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "east",
|
||||
"from": [14, 0, 4],
|
||||
"to": [16, 2, 12],
|
||||
"faces": {
|
||||
"north": {"uv": [14, 2, 16, 4], "texture": "#core"},
|
||||
"east": {"uv": [0, 0, 4, 1], "texture": "#common"},
|
||||
"south": {"uv": [16, 2, 14, 4], "texture": "#core"},
|
||||
"west": {"uv": [0, 0, 4, 1], "texture": "#missing"},
|
||||
"up": {"uv": [14, 4, 16, 12], "texture": "#core"},
|
||||
"down": {"uv": [14, 4, 16, 12], "rotation": 180, "texture": "#core"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, 225, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, 3, 0],
|
||||
"scale": [0.25, 0.25, 0.25]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [30, 225, 0],
|
||||
"scale": [0.625, 0.625, 0.625]
|
||||
},
|
||||
"fixed": {
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [32, 32],
|
||||
"textures": {
|
||||
"particle": "hexcasting:block/circle/common",
|
||||
"common": "hexcasting:block/circle/common",
|
||||
"core": "hexcasting:block/circle/impetus"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "north",
|
||||
"from": [4, 0, 0],
|
||||
"to": [12, 2, 2],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 1], "texture": "#common"},
|
||||
"east": {"uv": [2, 0, 4, 2], "texture": "#core"},
|
||||
"south": {"uv": [0, 0, 4, 1], "texture": "#missing"},
|
||||
"west": {"uv": [2, 0, 4, 2], "texture": "#core"},
|
||||
"up": {"uv": [4, 0, 12, 2], "texture": "#core"},
|
||||
"down": {"uv": [4, 0, 12, 2], "rotation": 180, "texture": "#core"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, 225, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, 3, 0],
|
||||
"scale": [0.25, 0.25, 0.25]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [30, 225, 0],
|
||||
"scale": [0.625, 0.625, 0.625]
|
||||
},
|
||||
"fixed": {
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [32, 32],
|
||||
"textures": {
|
||||
"particle": "hexcasting:block/circle/common",
|
||||
"common": "hexcasting:block/circle/common",
|
||||
"core": "hexcasting:block/circle/impetus"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "south",
|
||||
"from": [4, 0, 14],
|
||||
"to": [12, 2, 16],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 4, 1], "texture": "#missing"},
|
||||
"east": {"uv": [2, 14, 4, 16], "texture": "#core"},
|
||||
"south": {"uv": [0, 0, 4, 1], "texture": "#common"},
|
||||
"west": {"uv": [2, 14, 4, 16], "texture": "#core"},
|
||||
"up": {"uv": [4, 0, 12, 2], "texture": "#core"},
|
||||
"down": {"uv": [4, 14, 12, 16], "rotation": 180, "texture": "#core"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, 225, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, 3, 0],
|
||||
"scale": [0.25, 0.25, 0.25]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [30, 225, 0],
|
||||
"scale": [0.625, 0.625, 0.625]
|
||||
},
|
||||
"fixed": {
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
{
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [32, 32],
|
||||
"textures": {
|
||||
"particle": "hexcasting:block/circle/common",
|
||||
"common": "hexcasting:block/circle/common",
|
||||
"core": "hexcasting:block/circle/impetus"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
"name": "west",
|
||||
"from": [0, 0, 4],
|
||||
"to": [2, 2, 12],
|
||||
"faces": {
|
||||
"north": {"uv": [0, 2, 2, 4], "texture": "#core"},
|
||||
"east": {"uv": [0, 0, 4, 1], "texture": "#missing"},
|
||||
"south": {"uv": [0, 2, 2, 4], "texture": "#core"},
|
||||
"west": {"uv": [0, 0, 4, 1], "texture": "#common"},
|
||||
"up": {"uv": [0, 4, 2, 12], "texture": "#core"},
|
||||
"down": {"uv": [0, 4, 2, 12], "rotation": 180, "texture": "#core"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"display": {
|
||||
"thirdperson_righthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"thirdperson_lefthand": {
|
||||
"rotation": [75, 45, 0],
|
||||
"translation": [0, 2.5, 0],
|
||||
"scale": [0.375, 0.375, 0.375]
|
||||
},
|
||||
"firstperson_righthand": {
|
||||
"rotation": [0, 45, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"firstperson_lefthand": {
|
||||
"rotation": [0, 225, 0],
|
||||
"scale": [0.4, 0.4, 0.4]
|
||||
},
|
||||
"ground": {
|
||||
"translation": [0, 3, 0],
|
||||
"scale": [0.25, 0.25, 0.25]
|
||||
},
|
||||
"gui": {
|
||||
"rotation": [30, 225, 0],
|
||||
"scale": [0.625, 0.625, 0.625]
|
||||
},
|
||||
"fixed": {
|
||||
"scale": [0.5, 0.5, 0.5]
|
||||
}
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 391 B |
After Width: | Height: | Size: 487 B |
After Width: | Height: | Size: 458 B |
After Width: | Height: | Size: 484 B |
After Width: | Height: | Size: 341 B |
After Width: | Height: | Size: 308 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 330 B |
After Width: | Height: | Size: 154 B |
|
@ -1,6 +1,7 @@
|
|||
package at.petrak.hexcasting.fabric.cc;
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntitySidedCircleWidget;
|
||||
import at.petrak.hexcasting.api.item.ColorizerItem;
|
||||
import at.petrak.hexcasting.api.item.HexHolderItem;
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
|
@ -8,10 +9,9 @@ import at.petrak.hexcasting.api.item.MediaHolderItem;
|
|||
import at.petrak.hexcasting.api.mod.HexConfig;
|
||||
import at.petrak.hexcasting.api.spell.iota.DoubleIota;
|
||||
import at.petrak.hexcasting.common.lib.HexItems;
|
||||
import at.petrak.hexcasting.fabric.cc.adimpl.CCColorizer;
|
||||
import at.petrak.hexcasting.fabric.cc.adimpl.CCHexHolder;
|
||||
import at.petrak.hexcasting.fabric.cc.adimpl.CCIotaHolder;
|
||||
import at.petrak.hexcasting.fabric.cc.adimpl.CCMediaHolder;
|
||||
import at.petrak.hexcasting.fabric.cc.adimpl.*;
|
||||
import dev.onyxstudios.cca.api.v3.block.BlockComponentFactoryRegistry;
|
||||
import dev.onyxstudios.cca.api.v3.block.BlockComponentInitializer;
|
||||
import dev.onyxstudios.cca.api.v3.component.ComponentKey;
|
||||
import dev.onyxstudios.cca.api.v3.component.ComponentRegistry;
|
||||
import dev.onyxstudios.cca.api.v3.entity.EntityComponentFactoryRegistry;
|
||||
|
@ -25,7 +25,7 @@ import net.minecraft.world.item.Items;
|
|||
|
||||
import static at.petrak.hexcasting.api.HexAPI.modLoc;
|
||||
|
||||
public class HexCardinalComponents implements EntityComponentInitializer, ItemComponentInitializer {
|
||||
public class HexCardinalComponents implements EntityComponentInitializer, ItemComponentInitializer, BlockComponentInitializer {
|
||||
// entities
|
||||
public static final ComponentKey<CCBrainswept> BRAINSWEPT = ComponentRegistry.getOrCreate(modLoc("brainswept"),
|
||||
CCBrainswept.class);
|
||||
|
@ -48,6 +48,9 @@ public class HexCardinalComponents implements EntityComponentInitializer, ItemCo
|
|||
CCMediaHolder.class);
|
||||
public static final ComponentKey<CCHexHolder> HEX_HOLDER = ComponentRegistry.getOrCreate(modLoc("hex_holder"),
|
||||
CCHexHolder.class);
|
||||
public static final ComponentKey<CCCircleWidget> CIRCLE_WIDGET = ComponentRegistry.getOrCreate(
|
||||
modLoc("circle_widget"),
|
||||
CCCircleWidget.class);
|
||||
|
||||
@Override
|
||||
public void registerEntityComponentFactories(EntityComponentFactoryRegistry registry) {
|
||||
|
@ -83,4 +86,11 @@ public class HexCardinalComponents implements EntityComponentInitializer, ItemCo
|
|||
|
||||
registry.register(i -> i instanceof HexHolderItem, HEX_HOLDER, CCHexHolder.ItemBased::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerBlockComponentFactories(BlockComponentFactoryRegistry registry) {
|
||||
registry.registerFor(BlockEntitySidedCircleWidget.class, CIRCLE_WIDGET,
|
||||
CCCircleWidget.SidedCircleWidgetBased::new);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
package at.petrak.hexcasting.fabric.cc.adimpl;
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.ADCircleWidget;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntitySidedCircleWidget;
|
||||
import at.petrak.hexcasting.api.circles.BlockEdge;
|
||||
import at.petrak.hexcasting.api.circles.FlowUpdate;
|
||||
import at.petrak.hexcasting.api.circles.ICircleState;
|
||||
import dev.onyxstudios.cca.api.v3.component.Component;
|
||||
import dev.onyxstudios.cca.api.v3.component.sync.AutoSyncedComponent;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
public abstract class CCCircleWidget implements ADCircleWidget, Component, AutoSyncedComponent {
|
||||
// The serde methods here are no-ops because any and all state should be synced by the implementor.
|
||||
@Override
|
||||
public void readFromNbt(CompoundTag tag) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToNbt(CompoundTag tag) {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
public static class SidedCircleWidgetBased extends CCCircleWidget {
|
||||
private final BlockEntitySidedCircleWidget inner;
|
||||
|
||||
public SidedCircleWidgetBased(BlockEntitySidedCircleWidget inner) {
|
||||
this.inner = inner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptsFlow(BlockEdge edge) {
|
||||
var margin = inner.getMargin(edge);
|
||||
if (margin == null) {
|
||||
return false;
|
||||
}
|
||||
return inner.acceptsFlow(margin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<BlockEdge> possibleExitDirs(BlockEdge inputEdge) {
|
||||
var margin = inner.getMargin(inputEdge);
|
||||
if (margin == null) {
|
||||
return EnumSet.noneOf(BlockEdge.class);
|
||||
}
|
||||
return inner.possibleExitDirs(margin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FlowUpdate onReceiveFlow(BlockEdge inputEdge, BlockEntity sender, ICircleState state) {
|
||||
var margin = inner.getMargin(inputEdge);
|
||||
if (margin == null) {
|
||||
return null;
|
||||
}
|
||||
return inner.onReceiveFlow(margin, sender, state);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -69,7 +69,8 @@
|
|||
"hexcasting:colorizer",
|
||||
"hexcasting:iota_holder",
|
||||
"hexcasting:media_holder",
|
||||
"hexcasting:hex_holder"
|
||||
"hexcasting:hex_holder",
|
||||
"hexcasting:circle_widget"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
package at.petrak.hexcasting.forge.cap;
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.ADColorizer;
|
||||
import at.petrak.hexcasting.api.addldata.ADHexHolder;
|
||||
import at.petrak.hexcasting.api.addldata.ADIotaHolder;
|
||||
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntityAbstractImpetus;
|
||||
import at.petrak.hexcasting.api.addldata.*;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockEntitySidedCircleWidget;
|
||||
import at.petrak.hexcasting.api.circles.BlockEdge;
|
||||
import at.petrak.hexcasting.api.circles.FlowUpdate;
|
||||
import at.petrak.hexcasting.api.circles.ICircleState;
|
||||
import at.petrak.hexcasting.api.item.ColorizerItem;
|
||||
import at.petrak.hexcasting.api.item.HexHolderItem;
|
||||
import at.petrak.hexcasting.api.item.IotaHolderItem;
|
||||
|
@ -28,10 +28,10 @@ import net.minecraftforge.common.capabilities.RegisterCapabilitiesEvent;
|
|||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.common.util.NonNullSupplier;
|
||||
import net.minecraftforge.event.AttachCapabilitiesEvent;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
@ -65,6 +65,10 @@ public class ForgeCapabilityHandler {
|
|||
* Items that work as pigments.
|
||||
*/
|
||||
public static final ResourceLocation PIGMENT_CAP = modLoc("colorizer");
|
||||
/**
|
||||
* Block entities that work as circle widgets.
|
||||
*/
|
||||
public static final ResourceLocation CIRCLE_WIDGET_CAP = modLoc("circle_widget");
|
||||
|
||||
private static final ResourceLocation IMPETUS_HANDLER = modLoc("impetus_items");
|
||||
|
||||
|
@ -114,10 +118,19 @@ public class ForgeCapabilityHandler {
|
|||
}
|
||||
|
||||
public static void attachBlockEntityCaps(AttachCapabilitiesEvent<BlockEntity> evt) {
|
||||
// TODO
|
||||
/*
|
||||
if (evt.getObject() instanceof BlockEntityAbstractImpetus impetus) {
|
||||
evt.addCapability(IMPETUS_HANDLER, provide(impetus, CapabilityItemHandler.ITEM_HANDLER_CAPABILITY,
|
||||
() -> new ForgeImpetusCapability(impetus)));
|
||||
}
|
||||
*/
|
||||
|
||||
var be = evt.getObject();
|
||||
if (be instanceof BlockEntitySidedCircleWidget bescw) {
|
||||
evt.addCapability(CIRCLE_WIDGET_CAP,
|
||||
provide(be, HexCapabilities.CIRCLE_WIDGET, () -> new BESidedCircleWidgetBased(bescw, be)));
|
||||
}
|
||||
}
|
||||
|
||||
private static <CAP> SimpleProvider<CAP> provide(BlockEntity be, Capability<CAP> capability,
|
||||
|
@ -338,4 +351,35 @@ public class ForgeCapabilityHandler {
|
|||
return holder.color(stack, owner, time, position);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO can we *please* stop putting all of these records in this god object please
|
||||
private record BESidedCircleWidgetBased(BlockEntitySidedCircleWidget inner,
|
||||
BlockEntity be) implements ADCircleWidget {
|
||||
@Override
|
||||
public boolean acceptsFlow(BlockEdge edge) {
|
||||
var margin = inner.getMargin(edge);
|
||||
if (margin == null) {
|
||||
return false;
|
||||
}
|
||||
return inner.acceptsFlow(margin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumSet<BlockEdge> possibleExitDirs(BlockEdge inputEdge) {
|
||||
var margin = inner.getMargin(inputEdge);
|
||||
if (margin == null) {
|
||||
return EnumSet.noneOf(BlockEdge.class);
|
||||
}
|
||||
return inner.possibleExitDirs(margin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable FlowUpdate onReceiveFlow(BlockEdge inputEdge, BlockEntity sender, ICircleState state) {
|
||||
var margin = inner.getMargin(inputEdge);
|
||||
if (margin == null) {
|
||||
return null;
|
||||
}
|
||||
return inner.onReceiveFlow(margin, sender, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package at.petrak.hexcasting.forge.cap;
|
||||
|
||||
import at.petrak.hexcasting.api.addldata.ADColorizer;
|
||||
import at.petrak.hexcasting.api.addldata.ADHexHolder;
|
||||
import at.petrak.hexcasting.api.addldata.ADIotaHolder;
|
||||
import at.petrak.hexcasting.api.addldata.ADMediaHolder;
|
||||
import at.petrak.hexcasting.api.addldata.*;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||
|
@ -18,4 +15,6 @@ public final class HexCapabilities {
|
|||
});
|
||||
public static final Capability<ADColorizer> COLOR = CapabilityManager.get(new CapabilityToken<>() {
|
||||
});
|
||||
public static final Capability<ADCircleWidget> CIRCLE_WIDGET = CapabilityManager.get(new CapabilityToken<>() {
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,17 +1,15 @@
|
|||
package at.petrak.hexcasting.forge.datagen.xplat;
|
||||
|
||||
import at.petrak.hexcasting.api.HexAPI;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
|
||||
import at.petrak.hexcasting.api.block.circle.BlockSidedCircleWidget;
|
||||
import at.petrak.hexcasting.api.circles.Margin;
|
||||
import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicBookshelf;
|
||||
import at.petrak.hexcasting.common.blocks.circles.BlockSlate;
|
||||
import at.petrak.hexcasting.common.blocks.circles.directrix.BlockRedstoneDirectrix;
|
||||
import at.petrak.hexcasting.common.lib.HexBlocks;
|
||||
import at.petrak.paucal.api.forge.datagen.PaucalBlockStateAndModelProvider;
|
||||
import it.unimi.dsi.fastutil.booleans.Boolean2ObjectFunction;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||
import net.minecraftforge.client.model.generators.BlockModelBuilder;
|
||||
import net.minecraftforge.client.model.generators.ConfiguredModel;
|
||||
import net.minecraftforge.common.data.ExistingFileHelper;
|
||||
|
@ -26,101 +24,6 @@ public class HexBlockStatesAndModels extends PaucalBlockStateAndModelProvider {
|
|||
@Override
|
||||
protected void registerStatesAndModels() {
|
||||
var slateModel = models().getExistingFile(modLoc("slate"));
|
||||
getVariantBuilder(HexBlocks.SLATE).forAllStatesExcept(bs -> {
|
||||
int rotationX = 0;
|
||||
int rotationY = 0;
|
||||
switch (bs.getValue(BlockSlate.ATTACH_FACE)) {
|
||||
case CEILING -> rotationX = 180;
|
||||
case WALL -> {
|
||||
rotationX = 90;
|
||||
rotationY = bs.getValue(BlockSlate.FACING).getOpposite().get2DDataValue() * 90;
|
||||
}
|
||||
}
|
||||
return ConfiguredModel.builder()
|
||||
.modelFile(slateModel)
|
||||
.rotationX(rotationX)
|
||||
.rotationY(rotationY)
|
||||
.uvLock(true)
|
||||
.build();
|
||||
}, BlockSlate.WATERLOGGED);
|
||||
|
||||
impetus(HexBlocks.IMPETUS_RIGHTCLICK, "impetus_rightclick", "rightclick");
|
||||
impetus(HexBlocks.IMPETUS_LOOK, "impetus_look", "look");
|
||||
impetus(HexBlocks.IMPETUS_STOREDPLAYER, "impetus_storedplayer", "storedplayer");
|
||||
arrowCircleBlock(HexBlocks.EMPTY_IMPETUS, "empty_impetus", modLoc("block/slate"),
|
||||
"impetus/front_empty",
|
||||
"impetus/back_empty",
|
||||
"impetus/up_empty",
|
||||
"impetus/down_empty",
|
||||
"impetus/left_empty",
|
||||
"impetus/right_empty"
|
||||
);
|
||||
|
||||
// auugh
|
||||
getVariantBuilder(HexBlocks.DIRECTRIX_REDSTONE).forAllStates(bs -> {
|
||||
var isLit = bs.getValue(BlockCircleComponent.ENERGIZED);
|
||||
var litness = isLit ? "lit" : "dim";
|
||||
var isPowered = bs.getValue(BlockRedstoneDirectrix.REDSTONE_POWERED);
|
||||
var poweredness = isPowered ? "powered" : "unpowered";
|
||||
var dir = bs.getValue(BlockStateProperties.FACING);
|
||||
|
||||
var up = modLoc("block/directrix/redstone/up_" + poweredness + "_" + litness);
|
||||
var left = modLoc("block/directrix/redstone/left_" + poweredness + "_" + litness);
|
||||
var right = modLoc("block/directrix/redstone/right_" + poweredness + "_" + litness);
|
||||
var down = modLoc("block/directrix/redstone/down_" + poweredness + "_" + litness);
|
||||
var front = modLoc("block/directrix/redstone/front_" + litness);
|
||||
var back = modLoc("block/directrix/redstone/back_" + poweredness);
|
||||
|
||||
var routing = routeReslocsForArrowBlock(dir, front, back, up, down, left, right);
|
||||
|
||||
var modelName = "redstone_directrix_" + poweredness + "_" + litness + "_" + dir.getName();
|
||||
var model = models().cube(modelName, routing[0], routing[1], routing[2], routing[3], routing[4], routing[5])
|
||||
.texture("particle", modLoc("block/slate"));
|
||||
if (!isLit && !isPowered && dir == Direction.NORTH) {
|
||||
simpleBlockItem(HexBlocks.DIRECTRIX_REDSTONE, model);
|
||||
}
|
||||
return ConfiguredModel.builder()
|
||||
.modelFile(model)
|
||||
.build();
|
||||
});
|
||||
getVariantBuilder(HexBlocks.EMPTY_DIRECTRIX).forAllStates(bs -> {
|
||||
var isLit = bs.getValue(BlockCircleComponent.ENERGIZED);
|
||||
var litness = isLit ? "lit" : "dim";
|
||||
var axis = bs.getValue(BlockStateProperties.AXIS);
|
||||
|
||||
var horiz = modLoc("block/directrix/empty/horiz_" + litness);
|
||||
var vert = modLoc("block/directrix/empty/vert_" + litness);
|
||||
var end = modLoc("block/directrix/empty/end_" + litness);
|
||||
|
||||
ResourceLocation x = null, y = null, z = null;
|
||||
switch (axis) {
|
||||
case X -> {
|
||||
x = end;
|
||||
y = horiz;
|
||||
z = horiz;
|
||||
}
|
||||
case Y -> {
|
||||
x = vert;
|
||||
y = end;
|
||||
z = vert;
|
||||
}
|
||||
case Z -> {
|
||||
x = horiz;
|
||||
y = vert;
|
||||
z = end;
|
||||
}
|
||||
}
|
||||
|
||||
var modelName = "empty_directrix_" + litness + "_" + axis.getName();
|
||||
var model = models().cube(modelName, y, y, z, z, x, x)
|
||||
.texture("particle", modLoc("block/slate"));
|
||||
if (!isLit && axis == Direction.Axis.Z) {
|
||||
simpleBlockItem(HexBlocks.EMPTY_DIRECTRIX, model);
|
||||
}
|
||||
return ConfiguredModel.builder()
|
||||
.modelFile(model)
|
||||
.build();
|
||||
});
|
||||
|
||||
var akashicRecordModel = models().getExistingFile(modLoc("block/akashic_record"));
|
||||
simpleBlock(HexBlocks.AKASHIC_RECORD, akashicRecordModel);
|
||||
|
@ -236,99 +139,58 @@ public class HexBlockStatesAndModels extends PaucalBlockStateAndModelProvider {
|
|||
simpleBlock(HexBlocks.CONJURED_LIGHT, conjuredModel);
|
||||
}
|
||||
|
||||
private void impetus(Block block, String name, String stub) {
|
||||
arrowCircleBlock(block, name, modLoc("block/slate"),
|
||||
"impetus/" + stub,
|
||||
"impetus/back",
|
||||
"impetus/up",
|
||||
"impetus/down",
|
||||
"impetus/left",
|
||||
"impetus/right"
|
||||
);
|
||||
}
|
||||
private void sidedCircleWidget(BlockSidedCircleWidget widget, String commonName, String coreName) {
|
||||
// We do really need to do this with a multipart builder. Unfortunately we cannot combine this with a variant builder,
|
||||
// so there's gonna be a *lot* of model files.
|
||||
// Each block needs a body + 4 connector models, for unenergized and energized versions, for non-margin
|
||||
// and margin versions. ("Margin" = the overlay model we stick on top, so some of the model rotates and some doesn't.)
|
||||
// Due to the third dimension being a mistake, we *also* need a version of the model that points up and one that points sideways.
|
||||
// This is because minecraft only accepts N quarters around the X axis and N quarters around the Y axis, and this doesn't
|
||||
// let us get all the 24 rotations we need.
|
||||
//
|
||||
// To avoid the generated folder becoming incredibly thicc and to save resource pack makers we put everything in a folder.
|
||||
//
|
||||
// Files are named {blockpath}/{part}['_' margin]['_' energized].
|
||||
// So, `toolsmith_impetus/body_energized` for the large chunk in the middle when active,
|
||||
// `farmer_locus/north_margin` for the north connector when rendering the overlay, etc.
|
||||
var cursor = getMultipartBuilder(widget);
|
||||
var parts = new String[]{
|
||||
"body", "north", "south", "east", "west"
|
||||
};
|
||||
for (var part : parts) {
|
||||
for (boolean energized : BlockSidedCircleWidget.ENERGIZED.getPossibleValues()) {
|
||||
String energizedStr = energized ? "" : "_energized";
|
||||
Boolean2ObjectFunction<BlockModelBuilder> modelMaker = (margin) -> {
|
||||
String marginStr = margin ? "" : "_margin";
|
||||
return models().withExistingParent(widget.getRegistryName().getPath() + "/" + part
|
||||
+ marginStr + energizedStr,
|
||||
modLoc("template_sided_circle_widget_" + part))
|
||||
.texture("common", modLoc("block/circle/" + commonName + energizedStr))
|
||||
.texture("core", modLoc("block/circle/" + coreName + marginStr + energizedStr));
|
||||
|
||||
private void arrowCircleBlock(Block block, String name, ResourceLocation particle, String frontStub,
|
||||
String backStub, String upStub, String downStub, String leftStub, String rightStub) {
|
||||
getVariantBuilder(block).forAllStates(bs -> {
|
||||
var isLit = bs.getValue(BlockCircleComponent.ENERGIZED);
|
||||
var litness = isLit ? "lit" : "dim";
|
||||
var dir = bs.getValue(BlockStateProperties.FACING);
|
||||
};
|
||||
var unmarginModel = modelMaker.apply(false);
|
||||
var marginModel = modelMaker.apply(true);
|
||||
|
||||
var up = modLoc("block/" + upStub + "_" + litness);
|
||||
var front = modLoc("block/" + frontStub + "_" + litness);
|
||||
var back = modLoc("block/" + backStub + "_" + litness);
|
||||
var left = modLoc("block/" + leftStub + "_" + litness);
|
||||
var right = modLoc("block/" + rightStub + "_" + litness);
|
||||
var down = modLoc("block/" + downStub + "_" + litness);
|
||||
for (var normal : Direction.values()) {
|
||||
// For each normal dir we add the base model, THEN the margin-textured model on top of it.
|
||||
// Phew.
|
||||
int xRot = normal == Direction.DOWN ? 180 : normal.getAxis().isHorizontal() ? 90 : 0;
|
||||
int yRot = normal.getAxis().isVertical() ? 0 : (int) normal.toYRot();
|
||||
cursor.part()
|
||||
.modelFile(unmarginModel)
|
||||
.rotationX(xRot)
|
||||
.rotationY(yRot)
|
||||
.nextModel();
|
||||
// Additionally rotate the margin model by the margin amount, about the normal vector ...
|
||||
// but we need to transform that into a sequence of X and Y rotations.
|
||||
// Uegh.
|
||||
for (var margin : Margin.values()) {
|
||||
|
||||
var routing = routeReslocsForArrowBlock(dir, front, back, up, down, left, right);
|
||||
|
||||
var modelName = name + "_" + litness + "_" + dir.getName();
|
||||
var model = models().cube(modelName, routing[0], routing[1], routing[2], routing[3], routing[4], routing[5])
|
||||
.texture("particle", particle);
|
||||
// Ordinarily i would use north, because north is the lower-right direction in the inv
|
||||
// and that's where other blocks face.
|
||||
// But impetuses are only distinguished by their front faces and I don't want it covered
|
||||
// by the number.
|
||||
if (!isLit && dir == Direction.EAST) {
|
||||
simpleBlockItem(block, model);
|
||||
}
|
||||
return ConfiguredModel.builder()
|
||||
.modelFile(model)
|
||||
.build();
|
||||
});
|
||||
}
|
||||
|
||||
private static ResourceLocation[] routeReslocsForArrowBlock(Direction dir, ResourceLocation front,
|
||||
ResourceLocation back,
|
||||
ResourceLocation up, ResourceLocation down,
|
||||
ResourceLocation left, ResourceLocation right) {
|
||||
ResourceLocation bottom = null, top = null, north = null, south = null, east = null, west = null;
|
||||
switch (dir) {
|
||||
case UP -> {
|
||||
top = front;
|
||||
bottom = back;
|
||||
north = east = south = west = up;
|
||||
}
|
||||
case DOWN -> {
|
||||
bottom = front;
|
||||
top = back;
|
||||
north = east = south = west = down;
|
||||
}
|
||||
case NORTH -> {
|
||||
north = front;
|
||||
south = back;
|
||||
west = left;
|
||||
east = right;
|
||||
top = up;
|
||||
bottom = down;
|
||||
}
|
||||
case SOUTH -> {
|
||||
south = front;
|
||||
north = back;
|
||||
west = right;
|
||||
east = left;
|
||||
top = down;
|
||||
bottom = up;
|
||||
}
|
||||
case WEST -> {
|
||||
west = front;
|
||||
east = back;
|
||||
north = right;
|
||||
south = left;
|
||||
top = left;
|
||||
bottom = left;
|
||||
}
|
||||
case EAST -> {
|
||||
east = front;
|
||||
west = back;
|
||||
north = left;
|
||||
south = right;
|
||||
top = right;
|
||||
bottom = right;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return new ResourceLocation[]{bottom, top, north, south, east, west};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|