Crystal doors can now be locked by providing them a signed bangle of comradery.

When locked they will only let the owner or players wearing a signed bangle through.
Also you can't cheat them
This commit is contained in:
Sollace 2024-04-11 19:37:25 +01:00
parent b99c6b6c29
commit 408c9d11c0
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
7 changed files with 182 additions and 28 deletions

View file

@ -1,64 +1,182 @@
package com.minelittlepony.unicopia.block;
import java.util.UUID;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.EquineContext;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.item.UItems;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.DoorBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.enums.DoubleBlockHalf;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.Properties;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.World;
import net.minecraft.world.event.GameEvent;
public class CrystalDoorBlock extends DoorBlock {
public class CrystalDoorBlock extends DoorBlock implements BlockEntityProvider {
public static final BooleanProperty LOCKED = Properties.LOCKED;
public CrystalDoorBlock(Settings settings, BlockSetType blockSet) {
super(settings, blockSet);
setDefaultState(getDefaultState().with(LOCKED, false));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder);
builder.add(LOCKED);
}
@Override
public boolean hasRandomTicks(BlockState state) {
return state.get(LOCKED);
}
@Deprecated
@Override
public void randomTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
super.randomTick(state, world, pos, random);
if (!isLocked(world, pos)) {
setOnGuard(state, world, pos, false);
}
}
@Override
public void neighborUpdate(BlockState state, World world, BlockPos pos, Block sourceBlock, BlockPos sourcePos, boolean notify) {
boolean powered = world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.offset(state.get(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN));
if (!getDefaultState().isOf(sourceBlock) && powered != state.get(POWERED)) {
if (powered) {
state = state.cycle(OPEN);
playOpenCloseSound(null, world, pos, state.get(OPEN));
world.emitGameEvent(null, state.get(OPEN) ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
}
if (!state.get(LOCKED)) {
boolean powered = world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.offset(state.get(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN));
if (!getDefaultState().isOf(sourceBlock) && powered != state.get(POWERED)) {
if (powered) {
state = state.cycle(OPEN);
playOpenCloseSound(null, world, pos, state.get(OPEN));
world.emitGameEvent(null, state.get(OPEN) ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
}
world.setBlockState(pos, state.with(POWERED, powered), Block.NOTIFY_LISTENERS);
world.setBlockState(pos, state.with(POWERED, powered), Block.NOTIFY_LISTENERS);
}
}
if (state.get(HALF) == DoubleBlockHalf.LOWER && sourcePos.getY() == pos.getY() - 1) {
if (!canPlaceAt(state, world, pos) && world.isAir(sourcePos)) {
world.setBlockState(sourcePos, Blocks.DIRT.getDefaultState());
}
}
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if (!EquineContext.of(player).getCompositeRace().any(Race::canCast)) {
if (!player.getStackInHand(hand).isOf(UItems.MEADOWBROOKS_STAFF)) {
if (!shouldProvideAccess(world, pos, player)) {
if (isLocked(world, pos) || !player.getStackInHand(hand).isOf(UItems.MEADOWBROOKS_STAFF)) {
playOpenCloseSound(player, world, pos, false);
return ActionResult.FAIL;
setOnGuard(state, world, pos, true);
return ActionResult.CONSUME;
} else {
world.playSound(player, pos, USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, SoundCategory.BLOCKS, 1, world.getRandom().nextFloat() * 0.1F + 0.9F);
}
} else if (!isLocked(world, pos)) {
ItemStack heldStack = player.getStackInHand(hand);
if (heldStack.isOf(UItems.FRIENDSHIP_BRACELET)) {
@Nullable
UUID signator = FriendshipBraceletItem.getSignatorId(heldStack);
if (signator != null) {
BlockEntityUtil.getOrCreateBlockEntity(world, state.get(HALF) == DoubleBlockHalf.LOWER ? pos.up() : pos, UBlockEntities.CRYSTAL_DOOR).ifPresent(data -> {
data.setSignator(signator);
setOnGuard(state, world, pos, true);
world.playSound(player, pos, USounds.ENTITY_CRYSTAL_SHARDS_AMBIENT, SoundCategory.BLOCKS, 1, world.getRandom().nextFloat() * 0.1F + 0.9F);
});
return ActionResult.SUCCESS;
}
} else {
setOnGuard(state, world, pos, false);
}
}
return super.onUse(state, world, pos, player, hand, hit);
}
private void setOnGuard(BlockState state, World world, BlockPos pos, boolean locked) {
world.setBlockState(pos, state.with(LOCKED, locked));
pos = pos.offset(state.get(HALF) == DoubleBlockHalf.LOWER ? Direction.UP : Direction.DOWN);
state = world.getBlockState(pos);
if (state.isOf(this)) {
world.setBlockState(pos, state.with(LOCKED, locked));
}
}
private boolean shouldProvideAccess(World world, BlockPos pos, PlayerEntity player) {
UUID signator = getSignator(world, pos);
if (signator != null) {
return signator.equals(player.getUuid()) || FriendshipBraceletItem.isComrade(signator, player);
}
return EquineContext.of(player).getCompositeRace().any(Race::canCast);
}
private boolean isLocked(World world, BlockPos pos) {
return getSignator(world, pos) != null;
}
@Nullable
private UUID getSignator(World world, BlockPos pos) {
pos = world.getBlockState(pos).get(HALF) == DoubleBlockHalf.LOWER ? pos.up() : pos;
var d = world.getBlockEntity(pos, UBlockEntities.CRYSTAL_DOOR);
return d.map(data -> data.signator).orElse(null);
}
private void playOpenCloseSound(@Nullable Entity entity, World world, BlockPos pos, boolean open) {
world.playSound(entity, pos, open ? getBlockSetType().doorOpen() : getBlockSetType().doorClose(), SoundCategory.BLOCKS, 1, world.getRandom().nextFloat() * 0.1f + 0.9f);
}
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return new TileData(pos, state);
}
public static class TileData extends BlockEntity {
@Nullable
private UUID signator;
public TileData(BlockPos pos, BlockState state) {
super(UBlockEntities.CRYSTAL_DOOR, pos, state);
}
public void setSignator(UUID signator) {
this.signator = signator;
markDirty();
}
@Override
public void readNbt(NbtCompound nbt) {
signator = nbt.containsUuid("signator") ? nbt.getUuid("signator") : null;
}
@Override
protected void writeNbt(NbtCompound nbt) {
if (signator != null) {
nbt.putUuid("signator", signator);
}
}
}
}

View file

@ -16,6 +16,7 @@ public interface UBlockEntities {
BlockEntityType<ChestBlockEntity> CLOUD_CHEST = create("cloud_chest", BlockEntityType.Builder.create(CloudChestBlock.TileData::new, UBlocks.CLOUD_CHEST));
BlockEntityType<HiveBlock.TileData> HIVE_STORAGE = create("hive_storage", BlockEntityType.Builder.create(HiveBlock.TileData::new, UBlocks.HIVE));
BlockEntityType<ItemJarBlock.TileData> ITEM_JAR = create("item_jar", BlockEntityType.Builder.create(ItemJarBlock.TileData::new, UBlocks.JAR));
BlockEntityType<CrystalDoorBlock.TileData> CRYSTAL_DOOR = create("crystal_door", BlockEntityType.Builder.create(CrystalDoorBlock.TileData::new, UBlocks.CRYSTAL_DOOR));
static <T extends BlockEntity> BlockEntityType<T> create(String id, Builder<T> builder) {
return Registry.register(Registries.BLOCK_ENTITY_TYPE, id, builder.build(null));

View file

@ -34,6 +34,7 @@ import net.minecraft.data.client.Model;
import net.minecraft.data.client.ModelIds;
import net.minecraft.data.client.Models;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.client.TextureKey;
import net.minecraft.data.client.TextureMap;
import net.minecraft.data.client.TexturedModel;
import net.minecraft.data.client.VariantSettings;
@ -89,7 +90,8 @@ public class UBlockStateModelGenerator extends BlockStateModelGenerator {
registerAll((g, block) -> g.registerParentedItemModel(block, ModelIds.getBlockModelId(block)), UBlocks.SHAPING_BENCH, UBlocks.SURFACE_CHITIN);
registerAll(UBlockStateModelGenerator::registerSimpleState, UBlocks.SHAPING_BENCH, UBlocks.BANANAS);
// doors
registerAll(UBlockStateModelGenerator::registerStableDoor, UBlocks.STABLE_DOOR, UBlocks.DARK_OAK_DOOR, UBlocks.CRYSTAL_DOOR, UBlocks.CLOUD_DOOR);
registerAll(UBlockStateModelGenerator::registerStableDoor, UBlocks.STABLE_DOOR, UBlocks.DARK_OAK_DOOR, UBlocks.CLOUD_DOOR);
registerLockingDoor(UBlocks.CRYSTAL_DOOR);
// cloud blocks
createCustomTexturePool(UBlocks.CLOUD, TexturedModel.CUBE_ALL).same(UBlocks.UNSTABLE_CLOUD).slab(UBlocks.CLOUD_SLAB).stairs(UBlocks.CLOUD_STAIRS);
@ -347,23 +349,37 @@ public class UBlockStateModelGenerator extends BlockStateModelGenerator {
}
public void registerStableDoor(Block door) {
TextureMap topTextures = TextureMap.topBottom(door);
TextureMap bottomTextures = topTextures.copyAndAdd(TOP, topTextures.getTexture(BOTTOM));
registerItemModel(door.asItem());
var variants = BlockStateVariantMap.create(Properties.HORIZONTAL_FACING, Properties.DOUBLE_BLOCK_HALF, Properties.DOOR_HINGE, Properties.OPEN);
fillStableDoorVariantMap(variants, DoubleBlockHalf.LOWER,
BlockModels.DOOR_LEFT.upload(door, "_bottom_left", bottomTextures, modelCollector),
BlockModels.DOOR_RIGHT.upload(door, "_bottom_right", bottomTextures, modelCollector)
);
fillStableDoorVariantMap(variants, DoubleBlockHalf.UPPER,
BlockModels.DOOR_LEFT.upload(door, "_top_left", topTextures, modelCollector),
BlockModels.DOOR_RIGHT.upload(door, "_top_right", topTextures, modelCollector)
);
registerItemModel(door.asItem());
buildDoorStateModels(door, "", variants::register);
blockStateCollector.accept(VariantsBlockStateSupplier.create(door).coordinate(variants));
}
public static void fillStableDoorVariantMap(
BlockStateVariantMap.QuadrupleProperty<Direction, DoubleBlockHalf, DoorHinge, Boolean> variantMap,
public void registerLockingDoor(Block door) {
var variants = BlockStateVariantMap.create(Properties.HORIZONTAL_FACING, Properties.DOUBLE_BLOCK_HALF, Properties.DOOR_HINGE, Properties.OPEN, Properties.LOCKED);
registerItemModel(door.asItem());
buildDoorStateModels(door, "", (facing, half, hinge, open, map) -> variants.register(facing, half, hinge, open, false, map));
buildDoorStateModels(door, "_locked", (facing, half, hinge, open, map) -> variants.register(facing, half, hinge, open, true, map));
blockStateCollector.accept(VariantsBlockStateSupplier.create(door).coordinate(variants));
}
private void buildDoorStateModels(Block door, String suffex, DoorStateConsumer variants) {
TextureMap topTextures = new TextureMap()
.put(TextureKey.TOP, TextureMap.getSubId(door, "_top" + suffex))
.put(TextureKey.BOTTOM, TextureMap.getSubId(door, "_bottom" + suffex));
TextureMap bottomTextures = topTextures.copyAndAdd(TOP, topTextures.getTexture(BOTTOM));
fillStableDoorVariantMap(variants, DoubleBlockHalf.LOWER,
BlockModels.DOOR_LEFT.upload(door, "_bottom_left" + suffex, bottomTextures, modelCollector),
BlockModels.DOOR_RIGHT.upload(door, "_bottom_right" + suffex, bottomTextures, modelCollector)
);
fillStableDoorVariantMap(variants, DoubleBlockHalf.UPPER,
BlockModels.DOOR_LEFT.upload(door, "_top_left" + suffex, topTextures, modelCollector),
BlockModels.DOOR_RIGHT.upload(door, "_top_right" + suffex, topTextures, modelCollector)
);
}
private static void fillStableDoorVariantMap(
DoorStateConsumer variantMap,
DoubleBlockHalf targetHalf, Identifier leftModelId, Identifier rightModelId) {
fillStableDoorVariantMap(variantMap, targetHalf, DoorHinge.LEFT, false, R0, leftModelId);
fillStableDoorVariantMap(variantMap, targetHalf, DoorHinge.RIGHT, false, R0, rightModelId);
@ -373,7 +389,7 @@ public class UBlockStateModelGenerator extends BlockStateModelGenerator {
}
public static void fillStableDoorVariantMap(
BlockStateVariantMap.QuadrupleProperty<Direction, DoubleBlockHalf, DoorHinge, Boolean> variantMap,
DoorStateConsumer variantMap,
DoubleBlockHalf targetHalf,
DoorHinge hinge, boolean open, Rotation rotation,
Identifier modelId) {
@ -386,6 +402,10 @@ public class UBlockStateModelGenerator extends BlockStateModelGenerator {
}
}
interface DoorStateConsumer {
void register(Direction direction, DoubleBlockHalf half, DoorHinge hinge, boolean open, BlockStateVariant variant);
}
public void registerPillar(Block pillar) {
TextureMap textures = new TextureMap()
.put(SIDE, ModelIds.getBlockSubModelId(pillar, "_side"))

View file

@ -124,6 +124,10 @@ public class FriendshipBraceletItem extends WearableItem implements DyeableItem,
.isPresent();
}
public static boolean isComrade(UUID signator, Entity entity) {
return entity instanceof LivingEntity l && getWornBangles(l).anyMatch(stack -> isSignedBy(stack, signator));
}
public static Stream<Pony> getPartyMembers(Caster<?> caster, double radius) {
return Pony.stream(caster.findAllEntitiesInRange(radius, entity -> isComrade(caster, entity)));
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,11 @@
{
"animation": {
"frametime": 200,
"frames": [
0, 1, 2, 3,
1, 2, 3, 0,
2, 3, 0, 1,
3, 0, 1, 2
]
}
}