truffalo, hoverboard first drafts, underside music disc

This commit is contained in:
craisinlord committed 2026-08-23 01:40:03 -05:00
1 parent 5596a9187d
commit 0b75b1ab1d
554 files changed
+14434 -512

No files matched your search

@@ -0,0 +1,76 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity;
import com.mojang.serialization.MapCodec;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.RenderShape;
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.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public class PortalGunPortalBaseBlock extends BaseEntityBlock {
private static final VoxelShape EMPTY = Shapes.empty();
private final Supplier<? extends BlockEntityType<PortalGunPortalBaseBlockEntity>> blockEntityTypeSupplier;
private final MapCodec<PortalGunPortalBaseBlock> codec;
public PortalGunPortalBaseBlock(Supplier<? extends BlockEntityType<PortalGunPortalBaseBlockEntity>> blockEntityTypeSupplier, BlockBehaviour.Properties properties) {
super(properties);
this.blockEntityTypeSupplier = blockEntityTypeSupplier;
this.codec = Block.simpleCodec(ignored -> this);
}
@Override
public MapCodec<PortalGunPortalBaseBlock> codec() {
return this.codec;
}
@Override
protected RenderShape getRenderShape(BlockState state) {
return RenderShape.INVISIBLE;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return EMPTY;
}
@Override
protected VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return EMPTY;
}
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return new PortalGunPortalBaseBlockEntity(pos, state, this.blockEntityTypeSupplier);
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (level.isClientSide) {
return null;
}
return createTickerHelper(blockEntityType, this.blockEntityTypeSupplier.get(), (tickLevel, tickPos, tickState, blockEntity) -> {
if (tickLevel instanceof net.minecraft.server.level.ServerLevel serverLevel) {
PortalGunPortalBaseBlockEntity.serverTick(serverLevel, tickPos, tickState, blockEntity);
}
});
}
@Override
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock()) && level.getBlockEntity(pos) instanceof PortalGunPortalBaseBlockEntity blockEntity) {
blockEntity.onBroken();
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
}
@@ -0,0 +1,76 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity;
import com.mojang.serialization.MapCodec;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.RenderShape;
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.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public class PortalGunPortalMasterBlock extends BaseEntityBlock {
private static final VoxelShape EMPTY = Shapes.empty();
private final Supplier<? extends BlockEntityType<PortalGunPortalMasterBlockEntity>> blockEntityTypeSupplier;
private final MapCodec<PortalGunPortalMasterBlock> codec;
public PortalGunPortalMasterBlock(Supplier<? extends BlockEntityType<PortalGunPortalMasterBlockEntity>> blockEntityTypeSupplier, BlockBehaviour.Properties properties) {
super(properties);
this.blockEntityTypeSupplier = blockEntityTypeSupplier;
this.codec = Block.simpleCodec(ignored -> this);
}
@Override
public MapCodec<PortalGunPortalMasterBlock> codec() {
return this.codec;
}
@Override
protected RenderShape getRenderShape(BlockState state) {
return RenderShape.INVISIBLE;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return EMPTY;
}
@Override
protected VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return EMPTY;
}
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return new PortalGunPortalMasterBlockEntity(pos, state, this.blockEntityTypeSupplier);
}
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (level.isClientSide) {
return null;
}
return createTickerHelper(blockEntityType, this.blockEntityTypeSupplier.get(), (tickLevel, tickPos, tickState, blockEntity) -> {
if (tickLevel instanceof net.minecraft.server.level.ServerLevel serverLevel) {
PortalGunPortalMasterBlockEntity.serverTick(serverLevel, tickPos, tickState, blockEntity);
}
});
}
@Override
protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) {
if (!state.is(newState.getBlock()) && level.getBlockEntity(pos) instanceof PortalGunPortalMasterBlockEntity blockEntity) {
blockEntity.onBroken();
}
super.onRemove(state, level, pos, newState, movedByPiston);
}
}
@@ -0,0 +1,60 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.BlockGetter;
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.Blocks;
import net.minecraft.world.level.block.BushBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class RoyalSaplingBlock extends BushBlock {
public static final MapCodec<RoyalSaplingBlock> CODEC = Block.simpleCodec(RoyalSaplingBlock::new);
private static final VoxelShape SHAPE = Block.box(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D);
public RoyalSaplingBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@Override
public MapCodec<RoyalSaplingBlock> codec() {
return CODEC;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
}
@Override
protected boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
return state.is(BlockTags.DIRT);
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
BlockState belowState = level.getBlockState(pos.below());
return belowState.is(BlockTags.DIRT);
}
@Override
protected BlockState updateShape(
BlockState state,
Direction direction,
BlockState neighborState,
LevelAccessor level,
BlockPos pos,
BlockPos neighborPos
) {
return state.canSurvive(level, pos)
? super.updateShape(state, direction, neighborState, level, pos, neighborPos)
: Blocks.AIR.defaultBlockState();
}
}
@@ -0,0 +1,52 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.RotatedPillarBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class TruffaloLogBlock extends RotatedPillarBlock {
public static final MapCodec<TruffaloLogBlock> CODEC = Block.simpleCodec(TruffaloLogBlock::new);
private static final VoxelShape SHAPE_Y = Block.box(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D);
private static final VoxelShape SHAPE_X = Block.box(0.0D, 4.0D, 4.0D, 16.0D, 12.0D, 12.0D);
private static final VoxelShape SHAPE_Z = Block.box(4.0D, 4.0D, 0.0D, 12.0D, 12.0D, 16.0D);
public TruffaloLogBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@Override
public MapCodec<TruffaloLogBlock> codec() {
return CODEC;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return shapeFor(state);
}
@Override
protected VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return shapeFor(state);
}
@Override
protected VoxelShape getOcclusionShape(BlockState state, BlockGetter level, BlockPos pos) {
return shapeFor(state);
}
private static VoxelShape shapeFor(BlockState state) {
return switch (state.getValue(AXIS)) {
case X -> SHAPE_X;
case Z -> SHAPE_Z;
default -> SHAPE_Y;
};
}
}
@@ -0,0 +1,110 @@
package com.craisinlord.antarchy.content.block;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.worldgen.truffalo.TruffaloTreeGrowers;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.TagKey;
import net.minecraft.util.RandomSource;
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.Blocks;
import net.minecraft.world.level.block.BonemealableBlock;
import net.minecraft.world.level.block.BushBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class TruffaloSaplingBlock extends BushBlock implements BonemealableBlock {
public static final MapCodec<TruffaloSaplingBlock> CODEC = Block.simpleCodec(TruffaloSaplingBlock::new);
private static final TagKey<Block> SUPPORTS = TagKey.create(
Registries.BLOCK,
ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "truffalo_sapling_supports")
);
private static final VoxelShape SHAPE = Block.box(2.0D, 4.0D, 2.0D, 14.0D, 16.0D, 14.0D);
public TruffaloSaplingBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@Override
public MapCodec<TruffaloSaplingBlock> codec() {
return CODEC;
}
@Override
protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return SHAPE;
}
@Override
protected boolean mayPlaceOn(BlockState state, BlockGetter level, BlockPos pos) {
return state.is(SUPPORTS);
}
@Override
protected boolean canSurvive(BlockState state, LevelReader level, BlockPos pos) {
BlockPos supportPos = pos.below();
BlockState supportState = level.getBlockState(supportPos);
return supportState.is(SUPPORTS) && supportState.isFaceSturdy(level, supportPos, Direction.UP);
}
@Override
protected void randomTick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
if (random.nextInt(7) == 0) {
this.growConfiguredTree(level, pos, state, random);
}
}
@Override
protected BlockState updateShape(
BlockState state,
Direction direction,
BlockState neighborState,
LevelAccessor level,
BlockPos pos,
BlockPos neighborPos
) {
return state.canSurvive(level, pos)
? super.updateShape(state, direction, neighborState, level, pos, neighborPos)
: Blocks.AIR.defaultBlockState();
}
@Override
public boolean isValidBonemealTarget(LevelReader level, BlockPos pos, BlockState state) {
return true;
}
@Override
public boolean isBonemealSuccess(Level level, RandomSource random, BlockPos pos, BlockState state) {
return random.nextFloat() < 0.45F;
}
@Override
public void performBonemeal(ServerLevel level, RandomSource random, BlockPos pos, BlockState state) {
this.growConfiguredTree(level, pos, state, random);
}
private void growConfiguredTree(ServerLevel level, BlockPos pos, BlockState state, RandomSource random) {
Holder.Reference<ConfiguredFeature<?, ?>> feature = level.registryAccess()
.lookupOrThrow(Registries.CONFIGURED_FEATURE)
.getOrThrow(TruffaloTreeGrowers.TRUFFALO_TREE);
level.removeBlock(pos, false);
if (feature.value().place(level, level.getChunkSource().getGenerator(), random, pos)) {
return;
}
level.setBlock(pos, state, Block.UPDATE_ALL);
}
}
@@ -0,0 +1,33 @@
package com.craisinlord.antarchy.content.block;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ShearsItem;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
public class TruffaloTuftBlock extends Block {
public static final MapCodec<TruffaloTuftBlock> CODEC = Block.simpleCodec(TruffaloTuftBlock::new);
private static final float SHEARS_DESTROY_SPEED = 10.0F;
public TruffaloTuftBlock(BlockBehaviour.Properties properties) {
super(properties);
}
@Override
public MapCodec<TruffaloTuftBlock> codec() {
return CODEC;
}
@Override
protected float getDestroyProgress(BlockState state, Player player, BlockGetter level, BlockPos pos) {
if (player.getMainHandItem().getItem() instanceof ShearsItem) {
return SHEARS_DESTROY_SPEED;
}
return super.getDestroyProgress(state, player, level, pos);
}
}
@@ -0,0 +1,148 @@
package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
public class PortalGunPortalBaseBlockEntity extends BlockEntity {
private UUID ownerId;
private UUID portalId;
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
private BlockPos masterPos = BlockPos.ZERO;
private UUID linkedPortalId;
private int pairTime;
public PortalGunPortalBaseBlockEntity(BlockPos pos, BlockState state, Supplier<? extends BlockEntityType<PortalGunPortalBaseBlockEntity>> typeSupplier) {
super(typeSupplier.get(), pos, state);
}
public void configure(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side, BlockPos masterPos) {
this.ownerId = ownerId;
this.portalId = portalId;
this.side = side;
this.masterPos = masterPos.immutable();
this.setChanged();
}
public UUID getOwnerId() {
return this.ownerId;
}
public UUID getPortalId() {
return this.portalId;
}
public PortalGunPortalEntity.PortalSide getSide() {
return this.side;
}
public BlockPos getMasterPos() {
return this.masterPos;
}
public UUID getLinkedPortalId() {
return this.linkedPortalId;
}
public int getPairTime() {
return this.pairTime;
}
public void updatePair(UUID linkedPortalId, int pairTime) {
this.linkedPortalId = linkedPortalId;
this.pairTime = pairTime;
this.setChanged();
}
public boolean matches(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side) {
return ownerId != null && portalId != null && ownerId.equals(this.ownerId) && portalId.equals(this.portalId) && this.side == side;
}
public void onBroken() {
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
return;
}
if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
portal.discard();
}
}
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalBaseBlockEntity blockEntity) {
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
return;
}
if (!(level.getBlockEntity(blockEntity.masterPos) instanceof PortalGunPortalMasterBlockEntity master)
|| !master.matches(blockEntity.ownerId, blockEntity.portalId, blockEntity.side)
|| !pos.equals(master.getBasePos())) {
level.removeBlock(pos, false);
return;
}
if (!(level.getEntity(blockEntity.portalId) instanceof PortalGunPortalEntity portal)
|| portal.isRemoved()
|| !pos.equals(portal.getBasePos())
|| blockEntity.ownerId == null
|| !blockEntity.ownerId.equals(portal.getOwnerId())
|| blockEntity.side != portal.getPortalSide()) {
level.removeBlock(pos, false);
return;
}
UUID linkedPortalId = portal.getLinkedPortalId();
if ((linkedPortalId == null && blockEntity.linkedPortalId != null)
|| (linkedPortalId != null && !linkedPortalId.equals(blockEntity.linkedPortalId))
|| blockEntity.pairTime != portal.getPairTime()) {
blockEntity.linkedPortalId = linkedPortalId;
blockEntity.pairTime = portal.getPairTime();
blockEntity.setChanged();
}
}
@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
if (this.ownerId != null) {
tag.putUUID("OwnerId", this.ownerId);
}
if (this.portalId != null) {
tag.putUUID("PortalId", this.portalId);
}
if (this.linkedPortalId != null) {
tag.putUUID("LinkedPortalId", this.linkedPortalId);
}
tag.putInt("Side", this.side.ordinal());
tag.putInt("MasterX", this.masterPos.getX());
tag.putInt("MasterY", this.masterPos.getY());
tag.putInt("MasterZ", this.masterPos.getZ());
tag.putInt("PairTime", this.pairTime);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null;
this.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null;
this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ"));
this.pairTime = tag.getInt("PairTime");
}
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
return this.saveWithoutMetadata(registries);
}
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
}
@@ -0,0 +1,300 @@
package com.craisinlord.antarchy.content.block.entity;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.HolderLookup;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
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 net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
public class PortalGunPortalMasterBlockEntity extends BlockEntity {
private UUID ownerId;
private UUID portalId;
private PortalGunPortalEntity.PortalSide side = PortalGunPortalEntity.PortalSide.BLUE;
private Direction facing = Direction.NORTH;
private Direction upAxis = Direction.UP;
private BlockPos basePos = BlockPos.ZERO;
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
private Set<BlockPos> compensatedSpots = Set.of();
private UUID linkedPortalId;
private int pairTime;
public PortalGunPortalMasterBlockEntity(BlockPos pos, BlockState state, Supplier<? extends BlockEntityType<PortalGunPortalMasterBlockEntity>> typeSupplier) {
super(typeSupplier.get(), pos, state);
}
public void configure(
UUID ownerId,
UUID portalId,
PortalGunPortalEntity.PortalSide side,
Direction facing,
Direction upAxis,
BlockPos basePos,
BlockPos[] portalSpots,
Set<BlockPos> compensatedSpots,
int pairTime
) {
this.ownerId = ownerId;
this.portalId = portalId;
this.side = side;
this.facing = facing;
this.upAxis = upAxis;
this.basePos = basePos.immutable();
this.portalSpots = new BlockPos[] {portalSpots[0].immutable(), portalSpots[1].immutable()};
this.compensatedSpots = new HashSet<>(compensatedSpots);
this.pairTime = pairTime;
this.setChanged();
}
public UUID getPortalId() {
return this.portalId;
}
public UUID getOwnerId() {
return this.ownerId;
}
public PortalGunPortalEntity.PortalSide getSide() {
return this.side;
}
public Direction getFacing() {
return this.facing;
}
public Direction getUpAxis() {
return this.upAxis;
}
public BlockPos getBasePos() {
return this.basePos;
}
public BlockPos[] getPortalSpots() {
return new BlockPos[] {this.portalSpots[0], this.portalSpots[1]};
}
public Set<BlockPos> getCompensatedSpots() {
return Set.copyOf(this.compensatedSpots);
}
public int getPairTime() {
return this.pairTime;
}
public UUID getLinkedPortalId() {
return this.linkedPortalId;
}
public void updatePair(UUID linkedPortalId, int pairTime) {
this.linkedPortalId = linkedPortalId;
this.pairTime = pairTime;
this.setChanged();
}
public boolean matches(UUID ownerId, UUID portalId, PortalGunPortalEntity.PortalSide side) {
return ownerId != null && portalId != null && ownerId.equals(this.ownerId) && portalId.equals(this.portalId) && this.side == side;
}
public void onBroken() {
if (!(this.level instanceof ServerLevel serverLevel) || this.portalId == null) {
return;
}
if (serverLevel.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
portal.discard();
}
}
public static void serverTick(ServerLevel level, BlockPos pos, BlockState state, PortalGunPortalMasterBlockEntity blockEntity) {
if (((level.getGameTime() + pos.asLong()) & 15L) != 0L) {
return;
}
if (!blockEntity.hasValidFootprint(level, pos)) {
level.removeBlock(pos, false);
return;
}
PortalGunPortalEntity portal = blockEntity.ensurePortalEntity(level);
if (portal == null
|| portal.isRemoved()
|| !pos.equals(portal.getMasterPos())
|| blockEntity.ownerId == null
|| !blockEntity.ownerId.equals(portal.getOwnerId())
|| blockEntity.side != portal.getPortalSide()) {
level.removeBlock(pos, false);
return;
}
blockEntity.ensureLinkedPortal(level, portal);
UUID linkedPortalId = portal.getLinkedPortalId();
if ((linkedPortalId == null && blockEntity.linkedPortalId != null)
|| (linkedPortalId != null && !linkedPortalId.equals(blockEntity.linkedPortalId))
|| blockEntity.pairTime != portal.getPairTime()) {
blockEntity.linkedPortalId = linkedPortalId;
blockEntity.pairTime = portal.getPairTime();
blockEntity.setChanged();
}
}
private boolean hasValidFootprint(ServerLevel level, BlockPos pos) {
if (this.ownerId == null || this.portalId == null || !pos.equals(this.worldPosition)) {
return false;
}
if (!(level.getBlockEntity(this.basePos) instanceof PortalGunPortalBaseBlockEntity base)
|| !base.matches(this.ownerId, this.portalId, this.side)
|| !pos.equals(base.getMasterPos())) {
return false;
}
List<BlockPos> supportPositions = new ArrayList<>(2);
for (BlockPos portalSpot : this.portalSpots) {
if (portalSpot == null || BlockPos.ZERO.equals(portalSpot)) {
return false;
}
supportPositions.add(portalSpot.relative(this.facing.getOpposite()));
}
for (BlockPos supportPos : supportPositions) {
BlockState supportState = level.getBlockState(supportPos);
if (!supportState.isFaceSturdy(level, supportPos, this.facing)) {
return false;
}
}
return true;
}
private PortalGunPortalEntity ensurePortalEntity(ServerLevel level) {
if (this.portalId == null || this.ownerId == null) {
return null;
}
if (level.getEntity(this.portalId) instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
return portal;
}
EntityType<?> rawType = BuiltInRegistries.ENTITY_TYPE.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "portal_gun_portal"));
if (!(rawType instanceof EntityType<?>)) {
return null;
}
PortalGunPlacement placement = PortalGunPlacement.fromStored(this.facing, this.upAxis, this.worldPosition, this.basePos, this.compensatedSpots);
@SuppressWarnings("unchecked")
PortalGunPortalEntity restored = new PortalGunPortalEntity((EntityType<? extends PortalGunPortalEntity>) rawType, level);
restored.setUUID(this.portalId);
restored.configure(this.ownerId, this.side, placement);
restored.restorePair(this.linkedPortalId, this.pairTime);
Vec3 center = placement.center();
restored.moveTo(center.x, center.y, center.z, placement.yaw(), 0.0F);
level.addFreshEntity(restored);
PortalGunSavedData.setPortal(level.getServer(), this.ownerId, this.side, restored.getUUID());
return restored;
}
private void ensureLinkedPortal(ServerLevel level, PortalGunPortalEntity portal) {
if (this.ownerId == null) {
return;
}
PortalGunPortalEntity counterpart = PortalGunSavedData.findLoadedPortal(
level.getServer(),
this.ownerId,
this.side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE
);
if (counterpart == null || counterpart == portal || counterpart.isRemoved()) {
return;
}
if (!counterpart.getUUID().equals(portal.getLinkedPortalId()) || !portal.getUUID().equals(counterpart.getLinkedPortalId())) {
portal.linkTo(counterpart);
counterpart.linkTo(portal);
}
}
@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
if (this.ownerId != null) {
tag.putUUID("OwnerId", this.ownerId);
}
if (this.portalId != null) {
tag.putUUID("PortalId", this.portalId);
}
if (this.linkedPortalId != null) {
tag.putUUID("LinkedPortalId", this.linkedPortalId);
}
tag.putInt("Side", this.side.ordinal());
tag.putInt("Facing", this.facing.get3DDataValue());
tag.putInt("UpAxis", this.upAxis.get3DDataValue());
tag.putInt("BaseX", this.basePos.getX());
tag.putInt("BaseY", this.basePos.getY());
tag.putInt("BaseZ", this.basePos.getZ());
tag.putInt("PairTime", this.pairTime);
ListTag spots = new ListTag();
for (BlockPos portalSpot : this.portalSpots) {
CompoundTag spot = new CompoundTag();
spot.putInt("X", portalSpot.getX());
spot.putInt("Y", portalSpot.getY());
spot.putInt("Z", portalSpot.getZ());
spots.add(spot);
}
tag.put("PortalSpots", spots);
ListTag compensated = new ListTag();
for (BlockPos compensatedSpot : this.compensatedSpots) {
CompoundTag spot = new CompoundTag();
spot.putInt("X", compensatedSpot.getX());
spot.putInt("Y", compensatedSpot.getY());
spot.putInt("Z", compensatedSpot.getZ());
compensated.add(spot);
}
tag.put("CompensatedSpots", compensated);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
this.ownerId = tag.hasUUID("OwnerId") ? tag.getUUID("OwnerId") : null;
this.portalId = tag.hasUUID("PortalId") ? tag.getUUID("PortalId") : null;
this.linkedPortalId = tag.hasUUID("LinkedPortalId") ? tag.getUUID("LinkedPortalId") : null;
this.side = tag.getInt("Side") == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
this.facing = Direction.from3DDataValue(tag.getInt("Facing"));
this.upAxis = Direction.from3DDataValue(tag.getInt("UpAxis"));
this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ"));
this.pairTime = tag.getInt("PairTime");
ListTag spots = tag.getList("PortalSpots", Tag.TAG_COMPOUND);
for (int i = 0; i < Math.min(2, spots.size()); i++) {
CompoundTag spot = spots.getCompound(i);
this.portalSpots[i] = new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z"));
}
ListTag compensated = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND);
Set<BlockPos> compensatedSpots = new HashSet<>();
for (int i = 0; i < compensated.size(); i++) {
CompoundTag spot = compensated.getCompound(i);
compensatedSpots.add(new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z")));
}
this.compensatedSpots = compensatedSpots;
}
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
return this.saveWithoutMetadata(registries);
}
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
}
@@ -0,0 +1,75 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import java.util.ArrayDeque;
import java.util.Deque;
public final class PortalGunPortalRenderState {
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_DESTINATION_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Deque<PortalGunWorldPortalShape>> ACTIVE_SOURCE_SHAPES = ThreadLocal.withInitial(ArrayDeque::new);
private static final ThreadLocal<Integer> RENDER_ALL_DEPTH = ThreadLocal.withInitial(() -> 0);
private PortalGunPortalRenderState() {
}
public static void pushPortalView(PortalGunWorldPortalShape sourceShape, PortalGunWorldPortalShape destinationShape, boolean renderAll) {
ACTIVE_SOURCE_SHAPES.get().push(sourceShape);
ACTIVE_DESTINATION_SHAPES.get().push(destinationShape);
if (renderAll) {
RENDER_ALL_DEPTH.set(RENDER_ALL_DEPTH.get() + 1);
}
}
public static void popPortalView(boolean renderAll) {
Deque<PortalGunWorldPortalShape> sourceShapes = ACTIVE_SOURCE_SHAPES.get();
if (!sourceShapes.isEmpty()) {
sourceShapes.pop();
}
if (sourceShapes.isEmpty()) {
ACTIVE_SOURCE_SHAPES.remove();
}
Deque<PortalGunWorldPortalShape> destinationShapes = ACTIVE_DESTINATION_SHAPES.get();
if (!destinationShapes.isEmpty()) {
destinationShapes.pop();
}
if (destinationShapes.isEmpty()) {
ACTIVE_DESTINATION_SHAPES.remove();
}
if (renderAll) {
int depth = Math.max(0, RENDER_ALL_DEPTH.get() - 1);
if (depth == 0) {
RENDER_ALL_DEPTH.remove();
} else {
RENDER_ALL_DEPTH.set(depth);
}
}
}
public static void pushDestinationShape(PortalGunWorldPortalShape shape) {
ACTIVE_DESTINATION_SHAPES.get().push(shape);
}
public static void popDestinationShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_DESTINATION_SHAPES.get();
if (!shapes.isEmpty()) {
shapes.pop();
}
if (shapes.isEmpty()) {
ACTIVE_DESTINATION_SHAPES.remove();
}
}
public static PortalGunWorldPortalShape getDestinationShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_DESTINATION_SHAPES.get();
return shapes.isEmpty() ? null : shapes.peek();
}
public static PortalGunWorldPortalShape getSourceShape() {
Deque<PortalGunWorldPortalShape> shapes = ACTIVE_SOURCE_SHAPES.get();
return shapes.isEmpty() ? null : shapes.peek();
}
public static boolean renderAll() {
return RENDER_ALL_DEPTH.get() > 0;
}
}
@@ -0,0 +1,108 @@
package com.craisinlord.antarchy.content.client;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import net.minecraft.client.Minecraft;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.joml.Quaternionf;
public final class PortalGunRollClientState {
private static final int TELEPORT_COOLDOWN_TICKS = 15;
private static final float ROLL_DECAY = 0.85F;
private static final float ROLL_EPSILON = 0.05F;
private static final Map<UUID, Long> TELEPORT_COOLDOWNS = new HashMap<>();
private static float previousRollDegrees;
private static float rollDegrees;
private PortalGunRollClientState() {
}
public static float getRollDegrees() {
return rollDegrees;
}
public static float getRollDegrees(float partialTick) {
return previousRollDegrees + (rollDegrees - previousRollDegrees) * partialTick;
}
public static boolean isEntityInsidePortal(Minecraft minecraft, Entity entity) {
if (minecraft.level == null || entity == null) {
return false;
}
for (Entity renderEntity : minecraft.level.entitiesForRendering()) {
if (renderEntity instanceof PortalGunPortalEntity portal && portal.isAlive() && portal.intersectsEntityBounds(entity)) {
return true;
}
}
return false;
}
public static boolean isCameraInsidePortal(Minecraft minecraft) {
Entity cameraEntity = minecraft.getCameraEntity();
return cameraEntity != null && isEntityInsidePortal(minecraft, cameraEntity);
}
public static void clear() {
previousRollDegrees = 0.0F;
rollDegrees = 0.0F;
TELEPORT_COOLDOWNS.clear();
}
public static void tick(Minecraft minecraft) {
if (minecraft.level == null || minecraft.player == null) {
clear();
return;
}
Entity cameraEntity = minecraft.getCameraEntity();
if (cameraEntity == null) {
return;
}
previousRollDegrees = rollDegrees;
rollDegrees *= ROLL_DECAY;
if (Math.abs(rollDegrees) < ROLL_EPSILON) {
rollDegrees = 0.0F;
}
long gameTime = minecraft.level.getGameTime();
TELEPORT_COOLDOWNS.entrySet().removeIf(entry -> entry.getValue() <= gameTime);
for (Entity entity : minecraft.level.entitiesForRendering()) {
if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) {
continue;
}
PortalGunPortalEntity linkedPortal = portal.getLinkedPortal();
if (linkedPortal == null || !linkedPortal.isAlive()) {
continue;
}
if (!portal.shouldRenderFront(cameraEntity.position())) {
continue;
}
if (!canTeleport(cameraEntity, gameTime)) {
continue;
}
if (portal.resolveCrossingProbe(cameraEntity) == null) {
continue;
}
applyPredictedTeleport(cameraEntity, portal, linkedPortal, gameTime);
break;
}
}
private static boolean canTeleport(Entity entity, long gameTime) {
if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger() || entity.isVehicle()) {
return false;
}
long cooldownUntil = TELEPORT_COOLDOWNS.getOrDefault(entity.getUUID(), 0L);
return cooldownUntil <= gameTime;
}
private static void applyPredictedTeleport(Entity entity, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, long gameTime) {
Quaternionf transform = PortalGunTransformUtil.createTransform(sourcePortal, destinationPortal);
Vec3 transformedLook = PortalGunTransformUtil.transform(entity.getLookAngle(), transform).normalize();
Vec3 transformedUp = PortalGunTransformUtil.transform(PortalGunTransformUtil.upVectorFromLookAndRoll(entity.getLookAngle(), rollDegrees), transform).normalize();
rollDegrees = PortalGunTransformUtil.rollFromOrientation(transformedLook, transformedUp);
TELEPORT_COOLDOWNS.put(entity.getUUID(), gameTime + TELEPORT_COOLDOWN_TICKS);
}
}
@@ -0,0 +1,32 @@
package com.craisinlord.antarchy.content.client.model;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.entity.HoverboardEntity;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.DyeColor;
import software.bernie.geckolib.model.GeoModel;
public class HoverboardModel extends GeoModel<HoverboardEntity> {
private static final ResourceLocation MODEL = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/hoverboard.geo.json");
private static final ResourceLocation ANIMATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/hoverboard.animation.json");
private static final ResourceLocation BASE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/hoverboard/hoverboard.png");
@Override
public ResourceLocation getModelResource(HoverboardEntity animatable) {
return MODEL;
}
@Override
public ResourceLocation getTextureResource(HoverboardEntity animatable) {
DyeColor color = animatable.getColor();
if (color == null) {
return BASE_TEXTURE;
}
return ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/entity/hoverboard/hoverboard_" + color.getName() + ".png");
}
@Override
public ResourceLocation getAnimationResource(HoverboardEntity animatable) {
return ANIMATION;
}
}
@@ -0,0 +1,13 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.model.HoverboardModel;
import com.craisinlord.antarchy.content.entity.HoverboardEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class HoverboardRenderer extends GeoEntityRenderer<HoverboardEntity> {
public HoverboardRenderer(EntityRendererProvider.Context context) {
super(context, new HoverboardModel());
this.shadowRadius = 0.5F;
}
}
@@ -3,13 +3,15 @@ package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.content.client.model.PortalGunPortalModel;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Axis;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEntity> {
@@ -20,13 +22,20 @@ public class PortalGunPortalRenderer extends GeoEntityRenderer<PortalGunPortalEn
@Override
public void render(PortalGunPortalEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
Direction facing = entity.getFacingDirection();
poseStack.pushPose();
if (facing.getAxis() == Direction.Axis.Y) {
poseStack.mulPose(Axis.XP.rotationDegrees(facing == Direction.UP ? -90.0F : 90.0F));
}
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, 0xF000F0);
poseStack.popPose();
}
@Override
protected void applyRotations(PortalGunPortalEntity animatable, PoseStack poseStack, float ageInTicks, float rotationYaw, float partialTick, float nativeScale) {
Vec3 width = animatable.getWidthVec();
Vec3 up = animatable.getUpVec();
Vec3 normal = animatable.getNormalVec();
Matrix3f basis = new Matrix3f(
(float) width.x, (float) up.x, (float) -normal.x,
(float) width.y, (float) up.y, (float) -normal.y,
(float) width.z, (float) up.z, (float) -normal.z
);
poseStack.mulPose(basis.getNormalizedRotation(new Quaternionf()));
}
@Override
@@ -0,0 +1,410 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.content.portalgun.PortalGunTransformUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.mojang.blaze3d.platform.GlStateManager;
import com.mojang.blaze3d.pipeline.TextureTarget;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferBuilder;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.MeshData;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexFormat;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.joml.Quaternionf;
public final class PortalGunPortalViewRenderer {
private static final int MAX_RECURSION_DEPTH = 3;
private static final double SURFACE_OFFSET = 0.03125D;
private static final double CAMERA_OFFSET = 0.0625D;
private static final double OVERLAY_OFFSET = 0.03135D;
private static final ResourceLocation BLUE_OVERLAY = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png");
private static final ResourceLocation ORANGE_OVERLAY = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
private static final TextureTarget[] PORTAL_TARGETS = new TextureTarget[MAX_RECURSION_DEPTH + 1];
private static boolean renderingPortalView;
private PortalGunPortalViewRenderer() {
}
public static void render(Camera camera, Matrix4f poseMatrix, DeltaTracker tickCounter) {
if (renderingPortalView) {
return;
}
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.level == null || minecraft.gameRenderer == null || camera == null || poseMatrix == null || tickCounter == null) {
return;
}
Entity cameraEntity = minecraft.getCameraEntity();
if (cameraEntity == null) {
return;
}
List<PortalGunPortalEntity> portals = collectVisiblePortals(minecraft, camera);
if (portals.isEmpty()) {
return;
}
ensureTargets(minecraft);
if (PORTAL_TARGETS[0] == null) {
return;
}
Vec3 cameraPos = camera.getPosition();
Vec3 rootLook = new Vec3(camera.getLookVector()).normalize();
Vec3 rootUp = new Vec3(camera.getUpVector()).normalize();
Matrix4f rootViewMatrix = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(rootLook, rootUp).conjugate(new Quaternionf()));
RenderContext rootContext = new RenderContext(cameraPos, rootLook, rootUp, rootViewMatrix);
renderingPortalView = true;
RenderSystem.backupProjectionMatrix();
for (PortalGunPortalEntity portal : portals) {
PortalGunPortalEntity linkedPortal = portal.getLinkedPortal();
if (linkedPortal == null || !linkedPortal.isAlive()) {
continue;
}
int textureId = renderPortalSurface(minecraft, cameraEntity, tickCounter, rootContext, portal, linkedPortal, 0);
minecraft.getMainRenderTarget().bindWrite(true);
drawPortalQuad(cameraPos, poseMatrix, portal, textureId);
drawPortalOverlay(minecraft, cameraPos, poseMatrix, portal);
}
RenderSystem.restoreProjectionMatrix();
renderingPortalView = false;
}
private static List<PortalGunPortalEntity> collectVisiblePortals(Minecraft minecraft, Camera camera) {
return collectVisiblePortals(minecraft, camera.getPosition());
}
private static List<PortalGunPortalEntity> collectVisiblePortals(Minecraft minecraft, Vec3 cameraPos) {
List<PortalGunPortalEntity> portals = new ArrayList<>();
for (Entity entity : minecraft.level.entitiesForRendering()) {
if (!(entity instanceof PortalGunPortalEntity portal) || !portal.isAlive()) {
continue;
}
if (portal.getLinkedPortalId() == null) {
continue;
}
if (!portal.shouldRenderFront(cameraPos)) {
continue;
}
portals.add(portal);
}
portals.sort((left, right) -> Double.compare(left.distanceToSqr(cameraPos), right.distanceToSqr(cameraPos)));
return portals;
}
private static void ensureTargets(Minecraft minecraft) {
int width = minecraft.getWindow().getWidth();
int height = minecraft.getWindow().getHeight();
if (width <= 0 || height <= 0) {
return;
}
for (int i = 0; i < PORTAL_TARGETS.length; i++) {
TextureTarget target = PORTAL_TARGETS[i];
if (target == null) {
target = new TextureTarget(width, height, true, Minecraft.ON_OSX);
target.setClearColor(0.0F, 0.0F, 0.0F, 1.0F);
PORTAL_TARGETS[i] = target;
continue;
}
if (target.width != width || target.height != height) {
target.resize(width, height, Minecraft.ON_OSX);
}
}
}
private static int renderPortalSurface(
Minecraft minecraft,
Entity cameraEntity,
DeltaTracker tickCounter,
RenderContext currentContext,
PortalGunPortalEntity sourcePortal,
PortalGunPortalEntity destinationPortal,
int depth
) {
TextureTarget target = PORTAL_TARGETS[depth];
RenderContext transformedContext = transformContext(currentContext, sourcePortal, destinationPortal);
PortalCamera portalCamera = new PortalCamera();
portalCamera.setup(minecraft.level, cameraEntity, tickCounter.getGameTimeDeltaPartialTick(true), transformedContext.cameraPos(), transformedContext.look(), transformedContext.up());
target.bindWrite(true);
target.clear(Minecraft.ON_OSX);
double fov = minecraft.options.fov().get();
Matrix4f baseProjectionMatrix = minecraft.gameRenderer.getProjectionMatrix(fov);
Matrix4f clippedProjectionMatrix = applyPortalClipPlane(baseProjectionMatrix, transformedContext, destinationPortal);
minecraft.gameRenderer.resetProjectionMatrix(clippedProjectionMatrix);
ScreenClip clip = computeScreenClip(minecraft, currentContext.cameraPos(), currentContext.viewMatrix(), baseProjectionMatrix, sourcePortal);
boolean clipEnabled = clip != null;
boolean renderAll = clip == null || clip.area() >= widthTimesHeight(minecraft);
if (clipEnabled) {
RenderSystem.enableScissor(clip.x(), clip.y(), clip.width(), clip.height());
}
PortalGunPortalRenderState.pushPortalView(sourcePortal.getWorldPortalShape(), destinationPortal.getWorldPortalShape(), renderAll);
try {
minecraft.levelRenderer.prepareCullFrustum(portalCamera.getPosition(), transformedContext.viewMatrix(), clippedProjectionMatrix);
minecraft.levelRenderer.renderLevel(tickCounter, false, portalCamera, minecraft.gameRenderer, minecraft.gameRenderer.lightTexture(), transformedContext.viewMatrix(), clippedProjectionMatrix);
if (depth < MAX_RECURSION_DEPTH) {
List<PortalGunPortalEntity> nestedPortals = collectVisiblePortals(minecraft, transformedContext.cameraPos());
Matrix4f nestedPose = new Matrix4f().identity();
for (PortalGunPortalEntity nestedPortal : nestedPortals) {
PortalGunPortalEntity nestedLinked = nestedPortal.getLinkedPortal();
if (nestedLinked == null || !nestedLinked.isAlive() || nestedPortal == destinationPortal || nestedPortal == sourcePortal) {
continue;
}
if (clip != null && clip.area() < 6400) {
continue;
}
int nestedTextureId = renderPortalSurface(minecraft, cameraEntity, tickCounter, transformedContext, nestedPortal, nestedLinked, depth + 1);
target.bindWrite(true);
drawPortalQuad(transformedContext.cameraPos(), nestedPose, nestedPortal, nestedTextureId);
drawPortalOverlay(minecraft, transformedContext.cameraPos(), nestedPose, nestedPortal);
}
}
} finally {
PortalGunPortalRenderState.popPortalView(renderAll);
}
if (clipEnabled) {
RenderSystem.disableScissor();
}
return target.getColorTextureId();
}
private static RenderContext transformContext(RenderContext currentContext, PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) {
Quaternionf transform = PortalGunTransformUtil.createTransform(sourcePortal, destinationPortal);
Vec3 destinationNormal = destinationPortal.getNormalVec().normalize();
Vec3 relativeEye = currentContext.cameraPos().subtract(sourcePortal.position());
double depthOffset = Math.max(CAMERA_OFFSET, relativeEye.dot(sourcePortal.getNormalVec().normalize()));
Vec3 transformedEyeOffset = PortalGunTransformUtil.transformRelativePosition(sourcePortal, destinationPortal, relativeEye);
Vec3 destinationEye = destinationPortal.position()
.add(transformedEyeOffset)
.add(destinationNormal.scale(depthOffset + CAMERA_OFFSET - transformedEyeOffset.dot(destinationNormal)));
Vec3 transformedLook = PortalGunTransformUtil.transform(currentContext.look(), transform).normalize();
Vec3 transformedUp = PortalGunTransformUtil.transform(currentContext.up(), transform).normalize();
Matrix4f viewMatrix = new Matrix4f().rotation(PortalGunTransformUtil.orientationQuaternion(transformedLook, transformedUp).conjugate(new Quaternionf()));
return new RenderContext(destinationEye, transformedLook, transformedUp, viewMatrix);
}
private static Matrix4f applyPortalClipPlane(Matrix4f projectionMatrix, RenderContext renderContext, PortalGunPortalEntity destinationPortal) {
Vec3 planePointWorld = destinationPortal.position().add(destinationPortal.getNormalVec().normalize().scale(0.01D));
Vec3 planeNormalWorld = destinationPortal.getNormalVec().normalize();
Vector4f clipPlane = portalPlaneToCameraSpace(renderContext, planePointWorld, planeNormalWorld);
if (clipPlane.lengthSquared() <= 1.0E-6F) {
return projectionMatrix;
}
Matrix4f clippedProjection = new Matrix4f(projectionMatrix);
Vector4f q = new Vector4f(
signNonZero(clipPlane.x()) / clippedProjection.m00(),
signNonZero(clipPlane.y()) / clippedProjection.m11(),
-1.0F,
(1.0F + clippedProjection.m22()) / clippedProjection.m32()
);
float scale = 2.0F / clipPlane.dot(q);
Vector4f c = clipPlane.mul(scale, new Vector4f());
clippedProjection.m02(c.x());
clippedProjection.m12(c.y());
clippedProjection.m22(c.z() + 1.0F);
clippedProjection.m32(c.w());
return clippedProjection;
}
private static Vector4f portalPlaneToCameraSpace(RenderContext renderContext, Vec3 planePointWorld, Vec3 planeNormalWorld) {
Vec3 look = renderContext.look().normalize();
Vec3 up = renderContext.up().normalize();
Vec3 right = up.cross(look).normalize();
Vec3 relativePoint = planePointWorld.subtract(renderContext.cameraPos());
Vector4f point = new Vector4f(
(float) relativePoint.dot(right),
(float) relativePoint.dot(up),
(float) -relativePoint.dot(look),
1.0F
);
Vector4f normal = new Vector4f(
(float) planeNormalWorld.dot(right),
(float) planeNormalWorld.dot(up),
(float) -planeNormalWorld.dot(look),
0.0F
);
if (normal.z() > 0.0F) {
normal.mul(-1.0F);
}
return new Vector4f(normal.x(), normal.y(), normal.z(), -(normal.x() * point.x() + normal.y() * point.y() + normal.z() * point.z()));
}
private static float signNonZero(float value) {
return value >= 0.0F ? 1.0F : -1.0F;
}
private static ScreenClip computeScreenClip(Minecraft minecraft, Vec3 cameraPos, Matrix4f viewMatrix, Matrix4f projectionMatrix, PortalGunPortalEntity portal) {
Vec3[] worldCorners = portal.getWorldPortalShape().getCorners(SURFACE_OFFSET);
int width = minecraft.getWindow().getWidth();
int height = minecraft.getWindow().getHeight();
float minX = Float.POSITIVE_INFINITY;
float minY = Float.POSITIVE_INFINITY;
float maxX = Float.NEGATIVE_INFINITY;
float maxY = Float.NEGATIVE_INFINITY;
boolean visible = false;
Matrix4f combined = new Matrix4f(projectionMatrix).mul(viewMatrix);
for (Vec3 worldCorner : worldCorners) {
Vec3 corner = worldCorner.subtract(cameraPos);
Vector4f projected = new Vector4f((float) corner.x, (float) corner.y, (float) corner.z, 1.0F).mul(combined);
if (projected.w <= 0.001F) {
continue;
}
float invW = 1.0F / projected.w;
float ndcX = projected.x * invW;
float ndcY = projected.y * invW;
if (ndcX < -1.25F || ndcX > 1.25F || ndcY < -1.25F || ndcY > 1.25F) {
continue;
}
visible = true;
float screenX = (ndcX * 0.5F + 0.5F) * width;
float screenY = (ndcY * 0.5F + 0.5F) * height;
minX = Math.min(minX, screenX);
minY = Math.min(minY, screenY);
maxX = Math.max(maxX, screenX);
maxY = Math.max(maxY, screenY);
}
if (!visible) {
return null;
}
int scissorX = Mth.clamp((int) Math.floor(minX), 0, width - 1);
int scissorMaxX = Mth.clamp((int) Math.ceil(maxX), scissorX + 1, width);
int scissorYTop = Mth.clamp((int) Math.floor(minY), 0, height - 1);
int scissorYBottom = Mth.clamp((int) Math.ceil(maxY), scissorYTop + 1, height);
int scissorY = height - scissorYBottom;
int scissorWidth = Math.max(1, scissorMaxX - scissorX);
int scissorHeight = Math.max(1, scissorYBottom - scissorYTop);
return new ScreenClip(scissorX, scissorY, scissorWidth, scissorHeight);
}
private static void drawPortalQuad(Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal, int textureId) {
drawTexturedPortal(cameraPos, poseMatrix, portal, textureId, SURFACE_OFFSET, 1.0F, 1.0F, 1.0F, 1.0F, false);
}
private static int widthTimesHeight(Minecraft minecraft) {
return minecraft.getWindow().getWidth() * minecraft.getWindow().getHeight();
}
private static void drawPortalOverlay(Minecraft minecraft, Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal) {
float time = (minecraft.level.getGameTime() + minecraft.getTimer().getGameTimeDeltaPartialTick(false)) * 0.075F;
float pulse = 0.72F + 0.18F * (float) Math.sin(time);
float edge = 0.38F + 0.08F * (float) Math.cos(time * 1.7F);
if (portal.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
drawTexturedPortal(cameraPos, poseMatrix, portal, BLUE_OVERLAY, OVERLAY_OFFSET, 0.78F, 0.94F, 1.0F, pulse, false);
drawTexturedPortal(cameraPos, poseMatrix, portal, BLUE_OVERLAY, OVERLAY_OFFSET + 0.0008D, 0.26F, 0.62F, 1.0F, edge, true);
return;
}
drawTexturedPortal(cameraPos, poseMatrix, portal, ORANGE_OVERLAY, OVERLAY_OFFSET, 1.0F, 0.84F, 0.46F, pulse, false);
drawTexturedPortal(cameraPos, poseMatrix, portal, ORANGE_OVERLAY, OVERLAY_OFFSET + 0.0008D, 1.0F, 0.48F, 0.16F, edge, true);
}
private static void drawTexturedPortal(Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal, ResourceLocation texture, double offset, float red, float green, float blue, float alpha, boolean additive) {
Vec3 widthVec = portal.getWidthVec().normalize().scale(0.5D);
Vec3 upVec = portal.getUpVec().normalize().scale(1.0D);
Vec3 normalVec = portal.getNormalVec().normalize().scale(offset);
Vec3 center = portal.position().add(normalVec);
Vec3 bottomLeft = center.subtract(widthVec).subtract(upVec).subtract(cameraPos);
Vec3 topLeft = center.subtract(widthVec).add(upVec).subtract(cameraPos);
Vec3 topRight = center.add(widthVec).add(upVec).subtract(cameraPos);
Vec3 bottomRight = center.add(widthVec).subtract(upVec).subtract(cameraPos);
RenderSystem.enableBlend();
if (additive) {
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
} else {
RenderSystem.defaultBlendFunc();
}
RenderSystem.depthMask(false);
RenderSystem.disableCull();
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, texture);
RenderSystem.setShaderColor(red, green, blue, alpha);
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX);
buffer.addVertex(poseMatrix, (float) bottomLeft.x, (float) bottomLeft.y, (float) bottomLeft.z).setUv(0.0F, 1.0F);
buffer.addVertex(poseMatrix, (float) topLeft.x, (float) topLeft.y, (float) topLeft.z).setUv(0.0F, 0.0F);
buffer.addVertex(poseMatrix, (float) topRight.x, (float) topRight.y, (float) topRight.z).setUv(1.0F, 0.0F);
buffer.addVertex(poseMatrix, (float) bottomRight.x, (float) bottomRight.y, (float) bottomRight.z).setUv(1.0F, 1.0F);
MeshData mesh = buffer.build();
if (mesh != null) {
BufferUploader.drawWithShader(mesh);
}
RenderSystem.enableCull();
RenderSystem.depthMask(true);
RenderSystem.disableBlend();
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
}
private static void drawTexturedPortal(Vec3 cameraPos, Matrix4f poseMatrix, PortalGunPortalEntity portal, int textureId, double offset, float red, float green, float blue, float alpha, boolean additive) {
Vec3 widthVec = portal.getWidthVec().normalize().scale(0.5D);
Vec3 upVec = portal.getUpVec().normalize().scale(1.0D);
Vec3 normalVec = portal.getNormalVec().normalize().scale(offset);
Vec3 center = portal.position().add(normalVec);
Vec3 bottomLeft = center.subtract(widthVec).subtract(upVec).subtract(cameraPos);
Vec3 topLeft = center.subtract(widthVec).add(upVec).subtract(cameraPos);
Vec3 topRight = center.add(widthVec).add(upVec).subtract(cameraPos);
Vec3 bottomRight = center.add(widthVec).subtract(upVec).subtract(cameraPos);
RenderSystem.enableBlend();
if (additive) {
RenderSystem.blendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO);
} else {
RenderSystem.defaultBlendFunc();
}
RenderSystem.depthMask(false);
RenderSystem.disableCull();
RenderSystem.setShader(GameRenderer::getPositionTexShader);
RenderSystem.setShaderTexture(0, textureId);
RenderSystem.setShaderColor(red, green, blue, alpha);
BufferBuilder buffer = Tesselator.getInstance().begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_TEX);
buffer.addVertex(poseMatrix, (float) bottomLeft.x, (float) bottomLeft.y, (float) bottomLeft.z).setUv(0.0F, 1.0F);
buffer.addVertex(poseMatrix, (float) topLeft.x, (float) topLeft.y, (float) topLeft.z).setUv(0.0F, 0.0F);
buffer.addVertex(poseMatrix, (float) topRight.x, (float) topRight.y, (float) topRight.z).setUv(1.0F, 0.0F);
buffer.addVertex(poseMatrix, (float) bottomRight.x, (float) bottomRight.y, (float) bottomRight.z).setUv(1.0F, 1.0F);
MeshData mesh = buffer.build();
if (mesh != null) {
BufferUploader.drawWithShader(mesh);
}
RenderSystem.enableCull();
RenderSystem.depthMask(true);
RenderSystem.disableBlend();
RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
}
private static final class PortalCamera extends Camera {
private void setup(net.minecraft.world.level.BlockGetter level, Entity cameraEntity, float partialTick, Vec3 position, Vec3 look, Vec3 up) {
super.setup(level, cameraEntity, false, false, partialTick);
Vec3 normalizedLook = look.normalize();
Vec3 normalizedUp = up.subtract(normalizedLook.scale(up.dot(normalizedLook))).normalize();
float yaw = PortalGunTransformUtil.yawFromLook(normalizedLook);
float pitch = PortalGunTransformUtil.pitchFromLook(normalizedLook);
this.setPosition(position);
this.setRotation(yaw, pitch);
Vec3 currentUp = new Vec3(this.getUpVector()).normalize();
if (currentUp.dot(normalizedUp) < 0.0D) {
this.setRotation(yaw + 180.0F, -pitch);
}
}
}
private record RenderContext(Vec3 cameraPos, Vec3 look, Vec3 up, Matrix4f viewMatrix) {
}
private record ScreenClip(int x, int y, int width, int height) {
private int area() {
return this.width * this.height;
}
}
}
@@ -0,0 +1,80 @@
package com.craisinlord.antarchy.content.client.renderer;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import org.joml.Matrix4f;
public class PortalGunProjectileRenderer extends EntityRenderer<PortalGunProjectileEntity> {
private static final ResourceLocation BLUE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_blue.png");
private static final ResourceLocation ORANGE_TEXTURE = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/vfx/portal_gun_portal_orange.png");
public PortalGunProjectileRenderer(EntityRendererProvider.Context context) {
super(context);
}
@Override
public void render(PortalGunProjectileEntity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) {
poseStack.pushPose();
poseStack.mulPose(this.entityRenderDispatcher.cameraOrientation());
float age = entity.tickCount + partialTick;
float pulse = 0.8F + 0.2F * (float) Math.sin(age * 0.9F);
float trailStretch = 1.1F + (float) Math.min(entity.getDeltaMovement().length() * 0.3D, 1.4D);
float baseScale = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.44F : 0.48F;
ResourceLocation texture = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
float red = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.55F : 1.0F;
float green = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 0.82F : 0.56F;
float blue = entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? 1.0F : 0.18F;
poseStack.scale(baseScale * trailStretch, baseScale, baseScale);
PoseStack.Pose pose = poseStack.last();
Matrix4f matrix = pose.pose();
VertexConsumer core = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
addQuad(core, pose, matrix, red, green, blue, 0.92F, packedLight, -0.9F, -0.5F, 0.9F, 0.5F);
VertexConsumer glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture));
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.8F * pulse, 0xF000F0, -1.15F, -0.7F, 1.15F, 0.7F);
poseStack.mulPose(com.mojang.math.Axis.ZP.rotationDegrees(90.0F));
pose = poseStack.last();
matrix = pose.pose();
core = bufferSource.getBuffer(RenderType.entityTranslucent(texture));
addQuad(core, pose, matrix, red, green, blue, 0.74F, packedLight, -0.45F, -0.95F, 0.45F, 0.95F);
glow = bufferSource.getBuffer(RenderType.entityTranslucentEmissive(texture));
addQuad(glow, pose, matrix, 1.0F, 1.0F, 1.0F, 0.52F * pulse, 0xF000F0, -0.62F, -1.15F, 0.62F, 1.15F);
poseStack.popPose();
}
@Override
protected int getBlockLightLevel(PortalGunProjectileEntity entity, BlockPos pos) {
return 15;
}
@Override
public ResourceLocation getTextureLocation(PortalGunProjectileEntity entity) {
return entity.getPortalSide() == com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity.PortalSide.BLUE ? BLUE_TEXTURE : ORANGE_TEXTURE;
}
private static void addQuad(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float red, float green, float blue, float alpha, int packedLight, float minX, float minY, float maxX, float maxY) {
addVertex(consumer, pose, matrix, minX, minY, 0.0F, 1.0F, red, green, blue, alpha, packedLight);
addVertex(consumer, pose, matrix, maxX, minY, 1.0F, 1.0F, red, green, blue, alpha, packedLight);
addVertex(consumer, pose, matrix, maxX, maxY, 1.0F, 0.0F, red, green, blue, alpha, packedLight);
addVertex(consumer, pose, matrix, minX, maxY, 0.0F, 0.0F, red, green, blue, alpha, packedLight);
}
private static void addVertex(VertexConsumer consumer, PoseStack.Pose pose, Matrix4f matrix, float x, float y, float u, float v, float red, float green, float blue, float alpha, int packedLight) {
consumer.addVertex(matrix, x, y, 0.0F)
.setColor(red, green, blue, alpha)
.setUv(u, v)
.setOverlay(OverlayTexture.NO_OVERLAY)
.setLight(packedLight)
.setNormal(pose, 0.0F, 0.0F, 1.0F);
}
}
@@ -72,6 +72,40 @@ public final class CreativeTabContents {
entry("peach_button", "building_blocks"),
entry("peach_sign", "building_blocks"),
entry("peach_hanging_sign", "building_blocks"),
entry("truffalo_log", "building_blocks"),
entry("truffalo_wood", "building_blocks"),
entry("stripped_truffalo_log", "building_blocks"),
entry("stripped_truffalo_wood", "building_blocks"),
entry("truffalo_planks", "building_blocks"),
entry("truffalo_stairs", "building_blocks"),
entry("truffalo_slab", "building_blocks"),
entry("truffalo_fence", "building_blocks"),
entry("truffalo_fence_gate", "building_blocks"),
entry("truffalo_boat", "building_blocks"),
entry("truffalo_chest_boat", "building_blocks"),
entry("truffalo_door", "building_blocks"),
entry("truffalo_trapdoor", "building_blocks"),
entry("truffalo_pressure_plate", "building_blocks"),
entry("truffalo_button", "building_blocks"),
entry("truffalo_sign", "building_blocks"),
entry("truffalo_hanging_sign", "building_blocks"),
entry("royal_log", "building_blocks"),
entry("royal_wood", "building_blocks"),
entry("stripped_royal_log", "building_blocks"),
entry("stripped_royal_wood", "building_blocks"),
entry("royal_planks", "building_blocks"),
entry("royal_stairs", "building_blocks"),
entry("royal_slab", "building_blocks"),
entry("royal_fence", "building_blocks"),
entry("royal_fence_gate", "building_blocks"),
entry("royal_boat", "building_blocks"),
entry("royal_chest_boat", "building_blocks"),
entry("royal_door", "building_blocks"),
entry("royal_trapdoor", "building_blocks"),
entry("royal_pressure_plate", "building_blocks"),
entry("royal_button", "building_blocks"),
entry("royal_sign", "building_blocks"),
entry("royal_hanging_sign", "building_blocks"),
entry("shellstone", "building_blocks"),
entry("shellstone_stairs", "building_blocks"),
entry("shellstone_slab", "building_blocks"),
@@ -254,8 +288,28 @@ public final class CreativeTabContents {
entry("ouranwood_acorn", "natural_blocks"),
entry("peach_leaves", "natural_blocks"),
entry("peach_sapling", "natural_blocks"),
entry("truffalo_sapling", "natural_blocks"),
entry("royal_leaves", "natural_blocks"),
entry("royal_flowering_leaves", "natural_blocks"),
entry("royal_sapling", "natural_blocks"),
entry("nadir_veil", "natural_blocks"),
entry("nadir_sapling", "natural_blocks"),
entry("white_truffalo_tuft", "natural_blocks"),
entry("orange_truffalo_tuft", "natural_blocks"),
entry("magenta_truffalo_tuft", "natural_blocks"),
entry("light_blue_truffalo_tuft", "natural_blocks"),
entry("yellow_truffalo_tuft", "natural_blocks"),
entry("lime_truffalo_tuft", "natural_blocks"),
entry("pink_truffalo_tuft", "natural_blocks"),
entry("gray_truffalo_tuft", "natural_blocks"),
entry("light_gray_truffalo_tuft", "natural_blocks"),
entry("cyan_truffalo_tuft", "natural_blocks"),
entry("purple_truffalo_tuft", "natural_blocks"),
entry("blue_truffalo_tuft", "natural_blocks"),
entry("brown_truffalo_tuft", "natural_blocks"),
entry("green_truffalo_tuft", "natural_blocks"),
entry("red_truffalo_tuft", "natural_blocks"),
entry("black_truffalo_tuft", "natural_blocks"),
entry("duplicator_log", "natural_blocks"),
entry("duplicator_sapling", "natural_blocks"),
entry("orange_milkweed", "natural_blocks"),
@@ -300,6 +354,7 @@ public final class CreativeTabContents {
entry("dead_star_coral_fan", "natural_blocks"),
entry("dead_star_coral_block", "natural_blocks"),
entry("duct_tape", "tools_and_utilities", "shears"),
entry("music_disc_underside", "tools_and_utilities"),
entry("diamond_minecart", "tools_and_utilities", "minecart"),
entry("bile_bucket", "tools_and_utilities"),
entry("ichor_bucket", "tools_and_utilities"),
@@ -419,6 +474,23 @@ public final class CreativeTabContents {
entry("big_bertha_blade", "ingredients"),
entry("big_bertha_handle", "ingredients"),
entry("big_bertha_hilt", "ingredients"),
entry("hoverboard", "building_blocks"),
entry("hoverboard_white", "building_blocks"),
entry("hoverboard_orange", "building_blocks"),
entry("hoverboard_magenta", "building_blocks"),
entry("hoverboard_light_blue", "building_blocks"),
entry("hoverboard_yellow", "building_blocks"),
entry("hoverboard_lime", "building_blocks"),
entry("hoverboard_pink", "building_blocks"),
entry("hoverboard_gray", "building_blocks"),
entry("hoverboard_light_gray", "building_blocks"),
entry("hoverboard_cyan", "building_blocks"),
entry("hoverboard_purple", "building_blocks"),
entry("hoverboard_blue", "building_blocks"),
entry("hoverboard_brown", "building_blocks"),
entry("hoverboard_green", "building_blocks"),
entry("hoverboard_red", "building_blocks"),
entry("hoverboard_black", "building_blocks"),
entry("easter_bunny_spawn_egg", "spawn_eggs"),
entry("flying_squirrel_spawn_egg", "spawn_eggs"),
entry("caterpillar_spawn_egg", "spawn_eggs"),
@@ -0,0 +1,243 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.mixins.LivingEntityJumpingAccessor;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
import software.bernie.geckolib.animation.AnimationController;
import software.bernie.geckolib.animation.AnimationState;
import software.bernie.geckolib.animation.PlayState;
import software.bernie.geckolib.animation.RawAnimation;
import software.bernie.geckolib.util.GeckoLibUtil;
public class HoverboardEntity extends PathfinderMob implements GeoEntity {
private static final EntityDataAccessor<Integer> COLOR = SynchedEntityData.defineId(HoverboardEntity.class, EntityDataSerializers.INT);
private static final RawAnimation IDLE_ANIM = RawAnimation.begin().thenLoop("idle");
private static final RawAnimation MOVE_ANIM = RawAnimation.begin().thenLoop("move");
private static final double MAX_HOVER_HEIGHT = 5.0D;
private static final double ACCELERATION = 0.08D;
private static final double MAX_SPEED = 1.8D;
private static final double ASCEND_SPEED = 0.15D;
private static final double DAMPING = 0.955D;
private static final double RIDER_Y_OFFSET = 0.65D;
private static final float RIDER_FORWARD_HEAD_OFFSET = 90.0F;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
public HoverboardEntity(EntityType<? extends HoverboardEntity> entityType, Level level) {
super(entityType, level);
this.setNoGravity(true);
this.setPersistenceRequired();
}
public static AttributeSupplier.Builder createAttributes() {
return Mob.createMobAttributes().add(Attributes.MAX_HEALTH, 6.0D);
}
@Override
public boolean hurt(DamageSource source, float amount) {
if (this.level().isClientSide || !this.isAlive()) {
return false;
}
if (this.isInvulnerableTo(source)) {
return false;
}
this.ejectPassengers();
this.playSound(SoundEvents.WOOD_BREAK, 1.0F, 1.0F);
this.spawnAtLocation(this.getDropStack());
this.discard();
return true;
}
@Override
protected void registerGoals() {
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder builder) {
super.defineSynchedData(builder);
builder.define(COLOR, -1);
}
@Nullable
public DyeColor getColor() {
int id = this.entityData.get(COLOR);
return id < 0 ? null : DyeColor.byId(id);
}
public void setColor(@Nullable DyeColor color) {
this.entityData.set(COLOR, color == null ? -1 : color.getId());
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
if (this.getColor() != null) {
tag.putString("Color", this.getColor().getName());
}
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.setColor(tag.contains("Color") ? DyeColor.byName(tag.getString("Color"), null) : null);
}
@Override
protected InteractionResult mobInteract(Player player, InteractionHand hand) {
if (!this.isVehicle() && !player.isSecondaryUseActive()) {
if (!this.level().isClientSide) {
player.startRiding(this);
}
return InteractionResult.sidedSuccess(this.level().isClientSide);
}
return super.mobInteract(player, hand);
}
@Override
@Nullable
public LivingEntity getControllingPassenger() {
Entity passenger = this.getFirstPassenger();
return passenger instanceof Player player ? player : null;
}
@Override
public boolean canAddPassenger(Entity passenger) {
return this.getPassengers().isEmpty();
}
@Override
protected void positionRider(Entity passenger, MoveFunction moveFunction) {
if (!this.hasPassenger(passenger)) {
return;
}
moveFunction.accept(passenger, this.getX(), this.getY() + RIDER_Y_OFFSET, this.getZ());
}
@Override
public void travel(Vec3 travelVector) {
if (this.getControllingPassenger() instanceof Player rider) {
this.setYRot(rider.getYRot());
this.yRotO = this.getYRot();
this.yBodyRot = this.getYRot();
this.yHeadRot = this.getYRot();
this.setXRot(0.0F);
rider.setYHeadRot(rider.getYRot() + RIDER_FORWARD_HEAD_OFFSET);
Vec3 look = Vec3.directionFromRotation(0.0F, this.getYRot());
float forward = rider.zza;
boolean holdingJump = ((LivingEntityJumpingAccessor) rider).antarchy$isJumping();
double ascend = holdingJump ? ASCEND_SPEED : (rider.isShiftKeyDown() ? -ASCEND_SPEED : 0.0D);
double groundHeight = this.findGroundHeight();
double maxY = groundHeight + MAX_HOVER_HEIGHT;
Vec3 currentHorizontal = new Vec3(this.getDeltaMovement().x, 0.0D, this.getDeltaMovement().z);
Vec3 horizontalMotion = currentHorizontal.scale(DAMPING).add(look.scale(forward * ACCELERATION));
double horizontalSpeed = horizontalMotion.horizontalDistance();
if (horizontalSpeed > MAX_SPEED) {
double scale = MAX_SPEED / horizontalSpeed;
horizontalMotion = new Vec3(horizontalMotion.x * scale, 0.0D, horizontalMotion.z * scale);
}
double verticalMotion = ascend;
if (this.getY() >= maxY && verticalMotion > 0.0D) {
verticalMotion = 0.0D;
}
if (this.getY() <= groundHeight && verticalMotion < 0.0D) {
verticalMotion = 0.0D;
}
this.setDeltaMovement(horizontalMotion.x, verticalMotion, horizontalMotion.z);
this.move(MoverType.SELF, this.getDeltaMovement());
return;
}
this.setDeltaMovement(this.getDeltaMovement().scale(DAMPING));
this.move(MoverType.SELF, this.getDeltaMovement());
}
private double findGroundHeight() {
BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos(Mth.floor(this.getX()), Mth.floor(this.getY()), Mth.floor(this.getZ()));
int minY = this.level().getMinBuildHeight();
while (pos.getY() > minY && this.level().getBlockState(pos).isAir()) {
pos.move(Direction.DOWN);
}
return pos.getY() + 1.0D;
}
@Override
public boolean causeFallDamage(float fallDistance, float multiplier, DamageSource source) {
return false;
}
@Override
public boolean removeWhenFarAway(double distanceToClosestPlayer) {
return false;
}
@Override
public void die(DamageSource damageSource) {
super.die(damageSource);
if (!this.level().isClientSide) {
this.spawnAtLocation(this.getDropStack());
}
}
public ItemStack getDropStack() {
String path = this.getColor() == null ? "hoverboard" : "hoverboard_" + this.getColor().getName();
Item item = BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path));
return new ItemStack(item);
}
@Override
public ItemStack getPickResult() {
return this.getDropStack();
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "movement", 0, this::movementController));
}
private PlayState movementController(AnimationState<HoverboardEntity> state) {
return state.setAndContinue(this.getDeltaMovement().horizontalDistanceSqr() > 0.0025D ? MOVE_ANIM : IDLE_ANIM);
}
@Override
public AnimatableInstanceCache getAnimatableInstanceCache() {
return this.geoCache;
}
}
@@ -0,0 +1,21 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
public class RoyalBoatEntity extends Boat {
public RoyalBoatEntity(EntityType<? extends Boat> entityType, Level level) {
super(entityType, level);
this.setVariant(Type.OAK);
}
@Override
public Item getDropItem() {
return BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "royal_boat"));
}
}
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.entity.vehicle.ChestBoat;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
public class RoyalChestBoatEntity extends ChestBoat {
public RoyalChestBoatEntity(EntityType<? extends Boat> entityType, Level level) {
super(entityType, level);
this.setVariant(Type.OAK);
}
@Override
public Item getDropItem() {
return BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "royal_chest_boat"));
}
}
@@ -0,0 +1,21 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
public class TruffaloBoatEntity extends Boat {
public TruffaloBoatEntity(EntityType<? extends Boat> entityType, Level level) {
super(entityType, level);
this.setVariant(Type.OAK);
}
@Override
public Item getDropItem() {
return BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "truffalo_boat"));
}
}
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.content.entity;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.entity.vehicle.ChestBoat;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.Level;
public class TruffaloChestBoatEntity extends ChestBoat {
public TruffaloChestBoatEntity(EntityType<? extends Boat> entityType, Level level) {
super(entityType, level);
this.setVariant(Type.OAK);
}
@Override
public Item getDropItem() {
return BuiltInRegistries.ITEM.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "truffalo_chest_boat"));
}
}
@@ -0,0 +1,74 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.entity.HoverboardEntity;
import java.util.function.Predicate;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntitySelector;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
public class HoverboardItem extends Item {
private static final Predicate<Entity> ENTITY_PREDICATE = EntitySelector.NO_SPECTATORS.and(Entity::isPickable);
private final EntityType<? extends HoverboardEntity> entityType;
@Nullable
private final DyeColor color;
public HoverboardItem(EntityType<? extends HoverboardEntity> entityType, @Nullable DyeColor color, Properties properties) {
super(properties);
this.entityType = entityType;
this.color = color;
}
@Override
public InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand hand) {
ItemStack stack = player.getItemInHand(hand);
BlockHitResult hitResult = getPlayerPOVHitResult(level, player, ClipContext.Fluid.ANY);
if (hitResult.getType() == HitResult.Type.MISS) {
return InteractionResultHolder.pass(stack);
}
Vec3 viewVector = player.getViewVector(1.0F);
for (Entity entity : level.getEntities(player, player.getBoundingBox().expandTowards(viewVector.scale(5.0D)).inflate(1.0D), ENTITY_PREDICATE)) {
if (entity.getBoundingBox().inflate(entity.getPickRadius()).contains(player.getEyePosition())) {
return InteractionResultHolder.pass(stack);
}
}
if (hitResult.getType() != HitResult.Type.BLOCK) {
return InteractionResultHolder.pass(stack);
}
HoverboardEntity hoverboard = new HoverboardEntity(this.entityType, level);
Vec3 spawnPos = hitResult.getLocation();
hoverboard.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, player.getYRot(), 0.0F);
hoverboard.setColor(this.color);
if (!level.noCollision(hoverboard, hoverboard.getBoundingBox())) {
return InteractionResultHolder.fail(stack);
}
if (!level.isClientSide) {
level.addFreshEntity(hoverboard);
level.gameEvent(player, GameEvent.ENTITY_PLACE, hitResult.getLocation());
if (!player.getAbilities().instabuild) {
stack.consume(1, player);
}
}
player.awardStat(Stats.ITEM_USED.get(this));
return InteractionResultHolder.sidedSuccess(stack, level.isClientSide());
}
}
@@ -1,14 +1,20 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity;
import com.craisinlord.antarchy.content.client.model.ResourceBackedGeoItemModel;
import com.craisinlord.antarchy.content.client.renderer.AnimatedHeldItemRenderer;
import com.craisinlord.antarchy.content.network.PortalGunPrimaryPayload;
import com.craisinlord.antarchy.content.portalgun.PortalGunBlackHoleEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunPlacement;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunProjectileEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunSavedData;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -38,6 +44,9 @@ import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.UseAnim;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.phys.BlockHitResult;
@@ -58,17 +67,32 @@ public class PortalGunItem extends Item implements GeoItem {
private static final String LED_CONTROLLER = "led_controller";
private static final String FIRE_CONTROLLER = "fire_controller";
private static final String LAST_SIDE_TAG = "antarchy.portal_gun_last_side";
private static final String MOON_SIDE_TAG = "antarchy.portal_gun_moon_side";
public static final String GUN_ID_TAG = "antarchy.portal_gun_id";
private static final ResourceLocation MODEL_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "geo/portal_gun.geo.json");
private static final ResourceLocation TEXTURE_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "textures/models/item/portal_gun.png");
private static final ResourceLocation ANIMATION_LOCATION = ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, "animations/portal_gun.animation.json");
private final Supplier<? extends EntityType<? extends PortalGunPortalEntity>> portalType;
private final Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType;
private final Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType;
private final Supplier<? extends Block> portalMasterBlock;
private final Supplier<? extends Block> portalBaseBlock;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
public PortalGunItem(Item.Properties properties, Supplier<? extends EntityType<? extends PortalGunPortalEntity>> portalType, Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType) {
public PortalGunItem(
Item.Properties properties,
Supplier<? extends EntityType<? extends PortalGunPortalEntity>> portalType,
Supplier<? extends EntityType<? extends PortalGunBlackHoleEntity>> blackHoleType,
Supplier<? extends EntityType<? extends PortalGunProjectileEntity>> projectileType,
Supplier<? extends Block> portalMasterBlock,
Supplier<? extends Block> portalBaseBlock
) {
super(properties);
this.portalType = portalType;
this.blackHoleType = blackHoleType;
this.projectileType = projectileType;
this.portalMasterBlock = portalMasterBlock;
this.portalBaseBlock = portalBaseBlock;
GeoItem.registerSyncedAnimatable(this);
}
@@ -82,6 +106,7 @@ public class PortalGunItem extends Item implements GeoItem {
if (player.isShiftKeyDown()) {
PortalGunSavedData.clearAllPortals(serverLevel.getServer(), player.getUUID());
this.setLastSide(stack, null);
this.setMoonSide(stack, null);
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_fizzle", SoundEvents.ENDER_CHEST_CLOSE), SoundSource.PLAYERS, 0.6F, 1.0F);
return InteractionResultHolder.consume(stack);
}
@@ -94,66 +119,105 @@ public class PortalGunItem extends Item implements GeoItem {
}
private boolean firePortal(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
BlockHitResult hitResult = this.raycast(level, player);
if (hitResult.getType() == HitResult.Type.MISS) {
if (this.tryShootMoon(level, player, stack, side)) {
if (this.raycast(level, player).getType() == HitResult.Type.MISS) {
if (this.tryArmMoonShot(level, player, stack, side)) {
return true;
}
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
return false;
}
this.spawnShotTrail(level, player, hitResult.getLocation(), side);
Placement placement = this.findPlacement(level, player, hitResult);
if (placement == null) {
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
return false;
}
this.triggerFireAnimation(level, player, stack);
this.setLastSide(stack, side);
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND);
player.awardStat(Stats.ITEM_USED.get(this));
player.getCooldowns().addCooldown(this, 6);
String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange";
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(firePath, SoundEvents.BEACON_ACTIVATE), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
this.spawnProjectile(level, player, stack, side);
return true;
}
public void handlePortalImpact(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, Vec3 impactPos) {
PortalGunPortalEntity existing = this.findPlacedPortal(level, player.getUUID(), side);
PortalGunPlacement placement = this.findPlacement(level, player, side, hitResult, existing);
if (placement == null) {
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
return;
}
if (existing != null) {
existing.discard();
}
if (this.isMoonSideArmed(stack, side)) {
this.setMoonSide(stack, null);
}
if (this.isMoonSideArmed(stack, this.otherSide(side))) {
this.spawnBlackHole(level, player, stack, side, placement.center());
return;
}
PortalGunPortalEntity portal = new PortalGunPortalEntity(this.portalType.get(), level);
portal.configure(player.getUUID(), side, placement.facing(), placement.upAxis(), placement.supportOrigin());
portal.configure(player.getUUID(), side, placement);
portal.moveTo(placement.center().x, placement.center().y, placement.center().z, placement.yaw(), 0.0F);
if (!this.placePortalBlocks(level, player.getUUID(), side, portal, placement)) {
level.playSound(null, impactPos.x, impactPos.y, impactPos.z, PortalGunPortalEntity.sound("portal_gun_invalid_surface", SoundEvents.DISPENSER_FAIL), SoundSource.PLAYERS, 0.55F, 1.0F);
return;
}
level.addFreshEntity(portal);
PortalGunSavedData.setPortal(level.getServer(), player.getUUID(), side, portal.getUUID());
PortalGunPortalEntity other = this.findCounterpart(level, player.getUUID(), side);
if (other != null) {
portal.linkTo(other);
other.linkTo(portal);
this.syncPortalBlocks(level, portal);
this.syncPortalBlocks(level, other);
}
String openPath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_open_blue" : "portal_open_orange";
level.playSound(null, portal.getX(), portal.getY(), portal.getZ(), PortalGunPortalEntity.sound(openPath, SoundEvents.END_PORTAL_SPAWN), SoundSource.PLAYERS, 0.45F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.05F : 0.92F);
}
public ItemStack findMatchingGunStack(Player player, UUID gunId) {
ItemStack mainHand = player.getMainHandItem();
if (this.matchesGunId(mainHand, gunId)) {
return mainHand;
}
ItemStack offhand = player.getOffhandItem();
if (this.matchesGunId(offhand, gunId)) {
return offhand;
}
for (int slot = 0; slot < player.getInventory().getContainerSize(); slot++) {
ItemStack stack = player.getInventory().getItem(slot);
if (this.matchesGunId(stack, gunId)) {
return stack;
}
}
return ItemStack.EMPTY;
}
private boolean tryArmMoonShot(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) {
return false;
}
PortalGunPortalEntity existing = this.findPlacedPortal(level, player.getUUID(), side);
if (existing != null) {
existing.discard();
}
this.setMoonSide(stack, side);
this.setLastSide(stack, side);
this.triggerFireAnimation(level, player, stack);
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND);
player.awardStat(Stats.ITEM_USED.get(this));
player.getCooldowns().addCooldown(this, 6);
String firePath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange";
String openPath = side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_open_blue" : "portal_open_orange";
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(firePath, SoundEvents.BEACON_ACTIVATE), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
level.playSound(null, portal.getX(), portal.getY(), portal.getZ(), PortalGunPortalEntity.sound(openPath, SoundEvents.END_PORTAL_SPAWN), SoundSource.PLAYERS, 0.45F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.05F : 0.92F);
level.playSound(null, player.getX(), player.getY(), player.getZ(), PortalGunPortalEntity.sound(side == PortalGunPortalEntity.PortalSide.BLUE ? "portal_gun_fire_blue" : "portal_gun_fire_orange", SoundEvents.BEACON_ACTIVATE), SoundSource.PLAYERS, 0.65F, side == PortalGunPortalEntity.PortalSide.BLUE ? 1.15F : 0.88F);
this.spawnShotTrail(level, player, player.getEyePosition().add(player.getLookAngle().scale(64.0D)), side);
return true;
}
private boolean tryShootMoon(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
if (!level.dimensionType().hasSkyLight() || !level.isNight() || player.getXRot() > -40.0F) {
return false;
}
PortalGunPortalEntity counterpart = this.findCounterpart(level, player.getUUID(), side);
Vec3 spawnPos;
if (counterpart != null) {
spawnPos = counterpart.position();
counterpart.discard();
} else {
spawnPos = player.getEyePosition().add(player.getLookAngle().scale(20.0D));
}
private boolean spawnBlackHole(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side, Vec3 spawnPos) {
this.setMoonSide(stack, null);
PortalGunBlackHoleEntity blackHole = new PortalGunBlackHoleEntity(this.blackHoleType.get(), level);
blackHole.configure(player.getUUID());
blackHole.moveTo(spawnPos.x, spawnPos.y, spawnPos.z, 0.0F, 0.0F);
level.addFreshEntity(blackHole);
this.setLastSide(stack, side);
this.triggerFireAnimation(level, player, stack);
stack.hurtAndBreak(1, player, EquipmentSlot.MAINHAND);
player.awardStat(Stats.ITEM_USED.get(this));
player.getCooldowns().addCooldown(this, 6);
level.playSound(null, spawnPos.x, spawnPos.y, spawnPos.z, PortalGunPortalEntity.sound("portal_gun_black_hole", SoundEvents.WARDEN_SONIC_BOOM), SoundSource.PLAYERS, 0.6F, 0.6F);
this.spawnShotTrail(level, player, spawnPos, side);
return true;
}
@@ -170,6 +234,17 @@ public class PortalGunItem extends Item implements GeoItem {
}
}
private void spawnProjectile(ServerLevel level, Player player, ItemStack stack, PortalGunPortalEntity.PortalSide side) {
UUID gunId = this.ensureGunId(stack);
PortalGunProjectileEntity projectile = new PortalGunProjectileEntity(this.projectileType.get(), player, level);
projectile.configure(side, gunId, stack);
Vec3 look = player.getLookAngle();
Vec3 start = player.getEyePosition().add(look.scale(0.4D));
projectile.setPos(start.x, start.y, start.z);
projectile.setDeltaMovement(look.scale(2.5D));
level.addFreshEntity(projectile);
}
private PortalGunPortalEntity findCounterpart(ServerLevel level, UUID owner, PortalGunPortalEntity.PortalSide side) {
PortalGunPortalEntity.PortalSide otherSide = side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
Optional<UUID> otherId = PortalGunSavedData.getPortalId(level.getServer(), owner, otherSide);
@@ -180,37 +255,90 @@ public class PortalGunItem extends Item implements GeoItem {
return entity instanceof PortalGunPortalEntity portal ? portal : null;
}
private PortalGunPortalEntity findPlacedPortal(ServerLevel level, UUID owner, PortalGunPortalEntity.PortalSide side) {
Optional<UUID> portalId = PortalGunSavedData.getPortalId(level.getServer(), owner, side);
if (portalId.isEmpty()) {
return null;
}
Entity entity = level.getEntity(portalId.get());
return entity instanceof PortalGunPortalEntity portal ? portal : null;
}
private PortalGunPortalEntity.PortalSide otherSide(PortalGunPortalEntity.PortalSide side) {
return side == PortalGunPortalEntity.PortalSide.BLUE ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
}
private BlockHitResult raycast(Level level, Player player) {
Vec3 start = player.getEyePosition();
Vec3 end = start.add(player.getLookAngle().scale(64.0D));
return level.clip(new ClipContext(start, end, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, player));
}
private Placement findPlacement(Level level, Player player, BlockHitResult hitResult) {
private PortalGunPlacement findPlacement(Level level, Player player, PortalGunPortalEntity.PortalSide side, BlockHitResult hitResult, PortalGunPortalEntity replacingPortal) {
Direction facing = hitResult.getDirection();
Direction heightAxis;
float yaw;
if (facing.getAxis() == Direction.Axis.Y) {
heightAxis = Direction.fromYRot(player.getYRot());
yaw = heightAxis.toYRot();
} else {
heightAxis = Direction.UP;
yaw = facing.toYRot();
}
Direction heightAxis = this.resolveUpAxis(player, facing);
Direction widthAxis = PortalGunPlacement.widthAxis(facing, heightAxis);
float yaw = PortalGunPlacement.yawFor(facing, heightAxis);
BlockPos hitPos = hitResult.getBlockPos();
for (int offset = -1; offset <= 0; offset++) {
BlockPos supportOrigin = hitPos.relative(heightAxis, offset);
if (this.canPlaceAt(level, supportOrigin, facing, heightAxis)) {
Vec3 firstCellCenter = supportOrigin.relative(facing).getCenter();
Vec3 halfStep = Vec3.atLowerCornerOf(heightAxis.getNormal()).scale(0.5D);
Vec3 center = firstCellCenter.add(halfStep);
return new Placement(center, facing, heightAxis, yaw, supportOrigin);
Vec3 hitLocation = hitResult.getLocation();
UUID replacingPortalId = replacingPortal == null ? null : replacingPortal.getUUID();
PortalGunPlacement bestPlacement = null;
double bestScore = Double.MAX_VALUE;
for (int widthOffset = -3; widthOffset <= 3; widthOffset++) {
for (int heightOffset = -4; heightOffset <= 3; heightOffset++) {
BlockPos supportOrigin = hitPos.relative(widthAxis, widthOffset).relative(heightAxis, heightOffset);
if (!this.canPlaceAt(level, player.getUUID(), side, supportOrigin, facing, heightAxis, replacingPortalId)) {
continue;
}
BlockPos masterPos = supportOrigin.relative(facing);
BlockPos basePos = masterPos.relative(heightAxis);
BlockPos[] portalSpots = new BlockPos[] {masterPos, basePos};
Vec3 firstCellCenter = masterPos.getCenter();
Vec3 center = firstCellCenter
.add(Vec3.atLowerCornerOf(heightAxis.getNormal()).scale(0.5D))
.subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D));
Vec3 local = hitLocation.subtract(center);
double widthLocal = local.dot(Vec3.atLowerCornerOf(widthAxis.getNormal()));
double heightLocal = local.dot(Vec3.atLowerCornerOf(heightAxis.getNormal()));
double depthLocal = local.dot(Vec3.atLowerCornerOf(facing.getNormal()));
double widthDistance = Math.max(0.0D, Math.abs(widthLocal) - 0.5D);
double heightDistance = Math.max(0.0D, Math.abs(heightLocal) - 1.0D);
double score = widthDistance * widthDistance
+ heightDistance * heightDistance
+ Math.abs(depthLocal) * 0.25D
+ supportOrigin.distSqr(hitPos) * 0.12D
+ Math.abs(widthOffset) * 0.04D
+ Math.abs(heightOffset) * 0.025D
+ (widthOffset != 0 || heightOffset != 0 ? 0.02D : 0.0D);
if (score < bestScore) {
bestScore = score;
Set<BlockPos> compensatedSpots = new HashSet<>();
if (widthOffset != 0 || heightOffset != 0) {
compensatedSpots.add(hitPos.relative(facing));
compensatedSpots.add(masterPos);
compensatedSpots.add(basePos);
compensatedSpots.add(supportOrigin);
compensatedSpots.add(basePos.relative(facing.getOpposite()));
}
bestPlacement = new PortalGunPlacement(center, facing, heightAxis, widthAxis, yaw, supportOrigin, masterPos, basePos, portalSpots, compensatedSpots);
}
}
}
return null;
return bestPlacement;
}
private boolean canPlaceAt(Level level, BlockPos supportOrigin, Direction facing, Direction heightAxis) {
private Direction resolveUpAxis(Player player, Direction facing) {
if (facing.getAxis() != Direction.Axis.Y) {
return Direction.UP;
}
Direction horizontal = player.getDirection();
if (horizontal.getAxis() == Direction.Axis.Y) {
return Direction.NORTH;
}
return horizontal.getOpposite();
}
private boolean canPlaceAt(Level level, UUID ownerId, PortalGunPortalEntity.PortalSide side, BlockPos supportOrigin, Direction facing, Direction heightAxis, UUID replacingPortalId) {
for (int h = 0; h < 2; h++) {
BlockPos supportPos = supportOrigin.relative(heightAxis, h);
BlockPos airPos = supportPos.relative(facing);
@@ -220,7 +348,7 @@ public class PortalGunItem extends Item implements GeoItem {
if (!supportState.isFaceSturdy(level, supportPos, facing)) {
return false;
}
if (!airState.isAir() && !airState.canBeReplaced()) {
if (!this.isPortalSpaceAvailable(level, airPos, airState, ownerId, side, replacingPortalId)) {
return false;
}
if (!fluidState.isEmpty()) {
@@ -230,6 +358,53 @@ public class PortalGunItem extends Item implements GeoItem {
return true;
}
private boolean isPortalSpaceAvailable(Level level, BlockPos pos, BlockState state, UUID ownerId, PortalGunPortalEntity.PortalSide side, UUID replacingPortalId) {
if (state.isAir() || state.canBeReplaced()) {
return true;
}
BlockEntity blockEntity = level.getBlockEntity(pos);
if (blockEntity instanceof PortalGunPortalMasterBlockEntity master) {
return ownerId != null
&& ownerId.equals(master.getOwnerId())
&& master.getSide() == side
&& (replacingPortalId == null || replacingPortalId.equals(master.getPortalId()));
}
if (blockEntity instanceof PortalGunPortalBaseBlockEntity base) {
return ownerId != null
&& ownerId.equals(base.getOwnerId())
&& base.getSide() == side
&& (replacingPortalId == null || replacingPortalId.equals(base.getPortalId()));
}
return false;
}
private boolean placePortalBlocks(ServerLevel level, UUID ownerId, PortalGunPortalEntity.PortalSide side, PortalGunPortalEntity portal, PortalGunPlacement placement) {
if (!level.setBlock(placement.masterPos(), this.portalMasterBlock.get().defaultBlockState(), 3)) {
return false;
}
if (!level.setBlock(placement.basePos(), this.portalBaseBlock.get().defaultBlockState(), 3)) {
level.setBlock(placement.masterPos(), Blocks.AIR.defaultBlockState(), 3);
return false;
}
if (!(level.getBlockEntity(placement.masterPos()) instanceof PortalGunPortalMasterBlockEntity master) || !(level.getBlockEntity(placement.basePos()) instanceof PortalGunPortalBaseBlockEntity base)) {
level.setBlock(placement.masterPos(), Blocks.AIR.defaultBlockState(), 3);
level.setBlock(placement.basePos(), Blocks.AIR.defaultBlockState(), 3);
return false;
}
master.configure(ownerId, portal.getUUID(), side, placement.facing(), placement.upAxis(), placement.basePos(), placement.portalSpots(), placement.compensatedSpots(), level.getServer().getTickCount());
base.configure(ownerId, portal.getUUID(), side, placement.masterPos());
return true;
}
private void syncPortalBlocks(ServerLevel level, PortalGunPortalEntity portal) {
if (level.getBlockEntity(portal.getMasterPos()) instanceof PortalGunPortalMasterBlockEntity master) {
master.updatePair(portal.getLinkedPortalId(), portal.getPairTime());
}
if (level.getBlockEntity(portal.getBasePos()) instanceof PortalGunPortalBaseBlockEntity base) {
base.updatePair(portal.getLinkedPortalId(), portal.getPairTime());
}
}
private void setLastSide(ItemStack stack, PortalGunPortalEntity.PortalSide side) {
if (side == null) {
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.remove(LAST_SIDE_TAG));
@@ -238,6 +413,14 @@ public class PortalGunItem extends Item implements GeoItem {
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.putString(LAST_SIDE_TAG, side.name()));
}
private void setMoonSide(ItemStack stack, PortalGunPortalEntity.PortalSide side) {
if (side == null) {
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.remove(MOON_SIDE_TAG));
return;
}
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.putString(MOON_SIDE_TAG, side.name()));
}
private PortalGunPortalEntity.PortalSide getLastSide(ItemStack stack) {
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
if (customData == null) {
@@ -253,6 +436,33 @@ public class PortalGunItem extends Item implements GeoItem {
return null;
}
private boolean isMoonSideArmed(ItemStack stack, PortalGunPortalEntity.PortalSide side) {
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
if (customData == null) {
return false;
}
String sideName = customData.copyTag().getString(MOON_SIDE_TAG);
return side.name().equals(sideName);
}
private UUID ensureGunId(ItemStack stack) {
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
if (customData != null && customData.copyTag().hasUUID(GUN_ID_TAG)) {
return customData.copyTag().getUUID(GUN_ID_TAG);
}
UUID gunId = UUID.randomUUID();
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, stack, tag -> tag.putUUID(GUN_ID_TAG, gunId));
return gunId;
}
private boolean matchesGunId(ItemStack stack, UUID gunId) {
if (stack.isEmpty() || stack.getItem() != this) {
return false;
}
net.minecraft.world.item.component.CustomData customData = stack.get(DataComponents.CUSTOM_DATA);
return customData != null && customData.copyTag().hasUUID(GUN_ID_TAG) && gunId.equals(customData.copyTag().getUUID(GUN_ID_TAG));
}
private void triggerFireAnimation(ServerLevel level, LivingEntity livingEntity, ItemStack stack) {
long animatableId = GeoItem.getOrAssignId(stack, level);
triggerAnim(livingEntity, animatableId, FIRE_CONTROLLER, "fire");
@@ -302,6 +512,4 @@ public class PortalGunItem extends Item implements GeoItem {
});
}
private record Placement(Vec3 center, Direction facing, Direction upAxis, float yaw, BlockPos supportOrigin) {
}
}
@@ -0,0 +1,31 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.entity.RoyalBoatEntity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class RoyalBoatOnlyItem extends OuranwoodBoatItem {
private final EntityType<RoyalBoatEntity> entityType;
public RoyalBoatOnlyItem(EntityType<RoyalBoatEntity> entityType, Properties properties) {
super(properties);
this.entityType = entityType;
}
@Override
protected Boat createBoat(Level level, HitResult hitResult, ItemStack stack, Player player) {
Vec3 pos = hitResult.getLocation();
RoyalBoatEntity boat = new RoyalBoatEntity(this.entityType, level);
boat.setPos(pos.x, pos.y, pos.z);
boat.xo = pos.x;
boat.yo = pos.y;
boat.zo = pos.z;
this.applyDefaultStackConfig(level, stack, player, boat);
return boat;
}
}
@@ -0,0 +1,31 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.entity.RoyalChestBoatEntity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class RoyalChestBoatItem extends OuranwoodBoatItem {
private final EntityType<RoyalChestBoatEntity> entityType;
public RoyalChestBoatItem(EntityType<RoyalChestBoatEntity> entityType, Properties properties) {
super(properties);
this.entityType = entityType;
}
@Override
protected Boat createBoat(Level level, HitResult hitResult, ItemStack stack, Player player) {
Vec3 pos = hitResult.getLocation();
RoyalChestBoatEntity boat = new RoyalChestBoatEntity(this.entityType, level);
boat.setPos(pos.x, pos.y, pos.z);
boat.xo = pos.x;
boat.yo = pos.y;
boat.zo = pos.z;
this.applyDefaultStackConfig(level, stack, player, boat);
return boat;
}
}
@@ -0,0 +1,31 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.entity.TruffaloBoatEntity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class TruffaloBoatOnlyItem extends OuranwoodBoatItem {
private final EntityType<TruffaloBoatEntity> entityType;
public TruffaloBoatOnlyItem(EntityType<TruffaloBoatEntity> entityType, Properties properties) {
super(properties);
this.entityType = entityType;
}
@Override
protected Boat createBoat(Level level, HitResult hitResult, ItemStack stack, Player player) {
Vec3 pos = hitResult.getLocation();
TruffaloBoatEntity boat = new TruffaloBoatEntity(this.entityType, level);
boat.setPos(pos.x, pos.y, pos.z);
boat.xo = pos.x;
boat.yo = pos.y;
boat.zo = pos.z;
this.applyDefaultStackConfig(level, stack, player, boat);
return boat;
}
}
@@ -0,0 +1,31 @@
package com.craisinlord.antarchy.content.item;
import com.craisinlord.antarchy.content.entity.TruffaloChestBoatEntity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.vehicle.Boat;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class TruffaloChestBoatItem extends OuranwoodBoatItem {
private final EntityType<TruffaloChestBoatEntity> entityType;
public TruffaloChestBoatItem(EntityType<TruffaloChestBoatEntity> entityType, Properties properties) {
super(properties);
this.entityType = entityType;
}
@Override
protected Boat createBoat(Level level, HitResult hitResult, ItemStack stack, Player player) {
Vec3 pos = hitResult.getLocation();
TruffaloChestBoatEntity boat = new TruffaloChestBoatEntity(this.entityType, level);
boat.setPos(pos.x, pos.y, pos.z);
boat.xo = pos.x;
boat.yo = pos.y;
boat.zo = pos.z;
this.applyDefaultStackConfig(level, stack, player, boat);
return boat;
}
}
@@ -36,6 +36,7 @@ public final class WormHookItem extends Item {
WormHookProjectileEntity hook = new WormHookProjectileEntity(AntarchyObjects.WORM_HOOK_PROJECTILE.get(), player, level);
hook.shootFromRotation(player, player.getXRot(), player.getYRot(), 0.0F, 2.2F, 0.5F);
level.addFreshEntity(hook);
WormHookTetherManager.launch(serverPlayer, hook);
stack.hurtAndBreak(1, serverPlayer, LivingEntity.getSlotForHand(hand));
level.playSound(null, player.getX(), player.getY(), player.getZ(), SoundEvents.FISHING_BOBBER_THROW, SoundSource.PLAYERS, 0.7F, 0.9F);
@@ -17,12 +17,16 @@ import net.minecraft.world.phys.Vec3;
* a rope anchored to a stuck {@link WormHookProjectileEntity}, wrapping the rope around block
* corners via {@link WormHookRope} so the swing radius shortens realistically when the player
* swings around an obstacle.
*
* A tether entry is created the moment the hook is thrown (so the rope can be rendered following
* the flying projectile) and only gains real swing physics once the hook sticks into a block.
*/
public final class WormHookTetherManager {
private static final double DEFAULT_MAX_LEN = 24.0D;
private static final double MIN_LEN = 2.0D;
private static final double REEL_SPEED = 0.12D;
private static final double REEL_PULL_STRENGTH = 0.16D;
private static final double BREAK_RANGE_MULTIPLIER = 1.5D;
private static final double DEFAULT_BREAK_RANGE = 48.0D;
private static final double SNAP_BUFFER = 4.0D;
private static final double PULL_CORRECTION = 0.35D;
@@ -44,18 +48,28 @@ public final class WormHookTetherManager {
return hook instanceof WormHookProjectileEntity wormHook ? wormHook : null;
}
public static boolean attach(ServerPlayer player, WormHookProjectileEntity hook) {
/** Registers the hook the moment it's thrown, purely so the rope can be rendered following it. */
public static void launch(ServerPlayer player, WormHookProjectileEntity hook) {
clear(player);
TETHERS.put(player.getUUID(), new TetherState(hook.getId()));
WormHookTetherSync.send(player, hook.getId());
}
double dist = player.getEyePosition().distanceTo(hook.position());
if (dist < MIN_LEN) {
/** Called once the hook sticks into a block; turns the cosmetic tether into real swing physics. */
public static boolean attach(ServerPlayer player, WormHookProjectileEntity hook) {
TetherState state = TETHERS.get(player.getUUID());
if (state == null || state.hookEntityId != hook.getId()) {
return false;
}
TetherState state = new TetherState(hook.getId(), Math.max(DEFAULT_MAX_LEN, dist));
double dist = player.getEyePosition().distanceTo(hook.position());
if (dist < MIN_LEN) {
clear(player);
return false;
}
state.maxLen = Math.max(MIN_LEN, dist);
state.rope = new WormHookRope(hook.position(), player.getEyePosition());
TETHERS.put(player.getUUID(), state);
WormHookTetherSync.send(player, hook.getId());
return true;
}
@@ -90,7 +104,13 @@ public final class WormHookTetherManager {
return;
}
if (player.isShiftKeyDown()) {
if (state.rope == null) {
// Hook is still flying; nothing to simulate yet, just tracking it for the render sync.
return;
}
boolean reeling = player.isShiftKeyDown();
if (reeling) {
state.maxLen = Math.max(MIN_LEN, state.maxLen - REEL_SPEED);
}
@@ -109,6 +129,19 @@ public final class WormHookTetherManager {
return;
}
Vec3 towardPivot = toPlayer.scale(1.0D / dist);
// Sneaking actively winds the player in toward the anchor, not just shortening
// the max length (which alone has no effect until the rope goes taut).
if (reeling && dist > MIN_LEN) {
Vec3 pull = towardPivot.scale(-REEL_PULL_STRENGTH);
Vec3 reelMotion = player.getDeltaMovement().add(pull);
reelMotion = new Vec3(reelMotion.x, Mth.clamp(reelMotion.y, -0.6D, 0.6D), reelMotion.z);
player.setDeltaMovement(reelMotion);
player.hurtMarked = true;
player.fallDistance = 0;
}
if (dist <= remaining) {
return;
}
@@ -121,14 +154,13 @@ public final class WormHookTetherManager {
return;
}
Vec3 direction = toPlayer.scale(1.0D / dist);
Vec3 motion = player.getDeltaMovement();
double outward = motion.dot(direction);
double outward = motion.dot(towardPivot);
if (outward > 0) {
motion = motion.subtract(direction.scale(outward));
motion = motion.subtract(towardPivot.scale(outward));
}
Vec3 correction = direction.scale(-overshoot * PULL_CORRECTION);
Vec3 correction = towardPivot.scale(-overshoot * PULL_CORRECTION);
Vec3 newMotion = motion.add(correction);
newMotion = new Vec3(newMotion.x, Mth.clamp(newMotion.y, -1.0D, 1.0D), newMotion.z);
player.setDeltaMovement(newMotion);
@@ -137,12 +169,11 @@ public final class WormHookTetherManager {
}
private static boolean isValid(ServerPlayer player, WormHookProjectileEntity hook, TetherState state) {
double breakRange = state.maxLen * BREAK_RANGE_MULTIPLIER;
double breakRange = (state.rope == null ? DEFAULT_BREAK_RANGE : state.maxLen) * BREAK_RANGE_MULTIPLIER;
return player.isAlive()
&& hook != null
&& hook.isAlive()
&& !hook.isRemoved()
&& hook.isStuck()
&& player.level() == hook.level()
&& isHoldingHook(player)
&& player.distanceToSqr(hook) <= breakRange * breakRange;
@@ -158,9 +189,8 @@ public final class WormHookTetherManager {
private double maxLen;
private WormHookRope rope;
private TetherState(int hookEntityId, double maxLen) {
private TetherState(int hookEntityId) {
this.hookEntityId = hookEntityId;
this.maxLen = maxLen;
}
}
}
@@ -0,0 +1,203 @@
package com.craisinlord.antarchy.content.portalgun;
import java.util.List;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public final class PortalGunCollisionHelper {
private static final double SEARCH_PADDING = 1.5D;
private static final double PLANE_TOLERANCE = 0.06D;
private static final double EDGE_PADDING = 0.2D;
private static final double DEPTH_PADDING = 0.45D;
private static final double BORDER_TOLERANCE = 0.02D;
private static final double BORDER_THICKNESS = 0.0125D;
private PortalGunCollisionHelper() {
}
public static Vec3 resolveCollision(Entity entity, AABB startBox, Vec3 movement, Vec3 collided) {
if (movement.lengthSqr() <= 1.0E-7D) {
return collided;
}
AABB pathBox = startBox.expandTowards(movement).inflate(SEARCH_PADDING);
List<Entity> nearby = entity.level().getEntities(entity, pathBox, candidate -> candidate instanceof PortalGunPortalEntity && candidate.isAlive());
if (nearby.isEmpty()) {
return collided;
}
Vec3 adjusted = collided;
for (Entity candidate : nearby) {
PortalGunPortalEntity portal = (PortalGunPortalEntity) candidate;
Vec3 candidateAdjusted = resolveCollisionAgainstPortal(entity, startBox, movement, adjusted, portal);
if (candidateAdjusted.distanceToSqr(movement) < adjusted.distanceToSqr(movement)) {
adjusted = candidateAdjusted;
}
}
return adjusted;
}
private static Vec3 resolveCollisionAgainstPortal(Entity entity, AABB startBox, Vec3 movement, Vec3 collided, PortalGunPortalEntity portal) {
PortalGunWorldPortalShape shape = portal.getWorldPortalShape();
Vec3 normal = portal.getNormalVec().normalize();
double desiredNormal = movement.dot(normal);
double collidedNormal = collided.dot(normal);
if (desiredNormal >= -1.0E-5D || desiredNormal >= collidedNormal - 1.0E-5D) {
return collided;
}
if (!canUsePortalPassThrough(entity, startBox, movement, portal, shape)) {
return collided;
}
double restoreAmount = desiredNormal - collidedNormal;
Vec3 adjusted = collided.add(normal.scale(restoreAmount));
return applyBorderCollision(entity, startBox, adjusted, portal);
}
private static boolean canUsePortalPassThrough(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal, PortalGunWorldPortalShape shape) {
AABB endBox = startBox.move(movement);
if (!portal.getPortalInsides(entity).intersects(startBox)
&& !portal.getPortalInsides(entity).intersects(endBox)
&& !intersectsPortalWindow(startBox, shape)
&& !intersectsPortalWindow(endBox, shape)
&& !segmentIntersectsPortalWindow(shape, startBox, endBox)) {
return false;
}
if (crossesPortal(shape, entity, startBox, movement)) {
return true;
}
Vec3 startCenter = startBox.getCenter();
Vec3 endCenter = endBox.getCenter();
PortalGunWorldPortalShape.PortalLocalCoords startCoords = shape.localCoords(startCenter);
PortalGunWorldPortalShape.PortalLocalCoords endCoords = shape.localCoords(endCenter);
return startCoords.depth() < 0.0D
&& endCoords.depth() <= PLANE_TOLERANCE
&& Math.abs(endCoords.horizontal()) <= shape.halfWidth() + EDGE_PADDING
&& Math.abs(endCoords.vertical()) <= shape.halfHeight() + EDGE_PADDING
&& projectedWindowOverlap(shape, startBox, endBox);
}
private static boolean intersectsPortalWindow(AABB box, PortalGunWorldPortalShape shape) {
for (Vec3 corner : corners(box)) {
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner);
if (Math.abs(coords.horizontal()) <= shape.halfWidth() + EDGE_PADDING
&& Math.abs(coords.vertical()) <= shape.halfHeight() + EDGE_PADDING
&& coords.depth() >= -DEPTH_PADDING
&& coords.depth() <= PLANE_TOLERANCE) {
return true;
}
}
return false;
}
private static boolean segmentIntersectsPortalWindow(PortalGunWorldPortalShape shape, AABB startBox, AABB endBox) {
for (Vec3 startCorner : corners(startBox)) {
Vec3 endCorner = matchSample(startCorner, startBox, endBox);
if (shape.crosses(startCorner, endCorner)) {
return true;
}
}
return false;
}
private static boolean projectedWindowOverlap(PortalGunWorldPortalShape shape, AABB startBox, AABB endBox) {
double minHorizontal = Double.POSITIVE_INFINITY;
double maxHorizontal = Double.NEGATIVE_INFINITY;
double minVertical = Double.POSITIVE_INFINITY;
double maxVertical = Double.NEGATIVE_INFINITY;
for (Vec3 corner : corners(startBox)) {
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner);
minHorizontal = Math.min(minHorizontal, coords.horizontal());
maxHorizontal = Math.max(maxHorizontal, coords.horizontal());
minVertical = Math.min(minVertical, coords.vertical());
maxVertical = Math.max(maxVertical, coords.vertical());
}
for (Vec3 corner : corners(endBox)) {
PortalGunWorldPortalShape.PortalLocalCoords coords = shape.localCoords(corner);
minHorizontal = Math.min(minHorizontal, coords.horizontal());
maxHorizontal = Math.max(maxHorizontal, coords.horizontal());
minVertical = Math.min(minVertical, coords.vertical());
maxVertical = Math.max(maxVertical, coords.vertical());
}
return maxHorizontal >= -shape.halfWidth() - BORDER_TOLERANCE
&& minHorizontal <= shape.halfWidth() + BORDER_TOLERANCE
&& maxVertical >= -shape.halfHeight() - BORDER_TOLERANCE
&& minVertical <= shape.halfHeight() + BORDER_TOLERANCE;
}
private static Vec3 applyBorderCollision(Entity entity, AABB startBox, Vec3 movement, PortalGunPortalEntity portal) {
if (movement.lengthSqr() <= 1.0E-7D) {
return movement;
}
AABB endBox = startBox.move(movement);
if (!portal.getPortalInsides(entity).intersects(startBox)
&& !portal.getPortalInsides(entity).intersects(endBox)
&& !intersectsPortalWindow(startBox, portal.getWorldPortalShape())
&& !intersectsPortalWindow(endBox, portal.getWorldPortalShape())) {
return movement;
}
List<VoxelShape> shapes = portal.getWorldPortalShape().getCollisionBoundaries(BORDER_THICKNESS).stream()
.map(Shapes::create)
.toList();
Vec3 collided = Entity.collideBoundingBox(entity, movement, startBox, entity.level(), shapes);
return collided.distanceToSqr(movement) < 1.0E-7D ? movement : collided;
}
private static boolean crossesPortal(PortalGunWorldPortalShape shape, Entity entity, AABB startBox, Vec3 movement) {
AABB endBox = startBox.move(movement);
for (Vec3 startSample : portalSamples(entity, startBox, true)) {
Vec3 endSample = matchSample(startSample, startBox, endBox);
if (shape.crosses(startSample, endSample)) {
return true;
}
}
return false;
}
private static List<Vec3> portalSamples(Entity entity, AABB box, boolean includeEye) {
Vec3 center = new Vec3((box.minX + box.maxX) * 0.5D, (box.minY + box.maxY) * 0.5D, (box.minZ + box.maxZ) * 0.5D);
Vec3 topCenter = new Vec3(center.x, box.maxY, center.z);
Vec3 bottomCenter = new Vec3(center.x, box.minY, center.z);
if (includeEye) {
Vec3 eye = new Vec3(entity.getX(), entity.getY() + entity.getEyeHeight(), entity.getZ());
return List.of(center, eye, topCenter, bottomCenter);
}
return List.of(center, topCenter, bottomCenter);
}
private static Vec3 matchSample(Vec3 startSample, AABB startBox, AABB endBox) {
double xLerp = axisRatio(startSample.x, startBox.minX, startBox.maxX);
double yLerp = axisRatio(startSample.y, startBox.minY, startBox.maxY);
double zLerp = axisRatio(startSample.z, startBox.minZ, startBox.maxZ);
return new Vec3(
lerp(endBox.minX, endBox.maxX, xLerp),
lerp(endBox.minY, endBox.maxY, yLerp),
lerp(endBox.minZ, endBox.maxZ, zLerp)
);
}
private static double axisRatio(double value, double min, double max) {
double size = max - min;
if (Math.abs(size) < 1.0E-6D) {
return 0.5D;
}
return (value - min) / size;
}
private static double lerp(double min, double max, double delta) {
return min + (max - min) * delta;
}
private static Vec3[] corners(AABB box) {
return new Vec3[] {
new Vec3(box.minX, box.minY, box.minZ),
new Vec3(box.minX, box.minY, box.maxZ),
new Vec3(box.minX, box.maxY, box.minZ),
new Vec3(box.minX, box.maxY, box.maxZ),
new Vec3(box.maxX, box.minY, box.minZ),
new Vec3(box.maxX, box.minY, box.maxZ),
new Vec3(box.maxX, box.maxY, box.minZ),
new Vec3(box.maxX, box.maxY, box.maxZ)
};
}
}
@@ -0,0 +1,50 @@
package com.craisinlord.antarchy.content.portalgun;
import java.util.Set;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.Vec3;
public record PortalGunPlacement(
Vec3 center,
Direction facing,
Direction upAxis,
Direction widthAxis,
float yaw,
BlockPos supportOrigin,
BlockPos masterPos,
BlockPos basePos,
BlockPos[] portalSpots,
Set<BlockPos> compensatedSpots
) {
public static PortalGunPlacement fromStored(Direction facing, Direction upAxis, BlockPos masterPos, BlockPos basePos, Set<BlockPos> compensatedSpots) {
Direction widthAxis = widthAxis(facing, upAxis);
BlockPos supportOrigin = masterPos.relative(facing.getOpposite());
Vec3 center = masterPos.getCenter()
.add(Vec3.atLowerCornerOf(upAxis.getNormal()).scale(0.5D))
.subtract(Vec3.atLowerCornerOf(facing.getNormal()).scale(0.5D));
return new PortalGunPlacement(
center,
facing,
upAxis,
widthAxis,
yawFor(facing, upAxis),
supportOrigin,
masterPos,
basePos,
new BlockPos[] {masterPos, basePos},
compensatedSpots
);
}
public static Direction widthAxis(Direction facing, Direction upAxis) {
Vec3 normal = Vec3.atLowerCornerOf(facing.getNormal());
Vec3 up = Vec3.atLowerCornerOf(upAxis.getNormal());
Vec3 width = normal.cross(up);
return Direction.getNearest(width.x, width.y, width.z);
}
public static float yawFor(Direction facing, Direction upAxis) {
return facing.getAxis() == Direction.Axis.Y ? upAxis.toYRot() : facing.toYRot();
}
}
@@ -1,12 +1,22 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.Antarchy;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalBaseBlockEntity;
import com.craisinlord.antarchy.content.block.entity.PortalGunPortalMasterBlockEntity;
import com.craisinlord.antarchy.mixins.AbstractArrowAccessor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
@@ -15,16 +25,18 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.AbstractArrow;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.joml.Quaternionf;
import software.bernie.geckolib.animatable.GeoEntity;
import software.bernie.geckolib.animatable.instance.AnimatableInstanceCache;
import software.bernie.geckolib.animation.AnimatableManager;
@@ -51,15 +63,21 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private static final int CLOSE_TICKS = 10;
private static final int MAX_LIFETIME_TICKS = 20 * 60 * 20;
private static final int TELEPORT_COOLDOWN_TICKS = 15;
private static final double PLAYER_PORTAL_INSIDE_SHIFT = 0.05D;
private static final double HALF_WIDTH = 0.5D;
private static final double HALF_HEIGHT = 1.0D;
private static final double HALF_DEPTH = 0.35D;
private final AnimatableInstanceCache geoCache = GeckoLibUtil.createInstanceCache(this);
private final Map<UUID, Integer> teleportCooldowns = new HashMap<>();
private UUID ownerId;
private UUID linkedPortalId;
private int ageTicks;
private int pairTime;
private BlockPos supportOrigin = BlockPos.ZERO;
private static final java.util.Map<UUID, Long> TELEPORT_COOLDOWNS = new java.util.HashMap<>();
private BlockPos masterPos = BlockPos.ZERO;
private BlockPos basePos = BlockPos.ZERO;
private BlockPos[] portalSpots = new BlockPos[] {BlockPos.ZERO, BlockPos.ZERO};
private Set<BlockPos> compensatedSpots = Set.of();
public PortalGunPortalEntity(EntityType<? extends PortalGunPortalEntity> entityType, Level level) {
super(entityType, level);
@@ -76,16 +94,26 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
builder.define(CLOSING, false);
}
public void configure(UUID ownerId, PortalSide side, Direction facing, Direction upAxis, BlockPos supportOrigin) {
public void configure(UUID ownerId, PortalSide side, PortalGunPlacement placement) {
this.ownerId = ownerId;
this.entityData.set(SIDE, side.ordinal());
this.entityData.set(FACING, facing.get3DDataValue());
this.entityData.set(UP_AXIS, upAxis.get3DDataValue());
this.supportOrigin = supportOrigin.immutable();
this.entityData.set(FACING, placement.facing().get3DDataValue());
this.entityData.set(UP_AXIS, placement.upAxis().get3DDataValue());
this.supportOrigin = placement.supportOrigin().immutable();
this.masterPos = placement.masterPos().immutable();
this.basePos = placement.basePos().immutable();
this.portalSpots = new BlockPos[] {placement.portalSpots()[0].immutable(), placement.portalSpots()[1].immutable()};
this.compensatedSpots = Set.copyOf(placement.compensatedSpots());
}
public void linkTo(PortalGunPortalEntity other) {
this.linkedPortalId = other.getUUID();
this.pairTime = (int) this.level().getGameTime();
}
public void restorePair(UUID linkedPortalId, int pairTime) {
this.linkedPortalId = linkedPortalId;
this.pairTime = pairTime;
}
public PortalSide getPortalSide() {
@@ -101,6 +129,46 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
return Direction.from3DDataValue(this.entityData.get(UP_AXIS));
}
public UUID getLinkedPortalId() {
return this.linkedPortalId;
}
public BlockPos getMasterPos() {
return this.masterPos;
}
public UUID getOwnerId() {
return this.ownerId;
}
public BlockPos getBasePos() {
return this.basePos;
}
public BlockPos[] getPortalSpots() {
return new BlockPos[] {this.portalSpots[0], this.portalSpots[1]};
}
public Set<BlockPos> getCompensatedSpots() {
return Set.copyOf(this.compensatedSpots);
}
public int getPairTime() {
return this.pairTime;
}
public PortalGunPortalEntity getLinkedPortal() {
if (this.linkedPortalId == null) {
return null;
}
List<Entity> entities = this.level().getEntities(this, new AABB(-3.0E7D, -3.0E7D, -3.0E7D, 3.0E7D, 3.0E7D, 3.0E7D), entity -> this.linkedPortalId.equals(entity.getUUID()));
if (entities.isEmpty()) {
return null;
}
Entity entity = entities.getFirst();
return entity instanceof PortalGunPortalEntity portal ? portal : null;
}
public Vec3 getNormalVec() {
return Vec3.atLowerCornerOf(this.getFacingDirection().getNormal());
}
@@ -113,16 +181,125 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
return this.getNormalVec().cross(this.getUpVec()).normalize();
}
public PortalGunWorldPortalShape getWorldPortalShape() {
return new PortalGunWorldPortalShape(this.position(), this.getNormalVec().normalize(), this.getUpVec().normalize(), this.getWidthVec().normalize(), HALF_WIDTH, HALF_HEIGHT, HALF_DEPTH);
}
public AABB getPlane() {
return this.getWorldPortalShape().getPlane();
}
public AABB getFlatPlane() {
return this.getWorldPortalShape().getFlatPlane();
}
public AABB getScanRange() {
return this.getWorldPortalShape().getScanRange();
}
public AABB getPortalInsides() {
return this.getWorldPortalShape().getPortalInsides();
}
public AABB getPortalInsides(Entity entity) {
if (!(entity instanceof Player)) {
return this.getPortalInsides();
}
double shiftAmount = Math.min(PLAYER_PORTAL_INSIDE_SHIFT, Math.abs(this.getWorldPortalShape().localCoords(entity.position()).depth()));
return this.getPortalInsides().move(this.getNormalVec().normalize().scale(shiftAmount));
}
public AABB getTeleportPlane(double offset) {
return this.getWorldPortalShape().getTeleportPlane(offset);
}
public AABB getCollisionRemovalAabbForEntity(Entity entity) {
double speed = entity.getDeltaMovement().length();
double extent = Math.max(1.0D, Math.max(entity.getBbWidth(), entity.getBbHeight()) + speed);
return this.getFlatPlane().expandTowards(this.getNormalVec().normalize().scale(-extent));
}
public Vec3 teleportProbePosition(Entity entity) {
if (entity instanceof Player player) {
return player.getEyePosition();
}
if (entity instanceof LivingEntity living) {
return entity.position().add(0.0D, living.getBbHeight() * 0.5D, 0.0D);
}
return entity.position();
}
public Vec3 previousTeleportProbePosition(Entity entity) {
double prevX = entity.xo;
double prevY = entity.yo;
double prevZ = entity.zo;
if (entity instanceof Player player) {
return new Vec3(prevX, prevY + player.getEyeHeight(player.getPose()), prevZ);
}
if (entity instanceof LivingEntity living) {
return new Vec3(prevX, prevY + living.getBbHeight() * 0.5D, prevZ);
}
return new Vec3(prevX, prevY, prevZ);
}
public Vec3 resolveCrossingProbe(Entity entity) {
AABB currentBox = entity.getBoundingBox();
AABB previousBox = currentBox.move(entity.xo - entity.getX(), entity.yo - entity.getY(), entity.zo - entity.getZ());
for (Vec3 currentSample : portalSamples(entity, currentBox, false)) {
Vec3 previousSample = matchPreviousSample(currentSample, currentBox, previousBox);
if (this.crossesPortal(previousSample, currentSample)) {
return currentSample;
}
}
Vec3 previousProbe = this.previousTeleportProbePosition(entity);
Vec3 currentProbe = this.teleportProbePosition(entity);
return this.crossesPortal(previousProbe, currentProbe) ? currentProbe : null;
}
public boolean containsPoint(Vec3 position) {
return this.getWorldPortalShape().contains(position, 0.0D);
}
public boolean intersectsEntityBounds(Entity entity) {
AABB bounds = entity.getBoundingBox();
for (Vec3 corner : new Vec3[] {
new Vec3(bounds.minX, bounds.minY, bounds.minZ),
new Vec3(bounds.minX, bounds.minY, bounds.maxZ),
new Vec3(bounds.minX, bounds.maxY, bounds.minZ),
new Vec3(bounds.minX, bounds.maxY, bounds.maxZ),
new Vec3(bounds.maxX, bounds.minY, bounds.minZ),
new Vec3(bounds.maxX, bounds.minY, bounds.maxZ),
new Vec3(bounds.maxX, bounds.maxY, bounds.minZ),
new Vec3(bounds.maxX, bounds.maxY, bounds.maxZ)
}) {
if (this.getWorldPortalShape().contains(corner, 0.08D)) {
return true;
}
}
return this.getPortalInsides(entity).intersects(bounds);
}
public boolean crossesPortal(Vec3 previousProbePosition, Vec3 currentProbePosition) {
return this.getWorldPortalShape().crosses(previousProbePosition, currentProbePosition);
}
public boolean shouldRenderFront(Vec3 cameraPos) {
return this.getWorldPortalShape().shouldRenderFront(cameraPos);
}
@Override
public void tick() {
super.tick();
this.ageTicks++;
this.yRotO = this.getYRot();
this.xRotO = this.getXRot();
if (this.level().isClientSide) {
return;
}
if (!(this.level() instanceof ServerLevel serverLevel)) {
return;
}
this.tickTeleportCooldowns();
if (this.ownerId == null || !this.isStillRegistered(serverLevel)) {
this.discard();
return;
@@ -151,32 +328,35 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
private boolean hasValidSurface() {
Direction facing = this.getFacingDirection();
Direction heightAxis = this.getUpAxis();
for (int h = 0; h < 2; h++) {
BlockPos supportPos = this.supportOrigin.relative(heightAxis, h);
BlockPos airPos = supportPos.relative(facing);
for (int i = 0; i < this.portalSpots.length; i++) {
BlockPos portalSpot = this.portalSpots[i];
BlockPos supportPos = portalSpot.relative(facing.getOpposite());
BlockState supportState = this.level().getBlockState(supportPos);
BlockState airState = this.level().getBlockState(airPos);
FluidState fluidState = this.level().getFluidState(airPos);
if (!supportState.isFaceSturdy(this.level(), supportPos, facing)) {
return false;
}
if (!airState.isAir() && !airState.canBeReplaced()) {
return false;
}
if (!fluidState.isEmpty()) {
return false;
BlockEntity blockEntity = this.level().getBlockEntity(portalSpot);
if (i == 0) {
if (!(blockEntity instanceof PortalGunPortalMasterBlockEntity master) || !master.matches(this.ownerId, this.getUUID(), this.getPortalSide())) {
return false;
}
} else {
if (!(blockEntity instanceof PortalGunPortalBaseBlockEntity base) || !base.matches(this.ownerId, this.getUUID(), this.getPortalSide())) {
return false;
}
}
}
return true;
}
private void tickTeleport(PortalGunPortalEntity linked) {
List<Entity> entities = this.level().getEntities(this, this.getBoundingBox().inflate(0.75D), this::canTeleportEntity);
List<Entity> entities = this.level().getEntities(this, this.getScanRange(), this::canTeleportEntity);
for (Entity entity : entities) {
if (this.isInsidePortal(entity.position())) {
this.teleportEntity(entity, linked);
Vec3 crossingProbe = this.resolveCrossingProbe(entity);
if (crossingProbe == null) {
continue;
}
this.teleportEntity(entity, linked, crossingProbe);
}
}
@@ -184,45 +364,109 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
if (!entity.isAlive() || entity instanceof PortalGunPortalEntity || entity.isPassenger() || entity.isVehicle()) {
return false;
}
long gameTime = this.level().getGameTime();
long cooldownUntil = TELEPORT_COOLDOWNS.getOrDefault(entity.getUUID(), 0L);
if (cooldownUntil <= gameTime) {
TELEPORT_COOLDOWNS.remove(entity.getUUID());
return true;
}
return false;
return !this.teleportCooldowns.containsKey(entity.getUUID());
}
private boolean isInsidePortal(Vec3 position) {
Vec3 relative = position.subtract(this.position());
double horizontal = relative.dot(this.getWidthVec());
double vertical = relative.dot(this.getUpVec());
double depth = Math.abs(relative.dot(this.getNormalVec()));
return Math.abs(horizontal) <= HALF_WIDTH && Math.abs(vertical) <= HALF_HEIGHT && depth <= HALF_DEPTH;
return this.getWorldPortalShape().contains(position, 0.0D);
}
private void teleportEntity(Entity entity, PortalGunPortalEntity destination) {
Vec3 sourceNormal = this.getNormalVec();
Vec3 sourceUp = this.getUpVec();
Vec3 sourceWidth = this.getWidthVec();
Vec3 destinationNormal = destination.getNormalVec();
Vec3 destinationUp = destination.getUpVec();
Vec3 destinationWidth = destination.getWidthVec();
Vec3 motion = entity.getDeltaMovement();
double widthComponent = motion.dot(sourceWidth);
double upComponent = motion.dot(sourceUp);
double forward = -motion.dot(sourceNormal);
Vec3 transformedMotion = destinationWidth.scale(widthComponent).add(destinationUp.scale(upComponent)).add(destinationNormal.scale(forward));
Vec3 exitPos = destination.position().add(destinationNormal.scale(1.0D));
TELEPORT_COOLDOWNS.put(entity.getUUID(), this.level().getGameTime() + TELEPORT_COOLDOWN_TICKS);
private PortalLocalCoords localCoords(Vec3 position) {
PortalGunWorldPortalShape.PortalLocalCoords coords = this.getWorldPortalShape().localCoords(position);
return new PortalLocalCoords(coords.horizontal(), coords.vertical(), coords.depth());
}
private List<Vec3> portalSamples(Entity entity, AABB box, boolean previous) {
Vec3 center = new Vec3((box.minX + box.maxX) * 0.5D, (box.minY + box.maxY) * 0.5D, (box.minZ + box.maxZ) * 0.5D);
Vec3 topCenter = new Vec3(center.x, box.maxY, center.z);
Vec3 bottomCenter = new Vec3(center.x, box.minY, center.z);
if (entity instanceof Player player) {
Vec3 eye = previous ? this.previousTeleportProbePosition(player) : this.teleportProbePosition(player);
return List.of(center, eye, topCenter, bottomCenter);
}
if (entity instanceof LivingEntity) {
return List.of(center, topCenter, bottomCenter);
}
return List.of(center);
}
private Vec3 matchPreviousSample(Vec3 currentSample, AABB currentBox, AABB previousBox) {
double xLerp = axisRatio(currentSample.x, currentBox.minX, currentBox.maxX);
double yLerp = axisRatio(currentSample.y, currentBox.minY, currentBox.maxY);
double zLerp = axisRatio(currentSample.z, currentBox.minZ, currentBox.maxZ);
return new Vec3(
lerp(previousBox.minX, previousBox.maxX, xLerp),
lerp(previousBox.minY, previousBox.maxY, yLerp),
lerp(previousBox.minZ, previousBox.maxZ, zLerp)
);
}
private static double axisRatio(double value, double min, double max) {
double size = max - min;
if (Math.abs(size) < 1.0E-6D) {
return 0.5D;
}
return (value - min) / size;
}
private static double lerp(double min, double max, double delta) {
return min + (max - min) * delta;
}
private void teleportEntity(Entity entity, PortalGunPortalEntity destination, Vec3 currentProbe) {
Quaternionf transform = PortalGunTransformUtil.createTransform(this, destination);
Vec3 relativeProbe = currentProbe.subtract(this.position());
Vec3 transformedProbe = PortalGunTransformUtil.transform(relativeProbe, transform);
Vec3 entityOffsetFromProbe = entity.position().subtract(currentProbe);
Vec3 transformedEntityOffset = PortalGunTransformUtil.transform(entityOffsetFromProbe, transform);
Vec3 transformedLook = PortalGunTransformUtil.transform(entity.getLookAngle(), transform).normalize();
Vec3 transformedMotion = PortalGunTransformUtil.transform(entity.getDeltaMovement(), transform);
Vec3 exitProbe = destination.position().add(transformedProbe).add(destination.getNormalVec().normalize().scale(0.08D - transformedProbe.dot(destination.getNormalVec().normalize())));
Vec3 exitPos = exitProbe.add(transformedEntityOffset);
float yaw = PortalGunTransformUtil.yawFromLook(transformedLook);
float pitch = PortalGunTransformUtil.pitchFromLook(transformedLook);
this.applyTeleportCooldown(entity, destination);
if (entity instanceof ServerPlayer player && destination.level() instanceof ServerLevel destinationLevel) {
player.teleportTo(destinationLevel, exitPos.x, exitPos.y, exitPos.z, player.getYRot(), player.getXRot());
player.teleportTo(destinationLevel, exitPos.x, exitPos.y, exitPos.z, yaw, pitch);
} else {
entity.teleportTo(exitPos.x, exitPos.y, exitPos.z);
entity.setYRot(yaw);
entity.setXRot(pitch);
entity.setYHeadRot(yaw);
entity.setYBodyRot(yaw);
}
entity.setDeltaMovement(transformedMotion);
entity.hasImpulse = true;
entity.fallDistance = 0.0F;
this.handleSpecialEntityPostTeleport(entity);
}
private void tickTeleportCooldowns() {
Iterator<Map.Entry<UUID, Integer>> iterator = this.teleportCooldowns.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<UUID, Integer> entry = iterator.next();
int cooldown = entry.getValue() - 1;
if (cooldown < 0) {
iterator.remove();
} else {
entry.setValue(cooldown);
}
}
}
private void applyTeleportCooldown(Entity entity, PortalGunPortalEntity destination) {
UUID entityId = entity.getUUID();
this.teleportCooldowns.put(entityId, TELEPORT_COOLDOWN_TICKS);
destination.teleportCooldowns.put(entityId, TELEPORT_COOLDOWN_TICKS);
}
private void handleSpecialEntityPostTeleport(Entity entity) {
if (entity instanceof AbstractArrow arrow) {
((AbstractArrowAccessor) arrow).antarchy$setInGround(false);
((AbstractArrowAccessor) arrow).antarchy$setInGroundTime(0);
((AbstractArrowAccessor) arrow).antarchy$setShakeTime(0);
arrow.setNoPhysics(false);
}
}
private PortalGunPortalEntity findLinkedPortal(ServerLevel level) {
@@ -235,18 +479,12 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
@Override
public AABB getBoundingBoxForCulling() {
return this.getBoundingBox().inflate(1.5D);
return this.getWorldPortalShape().getBoundsForCulling();
}
@Override
protected AABB makeBoundingBox() {
Vec3 widthVec = this.getWidthVec();
Vec3 upVec = this.getUpVec();
Vec3 normalVec = this.getNormalVec();
double halfX = HALF_WIDTH * Math.abs(widthVec.x) + HALF_HEIGHT * Math.abs(upVec.x) + HALF_DEPTH * Math.abs(normalVec.x);
double halfY = HALF_WIDTH * Math.abs(widthVec.y) + HALF_HEIGHT * Math.abs(upVec.y) + HALF_DEPTH * Math.abs(normalVec.y);
double halfZ = HALF_WIDTH * Math.abs(widthVec.z) + HALF_HEIGHT * Math.abs(upVec.z) + HALF_DEPTH * Math.abs(normalVec.z);
return new AABB(this.getX() - halfX, this.getY() - halfY, this.getZ() - halfZ, this.getX() + halfX, this.getY() + halfY, this.getZ() + halfZ);
return this.getPortalInsides();
}
@Override
@@ -258,11 +496,37 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
tag.putUUID("LinkedPortalId", this.linkedPortalId);
}
tag.putInt("AgeTicks", this.ageTicks);
tag.putInt("PairTime", this.pairTime);
tag.putInt("Side", this.getPortalSide().ordinal());
tag.putInt("Facing", this.getFacingDirection().get3DDataValue());
tag.putInt("UpAxis", this.getUpAxis().get3DDataValue());
tag.putInt("SupportX", this.supportOrigin.getX());
tag.putInt("SupportY", this.supportOrigin.getY());
tag.putInt("SupportZ", this.supportOrigin.getZ());
tag.putInt("MasterX", this.masterPos.getX());
tag.putInt("MasterY", this.masterPos.getY());
tag.putInt("MasterZ", this.masterPos.getZ());
tag.putInt("BaseX", this.basePos.getX());
tag.putInt("BaseY", this.basePos.getY());
tag.putInt("BaseZ", this.basePos.getZ());
ListTag spots = new ListTag();
for (BlockPos portalSpot : this.portalSpots) {
CompoundTag spot = new CompoundTag();
spot.putInt("X", portalSpot.getX());
spot.putInt("Y", portalSpot.getY());
spot.putInt("Z", portalSpot.getZ());
spots.add(spot);
}
tag.put("PortalSpots", spots);
ListTag compensated = new ListTag();
for (BlockPos compensatedSpot : this.compensatedSpots) {
CompoundTag spot = new CompoundTag();
spot.putInt("X", compensatedSpot.getX());
spot.putInt("Y", compensatedSpot.getY());
spot.putInt("Z", compensatedSpot.getZ());
compensated.add(spot);
}
tag.put("CompensatedSpots", compensated);
}
@Override
@@ -274,17 +538,48 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
this.linkedPortalId = tag.getUUID("LinkedPortalId");
}
this.ageTicks = tag.getInt("AgeTicks");
this.pairTime = tag.getInt("PairTime");
this.entityData.set(SIDE, tag.getInt("Side"));
this.entityData.set(FACING, tag.getInt("Facing"));
this.entityData.set(UP_AXIS, tag.getInt("UpAxis"));
this.supportOrigin = new BlockPos(tag.getInt("SupportX"), tag.getInt("SupportY"), tag.getInt("SupportZ"));
this.masterPos = new BlockPos(tag.getInt("MasterX"), tag.getInt("MasterY"), tag.getInt("MasterZ"));
this.basePos = new BlockPos(tag.getInt("BaseX"), tag.getInt("BaseY"), tag.getInt("BaseZ"));
ListTag spots = tag.getList("PortalSpots", Tag.TAG_COMPOUND);
for (int i = 0; i < Math.min(2, spots.size()); i++) {
CompoundTag spot = spots.getCompound(i);
this.portalSpots[i] = new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z"));
}
ListTag compensated = tag.getList("CompensatedSpots", Tag.TAG_COMPOUND);
Set<BlockPos> compensatedSpots = new HashSet<>();
for (int i = 0; i < compensated.size(); i++) {
CompoundTag spot = compensated.getCompound(i);
compensatedSpots.add(new BlockPos(spot.getInt("X"), spot.getInt("Y"), spot.getInt("Z")));
}
this.compensatedSpots = compensatedSpots;
}
@Override
public void remove(Entity.RemovalReason reason) {
if (!this.level().isClientSide && this.ownerId != null && this.level() instanceof ServerLevel serverLevel) {
PortalGunSavedData.clearPortal(serverLevel.getServer(), this.ownerId, this.getPortalSide(), this.getUUID());
}
Level level = this.level();
UUID ownerId = this.ownerId;
super.remove(reason);
if (!level.isClientSide && ownerId != null && level instanceof ServerLevel serverLevel) {
PortalGunSavedData.clearPortal(serverLevel.getServer(), ownerId, this.getPortalSide(), this.getUUID());
this.clearPortalBlocks(serverLevel);
}
}
private void clearPortalBlocks(ServerLevel level) {
for (BlockPos portalSpot : this.portalSpots) {
if (level.getBlockEntity(portalSpot) instanceof PortalGunPortalMasterBlockEntity master && this.getUUID().equals(master.getPortalId())) {
level.removeBlock(portalSpot, false);
continue;
}
if (level.getBlockEntity(portalSpot) instanceof PortalGunPortalBaseBlockEntity base && this.getUUID().equals(base.getPortalId())) {
level.removeBlock(portalSpot, false);
}
}
}
@Override
@@ -340,4 +635,7 @@ public class PortalGunPortalEntity extends Entity implements GeoEntity {
public static SoundEvent sound(String path, SoundEvent fallback) {
return BuiltInRegistries.SOUND_EVENT.getOptional(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path)).orElse(fallback);
}
private record PortalLocalCoords(double horizontal, double vertical, double depth) {
}
}
@@ -0,0 +1,166 @@
package com.craisinlord.antarchy.content.portalgun;
import com.craisinlord.antarchy.content.item.PortalGunItem;
import java.util.UUID;
import net.minecraft.core.component.DataComponents;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.EntityDataAccessor;
import net.minecraft.network.syncher.EntityDataSerializers;
import net.minecraft.network.syncher.SynchedEntityData;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.entity.projectile.ThrowableItemProjectile;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.EntityHitResult;
import net.minecraft.world.phys.Vec3;
public class PortalGunProjectileEntity extends ThrowableItemProjectile {
private static final EntityDataAccessor<Integer> SIDE = SynchedEntityData.defineId(PortalGunProjectileEntity.class, EntityDataSerializers.INT);
private static final int MAX_FLIGHT_TICKS = 80;
private UUID gunId;
public PortalGunProjectileEntity(EntityType<? extends PortalGunProjectileEntity> entityType, Level level) {
super(entityType, level);
this.configureProjectile();
}
public PortalGunProjectileEntity(EntityType<? extends PortalGunProjectileEntity> entityType, LivingEntity owner, Level level) {
super(entityType, owner, level);
this.configureProjectile();
}
private void configureProjectile() {
this.setNoGravity(true);
this.noCulling = true;
}
@Override
protected void defineSynchedData(SynchedEntityData.Builder builder) {
super.defineSynchedData(builder);
builder.define(SIDE, PortalGunPortalEntity.PortalSide.BLUE.ordinal());
}
public void configure(PortalGunPortalEntity.PortalSide side, UUID gunId, ItemStack gunStack) {
this.entityData.set(SIDE, side.ordinal());
this.gunId = gunId;
this.setItem(gunStack.copyWithCount(1));
}
public PortalGunPortalEntity.PortalSide getPortalSide() {
return this.entityData.get(SIDE) == PortalGunPortalEntity.PortalSide.ORANGE.ordinal() ? PortalGunPortalEntity.PortalSide.ORANGE : PortalGunPortalEntity.PortalSide.BLUE;
}
@Override
protected Item getDefaultItem() {
return Items.AIR;
}
@Override
protected double getDefaultGravity() {
return 0.0D;
}
@Override
protected boolean canHitEntity(Entity target) {
return false;
}
@Override
public boolean canBeCollidedWith() {
return false;
}
@Override
public boolean isAttackable() {
return false;
}
@Override
public void tick() {
super.tick();
if (this.tickCount > MAX_FLIGHT_TICKS) {
this.discard();
return;
}
Vec3 motion = this.getDeltaMovement();
if (motion.lengthSqr() > 1.0E-6D) {
double horizontal = Math.sqrt(motion.x * motion.x + motion.z * motion.z);
this.setYRot((float) (net.minecraft.util.Mth.atan2(motion.z, motion.x) * (180.0D / Math.PI)) - 90.0F);
this.setXRot((float) (-(net.minecraft.util.Mth.atan2(motion.y, horizontal) * (180.0D / Math.PI))));
}
if (this.level().isClientSide) {
Vec3 point = this.position();
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.SOUL_FIRE_FLAME, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.END_ROD, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
} else {
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.FLAME, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
this.level().addParticle(net.minecraft.core.particles.ParticleTypes.CRIT, point.x, point.y, point.z, 0.0D, 0.0D, 0.0D);
}
}
}
@Override
protected void onHitEntity(EntityHitResult result) {
}
@Override
protected void onHitBlock(BlockHitResult result) {
if (!this.level().isClientSide && this.level() instanceof ServerLevel serverLevel) {
this.spawnImpactParticles(serverLevel, result.getLocation());
ItemStack sourceStack = this.resolveSourceStack();
if (this.getOwner() instanceof Player player && sourceStack.getItem() instanceof PortalGunItem portalGunItem) {
portalGunItem.handlePortalImpact(serverLevel, player, sourceStack, this.getPortalSide(), result, this.position());
}
}
this.discard();
}
private void spawnImpactParticles(ServerLevel level, Vec3 impactPos) {
if (this.getPortalSide() == PortalGunPortalEntity.PortalSide.BLUE) {
level.sendParticles(net.minecraft.core.particles.ParticleTypes.SOUL_FIRE_FLAME, impactPos.x, impactPos.y, impactPos.z, 10, 0.08D, 0.08D, 0.08D, 0.01D);
level.sendParticles(net.minecraft.core.particles.ParticleTypes.END_ROD, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D);
return;
}
level.sendParticles(net.minecraft.core.particles.ParticleTypes.FLAME, impactPos.x, impactPos.y, impactPos.z, 10, 0.08D, 0.08D, 0.08D, 0.01D);
level.sendParticles(net.minecraft.core.particles.ParticleTypes.CRIT, impactPos.x, impactPos.y, impactPos.z, 6, 0.04D, 0.04D, 0.04D, 0.01D);
}
private ItemStack resolveSourceStack() {
ItemStack projectileStack = this.getItem();
if (!(this.getOwner() instanceof Player player) || !(projectileStack.getItem() instanceof PortalGunItem portalGunItem) || this.gunId == null) {
return projectileStack;
}
ItemStack matching = portalGunItem.findMatchingGunStack(player, this.gunId);
return matching.isEmpty() ? projectileStack : matching;
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
tag.putInt("Side", this.entityData.get(SIDE));
if (this.gunId != null) {
tag.putUUID("GunId", this.gunId);
}
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
this.entityData.set(SIDE, tag.getInt("Side"));
if (tag.hasUUID("GunId")) {
this.gunId = tag.getUUID("GunId");
}
ItemStack itemStack = this.getItem();
if (this.gunId != null && !itemStack.isEmpty()) {
net.minecraft.world.item.component.CustomData.update(DataComponents.CUSTOM_DATA, itemStack, customData -> customData.putUUID(PortalGunItem.GUN_ID_TAG, this.gunId));
}
}
}
@@ -9,6 +9,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.level.saveddata.SavedData;
public final class PortalGunSavedData extends SavedData {
@@ -119,6 +120,20 @@ public final class PortalGunSavedData extends SavedData {
discardIfLoaded(server, current.orangePortalId());
}
public static PortalGunPortalEntity findLoadedPortal(MinecraftServer server, UUID owner, PortalGunPortalEntity.PortalSide side) {
Optional<UUID> portalId = getPortalId(server, owner, side);
if (portalId.isEmpty()) {
return null;
}
for (ServerLevel level : server.getAllLevels()) {
Entity entity = level.getEntity(portalId.get());
if (entity instanceof PortalGunPortalEntity portal && !portal.isRemoved()) {
return portal;
}
}
return null;
}
private static void discardIfLoaded(MinecraftServer server, UUID portalId) {
if (portalId == null) {
return;
@@ -0,0 +1,93 @@
package com.craisinlord.antarchy.content.portalgun;
import net.minecraft.util.Mth;
import net.minecraft.world.phys.Vec3;
import org.joml.Matrix3f;
import org.joml.Quaternionf;
import org.joml.Vector3f;
public final class PortalGunTransformUtil {
private static final Vec3 WORLD_UP = new Vec3(0.0D, 1.0D, 0.0D);
private static final Vec3 WORLD_FORWARD_FALLBACK = new Vec3(0.0D, 0.0D, 1.0D);
private PortalGunTransformUtil() {
}
public static Quaternionf createTransform(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal) {
return createTransform(sourcePortal.getWidthVec(), sourcePortal.getUpVec(), sourcePortal.getNormalVec().scale(-1.0D), destinationPortal.getWidthVec(), destinationPortal.getUpVec(), destinationPortal.getNormalVec());
}
public static Quaternionf createTransform(Vec3 sourceWidth, Vec3 sourceUp, Vec3 sourceForward, Vec3 destinationWidth, Vec3 destinationUp, Vec3 destinationForward) {
Matrix3f sourceBasis = new Matrix3f(
(float) sourceWidth.x, (float) sourceUp.x, (float) sourceForward.x,
(float) sourceWidth.y, (float) sourceUp.y, (float) sourceForward.y,
(float) sourceWidth.z, (float) sourceUp.z, (float) sourceForward.z
);
Matrix3f destinationBasis = new Matrix3f(
(float) destinationWidth.x, (float) destinationUp.x, (float) destinationForward.x,
(float) destinationWidth.y, (float) destinationUp.y, (float) destinationForward.y,
(float) destinationWidth.z, (float) destinationUp.z, (float) destinationForward.z
);
Matrix3f transform = destinationBasis.mul(sourceBasis.transpose(new Matrix3f()), new Matrix3f());
return transform.getNormalizedRotation(new Quaternionf());
}
public static Vec3 transform(Vec3 vector, Quaternionf quaternion) {
Vector3f rotated = new Vector3f((float) vector.x, (float) vector.y, (float) vector.z).rotate(quaternion);
return new Vec3(rotated.x(), rotated.y(), rotated.z());
}
public static Vec3 transformRelativePosition(PortalGunPortalEntity sourcePortal, PortalGunPortalEntity destinationPortal, Vec3 relativePosition) {
return transform(relativePosition, createTransform(sourcePortal, destinationPortal));
}
public static float yawFromLook(Vec3 look) {
return (float) Math.toDegrees(Math.atan2(-look.x, look.z));
}
public static float pitchFromLook(Vec3 look) {
return (float) Math.toDegrees(-Math.asin(Mth.clamp(look.y, -1.0D, 1.0D)));
}
public static Vec3 upVectorFromLookAndRoll(Vec3 look, float rollDegrees) {
Vec3 normalizedLook = look.normalize();
Vec3 referenceUp = Math.abs(normalizedLook.dot(WORLD_UP)) > 0.999D ? WORLD_FORWARD_FALLBACK : WORLD_UP;
Vec3 right = referenceUp.cross(normalizedLook).normalize();
Vec3 up = normalizedLook.cross(right).normalize();
if (Math.abs(rollDegrees) <= 1.0E-4F) {
return up;
}
Quaternionf roll = new Quaternionf().fromAxisAngleDeg((float) normalizedLook.x, (float) normalizedLook.y, (float) normalizedLook.z, rollDegrees);
return transform(up, roll).normalize();
}
public static float rollFromOrientation(Vec3 look, Vec3 up) {
Vec3 normalizedLook = look.normalize();
Vec3 normalizedUp = up.normalize();
Vec3 baseUp = upVectorFromLookAndRoll(normalizedLook, 0.0F);
Vec3 projectedUp = normalizedUp.subtract(normalizedLook.scale(normalizedUp.dot(normalizedLook)));
if (projectedUp.lengthSqr() < 1.0E-8D) {
return 0.0F;
}
projectedUp = projectedUp.normalize();
double sin = normalizedLook.dot(baseUp.cross(projectedUp));
double cos = Mth.clamp(baseUp.dot(projectedUp), -1.0D, 1.0D);
return normalizeDegrees((float) Math.toDegrees(Math.atan2(sin, cos)));
}
public static Quaternionf orientationQuaternion(Vec3 look, Vec3 up) {
Vec3 forward = look.normalize();
Vec3 correctedUp = up.subtract(forward.scale(up.dot(forward))).normalize();
Vec3 right = correctedUp.cross(forward).normalize();
Matrix3f basis = new Matrix3f(
(float) right.x, (float) correctedUp.x, (float) (-forward.x),
(float) right.y, (float) correctedUp.y, (float) (-forward.y),
(float) right.z, (float) correctedUp.z, (float) (-forward.z)
);
return basis.getNormalizedRotation(new Quaternionf());
}
public static float normalizeDegrees(float degrees) {
return Mth.wrapDegrees(degrees);
}
}
@@ -0,0 +1,165 @@
package com.craisinlord.antarchy.content.portalgun;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import java.util.List;
public record PortalGunWorldPortalShape(
Vec3 center,
Vec3 normal,
Vec3 up,
Vec3 right,
double halfWidth,
double halfHeight,
double halfDepth
) {
public static final double DEFAULT_SCAN_DISTANCE = 3.0D;
public AABB getPlane() {
return this.createPlaneAround(0.0D, 0.0125D);
}
public AABB getFlatPlane() {
return this.createPlaneAround(0.0D, 0.0D);
}
public AABB getScanRange() {
return this.getFlatPlane().expandTowards(this.normal.scale(-DEFAULT_SCAN_DISTANCE));
}
public AABB getPortalInsides() {
return this.createPlaneAround(0.0D, this.halfDepth);
}
public AABB getTeleportPlane(double offset) {
return this.createPlaneAround(offset, this.halfDepth);
}
public Vec3[] getCorners(double normalOffset) {
Vec3 center = this.center.add(this.normal.scale(normalOffset));
Vec3 width = this.right.scale(this.halfWidth);
Vec3 height = this.up.scale(this.halfHeight);
return new Vec3[] {
center.subtract(width).subtract(height),
center.subtract(width).add(height),
center.add(width).add(height),
center.add(width).subtract(height)
};
}
public PortalLocalCoords localCoords(Vec3 position) {
Vec3 relative = position.subtract(this.center);
return new PortalLocalCoords(relative.dot(this.right), relative.dot(this.up), relative.dot(this.normal));
}
public boolean contains(Vec3 position, double depthPadding) {
PortalLocalCoords coords = this.localCoords(position);
return Math.abs(coords.horizontal()) <= this.halfWidth
&& Math.abs(coords.vertical()) <= this.halfHeight
&& Math.abs(coords.depth()) <= this.halfDepth + depthPadding;
}
public boolean crosses(Vec3 previousPosition, Vec3 currentPosition) {
PortalLocalCoords previous = this.localCoords(previousPosition);
PortalLocalCoords current = this.localCoords(currentPosition);
double depthDelta = current.depth() - previous.depth();
if (depthDelta > -1.0E-6D || previous.depth() <= 0.0D || current.depth() > 0.0D) {
return false;
}
double t = -previous.depth() / depthDelta;
if (t < 0.0D || t > 1.0D) {
return false;
}
double horizontal = previous.horizontal() + (current.horizontal() - previous.horizontal()) * t;
double vertical = previous.vertical() + (current.vertical() - previous.vertical()) * t;
return Math.abs(horizontal) <= this.halfWidth && Math.abs(vertical) <= this.halfHeight;
}
public boolean shouldRenderFront(Vec3 cameraPos) {
PortalLocalCoords coords = this.localCoords(cameraPos);
if (coords.depth() <= 0.0D) {
return false;
}
double depthScale = Math.min(DEFAULT_SCAN_DISTANCE, Math.max(0.0D, coords.depth()));
return Math.abs(coords.horizontal()) <= this.halfWidth + depthScale
&& Math.abs(coords.vertical()) <= this.halfHeight + depthScale;
}
public boolean intersectsFront(AABB box, double tolerance) {
double maxDepth = Double.NEGATIVE_INFINITY;
for (Vec3 corner : corners(box)) {
maxDepth = Math.max(maxDepth, this.localCoords(corner).depth());
if (maxDepth >= -tolerance) {
return true;
}
}
return false;
}
public AABB getBoundsForCulling() {
return this.createPlaneAround(0.0D, this.halfDepth).inflate(1.5D);
}
public List<AABB> getCollisionBoundaries(double borderThickness) {
double horizontalOuter = this.halfWidth + borderThickness;
double verticalOuter = this.halfHeight + borderThickness;
return List.of(
this.createLocalBox(-horizontalOuter, this.halfWidth, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth),
this.createLocalBox(-this.halfWidth, horizontalOuter, -verticalOuter, verticalOuter, -this.halfDepth, this.halfDepth),
this.createLocalBox(-this.halfWidth, this.halfWidth, -verticalOuter, -this.halfHeight, -this.halfDepth, this.halfDepth),
this.createLocalBox(-this.halfWidth, this.halfWidth, this.halfHeight, verticalOuter, -this.halfDepth, this.halfDepth)
);
}
private AABB createPlaneAround(double planeOffset, double thickness) {
Vec3 boxCenter = this.center.add(this.normal.scale(planeOffset));
double halfX = this.halfWidth * Math.abs(this.right.x) + this.halfHeight * Math.abs(this.up.x) + thickness * Math.abs(this.normal.x);
double halfY = this.halfWidth * Math.abs(this.right.y) + this.halfHeight * Math.abs(this.up.y) + thickness * Math.abs(this.normal.y);
double halfZ = this.halfWidth * Math.abs(this.right.z) + this.halfHeight * Math.abs(this.up.z) + thickness * Math.abs(this.normal.z);
return new AABB(boxCenter.x - halfX, boxCenter.y - halfY, boxCenter.z - halfZ, boxCenter.x + halfX, boxCenter.y + halfY, boxCenter.z + halfZ);
}
private AABB createLocalBox(double minHorizontal, double maxHorizontal, double minVertical, double maxVertical, double minDepth, double maxDepth) {
Vec3[] corners = new Vec3[] {
this.center.add(this.right.scale(minHorizontal)).add(this.up.scale(minVertical)).add(this.normal.scale(minDepth)),
this.center.add(this.right.scale(minHorizontal)).add(this.up.scale(minVertical)).add(this.normal.scale(maxDepth)),
this.center.add(this.right.scale(minHorizontal)).add(this.up.scale(maxVertical)).add(this.normal.scale(minDepth)),
this.center.add(this.right.scale(minHorizontal)).add(this.up.scale(maxVertical)).add(this.normal.scale(maxDepth)),
this.center.add(this.right.scale(maxHorizontal)).add(this.up.scale(minVertical)).add(this.normal.scale(minDepth)),
this.center.add(this.right.scale(maxHorizontal)).add(this.up.scale(minVertical)).add(this.normal.scale(maxDepth)),
this.center.add(this.right.scale(maxHorizontal)).add(this.up.scale(maxVertical)).add(this.normal.scale(minDepth)),
this.center.add(this.right.scale(maxHorizontal)).add(this.up.scale(maxVertical)).add(this.normal.scale(maxDepth))
};
double minX = Double.POSITIVE_INFINITY;
double minY = Double.POSITIVE_INFINITY;
double minZ = Double.POSITIVE_INFINITY;
double maxX = Double.NEGATIVE_INFINITY;
double maxY = Double.NEGATIVE_INFINITY;
double maxZ = Double.NEGATIVE_INFINITY;
for (Vec3 corner : corners) {
minX = Math.min(minX, corner.x);
minY = Math.min(minY, corner.y);
minZ = Math.min(minZ, corner.z);
maxX = Math.max(maxX, corner.x);
maxY = Math.max(maxY, corner.y);
maxZ = Math.max(maxZ, corner.z);
}
return new AABB(minX, minY, minZ, maxX, maxY, maxZ);
}
private static Vec3[] corners(AABB box) {
return new Vec3[] {
new Vec3(box.minX, box.minY, box.minZ),
new Vec3(box.minX, box.minY, box.maxZ),
new Vec3(box.minX, box.maxY, box.minZ),
new Vec3(box.minX, box.maxY, box.maxZ),
new Vec3(box.maxX, box.minY, box.minZ),
new Vec3(box.maxX, box.minY, box.maxZ),
new Vec3(box.maxX, box.maxY, box.minZ),
new Vec3(box.maxX, box.maxY, box.maxZ)
};
}
public record PortalLocalCoords(double horizontal, double vertical, double depth) {
}
}
@@ -0,0 +1,17 @@
package com.craisinlord.antarchy.content.worldgen.truffalo;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.levelgen.feature.stateproviders.BlockStateProvider;
public record TruffaloTreeConfiguration(
BlockStateProvider trunkProvider,
IntProvider height
) implements FeatureConfiguration {
public static final Codec<TruffaloTreeConfiguration> CODEC = RecordCodecBuilder.create(instance -> instance.group(
BlockStateProvider.CODEC.fieldOf("trunk_provider").forGetter(TruffaloTreeConfiguration::trunkProvider),
IntProvider.codec(4, 9).fieldOf("height").forGetter(TruffaloTreeConfiguration::height)
).apply(instance, TruffaloTreeConfiguration::new));
}
@@ -0,0 +1,88 @@
package com.craisinlord.antarchy.content.worldgen.truffalo;
import com.craisinlord.antarchy.Antarchy;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
import net.minecraft.world.item.DyeColor;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.levelgen.feature.FeaturePlaceContext;
public class TruffaloTreeFeature extends Feature<TruffaloTreeConfiguration> {
private static final DyeColor[] TUFT_COLORS = {
DyeColor.MAGENTA,
DyeColor.ORANGE,
DyeColor.RED,
DyeColor.YELLOW,
DyeColor.LIME,
DyeColor.GREEN,
DyeColor.BLUE,
DyeColor.CYAN,
DyeColor.LIGHT_BLUE,
DyeColor.PINK,
DyeColor.PURPLE
};
public TruffaloTreeFeature(Codec<TruffaloTreeConfiguration> codec) {
super(codec);
}
@Override
public boolean place(FeaturePlaceContext<TruffaloTreeConfiguration> context) {
WorldGenLevel level = context.level();
BlockPos origin = context.origin();
BlockPos supportPos = origin.below();
if (!level.getBlockState(supportPos).isFaceSturdy(level, supportPos, Direction.UP)) {
return false;
}
RandomSource random = context.random();
TruffaloTreeConfiguration config = context.config();
int height = config.height().sample(random);
DyeColor color = TUFT_COLORS[random.nextInt(TUFT_COLORS.length)];
BlockPos[] trunk = new BlockPos[height];
for (int i = 0; i < height; i++) {
trunk[i] = origin.above(i);
}
BlockPos capCenter = trunk[height - 1].above();
for (BlockPos pos : trunk) {
if (!canReplace(level, pos)) {
return false;
}
}
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
if (!canReplace(level, capCenter.offset(dx, 0, dz))) {
return false;
}
}
}
for (BlockPos pos : trunk) {
setBlock(level, pos, config.trunkProvider().getState(random, pos));
}
Block tuft = BuiltInRegistries.BLOCK.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, color.getName() + "_truffalo_tuft"));
BlockState tuftState = tuft.defaultBlockState();
for (int dx = -1; dx <= 1; dx++) {
for (int dz = -1; dz <= 1; dz++) {
setBlock(level, capCenter.offset(dx, 0, dz), tuftState);
}
}
return true;
}
private static boolean canReplace(WorldGenLevel level, BlockPos pos) {
BlockState state = level.getBlockState(pos);
return state.isAir() || state.canBeReplaced();
}
}
@@ -0,0 +1,18 @@
package com.craisinlord.antarchy.content.worldgen.truffalo;
import com.craisinlord.antarchy.Antarchy;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
public final class TruffaloTreeGrowers {
public static final ResourceKey<ConfiguredFeature<?, ?>> TRUFFALO_TREE = featureKey("truffalo_tree");
private TruffaloTreeGrowers() {
}
private static ResourceKey<ConfiguredFeature<?, ?>> featureKey(String path) {
return ResourceKey.create(Registries.CONFIGURED_FEATURE, ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path));
}
}
@@ -0,0 +1,17 @@
package com.craisinlord.antarchy.mixins;
import net.minecraft.world.entity.projectile.AbstractArrow;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(AbstractArrow.class)
public interface AbstractArrowAccessor {
@Accessor("inGround")
void antarchy$setInGround(boolean inGround);
@Accessor("inGroundTime")
void antarchy$setInGroundTime(int inGroundTime);
@Accessor("shakeTime")
void antarchy$setShakeTime(int shakeTime);
}
@@ -38,6 +38,22 @@ public abstract class BlockEntityTypeSignMixin {
private static Block antarchy$nadirHangingSign;
@Unique
private static Block antarchy$nadirWallHangingSign;
@Unique
private static Block antarchy$royalSign;
@Unique
private static Block antarchy$royalWallSign;
@Unique
private static Block antarchy$royalHangingSign;
@Unique
private static Block antarchy$royalWallHangingSign;
@Unique
private static Block antarchy$truffaloSign;
@Unique
private static Block antarchy$truffaloWallSign;
@Unique
private static Block antarchy$truffaloHangingSign;
@Unique
private static Block antarchy$truffaloWallHangingSign;
@Inject(method = "isValid", at = @At("HEAD"), cancellable = true)
private void antarchy$allowOuranwoodSigns(BlockState state, CallbackInfoReturnable<Boolean> cir) {
@@ -45,13 +61,17 @@ public abstract class BlockEntityTypeSignMixin {
if (self == BlockEntityType.SIGN) {
if (state.is(antarchy$sign()) || state.is(antarchy$wallSign())
|| state.is(antarchy$peachSign()) || state.is(antarchy$peachWallSign())
|| state.is(antarchy$nadirSign()) || state.is(antarchy$nadirWallSign())) {
|| state.is(antarchy$nadirSign()) || state.is(antarchy$nadirWallSign())
|| state.is(antarchy$royalSign()) || state.is(antarchy$royalWallSign())
|| state.is(antarchy$truffaloSign()) || state.is(antarchy$truffaloWallSign())) {
cir.setReturnValue(true);
}
} else if (self == BlockEntityType.HANGING_SIGN) {
if (state.is(antarchy$hangingSign()) || state.is(antarchy$wallHangingSign())
|| state.is(antarchy$peachHangingSign()) || state.is(antarchy$peachWallHangingSign())
|| state.is(antarchy$nadirHangingSign()) || state.is(antarchy$nadirWallHangingSign())) {
|| state.is(antarchy$nadirHangingSign()) || state.is(antarchy$nadirWallHangingSign())
|| state.is(antarchy$royalHangingSign()) || state.is(antarchy$royalWallHangingSign())
|| state.is(antarchy$truffaloHangingSign()) || state.is(antarchy$truffaloWallHangingSign())) {
cir.setReturnValue(true);
}
}
@@ -153,6 +173,70 @@ public abstract class BlockEntityTypeSignMixin {
return antarchy$nadirWallHangingSign;
}
@Unique
private static Block antarchy$royalSign() {
if (antarchy$royalSign == null) {
antarchy$royalSign = antarchy$resolve("royal_sign");
}
return antarchy$royalSign;
}
@Unique
private static Block antarchy$royalWallSign() {
if (antarchy$royalWallSign == null) {
antarchy$royalWallSign = antarchy$resolve("royal_wall_sign");
}
return antarchy$royalWallSign;
}
@Unique
private static Block antarchy$royalHangingSign() {
if (antarchy$royalHangingSign == null) {
antarchy$royalHangingSign = antarchy$resolve("royal_hanging_sign");
}
return antarchy$royalHangingSign;
}
@Unique
private static Block antarchy$royalWallHangingSign() {
if (antarchy$royalWallHangingSign == null) {
antarchy$royalWallHangingSign = antarchy$resolve("royal_wall_hanging_sign");
}
return antarchy$royalWallHangingSign;
}
@Unique
private static Block antarchy$truffaloSign() {
if (antarchy$truffaloSign == null) {
antarchy$truffaloSign = antarchy$resolve("truffalo_sign");
}
return antarchy$truffaloSign;
}
@Unique
private static Block antarchy$truffaloWallSign() {
if (antarchy$truffaloWallSign == null) {
antarchy$truffaloWallSign = antarchy$resolve("truffalo_wall_sign");
}
return antarchy$truffaloWallSign;
}
@Unique
private static Block antarchy$truffaloHangingSign() {
if (antarchy$truffaloHangingSign == null) {
antarchy$truffaloHangingSign = antarchy$resolve("truffalo_hanging_sign");
}
return antarchy$truffaloHangingSign;
}
@Unique
private static Block antarchy$truffaloWallHangingSign() {
if (antarchy$truffaloWallHangingSign == null) {
antarchy$truffaloWallHangingSign = antarchy$resolve("truffalo_wall_hanging_sign");
}
return antarchy$truffaloWallHangingSign;
}
@Unique
private static Block antarchy$resolve(String path) {
return BuiltInRegistries.BLOCK.get(ResourceLocation.fromNamespaceAndPath(Antarchy.MODID, path));
@@ -0,0 +1,11 @@
package com.craisinlord.antarchy.mixins;
import net.minecraft.world.entity.LivingEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(LivingEntity.class)
public interface LivingEntityJumpingAccessor {
@Accessor("jumping")
boolean antarchy$isJumping();
}
@@ -0,0 +1,33 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Entity.class)
public abstract class PortalGunEntityCollisionMixin {
@Unique
private AABB antarchy$portalGunStartBox;
@Inject(method = "collide", at = @At("HEAD"))
private void antarchy$capturePortalGunStartBox(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
this.antarchy$portalGunStartBox = ((Entity) (Object) this).getBoundingBox();
}
@Inject(method = "collide", at = @At("RETURN"), cancellable = true)
private void antarchy$restorePortalCrossingMotion(Vec3 movement, CallbackInfoReturnable<Vec3> cir) {
Entity entity = (Entity) (Object) this;
if (this.antarchy$portalGunStartBox == null || AntarchyGravityApi.isGravityInverted(entity)) {
return;
}
Vec3 adjusted = PortalGunCollisionHelper.resolveCollision(entity, this.antarchy$portalGunStartBox, movement, cir.getReturnValue());
cir.setReturnValue(adjusted);
}
}
@@ -0,0 +1,25 @@
package com.craisinlord.antarchy.mixins;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Entity.class)
public abstract class PortalGunEntityInWallMixin {
@Inject(method = "isInWall", at = @At("HEAD"), cancellable = true)
private void antarchy$ignorePortalInWall(CallbackInfoReturnable<Boolean> cir) {
Entity entity = (Entity) (Object) this;
if (entity.level() == null) {
return;
}
for (PortalGunPortalEntity portal : entity.level().getEntitiesOfClass(PortalGunPortalEntity.class, entity.getBoundingBox().inflate(2.0D), PortalGunPortalEntity::isAlive)) {
if (portal.intersectsEntityBounds(entity) || portal.getCollisionRemovalAabbForEntity(entity).intersects(entity.getBoundingBox())) {
cir.setReturnValue(false);
return;
}
}
}
}
@@ -0,0 +1,39 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderDispatcher;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.AABB;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(BlockEntityRenderDispatcher.class)
public abstract class BlockEntityRenderDispatcherPortalMixin {
@Inject(method = "render(Lnet/minecraft/world/level/block/entity/BlockEntity;FLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;)V", at = @At("HEAD"), cancellable = true)
private <E extends BlockEntity> void antarchy$clipPortalViewBlockEntities(E blockEntity, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, CallbackInfo ci) {
if (PortalGunPortalRenderState.renderAll() || blockEntity == null || blockEntity.isRemoved()) {
return;
}
PortalGunWorldPortalShape destinationShape = PortalGunPortalRenderState.getDestinationShape();
PortalGunWorldPortalShape sourceShape = PortalGunPortalRenderState.getSourceShape();
if (destinationShape == null) {
return;
}
BlockPos pos = blockEntity.getBlockPos();
AABB bounds = new AABB(pos);
if (!destinationShape.intersectsFront(bounds, 0.1D)) {
ci.cancel();
return;
}
if (sourceShape != null && sourceShape.intersectsFront(bounds, -0.02D)) {
ci.cancel();
}
}
}
@@ -0,0 +1,34 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.PortalGunPortalRenderState;
import com.craisinlord.antarchy.content.portalgun.PortalGunPortalEntity;
import com.craisinlord.antarchy.content.portalgun.PortalGunWorldPortalShape;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.entity.EntityRenderDispatcher;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(EntityRenderDispatcher.class)
public abstract class EntityRenderDispatcherPortalMixin {
@Inject(method = "shouldRender", at = @At("HEAD"), cancellable = true)
private <E extends Entity> void antarchy$clipPortalViewEntities(E entity, Frustum frustum, double x, double y, double z, CallbackInfoReturnable<Boolean> cir) {
if (PortalGunPortalRenderState.renderAll() || entity instanceof PortalGunPortalEntity) {
return;
}
PortalGunWorldPortalShape destinationShape = PortalGunPortalRenderState.getDestinationShape();
PortalGunWorldPortalShape sourceShape = PortalGunPortalRenderState.getSourceShape();
if (destinationShape == null) {
return;
}
if (!destinationShape.intersectsFront(entity.getBoundingBox(), 0.1D)) {
cir.setReturnValue(false);
return;
}
if (sourceShape != null && sourceShape.intersectsFront(entity.getBoundingBox(), -0.02D)) {
cir.setReturnValue(false);
}
}
}
@@ -0,0 +1,48 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.entity.HoverboardEntity;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.model.EntityModel;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.entity.LivingEntityRenderer;
import net.minecraft.world.entity.LivingEntity;
import com.mojang.math.Axis;
import org.objectweb.asm.Opcodes;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntityRenderer.class)
public abstract class HoverboardRiderPoseMixin<T extends LivingEntity, M extends EntityModel<T>> {
private static final float HOVERBOARD_RIDER_RENDER_YAW = -90.0F;
@Shadow
public abstract M getModel();
@Inject(
method = "setupRotations(Lnet/minecraft/world/entity/LivingEntity;Lcom/mojang/blaze3d/vertex/PoseStack;FFFF)V",
at = @At("TAIL")
)
private void antarchy$rotateHoverboardRider(T entity, PoseStack poseStack, float ageInTicks, float rotationYaw, float partialTick, float scale, CallbackInfo ci) {
if (entity.getVehicle() instanceof HoverboardEntity) {
poseStack.mulPose(Axis.YP.rotationDegrees(HOVERBOARD_RIDER_RENDER_YAW));
}
}
@Inject(
method = "render(Lnet/minecraft/world/entity/LivingEntity;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/client/model/EntityModel;riding:Z",
opcode = Opcodes.PUTFIELD,
shift = At.Shift.AFTER
)
)
private void antarchy$standOnHoverboard(T entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, CallbackInfo ci) {
if (entity.getVehicle() instanceof HoverboardEntity) {
this.getModel().riding = false;
}
}
}
@@ -0,0 +1,30 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.PortalGunRollClientState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(LocalPlayer.class)
public abstract class LocalPlayerPortalMixin {
@Inject(method = "suffocatesAt", at = @At("HEAD"), cancellable = true)
private void antarchy$ignorePortalSuffocation(BlockPos pos, CallbackInfoReturnable<Boolean> cir) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player != null && PortalGunRollClientState.isEntityInsidePortal(minecraft, minecraft.player)) {
cir.setReturnValue(false);
}
}
@Inject(method = "moveTowardsClosestSpace", at = @At("HEAD"), cancellable = true)
private void antarchy$cancelPortalPushOut(double x, double z, CallbackInfo ci) {
Minecraft minecraft = Minecraft.getInstance();
if (minecraft.player != null && PortalGunRollClientState.isEntityInsidePortal(minecraft, minecraft.player)) {
ci.cancel();
}
}
}
@@ -0,0 +1,22 @@
package com.craisinlord.antarchy.mixins.client;
import com.craisinlord.antarchy.content.client.PortalGunRollClientState;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ScreenEffectRenderer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.Blocks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(ScreenEffectRenderer.class)
public abstract class ScreenEffectRendererPortalMixin {
@Inject(method = "getViewBlockingState", at = @At("HEAD"), cancellable = true)
private static void antarchy$ignorePortalViewBlock(Player player, CallbackInfoReturnable<net.minecraft.world.level.block.state.BlockState> cir) {
Minecraft minecraft = Minecraft.getInstance();
if (player != null && PortalGunRollClientState.isEntityInsidePortal(minecraft, player)) {
cir.setReturnValue(Blocks.AIR.defaultBlockState());
}
}
}
@@ -4,6 +4,7 @@ import com.craisinlord.antarchy.content.fluid.AntarchyFluidChecks;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityDirection;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import com.craisinlord.antarchy.content.portalgun.PortalGunCollisionHelper;
import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;
@@ -174,7 +175,7 @@ public abstract class EntityMoveMixin {
float maxUpStep = entity.maxUpStep();
if (!(maxUpStep > 0.0F) || !(pressingIntoCeiling || entity.onGround()) || !(movedX || movedZ)) {
cir.setReturnValue(collided);
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(entity, aabb, movement, collided));
return;
}
@@ -193,12 +194,12 @@ public abstract class EntityMoveMixin {
Vec3 stepped = Entity.collideBoundingBox(entity, new Vec3(movement.x, -(double) candidate, movement.z), steppedBox, entity.level(), stepShapes);
if (stepped.horizontalDistanceSqr() > collided.horizontalDistanceSqr()) {
double offset = steppedBox.maxY - aabb.maxY;
cir.setReturnValue(stepped.add(0.0, offset, 0.0));
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(entity, aabb, movement, stepped.add(0.0, offset, 0.0)));
return;
}
}
cir.setReturnValue(collided);
cir.setReturnValue(PortalGunCollisionHelper.resolveCollision(entity, aabb, movement, collided));
}
@Unique
@@ -1,5 +1,6 @@
package com.craisinlord.antarchy.mixins.gravity.client;
import com.craisinlord.antarchy.content.client.PortalGunRollClientState;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityApi;
import com.craisinlord.antarchy.content.gravity.AntarchyGravityRotationUtil;
import net.minecraft.client.Camera;
@@ -128,9 +129,12 @@ public abstract class CameraGravityMixin {
return;
}
if (!AntarchyGravityApi.isGravityInverted(this.entity) && AntarchyGravityApi.getGravityFlipProgress(this.entity, this.antarchy$storedTickDelta) <= 0.0F) {
this.rotation.premul(new Quaternionf().fromAxisAngleDeg(0.0F, 0.0F, -1.0F, PortalGunRollClientState.getRollDegrees(this.antarchy$storedTickDelta)));
this.antarchy$gravityApplied = true;
return;
}
this.rotation.premul(AntarchyGravityRotationUtil.getCameraRotationQuaternion(this.entity, this.antarchy$storedTickDelta));
this.rotation.premul(new Quaternionf().fromAxisAngleDeg(0.0F, 0.0F, -1.0F, PortalGunRollClientState.getRollDegrees(this.antarchy$storedTickDelta)));
this.antarchy$gravityApplied = true;
}
}
@@ -5,6 +5,7 @@
"compatibilityLevel": "JAVA_21",
"mixins": [
"AbstractMinecartMixin",
"AbstractArrowAccessor",
"AccessorFireBlock",
"AccessorRangedAttribute",
"AmericanFoodConsumeMixin",
@@ -27,6 +28,7 @@
"GlowSquidLumenSpawnMixin",
"GlowVinesUnderLeavesMixin",
"GrindstoneMenuMixin",
"LivingEntityJumpingAccessor",
"MantisClawWallClimbMixin",
"MilkBucketSizeResetMixin",
"MinecartItemMixin",
@@ -35,6 +37,8 @@
"NightmareSwordDamageMixin",
"NightmareArmorEffectImmunityMixin",
"PotionBrewingMixin",
"PortalGunEntityCollisionMixin",
"PortalGunEntityInWallMixin",
"RedStoneWireBlockMixin",
"StinkyCrouchMixin",
"VillagerTradesMixin",
@@ -78,9 +82,11 @@
],
"client": [
"client.AbstractMinecartRendererMixin",
"client.BlockEntityRenderDispatcherPortalMixin",
"client.EntityLightProbeMixin",
"client.CustomBossBarMixin",
"client.DimensionMusicMixin",
"client.EntityRenderDispatcherPortalMixin",
"client.CavarynSkyMixin",
"client.CavarynHordeFogMixin",
"client.CavarynHordeLightTextureMixin",
@@ -93,8 +99,11 @@
"client.LightningBoltRendererMixin",
"client.MountedVehicleCameraMixin",
"client.ParalyzedMouseTurnMixin",
"client.LocalPlayerPortalMixin",
"client.ScreenEffectRendererPortalMixin",
"client.ScreenEffectRendererFireVisualMixin",
"client.GlimmeringTranslucencyMixin",
"client.HoverboardRiderPoseMixin",
"client.SplashManagerMixin",
"client.ThoraxisFogMixin",
"client.TigerEyeArmorHideMixin",
@@ -0,0 +1,361 @@
{
"format_version": "1.8.0",
"animations": {
"idle": {
"loop": true,
"animation_length": 1,
"bones": {
"hoverboard": {
"rotation": {
"0.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
},
"0.25": {
"post": {
"vector": [0, 0, -9.85]
},
"lerp_mode": "catmullrom"
},
"0.7083": {
"post": {
"vector": [0, 0, 7.5]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
}
},
"position": {
"0.0": {
"post": {
"vector": [0, 2, 0]
},
"lerp_mode": "catmullrom"
},
"0.5": {
"post": {
"vector": [0, 3, 0]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [0, 2, 0]
},
"lerp_mode": "catmullrom"
}
}
},
"disk_front": {
"position": {
"0.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
},
"0.6667": {
"post": {
"vector": [0.35282, -2.97918, 0]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
}
}
},
"disk_back": {
"position": {
"0.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
},
"0.5": {
"post": {
"vector": [-0.00833, -1.99998, 0]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
}
}
},
"motor_right": {
"scale": {
"0.0": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
},
"0.25": {
"post": {
"vector": [1.1, 1, 1.1]
},
"lerp_mode": "catmullrom"
},
"0.5": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
},
"0.75": {
"post": {
"vector": [1.1, 1, 1.1]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
}
}
},
"motor_left": {
"scale": {
"0.0": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
},
"0.25": {
"post": {
"vector": [1.1, 1, 1.1]
},
"lerp_mode": "catmullrom"
},
"0.5": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
},
"0.75": {
"post": {
"vector": [1.1, 1, 1.1]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
}
}
}
}
},
"move": {
"loop": true,
"animation_length": 1,
"bones": {
"hoverboard": {
"rotation": {
"0.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
},
"0.25": {
"post": {
"vector": [0, 0, -9.85]
},
"lerp_mode": "catmullrom"
},
"0.7083": {
"post": {
"vector": [0, 0, 7.5]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
}
},
"position": {
"0.0": {
"post": {
"vector": [0, 2, 0]
},
"lerp_mode": "catmullrom"
},
"0.125": {
"post": {
"vector": [0, 3.16, 0]
},
"lerp_mode": "catmullrom"
},
"0.3333": {
"post": {
"vector": [0, 2.2, 0]
},
"lerp_mode": "catmullrom"
},
"0.5": {
"post": {
"vector": [0, 4, 0]
},
"lerp_mode": "catmullrom"
},
"0.7083": {
"post": {
"vector": [0, 2.28, 0]
},
"lerp_mode": "catmullrom"
},
"0.875": {
"post": {
"vector": [0, 2.94, 0]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [0, 2, 0]
},
"lerp_mode": "catmullrom"
}
}
},
"disk_front": {
"position": {
"0.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
},
"0.6667": {
"post": {
"vector": [0.35282, -2.97918, 0]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
}
}
},
"disk_back": {
"position": {
"0.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
},
"0.5": {
"post": {
"vector": [-0.00833, -1.99998, 0]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [0, 0, 0]
},
"lerp_mode": "catmullrom"
}
}
},
"motor_right": {
"scale": {
"0.0": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
},
"0.25": {
"post": {
"vector": [1.1, 1, 1.4]
},
"lerp_mode": "catmullrom"
},
"0.5": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
},
"0.75": {
"post": {
"vector": [1.1, 1, 1.4]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
}
}
},
"motor_left": {
"scale": {
"0.0": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
},
"0.25": {
"post": {
"vector": [1.1, 1, 1.4]
},
"lerp_mode": "catmullrom"
},
"0.5": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
},
"0.75": {
"post": {
"vector": [1.1, 1, 1.4]
},
"lerp_mode": "catmullrom"
},
"1.0": {
"post": {
"vector": [1, 1, 1]
},
"lerp_mode": "catmullrom"
}
}
}
}
}
}
}
@@ -4,20 +4,7 @@
"idle": {
"loop": true,
"animation_length": 4.0,
"bones": {
"Portal_Layer": {
"rotation": {
"0.0": {"vector": [0, 0, 0]},
"4.0": {"vector": [0, 0, 360]}
}
},
"Portal_Layer2": {
"rotation": {
"0.0": {"vector": [0, 0, 0]},
"4.0": {"vector": [0, 0, -360]}
}
}
}
"bones": {}
},
"open": {
"loop": false,
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_black"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_blue"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_brown"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_cyan"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_gray"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_green"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_light_blue"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_light_gray"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_lime"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_magenta"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_orange"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_pink"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/potted_royal_sapling"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/potted_truffalo_sapling"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_purple"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_tuft_red"
}
}
}
@@ -0,0 +1,119 @@
{
"variants": {
"face=ceiling,facing=east,powered=false": {
"model": "antarchy:block/royal_button",
"x": 180,
"y": 270
},
"face=ceiling,facing=east,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"x": 180,
"y": 270
},
"face=ceiling,facing=north,powered=false": {
"model": "antarchy:block/royal_button",
"x": 180,
"y": 180
},
"face=ceiling,facing=north,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"x": 180,
"y": 180
},
"face=ceiling,facing=south,powered=false": {
"model": "antarchy:block/royal_button",
"x": 180
},
"face=ceiling,facing=south,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"x": 180
},
"face=ceiling,facing=west,powered=false": {
"model": "antarchy:block/royal_button",
"x": 180,
"y": 90
},
"face=ceiling,facing=west,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"x": 180,
"y": 90
},
"face=floor,facing=east,powered=false": {
"model": "antarchy:block/royal_button",
"y": 90
},
"face=floor,facing=east,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"y": 90
},
"face=floor,facing=north,powered=false": {
"model": "antarchy:block/royal_button"
},
"face=floor,facing=north,powered=true": {
"model": "antarchy:block/royal_button_pressed"
},
"face=floor,facing=south,powered=false": {
"model": "antarchy:block/royal_button",
"y": 180
},
"face=floor,facing=south,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"y": 180
},
"face=floor,facing=west,powered=false": {
"model": "antarchy:block/royal_button",
"y": 270
},
"face=floor,facing=west,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"y": 270
},
"face=wall,facing=east,powered=false": {
"model": "antarchy:block/royal_button",
"uvlock": true,
"x": 90,
"y": 90
},
"face=wall,facing=east,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"uvlock": true,
"x": 90,
"y": 90
},
"face=wall,facing=north,powered=false": {
"model": "antarchy:block/royal_button",
"uvlock": true,
"x": 90
},
"face=wall,facing=north,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"uvlock": true,
"x": 90
},
"face=wall,facing=south,powered=false": {
"model": "antarchy:block/royal_button",
"uvlock": true,
"x": 90,
"y": 180
},
"face=wall,facing=south,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"uvlock": true,
"x": 90,
"y": 180
},
"face=wall,facing=west,powered=false": {
"model": "antarchy:block/royal_button",
"uvlock": true,
"x": 90,
"y": 270
},
"face=wall,facing=west,powered=true": {
"model": "antarchy:block/royal_button_pressed",
"uvlock": true,
"x": 90,
"y": 270
}
}
}
@@ -0,0 +1,125 @@
{
"variants": {
"facing=east,half=lower,hinge=left,open=false": {
"model": "antarchy:block/royal_door_bottom_left"
},
"facing=east,half=lower,hinge=left,open=true": {
"model": "antarchy:block/royal_door_bottom_left_open",
"y": 90
},
"facing=east,half=lower,hinge=right,open=false": {
"model": "antarchy:block/royal_door_bottom_right"
},
"facing=east,half=lower,hinge=right,open=true": {
"model": "antarchy:block/royal_door_bottom_right_open",
"y": 270
},
"facing=east,half=upper,hinge=left,open=false": {
"model": "antarchy:block/royal_door_top_left"
},
"facing=east,half=upper,hinge=left,open=true": {
"model": "antarchy:block/royal_door_top_left_open",
"y": 90
},
"facing=east,half=upper,hinge=right,open=false": {
"model": "antarchy:block/royal_door_top_right"
},
"facing=east,half=upper,hinge=right,open=true": {
"model": "antarchy:block/royal_door_top_right_open",
"y": 270
},
"facing=north,half=lower,hinge=left,open=false": {
"model": "antarchy:block/royal_door_bottom_left",
"y": 270
},
"facing=north,half=lower,hinge=left,open=true": {
"model": "antarchy:block/royal_door_bottom_left_open"
},
"facing=north,half=lower,hinge=right,open=false": {
"model": "antarchy:block/royal_door_bottom_right",
"y": 270
},
"facing=north,half=lower,hinge=right,open=true": {
"model": "antarchy:block/royal_door_bottom_right_open",
"y": 180
},
"facing=north,half=upper,hinge=left,open=false": {
"model": "antarchy:block/royal_door_top_left",
"y": 270
},
"facing=north,half=upper,hinge=left,open=true": {
"model": "antarchy:block/royal_door_top_left_open"
},
"facing=north,half=upper,hinge=right,open=false": {
"model": "antarchy:block/royal_door_top_right",
"y": 270
},
"facing=north,half=upper,hinge=right,open=true": {
"model": "antarchy:block/royal_door_top_right_open",
"y": 180
},
"facing=south,half=lower,hinge=left,open=false": {
"model": "antarchy:block/royal_door_bottom_left",
"y": 90
},
"facing=south,half=lower,hinge=left,open=true": {
"model": "antarchy:block/royal_door_bottom_left_open",
"y": 180
},
"facing=south,half=lower,hinge=right,open=false": {
"model": "antarchy:block/royal_door_bottom_right",
"y": 90
},
"facing=south,half=lower,hinge=right,open=true": {
"model": "antarchy:block/royal_door_bottom_right_open"
},
"facing=south,half=upper,hinge=left,open=false": {
"model": "antarchy:block/royal_door_top_left",
"y": 90
},
"facing=south,half=upper,hinge=left,open=true": {
"model": "antarchy:block/royal_door_top_left_open",
"y": 180
},
"facing=south,half=upper,hinge=right,open=false": {
"model": "antarchy:block/royal_door_top_right",
"y": 90
},
"facing=south,half=upper,hinge=right,open=true": {
"model": "antarchy:block/royal_door_top_right_open"
},
"facing=west,half=lower,hinge=left,open=false": {
"model": "antarchy:block/royal_door_bottom_left",
"y": 180
},
"facing=west,half=lower,hinge=left,open=true": {
"model": "antarchy:block/royal_door_bottom_left_open",
"y": 270
},
"facing=west,half=lower,hinge=right,open=false": {
"model": "antarchy:block/royal_door_bottom_right",
"y": 180
},
"facing=west,half=lower,hinge=right,open=true": {
"model": "antarchy:block/royal_door_bottom_right_open",
"y": 90
},
"facing=west,half=upper,hinge=left,open=false": {
"model": "antarchy:block/royal_door_top_left",
"y": 180
},
"facing=west,half=upper,hinge=left,open=true": {
"model": "antarchy:block/royal_door_top_left_open",
"y": 270
},
"facing=west,half=upper,hinge=right,open=false": {
"model": "antarchy:block/royal_door_top_right",
"y": 180
},
"facing=west,half=upper,hinge=right,open=true": {
"model": "antarchy:block/royal_door_top_right_open",
"y": 90
}
}
}
@@ -0,0 +1,49 @@
{
"multipart": [
{
"apply": {
"model": "antarchy:block/royal_fence_post"
}
},
{
"apply": {
"model": "antarchy:block/royal_fence_side",
"uvlock": true
},
"when": {
"north": "true"
}
},
{
"apply": {
"model": "antarchy:block/royal_fence_side",
"uvlock": true,
"y": 90
},
"when": {
"east": "true"
}
},
{
"apply": {
"model": "antarchy:block/royal_fence_side",
"uvlock": true,
"y": 180
},
"when": {
"south": "true"
}
},
{
"apply": {
"model": "antarchy:block/royal_fence_side",
"uvlock": true,
"y": 270
},
"when": {
"west": "true"
}
}
]
}
@@ -0,0 +1,81 @@
{
"variants": {
"facing=east,in_wall=false,open=false": {
"model": "antarchy:block/royal_fence_gate",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=false,open=true": {
"model": "antarchy:block/royal_fence_gate_open",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=true,open=false": {
"model": "antarchy:block/royal_fence_gate_wall",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=true,open=true": {
"model": "antarchy:block/royal_fence_gate_wall_open",
"uvlock": true,
"y": 270
},
"facing=north,in_wall=false,open=false": {
"model": "antarchy:block/royal_fence_gate",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=false,open=true": {
"model": "antarchy:block/royal_fence_gate_open",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=true,open=false": {
"model": "antarchy:block/royal_fence_gate_wall",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=true,open=true": {
"model": "antarchy:block/royal_fence_gate_wall_open",
"uvlock": true,
"y": 180
},
"facing=south,in_wall=false,open=false": {
"model": "antarchy:block/royal_fence_gate",
"uvlock": true
},
"facing=south,in_wall=false,open=true": {
"model": "antarchy:block/royal_fence_gate_open",
"uvlock": true
},
"facing=south,in_wall=true,open=false": {
"model": "antarchy:block/royal_fence_gate_wall",
"uvlock": true
},
"facing=south,in_wall=true,open=true": {
"model": "antarchy:block/royal_fence_gate_wall_open",
"uvlock": true
},
"facing=west,in_wall=false,open=false": {
"model": "antarchy:block/royal_fence_gate",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=false,open=true": {
"model": "antarchy:block/royal_fence_gate_open",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=true,open=false": {
"model": "antarchy:block/royal_fence_gate_wall",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=true,open=true": {
"model": "antarchy:block/royal_fence_gate_wall_open",
"uvlock": true,
"y": 90
}
}
}
@@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "antarchy:block/royal_flowering_leaves" }
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/royal_hanging_sign"
}
}
}
@@ -0,0 +1,5 @@
{
"variants": {
"": { "model": "antarchy:block/royal_leaves" }
}
}
@@ -0,0 +1,16 @@
{
"variants": {
"axis=x": {
"model": "antarchy:block/royal_log_horizontal",
"x": 90,
"y": 90
},
"axis=y": {
"model": "antarchy:block/royal_log"
},
"axis=z": {
"model": "antarchy:block/royal_log_horizontal",
"x": 90
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/royal_planks"
}
}
}
@@ -0,0 +1,10 @@
{
"variants": {
"powered=false": {
"model": "antarchy:block/royal_pressure_plate"
},
"powered=true": {
"model": "antarchy:block/royal_pressure_plate_down"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/royal_sapling"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/royal_sign"
}
}
}
@@ -0,0 +1,13 @@
{
"variants": {
"type=bottom": {
"model": "antarchy:block/royal_slab"
},
"type=double": {
"model": "antarchy:block/royal_planks"
},
"type=top": {
"model": "antarchy:block/royal_slab_top"
}
}
}
@@ -0,0 +1,210 @@
{
"variants": {
"facing=east,half=bottom,shape=inner_left": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"y": 270
},
"facing=east,half=bottom,shape=inner_right": {
"model": "antarchy:block/royal_stairs_inner"
},
"facing=east,half=bottom,shape=outer_left": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"y": 270
},
"facing=east,half=bottom,shape=outer_right": {
"model": "antarchy:block/royal_stairs_outer"
},
"facing=east,half=bottom,shape=straight": {
"model": "antarchy:block/royal_stairs"
},
"facing=east,half=top,shape=inner_left": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"x": 180
},
"facing=east,half=top,shape=inner_right": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"x": 180,
"y": 90
},
"facing=east,half=top,shape=outer_left": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"x": 180
},
"facing=east,half=top,shape=outer_right": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"x": 180,
"y": 90
},
"facing=east,half=top,shape=straight": {
"model": "antarchy:block/royal_stairs",
"uvlock": true,
"x": 180
},
"facing=north,half=bottom,shape=inner_left": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"y": 180
},
"facing=north,half=bottom,shape=inner_right": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"y": 270
},
"facing=north,half=bottom,shape=outer_left": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"y": 180
},
"facing=north,half=bottom,shape=outer_right": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"y": 270
},
"facing=north,half=bottom,shape=straight": {
"model": "antarchy:block/royal_stairs",
"uvlock": true,
"y": 270
},
"facing=north,half=top,shape=inner_left": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"x": 180,
"y": 270
},
"facing=north,half=top,shape=inner_right": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"x": 180
},
"facing=north,half=top,shape=outer_left": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"x": 180,
"y": 270
},
"facing=north,half=top,shape=outer_right": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"x": 180
},
"facing=north,half=top,shape=straight": {
"model": "antarchy:block/royal_stairs",
"uvlock": true,
"x": 180,
"y": 270
},
"facing=south,half=bottom,shape=inner_left": {
"model": "antarchy:block/royal_stairs_inner"
},
"facing=south,half=bottom,shape=inner_right": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"y": 90
},
"facing=south,half=bottom,shape=outer_left": {
"model": "antarchy:block/royal_stairs_outer"
},
"facing=south,half=bottom,shape=outer_right": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"y": 90
},
"facing=south,half=bottom,shape=straight": {
"model": "antarchy:block/royal_stairs",
"uvlock": true,
"y": 90
},
"facing=south,half=top,shape=inner_left": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"x": 180,
"y": 90
},
"facing=south,half=top,shape=inner_right": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"x": 180,
"y": 180
},
"facing=south,half=top,shape=outer_left": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"x": 180,
"y": 90
},
"facing=south,half=top,shape=outer_right": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"x": 180,
"y": 180
},
"facing=south,half=top,shape=straight": {
"model": "antarchy:block/royal_stairs",
"uvlock": true,
"x": 180,
"y": 90
},
"facing=west,half=bottom,shape=inner_left": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"y": 90
},
"facing=west,half=bottom,shape=inner_right": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"y": 180
},
"facing=west,half=bottom,shape=outer_left": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"y": 90
},
"facing=west,half=bottom,shape=outer_right": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"y": 180
},
"facing=west,half=bottom,shape=straight": {
"model": "antarchy:block/royal_stairs",
"uvlock": true,
"y": 180
},
"facing=west,half=top,shape=inner_left": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"x": 180,
"y": 180
},
"facing=west,half=top,shape=inner_right": {
"model": "antarchy:block/royal_stairs_inner",
"uvlock": true,
"x": 180,
"y": 270
},
"facing=west,half=top,shape=outer_left": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"x": 180,
"y": 180
},
"facing=west,half=top,shape=outer_right": {
"model": "antarchy:block/royal_stairs_outer",
"uvlock": true,
"x": 180,
"y": 270
},
"facing=west,half=top,shape=straight": {
"model": "antarchy:block/royal_stairs",
"uvlock": true,
"x": 180,
"y": 180
}
}
}
@@ -0,0 +1,70 @@
{
"variants": {
"facing=east,half=bottom,open=false": {
"model": "antarchy:block/royal_trapdoor_bottom",
"y": 90
},
"facing=east,half=bottom,open=true": {
"model": "antarchy:block/royal_trapdoor_open",
"y": 90
},
"facing=east,half=top,open=false": {
"model": "antarchy:block/royal_trapdoor_top",
"y": 90
},
"facing=east,half=top,open=true": {
"model": "antarchy:block/royal_trapdoor_open",
"x": 180,
"y": 270
},
"facing=north,half=bottom,open=false": {
"model": "antarchy:block/royal_trapdoor_bottom"
},
"facing=north,half=bottom,open=true": {
"model": "antarchy:block/royal_trapdoor_open"
},
"facing=north,half=top,open=false": {
"model": "antarchy:block/royal_trapdoor_top"
},
"facing=north,half=top,open=true": {
"model": "antarchy:block/royal_trapdoor_open",
"x": 180,
"y": 180
},
"facing=south,half=bottom,open=false": {
"model": "antarchy:block/royal_trapdoor_bottom",
"y": 180
},
"facing=south,half=bottom,open=true": {
"model": "antarchy:block/royal_trapdoor_open",
"y": 180
},
"facing=south,half=top,open=false": {
"model": "antarchy:block/royal_trapdoor_top",
"y": 180
},
"facing=south,half=top,open=true": {
"model": "antarchy:block/royal_trapdoor_open",
"x": 180,
"y": 0
},
"facing=west,half=bottom,open=false": {
"model": "antarchy:block/royal_trapdoor_bottom",
"y": 270
},
"facing=west,half=bottom,open=true": {
"model": "antarchy:block/royal_trapdoor_open",
"y": 270
},
"facing=west,half=top,open=false": {
"model": "antarchy:block/royal_trapdoor_top",
"y": 270
},
"facing=west,half=top,open=true": {
"model": "antarchy:block/royal_trapdoor_open",
"x": 180,
"y": 90
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/royal_wall_hanging_sign"
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/royal_wall_sign"
}
}
}
@@ -0,0 +1,16 @@
{
"variants": {
"axis=x": {
"model": "antarchy:block/royal_wood",
"x": 90,
"y": 90
},
"axis=y": {
"model": "antarchy:block/royal_wood"
},
"axis=z": {
"model": "antarchy:block/royal_wood",
"x": 90
}
}
}
@@ -0,0 +1,16 @@
{
"variants": {
"axis=x": {
"model": "antarchy:block/stripped_royal_log_horizontal",
"x": 90,
"y": 90
},
"axis=y": {
"model": "antarchy:block/stripped_royal_log"
},
"axis=z": {
"model": "antarchy:block/stripped_royal_log_horizontal",
"x": 90
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/stripped_royal_wood"
}
}
}
@@ -0,0 +1,14 @@
{
"variants": {
"axis=x": {
"model": "antarchy:block/stripped_truffalo_log_horizontal"
},
"axis=y": {
"model": "antarchy:block/stripped_truffalo_log"
},
"axis=z": {
"model": "antarchy:block/stripped_truffalo_log_horizontal",
"y": 90
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/stripped_truffalo_wood"
}
}
}
@@ -0,0 +1,119 @@
{
"variants": {
"face=ceiling,facing=east,powered=false": {
"model": "antarchy:block/truffalo_button",
"x": 180,
"y": 270
},
"face=ceiling,facing=east,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"x": 180,
"y": 270
},
"face=ceiling,facing=north,powered=false": {
"model": "antarchy:block/truffalo_button",
"x": 180,
"y": 180
},
"face=ceiling,facing=north,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"x": 180,
"y": 180
},
"face=ceiling,facing=south,powered=false": {
"model": "antarchy:block/truffalo_button",
"x": 180
},
"face=ceiling,facing=south,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"x": 180
},
"face=ceiling,facing=west,powered=false": {
"model": "antarchy:block/truffalo_button",
"x": 180,
"y": 90
},
"face=ceiling,facing=west,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"x": 180,
"y": 90
},
"face=floor,facing=east,powered=false": {
"model": "antarchy:block/truffalo_button",
"y": 90
},
"face=floor,facing=east,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"y": 90
},
"face=floor,facing=north,powered=false": {
"model": "antarchy:block/truffalo_button"
},
"face=floor,facing=north,powered=true": {
"model": "antarchy:block/truffalo_button_pressed"
},
"face=floor,facing=south,powered=false": {
"model": "antarchy:block/truffalo_button",
"y": 180
},
"face=floor,facing=south,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"y": 180
},
"face=floor,facing=west,powered=false": {
"model": "antarchy:block/truffalo_button",
"y": 270
},
"face=floor,facing=west,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"y": 270
},
"face=wall,facing=east,powered=false": {
"model": "antarchy:block/truffalo_button",
"uvlock": true,
"x": 90,
"y": 90
},
"face=wall,facing=east,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"uvlock": true,
"x": 90,
"y": 90
},
"face=wall,facing=north,powered=false": {
"model": "antarchy:block/truffalo_button",
"uvlock": true,
"x": 90
},
"face=wall,facing=north,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"uvlock": true,
"x": 90
},
"face=wall,facing=south,powered=false": {
"model": "antarchy:block/truffalo_button",
"uvlock": true,
"x": 90,
"y": 180
},
"face=wall,facing=south,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"uvlock": true,
"x": 90,
"y": 180
},
"face=wall,facing=west,powered=false": {
"model": "antarchy:block/truffalo_button",
"uvlock": true,
"x": 90,
"y": 270
},
"face=wall,facing=west,powered=true": {
"model": "antarchy:block/truffalo_button_pressed",
"uvlock": true,
"x": 90,
"y": 270
}
}
}
@@ -0,0 +1,125 @@
{
"variants": {
"facing=east,half=lower,hinge=left,open=false": {
"model": "antarchy:block/truffalo_door_bottom_left"
},
"facing=east,half=lower,hinge=left,open=true": {
"model": "antarchy:block/truffalo_door_bottom_left_open",
"y": 90
},
"facing=east,half=lower,hinge=right,open=false": {
"model": "antarchy:block/truffalo_door_bottom_right"
},
"facing=east,half=lower,hinge=right,open=true": {
"model": "antarchy:block/truffalo_door_bottom_right_open",
"y": 270
},
"facing=east,half=upper,hinge=left,open=false": {
"model": "antarchy:block/truffalo_door_top_left"
},
"facing=east,half=upper,hinge=left,open=true": {
"model": "antarchy:block/truffalo_door_top_left_open",
"y": 90
},
"facing=east,half=upper,hinge=right,open=false": {
"model": "antarchy:block/truffalo_door_top_right"
},
"facing=east,half=upper,hinge=right,open=true": {
"model": "antarchy:block/truffalo_door_top_right_open",
"y": 270
},
"facing=north,half=lower,hinge=left,open=false": {
"model": "antarchy:block/truffalo_door_bottom_left",
"y": 270
},
"facing=north,half=lower,hinge=left,open=true": {
"model": "antarchy:block/truffalo_door_bottom_left_open"
},
"facing=north,half=lower,hinge=right,open=false": {
"model": "antarchy:block/truffalo_door_bottom_right",
"y": 270
},
"facing=north,half=lower,hinge=right,open=true": {
"model": "antarchy:block/truffalo_door_bottom_right_open",
"y": 180
},
"facing=north,half=upper,hinge=left,open=false": {
"model": "antarchy:block/truffalo_door_top_left",
"y": 270
},
"facing=north,half=upper,hinge=left,open=true": {
"model": "antarchy:block/truffalo_door_top_left_open"
},
"facing=north,half=upper,hinge=right,open=false": {
"model": "antarchy:block/truffalo_door_top_right",
"y": 270
},
"facing=north,half=upper,hinge=right,open=true": {
"model": "antarchy:block/truffalo_door_top_right_open",
"y": 180
},
"facing=south,half=lower,hinge=left,open=false": {
"model": "antarchy:block/truffalo_door_bottom_left",
"y": 90
},
"facing=south,half=lower,hinge=left,open=true": {
"model": "antarchy:block/truffalo_door_bottom_left_open",
"y": 180
},
"facing=south,half=lower,hinge=right,open=false": {
"model": "antarchy:block/truffalo_door_bottom_right",
"y": 90
},
"facing=south,half=lower,hinge=right,open=true": {
"model": "antarchy:block/truffalo_door_bottom_right_open"
},
"facing=south,half=upper,hinge=left,open=false": {
"model": "antarchy:block/truffalo_door_top_left",
"y": 90
},
"facing=south,half=upper,hinge=left,open=true": {
"model": "antarchy:block/truffalo_door_top_left_open",
"y": 180
},
"facing=south,half=upper,hinge=right,open=false": {
"model": "antarchy:block/truffalo_door_top_right",
"y": 90
},
"facing=south,half=upper,hinge=right,open=true": {
"model": "antarchy:block/truffalo_door_top_right_open"
},
"facing=west,half=lower,hinge=left,open=false": {
"model": "antarchy:block/truffalo_door_bottom_left",
"y": 180
},
"facing=west,half=lower,hinge=left,open=true": {
"model": "antarchy:block/truffalo_door_bottom_left_open",
"y": 270
},
"facing=west,half=lower,hinge=right,open=false": {
"model": "antarchy:block/truffalo_door_bottom_right",
"y": 180
},
"facing=west,half=lower,hinge=right,open=true": {
"model": "antarchy:block/truffalo_door_bottom_right_open",
"y": 90
},
"facing=west,half=upper,hinge=left,open=false": {
"model": "antarchy:block/truffalo_door_top_left",
"y": 180
},
"facing=west,half=upper,hinge=left,open=true": {
"model": "antarchy:block/truffalo_door_top_left_open",
"y": 270
},
"facing=west,half=upper,hinge=right,open=false": {
"model": "antarchy:block/truffalo_door_top_right",
"y": 180
},
"facing=west,half=upper,hinge=right,open=true": {
"model": "antarchy:block/truffalo_door_top_right_open",
"y": 90
}
}
}
@@ -0,0 +1,49 @@
{
"multipart": [
{
"apply": {
"model": "antarchy:block/truffalo_fence_post"
}
},
{
"apply": {
"model": "antarchy:block/truffalo_fence_side",
"uvlock": true
},
"when": {
"north": "true"
}
},
{
"apply": {
"model": "antarchy:block/truffalo_fence_side",
"uvlock": true,
"y": 90
},
"when": {
"east": "true"
}
},
{
"apply": {
"model": "antarchy:block/truffalo_fence_side",
"uvlock": true,
"y": 180
},
"when": {
"south": "true"
}
},
{
"apply": {
"model": "antarchy:block/truffalo_fence_side",
"uvlock": true,
"y": 270
},
"when": {
"west": "true"
}
}
]
}
@@ -0,0 +1,81 @@
{
"variants": {
"facing=east,in_wall=false,open=false": {
"model": "antarchy:block/truffalo_fence_gate",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=false,open=true": {
"model": "antarchy:block/truffalo_fence_gate_open",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=true,open=false": {
"model": "antarchy:block/truffalo_fence_gate_wall",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=true,open=true": {
"model": "antarchy:block/truffalo_fence_gate_wall_open",
"uvlock": true,
"y": 270
},
"facing=north,in_wall=false,open=false": {
"model": "antarchy:block/truffalo_fence_gate",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=false,open=true": {
"model": "antarchy:block/truffalo_fence_gate_open",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=true,open=false": {
"model": "antarchy:block/truffalo_fence_gate_wall",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=true,open=true": {
"model": "antarchy:block/truffalo_fence_gate_wall_open",
"uvlock": true,
"y": 180
},
"facing=south,in_wall=false,open=false": {
"model": "antarchy:block/truffalo_fence_gate",
"uvlock": true
},
"facing=south,in_wall=false,open=true": {
"model": "antarchy:block/truffalo_fence_gate_open",
"uvlock": true
},
"facing=south,in_wall=true,open=false": {
"model": "antarchy:block/truffalo_fence_gate_wall",
"uvlock": true
},
"facing=south,in_wall=true,open=true": {
"model": "antarchy:block/truffalo_fence_gate_wall_open",
"uvlock": true
},
"facing=west,in_wall=false,open=false": {
"model": "antarchy:block/truffalo_fence_gate",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=false,open=true": {
"model": "antarchy:block/truffalo_fence_gate_open",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=true,open=false": {
"model": "antarchy:block/truffalo_fence_gate_wall",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=true,open=true": {
"model": "antarchy:block/truffalo_fence_gate_wall_open",
"uvlock": true,
"y": 90
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_hanging_sign"
}
}
}
@@ -0,0 +1,14 @@
{
"variants": {
"axis=x": {
"model": "antarchy:block/truffalo_log_horizontal"
},
"axis=y": {
"model": "antarchy:block/truffalo_log"
},
"axis=z": {
"model": "antarchy:block/truffalo_log_horizontal",
"y": 90
}
}
}
@@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "antarchy:block/truffalo_planks"
}
}
}
@@ -0,0 +1,10 @@
{
"variants": {
"powered=false": {
"model": "antarchy:block/truffalo_pressure_plate"
},
"powered=true": {
"model": "antarchy:block/truffalo_pressure_plate_down"
}
}
}
Loaded 100 of 554 files, more files were not shown because too many files have changed in this diff. Show more