From 4c4be538fd6fbadaf4017d35fb1e696da34371f6 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Wed, 4 Nov 2026 18:10:51 +0000 Subject: [PATCH] Add dark oak soffits and refine clay fascias --- CHANGELOG.txt | 10 ++ README.md | 1 + docs/BUILDING-GUIDE.md | 11 +- docs/DEVELOPMENT.md | 2 +- gradle.properties | 2 +- scripts/generate-resources.cjs | 8 + scripts/invoke-client-preview.ps1 | 4 +- scripts/invoke-runtime-probe.ps1 | 4 +- .../ArchitectureBlocks.java | 13 +- .../ArchitectureRecipes.java | 5 +- .../mc/ironagearchitecture/EraCatalog.java | 2 +- .../IronAgeArchitecture.java | 2 +- .../block/FasciaBlock.java | 66 ++++++-- .../ironagearchitecture/block/SoffitItem.java | 40 +++++ .../client/ClientProxy.java | 5 + .../client/RoofBakedModel.java | 26 ++- .../blockstates/clay_tile_fascia_lined.json | 148 ++++++++++++++++++ .../ironagearchitecture/lang/en_GB.lang | 3 + .../ironagearchitecture/lang/en_US.lang | 3 + .../models/block/dark_oak_soffit.json | 41 +++++ .../models/item/dark_oak_soffit.json | 3 + .../probe/ArchitectureProbe.java | 18 ++- .../probe/ClientProbe.java | 16 +- .../ironagearchitecture/probe/EraProbe.java | 4 +- .../probe/GutterProbe.java | 45 +++++- .../probe/RyeTilePreview.java | 6 +- 26 files changed, 441 insertions(+), 47 deletions(-) create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/block/SoffitItem.java create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/clay_tile_fascia_lined.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/block/dark_oak_soffit.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/dark_oak_soffit.json diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 3379ab0..9ad0cbe 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,13 @@ +0.1.12.110021 - Alpha + +I've tidied up the eaves on the Rye cottages. The grain now runs along the fascia +board, and you can see its timber edge from below. + +- Fit a dark oak soffit underneath a clay fascia to line the eaves. Stack two dark + oak slabs vertically to make four soffits. +- You can add or remove the soffit with the gutter in place. Lined and exposed + sections can share the same run of guttering. + 0.1.11.110021 - Alpha I've added iron guttering and downpipes to the Rye kit. diff --git a/README.md b/README.md index 4b147b6..479ee87 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,7 @@ All six vanilla wood types are available. Cob comes in blocks, slabs and stairs, thatch, slate and clay tile roofs have automatic corners and separate ridge caps. Clay tiles also come as full and half-depth hanging panels for gables and upper walls. Finish clay roofs with timber fascia boards at the eaves and matching trim along the gable and ridge. +Fit a dark oak soffit underneath to line the eaves. Fit iron guttering to the fascia and run a downpipe under the overhang. Connected outlets and pipes drip in the rain. Leaded windows bring some light into the houses. Choose diamond or rectangular leading, diff --git a/docs/BUILDING-GUIDE.md b/docs/BUILDING-GUIDE.md index 68039c8..ae17b46 100644 --- a/docs/BUILDING-GUIDE.md +++ b/docs/BUILDING-GUIDE.md @@ -3,10 +3,10 @@ Permanent building components for the Iron Age series, starting on Minecraft 1.10.2 Forge. Architecture runs independently of Iron Age Furniture. -Version 0.1.11.110021 contains raw and limewashed cob blocks, +Version 0.1.12.110021 contains raw and limewashed cob blocks, stairs and slabs; modular wattle-and-daub walls in all six vanilla woods; and smooth thatch, slate and clay tile roofs with automatic corners and separate ridge caps, plus jettied-storey walls, -timber floors, support brackets, hanging clay tiles and openable leaded windows. There are 525 visible +timber floors, support brackets, hanging clay tiles and openable leaded windows. There are 526 visible variants including the wood and finish choices. Architecture remains standalone. ## Wattle and daub @@ -75,6 +75,11 @@ Use full hanging tiles to fill the stepped gable beneath a clay roof. Thin hangi tiles share the half-block plane with straight thin timber panels and thin leaded windows. Place against one of these pieces to inherit its occupied half. Sneak to choose the half manually. +Two dark oak slabs stacked vertically make four soffits. Right-click the underside of +a clay fascia with a soffit to line that section of the eaves. Sneak and right-click +underneath with empty hands to take the lining back off. You can fit a soffit before +or after the gutter. The fascia still takes the same gutter or outlet either way. + ## Guttering Use a gutter or outlet on the outside face of a bare clay fascia. Level sections join @@ -240,4 +245,4 @@ Thatch and timber-bearing walls burn; plain daub, cob and slate do not. There is See [development instructions](DEVELOPMENT.md) for builds, Eclipse and disposable runtime checks. This is an early alpha. Automated checks and manual visual/gameplay review are recorded separately. -The Rye Downpipe Shoe Preview shows both pipe offsets and the shoe on a cobbled street. +The Rye Eaves Preview shows lined and exposed eaves on neighbouring cottages. diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index a9c0026..ffba78b 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -190,7 +190,7 @@ ends, both offsets and the rain path. The legacy-fascia mode opens a copy of the preview and verifies the original bare fascias. Use a fresh runtime root for each candidate. The optional client probe requires iaa.probe=true, iaa.clientProbe=true and iaa.keepRunning=true. -It creates its own Rye Downpipe Shoe Preview, checks mesh and material variants and both +It creates its own Rye Eaves Preview, checks mesh and material variants and both creative tabs, captures dry and rain views of the cottage row, then shuts down. Every visible item must also provide a translated crafting hint without tooltip errors. The client script's PreviewName argument selects a separate save without replacing an earlier preview. diff --git a/gradle.properties b/gradle.properties index eda3dfe..f7d5a0f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ org.gradle.parallel=false net.minecraftforge.gradle.merge-source-sets=false minecraft_version=1.10.2 forge_version=12.18.3.2511 -mod_version=0.1.11.110021 +mod_version=0.1.12.110021 mod_group=zone.moddev.mc mod_id=ironagearchitecture mod_name=Iron Age Architecture diff --git a/scripts/generate-resources.cjs b/scripts/generate-resources.cjs index 52cbb46..14bd967 100644 --- a/scripts/generate-resources.cjs +++ b/scripts/generate-resources.cjs @@ -146,8 +146,14 @@ for (const [part,label] of [['fascia','Fascia'],['verge','Verge'],['ridge_end',' variants[`facing=${facing},right=${right}`] = {model:`${ns}:${mesh}`}; } state(name,variants); item(name,mesh); + if (part === 'fascia') state(name + '_lined',variants); lang[`tile.${ns}.${name}.name`] = `Clay Tile ${label}`; } +model('dark_oak_soffit',{parent:'minecraft:block/block',textures:{wood:'minecraft:blocks/planks_big_oak',particle:'minecraft:blocks/planks_big_oak'}, + elements:[{from:[0,7,1],to:[16,8.25,15],faces:Object.fromEntries( + ['down','up','north','south','west','east'].map(face=>[face,{texture:'#wood'}]))}]}); +item('dark_oak_soffit','dark_oak_soffit'); +lang[`item.${ns}.dark_oak_soffit.name`] = 'Dark Oak Soffit'; model('drain_mesh',{parent:'minecraft:block/block',textures:{particle:'minecraft:blocks/iron_block'}}); for (const [name,label] of [['iron_gutter','Iron Gutter'],['iron_gutter_outlet','Iron Gutter Outlet']]) { item(name,'drain_mesh'); @@ -191,6 +197,8 @@ for(const [w,wood] of woods.entries()) for(const finish of [0,1]) for(const thin state('open_window_leaf',Object.fromEntries(Object.keys(facings).map(facing=>[`facing=${facing}`,{model:`${ns}:window_mesh`}]))); for(let meta=0;meta<2;meta++) {item(`leaded_pane_${meta}`,'window_mesh');lang[`item.${ns}.leaded_pane.${meta}.name`]=`${meta?'Rectangular':'Diamond'} Leaded Pane`;} for (const [key,label] of Object.entries({ + 'soffit.fit':'Fit under a clay fascia. Sneak and use an empty hand underneath to recover it.', + 'soffit.craft':'Stack two dark oak slabs in the crafting grid to make four soffits.', 'gutter.fit':'Fit to the outside face of a bare clay fascia. Sneak and use an empty hand to recover it.', 'gutter.outlet':'Place a downpipe beneath the outlet to carry rain down.', 'gutter.craft':'Three iron ingots in a row make six gutters. Add an ingot to make an outlet.', diff --git a/scripts/invoke-client-preview.ps1 b/scripts/invoke-client-preview.ps1 index cb57ada..b39060f 100644 --- a/scripts/invoke-client-preview.ps1 +++ b/scripts/invoke-client-preview.ps1 @@ -1,6 +1,6 @@ param( - [string]$Runtime='D:\IronAgeArchitecture\manual-testing\rye-downpipe-shoe-0.1.11\native-client-final', - [string]$PreviewName='Rye Downpipe Shoe Preview', + [string]$Runtime='D:\IronAgeArchitecture\manual-testing\rye-eaves-0.1.12\native-client-verified', + [string]$PreviewName='Rye Eaves Preview', [string]$ServerTemplate='D:\IronAgeArchitecture\manual-testing\jetties-0.1.2-final\core', [string]$GradleHome='D:\GradleCaches\minecraft-mods-shared', [string]$AssetsDirectory='', diff --git a/scripts/invoke-runtime-probe.ps1 b/scripts/invoke-runtime-probe.ps1 index 7872792..72ccb1c 100644 --- a/scripts/invoke-runtime-probe.ps1 +++ b/scripts/invoke-runtime-probe.ps1 @@ -1,6 +1,6 @@ param( [ValidateSet('core','reload','furniture','disabled','legacy','legacy-daub','legacy-jetties','legacy-fascia','iron-age','late-medieval','mixed-eras','no-eras','invalid-era','windows-disabled','clay-disabled','clay-only')][string]$Mode='core', - [string]$RuntimeRoot='D:\IronAgeArchitecture\manual-testing\rye-downpipe-shoe-0.1.11', + [string]$RuntimeRoot='D:\IronAgeArchitecture\manual-testing\rye-eaves-0.1.12', [string]$ServerTemplate='D:\IronAgeArchitecture\manual-testing\jetties-0.1.2-final\core', [string]$FurnitureJar='D:\IronAgeArchitecture\manual-testing\jetties-0.1.2-final\furniture\mods\iron-age-furniture-0.3.0.110021.jar', [string]$Java8='C:\Users\johnb\.gradle\jdks\temurin-8-amd64-windows.2\bin\java.exe' @@ -101,7 +101,7 @@ if($Mode -eq 'legacy-jetties') {$args+='-Diaa.legacyJetties=true'} if($Mode -eq 'legacy-fascia') {$args+='-Diaa.legacyFascia=true'} if($Mode -eq 'core') {$args+=@('-Diaa.eraMatrix=true','-Diaa.recipeMatrix=true')} if($Mode -eq 'disabled') {$args+='-Diaa.expectedVariants=0'} -if($Mode -eq 'clay-only') {$args+=@('-Diaa.expectedVariants=15','-Diaa.clayOnly=true','-Diaa.recipeMatrix=true')} +if($Mode -eq 'clay-only') {$args+=@('-Diaa.expectedVariants=16','-Diaa.clayOnly=true','-Diaa.recipeMatrix=true')} $args+=@('-jar',$forge,'nogui') Push-Location $folder try { diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureBlocks.java b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureBlocks.java index 58e023e..e6722b5 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureBlocks.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureBlocks.java @@ -39,6 +39,7 @@ public final class ArchitectureBlocks { public static WindowLeafBlock WINDOW_LEAF; public static LeadedPaneItem LEADED_PANE; public static GutterItem GUTTER, GUTTER_OUTLET; + public static SoffitItem SOFFIT; public static DownpipeBlock DOWNPIPE, DOWNPIPE_OFFSET, DOWNPIPE_SHOE; public static int variants(Block block) { @@ -53,6 +54,7 @@ public static List visibleStacks() { if (GUTTER != null) { result.add(new ItemStack(GUTTER)); result.add(new ItemStack(GUTTER_OUTLET)); + result.add(new ItemStack(SOFFIT)); } return result; } @@ -62,7 +64,7 @@ public static List meshes() { public static FrameBlock DAUB; public static final List ROOFS = new ArrayList<>(); public static TileHangingBlock CLAY_HANGING, CLAY_HANGING_THIN; - public static FasciaBlock CLAY_FASCIA; + public static FasciaBlock CLAY_FASCIA, CLAY_FASCIA_LINED; public static TrimmedRoofBlock CLAY_VERGE, CLAY_RIDGE_END; private ArchitectureBlocks() { } public static T block(T block, String name) { @@ -109,7 +111,7 @@ public static void register() { CLAY_HANGING_THIN = block(new TileHangingBlock(true), "clay_tile_hanging_thin"); item(CLAY_HANGING, new TileHangingItem(CLAY_HANGING)); item(CLAY_HANGING_THIN, new TileHangingItem(CLAY_HANGING_THIN)); - CLAY_FASCIA = block(new FasciaBlock(), "clay_tile_fascia"); + CLAY_FASCIA = block(new FasciaBlock(false), "clay_tile_fascia"); item(CLAY_FASCIA, new EraItemBlock(CLAY_FASCIA)); ROOFS.add(CLAY_FASCIA); CLAY_VERGE = roofTrim("clay_tile_verge", TrimmedRoofBlock.Kind.VERGE); @@ -122,6 +124,13 @@ public static void register() { item(DOWNPIPE_OFFSET, new DownpipeItem(DOWNPIPE_OFFSET)); DOWNPIPE_SHOE = block(new DownpipeBlock(DownpipeBlock.Kind.SHOE), "iron_downpipe_shoe"); item(DOWNPIPE_SHOE, new DownpipeItem(DOWNPIPE_SHOE)); + // The lined fascia keeps the same orientation and gutter metadata as the bare one. + CLAY_FASCIA_LINED = block(new FasciaBlock(true), "clay_tile_fascia_lined"); + ROOFS.add(CLAY_FASCIA_LINED); + SOFFIT = new SoffitItem(); + SOFFIT.setRegistryName(new ResourceLocation(IronAgeArchitecture.MODID, "dark_oak_soffit")); + SOFFIT.setUnlocalizedName(IronAgeArchitecture.MODID + ".dark_oak_soffit"); + GameRegistry.register(SOFFIT); } EraCatalog.assignPrimaryTabs(); } diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureRecipes.java b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureRecipes.java index 3313c83..2275556 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureRecipes.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureRecipes.java @@ -26,7 +26,10 @@ public static void register() { } } if(ArchitectureConfig.windows) registerWindows(); - if (ArchitectureConfig.clayTiles) registerGuttering(); + if (ArchitectureConfig.clayTiles) { + registerGuttering(); + shaped(new ItemStack(ArchitectureBlocks.SOFFIT, 4), "D", "D", 'D', new ItemStack(Blocks.WOODEN_SLAB, 1, 5)); + } for(RoofBlock roof:ArchitectureBlocks.ROOFS) if(!roof.isRidge() && !(roof instanceof TrimmedRoofBlock)) { Object material; switch (roof.materialKey) { diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/EraCatalog.java b/src/main/java/zone/moddev/mc/ironagearchitecture/EraCatalog.java index 7a1bd89..163d41b 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/EraCatalog.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/EraCatalog.java @@ -10,7 +10,7 @@ public final class EraCatalog { private EraCatalog() { } public static EnumSet eras(Item item) { if(item==null||item.getRegistryName()==null||!IronAgeArchitecture.MODID.equals(item.getRegistryName().getResourceDomain())) return EnumSet.noneOf(Era.class); - if(item instanceof JettyBracketItem||item instanceof LeadedPaneItem||item instanceof GutterItem) return EnumSet.of(Era.LATE_MEDIEVAL_TUDOR); + if(item instanceof JettyBracketItem||item instanceof LeadedPaneItem||item instanceof GutterItem||item instanceof SoffitItem) return EnumSet.of(Era.LATE_MEDIEVAL_TUDOR); if(!(item instanceof ItemBlock)) return EnumSet.noneOf(Era.class); Block block=((ItemBlock)item).getBlock(); boolean shared=ArchitectureBlocks.COB.contains(block)||ArchitectureBlocks.SLABS.contains(block)||ArchitectureBlocks.STAIRS.contains(block) diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/IronAgeArchitecture.java b/src/main/java/zone/moddev/mc/ironagearchitecture/IronAgeArchitecture.java index c86966d..69cb874 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/IronAgeArchitecture.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/IronAgeArchitecture.java @@ -7,7 +7,7 @@ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; /** Permanent building components in the Iron Age series. */ -@Mod(modid = IronAgeArchitecture.MODID, name = "Iron Age Architecture", version = "0.1.11.110021", +@Mod(modid = IronAgeArchitecture.MODID, name = "Iron Age Architecture", version = "0.1.12.110021", acceptedMinecraftVersions = "[1.10.2]", dependencies = "required-after:Forge@[12.18.3.2511,)") public final class IronAgeArchitecture { public static final String MODID = "ironagearchitecture"; diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/FasciaBlock.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/FasciaBlock.java index 9a11351..64cd3d0 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/block/FasciaBlock.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/FasciaBlock.java @@ -9,12 +9,14 @@ import net.minecraft.block.state.IBlockState; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.RayTraceResult; import net.minecraft.world.IBlockAccess; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; @@ -25,8 +27,11 @@ public final class FasciaBlock extends TrimmedRoofBlock { public static final PropertyInteger FITTING = PropertyInteger.create("fitting", 0, 2); public static final PropertyInteger ENDS = PropertyInteger.create("ends", 0, 3); - public FasciaBlock() { + public final boolean lined; + + public FasciaBlock(boolean lined) { super(Kind.FASCIA); + this.lined = lined; setDefaultState(getDefaultState().withProperty(FITTING, 0).withProperty(ENDS, 3)); } @@ -56,7 +61,7 @@ public FasciaBlock() { private boolean connects(IBlockAccess world, BlockPos pos, EnumFacing facing) { if (world instanceof World && !((World)world).isBlockLoaded(pos)) return false; IBlockState other = world.getBlockState(pos); - return other.getBlock() == this && other.getValue(FACING) == facing && other.getValue(FITTING) != 0; + return other.getBlock() instanceof FasciaBlock && other.getValue(FACING) == facing && other.getValue(FITTING) != 0; } @Override public List collision(IBlockState state) { @@ -68,24 +73,46 @@ private boolean connects(IBlockAccess world, BlockPos pos, EnumFacing facing) { @Override public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, @Nullable ItemStack held, EnumFacing side, float x, float y, float z) { - int fitting = state.getValue(FITTING); - EnumFacing facing = state.getValue(FACING); - if (!player.isSneaking() || hand != EnumHand.MAIN_HAND || held != null || player.getHeldItemOffhand() != null || fitting == 0) + if (!player.isSneaking() || hand != EnumHand.MAIN_HAND || held != null || player.getHeldItemOffhand() != null) return false; - EnumFacing inverse = facing.getAxis() == EnumFacing.Axis.X ? facing.getOpposite() : facing; - if (RoofGeometry.rotate(x, y, z, inverse)[2] < RoofTrimGeometry.EAVE + 1e-4 || y > 3.0 / 16) - return false; - if (!world.isBlockModifiable(player, pos) || !player.canPlayerEdit(pos, side, fittingStack(fitting))) return false; - if (!world.isRemote && world.setBlockState(pos, state.withProperty(FITTING, 0), 3)) { - if (!player.capabilities.isCreativeMode) { - ItemStack recovered = fittingStack(fitting); - if (!player.inventory.addItemStackToInventory(recovered)) spawnAsEntity(world, pos, recovered); - } - playFit(world, player, pos); + + int fitting = state.getValue(FITTING); + boolean removeSoffit = lined && side == EnumFacing.DOWN && onUnderside(state, x, y, z); + boolean removeGutter = fitting != 0 && localDepth(state, x, y, z) >= RoofTrimGeometry.EAVE + 1e-4 && y <= 3.0 / 16; + if (!removeSoffit && !removeGutter) return false; + + ItemStack recovered = removeSoffit ? new ItemStack(ArchitectureBlocks.SOFFIT) : fittingStack(fitting); + if (!world.isBlockModifiable(player, pos) || !player.canPlayerEdit(pos, side, recovered)) return false; + if (!world.isRemote) { + IBlockState replacement = removeSoffit ? withLining(state, false) : state.withProperty(FITTING, 0); + if (!world.setBlockState(pos, replacement, 3)) return false; + if (!player.capabilities.isCreativeMode && !player.inventory.addItemStackToInventory(recovered)) + spawnAsEntity(world, pos, recovered); + if (removeSoffit) playLining(world, player, pos); + else playFit(world, player, pos); } return true; } + public static IBlockState withLining(IBlockState state, boolean lined) { + FasciaBlock target = lined ? ArchitectureBlocks.CLAY_FASCIA_LINED : ArchitectureBlocks.CLAY_FASCIA; + return target.getDefaultState().withProperty(FACING, state.getValue(FACING)).withProperty(FITTING, state.getValue(FITTING)); + } + + static boolean onUnderside(IBlockState state, float x, float y, float z) { + return y <= 1e-4 && localDepth(state, x, y, z) <= RoofTrimGeometry.EAVE + 1e-4; + } + + private static double localDepth(IBlockState state, float x, float y, float z) { + EnumFacing facing = state.getValue(FACING); + EnumFacing inverse = facing.getAxis() == EnumFacing.Axis.X ? facing.getOpposite() : facing; + return RoofGeometry.rotate(x, y, z, inverse)[2]; + } + + static void playLining(World world, EntityPlayer player, BlockPos pos) { + world.playSound(player, pos, SoundType.WOOD.getPlaceSound(), SoundCategory.BLOCKS, .6F, .9F); + } + public static void playFit(World world, EntityPlayer player, BlockPos pos) { world.playSound(player, pos, SoundType.METAL.getPlaceSound(), SoundCategory.BLOCKS, .6F, .9F); } @@ -94,10 +121,19 @@ private static ItemStack fittingStack(int fitting) { return new ItemStack(fitting == 2 ? ArchitectureBlocks.GUTTER_OUTLET : ArchitectureBlocks.GUTTER); } + @Override public Item getItemDropped(IBlockState state, Random random, int fortune) { + return Item.getItemFromBlock(ArchitectureBlocks.CLAY_FASCIA); + } + + @Override public ItemStack getPickBlock(IBlockState state, RayTraceResult hit, World world, BlockPos pos, EntityPlayer player) { + return new ItemStack(ArchitectureBlocks.CLAY_FASCIA); + } + @Override public List getDrops(IBlockAccess world, BlockPos pos, IBlockState state, int fortune) { List drops = super.getDrops(world, pos, state, fortune); int fitting = state.getValue(FITTING); if (fitting != 0) drops.add(fittingStack(fitting)); + if (lined) drops.add(new ItemStack(ArchitectureBlocks.SOFFIT)); return drops; } diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SoffitItem.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SoffitItem.java new file mode 100644 index 0000000..fba4f3a --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SoffitItem.java @@ -0,0 +1,40 @@ +package zone.moddev.mc.ironagearchitecture.block; + +import java.util.List; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.EnumActionResult; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import zone.moddev.mc.ironagearchitecture.Era; +import zone.moddev.mc.ironagearchitecture.EraTabs; + +public final class SoffitItem extends Item { + public SoffitItem() { + setCreativeTab(EraTabs.tab(Era.LATE_MEDIEVAL_TUDOR)); + } + + @Override public EnumActionResult onItemUse(ItemStack stack, EntityPlayer player, World world, BlockPos pos, + EnumHand hand, EnumFacing side, float x, float y, float z) { + IBlockState state = world.getBlockState(pos); + if (side != EnumFacing.DOWN || !(state.getBlock() instanceof FasciaBlock) + || !FasciaBlock.onUnderside(state, x, y, z)) return EnumActionResult.PASS; + if (((FasciaBlock)state.getBlock()).lined || stack.stackSize <= 0 || !world.isBlockModifiable(player, pos) + || !player.canPlayerEdit(pos, side, stack)) return EnumActionResult.FAIL; + if (!world.isRemote) { + if (!world.setBlockState(pos, FasciaBlock.withLining(state, true), 3)) return EnumActionResult.FAIL; + FasciaBlock.playLining(world, player, pos); + if (!player.capabilities.isCreativeMode) stack.stackSize--; + } + return EnumActionResult.SUCCESS; + } + + @Override public void addInformation(ItemStack stack, EntityPlayer player, List tooltip, boolean advanced) { + EraItemBlock.hint(tooltip, "soffit.fit"); + EraItemBlock.hint(tooltip, "soffit.craft"); + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/client/ClientProxy.java b/src/main/java/zone/moddev/mc/ironagearchitecture/client/ClientProxy.java index 8624ae0..fa8b99c 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/client/ClientProxy.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/client/ClientProxy.java @@ -38,6 +38,9 @@ public final class ClientProxy extends CommonProxy { ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(block),meta,new ModelResourceLocation(name,"inventory")); } } + if (ArchitectureBlocks.SOFFIT != null) + ModelLoader.setCustomModelResourceLocation(ArchitectureBlocks.SOFFIT, 0, + new ModelResourceLocation(ArchitectureBlocks.SOFFIT.getRegistryName(), "inventory")); if (ArchitectureBlocks.GUTTER != null) for (GutterItem item : gutterItems()) ModelLoader.setCustomModelResourceLocation(item, 0, new ModelResourceLocation(item.getRegistryName(), "inventory")); for(JettyBracketItem item:ArchitectureBlocks.BRACKETS) ModelLoader.setCustomModelResourceLocation(item,0,new ModelResourceLocation(item.getRegistryName(),"inventory")); @@ -90,6 +93,7 @@ public final class ClientProxy extends CommonProxy { event.getModelRegistry().putObject(key,new RoofBakedModel(base,block,state, Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(texture))); } + if (!ArchitectureBlocks.VISIBLE.contains(block)) continue; ModelResourceLocation inventory = new ModelResourceLocation(block.getRegistryName(),"inventory"); event.getModelRegistry().putObject(inventory,new RoofBakedModel(event.getModelRegistry().getObject(inventory),block, block.getDefaultState(),Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(texture))); @@ -101,6 +105,7 @@ private static GutterItem[] gutterItems() { @SubscribeEvent public void stitch(TextureStitchEvent.Pre event) { event.getMap().registerSprite(new ResourceLocation("minecraft:blocks/glass_silver"));event.getMap().registerSprite(new ResourceLocation("minecraft:blocks/iron_block")); + event.getMap().registerSprite(new ResourceLocation("minecraft:blocks/planks_big_oak")); for(String wood:ArchitectureBlocks.WOODS) { event.getMap().registerSprite(new ResourceLocation("ironagearchitecture:blocks/beam_"+wood)); event.getMap().registerSprite(new ResourceLocation("minecraft:blocks/log_"+(wood.equals("dark_oak")?"big_oak":wood)+"_top")); diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/client/RoofBakedModel.java b/src/main/java/zone/moddev/mc/ironagearchitecture/client/RoofBakedModel.java index 89544a4..cca98fc 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/client/RoofBakedModel.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/client/RoofBakedModel.java @@ -36,7 +36,8 @@ public RoofBakedModel(IBakedModel base, RoofBlock block, IBlockState state, Text TrimmedRoofBlock.Kind kind = ((TrimmedRoofBlock) block).kind; TextureAtlasSprite wood = net.minecraft.client.Minecraft.getMinecraft().getTextureMapBlocks() .getAtlasSprite("ironagearchitecture:blocks/beam_dark_oak"); - addTrim(kind, kind == TrimmedRoofBlock.Kind.VERGE && state.getValue(TrimmedRoofBlock.RIGHT), facing, wood); + boolean lined = block instanceof FasciaBlock && ((FasciaBlock)block).lined; + addTrim(kind, kind == TrimmedRoofBlock.Kind.VERGE && state.getValue(TrimmedRoofBlock.RIGHT), facing, wood, lined); if (block instanceof FasciaBlock && state.getValue(FasciaBlock.FITTING) != 0) DrainMesh.gutter(state.getValue(FasciaBlock.FITTING) == 2, state.getValue(FasciaBlock.ENDS), ironFaces(facing)); return; @@ -97,7 +98,7 @@ private DrainMesh.Face ironFaces(EnumFacing facing) { }; } - private void addTrim(TrimmedRoofBlock.Kind kind, boolean right, EnumFacing facing, TextureAtlasSprite wood) { + private void addTrim(TrimmedRoofBlock.Kind kind, boolean right, EnumFacing facing, TextureAtlasSprite wood, boolean lined) { RoofTrimGeometry.Section[] sections = RoofTrimGeometry.sections(kind, right); for (RoofTrimGeometry.Section section : sections) { double[][] corners = {{section.x0,section.z0},{section.x1,section.z0}, @@ -110,7 +111,8 @@ private void addTrim(TrimmedRoofBlock.Kind kind, boolean right, EnumFacing facin bottom[i] = new double[]{x,RoofTrimGeometry.underside(section,height),z}; } add(new double[][]{top[0],top[3],top[2],top[1]}, facing, true); - add(bottom, facing, true, section.thin ? wood : sprite, null); + if (kind == TrimmedRoofBlock.Kind.FASCIA) addEaveUnderside(facing, wood, lined); + else add(bottom, facing, true, section.thin ? wood : sprite, null); for (int side = 0; side < 4; side++) { int next = (side + 1) % 4; RoofTrimGeometry.Section neighbour = adjoining(sections, section, side); @@ -138,6 +140,18 @@ private void addTrim(TrimmedRoofBlock.Kind kind, boolean right, EnumFacing facin } } + private void addEaveUnderside(EnumFacing facing, TextureAtlasSprite wood, boolean lined) { + double edge = RoofTrimGeometry.EAVE; + double backOfBoard = edge - 3.0 / 16; + TextureAtlasSprite underside = lined ? net.minecraft.client.Minecraft.getMinecraft().getTextureMapBlocks() + .getAtlasSprite("minecraft:blocks/planks_big_oak") : sprite; + // Replace the original bottom with two strips on the same plane. The lip and lining + // stay within the roof cell, leaving the outlet and the pipe below it clear. + add(new double[][]{{0,0,0},{1,0,0},{1,0,backOfBoard},{0,0,backOfBoard}}, facing, true, underside, null); + add(new double[][]{{0,0,backOfBoard},{1,0,backOfBoard},{1,0,edge},{0,0,edge}}, + facing, true, wood, TrimmedRoofBlock.Kind.FASCIA); + } + private static RoofTrimGeometry.Section adjoining(RoofTrimGeometry.Section[] sections, RoofTrimGeometry.Section section, int side) { for (RoofTrimGeometry.Section other : sections) { if (other == section) continue; @@ -182,6 +196,12 @@ private void add(double[][] points, EnumFacing facing, boolean top, TextureAtlas double v = top ? source[2] : 1-source[1]; if (board != null) v = RoofTrimGeometry.height(board, source[0], source[2]) - source[1]; float textureV = (float)(Math.max(0,Math.min(1,v)) * 16); + if (board == TrimmedRoofBlock.Kind.FASCIA) { + // The beam sprite's grain runs vertically. Turn it along the length of the fascia. + float acrossGrain = textureV; + textureV = textureU; + textureU = acrossGrain; + } for (int element = 0; element < format.getElementCount(); element++) { switch (format.getElement(element).getUsage()) { case POSITION: builder.put(element,(float)p[0],(float)p[1],(float)p[2],1); break; diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/clay_tile_fascia_lined.json b/src/main/resources/assets/ironagearchitecture/blockstates/clay_tile_fascia_lined.json new file mode 100644 index 0000000..70092dc --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/clay_tile_fascia_lined.json @@ -0,0 +1,148 @@ +{ + "variants": { + "ends=0,facing=north,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=north,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=north,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=north,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=north,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=north,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=north,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=north,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=north,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=north,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=north,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=north,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=east,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=east,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=east,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=east,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=east,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=east,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=east,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=east,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=east,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=east,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=east,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=east,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=south,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=south,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=south,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=south,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=south,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=south,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=south,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=south,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=south,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=south,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=south,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=south,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=west,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=west,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=0,facing=west,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=west,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=west,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=1,facing=west,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=west,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=west,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=2,facing=west,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=west,fitting=0": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=west,fitting=1": { + "model": "ironagearchitecture:clay_tile_mesh" + }, + "ends=3,facing=west,fitting=2": { + "model": "ironagearchitecture:clay_tile_mesh" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/lang/en_GB.lang b/src/main/resources/assets/ironagearchitecture/lang/en_GB.lang index ffbc1ce..5ec06c6 100644 --- a/src/main/resources/assets/ironagearchitecture/lang/en_GB.lang +++ b/src/main/resources/assets/ironagearchitecture/lang/en_GB.lang @@ -490,6 +490,7 @@ tile.ironagearchitecture.clay_tile_ridge.name=Clay Tile Ridge Cap tile.ironagearchitecture.clay_tile_fascia.name=Clay Tile Fascia tile.ironagearchitecture.clay_tile_verge.name=Clay Tile Verge tile.ironagearchitecture.clay_tile_ridge_end.name=Clay Tile Ridge End +item.ironagearchitecture.dark_oak_soffit.name=Dark Oak Soffit item.ironagearchitecture.iron_gutter.name=Iron Gutter item.ironagearchitecture.iron_gutter_outlet.name=Iron Gutter Outlet tile.ironagearchitecture.iron_downpipe.name=Iron Downpipe @@ -550,6 +551,8 @@ tile.ironagearchitecture.leaded_window_thin_limewashed_cob_diamond_dark_oak.name tile.ironagearchitecture.leaded_window_thin_limewashed_cob_rectangle_dark_oak.name=Limewashed Dark Oak Rectangular Leaded Window (Thin) item.ironagearchitecture.leaded_pane.0.name=Diamond Leaded Pane item.ironagearchitecture.leaded_pane.1.name=Rectangular Leaded Pane +hint.ironagearchitecture.soffit.fit=Fit under a clay fascia. Sneak and use an empty hand underneath to recover it. +hint.ironagearchitecture.soffit.craft=Stack two dark oak slabs in the crafting grid to make four soffits. hint.ironagearchitecture.gutter.fit=Fit to the outside face of a bare clay fascia. Sneak and use an empty hand to recover it. hint.ironagearchitecture.gutter.outlet=Place a downpipe beneath the outlet to carry rain down. hint.ironagearchitecture.gutter.craft=Three iron ingots in a row make six gutters. Add an ingot to make an outlet. diff --git a/src/main/resources/assets/ironagearchitecture/lang/en_US.lang b/src/main/resources/assets/ironagearchitecture/lang/en_US.lang index ffbc1ce..5ec06c6 100644 --- a/src/main/resources/assets/ironagearchitecture/lang/en_US.lang +++ b/src/main/resources/assets/ironagearchitecture/lang/en_US.lang @@ -490,6 +490,7 @@ tile.ironagearchitecture.clay_tile_ridge.name=Clay Tile Ridge Cap tile.ironagearchitecture.clay_tile_fascia.name=Clay Tile Fascia tile.ironagearchitecture.clay_tile_verge.name=Clay Tile Verge tile.ironagearchitecture.clay_tile_ridge_end.name=Clay Tile Ridge End +item.ironagearchitecture.dark_oak_soffit.name=Dark Oak Soffit item.ironagearchitecture.iron_gutter.name=Iron Gutter item.ironagearchitecture.iron_gutter_outlet.name=Iron Gutter Outlet tile.ironagearchitecture.iron_downpipe.name=Iron Downpipe @@ -550,6 +551,8 @@ tile.ironagearchitecture.leaded_window_thin_limewashed_cob_diamond_dark_oak.name tile.ironagearchitecture.leaded_window_thin_limewashed_cob_rectangle_dark_oak.name=Limewashed Dark Oak Rectangular Leaded Window (Thin) item.ironagearchitecture.leaded_pane.0.name=Diamond Leaded Pane item.ironagearchitecture.leaded_pane.1.name=Rectangular Leaded Pane +hint.ironagearchitecture.soffit.fit=Fit under a clay fascia. Sneak and use an empty hand underneath to recover it. +hint.ironagearchitecture.soffit.craft=Stack two dark oak slabs in the crafting grid to make four soffits. hint.ironagearchitecture.gutter.fit=Fit to the outside face of a bare clay fascia. Sneak and use an empty hand to recover it. hint.ironagearchitecture.gutter.outlet=Place a downpipe beneath the outlet to carry rain down. hint.ironagearchitecture.gutter.craft=Three iron ingots in a row make six gutters. Add an ingot to make an outlet. diff --git a/src/main/resources/assets/ironagearchitecture/models/block/dark_oak_soffit.json b/src/main/resources/assets/ironagearchitecture/models/block/dark_oak_soffit.json new file mode 100644 index 0000000..9af08ae --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/block/dark_oak_soffit.json @@ -0,0 +1,41 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "wood": "minecraft:blocks/planks_big_oak", + "particle": "minecraft:blocks/planks_big_oak" + }, + "elements": [ + { + "from": [ + 0, + 7, + 1 + ], + "to": [ + 16, + 8.25, + 15 + ], + "faces": { + "down": { + "texture": "#wood" + }, + "up": { + "texture": "#wood" + }, + "north": { + "texture": "#wood" + }, + "south": { + "texture": "#wood" + }, + "west": { + "texture": "#wood" + }, + "east": { + "texture": "#wood" + } + } + } + ] +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/dark_oak_soffit.json b/src/main/resources/assets/ironagearchitecture/models/item/dark_oak_soffit.json new file mode 100644 index 0000000..6258709 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/dark_oak_soffit.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/dark_oak_soffit" +} diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ArchitectureProbe.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ArchitectureProbe.java index cfe0bf7..e552ae2 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ArchitectureProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ArchitectureProbe.java @@ -45,7 +45,7 @@ /** Disposable packaged-runtime verification mod. Never part of the player jar. */ @Mod(modid="ironagearchitectureprobe",name="Architecture Verification",version="1",acceptableRemoteVersions="*", - dependencies="required-after:ironagearchitecture@[0.1.11.110021]") + dependencies="required-after:ironagearchitecture@[0.1.12.110021]") public final class ArchitectureProbe { @SidedProxy(clientSide="zone.moddev.mc.ironagearchitecture.probe.ClientProbe",serverSide="zone.moddev.mc.ironagearchitecture.probe.ProbeProxy") public static ProbeProxy proxy; @@ -72,6 +72,7 @@ public final class ArchitectureProbe { variants=ArchitectureBlocks.visibleStacks().size(); List savedBlocks=new ArrayList<>(ArchitectureBlocks.VISIBLE); savedBlocks.removeIf(block->block instanceof WindowBlock); + if (ArchitectureBlocks.CLAY_FASCIA_LINED != null) savedBlocks.add(ArchitectureBlocks.CLAY_FASCIA_LINED); for(FrameBlock block:ArchitectureBlocks.meshes()) if(!savedBlocks.contains(block)) savedBlocks.add(block); if(Boolean.getBoolean("iaa.legacyJetties")) { int index=0; @@ -93,10 +94,15 @@ public final class ArchitectureProbe { else world.setBlockState(pos,state,2); List drops=block.getDrops(world,pos,state,0); Item expected=block instanceof PartialWallBlock?Item.getItemFromBlock(((PartialWallBlock)block).canonical()): - block instanceof JettyFloorBlock?Item.getItemFromBlock(((JettyFloorBlock)block).canonical()):Item.getItemFromBlock(block); - boolean attachment = block instanceof JettyFloorBlock && ((JettyFloorBlock)block).bracket != null - || block instanceof FasciaBlock && state.getValue(FasciaBlock.FITTING) != 0; - require(drops.size()==(attachment?2:1)&&drops.get(0).getItem()==expected,"Drop item "+block); + block instanceof JettyFloorBlock?Item.getItemFromBlock(((JettyFloorBlock)block).canonical()): + block instanceof FasciaBlock?Item.getItemFromBlock(ArchitectureBlocks.CLAY_FASCIA):Item.getItemFromBlock(block); + int dropCount = 1; + if (block instanceof JettyFloorBlock && ((JettyFloorBlock)block).bracket != null) dropCount++; + if (block instanceof FasciaBlock) { + if (state.getValue(FasciaBlock.FITTING) != 0) dropCount++; + if (((FasciaBlock)block).lined) dropCount++; + } + require(drops.size()==dropCount&&drops.get(0).getItem()==expected,"Drop item "+block); require(drops.get(0).getMetadata()==(block instanceof FrameBlock || block instanceof DownpipeBlock ? block.damageDropped(state) : 0),"Drop identity "+block); states++; } @@ -145,7 +151,7 @@ public final class ArchitectureProbe { GutterProbe.verify(world); if (!reload && clayClient) RyeTilePreview.build(world); if (Boolean.getBoolean("iaa.clayOnly")) { - require(variants == 15 && recipes == 19 && RecipeProbe.verifyReachability() == 15, "Standalone clay tiles"); + require(variants == 16 && recipes == 20 && RecipeProbe.verifyReachability() == 16, "Standalone clay tiles"); } world.setWorldTime(1000);world.getGameRules().setOrCreateGameRule("doDaylightCycle","false"); world.getGameRules().setOrCreateGameRule("doFireTick","false");world.getGameRules().setOrCreateGameRule("doMobSpawning","false"); diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClientProbe.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClientProbe.java index f166945..2e566d3 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClientProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClientProbe.java @@ -84,6 +84,14 @@ public final class ClientProbe extends ProbeProxy { } } checkDrainModels(mc); + IBakedModel lining = mc.getBlockRendererDispatcher().getModelForState( + ArchitectureBlocks.CLAY_FASCIA_LINED.getDefaultState()); + if (lining.getQuads(null, null, 0).stream().noneMatch( + quad -> "minecraft:blocks/planks_big_oak".equals(quad.getSprite().getIconName()))) + throw new IllegalStateException("Soffit lining missing from fascia mesh"); + IBakedModel soffit = mc.getRenderItem().getItemModelMesher().getItemModel(new ItemStack(ArchitectureBlocks.SOFFIT)); + if (soffit.getParticleTexture().getIconName().contains("missingno")) + throw new IllegalStateException("Missing soffit item model"); phase=60;ticks=0; return; } @@ -116,14 +124,14 @@ public final class ClientProbe extends ProbeProxy { checkPalettes(mc); mc.displayGuiScreen(new PaletteGui(mc,Era.IRON_AGE,true));phase=8;ticks=0; } else if (phase>=60&&phase<=65) { - double[][] cameras={{-62,25,-16,-46,17,1},{-32,9.1,-7,-46,16,0}, - {-51.8,15.5,-7,-55.2,18.3,-.5},{-31.8,10.4,-6,-34.5,13.5,-.3}, + double[][] cameras={{-62,25,-16,-46,17,1},{-44.5,11.7,-15,-45.6,16.5,-.4}, + {-51.8,15.5,-6.2,-55.2,18.8,-.6},{-31.8,10.4,-6,-35.4,14.2,-.4}, {-32.7,8.9,-2.8,-34.5,10.55,.5},{-32.7,8.9,-2.8,-34.5,10.55,.5}}; double[] camera=cameras[phase-60]; lookAt(mc,camera[0],camera[1],camera[2],camera[3],camera[4],camera[5]); if (ticks<=130) return; - String[] names={"rye-guttering-overview.png","rye-guttering-street.png", - "rye-guttering-half-jetty.png","rye-guttering-full-jetty.png","rye-downpipe-shoe-closeup.png","rye-downpipe-shoe-rain.png"}; + String[] names={"rye-eaves-overview.png","rye-eaves-street.png", + "rye-eaves-exposed.png","rye-eaves-lined.png","rye-eaves-shoe.png","rye-eaves-rain.png"}; if (phase == 65 && !rainReady(mc)) { if (ticks > 300) throw new IllegalStateException("Preview rain did not reach the pipe foot"); return; diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/EraProbe.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/EraProbe.java index 7927d17..7ce5cc0 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/EraProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/EraProbe.java @@ -15,10 +15,10 @@ final class EraProbe { private static void require(boolean condition,String message) {if(!condition) throw new IllegalStateException(message);} static int expectedRecipes() { - return ArchitectureConfig.survivalEras.allowed.contains(Era.LATE_MEDIEVAL_TUDOR)?(ArchitectureConfig.windows?2010:1863)+(ArchitectureConfig.clayTiles?19:0): + return ArchitectureConfig.survivalEras.allowed.contains(Era.LATE_MEDIEVAL_TUDOR)?(ArchitectureConfig.windows?2010:1863)+(ArchitectureConfig.clayTiles?20:0): ArchitectureConfig.survivalEras.allowed.contains(Era.IRON_AGE)?36:0; } - static int expectedVariants() { return (ArchitectureConfig.windows ? 510 : 460) + (ArchitectureConfig.clayTiles ? 15 : 0); } + static int expectedVariants() { return (ArchitectureConfig.windows ? 510 : 460) + (ArchitectureConfig.clayTiles ? 16 : 0); } private static boolean ours(IRecipe recipe) { ItemStack output=recipe.getRecipeOutput(); return output!=null&&"ironagearchitecture".equals(output.getItem().getRegistryName().getResourceDomain()); diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/GutterProbe.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/GutterProbe.java index 3f53a2f..1f27199 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/GutterProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/GutterProbe.java @@ -42,7 +42,7 @@ static int verifyOldPreview(WorldServer world) { static void verify(WorldServer world) { if (ArchitectureBlocks.GUTTER == null) { - for (String id : new String[]{"iron_gutter", "iron_gutter_outlet", "iron_downpipe", "iron_downpipe_offset", "iron_downpipe_shoe"}) + for (String id : new String[]{"iron_gutter", "iron_gutter_outlet", "iron_downpipe", "iron_downpipe_offset", "iron_downpipe_shoe", "dark_oak_soffit"}) require(Item.getByNameOrId("ironagearchitecture:" + id) == null, "Disabled drain item " + id); return; } @@ -54,7 +54,7 @@ static void verify(WorldServer world) { player.setSneaking(false); player.setHeldItem(EnumHand.MAIN_HAND, null); } - System.out.println("IAA_GUTTER_PASS facings=4 chunkJoin=true bothOffsets=true rainGuards=true shoeAbovePaving=true"); + System.out.println("IAA_GUTTER_PASS facings=4 chunkJoin=true bothOffsets=true rainGuards=true shoeAbovePaving=true soffitWithGutter=true"); } private static void verifyFacing(WorldServer world, FakePlayer player, BlockPos pos, EnumFacing facing) { @@ -77,6 +77,8 @@ private static void verifyFacing(WorldServer world, FakePlayer player, BlockPos require(use(world, player, pos, outlet, outside) == EnumActionResult.FAIL && outlet.stackSize == 1, "Fitting attached twice"); + verifySoffit(world, player, pos, fitted, facing); + BlockPos left = pos.offset(facing.rotateYCCW()), right = pos.offset(facing.rotateY()); world.setBlockState(left, bare.withProperty(FasciaBlock.FITTING, 1), 2); world.setBlockState(right, bare.withProperty(FasciaBlock.FITTING, 1), 2); @@ -147,6 +149,45 @@ private static void verifyFacing(WorldServer world, FakePlayer player, BlockPos world.setBlockToAir(pos); } + private static void verifySoffit(WorldServer world, FakePlayer player, BlockPos pos, IBlockState fitted, EnumFacing facing) { + FasciaBlock fascia = ArchitectureBlocks.CLAY_FASCIA; + FasciaBlock lined = ArchitectureBlocks.CLAY_FASCIA_LINED; + ItemStack panel = new ItemStack(ArchitectureBlocks.SOFFIT, 2); + Vec3d hit = DrainGeometry.point(0, 7, facing); + require(use(world, player, pos, panel, facing.getOpposite()) == EnumActionResult.PASS, + "Soffit fitted from the front"); + require(panel.getItem().onItemUse(panel, player, world, pos, EnumHand.MAIN_HAND, EnumFacing.DOWN, + (float)hit.xCoord, 0, (float)hit.zCoord) == EnumActionResult.SUCCESS && panel.stackSize == 1, + "Soffit could not be fitted to a guttered fascia"); + IBlockState state = world.getBlockState(pos); + require(state.equals(FasciaBlock.withLining(fitted, true)) + && lined.getMetaFromState(state) == fascia.getMetaFromState(fitted) + && lined.collision(state).equals(fascia.collision(fitted)) + && pos.equals(DrainRun.exposedInletForDrip(state, world, pos)), + "Soffit changed the fascia's geometry, facing or outlet"); + require(panel.getItem().onItemUse(panel, player, world, pos, EnumHand.MAIN_HAND, EnumFacing.DOWN, + (float)hit.xCoord, 0, (float)hit.zCoord) == EnumActionResult.FAIL && panel.stackSize == 1, + "Soffit was fitted twice"); + + BlockPos neighbour = pos.offset(facing.rotateY()); + world.setBlockState(neighbour, fascia.getDefaultState().withProperty(FasciaBlock.FACING, facing) + .withProperty(FasciaBlock.FITTING, 1), 2); + require(lined.getActualState(state, world, pos).getValue(FasciaBlock.ENDS) == 1 + && fascia.getActualState(world.getBlockState(neighbour), world, neighbour).getValue(FasciaBlock.ENDS) == 2, + "Gutter did not join across lined and unlined fascias"); + world.setBlockToAir(neighbour); + require(lined.getDrops(world, pos, state, 0).stream().anyMatch(s -> s.getItem() == ArchitectureBlocks.SOFFIT), + "Breaking a lined fascia lost its soffit"); + + player.setSneaking(true); + player.setHeldItem(EnumHand.MAIN_HAND, null); + player.setHeldItem(EnumHand.OFF_HAND, null); + require(lined.onBlockActivated(world, pos, state, player, EnumHand.MAIN_HAND, null, EnumFacing.DOWN, + (float)hit.xCoord, 0, (float)hit.zCoord) && world.getBlockState(pos).equals(fitted), + "Removing the soffit changed the gutter"); + player.setSneaking(false); + } + private static void verifyShoe(WorldServer world, FakePlayer player, BlockPos inlet, BlockPos pipePos, EnumFacing facing) { DownpipeBlock shoe = ArchitectureBlocks.DOWNPIPE_SHOE; BlockPos at = pipePos.down(); diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/RyeTilePreview.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/RyeTilePreview.java index 7ba1234..c8c8967 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/RyeTilePreview.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/RyeTilePreview.java @@ -13,7 +13,7 @@ /** Three attached cottages with eaves and projecting gables over a stepped street. */ final class RyeTilePreview { - static final String NAME = "Rye Downpipe Shoe Preview"; + static final String NAME = "Rye Eaves Preview"; static final BlockPos RAIN_FOOT = new BlockPos(-35, 10, 0); static final BlockPos SPAWN = new BlockPos(-34, 9, -5); @@ -89,6 +89,10 @@ private static void fullJettyDrain(WorldServer world) { set(world, x, y, 0, Blocks.AIR.getDefaultState()); } gutter(world, -41, -34, 15, outlet); + for (int x = -41; x <= -34; x++) { + BlockPos eave = new BlockPos(x, 15, -1); + world.setBlockState(eave, FasciaBlock.withLining(world.getBlockState(eave), true), 2); + } pipe(world, outlet, 14, -1, true, 0); pipe(world, outlet, 13, -1, false, 0); pipe(world, outlet, 12, -1, true, 2);