From 1f0117a689040e81eac86e9a97f8212a108912f0 Mon Sep 17 00:00:00 2001 From: TartaricAcid Date: Tue, 5 Oct 2021 00:19:17 +0800 Subject: [PATCH] Complete the altar design - Complete the construction and rendering of the multi-block structure of the altar - Complete the craft of the altar --- .../touhoulittlemaid/TouhouLittleMaid.java | 4 + .../touhoulittlemaid/api/ILittleMaid.java | 13 +- .../api/block/IMultiBlock.java | 64 ++ .../touhoulittlemaid/block/BlockAltar.java | 298 ++++++++ .../block/multiblock/MultiBlockAltar.java | 110 +++ .../block/multiblock/MultiBlockManager.java | 34 + .../client/init/InitEntitiesRender.java | 4 + .../client/model/AltarModel.java | 642 ++++++++++++++++++ .../tileentity/TileEntityAltarRenderer.java | 71 ++ .../renderer/tileentity/package-info.java | 7 + .../crafting/AltarRecipe.java | 110 +++ .../crafting/AltarRecipeSerializer.java | 67 ++ .../crafting/package-info.java | 7 + .../data/AltarRecipeProvider.java | 92 +++ .../touhoulittlemaid/data/DataGenEvent.java | 1 + .../touhoulittlemaid/data/package-info.java | 7 + .../entity/task/TaskManager.java | 36 +- .../touhoulittlemaid/init/InitBlocks.java | 7 + .../touhoulittlemaid/init/InitItems.java | 2 - .../touhoulittlemaid/init/InitRecipes.java | 38 ++ .../inventory/AltarItemHandler.java | 10 + .../inventory/AltarRecipeInventory.java | 68 ++ .../item/ItemHakureiGohei.java | 37 + .../item/bauble/BaubleManager.java | 26 +- .../tileentity/TileEntityAltar.java | 145 ++++ .../tileentity/package-info.java | 7 + .../util/EntityCraftingHelper.java | 42 ++ .../touhoulittlemaid/util/PosListData.java | 35 + .../resources/META-INF/accesstransformer.cfg | 3 +- .../touhou_little_maid/blockstates/altar.json | 7 + .../models/block/altar.json | 5 + .../textures/entity/altar.png | Bin 0 -> 46708 bytes .../altar/craft_explosion_protect_bauble.json | 28 + .../structures/altar_east.nbt | Bin 0 -> 343 bytes .../structures/altar_north.nbt | Bin 0 -> 332 bytes .../structures/altar_south.nbt | Bin 0 -> 332 bytes .../structures/altar_west.nbt | Bin 0 -> 339 bytes 37 files changed, 1991 insertions(+), 36 deletions(-) create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/api/block/IMultiBlock.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockAltar.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/multiblock/MultiBlockAltar.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/block/multiblock/MultiBlockManager.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/AltarModel.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityAltarRenderer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/package-info.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/AltarRecipe.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/AltarRecipeSerializer.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/package-info.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/data/AltarRecipeProvider.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/data/package-info.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitRecipes.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/AltarItemHandler.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/AltarRecipeInventory.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityAltar.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/package-info.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EntityCraftingHelper.java create mode 100644 src/main/java/com/github/tartaricacid/touhoulittlemaid/util/PosListData.java create mode 100644 src/main/resources/assets/touhou_little_maid/blockstates/altar.json create mode 100644 src/main/resources/assets/touhou_little_maid/models/block/altar.json create mode 100644 src/main/resources/assets/touhou_little_maid/textures/entity/altar.png create mode 100644 src/main/resources/data/touhou_little_maid/recipes/altar/craft_explosion_protect_bauble.json create mode 100644 src/main/resources/data/touhou_little_maid/structures/altar_east.nbt create mode 100644 src/main/resources/data/touhou_little_maid/structures/altar_north.nbt create mode 100644 src/main/resources/data/touhou_little_maid/structures/altar_south.nbt create mode 100644 src/main/resources/data/touhou_little_maid/structures/altar_west.nbt diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/TouhouLittleMaid.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/TouhouLittleMaid.java index caa765b46..64f95756c 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/TouhouLittleMaid.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/TouhouLittleMaid.java @@ -1,6 +1,7 @@ package com.github.tartaricacid.touhoulittlemaid; import com.github.tartaricacid.touhoulittlemaid.api.ILittleMaid; +import com.github.tartaricacid.touhoulittlemaid.block.multiblock.MultiBlockManager; import com.github.tartaricacid.touhoulittlemaid.config.Config; import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskManager; import com.github.tartaricacid.touhoulittlemaid.init.*; @@ -32,13 +33,16 @@ public TouhouLittleMaid() { InitEntities.SCHEDULES.register(FMLJavaModLoadingContext.get().getModEventBus()); InitEntities.DATA_SERIALIZERS.register(FMLJavaModLoadingContext.get().getModEventBus()); InitBlocks.BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus()); + InitBlocks.TILE_ENTITIES.register(FMLJavaModLoadingContext.get().getModEventBus()); InitItems.ITEMS.register(FMLJavaModLoadingContext.get().getModEventBus()); InitContainer.CONTAINER_TYPE.register(FMLJavaModLoadingContext.get().getModEventBus()); InitSounds.SOUNDS.register(FMLJavaModLoadingContext.get().getModEventBus()); + InitRecipes.RECIPE_SERIALIZERS.register(FMLJavaModLoadingContext.get().getModEventBus()); ModLoadingContext.get().registerConfig(ModConfig.Type.COMMON, Config.initConfig()); DeferredWorkQueue.runLater(NetworkHandler::init); EXTENSIONS = AnnotatedInstanceUtil.getModExtensions(); TaskManager.init(); BaubleManager.init(); + MultiBlockManager.init(); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/ILittleMaid.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/ILittleMaid.java index 75e5fdf91..5bee27981 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/ILittleMaid.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/ILittleMaid.java @@ -1,5 +1,6 @@ package com.github.tartaricacid.touhoulittlemaid.api; +import com.github.tartaricacid.touhoulittlemaid.block.multiblock.MultiBlockManager; import com.github.tartaricacid.touhoulittlemaid.entity.task.TaskManager; import com.github.tartaricacid.touhoulittlemaid.item.bauble.BaubleManager; @@ -13,10 +14,18 @@ default void bindMaidBauble(BaubleManager manager) { } /** - * 注册女仆的 Task + * 添加女仆的 Task * * @param manager 注册器 */ - default void registerMaidTask(TaskManager manager) { + default void addMaidTask(TaskManager manager) { + } + + /** + * 添加多方块结构 + * + * @param manager 注册器 + */ + default void addMultiBlock(MultiBlockManager manager) { } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/block/IMultiBlock.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/block/IMultiBlock.java new file mode 100644 index 000000000..ebe1ef734 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/api/block/IMultiBlock.java @@ -0,0 +1,64 @@ +package com.github.tartaricacid.touhoulittlemaid.api.block; + +import net.minecraft.block.BlockState; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.template.Template; +import net.minecraft.world.server.ServerWorld; + +public interface IMultiBlock { + /** + * 触发的构建多方块结构时,点击的方块是否为核心方块 + * + * @param blockState 点击的方块 + * @return 是否为核心方块 + */ + boolean isCoreBlock(BlockState blockState); + + /** + * 触发的构建多方块结构时,朝向是否正确 + * + * @param direction 朝向 + * @return 是否为合法的触发方向 + */ + boolean directionIsSuitable(Direction direction); + + /** + * 获取多方块结构的中心点 + * + * @param direction 朝向 + * @return 多方块结构的中心点 + */ + BlockPos getCenterPos(Direction direction); + + /** + * 获取多方块结构模板 + * + * @param world 世界 + * @param direction 朝向 + * @return 多方块结构模板 + */ + Template getTemplate(ServerWorld world, Direction direction); + + /** + * 是否匹配该多方块结构 + * + * @param world 世界实例 + * @param posStart 起始坐标 + * @param direction 判定匹配时的朝向,用来应用到一些具有方向的多方块结构 + * @param template 结构模板 + * @return 是否匹配 + */ + boolean isMatch(World world, BlockPos posStart, Direction direction, Template template); + + /** + * 修建多方块结构的逻辑 + * + * @param worldIn 世界实例 + * @param posStart 起始坐标 + * @param direction 判定匹配时的朝向,用来应用到一些具有方向的多方块结构 + * @param template 结构模板 + */ + void build(World worldIn, BlockPos posStart, Direction direction, Template template); +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockAltar.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockAltar.java new file mode 100644 index 000000000..9b4c31145 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/BlockAltar.java @@ -0,0 +1,298 @@ +package com.github.tartaricacid.touhoulittlemaid.block; + +import com.github.tartaricacid.touhoulittlemaid.capability.PowerCapability; +import com.github.tartaricacid.touhoulittlemaid.capability.PowerCapabilityProvider; +import com.github.tartaricacid.touhoulittlemaid.crafting.AltarRecipe; +import com.github.tartaricacid.touhoulittlemaid.init.InitRecipes; +import com.github.tartaricacid.touhoulittlemaid.init.InitSounds; +import com.github.tartaricacid.touhoulittlemaid.inventory.AltarRecipeInventory; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityAltar; +import com.github.tartaricacid.touhoulittlemaid.util.PosListData; +import net.minecraft.block.*; +import net.minecraft.block.material.Material; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.DiggingParticle; +import net.minecraft.client.particle.ParticleManager; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.pathfinding.PathType; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.*; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockRayTraceResult; +import net.minecraft.util.math.RayTraceResult; +import net.minecraft.util.text.TranslationTextComponent; +import net.minecraft.world.Explosion; +import net.minecraft.world.IBlockReader; +import net.minecraft.world.IWorldReader; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.items.ItemHandlerHelper; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Optional; + +import static net.minecraftforge.common.util.Constants.BlockFlags.DEFAULT; + +public class BlockAltar extends Block { + public BlockAltar() { + super(AbstractBlock.Properties.of(Material.STONE).strength(2, 2).noOcclusion()); + } + + @Override + public boolean hasTileEntity(BlockState state) { + return true; + } + + @Nullable + @Override + public TileEntity createTileEntity(BlockState state, IBlockReader world) { + return new TileEntityAltar(); + } + + @Override + public ActionResultType use(BlockState state, World worldIn, BlockPos pos, PlayerEntity player, Hand handIn, BlockRayTraceResult hit) { + return this.getAltar(worldIn, pos).filter(altar -> handIn == Hand.MAIN_HAND).map(altar -> { + if (player.isShiftKeyDown() || player.getMainHandItem().isEmpty()) { + takeOutItem(worldIn, pos, altar, player); + } else { + takeInOrCraft(worldIn, altar, player); + } + altar.refresh(); + return ActionResultType.sidedSuccess(worldIn.isClientSide); + }).orElse(super.use(state, worldIn, pos, player, handIn, hit)); + } + + @Override + public void onRemove(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { + if (!worldIn.isClientSide) { + this.getAltar(worldIn, pos).ifPresent(altar -> { + ItemStack stack = altar.handler.getStackInSlot(0); + if (!stack.isEmpty()) { + Block.popResource(worldIn, pos.offset(0, 1, 0), stack); + } + }); + } + super.onRemove(state, worldIn, pos, newState, isMoving); + } + + @Override + public void onBlockExploded(BlockState state, World world, BlockPos pos, Explosion explosion) { + if (!world.isClientSide) { + this.getAltar(world, pos).ifPresent(altar -> this.restoreStorageBlock(world, pos, altar.getBlockPosList())); + } + super.onBlockExploded(state, world, pos, explosion); + } + + @Override + public void playerWillDestroy(World worldIn, BlockPos pos, BlockState state, PlayerEntity player) { + if (!worldIn.isClientSide) { + this.getAltar(worldIn, pos).ifPresent(altar -> { + this.restoreStorageBlock(worldIn, pos, altar.getBlockPosList()); + if (!player.isCreative()) { + Block block = altar.getStorageState().getBlock(); + Block.popResource(worldIn, pos, new ItemStack(block)); + } + }); + } + super.playerWillDestroy(worldIn, pos, state, player); + } + + @Override + public ItemStack getPickBlock(BlockState state, RayTraceResult target, IBlockReader world, BlockPos pos, PlayerEntity player) { + return this.getAltar(world, pos) + .map(altar -> new ItemStack(altar.getStorageState().getBlock())) + .orElse(super.getPickBlock(state, target, world, pos, player)); + } + + @Override + @OnlyIn(Dist.CLIENT) + public boolean addDestroyEffects(BlockState state, World world, BlockPos pos, ParticleManager manager) { + this.getAltar(world, pos).ifPresent(altar -> Minecraft.getInstance().particleEngine.destroy(pos, altar.getStorageState())); + return true; + } + + @Override + @OnlyIn(Dist.CLIENT) + public boolean addHitEffects(BlockState state, World world, RayTraceResult target, ParticleManager manager) { + if (target instanceof BlockRayTraceResult && world instanceof ClientWorld) { + BlockRayTraceResult blockTarget = (BlockRayTraceResult) target; + BlockPos pos = blockTarget.getBlockPos(); + ClientWorld clientWorld = (ClientWorld) world; + this.getAltar(world, pos).ifPresent(altar -> this.crack(clientWorld, pos, altar.getStorageState(), blockTarget.getDirection())); + } + return true; + } + + @Override + public SoundType getSoundType(BlockState state, IWorldReader world, BlockPos pos, @Nullable Entity entity) { + return this.getAltar(world, pos) + .map(altar -> altar.getStorageState().getSoundType()) + .orElse(super.getSoundType(state, world, pos, entity)); + } + + @Override + public BlockRenderType getRenderShape(BlockState state) { + return BlockRenderType.ENTITYBLOCK_ANIMATED; + } + + @Override + public boolean isPathfindable(BlockState state, IBlockReader worldIn, BlockPos pos, PathType type) { + return false; + } + + private void restoreStorageBlock(World worldIn, BlockPos currentPos, PosListData posList) { + for (BlockPos storagePos : posList.getData()) { + if (storagePos.equals(currentPos)) { + continue; + } + this.getAltar(worldIn, storagePos).ifPresent(altar -> worldIn.setBlock(storagePos, altar.getStorageState(), DEFAULT)); + } + } + + private void takeOutItem(World world, BlockPos pos, TileEntityAltar altar, PlayerEntity player) { + if (altar.isCanPlaceItem()) { + if (!altar.handler.getStackInSlot(0).isEmpty()) { + Block.popResource(world, pos.offset(0, 1, 0), altar.handler.extractItem(0, 1, false)); + altarCraft(world, altar, player); + } + } + } + + private void takeInOrCraft(World world, TileEntityAltar altar, PlayerEntity playerIn) { + if (altar.isCanPlaceItem() && altar.handler.getStackInSlot(0).isEmpty()) { + altar.handler.setStackInSlot(0, ItemHandlerHelper.copyStackWithSize(playerIn.getMainHandItem(), 1)); + if (!playerIn.isCreative()) { + playerIn.getMainHandItem().shrink(1); + } + altarCraft(world, altar, playerIn); + } + } + + private void altarCraft(World world, TileEntityAltar altar, PlayerEntity playerIn) { + final AltarRecipeInventory inv = new AltarRecipeInventory(); + List posList = altar.getCanPlaceItemPosList().getData(); + for (int i = 0; i < posList.size(); i++) { + TileEntity te = world.getBlockEntity(posList.get(i)); + if (te instanceof TileEntityAltar) { + inv.setItem(i, ((TileEntityAltar) te).getStorageItem()); + } + } + if (inv.isEmpty()) { + return; + } + playerIn.getCapability(PowerCapabilityProvider.POWER_CAP, null) + .ifPresent(power -> world.getRecipeManager().getRecipeFor(InitRecipes.ALTAR_CRAFTING, inv, world) + .ifPresent(recipe -> spawnResultEntity(world, playerIn, power, recipe, inv, altar))); + } + + private Optional getAltar(IBlockReader world, BlockPos pos) { + TileEntity te = world.getBlockEntity(pos); + if (te instanceof TileEntityAltar) { + return Optional.of((TileEntityAltar) te); + } + return Optional.empty(); + } + + private void spawnResultEntity(World world, PlayerEntity playerIn, PowerCapability power, + AltarRecipe altarRecipe, AltarRecipeInventory inventory, TileEntityAltar altar) { + if (power.get() >= altarRecipe.getPowerCost()) { + power.min(altarRecipe.getPowerCost()); + BlockPos centrePos = getCentrePos(altar.getBlockPosList(), altar.getBlockPos()); + if (world instanceof ServerWorld) { + altarRecipe.spawnOutputEntity((ServerWorld) world, centrePos, inventory); + } + removeAllAltarItem(world, altar); + spawnParticleInCentre(world, centrePos); + world.playSound(null, centrePos, InitSounds.ALTAR_CRAFT.get(), SoundCategory.VOICE, 1.0f, 1.0f); + } else { + if (!world.isClientSide) { + playerIn.sendMessage(new TranslationTextComponent("message.touhou_little_maid.altar.not_enough_power"), Util.NIL_UUID); + } + } + } + + + @OnlyIn(Dist.CLIENT) + private void crack(ClientWorld world, BlockPos pos, BlockState state, Direction side) { + if (state.getRenderShape() != BlockRenderType.INVISIBLE) { + int posX = pos.getX(); + int posY = pos.getY(); + int posZ = pos.getZ(); + AxisAlignedBB aabb = state.getShape(world, pos).bounds(); + double x = posX + this.RANDOM.nextDouble() * (aabb.maxX - aabb.minX - 0.2) + 0.1 + aabb.minX; + double y = posY + this.RANDOM.nextDouble() * (aabb.maxY - aabb.minY - 0.2) + 0.1 + aabb.minY; + double z = posZ + this.RANDOM.nextDouble() * (aabb.maxZ - aabb.minZ - 0.2) + 0.1 + aabb.minZ; + if (side == Direction.DOWN) { + y = posY + aabb.minY - 0.1; + } + if (side == Direction.UP) { + y = posY + aabb.maxY + 0.1; + } + if (side == Direction.NORTH) { + z = posZ + aabb.minZ - 0.1; + } + if (side == Direction.SOUTH) { + z = posZ + aabb.maxZ + 0.1; + } + if (side == Direction.WEST) { + x = posX + aabb.minX - 0.1; + } + if (side == Direction.EAST) { + x = posX + aabb.maxX + 0.1; + } + DiggingParticle diggingParticle = new DiggingParticle(world, x, y, z, 0, 0, 0, state); + Minecraft.getInstance().particleEngine.add(diggingParticle.init(pos).setPower(0.2f).scale(0.6f)); + } + } + + private BlockPos getCentrePos(PosListData posList, BlockPos posClick) { + int x = 0; + int y = posClick.getY() - 2; + int z = 0; + for (BlockPos pos : posList.getData()) { + if (pos.getY() == y) { + x += pos.getX(); + z += pos.getZ(); + } + } + return new BlockPos(x / 8, y, z / 8); + } + + private void removeAllAltarItem(World world, TileEntityAltar altar) { + for (BlockPos pos : altar.getCanPlaceItemPosList().getData()) { + this.getAltar(world, pos).ifPresent(te -> { + te.handler.setStackInSlot(0, ItemStack.EMPTY); + te.refresh(); + spawnParticleInCentre(world, te.getBlockPos()); + }); + } + } + + private void spawnParticleInCentre(World world, BlockPos centrePos) { + int width = 1; + int height = 1; + for (int i = 0; i < 5; ++i) { + double xSpeed = RANDOM.nextGaussian() * 0.02; + double ySpeed = RANDOM.nextGaussian() * 0.02; + double zSpeed = RANDOM.nextGaussian() * 0.02; + world.addParticle(ParticleTypes.CLOUD, + centrePos.getX() + RANDOM.nextFloat() * width * 2 - width - xSpeed * 10, + centrePos.getY() + RANDOM.nextFloat() * height - ySpeed * 10, + centrePos.getZ() + RANDOM.nextFloat() * width * 2 - width - zSpeed * 10, + xSpeed, ySpeed, zSpeed); + world.addParticle(ParticleTypes.SMOKE, + centrePos.getX() + RANDOM.nextFloat() * width * 2 - width - xSpeed * 10, + centrePos.getY() + RANDOM.nextFloat() * height - ySpeed * 10, + centrePos.getZ() + RANDOM.nextFloat() * width * 2 - width - zSpeed * 10, + xSpeed, ySpeed, zSpeed); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/multiblock/MultiBlockAltar.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/multiblock/MultiBlockAltar.java new file mode 100644 index 000000000..580bac3d2 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/multiblock/MultiBlockAltar.java @@ -0,0 +1,110 @@ +package com.github.tartaricacid.touhoulittlemaid.block.multiblock; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.block.IMultiBlock; +import com.github.tartaricacid.touhoulittlemaid.init.InitBlocks; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityAltar; +import com.github.tartaricacid.touhoulittlemaid.util.PosListData; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.Direction; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.template.Template; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.util.Constants; + +public class MultiBlockAltar implements IMultiBlock { + private static final ResourceLocation ALTAR_SOUTH = new ResourceLocation(TouhouLittleMaid.MOD_ID, "altar_south"); + private static final ResourceLocation ALTAR_NORTH = new ResourceLocation(TouhouLittleMaid.MOD_ID, "altar_north"); + private static final ResourceLocation ALTAR_EAST = new ResourceLocation(TouhouLittleMaid.MOD_ID, "altar_east"); + private static final ResourceLocation ALTAR_WEST = new ResourceLocation(TouhouLittleMaid.MOD_ID, "altar_west"); + private static final BlockPos SOUTH_POS = new BlockPos(-4, -3, 0); + private static final BlockPos NORTH_POS = new BlockPos(-3, -3, -7); + private static final BlockPos EAST_POS = new BlockPos(0, -3, -3); + private static final BlockPos WEST_POS = new BlockPos(-7, -3, -4); + + @Override + public boolean isCoreBlock(BlockState blockState) { + return blockState.is(Blocks.RED_WOOL); + } + + @Override + public boolean isMatch(World world, BlockPos posStart, Direction direction, Template template) { + Template.Palette palette = template.palettes.get(0); + for (Template.BlockInfo blockInfo : palette.blocks()) { + if (!world.getBlockState(posStart.offset(blockInfo.pos)).equals(blockInfo.state)) { + return false; + } + } + return true; + } + + @Override + public void build(World worldIn, BlockPos posStart, Direction direction, Template template) { + PosListData posList = new PosListData(); + PosListData canPlaceItemPosList = new PosListData(); + Template.Palette palette = template.palettes.get(0); + + for (Template.BlockInfo blockInfo : palette.blocks()) { + posList.add(posStart.offset(blockInfo.pos)); + if (blockInfo.pos.getY() == 2 && blockInfo.state.is(Blocks.OAK_LOG)) { + canPlaceItemPosList.add(posStart.offset(blockInfo.pos)); + } + } + + BlockPos currentCenterPos = posStart.subtract(getCenterPos(direction)); + for (Template.BlockInfo blockInfo : palette.blocks()) { + BlockPos currentPos = posStart.offset(blockInfo.pos); + worldIn.setBlock(currentPos, InitBlocks.ALTAR.get().defaultBlockState(), Constants.BlockFlags.DEFAULT); + TileEntity te = worldIn.getBlockEntity(currentPos); + if (te instanceof TileEntityAltar) { + boolean isRender = currentPos.equals(currentCenterPos); + boolean canPlaceItem = blockInfo.pos.getY() == 2 && blockInfo.state.is(Blocks.OAK_LOG); + ((TileEntityAltar) te).setForgeData(blockInfo.state, isRender, + canPlaceItem, direction, posList, canPlaceItemPosList); + } + } + } + + @Override + public boolean directionIsSuitable(Direction direction) { + return direction != Direction.DOWN && direction != Direction.UP; + } + + @Override + public BlockPos getCenterPos(Direction facing) { + switch (facing) { + case NORTH: + return SOUTH_POS; + case EAST: + return WEST_POS; + case WEST: + return EAST_POS; + case SOUTH: + default: + return NORTH_POS; + } + } + + @Override + public Template getTemplate(ServerWorld world, Direction facing) { + switch (facing) { + case NORTH: + return getAltarTemplate(world, ALTAR_SOUTH); + case EAST: + return getAltarTemplate(world, ALTAR_WEST); + case WEST: + return getAltarTemplate(world, ALTAR_EAST); + case SOUTH: + default: + return getAltarTemplate(world, ALTAR_NORTH); + } + } + + private Template getAltarTemplate(ServerWorld world, ResourceLocation location) { + return world.getStructureManager().getOrCreate(location); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/multiblock/MultiBlockManager.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/multiblock/MultiBlockManager.java new file mode 100644 index 000000000..7fded3b0b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/block/multiblock/MultiBlockManager.java @@ -0,0 +1,34 @@ +package com.github.tartaricacid.touhoulittlemaid.block.multiblock; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.ILittleMaid; +import com.github.tartaricacid.touhoulittlemaid.api.block.IMultiBlock; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Lists; + +import java.util.List; + +public final class MultiBlockManager { + private static List MULTI_BLOCK_LIST; + + private MultiBlockManager() { + MULTI_BLOCK_LIST = Lists.newArrayList(); + } + + public static void init() { + MultiBlockManager manager = new MultiBlockManager(); + manager.add(new MultiBlockAltar()); + for (ILittleMaid littleMaid : TouhouLittleMaid.EXTENSIONS) { + littleMaid.addMultiBlock(manager); + } + MULTI_BLOCK_LIST = ImmutableList.copyOf(MULTI_BLOCK_LIST); + } + + public static List getMultiBlockList() { + return MULTI_BLOCK_LIST; + } + + public void add(IMultiBlock multiBlock) { + MULTI_BLOCK_LIST.add(multiBlock); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitEntitiesRender.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitEntitiesRender.java index 020304ed9..898a84ad7 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitEntitiesRender.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/init/InitEntitiesRender.java @@ -1,14 +1,17 @@ package com.github.tartaricacid.touhoulittlemaid.client.init; import com.github.tartaricacid.touhoulittlemaid.client.renderer.entity.*; +import com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity.TileEntityAltarRenderer; import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityChair; import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityExtinguishingAgent; import com.github.tartaricacid.touhoulittlemaid.entity.item.EntityPowerPoint; import com.github.tartaricacid.touhoulittlemaid.entity.monster.EntityFairy; import com.github.tartaricacid.touhoulittlemaid.entity.passive.EntityMaid; import com.github.tartaricacid.touhoulittlemaid.entity.projectile.EntityDanmaku; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityAltar; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.client.registry.ClientRegistry; import net.minecraftforge.fml.client.registry.RenderingRegistry; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; @@ -23,5 +26,6 @@ public static void clientSetup(FMLClientSetupEvent evt) { RenderingRegistry.registerEntityRenderingHandler(EntityDanmaku.TYPE, EntityDanmakuRenderer::new); RenderingRegistry.registerEntityRenderingHandler(EntityPowerPoint.TYPE, EntityPowerPointRenderer::new); RenderingRegistry.registerEntityRenderingHandler(EntityExtinguishingAgent.TYPE, EntityExtinguishingAgentRenderer::new); + ClientRegistry.bindTileEntityRenderer(TileEntityAltar.TYPE, TileEntityAltarRenderer::new); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/AltarModel.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/AltarModel.java new file mode 100644 index 000000000..b2018f6e9 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/model/AltarModel.java @@ -0,0 +1,642 @@ +package com.github.tartaricacid.touhoulittlemaid.client.model; + + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.vertex.IVertexBuilder; +import net.minecraft.client.renderer.entity.model.EntityModel; +import net.minecraft.client.renderer.model.ModelRenderer; +import net.minecraft.entity.Entity; + +public class AltarModel extends EntityModel { + private final ModelRenderer pillar; + private final ModelRenderer bone2; + private final ModelRenderer bone3; + private final ModelRenderer bone4; + private final ModelRenderer bone5; + private final ModelRenderer bone10; + private final ModelRenderer bone6; + private final ModelRenderer bone7; + private final ModelRenderer bone8; + private final ModelRenderer bone9; + private final ModelRenderer pillar2; + private final ModelRenderer bone11; + private final ModelRenderer bone12; + private final ModelRenderer bone13; + private final ModelRenderer bone14; + private final ModelRenderer bone15; + private final ModelRenderer bone16; + private final ModelRenderer bone17; + private final ModelRenderer bone18; + private final ModelRenderer bone19; + private final ModelRenderer pillar3; + private final ModelRenderer bone20; + private final ModelRenderer bone21; + private final ModelRenderer bone22; + private final ModelRenderer bone23; + private final ModelRenderer bone24; + private final ModelRenderer bone25; + private final ModelRenderer bone26; + private final ModelRenderer bone27; + private final ModelRenderer bone28; + private final ModelRenderer pillar4; + private final ModelRenderer bone29; + private final ModelRenderer bone30; + private final ModelRenderer bone31; + private final ModelRenderer bone32; + private final ModelRenderer bone33; + private final ModelRenderer bone34; + private final ModelRenderer bone35; + private final ModelRenderer bone36; + private final ModelRenderer bone37; + private final ModelRenderer pillar5; + private final ModelRenderer bone38; + private final ModelRenderer bone39; + private final ModelRenderer bone40; + private final ModelRenderer bone41; + private final ModelRenderer bone42; + private final ModelRenderer bone43; + private final ModelRenderer bone44; + private final ModelRenderer bone45; + private final ModelRenderer bone46; + private final ModelRenderer pillar6; + private final ModelRenderer bone47; + private final ModelRenderer bone48; + private final ModelRenderer bone49; + private final ModelRenderer bone50; + private final ModelRenderer bone51; + private final ModelRenderer bone52; + private final ModelRenderer bone53; + private final ModelRenderer bone54; + private final ModelRenderer bone55; + private final ModelRenderer bone; + private final ModelRenderer bone56; + private final ModelRenderer bone57; + private final ModelRenderer bone58; + private final ModelRenderer bone59; + private final ModelRenderer bone60; + private final ModelRenderer bone61; + private final ModelRenderer bone63; + private final ModelRenderer bone62; + private final ModelRenderer bone64; + + public AltarModel() { + texWidth = 256; + texHeight = 256; + + pillar = new ModelRenderer(this); + pillar.setPos(0.0F, 24.0F, 0.0F); + pillar.texOffs(0, 44).addBox(48.0F, -48.0F, 16.0F, 16.0F, 48.0F, 16.0F, 0.0F, false); + pillar.texOffs(0, 21).addBox(47.5F, -48.5F, 15.5F, 17.0F, 6.0F, 17.0F, 0.0F, false); + pillar.texOffs(0, 0).addBox(47.5F, -33.5F, 15.5F, 17.0F, 4.0F, 17.0F, 0.0F, false); + + bone2 = new ModelRenderer(this); + bone2.setPos(-4.25F, -27.5F, 14.8F); + pillar.addChild(bone2); + setRotationAngle(bone2, -0.1745F, 0.1745F, 0.7854F); + bone2.texOffs(0, 0).addBox(36.9617F, -42.2246F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone2.texOffs(0, 0).addBox(40.9617F, -38.2246F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone2.texOffs(0, 0).addBox(44.9617F, -34.2246F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone3 = new ModelRenderer(this); + bone3.setPos(4.25F, -27.5F, 14.8F); + pillar.addChild(bone3); + setRotationAngle(bone3, -0.1745F, -0.1745F, -0.7854F); + bone3.texOffs(0, 0).addBox(37.0311F, 38.1562F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone3.texOffs(0, 0).addBox(33.0311F, 42.1562F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone3.texOffs(0, 0).addBox(29.0311F, 46.1562F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone4 = new ModelRenderer(this); + bone4.setPos(-4.25F, -27.5F, 33.2F); + pillar.addChild(bone4); + setRotationAngle(bone4, 0.1745F, -0.1745F, 0.7854F); + bone4.texOffs(0, 0).addBox(36.9964F, -42.1904F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone4.texOffs(0, 0).addBox(40.9964F, -38.1904F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone4.texOffs(0, 0).addBox(44.9964F, -34.1904F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone5 = new ModelRenderer(this); + bone5.setPos(4.25F, -27.5F, 33.2F); + pillar.addChild(bone5); + setRotationAngle(bone5, 0.1745F, 0.1745F, -0.7854F); + bone5.texOffs(0, 0).addBox(36.9964F, 38.1904F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone5.texOffs(0, 0).addBox(32.9964F, 42.1904F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone5.texOffs(0, 0).addBox(28.9964F, 46.1904F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone10 = new ModelRenderer(this); + bone10.setPos(0.0F, 0.0F, 24.0F); + pillar.addChild(bone10); + setRotationAngle(bone10, 0.0F, 1.5708F, 0.0F); + + + bone6 = new ModelRenderer(this); + bone6.setPos(-4.25F, -27.5F, -9.2F); + bone10.addChild(bone6); + setRotationAngle(bone6, -0.1745F, 0.1745F, 0.7854F); + bone6.texOffs(0, 0).addBox(-11.7243F, -11.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone6.texOffs(0, 0).addBox(-7.7243F, -7.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone6.texOffs(0, 0).addBox(-3.7243F, -3.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone7 = new ModelRenderer(this); + bone7.setPos(4.25F, -27.5F, -9.2F); + bone10.addChild(bone7); + setRotationAngle(bone7, -0.1745F, -0.1745F, -0.7854F); + bone7.texOffs(0, 0).addBox(7.7243F, -11.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone7.texOffs(0, 0).addBox(3.7243F, -7.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone7.texOffs(0, 0).addBox(-0.2757F, -3.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone8 = new ModelRenderer(this); + bone8.setPos(-4.25F, -27.5F, 9.2F); + bone10.addChild(bone8); + setRotationAngle(bone8, 0.1745F, -0.1745F, 0.7854F); + bone8.texOffs(0, 0).addBox(7.7243F, 7.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone8.texOffs(0, 0).addBox(11.7243F, 11.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone8.texOffs(0, 0).addBox(15.7243F, 15.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone9 = new ModelRenderer(this); + bone9.setPos(4.25F, -27.5F, 9.2F); + bone10.addChild(bone9); + setRotationAngle(bone9, 0.1745F, 0.1745F, -0.7854F); + bone9.texOffs(0, 0).addBox(-11.7243F, 7.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone9.texOffs(0, 0).addBox(-15.7243F, 11.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone9.texOffs(0, 0).addBox(-19.7243F, 15.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + pillar2 = new ModelRenderer(this); + pillar2.setPos(0.0F, 24.0F, 0.0F); + pillar2.texOffs(0, 44).addBox(48.0F, -48.0F, -32.0F, 16.0F, 48.0F, 16.0F, 0.0F, false); + pillar2.texOffs(0, 21).addBox(47.5F, -48.5F, -32.5F, 17.0F, 6.0F, 17.0F, 0.0F, false); + pillar2.texOffs(0, 0).addBox(47.5F, -33.5F, -32.5F, 17.0F, 4.0F, 17.0F, 0.0F, false); + + bone11 = new ModelRenderer(this); + bone11.setPos(-4.25F, -27.5F, -14.8F); + pillar2.addChild(bone11); + setRotationAngle(bone11, 0.1745F, -0.1745F, 0.7854F); + bone11.texOffs(0, 0).addBox(36.9617F, -42.2246F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone11.texOffs(0, 0).addBox(40.9617F, -38.2246F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone11.texOffs(0, 0).addBox(44.9617F, -34.2246F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone12 = new ModelRenderer(this); + bone12.setPos(4.25F, -27.5F, -14.8F); + pillar2.addChild(bone12); + setRotationAngle(bone12, 0.1745F, 0.1745F, -0.7854F); + bone12.texOffs(0, 0).addBox(37.0311F, 38.1562F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone12.texOffs(0, 0).addBox(33.0311F, 42.1562F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone12.texOffs(0, 0).addBox(29.0311F, 46.1562F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone13 = new ModelRenderer(this); + bone13.setPos(-4.25F, -27.5F, -33.2F); + pillar2.addChild(bone13); + setRotationAngle(bone13, -0.1745F, 0.1745F, 0.7854F); + bone13.texOffs(0, 0).addBox(36.9964F, -42.1904F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone13.texOffs(0, 0).addBox(40.9964F, -38.1904F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone13.texOffs(0, 0).addBox(44.9964F, -34.1904F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone14 = new ModelRenderer(this); + bone14.setPos(4.25F, -27.5F, -33.2F); + pillar2.addChild(bone14); + setRotationAngle(bone14, -0.1745F, -0.1745F, -0.7854F); + bone14.texOffs(0, 0).addBox(36.9964F, 38.1904F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone14.texOffs(0, 0).addBox(32.9964F, 42.1904F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone14.texOffs(0, 0).addBox(28.9964F, 46.1904F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone15 = new ModelRenderer(this); + bone15.setPos(0.0F, 0.0F, -24.0F); + pillar2.addChild(bone15); + setRotationAngle(bone15, 0.0F, -1.5708F, 0.0F); + + + bone16 = new ModelRenderer(this); + bone16.setPos(-4.25F, -27.5F, 9.2F); + bone15.addChild(bone16); + setRotationAngle(bone16, 0.1745F, -0.1745F, 0.7854F); + bone16.texOffs(0, 0).addBox(-11.7243F, -11.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone16.texOffs(0, 0).addBox(-7.7243F, -7.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone16.texOffs(0, 0).addBox(-3.7243F, -3.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone17 = new ModelRenderer(this); + bone17.setPos(4.25F, -27.5F, 9.2F); + bone15.addChild(bone17); + setRotationAngle(bone17, 0.1745F, 0.1745F, -0.7854F); + bone17.texOffs(0, 0).addBox(7.7243F, -11.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone17.texOffs(0, 0).addBox(3.7243F, -7.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone17.texOffs(0, 0).addBox(-0.2757F, -3.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone18 = new ModelRenderer(this); + bone18.setPos(-4.25F, -27.5F, -9.2F); + bone15.addChild(bone18); + setRotationAngle(bone18, -0.1745F, 0.1745F, 0.7854F); + bone18.texOffs(0, 0).addBox(7.7243F, 7.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone18.texOffs(0, 0).addBox(11.7243F, 11.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone18.texOffs(0, 0).addBox(15.7243F, 15.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone19 = new ModelRenderer(this); + bone19.setPos(4.25F, -27.5F, -9.2F); + bone15.addChild(bone19); + setRotationAngle(bone19, -0.1745F, -0.1745F, -0.7854F); + bone19.texOffs(0, 0).addBox(-11.7243F, 7.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone19.texOffs(0, 0).addBox(-15.7243F, 11.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone19.texOffs(0, 0).addBox(-19.7243F, 15.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + pillar3 = new ModelRenderer(this); + pillar3.setPos(0.0F, 24.0F, 0.0F); + pillar3.texOffs(0, 44).addBox(-64.0F, -48.0F, 16.0F, 16.0F, 48.0F, 16.0F, 0.0F, true); + pillar3.texOffs(0, 21).addBox(-64.5F, -48.5F, 15.5F, 17.0F, 6.0F, 17.0F, 0.0F, true); + pillar3.texOffs(0, 0).addBox(-64.5F, -33.5F, 15.5F, 17.0F, 4.0F, 17.0F, 0.0F, true); + + bone20 = new ModelRenderer(this); + bone20.setPos(4.25F, -27.5F, 14.8F); + pillar3.addChild(bone20); + setRotationAngle(bone20, -0.1745F, -0.1745F, -0.7854F); + bone20.texOffs(0, 0).addBox(-40.9617F, -42.2246F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone20.texOffs(0, 0).addBox(-44.9617F, -38.2246F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone20.texOffs(0, 0).addBox(-48.9617F, -34.2246F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone21 = new ModelRenderer(this); + bone21.setPos(-4.25F, -27.5F, 14.8F); + pillar3.addChild(bone21); + setRotationAngle(bone21, -0.1745F, 0.1745F, 0.7854F); + bone21.texOffs(0, 0).addBox(-41.0311F, 38.1562F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone21.texOffs(0, 0).addBox(-37.0311F, 42.1562F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone21.texOffs(0, 0).addBox(-33.0311F, 46.1562F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone22 = new ModelRenderer(this); + bone22.setPos(4.25F, -27.5F, 33.2F); + pillar3.addChild(bone22); + setRotationAngle(bone22, 0.1745F, 0.1745F, -0.7854F); + bone22.texOffs(0, 0).addBox(-40.9964F, -42.1904F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone22.texOffs(0, 0).addBox(-44.9964F, -38.1904F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone22.texOffs(0, 0).addBox(-48.9964F, -34.1904F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone23 = new ModelRenderer(this); + bone23.setPos(-4.25F, -27.5F, 33.2F); + pillar3.addChild(bone23); + setRotationAngle(bone23, 0.1745F, -0.1745F, 0.7854F); + bone23.texOffs(0, 0).addBox(-40.9964F, 38.1904F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone23.texOffs(0, 0).addBox(-36.9964F, 42.1904F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone23.texOffs(0, 0).addBox(-32.9964F, 46.1904F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone24 = new ModelRenderer(this); + bone24.setPos(0.0F, 0.0F, 24.0F); + pillar3.addChild(bone24); + setRotationAngle(bone24, 0.0F, -1.5708F, 0.0F); + + + bone25 = new ModelRenderer(this); + bone25.setPos(4.25F, -27.5F, -9.2F); + bone24.addChild(bone25); + setRotationAngle(bone25, -0.1745F, -0.1745F, -0.7854F); + bone25.texOffs(0, 0).addBox(7.7243F, -11.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone25.texOffs(0, 0).addBox(3.7243F, -7.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone25.texOffs(0, 0).addBox(-0.2757F, -3.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone26 = new ModelRenderer(this); + bone26.setPos(-4.25F, -27.5F, -9.2F); + bone24.addChild(bone26); + setRotationAngle(bone26, -0.1745F, 0.1745F, 0.7854F); + bone26.texOffs(0, 0).addBox(-11.7243F, -11.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone26.texOffs(0, 0).addBox(-7.7243F, -7.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone26.texOffs(0, 0).addBox(-3.7243F, -3.5766F, 54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone27 = new ModelRenderer(this); + bone27.setPos(4.25F, -27.5F, 9.2F); + bone24.addChild(bone27); + setRotationAngle(bone27, 0.1745F, 0.1745F, -0.7854F); + bone27.texOffs(0, 0).addBox(-11.7243F, 7.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone27.texOffs(0, 0).addBox(-15.7243F, 11.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone27.texOffs(0, 0).addBox(-19.7243F, 15.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone28 = new ModelRenderer(this); + bone28.setPos(-4.25F, -27.5F, 9.2F); + bone24.addChild(bone28); + setRotationAngle(bone28, 0.1745F, -0.1745F, 0.7854F); + bone28.texOffs(0, 0).addBox(7.7243F, 7.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone28.texOffs(0, 0).addBox(11.7243F, 11.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone28.texOffs(0, 0).addBox(15.7243F, 15.5766F, 54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + pillar4 = new ModelRenderer(this); + pillar4.setPos(0.0F, 24.0F, 0.0F); + pillar4.texOffs(0, 44).addBox(-64.0F, -48.0F, -32.0F, 16.0F, 48.0F, 16.0F, 0.0F, true); + pillar4.texOffs(0, 21).addBox(-64.5F, -48.5F, -32.5F, 17.0F, 6.0F, 17.0F, 0.0F, true); + pillar4.texOffs(0, 0).addBox(-64.5F, -33.5F, -32.5F, 17.0F, 4.0F, 17.0F, 0.0F, true); + + bone29 = new ModelRenderer(this); + bone29.setPos(4.25F, -27.5F, -14.8F); + pillar4.addChild(bone29); + setRotationAngle(bone29, 0.1745F, 0.1745F, -0.7854F); + bone29.texOffs(0, 0).addBox(-40.9617F, -42.2246F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone29.texOffs(0, 0).addBox(-44.9617F, -38.2246F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone29.texOffs(0, 0).addBox(-48.9617F, -34.2246F, -0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone30 = new ModelRenderer(this); + bone30.setPos(-4.25F, -27.5F, -14.8F); + pillar4.addChild(bone30); + setRotationAngle(bone30, 0.1745F, -0.1745F, 0.7854F); + bone30.texOffs(0, 0).addBox(-41.0311F, 38.1562F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone30.texOffs(0, 0).addBox(-37.0311F, 42.1562F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone30.texOffs(0, 0).addBox(-33.0311F, 46.1562F, -0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone31 = new ModelRenderer(this); + bone31.setPos(4.25F, -27.5F, -33.2F); + pillar4.addChild(bone31); + setRotationAngle(bone31, -0.1745F, -0.1745F, -0.7854F); + bone31.texOffs(0, 0).addBox(-40.9964F, -42.1904F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone31.texOffs(0, 0).addBox(-44.9964F, -38.1904F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone31.texOffs(0, 0).addBox(-48.9964F, -34.1904F, 0.0955F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone32 = new ModelRenderer(this); + bone32.setPos(-4.25F, -27.5F, -33.2F); + pillar4.addChild(bone32); + setRotationAngle(bone32, -0.1745F, 0.1745F, 0.7854F); + bone32.texOffs(0, 0).addBox(-40.9964F, 38.1904F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone32.texOffs(0, 0).addBox(-36.9964F, 42.1904F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone32.texOffs(0, 0).addBox(-32.9964F, 46.1904F, 0.3045F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone33 = new ModelRenderer(this); + bone33.setPos(0.0F, 0.0F, -24.0F); + pillar4.addChild(bone33); + setRotationAngle(bone33, 0.0F, 1.5708F, 0.0F); + + + bone34 = new ModelRenderer(this); + bone34.setPos(4.25F, -27.5F, 9.2F); + bone33.addChild(bone34); + setRotationAngle(bone34, 0.1745F, 0.1745F, -0.7854F); + bone34.texOffs(0, 0).addBox(7.7243F, -11.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone34.texOffs(0, 0).addBox(3.7243F, -7.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone34.texOffs(0, 0).addBox(-0.2757F, -3.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone35 = new ModelRenderer(this); + bone35.setPos(-4.25F, -27.5F, 9.2F); + bone33.addChild(bone35); + setRotationAngle(bone35, 0.1745F, -0.1745F, 0.7854F); + bone35.texOffs(0, 0).addBox(-11.7243F, -11.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone35.texOffs(0, 0).addBox(-7.7243F, -7.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone35.texOffs(0, 0).addBox(-3.7243F, -3.5766F, -54.5114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone36 = new ModelRenderer(this); + bone36.setPos(4.25F, -27.5F, -9.2F); + bone33.addChild(bone36); + setRotationAngle(bone36, -0.1745F, -0.1745F, -0.7854F); + bone36.texOffs(0, 0).addBox(-11.7243F, 7.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone36.texOffs(0, 0).addBox(-15.7243F, 11.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone36.texOffs(0, 0).addBox(-19.7243F, 15.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone37 = new ModelRenderer(this); + bone37.setPos(-4.25F, -27.5F, -9.2F); + bone33.addChild(bone37); + setRotationAngle(bone37, -0.1745F, 0.1745F, 0.7854F); + bone37.texOffs(0, 0).addBox(7.7243F, 7.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone37.texOffs(0, 0).addBox(11.7243F, 11.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone37.texOffs(0, 0).addBox(15.7243F, 15.5766F, -54.1114F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + pillar5 = new ModelRenderer(this); + pillar5.setPos(0.0F, 24.0F, 0.0F); + pillar5.texOffs(0, 44).addBox(-32.0F, -48.0F, 48.0F, 16.0F, 48.0F, 16.0F, 0.0F, true); + pillar5.texOffs(0, 21).addBox(-32.5F, -48.5F, 47.5F, 17.0F, 6.0F, 17.0F, 0.0F, true); + pillar5.texOffs(0, 0).addBox(-32.5F, -33.5F, 47.5F, 17.0F, 4.0F, 17.0F, 0.0F, true); + + bone38 = new ModelRenderer(this); + bone38.setPos(4.25F, -27.5F, 14.8F); + pillar5.addChild(bone38); + setRotationAngle(bone38, -0.1745F, -0.1745F, -0.7854F); + bone38.texOffs(0, 0).addBox(-13.1213F, -24.731F, 31.1903F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone38.texOffs(0, 0).addBox(-17.1213F, -20.731F, 31.1903F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone38.texOffs(0, 0).addBox(-21.1213F, -16.731F, 31.1903F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone39 = new ModelRenderer(this); + bone39.setPos(-4.25F, -27.5F, 14.8F); + pillar5.addChild(bone39); + setRotationAngle(bone39, -0.1745F, 0.1745F, 0.7854F); + bone39.texOffs(0, 0).addBox(-24.3042F, 9.7179F, 31.2799F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone39.texOffs(0, 0).addBox(-20.3042F, 13.7179F, 31.2799F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone39.texOffs(0, 0).addBox(-16.3042F, 17.7179F, 31.2799F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone40 = new ModelRenderer(this); + bone40.setPos(4.25F, -27.5F, 33.2F); + pillar5.addChild(bone40); + setRotationAngle(bone40, 0.1745F, 0.1745F, -0.7854F); + bone40.texOffs(0, 0).addBox(-24.2695F, -13.7521F, 30.8799F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone40.texOffs(0, 0).addBox(-28.2695F, -9.7521F, 30.8799F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone40.texOffs(0, 0).addBox(-32.2695F, -5.7521F, 30.8799F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone41 = new ModelRenderer(this); + bone41.setPos(-4.25F, -27.5F, 33.2F); + pillar5.addChild(bone41); + setRotationAngle(bone41, 0.1745F, -0.1745F, 0.7854F); + bone41.texOffs(0, 0).addBox(-13.156F, 20.6968F, 30.7903F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone41.texOffs(0, 0).addBox(-9.156F, 24.6968F, 30.7903F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone41.texOffs(0, 0).addBox(-5.156F, 28.6968F, 30.7903F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone42 = new ModelRenderer(this); + bone42.setPos(0.0F, 0.0F, 24.0F); + pillar5.addChild(bone42); + setRotationAngle(bone42, 0.0F, -1.5708F, 0.0F); + + + bone43 = new ModelRenderer(this); + bone43.setPos(4.25F, -27.5F, -9.2F); + bone42.addChild(bone43); + setRotationAngle(bone43, -0.1745F, -0.1745F, -0.7854F); + bone43.texOffs(0, 0).addBox(24.4512F, 16.8617F, 23.536F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone43.texOffs(0, 0).addBox(20.4512F, 20.8617F, 23.536F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone43.texOffs(0, 0).addBox(16.4512F, 24.8617F, 23.536F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone44 = new ModelRenderer(this); + bone44.setPos(-4.25F, -27.5F, -9.2F); + bone42.addChild(bone44); + setRotationAngle(bone44, -0.1745F, 0.1745F, 0.7854F); + bone44.texOffs(0, 0).addBox(16.1161F, -29.0702F, 23.4166F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone44.texOffs(0, 0).addBox(20.1161F, -25.0702F, 23.4166F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone44.texOffs(0, 0).addBox(24.1161F, -21.0702F, 23.4166F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone45 = new ModelRenderer(this); + bone45.setPos(4.25F, -27.5F, 9.2F); + bone42.addChild(bone45); + setRotationAngle(bone45, 0.1745F, 0.1745F, -0.7854F); + bone45.texOffs(0, 0).addBox(16.1161F, 25.0702F, 23.0166F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone45.texOffs(0, 0).addBox(12.1161F, 29.0702F, 23.0166F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone45.texOffs(0, 0).addBox(8.1161F, 33.0702F, 23.0166F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone46 = new ModelRenderer(this); + bone46.setPos(-4.25F, -27.5F, 9.2F); + bone42.addChild(bone46); + setRotationAngle(bone46, 0.1745F, -0.1745F, 0.7854F); + bone46.texOffs(0, 0).addBox(24.4512F, -20.8617F, 23.136F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone46.texOffs(0, 0).addBox(28.4512F, -16.8617F, 23.136F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone46.texOffs(0, 0).addBox(32.4512F, -12.8617F, 23.136F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + pillar6 = new ModelRenderer(this); + pillar6.setPos(0.0F, 24.0F, 0.0F); + pillar6.texOffs(0, 44).addBox(16.0F, -48.0F, 48.0F, 16.0F, 48.0F, 16.0F, 0.0F, false); + pillar6.texOffs(0, 21).addBox(15.5F, -48.5F, 47.5F, 17.0F, 6.0F, 17.0F, 0.0F, false); + pillar6.texOffs(0, 0).addBox(15.5F, -33.5F, 47.5F, 17.0F, 4.0F, 17.0F, 0.0F, false); + + bone47 = new ModelRenderer(this); + bone47.setPos(-4.25F, -27.5F, 14.8F); + pillar6.addChild(bone47); + setRotationAngle(bone47, -0.1745F, 0.1745F, 0.7854F); + bone47.texOffs(0, 0).addBox(9.1213F, -24.731F, 31.1903F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone47.texOffs(0, 0).addBox(13.1213F, -20.731F, 31.1903F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone47.texOffs(0, 0).addBox(17.1213F, -16.731F, 31.1903F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone48 = new ModelRenderer(this); + bone48.setPos(4.25F, -27.5F, 14.8F); + pillar6.addChild(bone48); + setRotationAngle(bone48, -0.1745F, -0.1745F, -0.7854F); + bone48.texOffs(0, 0).addBox(20.3042F, 9.7179F, 31.2799F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone48.texOffs(0, 0).addBox(16.3042F, 13.7179F, 31.2799F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone48.texOffs(0, 0).addBox(12.3042F, 17.7179F, 31.2799F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone49 = new ModelRenderer(this); + bone49.setPos(-4.25F, -27.5F, 33.2F); + pillar6.addChild(bone49); + setRotationAngle(bone49, 0.1745F, -0.1745F, 0.7854F); + bone49.texOffs(0, 0).addBox(20.2695F, -13.7521F, 30.8799F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone49.texOffs(0, 0).addBox(24.2695F, -9.7521F, 30.8799F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone49.texOffs(0, 0).addBox(28.2695F, -5.7521F, 30.8799F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone50 = new ModelRenderer(this); + bone50.setPos(4.25F, -27.5F, 33.2F); + pillar6.addChild(bone50); + setRotationAngle(bone50, 0.1745F, 0.1745F, -0.7854F); + bone50.texOffs(0, 0).addBox(9.156F, 20.6968F, 30.7903F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone50.texOffs(0, 0).addBox(5.156F, 24.6968F, 30.7903F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone50.texOffs(0, 0).addBox(1.156F, 28.6968F, 30.7903F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone51 = new ModelRenderer(this); + bone51.setPos(0.0F, 0.0F, 24.0F); + pillar6.addChild(bone51); + setRotationAngle(bone51, 0.0F, 1.5708F, 0.0F); + + + bone52 = new ModelRenderer(this); + bone52.setPos(-4.25F, -27.5F, -9.2F); + bone51.addChild(bone52); + setRotationAngle(bone52, -0.1745F, 0.1745F, 0.7854F); + bone52.texOffs(0, 0).addBox(-28.4512F, 16.8617F, 23.536F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone52.texOffs(0, 0).addBox(-24.4512F, 20.8617F, 23.536F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone52.texOffs(0, 0).addBox(-20.4512F, 24.8617F, 23.536F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone53 = new ModelRenderer(this); + bone53.setPos(4.25F, -27.5F, -9.2F); + bone51.addChild(bone53); + setRotationAngle(bone53, -0.1745F, -0.1745F, -0.7854F); + bone53.texOffs(0, 0).addBox(-20.1161F, -29.0702F, 23.4166F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone53.texOffs(0, 0).addBox(-24.1161F, -25.0702F, 23.4166F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone53.texOffs(0, 0).addBox(-28.1161F, -21.0702F, 23.4166F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone54 = new ModelRenderer(this); + bone54.setPos(-4.25F, -27.5F, 9.2F); + bone51.addChild(bone54); + setRotationAngle(bone54, 0.1745F, -0.1745F, 0.7854F); + bone54.texOffs(0, 0).addBox(-20.1161F, 25.0702F, 23.0166F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone54.texOffs(0, 0).addBox(-16.1161F, 29.0702F, 23.0166F, 4.0F, 4.0F, 0.0F, 0.0F, false); + bone54.texOffs(0, 0).addBox(-12.1161F, 33.0702F, 23.0166F, 4.0F, 4.0F, 0.0F, 0.0F, false); + + bone55 = new ModelRenderer(this); + bone55.setPos(4.25F, -27.5F, 9.2F); + bone51.addChild(bone55); + setRotationAngle(bone55, 0.1745F, 0.1745F, -0.7854F); + bone55.texOffs(0, 0).addBox(-28.4512F, -20.8617F, 23.136F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone55.texOffs(0, 0).addBox(-32.4512F, -16.8617F, 23.136F, 4.0F, 4.0F, 0.0F, 0.0F, true); + bone55.texOffs(0, 0).addBox(-36.4512F, -12.8617F, 23.136F, 4.0F, 4.0F, 0.0F, 0.0F, true); + + bone = new ModelRenderer(this); + bone.setPos(0.0F, 24.0F, 0.0F); + bone.texOffs(64, 39).addBox(-24.0F, -1.0F, -24.0F, 48.0F, 0.0F, 48.0F, 0.0F, false); + + bone56 = new ModelRenderer(this); + bone56.setPos(0.0F, 24.0F, 0.0F); + bone56.texOffs(150, 227).addBox(-32.0F, -12.0F, -64.0F, 16.0F, 12.0F, 16.0F, 0.0F, false); + bone56.texOffs(150, 227).addBox(16.0F, -12.0F, -64.0F, 16.0F, 12.0F, 16.0F, 0.0F, true); + bone56.texOffs(0, 236).addBox(-60.0F, -101.0F, -58.0F, 120.0F, 6.0F, 4.0F, 0.0F, false); + bone56.texOffs(2, 176).addBox(-54.5F, -95.0F, -64.0F, 109.0F, 3.0F, 16.0F, 0.0F, false); + bone56.texOffs(0, 186).addBox(-45.0F, -66.3F, -57.0F, 90.0F, 8.0F, 2.0F, 0.0F, false); + bone56.texOffs(192, 171).addBox(-32.0F, -42.0F, -64.0F, 16.0F, 30.0F, 16.0F, 0.0F, false); + bone56.texOffs(192, 171).addBox(16.0F, -42.0F, -64.0F, 16.0F, 30.0F, 16.0F, 0.0F, true); + bone56.texOffs(153, 188).addBox(16.5F, -55.0F, -63.5F, 15.0F, 13.0F, 15.0F, 0.0F, true); + bone56.texOffs(153, 188).addBox(-31.5F, -55.0F, -63.5F, 15.0F, 13.0F, 15.0F, 0.0F, false); + bone56.texOffs(49, 184).addBox(17.0F, -73.0F, -63.0F, 14.0F, 18.0F, 14.0F, 0.0F, true); + bone56.texOffs(49, 184).addBox(-31.0F, -73.0F, -63.0F, 14.0F, 18.0F, 14.0F, 0.0F, false); + bone56.texOffs(30, 173).addBox(17.5F, -85.0F, -62.5F, 13.0F, 12.0F, 13.0F, 0.0F, true); + bone56.texOffs(30, 173).addBox(-30.5F, -85.0F, -62.5F, 13.0F, 12.0F, 13.0F, 0.0F, false); + bone56.texOffs(0, 192).addBox(17.0F, -92.0F, -63.0F, 14.0F, 7.0F, 14.0F, 0.0F, true); + bone56.texOffs(0, 192).addBox(-31.0F, -92.0F, -63.0F, 14.0F, 7.0F, 14.0F, 0.0F, false); + bone56.texOffs(28, 208).addBox(-47.0F, -68.3F, -59.0F, 94.0F, 2.0F, 6.0F, 0.0F, false); + bone56.texOffs(0, 180).addBox(-50.5F, -92.0F, -60.0F, 101.0F, 5.0F, 8.0F, 0.0F, false); + + bone57 = new ModelRenderer(this); + bone57.setPos(0.0F, -98.0F, -56.5F); + bone56.addChild(bone57); + setRotationAngle(bone57, 0.2618F, 0.0F, 0.0F); + bone57.texOffs(0, 228).addBox(-55.5F, -1.0F, -14.5F, 111.0F, 2.0F, 14.0F, 0.0F, false); + + bone58 = new ModelRenderer(this); + bone58.setPos(0.0F, -98.0F, -55.5F); + bone56.addChild(bone58); + setRotationAngle(bone58, -0.2618F, 0.0F, 0.0F); + bone58.texOffs(0, 222).addBox(-55.5F, -1.0F, 0.5F, 111.0F, 2.0F, 14.0F, 0.0F, false); + + bone59 = new ModelRenderer(this); + bone59.setPos(0.0F, -67.3F, -59.25F); + bone56.addChild(bone59); + setRotationAngle(bone59, 0.0873F, 0.0F, 0.0F); + bone59.texOffs(224, 234).addBox(-6.0F, -20.0F, -1.0F, 12.0F, 20.0F, 2.0F, 0.0F, false); + bone59.texOffs(230, 7).addBox(-4.5F, -18.5F, -1.7F, 9.0F, 17.0F, 2.0F, 0.0F, false); + + bone60 = new ModelRenderer(this); + bone60.setPos(0.0F, -67.3F, -52.75F); + bone56.addChild(bone60); + setRotationAngle(bone60, -0.0873F, 0.0F, 0.0F); + bone60.texOffs(224, 234).addBox(-6.0F, -20.0F, -1.0F, 12.0F, 20.0F, 2.0F, 0.0F, false); + bone60.texOffs(230, 7).addBox(-4.5F, -18.5F, -0.25F, 9.0F, 17.0F, 2.0F, 0.0F, false); + + bone61 = new ModelRenderer(this); + bone61.setPos(0.0F, 0.0F, 0.0F); + bone56.addChild(bone61); + bone61.texOffs(16, 243).addBox(35.25F, -8.0F, -58.0F, 4.0F, 8.0F, 4.0F, 0.0F, true); + bone61.texOffs(5, 178).addBox(35.25F, -27.0F, -58.0F, 4.0F, 19.0F, 4.0F, 0.0F, true); + bone61.texOffs(5, 180).addBox(32.0F, -24.0F, -57.0F, 9.0F, 2.0F, 2.0F, 0.0F, true); + bone61.texOffs(217, 178).addBox(32.0F, -31.0F, -58.5F, 12.0F, 4.0F, 5.0F, 0.0F, true); + + bone63 = new ModelRenderer(this); + bone63.setPos(38.0F, -31.75F, -56.0F); + bone61.addChild(bone63); + setRotationAngle(bone63, 0.0F, 0.0F, 0.0873F); + bone63.texOffs(204, 236).addBox(-6.0F, -0.5F, -3.0F, 14.0F, 2.0F, 6.0F, 0.0F, true); + + bone62 = new ModelRenderer(this); + bone62.setPos(0.0F, 0.0F, 0.0F); + bone56.addChild(bone62); + bone62.texOffs(16, 243).addBox(-39.25F, -8.0F, -58.0F, 4.0F, 8.0F, 4.0F, 0.0F, false); + bone62.texOffs(5, 178).addBox(-39.25F, -27.0F, -58.0F, 4.0F, 19.0F, 4.0F, 0.0F, false); + bone62.texOffs(5, 180).addBox(-41.0F, -24.0F, -57.0F, 9.0F, 2.0F, 2.0F, 0.0F, false); + bone62.texOffs(217, 178).addBox(-44.0F, -31.0F, -58.5F, 12.0F, 4.0F, 5.0F, 0.0F, false); + + bone64 = new ModelRenderer(this); + bone64.setPos(-38.0F, -31.75F, -56.0F); + bone62.addChild(bone64); + setRotationAngle(bone64, 0.0F, 0.0F, -0.0873F); + bone64.texOffs(204, 236).addBox(-8.0F, -0.5F, -3.0F, 14.0F, 2.0F, 6.0F, 0.0F, false); + } + + @Override + public void setupAnim(Entity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) { + } + + @Override + public void renderToBuffer(MatrixStack matrixStack, IVertexBuilder buffer, int packedLight, int packedOverlay, float red, float green, float blue, float alpha) { + pillar.render(matrixStack, buffer, packedLight, packedOverlay); + pillar2.render(matrixStack, buffer, packedLight, packedOverlay); + pillar3.render(matrixStack, buffer, packedLight, packedOverlay); + pillar4.render(matrixStack, buffer, packedLight, packedOverlay); + pillar5.render(matrixStack, buffer, packedLight, packedOverlay); + pillar6.render(matrixStack, buffer, packedLight, packedOverlay); + bone.render(matrixStack, buffer, packedLight, packedOverlay); + bone56.render(matrixStack, buffer, packedLight, packedOverlay); + } + + public void setRotationAngle(ModelRenderer modelRenderer, float x, float y, float z) { + modelRenderer.xRot = x; + modelRenderer.yRot = y; + modelRenderer.zRot = z; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityAltarRenderer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityAltarRenderer.java new file mode 100644 index 000000000..c036aca11 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/TileEntityAltarRenderer.java @@ -0,0 +1,71 @@ +package com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.client.model.AltarModel; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityAltar; +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.vertex.IVertexBuilder; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.tileentity.TileEntityRenderer; +import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.vector.Vector3f; + +import static net.minecraft.client.renderer.model.ItemCameraTransforms.TransformType.GROUND; + +public class TileEntityAltarRenderer extends TileEntityRenderer { + private static final AltarModel MODEL = new AltarModel(); + private static final ResourceLocation TEXTURE = new ResourceLocation(TouhouLittleMaid.MOD_ID, "textures/entity/altar.png"); + + public TileEntityAltarRenderer(TileEntityRendererDispatcher render) { + super(render); + } + + @Override + public void render(TileEntityAltar te, float partialTicks, MatrixStack matrixStack, IRenderTypeBuffer bufferIn, int combinedLightIn, int combinedOverlayIn) { + if (te.isRender()) { + matrixStack.pushPose(); + this.setTranslateAndPose(te, matrixStack); + matrixStack.mulPose(Vector3f.ZN.rotationDegrees(180)); + IVertexBuilder buffer = bufferIn.getBuffer(RenderType.entityTranslucent(TEXTURE)); + MODEL.renderToBuffer(matrixStack, buffer, combinedLightIn, combinedOverlayIn, 1.0F, 1.0F, 1.0F, 1.0F); + matrixStack.popPose(); + } + + if (te.isCanPlaceItem() && !te.handler.getStackInSlot(0).isEmpty()) { + ItemStack stack = te.handler.getStackInSlot(0); + matrixStack.pushPose(); + matrixStack.translate(0.5, 1.25, 0.5); + Minecraft.getInstance().getItemRenderer().renderStatic(stack, GROUND, combinedLightIn, combinedOverlayIn, matrixStack, bufferIn); + matrixStack.popPose(); + } + } + + @Override + public boolean shouldRenderOffScreen(TileEntityAltar te) { + return true; + } + + private void setTranslateAndPose(TileEntityAltar te, MatrixStack matrixStackIn) { + switch (te.getDirection()) { + case SOUTH: + matrixStackIn.translate(1, -1.5, -3); + matrixStackIn.mulPose(Vector3f.YP.rotationDegrees(180)); + break; + case EAST: + matrixStackIn.translate(-3, -1.5, 0); + matrixStackIn.mulPose(Vector3f.YP.rotationDegrees(270)); + break; + case WEST: + matrixStackIn.translate(4, -1.5, 1); + matrixStackIn.mulPose(Vector3f.YP.rotationDegrees(90)); + break; + case NORTH: + default: + matrixStackIn.translate(0, -1.5, 4); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/package-info.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/package-info.java new file mode 100644 index 000000000..d34975087 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/client/renderer/tileentity/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.tartaricacid.touhoulittlemaid.client.renderer.tileentity; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/AltarRecipe.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/AltarRecipe.java new file mode 100644 index 000000000..fc97820f5 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/AltarRecipe.java @@ -0,0 +1,110 @@ +package com.github.tartaricacid.touhoulittlemaid.crafting; + +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.init.InitRecipes; +import com.github.tartaricacid.touhoulittlemaid.inventory.AltarRecipeInventory; +import com.google.common.base.Preconditions; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.SpawnReason; +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.item.crafting.IRecipeType; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.NonNullList; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.text.StringTextComponent; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.util.RecipeMatcher; + +import javax.annotation.Nullable; + +import static com.github.tartaricacid.touhoulittlemaid.inventory.AltarRecipeInventory.RECIPES_SIZE; + +public class AltarRecipe implements IRecipe { + private final ResourceLocation id; + private final EntityType entityType; + @Nullable + private final CompoundNBT extraData; + private final float powerCost; + private final NonNullList inputs; + + public AltarRecipe(ResourceLocation id, EntityType entityType, @Nullable CompoundNBT extraData, float powerCost, Ingredient... inputs) { + Preconditions.checkArgument(0 < inputs.length && inputs.length <= RECIPES_SIZE, "Ingredients count is illegal!"); + this.id = id; + this.entityType = entityType; + this.extraData = extraData; + this.powerCost = powerCost; + this.inputs = NonNullList.of(Ingredient.EMPTY, inputs); + } + + @Override + public boolean matches(AltarRecipeInventory inv, World worldIn) { + return RecipeMatcher.findMatches(inv.getItems(), inputs) != null; + } + + @Override + public ItemStack assemble(AltarRecipeInventory inv) { + return getResultItem().copy(); + } + + @Override + public boolean canCraftInDimensions(int width, int height) { + return false; + } + + @Override + public NonNullList getIngredients() { + return inputs; + } + + @Override + public ItemStack getResultItem() { + return ItemStack.EMPTY; + } + + @Override + public ResourceLocation getId() { + return this.id; + } + + @Override + public IRecipeSerializer getSerializer() { + return new AltarRecipeSerializer(); + } + + @Override + public IRecipeType getType() { + return InitRecipes.ALTAR_CRAFTING; + } + + @Override + public boolean isSpecial() { + return true; + } + + @Override + public ItemStack getToastSymbol() { + return InitItems.HAKUREI_GOHEI.get().getDefaultInstance(); + } + + public EntityType getEntityType() { + return entityType; + } + + @Nullable + public CompoundNBT getExtraData() { + return extraData; + } + + public float getPowerCost() { + return powerCost; + } + + public void spawnOutputEntity(ServerWorld world, BlockPos pos, AltarRecipeInventory inventory) { + entityType.spawn(world, extraData, StringTextComponent.EMPTY, null, pos, SpawnReason.SPAWN_EGG, true, true); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/AltarRecipeSerializer.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/AltarRecipeSerializer.java new file mode 100644 index 000000000..8ac76be48 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/AltarRecipeSerializer.java @@ -0,0 +1,67 @@ +package com.github.tartaricacid.touhoulittlemaid.crafting; + +import com.github.tartaricacid.touhoulittlemaid.util.EntityCraftingHelper; +import com.google.common.collect.Lists; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.mojang.datafixers.util.Pair; +import net.minecraft.entity.EntityType; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.JSONUtils; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.registries.ForgeRegistryEntry; + +import javax.annotation.Nullable; +import java.util.List; +import java.util.Optional; + +public class AltarRecipeSerializer extends ForgeRegistryEntry> implements IRecipeSerializer { + @Override + public AltarRecipe fromJson(ResourceLocation recipeId, JsonObject json) { + Pair, CompoundNBT> output = EntityCraftingHelper.getEntityData(JSONUtils.getAsJsonObject(json, "output")); + float powerCost = JSONUtils.getAsFloat(json, "power"); + JsonArray ingredients = JSONUtils.getAsJsonArray(json, "ingredients"); + List inputs = Lists.newArrayList(); + for (JsonElement e : ingredients) { + inputs.add(Ingredient.fromJson(e)); + } + return new AltarRecipe(recipeId, output.getFirst(), output.getSecond(), powerCost, inputs.toArray(new Ingredient[0])); + } + + @Nullable + @Override + public AltarRecipe fromNetwork(ResourceLocation recipeId, PacketBuffer buffer) { + Optional> typeOptional = EntityType.byString(buffer.readUtf()); + if (typeOptional.isPresent()) { + EntityType entityType = typeOptional.get(); + CompoundNBT extraData = buffer.readNbt(); + float powerCost = buffer.readFloat(); + Ingredient[] inputs = new Ingredient[buffer.readVarInt()]; + for (int i = 0; i < inputs.length; i++) { + inputs[i] = Ingredient.fromNetwork(buffer); + } + return new AltarRecipe(recipeId, entityType, extraData, powerCost, inputs); + } + throw new JsonParseException("Entity Type Tag Not Found"); + } + + @Override + public void toNetwork(PacketBuffer buffer, AltarRecipe recipe) { + ResourceLocation name = recipe.getEntityType().getRegistryName(); + if (name != null) { + buffer.writeUtf(name.toString()); + buffer.writeNbt(recipe.getExtraData()); + buffer.writeFloat(recipe.getPowerCost()); + buffer.writeVarInt(recipe.getIngredients().size()); + for (Ingredient input : recipe.getIngredients()) { + input.toNetwork(buffer); + } + } + throw new JsonParseException("Entity Type Tag Not Found"); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/package-info.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/package-info.java new file mode 100644 index 000000000..d69862974 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/crafting/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.tartaricacid.touhoulittlemaid.crafting; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/AltarRecipeProvider.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/AltarRecipeProvider.java new file mode 100644 index 000000000..59fc4dd73 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/AltarRecipeProvider.java @@ -0,0 +1,92 @@ +package com.github.tartaricacid.touhoulittlemaid.data; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.crafting.AltarRecipe; +import com.github.tartaricacid.touhoulittlemaid.init.InitItems; +import com.github.tartaricacid.touhoulittlemaid.init.InitRecipes; +import com.google.common.collect.Lists; +import com.google.gson.*; +import net.minecraft.data.DataGenerator; +import net.minecraft.data.DirectoryCache; +import net.minecraft.data.IDataProvider; +import net.minecraft.entity.EntityType; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.crafting.Ingredient; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.common.Tags; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +public class AltarRecipeProvider implements IDataProvider { + private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().disableHtmlEscaping().create(); + protected final DataGenerator generator; + private final List recipes = Lists.newArrayList(); + + public AltarRecipeProvider(DataGenerator generator) { + this.generator = generator; + } + + @Override + public void run(DirectoryCache cache) throws IOException { + Path path = this.generator.getOutputFolder(); + recipes.clear(); + this.registerRecipes(); + for (AltarRecipe recipe : recipes) { + JsonObject jsonObject = recipeToJson(recipe); + Path savePath = path.resolve("data/" + recipe.getId().getNamespace() + "/recipes/altar/" + recipe.getId().getPath() + ".json"); + IDataProvider.save(GSON, cache, jsonObject, savePath); + } + } + + protected void registerRecipes() { + addItemRecipes(new ResourceLocation(TouhouLittleMaid.MOD_ID, "craft_explosion_protect_bauble"), + InitItems.EXPLOSION_PROTECT_BAUBLE.get().getDefaultInstance(), 0.2f, + Ingredient.of(Items.NETHER_WART), Ingredient.of(Tags.Items.DYES_ORANGE), Ingredient.of(Tags.Items.OBSIDIAN), + Ingredient.of(Tags.Items.OBSIDIAN), Ingredient.of(Tags.Items.OBSIDIAN), Ingredient.of(Tags.Items.OBSIDIAN)); + } + + public void addRecipes(AltarRecipe recipe) { + recipes.add(recipe); + } + + public void addItemRecipes(ResourceLocation id, ItemStack output, float powerCost, Ingredient... inputs) { + CompoundNBT extraData = new CompoundNBT(); + extraData.put("Item", output.serializeNBT()); + addRecipes(new AltarRecipe(id, EntityType.ITEM, extraData, powerCost, inputs)); + } + + private JsonObject recipeToJson(AltarRecipe recipe) { + JsonObject json = new JsonObject(); + json.addProperty("type", InitRecipes.ALTAR_CRAFTING.toString()); + { + JsonObject output = new JsonObject(); + ResourceLocation name = recipe.getEntityType().getRegistryName(); + if (name == null) { + throw new JsonParseException("Entity Registry Name Not Found"); + } + output.addProperty("type", name.toString()); + if (recipe.getExtraData() != null) { + output.addProperty("nbt", recipe.getExtraData().getAsString()); + } + json.add("output", output); + } + json.addProperty("power", recipe.getPowerCost()); + { + JsonArray ingredients = new JsonArray(); + for (Ingredient ingredient : recipe.getIngredients()) { + ingredients.add(ingredient.toJson()); + } + json.add("ingredients", ingredients); + } + return json; + } + + @Override + public String getName() { + return "Maid Altar Recipe"; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/DataGenEvent.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/DataGenEvent.java index 4f489757e..a42bc5fbc 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/DataGenEvent.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/DataGenEvent.java @@ -10,5 +10,6 @@ public class DataGenEvent { @SubscribeEvent public static void dataGen(GatherDataEvent event) { event.getGenerator().addProvider(new MaidBlockStateProvider(event.getGenerator(), TouhouLittleMaid.MOD_ID, event.getExistingFileHelper())); + event.getGenerator().addProvider(new AltarRecipeProvider(event.getGenerator())); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/package-info.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/package-info.java new file mode 100644 index 000000000..004a06378 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/data/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.tartaricacid.touhoulittlemaid.data; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskManager.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskManager.java index 69b6868d1..c84bedd2c 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskManager.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/entity/task/TaskManager.java @@ -26,24 +26,24 @@ private TaskManager() { public static void init() { TaskManager manager = new TaskManager(); - manager.registerTask(IDLE_TASK); - manager.registerTask(new TaskAttack()); - manager.registerTask(new TaskBowAttack()); - manager.registerTask(new TaskDanmakuAttack()); - manager.registerTask(new TaskNormalFarm()); - manager.registerTask(new TaskSugarCane()); - manager.registerTask(new TaskMelon()); - manager.registerTask(new TaskCocoa()); - manager.registerTask(new TaskGrass()); - manager.registerTask(new TaskSnow()); - manager.registerTask(new TaskFeedOwner()); - manager.registerTask(new TaskShears()); - manager.registerTask(new TaskMilk()); - manager.registerTask(new TaskTorch()); - manager.registerTask(new TaskFeedAnimal()); - manager.registerTask(new TaskExtinguishing()); + manager.add(IDLE_TASK); + manager.add(new TaskAttack()); + manager.add(new TaskBowAttack()); + manager.add(new TaskDanmakuAttack()); + manager.add(new TaskNormalFarm()); + manager.add(new TaskSugarCane()); + manager.add(new TaskMelon()); + manager.add(new TaskCocoa()); + manager.add(new TaskGrass()); + manager.add(new TaskSnow()); + manager.add(new TaskFeedOwner()); + manager.add(new TaskShears()); + manager.add(new TaskMilk()); + manager.add(new TaskTorch()); + manager.add(new TaskFeedAnimal()); + manager.add(new TaskExtinguishing()); for (ILittleMaid littleMaid : TouhouLittleMaid.EXTENSIONS) { - littleMaid.registerMaidTask(manager); + littleMaid.addMaidTask(manager); } TASK_MAP = ImmutableMap.copyOf(TASK_MAP); TASK_INDEX = ImmutableList.copyOf(TASK_INDEX); @@ -74,7 +74,7 @@ public static List getTaskIndex() { /** * 注册 Task */ - public void registerTask(IMaidTask task) { + public void add(IMaidTask task) { TASK_MAP.put(task.getUid(), task); TASK_INDEX.add(task); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitBlocks.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitBlocks.java index c59099815..73edd533b 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitBlocks.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitBlocks.java @@ -1,14 +1,21 @@ package com.github.tartaricacid.touhoulittlemaid.init; import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.block.BlockAltar; import com.github.tartaricacid.touhoulittlemaid.block.BlockMaidBed; +import com.github.tartaricacid.touhoulittlemaid.tileentity.TileEntityAltar; import net.minecraft.block.Block; +import net.minecraft.tileentity.TileEntityType; import net.minecraftforge.fml.RegistryObject; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; public final class InitBlocks { public static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, TouhouLittleMaid.MOD_ID); + public static final DeferredRegister> TILE_ENTITIES = DeferredRegister.create(ForgeRegistries.TILE_ENTITIES, TouhouLittleMaid.MOD_ID); public static RegistryObject MAID_BED = BLOCKS.register("maid_bed", BlockMaidBed::new); + public static RegistryObject ALTAR = BLOCKS.register("altar", BlockAltar::new); + + public static RegistryObject> ALTAR_TE = TILE_ENTITIES.register("altar", () -> TileEntityAltar.TYPE); } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitItems.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitItems.java index 6aec84f32..c43dbee04 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitItems.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitItems.java @@ -8,11 +8,9 @@ import net.minecraft.item.ItemGroup; import net.minecraft.item.SpawnEggItem; import net.minecraftforge.fml.RegistryObject; -import net.minecraftforge.fml.common.Mod; import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.ForgeRegistries; -@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) public final class InitItems { public static final DeferredRegister ITEMS = DeferredRegister.create(ForgeRegistries.ITEMS, TouhouLittleMaid.MOD_ID); diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitRecipes.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitRecipes.java new file mode 100644 index 000000000..bc91c26d4 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/init/InitRecipes.java @@ -0,0 +1,38 @@ +package com.github.tartaricacid.touhoulittlemaid.init; + +import com.github.tartaricacid.touhoulittlemaid.TouhouLittleMaid; +import com.github.tartaricacid.touhoulittlemaid.crafting.AltarRecipe; +import com.github.tartaricacid.touhoulittlemaid.crafting.AltarRecipeSerializer; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.item.crafting.IRecipeSerializer; +import net.minecraft.item.crafting.IRecipeType; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.registry.Registry; +import net.minecraftforge.event.RegistryEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.RegistryObject; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.registries.DeferredRegister; +import net.minecraftforge.registries.ForgeRegistries; + +@Mod.EventBusSubscriber(bus = Mod.EventBusSubscriber.Bus.MOD) +public final class InitRecipes { + public static final DeferredRegister> RECIPE_SERIALIZERS = DeferredRegister.create(ForgeRegistries.RECIPE_SERIALIZERS, TouhouLittleMaid.MOD_ID); + + public static RegistryObject> ALTAR_RECIPE_SERIALIZER = RECIPE_SERIALIZERS.register("altar_crafting", AltarRecipeSerializer::new); + public static IRecipeType ALTAR_CRAFTING; + + @SubscribeEvent + public static void register(RegistryEvent.Register> evt) { + ALTAR_CRAFTING = register(TouhouLittleMaid.MOD_ID + ":altar_crafting"); + } + + private static > IRecipeType register(final String key) { + return Registry.register(Registry.RECIPE_TYPE, new ResourceLocation(key), new IRecipeType() { + @Override + public String toString() { + return key; + } + }); + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/AltarItemHandler.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/AltarItemHandler.java new file mode 100644 index 000000000..6dcae7a24 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/AltarItemHandler.java @@ -0,0 +1,10 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory; + +import net.minecraftforge.items.ItemStackHandler; + +public class AltarItemHandler extends ItemStackHandler { + @Override + public int getSlotLimit(int slot) { + return 1; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/AltarRecipeInventory.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/AltarRecipeInventory.java new file mode 100644 index 000000000..aa0a07012 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/inventory/AltarRecipeInventory.java @@ -0,0 +1,68 @@ +package com.github.tartaricacid.touhoulittlemaid.inventory; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.ItemStackHelper; +import net.minecraft.item.ItemStack; +import net.minecraft.util.NonNullList; + +public class AltarRecipeInventory implements IInventory { + public static final int RECIPES_SIZE = 6; + public final NonNullList items = NonNullList.withSize(RECIPES_SIZE, ItemStack.EMPTY); + + public AltarRecipeInventory() { + } + + @Override + public int getContainerSize() { + return RECIPES_SIZE; + } + + @Override + public boolean isEmpty() { + for (ItemStack stack : this.items) { + if (!stack.isEmpty()) { + return false; + } + } + return true; + } + + @Override + public ItemStack getItem(int index) { + return this.getContainerSize() <= index ? ItemStack.EMPTY : this.items.get(index); + } + + @Override + public ItemStack removeItem(int index, int count) { + return ItemStackHelper.removeItem(this.items, index, count); + } + + @Override + public ItemStack removeItemNoUpdate(int index) { + return ItemStackHelper.takeItem(this.items, index); + } + + @Override + public void setItem(int index, ItemStack stack) { + this.items.set(index, stack); + } + + @Override + public void setChanged() { + } + + @Override + public boolean stillValid(PlayerEntity player) { + return true; + } + + @Override + public void clearContent() { + this.items.clear(); + } + + public NonNullList getItems() { + return items; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemHakureiGohei.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemHakureiGohei.java index 994fba29d..4665c4599 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemHakureiGohei.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/ItemHakureiGohei.java @@ -1,11 +1,23 @@ package com.github.tartaricacid.touhoulittlemaid.item; +import com.github.tartaricacid.touhoulittlemaid.api.block.IMultiBlock; +import com.github.tartaricacid.touhoulittlemaid.block.multiblock.MultiBlockManager; import com.github.tartaricacid.touhoulittlemaid.init.InitItems; import com.google.common.base.Predicates; +import net.minecraft.block.BlockState; import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUseContext; import net.minecraft.item.ShootableItem; import net.minecraft.item.UseAction; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.Direction; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.template.Template; +import net.minecraft.world.server.ServerWorld; +import java.util.List; import java.util.function.Predicate; public class ItemHakureiGohei extends ShootableItem { @@ -32,4 +44,29 @@ public int getUseDuration(ItemStack stack) { public UseAction getUseAnimation(ItemStack stack) { return UseAction.BOW; } + + @Override + public ActionResultType useOn(ItemUseContext context) { + if (context.getHand() == Hand.MAIN_HAND) { + List multiBlockList = MultiBlockManager.getMultiBlockList(); + BlockState blockState = context.getLevel().getBlockState(context.getClickedPos()); + World world = context.getLevel(); + BlockPos pos = context.getClickedPos(); + Direction direction = context.getClickedFace(); + + for (IMultiBlock multiBlock : multiBlockList) { + if (multiBlock.isCoreBlock(blockState) && multiBlock.directionIsSuitable(direction)) { + if (world instanceof ServerWorld) { + BlockPos posStart = pos.offset(multiBlock.getCenterPos(direction)); + Template template = multiBlock.getTemplate((ServerWorld) world, direction); + if (multiBlock.isMatch(world, posStart, direction, template)) { + multiBlock.build(world, posStart, direction, template); + } + } + return ActionResultType.SUCCESS; + } + } + } + return super.useOn(context); + } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/BaubleManager.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/BaubleManager.java index 5401d2104..ac5aa6f03 100644 --- a/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/BaubleManager.java +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/item/bauble/BaubleManager.java @@ -23,17 +23,17 @@ private BaubleManager() { public static void init() { BaubleManager manager = new BaubleManager(); - manager.bindBauble(InitItems.DROWN_PROTECT_BAUBLE, new DrownProtectBauble()); - manager.bindBauble(InitItems.EXPLOSION_PROTECT_BAUBLE, new ExplosionProtectBauble()); - manager.bindBauble(InitItems.ULTRAMARINE_ORB_ELIXIR, new ExtraLifeBauble()); - manager.bindBauble(InitItems.FALL_PROTECT_BAUBLE, new FallProtectBauble()); - manager.bindBauble(InitItems.FIRE_PROTECT_BAUBLE, new FireProtectBauble()); - manager.bindBauble(InitItems.ITEM_MAGNET_BAUBLE, new ItemMagnetBauble()); - manager.bindBauble(InitItems.MAGIC_PROTECT_BAUBLE, new MagicProtectBauble()); - manager.bindBauble(InitItems.NIMBLE_FABRIC, new NimbleFabricBauble()); - manager.bindBauble(InitItems.PROJECTILE_PROTECT_BAUBLE, new ProjectileProtectBauble()); - manager.bindBauble(InitItems.MUTE_BAUBLE, new MuteBauble()); - manager.bindBauble(Items.TOTEM_OF_UNDYING, new UndyingTotemBauble()); + manager.bind(InitItems.DROWN_PROTECT_BAUBLE, new DrownProtectBauble()); + manager.bind(InitItems.EXPLOSION_PROTECT_BAUBLE, new ExplosionProtectBauble()); + manager.bind(InitItems.ULTRAMARINE_ORB_ELIXIR, new ExtraLifeBauble()); + manager.bind(InitItems.FALL_PROTECT_BAUBLE, new FallProtectBauble()); + manager.bind(InitItems.FIRE_PROTECT_BAUBLE, new FireProtectBauble()); + manager.bind(InitItems.ITEM_MAGNET_BAUBLE, new ItemMagnetBauble()); + manager.bind(InitItems.MAGIC_PROTECT_BAUBLE, new MagicProtectBauble()); + manager.bind(InitItems.NIMBLE_FABRIC, new NimbleFabricBauble()); + manager.bind(InitItems.PROJECTILE_PROTECT_BAUBLE, new ProjectileProtectBauble()); + manager.bind(InitItems.MUTE_BAUBLE, new MuteBauble()); + manager.bind(Items.TOTEM_OF_UNDYING, new UndyingTotemBauble()); for (ILittleMaid littleMaid : TouhouLittleMaid.EXTENSIONS) { littleMaid.bindMaidBauble(manager); } @@ -51,11 +51,11 @@ public static IMaidBauble getBauble(ItemStack stack) { return getBauble(RegistryObject.of(item.getRegistryName(), item::getRegistryType)); } - public void bindBauble(RegistryObject item, IMaidBauble bauble) { + public void bind(RegistryObject item, IMaidBauble bauble) { BAUBLES.put(item, bauble); } - public void bindBauble(Item item, IMaidBauble bauble) { + public void bind(Item item, IMaidBauble bauble) { BAUBLES.put(RegistryObject.of(item.getRegistryName(), item::getRegistryType), bauble); } } diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityAltar.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityAltar.java new file mode 100644 index 000000000..0632d2d9d --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/TileEntityAltar.java @@ -0,0 +1,145 @@ +package com.github.tartaricacid.touhoulittlemaid.tileentity; + +import com.github.tartaricacid.touhoulittlemaid.init.InitBlocks; +import com.github.tartaricacid.touhoulittlemaid.inventory.AltarItemHandler; +import com.github.tartaricacid.touhoulittlemaid.util.PosListData; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.play.server.SUpdateTileEntityPacket; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.tileentity.TileEntityType; +import net.minecraft.util.Direction; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.util.Constants; +import net.minecraftforge.items.ItemStackHandler; + +import javax.annotation.Nullable; + +public class TileEntityAltar extends TileEntity { + public static final TileEntityType TYPE = TileEntityType.Builder.of(TileEntityAltar::new, InitBlocks.ALTAR.get()).build(null); + + private static final String STORAGE_ITEM = "StorageItem"; + private static final String IS_RENDER = "IsRender"; + private static final String CAN_PLACE_ITEM = "CanPlaceItem"; + private static final String STORAGE_STATE_ID = "StorageBlockStateId"; + private static final String DIRECTION = "Direction"; + private static final String STORAGE_BLOCK_LIST = "StorageBlockList"; + private static final String CAN_PLACE_ITEM_POS_LIST = "CanPlaceItemPosList"; + + public final ItemStackHandler handler = new AltarItemHandler(); + + private boolean isRender = false; + private boolean canPlaceItem = false; + private BlockState storageState = Blocks.AIR.defaultBlockState(); + private PosListData blockPosList = new PosListData(); + private PosListData canPlaceItemPosList = new PosListData(); + private Direction direction = Direction.SOUTH; + + public TileEntityAltar() { + super(TYPE); + } + + public void setForgeData(BlockState storageState, boolean isRender, boolean canPlaceItem, Direction direction, + PosListData blockPosList, PosListData canPlaceItemPosList) { + this.isRender = isRender; + this.canPlaceItem = canPlaceItem; + this.storageState = storageState; + this.direction = direction; + this.blockPosList = blockPosList; + this.canPlaceItemPosList = canPlaceItemPosList; + refresh(); + } + + @Override + public CompoundNBT save(CompoundNBT compound) { + getTileData().putBoolean(IS_RENDER, isRender); + getTileData().putBoolean(CAN_PLACE_ITEM, canPlaceItem); + getTileData().putInt(STORAGE_STATE_ID, Block.getId(storageState)); + getTileData().put(STORAGE_ITEM, handler.serializeNBT()); + getTileData().putString(DIRECTION, direction.getSerializedName()); + getTileData().put(STORAGE_BLOCK_LIST, blockPosList.serialize()); + getTileData().put(CAN_PLACE_ITEM_POS_LIST, canPlaceItemPosList.serialize()); + return super.save(compound); + } + + @Override + public void load(BlockState state, CompoundNBT nbt) { + super.load(state, nbt); + isRender = getTileData().getBoolean(IS_RENDER); + canPlaceItem = getTileData().getBoolean(CAN_PLACE_ITEM); + storageState = Block.stateById(getTileData().getInt(STORAGE_STATE_ID)); + handler.deserializeNBT(getTileData().getCompound(STORAGE_ITEM)); + direction = Direction.byName(getTileData().getString(DIRECTION)); + blockPosList.deserialize(getTileData().getList(STORAGE_BLOCK_LIST, Constants.NBT.TAG_COMPOUND)); + canPlaceItemPosList.deserialize(getTileData().getList(CAN_PLACE_ITEM_POS_LIST, Constants.NBT.TAG_COMPOUND)); + } + + @Override + @OnlyIn(Dist.CLIENT) + public AxisAlignedBB getRenderBoundingBox() { + return new AxisAlignedBB(worldPosition.offset(-9, -5, -9), worldPosition.offset(9, 5, 9)); + } + + @Override + public CompoundNBT getUpdateTag() { + return this.save(new CompoundNBT()); + } + + @Nullable + @Override + public SUpdateTileEntityPacket getUpdatePacket() { + return new SUpdateTileEntityPacket(getBlockPos(), -1, this.save(new CompoundNBT())); + } + + @Override + public void onDataPacket(NetworkManager net, SUpdateTileEntityPacket pkt) { + if (level != null) { + this.load(level.getBlockState(pkt.getPos()), pkt.getTag()); + } + } + + public void refresh() { + setChanged(); + if (level != null) { + BlockState state = level.getBlockState(worldPosition); + level.sendBlockUpdated(worldPosition, state, state, Constants.BlockFlags.DEFAULT); + } + } + + public boolean isRender() { + return isRender; + } + + public boolean isCanPlaceItem() { + return canPlaceItem; + } + + public BlockState getStorageState() { + return storageState; + } + + public PosListData getBlockPosList() { + return blockPosList; + } + + public PosListData getCanPlaceItemPosList() { + return canPlaceItemPosList; + } + + public ItemStack getStorageItem() { + if (canPlaceItem) { + return handler.getStackInSlot(0); + } + return ItemStack.EMPTY; + } + + public Direction getDirection() { + return direction; + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/package-info.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/package-info.java new file mode 100644 index 000000000..1341c4295 --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/tileentity/package-info.java @@ -0,0 +1,7 @@ +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +package com.github.tartaricacid.touhoulittlemaid.tileentity; + +import mcp.MethodsReturnNonnullByDefault; + +import javax.annotation.ParametersAreNonnullByDefault; \ No newline at end of file diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EntityCraftingHelper.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EntityCraftingHelper.java new file mode 100644 index 000000000..e467bedff --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/EntityCraftingHelper.java @@ -0,0 +1,42 @@ +package com.github.tartaricacid.touhoulittlemaid.util; + +import com.google.gson.*; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.datafixers.util.Pair; +import net.minecraft.entity.EntityType; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.JsonToNBT; +import net.minecraft.util.JSONUtils; + +import java.util.Optional; + +public final class EntityCraftingHelper { + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); + private static final String TYPE_TAG = "type"; + private static final String NBT_TAG = "nbt"; + + public static Pair, CompoundNBT> getEntityData(JsonObject json) { + try { + Optional> optional = EntityType.byString(JSONUtils.getAsString(json, TYPE_TAG)); + if (optional.isPresent()) { + EntityType type = optional.get(); + JsonElement element = json.get(NBT_TAG); + CompoundNBT outputData = new CompoundNBT(); + if (element == null) { + return Pair.of(type, outputData); + } + CompoundNBT readData; + if (element.isJsonObject()) { + readData = JsonToNBT.parseTag(GSON.toJson(element)); + } else { + readData = JsonToNBT.parseTag(JSONUtils.convertToString(element, NBT_TAG)); + } + outputData.put("EntityTag", readData); + return Pair.of(type, outputData); + } + throw new JsonParseException("Entity Type Tag Not Found"); + } catch (CommandSyntaxException e) { + throw new JsonSyntaxException("Invalid NBT Entry: " + e.toString()); + } + } +} diff --git a/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/PosListData.java b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/PosListData.java new file mode 100644 index 000000000..95c07684b --- /dev/null +++ b/src/main/java/com/github/tartaricacid/touhoulittlemaid/util/PosListData.java @@ -0,0 +1,35 @@ +package com.github.tartaricacid.touhoulittlemaid.util; + +import com.google.common.collect.Lists; +import net.minecraft.nbt.ListNBT; +import net.minecraft.nbt.NBTUtil; +import net.minecraft.util.math.BlockPos; + +import java.util.List; + +public final class PosListData { + private final List data = Lists.newArrayList(); + + public ListNBT serialize() { + ListNBT nbt = new ListNBT(); + for (BlockPos pos : data) { + nbt.add(NBTUtil.writeBlockPos(pos)); + } + return nbt; + } + + public void deserialize(ListNBT nbt) { + data.clear(); + for (int i = 0; i < nbt.size(); i++) { + data.add(NBTUtil.readBlockPos(nbt.getCompound(i))); + } + } + + public List getData() { + return data; + } + + public void add(BlockPos pos) { + this.data.add(pos); + } +} diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 7e0fdb141..02b3c883f 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -19,4 +19,5 @@ public net.minecraft.client.renderer.model.ModelRenderer field_78805_m # childre public net.minecraft.entity.projectile.AbstractArrowEntity field_70254_i # inGround public net.minecraft.entity.LivingEntity func_70669_a(Lnet/minecraft/item/ItemStack;)V # breakItem -public net.minecraft.util.math.EntityPosWrapper field_220611_a # entity \ No newline at end of file +public net.minecraft.util.math.EntityPosWrapper field_220611_a # entity +public net.minecraft.world.gen.feature.template.Template field_204769_a # palettes \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/blockstates/altar.json b/src/main/resources/assets/touhou_little_maid/blockstates/altar.json new file mode 100644 index 000000000..e21f5d2ed --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/blockstates/altar.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "touhou_little_maid:block/altar" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/models/block/altar.json b/src/main/resources/assets/touhou_little_maid/models/block/altar.json new file mode 100644 index 000000000..9406a8491 --- /dev/null +++ b/src/main/resources/assets/touhou_little_maid/models/block/altar.json @@ -0,0 +1,5 @@ +{ + "textures": { + "particle": "minecraft:block/oak_planks" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/touhou_little_maid/textures/entity/altar.png b/src/main/resources/assets/touhou_little_maid/textures/entity/altar.png new file mode 100644 index 0000000000000000000000000000000000000000..dd9a1d36a52e725a287fe5f104dc4f01142efc72 GIT binary patch literal 46708 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

mANbEYKLXtKZ&ub(lkzD{Q9eI4i4IsI|n5P zi=%0-YPnzGE6*XHrs0$QgmgRtpx4p17y?APB?qeCQK*k7&$c3^I+1d58nRyXe5&PN zGtS{7YBnm7h~h-;`M=(7wO>Q#s~hhnYoaLJzE{y6w%I=IIhKt9syva5`NgaO)~qPd zw++hHIZRj$q@%y6WZV4Q!d{1}y}kg)E4kP@E;$^HkZ3XinZN z-3%Tf^6{ume?C^I--8!f<>a+%0l-B9&SL{jR@GUm8k4_Q%Q+$FvG}EL_chstGs;>0 zU39q~t+?8t^JM{y7@NOM$Pm8Uj+)=HGec%GQ9)wWZMgz8v&~zYLtNLqCI*HEK?bP& znmYQ(wsf6?Nz1;U-5z2mt~}L@q9vl0C$d*iubiqwJk&f=M>jGQ#nBN5UsP-hKBZRJ z4^EK0eZ++i|4-eY722tl(N6!_fv0w2b0lH3gSB!Ccfm}I+GNaR)<#OKCQZ}V5oK^B zCvZaCj{f_R{~Oh;!MiEasr_%kZst- z#g}_XuPLUUarB0DVU0n3zIb9eQ$5M9NzzA=8JT0pQO8U#qE6?bel}Owj?;O6opdEg zrIE)Fqya06)1PqV7uoMXw(ZTp=(BTtwE83{2yy)c3a>LNO!OiTFATxb=na$?^q`nh zK1ksE14|}m*kcMNe*t-U-H;N!b76Il94z6%Oy;~B^gXJ-nB%z)HcK`-z%RbHD|#-7 zH4~>TqWX-X<3~FH4L@7TJe|1QMc%WEY9keQ%QcQ?jXL}h&7p+V`@3)>vg2ffPL4JpY;6d;g_5^ z=I-NyY5ntuVVZ90k2Rh;cg0tBfG$}BH(DJ7vh%LBQ7Wf0(kk!g?}_=DK4AAQ$OVX* zDA1bEs6cWqehN~q#Xf6$1))~SH*RS4Y1CLtU+PAVKWBU}@G_m)ocEuh{ykoo+*`Wy z_HzBn=Pm)++=?g=u{k1^AqDTQ4Y+G{@)y+v&Zvg@Cp8|s1luR8Pu_Ea$f`-}Y~Osg!xeH=ZNP<_;N<} z#Nv3t+HAM(x@mg_!s*)5(vAsnHi)}AV6<71GK<0a^?fSOc)K);xL8fyZkI3y^$d^i zQ2Ssm9^s1nMf;EC)F-x;qMIJe3H*+;!H_9U<;#TuI3Th$p2SSxOAy zdVRz5(qBy268niCW!Q(%*dP3I(W;8M6C*>V83O#wb)IP`l_~GdH z@<5He--xfv!oAt`#DMdk-U>M}9{p;@`^pmI0tb?EkDPvhMKxdXmS~iAPp#(L0TV+% z8Xgj^=Y2vkf|o;iF6LQ?-+kL_)#*!<-?0rMvJ{Dd2w+hTswvf`M>dQEEbi;8ITC31 zV-1CmC%@X?RQQr@w-4RUV+e!Mu{^r88*czdKWpM@S0b0aL zL}S2({^*})SN?)B(*ulwoIcc{+83LPA=TaAAq~uEDPEnl@4`Andqlr7F7!Jd79osA zL4~%VhmLI-Qz_vQv?-$*mDHBwU359n$Gi{k&}&@8l7~MDgh+JAnP%S=g%RX20EXIW zH9I?=g~advxuv~MQ#n4OWRuLGv$EPtX& z<5dD>Cr04WohVbs_BN5tY=g_X7OD^tj?+WLpGFG8Yj_sa+z?5FC4+&GHn>}r2E3988H z^mPrYIGD1fB=c&%%?=#P?w2qv&3#k!xNA~td=%!)8g~>vP^$@jS@xRVVU>-snM>he zGt87sP`Rhuid%OA)G+7Ipw5?eVOP}s`q`JwP(dO_oM;Bu>@Bz+>47h|%52jY%$eNR z$;M4GZzbWp9ce9)lI4pJENS$4VC>Xx44ZCH=pMhcOkbZkP0tsVUGY`A+AeedZUI`F zkMJHQ<}KWGz~d0@I(QIs2nz;b@6am5CjN8GQXdd1i^jk2x-b_wbHuk<$K0%v*`Kgd zxab=N;=9np;kM(_8h8Ltk!Pd(zPCQY#4M}JWe}^P_%QuVB{Ggfmkd$q@(%4d+tixR z@#5>@6h!E#r-^KnrK~Vivph2+Mho92Gau2C+*A`@uLpCfxk)IraE#SNQuQ(SA=Sw8 zd_EieW+u<$;0HZdEaniLfRSw_9PBr5ivK1`bcLS-iMxh`W=@vQ&+2r%SQJ;SVK8LY4)}C5Dc?3EN>n>3?o}#mR&bZoE z#|U|6=;u$wn^PSty?8lYrQrMGgG+o<)niD=o`D>N;OHVP2wYXJ&&Nv8N5YM`nEnAw zc{1rn!@%{hvxwXN@UEC50G8prpYhi4lC0tPWwI&~O~#nH>MD3i+_o-Ql9%%+vyU{4 zrlXI}j^`o2xcGaY>_e5#%-R{d>Pg%_pMa?@>+ENQ273>g-@4t~FQR&iT}nKEpo)0v zb?2kr8i+OUovzHv2 zKxmjah?ARmGr!CIa>>7cQ%fRUayvg<^+^Cx{U3Amwt8jwa+j8!#r_M7ZMUtx_ zf!2pf%FOfos7k(W38N{ghVtWklYF|A%Y_BqGvIK|=j8e9h7+S2)|)I{;{La38J~2} zlvX8ENr&`yvvjCD3B>&G4e?h1xgpTyl6p0Uo=K*lS~ zM=^Pd$bsG4yEO0L~M?a*d&lh9IRygU$mRKB;Tg-U$`@(23MmNObT2hvOj*e7GlWu3reE}uLv{F!=G{j{`+)=z(?NW!SIQ4U?FVXSoSRa4EhK88BpH~Yc7 zN&iale{NA~byP(D+O?gSrU!AT~%GlzoatFAxH=J~*wg3xxqsej=Gt(FpAt z+;}7c+BEL@6xsJ$ZIZYB^>Xj%g_z!R_Qq7bHe$}wgI@VbUm@k$#eyPGprFCB&2o_B zWJnmFiMU|kEO+b!1I7V$1+9FkudG$h6De0Zo%reNVT|u~V0iaM- z?_S0uqaUVhcED=3-#3=hyTLnL~OvN)H-FKRx_e8-SOK z&Rjp3G6hKgcSdS@+OdT;o?c$~|JAwLAB<08Nuo@cMY(k-?|fvxz*}bgR$X z(c|6sRft2rxg2mZYQ#5mnJgBI*$W;0!uwM`OH9j`E$1-PFMha){+LOTZE$d0@*-;< z8NbI`mv8u9bBYL4lu4n&7pA(LDL1~~c&h0CLTh9HF7hq4j#fFDTBsonv1ak_FnE6Y zv(5Ws6*d|f%1opq0bCq)+`2Jh)`?;X;o^SuX8qoDh5e^xw zh40jCybJqISCtn0*YacU9O_@s=%^AiCxwMm}|LR50 zh4r8JW?sa9D*RP7Ly8!1qp#dD5jpgr>paeT1j&%9+>a6a$;EPFp3Up#m@Q?_N{i)2 z_<5elUG7b}?8N8;jZ!SYTy_m*u&PJq@6wjP#oqbVrwILi+P5wx8uHFWSw4PpI3zy; z_mV$b_UJ>%RVT<;ruUv-^EZ}}_hW+klPcU%mqge=(M>h_4lb2tE8}Shf0nDNpQoF1 z4AOi=X`mmS;Hb~eL zErNEL6nRb2{@p7`TbKmrMX@q$rQq=6zxTUqR~BvTcm%F->%vA!y? zRJA_o)8=qE&U9;|nE3(EkGBs4%^bxg#KCPtrAmP#`BVlmAitOQX65Cr(QTtW#u3I# zE+(;lFLsaLEOmy0A|Fi=yT;ON^mJ@!_X$%p(zu$dHYmFIEdQW~Ap(RvKgKN~AN)ro zPVro)6eGWFn@v)&nM0!cRp+C=F2PH}oan;u!2Q2;z237SqZ$jGaed0Njsf^@Dx}tg zSra8qAeSuZxDOD~1M<)(-z2^q$VbRzHD($}T3jbPxfHWy1ZJ@|yvRU#<5IP_~INpvaJo3LfL%!rV> z(`kk?N|V^(Q8x42ho9J&yk`KDzj*`j9eg#MRa(1G(MbjM9WQiWAD4^kLu`Ltuut~? zPD`BmCK$@6cKq$NnyM-3+`Nj6{(I{pxtuMF-1_YQ3%xT!%wP($-?6g&sNOewi&y;x zagLpiD6;sWt7Rnw(htz%5=7vnAKp+t{RZDojV4^+pX#LmO-ro6;^7jnon(BbTl1Ke zv<5s6-GkwSDxN_}D`5gnRzL_J%ob;cfyu|M9e%q~(3@{yFb-hZz|0^7)Oce@z4%+L z=T1xkXkj)k)wJfg4z{J%z%HD??MS5NJ%rQlR)Vo+-3XkWBnQ47*S{t0h+{&+IrVqs$1Wc!h4go#GGI=|I(Q$kV^I zFWPu$d1SSk4^8pE_hz~e)a_2f1|xW>+;FE&P#<|SbH(XlvlfcN70ko;qjm+q+;oE- zEh-xO=I4taqCW$XTL}KoKh8wF$vFgc|MM5|f_B-_5?{ai34{)u1#W3c^Q^}`RW{P2 zuiB3nTJ58D6q@iVx>T=pF@T-r*2}V>onMSAa{y(%+_>}!+;O@+l>J)1eU&e3k@ltMI*vFJ zYy*S!GWb7jxkYUOQS=&88mP~a;68*uCl~Onli!^Jo!PUYXYlzbX0BS$&C`Byz~ip2 z&s-#}*+rUkVZfxDR5?zpivjL-rUsI8#&(b;^iCr0UQnZHh|GKAd2O!t5qXn>5@u__alyQbN z<9azr)u`AA`h4Sf(zPGA$onqJM6Uqa)Yj8(V%t(_=?#if5VCah8MVHG!roUsYgsl( zJaJXvbg$uLV=eGJLjbkXWdTmE`4PuBr7|&RAuY;5CtJhoiy;$F06oY}uQqa?4VUWx zS`Co2+WX`LqiEbCHv;eM2l!d01zZ>FQqg<6gFq=NwRX3z#iQT6Z)?g>f>?;6rqTNv zVzM_~fOT{C9%wzvdfgbBThwk(yEP4!TyBT^BCIj&Ar65vXvm9V^dCedYeLMYaaQ2$ zV=K@+`kh{v8U%_9>(V9GJig1($s!!+uum-%aLYXf1G;QL&`%Gqzf(rKh_PrPPgPp*9 zm-^VD8%3bwl?kn)(6U&V()gP=1p!v?yTs~hS3P_#n|rd7GlalJ!qSCgcH6?8z8me% zQl`1XGdJ6F{za+MimAM5)DC8cS?71jKqGe`J#q_29+XRvs-jYSHk|R~MVnUO%UVAE z5N9sci3xcw!X;fGbCt@J+g0zg`?fsfpWAWDH+m6muba4Oj~Q7nPKfqY-Qt<$2;|hB z2gm^V=}xt-TUI2e-b3)wxjdY06)>y-{)|!kF;jjSnfk0zElBVC*7}c&r~wk^sh8)H zG)NfhFh@sXJiee)m)f@2v`}2Pguf2HAob!c^G%@0Gw-?_e}FQZG`JVidNU2|#{?px ze(va9ZJTyQaufl>9&B@prff(@hu0sx#gTV9jdXN$G<0;_YpRu$Vltw%_@>ALI0g(} zT&I1lBkmDjPSJMY`+FAw$ONJq9j0|UqLrvX09@pxZ3JGxS<3Y|0D|x5d)%g~f0$~? z?S^z(zH_FuXNE(=yEw!&yC=+?8)t!Y6hr-KBg;1PJmuCZ_L;N1YMT{)0pTZE?@Dp& zWEQ%O+__+hA$KJ?ADxgMb%R?&Vr8KF_?aZ=gf4~_=1Cl}8fCX4-(00d>HGCel;s)- zv`W#fL}zlT{btSxDnpKb4-`3klAB(?_L+In6eRdFQ7yhqQ?L)8!`%Yk_QpQ^9`;&j z(W4&f=ApZl2Hp6|Zn?39%-eOR1xho(ECR<|H0c@N+o2S{bMiK+=*DP|@FFxV?ygPA z!=d{?Z6T(qN+DYyx`WdekDp~j1PGAf*fS?oUp9}eB15gvRiM&j|Nnlae30*&t)_ef zuPdvuUDwLQ_uNR3*IfpwDcV%nXyUN?47BoJw+f!tVB9kaXpq56z7DYim0N^5t9^=+N z>%kC$iagXvtCrI(VyR~6?^+ynTqy;8fG?!mVh{r=5l4h!`|AvkrGl7Gy*3W=AfiiL zgSvSO$YW4wzDXMO>?>&)>9omCGF$S^8x=-azinN?>Cu~<ql2>2gbE!11QmYFXHpyy>qj6f`hr(TQdSyZY3i$P? zak^g5^2P8zF;!US8=Uy0)IS)23d;iYzZ`_}4CYN@GVUNW1q#09DheI1&o7ax9PU{>*oQ9PFXlK?iM&odX zetg zv3caq;Ylcxpa*8fW;yMUddzQm#y~WdD!ycV;8_=+!|pXRp7Xw|_NZ%cAMA9RjiXp} z+bb26Z2sZw0h%S~z36h+|GPj<2Tqlp=PWav8sPI@Vkxi$3ZgtKng;Fclpms>Bl%A4 zsJ-*F2Hlnm2W4eitB7n1(qdX&MYyfknRHTwz*75(}9LxUjYG! zEQG!BOEUqSf_hx}+7cA)Bmh~1W9YGH-Ue_e-24!l9JK_BwlfvVw>-Hdz<7Ffh}g#` zvB-Xjh?7{ zVmo&el}!bb2ncySfEN&#-WrPJcO7gZPgFe$0MA20hD)LPJaNFGdrFa8Q2f`cY@U3m zEFP*gk0|;x60q4QVHzRv`}p@k&*0z~O#qZ`2bOL^p4)K%{vmVSn|=E$;IW49iY7pL z2KXR4Thh-e&&MluE-+co7@=uVTO0h!@b2JlT5#Z2-zA=hl3;1j1xTWoN51qBS&*k*>YVR18@U2%v^|Y}JsA z{;g7dB8|Qko1AbIrFe?)y?3=V%q54?WCSWb+quis!OA@(*<75p#_wC}r2b@|Z9xsK zSc0`7_bLp)2i<=XQ66`EdBPm@H3qG0NMZ+~a-dD6{&6rAPdMg!uByF!5USImC!kWy zHL(wiNg;`v?94@1-C zT`LYh6!3gIk1K%ijAUUmp{#C0nOEbxq)oT1&-D&lBwE{ep1{|yjuOG^T^ldr$KfVF z#I?AFKS&HokhLsAcnUGLOeuB!RoYvK) z3xl9?W6DDj|3Ry-g;(dMwJJB*n&t&QrI)A+2&c?*_O{kSTk8NPp|p6;sha~u8Yu~8 zr9xbBKNz$bO?`!e1Tz6mS;?$ffpeb-n37N?bU5?0$#}qbaodq*{sCR7eJ1N)m!K45 zbl5exE;U#3;eSHTC}i)I(~oaQm~`Wt71iJO%@p9@-?Im}*Asirf*iQNP8!{tDWPW$ zgMl1pRVH4!NAKRYS&M4n)OcuKCcK@LK&X2m^Nx8&fc^O6;FpK!GWXJu&sFU^XJ8Np zg8VrjVO?KqnQpBy)QS1(9OX5-4)6x8{Cdp}fYxTiSQIZ`wgXq8?YrK)+kq}&{|O+& zA&#}yM0<~ts@wn12bjGkWts*WbBmM%)b7y!cFE;k2Jj-?ll+g?Jom!O-C)BQVncN3 zF_kx90$!G?^X{>fn_xX1h$TD#|IM%}k}A4H5kZai?xIMQnet6x)vcd46E}2doM* zsf>kE;c%*u z2t_()KYSysYtgRZluhUiC2z>&+(*ltZ#;zmli3UuM1eSBD*)dw<>>4Poi?c-A}a%9 z_98I)SxP?f#pGJT579XkF6}VFqAH>wM)SWZ7ipaZd|2)e`a95 zs{&jxZ!Z;Ue&!hLnGZvjqC5+F5K1nEzN@hzNao9z)_`7M71TE zmhu94^x5D|ySV1>YybUo;8|jHoc{M6B=~*Y%)Ks<1n@0l*;Yr2naL_F;oWSnxh7Az zd?HZ%^^7`2jAQQ8&2X@EIpFMP zrtR~b2?Br-e{P7ZV!v@CI#V!VBkB+y0>_>U&olHQj&&1n zi2)CVV7+$DsnhLvy9}q=HZ>Ps>tm;6p3*3Bh|8Wtl*hYe$7WGNFJz~ro5~_aw_*@F zKr#o_dOL7=;}p$P{QA&AbD0o99HNXnv`LIPIGD^i8PwXD4rE(514gE8*Ytty%;vC8tL#agSxR$H?G-DW+myiLI(iofEHfhXpe2vj}u zo#+U*Fm?SmY1@QLIrJBBU3>TOdt;&&mj|2KO5^)qL*V?tFDp3?zp$uW=1yDzb z6?^FNT{wotW2KvDtuoCz@HiP!XrFmcHrFxbej1^AQIlMPDWPl5TQ7~Z&?M#bhlK)( zAIuAG@WUV09&0WWTsxjW1jpLw1Y9!F2J185RsbjJI5qFllpZr$KZ8UJ(vK&Gl0~_sEtLp zA7+HOx*fg*vYEB0(qhRaxG`XkT`PbY4e$QDZ zn1@Hr!{L;KXt~hw*61&$MRe`A)n_&;>Uv)|ALsz_k~@RAsoq5pUb7WByg z`JLSsZzh1vOl0qT4r2{_!-TsUMgzG4Yk&JpdHrw9C=`HK$i*7GgpChDLBUvo>lE`1 z3@AnfhHI&>uP-zJ-%8tmzu%?1P8}#Stjtm~T3u|xV0}g}c+LYDxOolm; zM5VdT%3im|znjus9u-c@H~k3rJeNbNwd6}{^p2*VN2|Fc4Z1gk47M!$t)QA4zb7-R z47cXmDN*L+d&#$Yn&v8uF-ViwI`2#dOkr0fB9S!o{jMrA96!pBBBHPxw<(c{vBc#! z3ho+ejEA?|qlIzn1g9_EBJk^Zeh?XkHn^kw25NlxQuGfnb*JpIpBv5vwhk_oYBMct zHCWMr*}GOT>0I;JdoFe3?@aXZ0DECPPknC*jJEyd0_}M!b>po^_n>fqQ}m^{Wx15E zL%eq4eZQq_)X3`Ab&H%j)GeGoT9p*WVEh?UrAc1F6+s}AIEikYIP5mGCO|c{0bB!K zX`({ryHZ1>K>K9(8d_9w6T4SkshT3asj9B&PW$oHHpklRc-KK` zwV+L+&KGOS;)|UQf>P!Q9QxI}YBwXfsLi~@B{!hLTl|c8>aF3|^Jr}}^;OR{_wjt= z&D_af4+(%AC|fJHX4|qGSp3nK$8l|HI)GygOaK#!-x9+(BrNgXSt4<57!0z8yV#zq zR~E(ZGjF4{j_hZCf}f{&otZU9pk?y8%8LDDAAVP>~E%COp3GO>+NS3untJ~OH$vg)(9hMtK(jDenskTu?3$MlAmnq{N7mnln8+bm{X0>wU`}KJ4G9 z_w={F{v}~p}}j%lqGUq08W+Ik2KueCK^{!h+nM1mz!vo>I{aSPa+N~jE zFJ*I;y_ec%r)6vYM+?@=9SmH)LF|p-PDUcm;FKQ$z?zRBm}vm=ourJXKu!qMlCMAp z0mL~Ep~&8K>k6z^Mc$yal3otNL=g8-piKwKejJh&he(t}z3M2t3bIpz$Gd0du_^EQ2VO9CWaqx{fi>bV(fpTJBSBYE864954U93uO!=p1ul3qg&{)OC%K+rXV>duoH93+prJVuRbuazyo*- zhy0K0N()FVLOGoFTX!MGArdB%*ka7na7+d}Def_cDf+3uPpK}Ha%b)`PpOPUD7v$+ zqc7RDb|o!Nu0aN>=KdOklXultLmtGQSp_>WkKUEj@Oyt)RORxo2Y<$+XM%rLYIL20 zO>1ZZIRR!EGer<{eQ)adUR84NKe~hj^ezWqflj{FQ*GCCX=i1qq02BMFrJ}?!oh~- zVg?`o`KLeT4rt5082yTG~;Yz20pT7jNuOGh5i)G(s( z5dhWJnrjww^A3Qc(q?{sw1#&lRs?>l4&OKyWf7?0<)ARsY4Yvwi>PSveCaaP1b(T* zbnjVz9&;yFCh?c#v%G5+zrq~{J3W&LdK9HqBD=(J)}uX^VZ5Uu!$Vd4I-@AkbulV< z0_N--C!zTJU;dmBxPliO|6@umeSQ z6M;@TctF4sbLhWqaLE6^D+6hD@B(uumg>o+>|}Xyg$d*ZKBX4N%rD~`*jpCj$N0j+ zl;RHvQz+$R2$%^IE+z-7WXcJL&-p4%Ol=(hOmvQ2Ul8%9U0|w2NSPN6Ss6gT0_whG zyC&M8HF*hkiLe^JC(D2Y@O*t$>*#%FZF;7P4i-$&y$P3FSl;XkQoZ#GPBvrddd{|O zhSDmt@%pEp5zQ<)zXz_dW$NzG00f8azhED~4d5Sh;vaKj0f^3k<Zt*$DQUP55)SS$b=xlh**o6N;T=<1hS_+;0piE+>8)vQ#reCYy@d#T zD-doNu)Z$XcDy5u>)jcjgF-;wiLEgM@WCKjpGBbYKb{R(VL%7afuMmlSdSv34{td7 z>}&96rS*9Lw8ox0bmQOPN|A#uL^uMPf+JRy+f<7RwwY$6`GxPAO*k`!&_)G~rnLI> z`Zs=!AtF$eJ=W;QXyR=6s^aVL8o=}o{%EsLwS>&JW4DF8iSPF%@b^5d8?Vpnp1L&y z%I^p|>5xU5)6TWsaxO{*B_Axi#5kozz1OqJyy2nfvI(S_3owt$I1euIvZr9%D6*;Vhek! zG+*YdO&6!Z*`+Mg?*z)YOX*vC=OQuv#~=SCnL;$6_lm@c0|=fdvI}@?*URiqll9X0 zJEDHs&cpm7s*N9jVU-i@m_Yi479*~CprvnrQcOL5%H|O7+Q=3hm()AkiUP9=?cZBg zTkbF4r9@%`d&hVCs-_dQ&<$cE5PfF(y_qR^df;2GHs|*<2&DAoGM;R$RBBZoT!*UC zOO1S_Q$V{%B%wMLxLSwIyjeyT^_mWRGW7#5_npk6ub_PHk1#2^wU~&T3+uo zm~wM1Spo#0ERoog%|t`Mub&Mr!&ECE<>>m5|EO0G9cnUD^KnF#i-EDsT7gtMENm?}x!!Rk1S#HKplpZQVw7r}sS z2VqJbTD5@`7CN`VnU*;4GZBZ$y&K70~$_!RU^*%R$AzGpl{h!ZH;a4 zDMah-l|H6|{i6-|El~K}EZV$%!2S36zABeCt=UwwTPITJ_V;(;5I)qQJ!Fs%KxJN~ zC2*GDtT6)7vWQ#Wcxx^C?IkdtFPel#$C^_Z0QDgT^eW?w5TPrO^3Fht{H5$`kW`|`UTolJ^rk7lxX3ITpQRMt zefk{%vqVflv1b8+I2pg}tvT|N!M0x3?FI^;XB0*>RpL$Ea0s&GtXj)C*_D~Atg44D z65|Lg15k(i8KYXu7A_eBsB9<5GZ+HRQrW`{;Vk;xfP-ol{T2!!U8#2N1GLrP4_zeA zM20fmh68qEvCDI34c5zK91!q3`Al1Yu(6#k#-WLD?ER0!R9#28XMhxOJP!Go&d97J zWEahA#vi*6zb6hj82{^!|C&|XAq$PIdK0Wep7B&bm}`+L3H3tPLIc}^bZKp(j=ojX zTh8i*I<;1>L*zEbjldcuz0FAtE}6JtaLp_{r=@kn8XRd2-$LQ%{>hBo30KGtWY}p z3LrZ|m7`!YUD*_6uQ&MwCpbnzn*W9s>;_dGu6nryI#=oCgXZ_&T9d3>U8sElfM7}Z z$Ta2n3ASS2`64q|nh|&b{Qvi5FmQ$w!pzk9SbC3M^OCyD=_qi#G@ze5_bSP~-;Mtr z31{4eRiZhgR2v@v;%|TXbC$$)AG+d!;XHY!ow_}MTXRmfQfiuPR`g^Y&*f;*sbhqQl>H+aG3^uTElLE-{PKganAF8^?{a&uh!56 z1RC5ayaLCxGQGJ0WZkAr->1w<99-<(W|rV))Tn1(d;Ks%F&GEk_)bZk5k1et+B&!y z-74bYKm}METi7{E8x!-O2W79rc-p-V)Se4@ABr32SSK6l zo!ML_0pYwEms`f1B$2rr#GtzIy%ydCzQJrHf&;TbDy`_AC?v-i9pc`HZjuc&lSTONMx2dVz@cwAmdi-+Fmda3WT*uwT!@42cJi8$2avS1641f@kX2@3n* z6VOd=5fG;`?Ssnu7E#6!gP*pw!}-4*iRSlrDf~+0wU5|LDJ90&Q@r;nNjM@9VqLZ- zL6G(#{QY|FT(l2&V13`W$^-iz(4Rn=Ns8gx0v#+7skuM}sk$VUT}AJBo-graCSXF6 zE~(n%$^e{!_~MuirPS9GJ>;L)FAISsg9j~Ix zbZTUOzt*T#2GcA%NdSiHmgH$rQFu7 z+fm0oY7;Fi$+g#94cFwcs~gK5h}4bJGV$ehCIn7+Y~$T(b=KKhm+SL{m5b)w zx7KQ0*a@!GKCGZ1X9^7S$MCpwvn;#Ip{P&Gpyh+AQp;d>&qUw5L}Hc&+vS)D8mKziS>G2JfS+;8T`AqCT<;Urf@W%JUE7XhDblQ z>)KG;>~f{`fekY5peX*dz@w=$9HZ<}~aS0Ht>8dxK7Wv+(QDJ*y z2{Dg$8ANhh@aJjs+R4To^WM3boZ!-`?MKfxF75_ZTlT|4$W()Iw(2{ZR`6vAARBHc zHO%)dq_&)O)oX`>2y}z_1v&_1GW9l6jJ*rxE+?G;p05Y7cCjzduo5!pQkxeDJeM(x z({3A7+@Nc(LmgFaam{A^?B0zjuoQ7B{b-&;i(?Dsa-an&V}q;t%$f<=L#2rVwF7Hw z4S!IObkHx8*zL6T1PmnGv!N_4x_tKrg+ndrs019gC2;2V#c0@@d%3_tl{k>?`O{f* zr$}?&Ck{$m+0_EL#?9*0sL$_m!?*MEG^r%pMG#^)tG=dxl`7H$XqPTh`!5gx)Lz~T zSpe=~hd#<2G82QnVcH9s83#oUYbURTQX0i4P-G(T}{CcY%$x#r$L4RjP=UVjE!)5^RXNI#a z?~gFoAFvVZ&V#|2rPmZbZ|US;g)78vn;Cs3cAZMzP8PV_LPc@#hyp!UM03b8Kf z19~SozBj(VzpLA_^f`ub180=cJ4TQQ*V84j0PBkg=HM28|MS1)2C$T_7)WutJPV5u@tV zx9w9Ghj9h3S5sEx_wF_~{Sk-{jj*GxQ*U7=dzaaW%gv=yiGw~)d22rgTx(A=lH0$6 zf+2&0TN%ez4Q_zmJe35_d<&calzM*-fW4o<7k~frzvaxt_u81b(Z_@5>x`Z4*hDma9i@%BNn60Uo!PpnzHP?g}6yzuRNK zz44%6QA7{_o_#FW*}g&5(~or&K-KNl8U_384u-?j-3a`a47^-wswO^%qe=oCt^}pk z>qo$HhonF)WpoYdtSE)=T36_M3}iKJ{6PFX`1e6q>T9~J>sSv`28T0L$ z%+89At96rf3B|X^sn0{7yBW-!vjSPoi|o{7jEe*Cy{JoB4g^|%diC3D5`|Uy^LbtJ zI$It+XJgl;{T2Z6pBq%vdCNwNI=`bnq?r(2v(3W&+rR%QryjPA0b7Foe&0%K4aRj@ zUps9D@}QOgVkaKiUYj{+5yZQX&69cdo`BOrw+BLwMCLCjuF#9<7kL9XQe3}`@@>~< zp#>G6vs{TphOwl$ycD0o7GnfrEppRcO6V4IE66thfuN`$p2#8xRi>pi^?F*tSwUVV zs-rhL=!U2H>~b0^sj9Lc-~8^XlbfG(qUaV@@jFJYMZC#LrDQdm@|;>x}n%|Hr=YE5)&mbUI} z`TcoMWM%>D)e-2O9S-d&Gd9aq7u+{R=SS$`J0;yviB!13fBf-ZGR9!!Y@zvQCa3J$ zn830622kKo0BFevF>$8Hkpo+uwE;9m?XL*LUKp_LSCojto7CgFovN8VWkV36p(!b4 zR0MSZAi2dD7$cuI`ZV|>tEy^EwRY09ut5XA*ZtOiR_U2z0Fh+2Od-0T3uaiJQlQ1r z0JxR|bn8Gtb55AcH4cWyj zH=&2#bi`krzWd$r=kWJ8e|@vOHOgfP2=vhpU3;I6AA!0Qid(Ps?rQ&aMC}KcT%r9H zm42gIwU&juG;&M%h|f?L4g=vaC2=X(^}e5wv^i9U0@#z{sb6ELeMyk~dvE+hpyt9c zKIxZQe5tt$B zT7Zuh03R*DM+;!A=A#ApXaRgkKU#o~7GQt003R&?q8{X<1^8$IK3af}7T}`=06toP zj~3vg1^8$IK3af}7C_I96YxJ;fR7g7qXo#17T}`=xazQv7T}`=_-Fw>T7Zuh0QhJD z_D2iw(E>OP`)C2QULP&MM+@-L0@MrrXaPQ200*~^7T}`=@EgQO3xJOnAU|4w|Be>m ZzX0jO%+jRs zZs>}HrPI(%fNwt94w(i(Kw;I7WB?Fm?PnRHfLiuWIVCNGA)tKebi22WzquB`25nm`Pm0Ooni8HA17pl4Ot!#q0ux5Q!c+^)(t0w2XU;5na)M{h zoI9U2PZ$M(;Y5Zdm=FtsC&e5&LcKVlUMbIC!|@}!v9TT-4B6VK=W|6D_f@9s?-*f=!)QtdOgYAc``u#?Wo(0=mQBW4tt8d21?LcbjD?Y3uLP zTMS)^My3;*2KdgInP$=!kl@~YY6f6hO2ABrhcO@t4{q$pRII)lMnGvz}uNWBq06q7<^^tHyMz9=7x zNqtd16qDw#UNFT!bGXK&Ii!3jCe0z`Lor1<10om2@C-&|FjZzfJR=vy6f;NVq8OIJ z@C-&|FjZzff6uT%rC!3`I&6Y+@iE<_>daQ69#yYH(9`u>wrKC29krXT=LYp_1l170 ztU5H{>fLx0AJL89siVf`Xa?Om^&GBxyTdQF=xiSoGN-pE#rcoo&DuUnE^e*c=^GW) egLdB7fkB?2*zR@g=4|o%`_mU5@1nb41^@sNYMtr; literal 0 HcmV?d00001 diff --git a/src/main/resources/data/touhou_little_maid/structures/altar_south.nbt b/src/main/resources/data/touhou_little_maid/structures/altar_south.nbt new file mode 100644 index 0000000000000000000000000000000000000000..c0dd7c8bf730019280a337802a1ea65f86261b92 GIT binary patch literal 332 zcmV-S0ki%eiwFP!000000F9K-PQx$^#@(iAo1!6}f`{P31#v{2xFXY;En3%9N%;Hp zq^KFwkY-znqNKm?bDT=<01Q;Yev<-#dA4cR^J<`_G1_P;z#InZm%;VB(520_0X4ah zMI|huK!=Et7R5261N>~dx;P(>$#ro)9FzM`bB56Z zCifrChhuX8;e7rxE!6lHx6Z>VqEEy2&)D0wfo8&iVx+nKmK}7D-c97^_S}N*-H;^8 z(>gg+54b0|+}n|QkFW9O!?^)YB+YyB#xa6&xJw%Rv;m5*gM9TTAKWIXukM}S&>cp6 eA|I?96_^8)%DGKdT^xP>J^BIXEIKk^1^@ulh@M*j literal 0 HcmV?d00001 diff --git a/src/main/resources/data/touhou_little_maid/structures/altar_west.nbt b/src/main/resources/data/touhou_little_maid/structures/altar_west.nbt new file mode 100644 index 0000000000000000000000000000000000000000..f5f4b2924819b78c3ffa2fc763d671853dea2f2e GIT binary patch literal 339 zcmV-Z0j&NXiwFP!000000F9K*PQ)M(fTso8QnSXV@FBc-F&>R4uLis0F4=Br0Dqre zFw0t-?lPo=wEgCr!ZgzYNZ?L?BLjdqn=q@mN@#HK+G}(WhlJ{-H{H&);pSQb8;wgr zB^(9=;k@#Ah?p{hz(h|$@Z^{>eN)DhW9q|NOqgPU5g8_pGLvi0EO`ooXU;5nN`hz3 zEbW~U7*1dWF&86vC=4gmOAzWM2=&T&=FF*jtn>_U&MzoeF(X_ lH+F+}%Bv@|PMd*&JVIg1w_!IIlh?nGegM>U-kx9v003t1p%nlC literal 0 HcmV?d00001