From 3b215bcbbd0c6904c222a555e1670245f1cf8e95 Mon Sep 17 00:00:00 2001 From: JohnBraham Date: Fri, 13 Nov 2026 12:06:04 +0000 Subject: [PATCH] Add smart wattle and daub with Automatic building mode --- AGENTS.md | 1 + CHANGELOG.txt | 16 ++ README.md | 22 ++- docs/BUILDING-GUIDE.md | 55 ++++++- docs/DEVELOPMENT.md | 52 ++++++- gradle.properties | 2 +- scripts/generate-resources.cjs | 20 +++ scripts/invoke-client-preview.ps1 | 6 +- scripts/invoke-runtime-probe.ps1 | 33 +++- .../ArchitectureBlocks.java | 29 +++- .../ArchitectureConfig.java | 12 ++ .../ArchitectureRecipes.java | 35 ++++- .../mc/ironagearchitecture/BuildingMode.java | 16 ++ .../mc/ironagearchitecture/CommonProxy.java | 10 ++ .../mc/ironagearchitecture/EraTabs.java | 7 + .../IronAgeArchitecture.java | 13 +- .../ironagearchitecture/SmartWallNetwork.java | 44 ++++++ .../ironagearchitecture/block/FrameBlock.java | 7 + .../ironagearchitecture/block/FrameItem.java | 1 + .../block/JettyFloorBlock.java | 4 + .../block/SmartFacade.java | 146 ++++++++++++++++++ .../block/SmartWallBlock.java | 127 +++++++++++++++ .../block/SmartWallData.java | 69 +++++++++ .../block/SmartWallEvents.java | 125 +++++++++++++++ .../block/SmartWallGeometry.java | 132 ++++++++++++++++ .../block/SmartWallItem.java | 59 +++++++ .../block/SmartWallPlanner.java | 119 ++++++++++++++ .../block/SurfaceGeometry.java | 65 +++++++- .../ironagearchitecture/block/WallRuns.java | 12 ++ .../block/WindowBlock.java | 2 +- .../client/ClientProxy.java | 43 +++++- .../client/WallBakedModel.java | 9 +- .../blockstates/smart_daub_corner_acacia.json | 52 +++++++ .../blockstates/smart_daub_corner_birch.json | 52 +++++++ .../smart_daub_corner_dark_oak.json | 52 +++++++ .../blockstates/smart_daub_corner_jungle.json | 52 +++++++ .../blockstates/smart_daub_corner_oak.json | 52 +++++++ .../blockstates/smart_daub_corner_spruce.json | 52 +++++++ .../blockstates/smart_daub_full_acacia.json | 52 +++++++ .../blockstates/smart_daub_full_birch.json | 52 +++++++ .../blockstates/smart_daub_full_dark_oak.json | 52 +++++++ .../blockstates/smart_daub_full_jungle.json | 52 +++++++ .../blockstates/smart_daub_full_oak.json | 52 +++++++ .../blockstates/smart_daub_full_spruce.json | 52 +++++++ .../blockstates/smart_daub_thin_acacia.json | 52 +++++++ .../blockstates/smart_daub_thin_birch.json | 52 +++++++ .../blockstates/smart_daub_thin_dark_oak.json | 52 +++++++ .../blockstates/smart_daub_thin_jungle.json | 52 +++++++ .../blockstates/smart_daub_thin_oak.json | 52 +++++++ .../blockstates/smart_daub_thin_spruce.json | 52 +++++++ .../ironagearchitecture/lang/en_GB.lang | 28 ++++ .../ironagearchitecture/lang/en_US.lang | 28 ++++ .../models/block/smart_daub_mesh_acacia.json | 7 + .../models/block/smart_daub_mesh_birch.json | 7 + .../block/smart_daub_mesh_dark_oak.json | 7 + .../models/block/smart_daub_mesh_jungle.json | 7 + .../models/block/smart_daub_mesh_oak.json | 7 + .../models/block/smart_daub_mesh_spruce.json | 7 + .../models/item/smart_daub_full_acacia_0.json | 3 + .../models/item/smart_daub_full_acacia_1.json | 3 + .../models/item/smart_daub_full_birch_0.json | 3 + .../models/item/smart_daub_full_birch_1.json | 3 + .../item/smart_daub_full_dark_oak_0.json | 3 + .../item/smart_daub_full_dark_oak_1.json | 3 + .../models/item/smart_daub_full_jungle_0.json | 3 + .../models/item/smart_daub_full_jungle_1.json | 3 + .../models/item/smart_daub_full_oak_0.json | 3 + .../models/item/smart_daub_full_oak_1.json | 3 + .../models/item/smart_daub_full_spruce_0.json | 3 + .../models/item/smart_daub_full_spruce_1.json | 3 + .../models/item/smart_daub_thin_acacia_0.json | 3 + .../models/item/smart_daub_thin_acacia_1.json | 3 + .../models/item/smart_daub_thin_birch_0.json | 3 + .../models/item/smart_daub_thin_birch_1.json | 3 + .../item/smart_daub_thin_dark_oak_0.json | 3 + .../item/smart_daub_thin_dark_oak_1.json | 3 + .../models/item/smart_daub_thin_jungle_0.json | 3 + .../models/item/smart_daub_thin_jungle_1.json | 3 + .../models/item/smart_daub_thin_oak_0.json | 3 + .../models/item/smart_daub_thin_oak_1.json | 3 + .../models/item/smart_daub_thin_spruce_0.json | 3 + .../models/item/smart_daub_thin_spruce_1.json | 3 + .../probe/ArchitectureProbe.java | 42 ++++- .../probe/ClayTileProbe.java | 2 +- .../probe/ClientProbe.java | 56 ++++++- .../ironagearchitecture/probe/EraProbe.java | 13 +- .../probe/RecipeProbe.java | 12 +- .../probe/SmartWallPreview.java | 139 +++++++++++++++++ .../probe/SmartWallProbe.java | 134 ++++++++++++++++ .../probe/WallRegistrationProbe.java | 72 +++++++++ .../probe/WindowProbe.java | 3 +- .../ironagearchitecture/SmartFacadeTest.java | 59 +++++++ .../SmartGeometryTest.java | 25 +++ .../SurfaceGeometryTest.java | 76 +++++++++ 94 files changed, 2979 insertions(+), 53 deletions(-) create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/BuildingMode.java create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/SmartWallNetwork.java create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartFacade.java create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallBlock.java create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallData.java create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallEvents.java create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallGeometry.java create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallItem.java create mode 100644 src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallPlanner.java create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_acacia.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_birch.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_dark_oak.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_jungle.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_oak.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_spruce.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_acacia.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_birch.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_dark_oak.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_jungle.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_oak.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_spruce.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_acacia.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_birch.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_dark_oak.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_jungle.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_oak.json create mode 100644 src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_spruce.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_acacia.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_birch.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_dark_oak.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_jungle.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_oak.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_spruce.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_acacia_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_acacia_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_birch_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_birch_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_dark_oak_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_dark_oak_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_jungle_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_jungle_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_oak_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_oak_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_spruce_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_spruce_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_acacia_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_acacia_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_birch_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_birch_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_dark_oak_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_dark_oak_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_jungle_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_jungle_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_oak_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_oak_1.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_spruce_0.json create mode 100644 src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_spruce_1.json create mode 100644 src/probe/java/zone/moddev/mc/ironagearchitecture/probe/SmartWallPreview.java create mode 100644 src/probe/java/zone/moddev/mc/ironagearchitecture/probe/SmartWallProbe.java create mode 100644 src/probe/java/zone/moddev/mc/ironagearchitecture/probe/WallRegistrationProbe.java create mode 100644 src/test/java/zone/moddev/mc/ironagearchitecture/SmartFacadeTest.java create mode 100644 src/test/java/zone/moddev/mc/ironagearchitecture/SmartGeometryTest.java diff --git a/AGENTS.md b/AGENTS.md index ec7bda5..96ea8ed 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,6 +7,7 @@ Read README.md and docs/DEVELOPMENT.md before changing this mod. - This is an independent member of the series. Do not introduce a Furniture dependency. - Keep shared behaviour in focused base classes; roof rendering and collision share RoofGeometry. - Metadata is limited to four bits. Spend models before block IDs; do not add tile entities for simple variants. +- Keep fresh Automatic registration economical for the 1.10–1.12 ID limit. Check actual registry counts and spare metadata before adding another block ID. Retain old identities through the compatibility setting. - Edit the resource generator, then run it explicitly. Its --check mode must never rewrite resources. - Keep common code free of client classes. Keep src/probe code outside player artifacts and ordinary Eclipse launches. - Use disposable worlds. Do not alter Furniture worlds, checkouts, configs or existing qualification evidence. diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 42dfb91..13737b2 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,19 @@ +0.1.14.110021 - Alpha + +I've added smart wattle and daub so you can build a house with a much smaller +inventory. The posts, rails and braces follow the walls and their openings. + +- Automatic is the default. Build with smart walls, or choose Hybrid to use the + individual posts, rails and braces alongside them. Manual is still available. +- New Automatic setups leave out the manual wall block IDs. Smart corners now + share a block ID with full walls. Existing configs keep the older IDs so saved + buildings still load. +- The kit comes in all six woods, with raw and limewashed finishes and full + or half-depth walls. Thin panels can join into corners. +- Manual posts, rails and braces guide the smart framing in Hybrid. +- Smart panels can be glazed with the existing leaded panes. +- Tidied up the timber and plaster joins to stop the surfaces flickering. + 0.1.13.110021 - Alpha I've added brick and stone fireplaces to the Rye kit. You can build a small diff --git a/README.md b/README.md index 589f59d..4f918ee 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,17 @@ match the timber and daub to your walls, then stack sections to make taller case They swing outwards with a right-click or redstone, and each column opens separately. Full and half-depth surrounds fit both ordinary walls and projecting upper floors. +## Smart walls + +Build the wall, add doors and windows, and let smart wattle and daub arrange the timber. +Posts frame the openings, rails follow the storeys and braces fill the solid bays. +Different sizes and openings give the houses different framing. + +Automatic is the default building mode. You build with smart walls, and the manual +wall patterns stay out of the inventory and registry. Choose Hybrid to add deliberate +posts, rails and braces alongside the smart walls, or Manual for the individual pieces. +Existing configurations keep their selected mode and retain the older block IDs for saved buildings. + ## Fireplaces and chimneys Build a small brick hearth or a wider stone inglenook with separate jambs and lintels. @@ -52,10 +63,13 @@ ingredients, and the main content families can be enabled or disabled in the con See the [CurseForge files](https://www.curseforge.com/minecraft/mc-mods/ironagearchitecture/files/all) for downloads and the [changelog](CHANGELOG.txt) for what's changed since the first alpha. -Start with plain wattle and daub, surround it with matching planks to make post panels, -then arrange those panels into rails, studs, braces and corners. The same patterns work -for full walls, thin walls and short pieces. Paired designs swap one-for-one, and offcuts -can be joined back together. Item tooltips give a reminder while you build. +Start with plain wattle and daub and surround it with four matching planks to make four +smart walls. Build the house and leave openings for doors and windows. Cut full panels +into thin ones for half-block overhangs, then sneak-use two thin panels together to turn +a corner. Item tooltips give a reminder while you build. + +In Hybrid and Manual, the same starting recipe makes post panels. Shape those into +rails, studs, braces and corners. Paired designs swap one-for-one, and offcuts can be joined back together. You can limewash pieces before or after shaping, singly or in batches of eight. The [crafting guide](docs/BUILDING-GUIDE.md#crafting) covers the wall patterns, floors, diff --git a/docs/BUILDING-GUIDE.md b/docs/BUILDING-GUIDE.md index c9c0deb..59dcbef 100644 --- a/docs/BUILDING-GUIDE.md +++ b/docs/BUILDING-GUIDE.md @@ -3,13 +3,16 @@ 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.13.110021 contains raw and limewashed cob blocks, +Version 0.1.14.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, openable leaded windows, and brick and -stone fireplaces. There are 542 visible variants including the wood and finish choices. +stone fireplaces. There are 158 visible variants in Automatic, or 566 in Hybrid, +including the wood and finish choices. -## Wattle and daub +## Manual wattle and daub + +These individual patterns are available in Hybrid and Manual modes. Build filled daub walls with principal posts, corner/end posts, sill/head rails, narrow studs or close studding, and deliberate left/right diagonals. Plain infill has no border. @@ -31,6 +34,52 @@ All modules are ordinary wall blocks with raw or limewashed infill. The connecti derived from neighbouring blocks, so there are no wall tile entities or ticking components. The original timber_frame block IDs and four item subtypes remain readable. +## Smart wattle and daub + +Automatic is the default. Four matching planks around full daub make four smart walls. +Build your walls with it, leaving openings for doors and windows. The framing adjusts +as the building takes shape. Floors divide the storeys, and stepped wall outlines form +gables. Posts and braces follow each facade, including the returns around corners. + +All six woods and both finishes are available. Two full smart panels vertically make +four thin panels. Two thin panels side by side rejoin into a full panel. Limewashing +works singly and in batches of eight, just like the ordinary walls. + +Thin panels inherit the occupied half of a neighbouring thin wall or window. Sneak +to choose another plane. Sneak-use another thin panel on the end of a thin smart wall +to turn a corner. Mining that joined corner returns both panels. Full walls keep their +full footprint as their framing changes. + +In Hybrid, add a stick to a full post panel to make a smart wall. Use the individual wall pieces to guide the result. A principal post +anchors a bay, rails set a horizontal line, and a diagonal asks the smart wall to continue +that brace. Craft a smart full panel alone to recover a matching principal post. + +A leaded pane and a matching smart full or thin wall also make a window. Place floors, +roofs, windows and the other building pieces in the usual way. + +Set `building.mode` in `config/ironagearchitecture.cfg` and restart. + +| Mode | Wall kit | +| --- | --- | +| `manual` | The individual wall patterns and their original recipes | +| `hybrid` | Both kits, with manual pieces guiding smart framing | +| `automatic` | Smart framing. Four matching planks around full daub make four smart walls | + +Automatic leaves the manual wall patterns out of registration on new installations, +saving block IDs as well as keeping the inventory smaller. The rest of the building +kit remains available. Client and server need matching registration settings and a restart. + +When upgrading an existing configuration, `building.retainLegacyWallIds` is enabled +to keep older manual walls and smart corners readable. Leave it enabled for your existing +villages. For a new world or modpack, Automatic with this setting `false` uses the smaller +registry. Changing the mode does not convert placed walls. A world that needs an omitted +wall ID will refuse to load and tell you to turn compatibility back on. + +Smart walls reconsider nearby sections after a building edit. Saved framing is retained +while an adjoining chunk is unavailable. A section can span up to 48 blocks horizontally +and 32 vertically. Larger uninterrupted sections use the simple provisional framing; +corners and floor levels divide ordinary houses into smaller sections. + ## Jettied storeys Thin walls are a full block high and half a block deep. Short walls are also half a block diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 6f1fc14..12a354d 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -56,7 +56,7 @@ Roof blocks save facing. Clay verges also save which half overhangs the gable. Inner/outer corners and ridge caps derive from same-material neighbours. Slopes rise one block per block; ridge caps rise half a block and belong one row above the adjacent slopes. The roof mesh is smooth; collision is a conservative approximation within 1/16 of the surface. -No architectural block needs a tile entity or scheduled ticking. +No architectural block needs a tile entity. Smart wall edits are coalesced into bounded end-of-tick work. WindowBlock is a separate light-transmitting family. Metadata is horizontal facing in bits 0-1, open in bit 2 and cached power in bit 3. Wood, finish, depth and leading live @@ -101,6 +101,8 @@ rather than the canonical inventory block's default shape. SurfaceGeometry clips buried faces from the shared solid parts. At coplanar intersections, boards, posts and rails have deterministic ownership; braces sit slightly behind the main frame. +Diagonal covers are subtracted as convex prisms, so their end caps replace the plaster +they occupy without cutting rectangular holes around the sloping timber. The collision union continues to use the solid parts. Horizontal ties reverse their reflected mesh winding so the exposed underside remains visible. One-sided automatic rails are clipped against the timber outlines at the rail's height. @@ -128,6 +130,45 @@ outlines turn the entire extrusion inside out, exposing the back of the attachme and making the support appear hollow. Unit tests and the native client check the actual bracket face winding; the native check covers both reaches and every saved direction. +## Smart walls + +SmartWallBlock keeps its wood, finish and full/half/corner footprint independent of its +appearance. The two visible profiles have canonical items. Joining two half panels +creates an L footprint which drops both original panels. Each wood uses two block IDs. +The full ID packs facing in bits 0-1, finish in bit 2 and the L footprint in bit 3, +using all 16 metadata values. The thin ID retains facing and finish in bits 0-2. +Its old low-eight values and the full wall's old low-eight values keep their meanings. +Separate smart-corner IDs only register with building.retainLegacyWallIds. + +SmartFacade is a deterministic, coordinate-independent grammar over an occupied facade +with explicit openings and manual anchors. SmartWallPlanner surveys loaded coplanar +sections, including window ownership cells, independently on each elevation. Floors +split the survey into storeys. It caps a section at 2,048 cells, 48 columns and 32 rows. +An incomplete or oversized survey retains its saved framing. Bare sections use a simple +frame until their surroundings are known. Manual pieces contribute constraints in Hybrid. + +SmartWallData saves four eight-bit face descriptors per wall in per-dimension WorldSavedData. +SmartWallEvents coalesces changes and repairs loaded chunk borders. Idle walls do no survey +work. Chunk-watch snapshots and changed descriptors are sent to the client; render workers +read only the cached descriptors. No tile entities, render-thread world scans or forced +chunk loads are used. Automatic is the fresh-install default and omits all 144 manual +framed-wall IDs. Hybrid and Manual register them. Plain daub, floors and brackets remain +independent of that choice. Existing configs default retainLegacyWallIds to true; a fresh +config defaults it to false. The missing-mapping handler fails affected world loads rather +than letting Forge discard omitted walls, including with automatic query confirmation. +Client recipe lists and creative browsing follow the server mode and +return to the local setting after disconnecting. + +With all content families enabled, packaged registry checks measure 320 block IDs +for fresh Automatic, 464 for fresh Hybrid/Manual, or 470 with legacy wall retention. +The 150-ID saving consists of 144 manual wall IDs and six separate smart-corner IDs. +Count actual registry entries when checking the 1.10–1.12 budget; creative variants, +unlisted framing descriptors and baked meshes do not each consume a block ID. + +The shared WallGeometry and SurfaceGeometry path clips the timber members. Smart occupancy +supplies collision, targeting, placement clearance and jetty support. Both depths can craft +leaded windows. Recipe accounting preserves framing quantity separately from glazing. + ## Era membership EraCatalog supplies both creative membership and survival recipe eligibility. Basic cob, @@ -164,7 +205,7 @@ resource generator and item tooltips. Run scripts/invoke-runtime-probe.ps1 after prepareProbe. Modes cover core, reload, furniture, disabled, windows-disabled, legacy, legacy-daub, legacy-jetties, iron-age, late-medieval, -mixed-eras, no-eras, invalid-era, clay-disabled, clay-only and legacy-fascia. +mixed-eras, no-eras, invalid-era, clay-disabled, clay-only, manual, automatic, legacy-fascia and legacy-fireplaces. Only Forge/server libraries and the explicitly named Furniture JAR are read from retained runtimes; no existing worlds or configs are copied or changed. Each mode has an isolated runtime except reload, which verifies the saved core world. Servers bind to loopback. Keep failed logs when correcting failures. @@ -194,9 +235,10 @@ flues across a chunk border, blocked and disconnected runs, the three outlets an The core reload checks that the fires still draw from the correct floor. The optional client probe requires iaa.probe=true, iaa.clientProbe=true and iaa.keepRunning=true. -It creates its own Rye Fireplace Preview, checks mesh and material variants and both -creative tabs, captures the interiors and the stack with one fire and then both lit, -then shuts down. With fireplaces disabled it captures the eaves and guttering instead. +It creates its own Smart Walls Preview, checks mesh and material variants and both +creative tabs, captures the three smart houses and their jetties, then shuts down. +The houses use 7, 9 and 11 block widths, different windows, both jetty depths and a +manual anchor. Server/client descriptor equality is checked before capture. 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. After verification copy the preview save into the ordinary run/saves directory and launch without the probe. diff --git a/gradle.properties b/gradle.properties index 864d600..d111eb4 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.13.110021 +mod_version=0.1.14.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 2cf5c1c..948a782 100644 --- a/scripts/generate-resources.cjs +++ b/scripts/generate-resources.cjs @@ -154,6 +154,26 @@ model('dark_oak_soffit',{parent:'minecraft:block/block',textures:{wood:'minecraf ['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'; +for(const [woodIndex,wood] of woods.entries()) { + const mesh=`smart_daub_mesh_${wood}`; + model(mesh,{parent:'minecraft:block/block',textures:{particle:`${ns}:blocks/limewashed_cob`},elements:[]}); + for(const profile of ['full','thin','corner']) { + // Full panels 2/3 are packed L footprints. Keep separate corner models for old saves. + const name=`smart_daub_${profile}_${wood}`,variants={}; + for(const facing of Object.keys(facings)) for(let panel=0;panel<4;panel++) + variants[`facing=${facing},panel=${panel}`]={model:`${ns}:${mesh}`}; + state(name,variants); + if(profile==='corner') continue; + for(let finish=0;finish<2;finish++) { + item(name+'_'+finish,mesh); + lang[`tile.${ns}.${name}.${finish}.name`]=`${finish?'Limewashed ':''}${names[woodIndex]} Smart ${profile==='thin'?'Thin ':''}Wattle and Daub`; + } + } +} +lang[`hint.${ns}.smart.place`]='Build the wall; its posts, rails and braces follow the facade and openings.'; +lang[`hint.${ns}.smart.corner`]='Sneak-use a thin panel on another thin smart wall to form a corner.'; +lang[`hint.${ns}.smart.craft`]='Hybrid: matching post panel + stick. Cut and rejoin full and thin panels.'; +lang[`hint.${ns}.smart.craft.auto`]='Four matching planks around full daub make four smart full walls.'; const fireplaceParts = { hearth:'Hearth', firebox:'Firebox', jamb:'Fireplace Jamb', lintel:'Fireplace Lintel', flue:'Chimney Flue', collar:'Clay Roof Collar', pot_cap:'Chimney Pot', slab_cap:'Vented Chimney Cap' diff --git a/scripts/invoke-client-preview.ps1 b/scripts/invoke-client-preview.ps1 index 9296526..1dd63ea 100644 --- a/scripts/invoke-client-preview.ps1 +++ b/scripts/invoke-client-preview.ps1 @@ -1,6 +1,7 @@ param( - [string]$Runtime='D:\IronAgeArchitecture\manual-testing\rye-fireplaces-0.1.13\native-client-verified', - [string]$PreviewName='Rye Fireplace Preview', + [string]$Runtime='D:\IronAgeArchitecture\manual-testing\smart-walls-auto-0.1.14\native-client-reviewed', + [string]$PreviewName='Automatic Walls Preview', + [switch]$SurfaceCheck, [string]$ServerTemplate='D:\IronAgeArchitecture\manual-testing\jetties-0.1.2-final\core', [string]$GradleHome='D:\GradleCaches\minecraft-mods-shared', [string]$AssetsDirectory='', @@ -61,6 +62,7 @@ $args=@('-Xmx2G',('-Djava.library.path='+(Join-Path $slime 'natives')),'-Diaa.pr 'net.minecraft.launchwrapper.Launch','--tweakClass','net.minecraftforge.fml.common.launcher.FMLTweaker', '--username','ArchitecturePreview','--version','1.10.2','--gameDir',$Runtime,'--assetsDir',$AssetsDirectory, '--assetIndex','1.10','--uuid','00000000000000000000000000000001','--accessToken','0','--userType','legacy','--versionType','release','--width','1280','--height','800') +if($SurfaceCheck) {$args=@('-Diaa.surfacePreview=true')+$args} Push-Location $Runtime try { & $Java8 @args *> 'native-client-console.log' diff --git a/scripts/invoke-runtime-probe.ps1 b/scripts/invoke-runtime-probe.ps1 index 82a58a3..d962a9b 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-fireplaces-0.1.13', + [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','manual','hybrid','automatic','legacy-fireplaces','legacy-smart')][string]$Mode='core', + [string]$RuntimeRoot='D:\IronAgeArchitecture\manual-testing\smart-walls-auto-0.1.14', [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' @@ -42,6 +42,23 @@ if($Mode -eq 'legacy-jetties') { [System.IO.Compression.ZipFile]::ExtractToDirectory('D:\IronAgeArchitecture\candidates\0.1.2.110021-jettied-storeys\Jettied Street Preview.zip',$staging) Copy-Item -LiteralPath (Join-Path $staging 'Jettied Street Preview') -Destination (Join-Path $folder 'world') -Recurse } +if($Mode -eq 'legacy-fireplaces') { + if(Test-Path -LiteralPath (Join-Path $folder 'world')) {throw 'Legacy fireplace probe requires a fresh destination'} + Add-Type -AssemblyName System.IO.Compression.FileSystem + $staging=Join-Path $folder 'fireplace-preview-source' + [System.IO.Compression.ZipFile]::ExtractToDirectory('D:\IronAgeArchitecture\manual-testing\rye-fireplaces-0.1.13\Rye Fireplace Preview.zip',$staging) + Copy-Item -LiteralPath (Join-Path $staging 'Rye Fireplace Preview') -Destination (Join-Path $folder 'world') -Recurse +} +if($Mode -eq 'legacy-smart') { + if(Test-Path -LiteralPath (Join-Path $folder 'world')) {throw 'Legacy smart wall probe requires a fresh destination'} + Add-Type -AssemblyName System.IO.Compression.FileSystem + $staging=Join-Path $folder 'smart-preview-source' + [System.IO.Compression.ZipFile]::ExtractToDirectory('D:\IronAgeArchitecture\manual-testing\smart-walls-0.1.14-surface-fix\Smart Walls Surface Preview.zip',$staging) + Copy-Item -LiteralPath (Join-Path $staging 'Smart Walls Surface Preview') -Destination (Join-Path $folder 'world') -Recurse + New-Item -ItemType Directory -Force -Path (Join-Path $folder 'config') | Out-Null + # An existing alpha config without the new setting must retain every old identity. + "building {`n S:mode=automatic`n}" | Set-Content -LiteralPath (Join-Path $folder 'config/ironagearchitecture.cfg') -Encoding ascii +} if($Mode -eq 'legacy-fascia') { if(Test-Path -LiteralPath (Join-Path $folder 'world')) {throw 'Legacy fascia probe requires a fresh disposable destination'} Add-Type -AssemblyName System.IO.Compression.FileSystem @@ -54,6 +71,12 @@ if($eraModes.ContainsKey($Mode)) { New-Item -ItemType Directory -Force -Path (Join-Path $folder 'config') | Out-Null ("recipes {`n S:survivalEras <`n"+($eraModes[$Mode] -join "`n")+"`n >`n}`n") | Set-Content -LiteralPath (Join-Path $folder 'config/ironagearchitecture.cfg') -Encoding ascii } +if($Mode -in @('manual','hybrid','automatic','legacy','legacy-daub','legacy-jetties','legacy-fascia','legacy-fireplaces')) { + New-Item -ItemType Directory -Force -Path (Join-Path $folder 'config') | Out-Null + $wallMode=if($Mode -like 'legacy*') {'manual'} else {$Mode} + $legacy=($Mode -like 'legacy*').ToString().ToLowerInvariant() + ("building {`n S:mode="+$wallMode+"`n B:retainLegacyWallIds="+$legacy+"`n}") | Set-Content -LiteralPath (Join-Path $folder 'config/ironagearchitecture.cfg') -Encoding ascii +} if($Mode -eq 'windows-disabled') { New-Item -ItemType Directory -Force -Path (Join-Path $folder 'config') | Out-Null "blocks {`n B:leadedWindows=false`n}" | Set-Content -LiteralPath (Join-Path $folder 'config/ironagearchitecture.cfg') -Encoding ascii @@ -76,6 +99,10 @@ blocks { } "@ | Set-Content -LiteralPath (Join-Path $folder 'config/ironagearchitecture.cfg') -Encoding ascii } +if($Mode -notin @('core','reload','manual','hybrid','automatic','legacy','legacy-daub','legacy-jetties','legacy-fascia','legacy-fireplaces','legacy-smart')) { + New-Item -ItemType Directory -Force -Path (Join-Path $folder 'config') | Out-Null + Add-Content -LiteralPath (Join-Path $folder 'config/ironagearchitecture.cfg') -Value "building {`n S:mode=automatic`n B:retainLegacyWallIds=false`n}" -Encoding ascii +} 'eula=true' | Set-Content -LiteralPath (Join-Path $folder 'eula.txt') -Encoding ascii @' level-type=FLAT @@ -100,6 +127,8 @@ if($Mode -eq 'legacy') {$args+='-Diaa.legacy=true'} if($Mode -eq 'legacy-daub') {$args+='-Diaa.legacyDaub=true'} if($Mode -eq 'legacy-jetties') {$args+='-Diaa.legacyJetties=true'} if($Mode -eq 'legacy-fascia') {$args+='-Diaa.legacyFascia=true'} +if($Mode -eq 'legacy-fireplaces') {$args+='-Diaa.legacyFireplaces=true'} +if($Mode -eq 'legacy-smart') {$args+='-Diaa.legacySmart=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=16','-Diaa.clayOnly=true','-Diaa.recipeMatrix=true')} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureBlocks.java b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureBlocks.java index 991ce3c..b79e93c 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureBlocks.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureBlocks.java @@ -27,6 +27,7 @@ public final class ArchitectureBlocks { public static final String[] WOODS = {"oak", "spruce", "birch", "jungle", "acacia", "dark_oak"}; public static final String[] FINISHES = {"cob", "limewashed_cob"}; public static final List VISIBLE = new ArrayList<>(); + public static final List REGISTERED = new ArrayList<>(); public static final List COB = new ArrayList<>(); public static final List SLABS = new ArrayList<>(); public static final List STAIRS = new ArrayList<>(); @@ -48,7 +49,7 @@ public static int variants(Block block) { } public static List visibleStacks() { List result=new ArrayList<>(); - for(Block block:VISIBLE) for(int meta=0;meta visibleStacks() { return result; } public static List meshes() { - List result=new ArrayList<>(WALLS);result.addAll(PARTIALS);result.addAll(FLOORS);return result; + List result=new ArrayList<>(WALLS);result.addAll(PARTIALS);result.addAll(FLOORS);result.addAll(SMART_WALLS);return result; } public static FrameBlock DAUB; + public static final List SMART_WALLS = new ArrayList<>(); public static final List FIREPLACES = new ArrayList<>(); public static final List ROOFS = new ArrayList<>(); public static TileHangingBlock CLAY_HANGING, CLAY_HANGING_THIN; @@ -72,6 +74,7 @@ public static T block(T block, String name) { block.setRegistryName(new ResourceLocation(IronAgeArchitecture.MODID, name)); block.setUnlocalizedName(IronAgeArchitecture.MODID + "." + name); GameRegistry.register(block); + REGISTERED.add(block); return block; } private static void item(Block block, ItemBlock item) { @@ -97,11 +100,18 @@ public static void register() { if (ArchitectureConfig.frames) { DAUB=wall(null,FrameFamily.INFILL); for (String wood : WOODS) for(FrameFamily family:FrameFamily.values()) { - if(family==FrameFamily.INFILL) continue; + if(family==FrameFamily.INFILL || !ArchitectureConfig.registerManualWalls()) continue; FrameBlock frame=wall(wood,family); if(family==FrameFamily.POST) FRAMES.add(frame); } registerJetties(); + for(String timber:WOODS) for(int profile=0;profile<3;profile++) { + if(profile==2 && !ArchitectureConfig.retainLegacyWallIds) continue; + SmartWallBlock smart=block(new SmartWallBlock(timber,profile),"smart_daub_"+(profile==0?"full_":profile==1?"thin_":"corner_")+timber); + SMART_WALLS.add(smart); + if(profile!=2) item(smart,new SmartWallItem(smart)); + Blocks.FIRE.setFireInfo(smart,5,20); + } if(ArchitectureConfig.windows) registerWindows(); } if (ArchitectureConfig.thatch) roofFamily("thatch", true); @@ -178,6 +188,17 @@ public static WindowBlock findWindow(String wood,int finish,boolean thin,boolean for(WindowBlock block:WINDOWS) if(block.wood.equals(wood)&&block.finish==finish&&block.thin==thin&&block.diamond==diamond&&block.right==right&&block.back==back) return block; throw new IllegalArgumentException("Unregistered window "+wood+" "+finish+" "+thin+" "+diamond+" "+right); } + public static SmartWallBlock findSmart(String wood,int profile) { + // New corners share the full wall's spare metadata; old corner IDs only load saves. + for(SmartWallBlock wall:SMART_WALLS) if(wall.wood.equals(wood)&&wall.profile==(profile==2?0:profile)) return wall; + throw new IllegalArgumentException("Unregistered smart wall"); + } + public static boolean available(Item item) { + if(item instanceof SmartWallItem) return ArchitectureConfig.buildingMode.automatic(); + Block block=Block.getBlockFromItem(item); + return ArchitectureConfig.buildingMode.manual()||!(block instanceof FrameBlock)||block instanceof JettyFloorBlock + ||((FrameBlock)block).family==FrameFamily.INFILL; + } private static FrameBlock wall(String wood,FrameFamily family) { FrameBlock frame=block(new FrameBlock(wood,family),family.registryPrefix+(wood==null?"":"_"+wood)); item(frame,new FrameItem(frame));WALLS.add(frame); @@ -192,7 +213,7 @@ private static void registerJetties() { for(WallProfile profile:WallProfile.values()) { if(!profile.corner) partial(null,FrameFamily.INFILL,profile); for(String wood:WOODS) for(FrameFamily family:FrameFamily.values()) { - if(family==FrameFamily.INFILL||profile.corner&&family!=FrameFamily.TERMINAL) continue; + if(family==FrameFamily.INFILL||profile.corner&&family!=FrameFamily.TERMINAL||!ArchitectureConfig.registerManualWalls()) continue; partial(wood,family,profile); } } diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureConfig.java b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureConfig.java index d2c12c1..166d36b 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureConfig.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureConfig.java @@ -6,9 +6,13 @@ /** Complete content-family switches. Framed panels require the cob family. */ public final class ArchitectureConfig { public static boolean cob, frames, thatch, slate, windows, clayTiles, fireplaces; + public static BuildingMode buildingMode = BuildingMode.AUTOMATIC; + public static boolean retainLegacyWallIds; + public static boolean registerManualWalls() { return buildingMode.manual() || retainLegacyWallIds; } public static EraSelection survivalEras=EraSelection.parse("all"); private ArchitectureConfig() { } public static void load(File file) { + boolean existingConfig = file.isFile(); Configuration config = new Configuration(file); config.load(); cob = config.getBoolean("cob", "blocks", true, "Raw and limewashed cob blocks, stairs and slabs."); @@ -23,6 +27,14 @@ public static void load(File file) { eras.setRequiresMcRestart(true); survivalEras=EraSelection.parse(eras.getStringList()); for(String unknown:survivalEras.unknown) org.apache.logging.log4j.LogManager.getLogger("IronAgeArchitecture").warn("Ignoring unknown survival era '{}'. Valid IDs: iron_age, late_medieval_tudor, all",unknown); + net.minecraftforge.common.config.Property mode = config.get("building", "mode", "automatic", + "Wall building mode: automatic, hybrid or manual. Automatic uses smart walls and omits manual wall block IDs unless retainLegacyWallIds is enabled. Match the server settings. Restart required."); + mode.setRequiresMcRestart(true); + buildingMode = BuildingMode.parse(mode.getString()); + net.minecraftforge.common.config.Property legacy = config.get("building", "retainLegacyWallIds", existingConfig, + "Keep the older manual wall and separate smart-corner IDs when loading existing worlds. Enabled when upgrading an existing config. New installations default to false to save block IDs. Do not turn this off for a world containing those blocks. Match the server settings. Restart required."); + legacy.setRequiresMcRestart(true); + retainLegacyWallIds = legacy.getBoolean(); if (config.hasChanged()) config.save(); } } diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureRecipes.java b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureRecipes.java index 81c957b..9681dbe 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureRecipes.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/ArchitectureRecipes.java @@ -10,14 +10,18 @@ /** Shared starters, lossless shaping and finish-preserving conversions. */ public final class ArchitectureRecipes { private ArchitectureRecipes() { } - private static void shaped(ItemStack output,Object... inputs) {if(EraCatalog.canCraft(output)) GameRegistry.addRecipe(output,inputs);} - private static void shapeless(ItemStack output,Object... inputs) {if(EraCatalog.canCraft(output)) GameRegistry.addShapelessRecipe(output,inputs);} + private static void shaped(ItemStack output,Object... inputs) {if(EraCatalog.canCraft(output)&&ArchitectureBlocks.available(output.getItem())) GameRegistry.addRecipe(output,inputs);} + private static void shapeless(ItemStack output,Object... inputs) {if(EraCatalog.canCraft(output)&&ArchitectureBlocks.available(output.getItem())) GameRegistry.addShapelessRecipe(output,inputs);} private static ItemStack stack(Block block,int count,int meta) {return new ItemStack(block,count,meta);} private static ItemStack count(ItemStack stack,int count) {ItemStack result=stack.copy();result.stackSize=count;return result;} public static void register() { if(ArchitectureConfig.cob) registerCob(); if(ArchitectureConfig.frames) { - registerWalls();registerWallCuts();registerCorners();registerFloors(); + registerDaub(); + if(ArchitectureConfig.buildingMode.manual()) {registerWalls();registerCorners();} + registerWallCuts(); + registerFloors(); + if(ArchitectureConfig.buildingMode.automatic()) registerSmartWalls(); for(Block block:ArchitectureBlocks.VISIBLE) if(block instanceof FrameBlock&&!(block instanceof JettyFloorBlock)) { FrameBlock wall=(FrameBlock)block; int patterns=wall.variants()/2; @@ -102,21 +106,40 @@ private static void registerCob() { wash(new ItemStack(ArchitectureBlocks.SLABS.get(0)),new ItemStack(ArchitectureBlocks.SLABS.get(1))); wash(new ItemStack(ArchitectureBlocks.STAIRS.get(0)),new ItemStack(ArchitectureBlocks.STAIRS.get(1))); } + private static void registerSmartWalls() { + for(int wood=0;wood<6;wood++) for(int finish=0;finish<2;finish++) { + String timber=ArchitectureBlocks.WOODS[wood]; + ItemStack full=stack(ArchitectureBlocks.findSmart(timber,0),1,finish); + ItemStack thin=stack(ArchitectureBlocks.findSmart(timber,1),1,finish); + if(ArchitectureConfig.buildingMode==BuildingMode.AUTOMATIC) + shaped(count(full,4)," P ","PDP"," P ",'P',new ItemStack(Blocks.PLANKS,1,wood),'D',stack(ArchitectureBlocks.DAUB,1,finish)); + else { + ItemStack post=stack(ArchitectureBlocks.findWall(timber,FrameFamily.POST),1,finish*2); + shapeless(full,post,Items.STICK);shapeless(post,full); + } + cut(full,thin);join(thin,full); + } + } private static void registerWindows() { shaped(new ItemStack(ArchitectureBlocks.LEADED_PANE,8,0),"GGG","GIG","GGG",'G',Blocks.GLASS_PANE,'I',Items.IRON_INGOT); for(int pattern=0;pattern<2;pattern++) shapeless(new ItemStack(ArchitectureBlocks.LEADED_PANE,1,1-pattern),new ItemStack(ArchitectureBlocks.LEADED_PANE,1,pattern)); for(WindowBlock window:ArchitectureBlocks.WINDOWS) if(!window.right&&!window.back) { - FrameBlock panel=window.thin?ArchitectureBlocks.findPartial(window.wood,FrameFamily.POST,WallProfile.THIN):ArchitectureBlocks.findWall(window.wood,FrameFamily.POST); - shapeless(new ItemStack(window),stack(panel,1,window.finish*2),new ItemStack(ArchitectureBlocks.LEADED_PANE,1,window.diamond?0:1)); + if(ArchitectureConfig.buildingMode.manual()) { + FrameBlock panel=window.thin?ArchitectureBlocks.findPartial(window.wood,FrameFamily.POST,WallProfile.THIN):ArchitectureBlocks.findWall(window.wood,FrameFamily.POST); + shapeless(new ItemStack(window),stack(panel,1,window.finish*2),new ItemStack(ArchitectureBlocks.LEADED_PANE,1,window.diamond?0:1)); + } + if(ArchitectureConfig.buildingMode.automatic()) shapeless(new ItemStack(window),stack(ArchitectureBlocks.findSmart(window.wood,window.thin?1:0),1,window.finish),new ItemStack(ArchitectureBlocks.LEADED_PANE,1,window.diamond?0:1)); shapeless(new ItemStack(ArchitectureBlocks.findWindow(window.wood,window.finish,window.thin,!window.diamond,false)),new ItemStack(window)); if(window.finish==0) wash(new ItemStack(window),new ItemStack(ArchitectureBlocks.findWindow(window.wood,1,window.thin,window.diamond,false))); } } - private static void registerWalls() { + private static void registerDaub() { for(int finish=0;finish<2;finish++) { shapeless(stack(ArchitectureBlocks.DAUB,1,finish),ArchitectureBlocks.COB.get(finish)); shapeless(new ItemStack(ArchitectureBlocks.COB.get(finish)),stack(ArchitectureBlocks.DAUB,1,finish)); } + } + private static void registerWalls() { for(int wood=0;wood<6;wood++) for(int finish=0;finish<2;finish++) { String timber=ArchitectureBlocks.WOODS[wood]; shaped(stack(ArchitectureBlocks.FRAMES.get(wood),4,finish*2)," P ","PDP"," P ", diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/BuildingMode.java b/src/main/java/zone/moddev/mc/ironagearchitecture/BuildingMode.java new file mode 100644 index 0000000..e219a51 --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/BuildingMode.java @@ -0,0 +1,16 @@ +package zone.moddev.mc.ironagearchitecture; + +import java.util.Locale; + +/** Controls wall acquisition and, with the compatibility setting, registration. */ +public enum BuildingMode { + MANUAL, HYBRID, AUTOMATIC; + + public boolean manual() { return this != AUTOMATIC; } + public boolean automatic() { return this != MANUAL; } + + public static BuildingMode parse(String value) { + try { return valueOf(value.trim().toUpperCase(Locale.ROOT)); } + catch (IllegalArgumentException failure) { return AUTOMATIC; } + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/CommonProxy.java b/src/main/java/zone/moddev/mc/ironagearchitecture/CommonProxy.java index 22b7460..8c51d67 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/CommonProxy.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/CommonProxy.java @@ -2,7 +2,17 @@ /** Keeps client model classes off dedicated servers. */ public class CommonProxy { + public int smartLayout(net.minecraft.world.IBlockAccess access,net.minecraft.util.math.BlockPos pos) { + if(access instanceof net.minecraft.world.World) + return zone.moddev.mc.ironagearchitecture.block.SmartWallData.get((net.minecraft.world.World)access).code(pos); + return 0; + } + public void smartSnapshot(SmartWallNetwork.Packet packet) { } public void preInit() { + SmartWallNetwork.register(); + zone.moddev.mc.ironagearchitecture.block.SmartWallEvents smart=new zone.moddev.mc.ironagearchitecture.block.SmartWallEvents(); + net.minecraftforge.common.MinecraftForge.EVENT_BUS.register(smart); + net.minecraftforge.fml.common.FMLCommonHandler.instance().bus().register(smart); zone.moddev.mc.ironagearchitecture.block.WindowEvents events=new zone.moddev.mc.ironagearchitecture.block.WindowEvents(); net.minecraftforge.common.MinecraftForge.EVENT_BUS.register(events); net.minecraftforge.fml.common.FMLCommonHandler.instance().bus().register(events); diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/EraTabs.java b/src/main/java/zone/moddev/mc/ironagearchitecture/EraTabs.java index 1cfd9f5..d6b426a 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/EraTabs.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/EraTabs.java @@ -10,7 +10,14 @@ public final class EraTabs { private static final EnumMap TABS=new EnumMap<>(Era.class); static { for(final Era era:Era.values()) TABS.put(era,new CreativeTabs("ironagearchitecture."+era.id) { + @Override public void displayAllRelevantItems(java.util.List items) { + super.displayAllRelevantItems(items); + if(era==Era.LATE_MEDIEVAL_TUDOR) + items.sort(java.util.Comparator.comparingInt(stack->stack.getItem() instanceof zone.moddev.mc.ironagearchitecture.block.SmartWallItem?0:1)); + } @Override public Item getTabIconItem() { + if(era==Era.LATE_MEDIEVAL_TUDOR&&ArchitectureConfig.buildingMode.automatic()&&!ArchitectureBlocks.SMART_WALLS.isEmpty()) + return Item.getItemFromBlock(ArchitectureBlocks.findSmart("oak",0)); if(era==Era.LATE_MEDIEVAL_TUDOR&&!ArchitectureBlocks.FRAMES.isEmpty()) return Item.getItemFromBlock(ArchitectureBlocks.FRAMES.get(0)); if(!ArchitectureBlocks.COB.isEmpty()) return Item.getItemFromBlock(ArchitectureBlocks.COB.get(0)); return Item.getItemFromBlock(Blocks.BRICK_BLOCK); diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/IronAgeArchitecture.java b/src/main/java/zone/moddev/mc/ironagearchitecture/IronAgeArchitecture.java index aa565d1..2ea27d5 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.13.110021", +@Mod(modid = IronAgeArchitecture.MODID, name = "Iron Age Architecture", version = "0.1.14.110021", acceptedMinecraftVersions = "[1.10.2]", dependencies = "required-after:Forge@[12.18.3.2511,)") public final class IronAgeArchitecture { public static final String MODID = "ironagearchitecture"; @@ -22,4 +22,15 @@ public final class IronAgeArchitecture { proxy.preInit(); } @Mod.EventHandler public void init(FMLInitializationEvent event) { ArchitectureRecipes.register(); } + @Mod.EventHandler public void missingMappings(net.minecraftforge.fml.common.event.FMLMissingMappingsEvent event) { + boolean blocked=false; + if(!ArchitectureConfig.frames || ArchitectureConfig.retainLegacyWallIds) return; + for(net.minecraftforge.fml.common.event.FMLMissingMappingsEvent.MissingMapping mapping:event.get()) { + String name=mapping.name; + if(name.startsWith(MODID+":timber_") || name.startsWith(MODID+":partial_")&&name.contains("_timber_") + || name.startsWith(MODID+":smart_daub_corner_")) {mapping.fail();blocked=true;} + } + if(blocked) org.apache.logging.log4j.LogManager.getLogger("IronAgeArchitecture").error( + "This world uses older wall IDs. Set building.retainLegacyWallIds=true in config/ironagearchitecture.cfg and restart to keep those buildings."); + } } diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/SmartWallNetwork.java b/src/main/java/zone/moddev/mc/ironagearchitecture/SmartWallNetwork.java new file mode 100644 index 0000000..57e798a --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/SmartWallNetwork.java @@ -0,0 +1,44 @@ +package zone.moddev.mc.ironagearchitecture; + +import java.util.*; +import io.netty.buffer.ByteBuf; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraftforge.fml.common.network.NetworkRegistry; +import net.minecraftforge.fml.common.network.simpleimpl.*; +import net.minecraftforge.fml.relauncher.Side; + +/** Server-owned chunk snapshots and changed cells; the client never proposes framing. */ +public final class SmartWallNetwork { + private static final SimpleNetworkWrapper CHANNEL=NetworkRegistry.INSTANCE.newSimpleChannel("iaa_smart_walls"); + private SmartWallNetwork() { } + public static void register(){CHANNEL.registerMessage(Handler.class,Packet.class,0,Side.CLIENT);} + public static void send(EntityPlayerMP player,int dimension,int x,int z,boolean replace,Map cells) { + CHANNEL.sendTo(new Packet(dimension,x,z,replace,cells),player); + } + + public static final class Packet implements IMessage { + public int dimension,x,z,mode;public boolean replace;public Map cells; + public Packet(){ } + Packet(int dimension,int x,int z,boolean replace,Map cells) { + this.dimension=dimension;this.x=x;this.z=z;this.replace=replace;this.cells=new HashMap<>(cells); + } + @Override public void toBytes(ByteBuf buffer) { + buffer.writeInt(dimension);buffer.writeInt(x);buffer.writeInt(z);buffer.writeBoolean(replace);buffer.writeByte(ArchitectureConfig.buildingMode.ordinal()); + buffer.writeInt(cells.size()); + for(Map.Entry cell:cells.entrySet()){buffer.writeLong(cell.getKey());buffer.writeInt(cell.getValue());} + } + @Override public void fromBytes(ByteBuf buffer) { + dimension=buffer.readInt();x=buffer.readInt();z=buffer.readInt();replace=buffer.readBoolean();mode=buffer.readUnsignedByte(); + if(mode>=BuildingMode.values().length) throw new IllegalArgumentException("Unknown building mode"); + int count=buffer.readInt(); + if(count<0||count>65536||buffer.readableBytes()!=count*12) throw new IllegalArgumentException("Invalid smart wall snapshot"); + cells=new HashMap<>(); + for(int i=0;i { + @Override public IMessage onMessage(Packet message,MessageContext context){ + IronAgeArchitecture.proxy.smartSnapshot(message);return null; + } + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/FrameBlock.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/FrameBlock.java index 6402617..33afe73 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/block/FrameBlock.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/FrameBlock.java @@ -72,6 +72,13 @@ private int railRequest(IBlockAccess world,BlockPos pos,EnumFacing direction,Enu IBlockState previous=world.getBlockState(pos);BlockPos previousPos=pos; for(int distance=1;distance<=16;distance++) { IBlockState neighbour=world.getBlockState(pos.offset(direction,distance)); + if(neighbour.getBlock() instanceof SmartWallBlock) { + SmartWallBlock smart=(SmartWallBlock)neighbour.getBlock(); + BlockPos at=pos.offset(direction,distance); + if(wood==null||!wood.equals(smart.wood)||!WallRuns.touches(previous,previousPos,neighbour,at,direction)) return 0; + int code=SmartWallGeometry.face(zone.moddev.mc.ironagearchitecture.IronAgeArchitecture.proxy.smartLayout(world,at),plane); + return ((code&SmartFacade.BOTTOM)!=0?1:0)|((code&SmartFacade.TOP)!=0?2:0); + } if(neighbour.getBlock() instanceof WindowBlock) { WindowBlock window=(WindowBlock)neighbour.getBlock();BlockPos at=pos.offset(direction,distance); if(wood==null||!wood.equals(window.wood)||!WallRuns.touches(previous,previousPos,neighbour,at,direction)) return 0; diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/FrameItem.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/FrameItem.java index e2b4f4b..2d4c8b0 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/block/FrameItem.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/FrameItem.java @@ -32,6 +32,7 @@ public final class FrameItem extends EraItemBlock { return EnumActionResult.SUCCESS; } @Override public void getSubItems(Item item, CreativeTabs tab, List list) { + if(!zone.moddev.mc.ironagearchitecture.ArchitectureBlocks.available(this)) return; for (int meta = 0; meta < ((FrameBlock)block).variants(); meta++) list.add(new ItemStack(item, 1, meta)); } @Override public void addInformation(ItemStack stack,EntityPlayer player,List tooltip,boolean advanced) { diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/JettyFloorBlock.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/JettyFloorBlock.java index ac42eaf..b0acc41 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/block/JettyFloorBlock.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/JettyFloorBlock.java @@ -99,6 +99,10 @@ public boolean covers(IBlockState state,double x,double z) { EnumFacing turn=below.getValue(WindowBlock.FACING),inverse=turn==EnumFacing.EAST?EnumFacing.WEST:turn==EnumFacing.WEST?EnumFacing.EAST:turn; occupied=(WallGeometry.rotate(point[0],0,point[2],inverse)[2]>=8)==((WindowBlock)below.getBlock()).back; } + if(below.getBlock() instanceof SmartWallBlock) { + int footprint=((SmartWallBlock)below.getBlock()).mask(below); + occupied=(footprint&(1<<((point[0]<8?0:1)+(point[2]<8?0:2))))!=0; + } if(below.getBlock() instanceof PartialWallBlock) { EnumFacing turn=below.getValue(FACING); EnumFacing inverse=turn==EnumFacing.EAST?EnumFacing.WEST:turn==EnumFacing.WEST?EnumFacing.EAST:turn; diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartFacade.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartFacade.java new file mode 100644 index 0000000..0fc41ba --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartFacade.java @@ -0,0 +1,146 @@ +package zone.moddev.mc.ironagearchitecture.block; + +import java.util.*; + +/** Pure facade grammar. Coordinates and openings determine repeatable timber bays. */ +public final class SmartFacade { + public static final int LEFT=1, RIGHT=2, CENTRE=4, BOTTOM=8, TOP=16, RISE=32, FALL=64, FACE=128; + public static final int WALL=1, OPENING=2, POST=3, RAIL=4, RISING=5, FALLING=6, HEAD=7, CROSS=8; + private SmartFacade() { } + + public static int[][] arrange(int[][] cells) { + cells=withOpenings(cells); + int height=cells.length, width=cells[0].length; + int[][] result=new int[height][width]; + int widest=0, gable=height; + for(int[] row:cells) { + int count=0;for(int value:row) if(value!=0) count++; + widest=Math.max(widest,count); + } + for(int y=2;y region=new ArrayList<>(); + ArrayDeque pending=new ArrayDeque<>(); + pending.add(new int[]{column,row}); + boolean outside=false; + while(!pending.isEmpty()) { + int[] at=pending.removeFirst();int x=at[0],y=at[1]; + if(x<0||x>=width||y<0||y>=height||visited[y][x]||source[y][x]!=0) continue; + visited[y][x]=true;region.add(at); + // Openings may meet the sill, as a doorway does, but need both jambs and a head. + if(x==0||x==width-1||y==height-1) outside=true; + pending.add(new int[]{x-1,y});pending.add(new int[]{x+1,y}); + pending.add(new int[]{x,y-1});pending.add(new int[]{x,y+1}); + } + if(!outside) for(int[] at:region) result[at[1]][at[0]]=OPENING; + } + return result; + } + + private static void extendBrace(int[][] cells,int[][] result,int x,int y,boolean rising) { + for(int direction:new int[]{-1,1}) for(int step=1;step=cells.length||column<0||column>=cells[0].length||cells[row][column]!=WALL) break; + int code=result[row][column]; + result[row][column]|=rising?RISE:FALL; + int horizontalEnd=direction>0?RIGHT:LEFT; + int verticalEnd=(rising?direction:-direction)>0?TOP:BOTTOM; + if((code&(horizontalEnd|verticalEnd))!=0) break; + } + } + + private static void storey(int[][] cells,int[][] result,int base,int end) { + int width=cells[0].length; + SortedSet anchors=new TreeSet<>(); + anchors.add(0);anchors.add(width); + int signature=width*31+(end-base); + for(int y=base;y required=new ArrayList<>(anchors), posts=new ArrayList<>(); + for(int a=1;a1) { + for(int y=base;y=3&&clearHeight>=3&&Math.floorMod(signature+i,3)==0) + diagonal(result,left,right,base,reach,!rising); + } + } + + private static void diagonal(int[][] result,int left,int right,int base,int reach,boolean rising) { + for(int step=0;step LAYOUT=new IUnlistedProperty() { + public String getName(){return "smart_layout";} + public boolean isValid(Integer value){return value!=null;} + public Class getType(){return Integer.class;} + public String valueToString(Integer value){return value.toString();} + }; + /** 0 full, 1 half, 2 two joined half panels. */ + public final int profile; + + public SmartWallBlock(String wood,int profile) { + super(wood,FrameFamily.POST);this.profile=profile; + useNeighborBrightness=profile!=0; + } + @Override public void onBlockAdded(World world,BlockPos pos,IBlockState state){SmartWallEvents.changed(world,pos);} + @Override public void breakBlock(World world,BlockPos pos,IBlockState state){ + SmartWallEvents.changed(world,pos);super.breakBlock(world,pos,state); + } + @Override public int variants(){return 2;} + @Override public int finish(int panel){return panel&1;} + /** Full walls use bit 3 for the L footprint; the old low-eight states stay unchanged. */ + public int profile(IBlockState state) {return profile==0&&state.getValue(PANEL)>=2?2:profile;} + public IBlockState state(int shape,EnumFacing facing,int finish) { + if(shape!=profile && !(profile==0&&shape==2)) throw new IllegalArgumentException("Wrong smart wall profile"); + return getDefaultState().withProperty(FACING,facing).withProperty(PANEL,(finish&1)+(profile==0&&shape==2?2:0)); + } + @Override public IBlockState getStateFromMeta(int meta) { + return getDefaultState().withProperty(FACING,EnumFacing.getHorizontal(meta&3)) + .withProperty(PANEL,(meta>>2)&(profile==0?3:1)); + } + @Override public int damageDropped(IBlockState state) {return finish(state.getValue(PANEL));} + @Override protected BlockStateContainer createBlockState() { + return new ExtendedBlockState(this,new IProperty[]{FACING,PANEL}, + new IUnlistedProperty[]{CONNECTIONS,TimberCorners.PROPERTY,LAYOUT}); + } + public int mask(IBlockState state){return SmartWallGeometry.mask(profile(state),state.getValue(FACING));} + @Override public IBlockState getStateForPlacement(World world,BlockPos pos,EnumFacing side,float x,float y,float z,int meta,EntityLivingBase player) { + EnumFacing facing=side.getAxis().isHorizontal()?side.getOpposite():player.getHorizontalFacing().getOpposite(); + if(profile==1&&!player.isSneaking()) { + EnumFacing inherited=WallRuns.straightHalfFacing(world.getBlockState(pos.offset(side.getOpposite()))); + if(inherited!=null) facing=inherited; + } + return getDefaultState().withProperty(FACING,facing).withProperty(PANEL,meta&1); + } + @Override public IBlockState getExtendedState(IBlockState state,IBlockAccess world,BlockPos pos) { + return ((IExtendedBlockState)state).withProperty(CONNECTIONS,SmartWallGeometry.coverage(world,pos)).withProperty(TimberCorners.PROPERTY,0) + .withProperty(LAYOUT,IronAgeArchitecture.proxy.smartLayout(world,pos)); + } + @Override public IBlockState withMirror(IBlockState state,net.minecraft.util.Mirror mirror) { + if(profile(state)!=2||mirror==net.minecraft.util.Mirror.NONE) return state.withProperty(FACING,mirror.mirror(state.getValue(FACING))); + int original=mask(state),target=0; + for(int q=0;q<4;q++) if((original&(1< parts(IBlockState state,int code){ + return SmartWallGeometry.parts(mask(state),code==0?SmartWallGeometry.FALLBACK:code,0); + } + @Override public List parts(IBlockState state,int code,int coverage){ + return SmartWallGeometry.parts(mask(state),code==0?SmartWallGeometry.FALLBACK:code,coverage); + } + @Nullable @Override public AxisAlignedBB getCollisionBoundingBox(IBlockState state,World world,BlockPos pos){return NULL_AABB;} + @Override public boolean isFullCube(IBlockState state){return profile(state)==0;} + @Override public boolean getUseNeighborBrightness(IBlockState state){return profile(state)!=0;} + @Override public boolean isSideSolid(IBlockState state,IBlockAccess world,BlockPos pos,EnumFacing side) { + if(profile(state)==0) return true; + if(!side.getAxis().isHorizontal()) return false; + int mask=mask(state); + for(AxisAlignedBB box:SmartWallGeometry.boxes(mask)) { + if(side==EnumFacing.NORTH&&box.minZ==0||side==EnumFacing.SOUTH&&box.maxZ==1 + ||side==EnumFacing.WEST&&box.minX==0||side==EnumFacing.EAST&&box.maxX==1) { + int required=side==EnumFacing.NORTH?3:side==EnumFacing.SOUTH?12:side==EnumFacing.WEST?5:10; + return (mask&required)==required; + } + } + return false; + } + @Override public AxisAlignedBB getBoundingBox(IBlockState state,IBlockAccess world,BlockPos pos){ + return WindowBlock.union(SmartWallGeometry.boxes(mask(state))); + } + @Override public void addCollisionBoxToList(IBlockState state,World world,BlockPos pos,AxisAlignedBB query, + List boxes,@Nullable Entity entity) { + for(AxisAlignedBB box:SmartWallGeometry.boxes(mask(state))) addCollisionBoxToList(pos,query,boxes,box); + } + @Nullable @Override public RayTraceResult collisionRayTrace(IBlockState state,World world,BlockPos pos,Vec3d start,Vec3d end) { + RayTraceResult closest=null; + for(AxisAlignedBB box:SmartWallGeometry.boxes(mask(state))) { + RayTraceResult hit=rayTrace(pos,start,end,box); + if(hit!=null&&(closest==null||start.squareDistanceTo(hit.hitVec) getDrops(IBlockAccess world,BlockPos pos,IBlockState state,int fortune) { + return Collections.singletonList(getSilkTouchDrop(state)); + } + @Override protected ItemStack getSilkTouchDrop(IBlockState state) { + int shape=profile(state); + return new ItemStack(ArchitectureBlocks.findSmart(wood,shape==2?1:shape),shape==2?2:1,finish(state.getValue(PANEL))); + } + @Override public ItemStack getPickBlock(IBlockState state,RayTraceResult hit,World world,BlockPos pos,EntityPlayer player) { + int shape=profile(state); + return new ItemStack(ArchitectureBlocks.findSmart(wood,shape==2?1:shape),1,finish(state.getValue(PANEL))); + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallData.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallData.java new file mode 100644 index 0000000..15def1c --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallData.java @@ -0,0 +1,69 @@ +package zone.moddev.mc.ironagearchitecture.block; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.WorldSavedData; + +/** Saved framing, indexed by chunk. Reads from render workers never touch the world. */ +public final class SmartWallData extends WorldSavedData { + private static final String NAME="ironagearchitecture_smart_walls_v1"; + private final Map> chunks=new ConcurrentHashMap<>(); + + public SmartWallData() { super(NAME); } + public SmartWallData(String name) { super(name); } + + public static SmartWallData get(World world) { + SmartWallData data=(SmartWallData)world.getPerWorldStorage().getOrLoadData(SmartWallData.class,NAME); + if(data==null) {data=new SmartWallData();world.getPerWorldStorage().setData(NAME,data);} + return data; + } + + public static long chunkKey(int x,int z) { return ((long)x<<32)|(z&0xffffffffL); } + private static long chunkKey(BlockPos pos) { return chunkKey(pos.getX()>>4,pos.getZ()>>4); } + + public int code(BlockPos pos) { return chunk(pos.getX()>>4,pos.getZ()>>4).getOrDefault(pos.toLong(),0); } + public Map chunk(int x,int z) { + Map values=chunks.get(chunkKey(x,z)); + return values==null?Collections.emptyMap():values; + } + public void clearChunk(int x,int z) { chunks.remove(chunkKey(x,z)); } + + public boolean put(BlockPos pos,int code) { + long key=chunkKey(pos), at=pos.toLong(); + if(code==0) { + Map values=chunks.get(key); + if(values==null||values.remove(at)==null) return false; + } else { + Map values=chunks.computeIfAbsent(key,k->new ConcurrentHashMap<>()); + Integer old=values.put(at,code); + if(old!=null&&old==code) return false; + } + markDirty(); + return true; + } + + @Override public void readFromNBT(NBTTagCompound root) { + chunks.clear(); + NBTTagList list=root.getTagList("cells",10); + for(int i=0;i=0&&pos.getY()<256) put(pos,entry.getInteger("c")); + } + setDirty(false); + } + + @Override public NBTTagCompound writeToNBT(NBTTagCompound root) { + NBTTagList list=new NBTTagList(); + for(Map chunk:chunks.values()) for(Map.Entry cell:chunk.entrySet()) { + NBTTagCompound entry=new NBTTagCompound(); + entry.setLong("p",cell.getKey());entry.setInteger("c",cell.getValue());list.appendTag(entry); + } + root.setTag("cells",list); + return root; + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallEvents.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallEvents.java new file mode 100644 index 0000000..bbfd567 --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallEvents.java @@ -0,0 +1,125 @@ +package zone.moddev.mc.ironagearchitecture.block; + +import java.util.*; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.*; +import net.minecraft.world.*; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; +import net.minecraftforge.event.world.*; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; +import zone.moddev.mc.ironagearchitecture.SmartWallNetwork; + +/** Coalesced server work, bounded per tick. Idle walls are read directly from their saved layout. */ +public final class SmartWallEvents { + private static final Map> DIRTY=new WeakHashMap<>(); + private static final Map>> WATCHERS=new WeakHashMap<>(); + + public static void changed(World world,BlockPos pos) { + if(world.isRemote) return; + SmartWallData data=SmartWallData.get(world); + LinkedHashSet queue=DIRTY.computeIfAbsent(world,w->new LinkedHashSet<>()); + if(world.getBlockState(pos).getBlock() instanceof SmartWallBlock) { + if(data.code(pos)==0) data.put(pos,SmartWallGeometry.FALLBACK); + queue.add(pos.toImmutable()); + } else if(data.put(pos,0)) send(world,pos,Collections.singletonMap(pos.toLong(),0),false); + for(int x=-2;x<=2;x++) for(int y=-2;y<=2;y++) for(int z=-2;z<=2;z++) { + BlockPos at=pos.add(x,y,z); + if(data.code(at)!=0||(Math.abs(x)<=1&&Math.abs(y)<=1&&Math.abs(z)<=1&&world.isBlockLoaded(at) + &&world.getBlockState(at).getBlock() instanceof SmartWallBlock)) queue.add(at); + } + } + + public static boolean pending(World world){return DIRTY.containsKey(world)&&!DIRTY.get(world).isEmpty();} + public static int flush(World world) { + LinkedHashSet queue=DIRTY.get(world); + if(queue==null||queue.isEmpty()) return 0; + SmartWallData data=SmartWallData.get(world); + Set visited=new HashSet<>(); + Map> changed=new HashMap<>(); + int work=0, seeds=0; + while(!queue.isEmpty()&&work<2048&&seeds++<512) { + Iterator iterator=queue.iterator();BlockPos seed=iterator.next();iterator.remove(); + if(!world.isBlockLoaded(seed)||!(world.getBlockState(seed).getBlock() instanceof SmartWallBlock)) continue; + for(EnumFacing side:EnumFacing.HORIZONTALS) { + if(visited.contains(seed.toLong()+"/"+side)) continue; + SmartWallPlanner.Plan plan=SmartWallPlanner.survey(world,seed,side); + if(plan==null) continue; + work+=plan.cells.size(); + for(Map.Entry entry:plan.cells.entrySet()) { + BlockPos at=entry.getKey(); + visited.add(at.toLong()+"/"+side); + int shift=side.getHorizontalIndex()*8; + int next=(data.code(at)&~(255<>4,at.getZ()>>4),k->new HashMap<>()).put(at.toLong(),next); + } + } + } + for(Map.Entry> entry:changed.entrySet()) { + int x=(int)(entry.getKey()>>32),z=(int)(long)entry.getKey(); + send(world,new BlockPos(x*16,0,z*16),entry.getValue(),false); + } + return work; + } + + private static void send(World world,BlockPos at,Map values,boolean replace) { + int x=at.getX()>>4,z=at.getZ()>>4; + Set players=WATCHERS.getOrDefault(world,Collections.emptyMap()).get(SmartWallData.chunkKey(x,z)); + if(players!=null) for(EntityPlayerMP player:new ArrayList<>(players)) + SmartWallNetwork.send(player,world.provider.getDimension(),x,z,replace,values); + } + + @SubscribeEvent public void loaded(WorldEvent.Load event) { + if(event.getWorld().isRemote) return; + event.getWorld().addEventListener(new Listener()); + } + @SubscribeEvent public void tick(TickEvent.WorldTickEvent event) { + if(event.phase==TickEvent.Phase.END&&!event.world.isRemote) flush(event.world); + } + @SubscribeEvent public void chunkLoaded(ChunkEvent.Load event) { + if(!(event.getWorld() instanceof WorldServer)) return; + WorldServer world=(WorldServer)event.getWorld();Chunk chunk=event.getChunk(); + world.addScheduledTask(()->{ + if(!world.isBlockLoaded(new BlockPos(chunk.xPosition*16,64,chunk.zPosition*16))) return; + for(ExtendedBlockStorage section:chunk.getBlockStorageArray()) if(section!=null&&!section.isEmpty()) + for(int x=0;x<16;x++) for(int z=0;z<16;z++) for(int y=0;y<16;y++) + if(section.get(x,y,z).getBlock() instanceof SmartWallBlock) + changed(world,new BlockPos(chunk.xPosition*16+x,section.getYLocation()+y,chunk.zPosition*16+z)); + SmartWallData data=SmartWallData.get(world); + for(int dx=-1;dx<=1;dx++) for(int dz=-1;dz<=1;dz++) + for(long pos:data.chunk(chunk.xPosition+dx,chunk.zPosition+dz).keySet()) + DIRTY.computeIfAbsent(world,w->new LinkedHashSet<>()).add(BlockPos.fromLong(pos)); + }); + } + @SubscribeEvent public void watch(ChunkWatchEvent.Watch event) { + World world=event.getPlayer().world; + int x=event.getChunk().chunkXPos,z=event.getChunk().chunkZPos; + WATCHERS.computeIfAbsent(world,w->new HashMap<>()).computeIfAbsent(SmartWallData.chunkKey(x,z),k->new HashSet<>()).add(event.getPlayer()); + SmartWallNetwork.send(event.getPlayer(),world.provider.getDimension(),x,z,true,SmartWallData.get(world).chunk(x,z)); + } + @SubscribeEvent public void unwatch(ChunkWatchEvent.UnWatch event) { + Map> chunks=WATCHERS.get(event.getPlayer().world); + if(chunks==null) return; + long key=SmartWallData.chunkKey(event.getChunk().chunkXPos,event.getChunk().chunkZPos); + Set players=chunks.get(key); + if(players!=null){players.remove(event.getPlayer());if(players.isEmpty())chunks.remove(key);} + } + @SubscribeEvent public void unloaded(WorldEvent.Unload event){DIRTY.remove(event.getWorld());WATCHERS.remove(event.getWorld());} + + private static final class Listener implements IWorldEventListener { + public void notifyBlockUpdate(World w,BlockPos p,IBlockState old,IBlockState next,int flags){changed(w,p);} + public void notifyLightSet(BlockPos pos){} + public void markBlockRangeForRenderUpdate(int a,int b,int c,int d,int e,int f){} + public void playSoundToAllNearExcept(net.minecraft.entity.player.EntityPlayer p,net.minecraft.util.SoundEvent s,net.minecraft.util.SoundCategory c,double x,double y,double z,float v,float pitch){} + public void playRecord(net.minecraft.util.SoundEvent sound,BlockPos pos){} + public void spawnParticle(int id,boolean ignore,double x,double y,double z,double vx,double vy,double vz,int...data){} + public void onEntityAdded(net.minecraft.entity.Entity entity){} + public void onEntityRemoved(net.minecraft.entity.Entity entity){} + public void broadcastSound(int id,BlockPos pos,int data){} + public void playEvent(net.minecraft.entity.player.EntityPlayer p,int type,BlockPos pos,int data){} + public void sendBlockBreakProgress(int id,BlockPos pos,int progress){} + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallGeometry.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallGeometry.java new file mode 100644 index 0000000..1037ef0 --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallGeometry.java @@ -0,0 +1,132 @@ +package zone.moddev.mc.ironagearchitecture.block; + +import java.util.*; +import net.minecraft.util.EnumFacing; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.util.math.AxisAlignedBB; +import zone.moddev.mc.ironagearchitecture.block.WallGeometry.Grain; +import zone.moddev.mc.ironagearchitecture.block.WallGeometry.Part; + +/** Fixed half-grid occupancy and face-local timbers share the ordinary clipping path. */ +public final class SmartWallGeometry { + public static final int FALLBACK=0x9b9b9b9b; + private SmartWallGeometry() { } + + public static int mask(SmartWallBlock block,EnumFacing facing) { + return mask(block.profile,facing); + } + public static int mask(int profile,EnumFacing facing) { + int source=profile==0?15:profile==1?3:7, result=0; + for(int q=0;q<4;q++) if((source&(1< boxes(int mask) { + List result=new ArrayList<>(); + for(int q=0;q<4;q++) if((mask&(1<>>(side.getHorizontalIndex()*8)&255;} + + public static List parts(int mask,int code,int coverage) { + List result=new ArrayList<>(); + for(int q=0;q<4;q++) if((mask&(1<0) beam(result,low,0,low+1.5,16,depth,side,Grain.VERTICAL); + if((pattern&SmartFacade.RIGHT)!=0||high<16) beam(result,high-1.5,0,high,16,depth,side,Grain.VERTICAL); + if((pattern&SmartFacade.CENTRE)!=0) beam(result,Math.max(low,7.2),0,Math.min(high,8.8),16,depth,side,Grain.VERTICAL); + if((pattern&SmartFacade.BOTTOM)!=0) beam(result,low,0,high,1.5,depth,side,Grain.EAST_WEST); + if((pattern&SmartFacade.TOP)!=0) beam(result,low,14.5,high,16,depth,side,Grain.EAST_WEST); + if((pattern&SmartFacade.RISE)!=0) brace(result,low,high,depth,side,true); + if((pattern&SmartFacade.FALL)!=0) brace(result,low,high,depth,side,false); + } + } + return result; + } + + public static int coverage(IBlockAccess world,BlockPos pos) { + int result=0; + for(EnumFacing side:EnumFacing.HORIZONTALS) { + BlockPos next=pos.offset(side); + if(world instanceof net.minecraft.world.World&&!((net.minecraft.world.World)world).isBlockLoaded(next)) continue; + result|=footprint(world.getBlockState(next))<<(side.getHorizontalIndex()*4); + } + return result; + } + + private static int footprint(IBlockState state) { + if(state.getBlock() instanceof SmartWallBlock) return ((SmartWallBlock)state.getBlock()).mask(state); + if(state.getBlock() instanceof PartialWallBlock) { + PartialWallBlock wall=(PartialWallBlock)state.getBlock(); + int result=0; + EnumFacing facing=state.getValue(FrameBlock.FACING); + for(int q=0;q<4;q++) { + double[] point=WallGeometry.rotate(q%2*8+4,0,q/2*8+4,facing==EnumFacing.EAST?EnumFacing.WEST:facing==EnumFacing.WEST?EnumFacing.EAST:facing); + if(wall.profile.contains(point[0],point[2])) result|=1<=0&&nextX<2&&nextZ>=0&&nextZ<2) return false; + int neighbour=(coverage>>>(side.getHorizontalIndex()*4))&15; + return (neighbour&(1<<(Math.floorMod(nextX,2)+Math.floorMod(nextZ,2)*2)))!=0; + } + + private static List strips(int mask,EnumFacing side,int coverage) { + List result=new ArrayList<>(); + EnumFacing inverse=side==EnumFacing.EAST?EnumFacing.WEST:side==EnumFacing.WEST?EnumFacing.EAST:side; + for(int q=0;q<4;q++) if((mask&(1<=0&&x<2&&z>=0&&z<2&&(mask&(1<<(x+z*2)))!=0;} + private static void beam(List list,double x0,double y0,double x1,double y1,double depth,EnumFacing side,Grain grain) { + if(x1>x0) list.add(new Part(new double[][]{{x0,y0},{x1,y0},{x1,y1},{x0,y1}},depth,depth+1.25,grain,side)); + } + private static void brace(List list,double low,double high,double depth,EnumFacing side,boolean rising) { + double w=1.15; + double[][] outline={{0,0},{w,0},{16,16-w},{16,16},{16-w,16},{0,w}}; + if(!rising) { + double[][] reflected=new double[outline.length][]; + for(int i=0;i=3) list.add(new Part(outline,depth+(rising?.25:.375),depth+(rising?1.15:1.1),rising?Grain.RISING:Grain.FALLING,side)); + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallItem.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallItem.java new file mode 100644 index 0000000..515cf8f --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallItem.java @@ -0,0 +1,59 @@ +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.*; +import net.minecraft.creativetab.CreativeTabs; +import net.minecraft.util.*; +import net.minecraft.util.math.*; +import net.minecraft.world.World; +import zone.moddev.mc.ironagearchitecture.*; + +public final class SmartWallItem extends EraItemBlock { + private final SmartWallBlock wall; + public SmartWallItem(SmartWallBlock wall){super(wall);this.wall=wall;setHasSubtypes(true);setMaxDamage(0);} + @Override public int getMetadata(int damage){return damage&1;} + @Override public String getUnlocalizedName(ItemStack stack){return super.getUnlocalizedName()+"."+(stack.getMetadata()&1);} + @Override public void getSubItems(Item item,CreativeTabs tab,List list){ + if(ArchitectureConfig.buildingMode.automatic()) for(int finish=0;finish<2;finish++) list.add(new ItemStack(item,1,finish)); + } + @Override public EnumActionResult onItemUse(ItemStack stack,EntityPlayer player,World world,BlockPos pos, + EnumHand hand,EnumFacing side,float x,float y,float z) { + IBlockState old=world.getBlockState(pos); + if(player.isSneaking()&&wall.profile==1&&old.getBlock() instanceof SmartWallBlock&&side.getAxis().isHorizontal()) { + SmartWallBlock previous=(SmartWallBlock)old.getBlock(); + if(previous.profile(old)==1&&previous.wood.equals(wall.wood)&&previous.finish(old.getValue(FrameBlock.PANEL))==getMetadata(stack.getMetadata())) { + int merged=previous.mask(old)|SmartWallGeometry.mask(wall,side.getOpposite()); + int count=Integer.bitCount(merged); + if(count==3||count==4) { + int shape=count==3?2:0; + SmartWallBlock target=ArchitectureBlocks.findSmart(wall.wood,shape); + for(EnumFacing facing:EnumFacing.HORIZONTALS) if(SmartWallGeometry.mask(shape,facing)==merged) { + IBlockState next=target.state(shape,facing,getMetadata(stack.getMetadata())); + if(!player.canPlayerEdit(pos,side,stack)||!world.isBlockModifiable(player,pos)) return EnumActionResult.FAIL; + for(AxisAlignedBB box:SmartWallGeometry.boxes(merged)) + if(!world.checkNoEntityCollision(box.offset(pos))) return EnumActionResult.FAIL; + if(!world.isRemote) { + if(!world.setBlockState(pos,next,3)) return EnumActionResult.FAIL; + if(!player.capabilities.isCreativeMode) stack.stackSize--; + } + return EnumActionResult.SUCCESS; + } + } + } + } + return super.onItemUse(stack,player,world,pos,hand,side,x,y,z); + } + @Override public boolean placeBlockAt(ItemStack stack,EntityPlayer player,World world,BlockPos pos, + EnumFacing side,float x,float y,float z,IBlockState state) { + for(AxisAlignedBB box:SmartWallGeometry.boxes(wall.mask(state))) if(!world.checkNoEntityCollision(box.offset(pos))) return false; + return super.placeBlockAt(stack,player,world,pos,side,x,y,z,state); + } + @Override public void addInformation(ItemStack stack,EntityPlayer player,List tooltip,boolean advanced){ + hint(tooltip,"smart.place"); + if(wall.profile==1) hint(tooltip,"smart.corner"); + hint(tooltip,ArchitectureConfig.buildingMode==BuildingMode.AUTOMATIC?"smart.craft.auto":"smart.craft"); + if((stack.getMetadata()&1)==0) hint(tooltip,"wash"); + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallPlanner.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallPlanner.java new file mode 100644 index 0000000..a533d7a --- /dev/null +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SmartWallPlanner.java @@ -0,0 +1,119 @@ +package zone.moddev.mc.ironagearchitecture.block; + +import java.util.*; +import net.minecraft.block.BlockDoor; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.*; +import net.minecraft.world.World; +import zone.moddev.mc.ironagearchitecture.ArchitectureConfig; + +/** Loaded, coplanar wall sections. A missing chunk is unknown, never a facade edge. */ +public final class SmartWallPlanner { + public static final int MAX_CELLS=2048, MAX_WIDTH=48, MAX_HEIGHT=32; + private SmartWallPlanner() { } + + public static final class Plan { + public final Map cells; + public final EnumFacing normal; + Plan(Map cells,EnumFacing normal){this.cells=cells;this.normal=normal;} + } + + public static Plan survey(World world,BlockPos seed,EnumFacing normal) { + if(!world.isBlockLoaded(seed)) return null; + IBlockState source=world.getBlockState(seed); + if(!(source.getBlock() instanceof SmartWallBlock)) return null; + SmartWallBlock smart=(SmartWallBlock)source.getBlock(); + double plane=plane(source,world,seed,normal); + ArrayDeque pending=new ArrayDeque<>(); + Set visited=new HashSet<>(); + Map kinds=new HashMap<>(); + pending.add(seed); + EnumFacing tangent=normal.getAxis()==EnumFacing.Axis.Z?EnumFacing.EAST:EnumFacing.SOUTH; + int minU=coordinate(seed,normal),maxU=minU,minY=seed.getY(),maxY=minY; + while(!pending.isEmpty()) { + BlockPos at=pending.removeFirst(); + if(!visited.add(at)||at.getY()<0||at.getY()>=256) continue; + if(!world.isBlockLoaded(at)) return null; + int kind=kind(world,at,normal,plane,smart.wood); + if(kind==0) continue; + kinds.put(at,kind); + int u=coordinate(at,normal); + minU=Math.min(minU,u);maxU=Math.max(maxU,u);minY=Math.min(minY,at.getY());maxY=Math.max(maxY,at.getY()); + if(kinds.size()>MAX_CELLS||maxU-minU>=MAX_WIDTH||maxY-minY>=MAX_HEIGHT) return null; + pending.add(at.offset(tangent));pending.add(at.offset(tangent.getOpposite())); + pending.add(at.up());pending.add(at.down()); + } + int[][] grid=new int[maxY-minY+1][maxU-minU+1]; + for(Map.Entry entry:kinds.entrySet()) + grid[entry.getKey().getY()-minY][coordinate(entry.getKey(),normal)-minU]=entry.getValue(); + int[][] framing=SmartFacade.arrange(grid); + Map result=new HashMap<>(); + for(BlockPos pos:kinds.keySet()) { + IBlockState state=world.getBlockState(pos); + if(!(state.getBlock() instanceof SmartWallBlock)) continue; + int code=framing[pos.getY()-minY][coordinate(pos,normal)-minU]; + if(normal==EnumFacing.SOUTH||normal==EnumFacing.WEST) + code=(code&~(SmartFacade.LEFT|SmartFacade.RIGHT|SmartFacade.RISE|SmartFacade.FALL)) + |((code&SmartFacade.LEFT)<<1)|((code&SmartFacade.RIGHT)>>1) + |((code&SmartFacade.RISE)<<1)|((code&SmartFacade.FALL)>>1); + BlockPos outside=pos.offset(normal); + if(!world.isBlockLoaded(outside)) return null; + result.put(pos,code); + } + return new Plan(result,normal); + } + + private static int coordinate(BlockPos pos,EnumFacing side){return side.getAxis()==EnumFacing.Axis.Z?pos.getX():pos.getZ();} + private static double plane(IBlockState state,World world,BlockPos pos,EnumFacing side) { + AxisAlignedBB bounds; + if(state.getBlock() instanceof FrameBlock&&!(state.getBlock() instanceof SmartWallBlock)) { + if(state.getBlock() instanceof PartialWallBlock) { + WallProfile profile=((PartialWallBlock)state.getBlock()).profile; + EnumFacing facing=state.getValue(FrameBlock.FACING); + double[] a=WallGeometry.rotate(profile.x0,0,profile.z0,facing),b=WallGeometry.rotate(profile.x1,16,profile.z1,facing); + bounds=new AxisAlignedBB(Math.min(a[0],b[0])/16,0,Math.min(a[2],b[2])/16,Math.max(a[0],b[0])/16,1,Math.max(a[2],b[2])/16); + } else bounds=new AxisAlignedBB(0,0,0,1,1,1); + } else bounds=state.getBoundingBox(world,pos); + return side==EnumFacing.NORTH?bounds.minZ:side==EnumFacing.SOUTH?bounds.maxZ:side==EnumFacing.WEST?bounds.minX:bounds.maxX; + } + private static int kind(World world,BlockPos pos,EnumFacing normal,double plane,String wood) { + IBlockState state=world.getBlockState(pos); + if(state.getBlock() instanceof SmartWallBlock) { + SmartWallBlock block=(SmartWallBlock)state.getBlock(); + return block.wood.equals(wood)&&Math.abs(plane(state,world,pos,normal)-plane)<.001?SmartFacade.WALL:0; + } + if(state.getBlock() instanceof WindowBlock) { + WindowBlock window=(WindowBlock)state.getBlock(); + if(window.wood.equals(wood)&&state.getValue(WindowBlock.FACING).getAxis()==normal.getAxis()) { + double depth=window.thin?(window.back?8:0):0; + double back=window.thin?depth+8:16; + double[] a=WallGeometry.rotate(0,0,depth,state.getValue(WindowBlock.FACING)); + double[] z=WallGeometry.rotate(16,0,back,state.getValue(WindowBlock.FACING)); + double edge=normal==EnumFacing.NORTH?Math.min(a[2],z[2])/16:normal==EnumFacing.SOUTH?Math.max(a[2],z[2])/16 + :normal==EnumFacing.WEST?Math.min(a[0],z[0])/16:Math.max(a[0],z[0])/16; + return Math.abs(edge-plane)<.001?SmartFacade.OPENING:0; + } + } + if(state.getBlock() instanceof BlockDoor) + return SmartFacade.OPENING; + if(state.getBlock() instanceof FrameBlock&&!(state.getBlock() instanceof JettyFloorBlock)) { + FrameBlock block=(FrameBlock)state.getBlock(); + if(block.wood!=null&&!wood.equals(block.wood)||state.getValue(FrameBlock.FACING).getAxis()!=normal.getAxis() + ||Math.abs(plane(state,world,pos,normal)-plane)>.08) return 0; + if(ArchitectureConfig.buildingMode==zone.moddev.mc.ironagearchitecture.BuildingMode.HYBRID) { + if(block.family==FrameFamily.POST) return (state.getValue(FrameBlock.PANEL)&1)==0?SmartFacade.POST:SmartFacade.CROSS; + if(block.family==FrameFamily.TERMINAL) return SmartFacade.POST; + if(block.family==FrameFamily.RAIL) return (state.getValue(FrameBlock.PANEL)&1)==0?SmartFacade.RAIL:SmartFacade.HEAD; + if(block.family==FrameFamily.BRACE) { + boolean rising=(state.getValue(FrameBlock.PANEL)&1)==0; + EnumFacing facing=state.getValue(FrameBlock.FACING); + if(facing==EnumFacing.SOUTH||facing==EnumFacing.WEST) rising=!rising; + return rising?SmartFacade.RISING:SmartFacade.FALLING; + } + } + return SmartFacade.WALL; + } + return 0; + } +} diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SurfaceGeometry.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SurfaceGeometry.java index c13111a..6ced34a 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/block/SurfaceGeometry.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/SurfaceGeometry.java @@ -112,16 +112,75 @@ private static List subtract(double[][] face,double[] box,boolean ow } return visible; } + private static double dot(double[] a,double[] b) {return a[0]*b[0]+a[1]*b[1]+a[2]*b[2];} + /** Convex cover planes in the owner's texture coordinates: outward normal and offset. */ + private static List planes(Part owner,Part cover,EnumFacing facing) { + double x=0,y=0; + for(double[] p:cover.outline) {x+=p[0];y+=p[1];} + double[] world=cover.transform(x/cover.outline.length,y/cover.outline.length,(cover.front+cover.back)/2,facing); + double[] centre=owner.inverse(world[0],world[1],world[2],facing); + List planes=new ArrayList<>(); + for(double[][] face:raw(cover)) { + double[][] local=new double[face.length][]; + for(int i=0;iedge) {for(int axis=0;axis<3;axis++) n[axis]=-n[axis];edge=-edge;} + planes.add(new double[]{n[0],n[1],n[2],edge}); + } + return planes; + } + private static double[][] clip(double[][] polygon,double[] plane,boolean inside) { + if(polygon.length==0) return polygon; + List out=new ArrayList<>();double[] previous=polygon[polygon.length-1]; + double before=plane[3]-dot(plane,previous);boolean was=inside?before>=-EPS:before<-EPS; + for(double[] current:polygon) { + double after=plane[3]-dot(plane,current);boolean now=inside?after>=-EPS:after<-EPS; + if(was!=now) { + double t=(-EPS-before)/(after-before);double[] cut=new double[3]; + for(int axis=0;axis<3;axis++) cut[axis]=previous[axis]+t*(current[axis]-previous[axis]); + out.add(cut); + } + if(now) out.add(current); + previous=current;before=after;was=now; + } + return clean(out); + } + private static List subtract(double[][] face,List planes,boolean ownerWins) { + double[] n=normal(face); + for(double[] plane:planes) { + boolean outside=true; + for(double[] p:face) if(plane[3]-dot(plane,p)>=-EPS) {outside=false;break;} + if(outside) return Collections.singletonList(face); + if(ownerWins&&dot(n,plane)>.999999&&Math.abs(dot(plane,face[0])-plane[3]) visible=new ArrayList<>();double[][] remaining=face; + for(double[] plane:planes) { + if(remaining.length<3) break; + double[][] outside=clip(remaining,plane,false); + if(area(outside)>1e-6) visible.add(outside); + remaining=clip(remaining,plane,true); + } + return visible; + } public static List faces(List parts,EnumFacing facing) { List result=new ArrayList<>(); for(int i=0;i faces=raw(owner); for(int j=0;j planes=rectangular?null:planes(owner,cover,facing); boolean wins=priority(owner)>priority(cover)||priority(owner)==priority(cover)&&i next=new ArrayList<>(); - for(double[][] face:faces) if(area(face)>1e-6) next.addAll(subtract(face,bounds,wins)); + for(double[][] face:faces) if(area(face)>1e-6) + next.addAll(rectangular?subtract(face,bounds,wins):subtract(face,planes,wins)); faces=next; } for(double[][] face:faces) if(area(face)>1e-6) result.add(new Face(owner,face)); diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/WallRuns.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/WallRuns.java index 4acb6b6..8a1e3f5 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/block/WallRuns.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/WallRuns.java @@ -43,6 +43,16 @@ public static List of(IBlockState state,BlockPos pos) { if (panel.isThin()) add(list, pos, state.getValue(TileHangingBlock.FACING), 0, 16, 0, 8); return list; } + if(state.getBlock() instanceof SmartWallBlock) { + SmartWallBlock smart=(SmartWallBlock)state.getBlock(); + EnumFacing facing=state.getValue(FrameBlock.FACING); + if(smart.profile(state)==0) {add(list,pos,EnumFacing.NORTH,0,16,0,16);add(list,pos,EnumFacing.EAST,0,16,0,16);} + else { + add(list,pos,facing,0,16,0,8); + if(smart.profile(state)==2) add(list,pos,facing.rotateYCCW(),0,16,8,16); + } + return list; + } if(!(state.getBlock() instanceof FrameBlock)||state.getBlock() instanceof JettyFloorBlock) return list; EnumFacing facing=state.getValue(FrameBlock.FACING); if(state.getBlock() instanceof PartialWallBlock) { @@ -73,6 +83,8 @@ public static boolean touches(IBlockState a,BlockPos pa,IBlockState b,BlockPos p /** Normal pointing towards the occupied half, independent of a casement's outward facing. */ static EnumFacing straightHalfFacing(IBlockState state) { + if(state.getBlock() instanceof SmartWallBlock&&((SmartWallBlock)state.getBlock()).profile(state)==1) + return state.getValue(FrameBlock.FACING); if (state.getBlock() instanceof TileHangingBlock && ((TileHangingBlock) state.getBlock()).isThin()) { return state.getValue(TileHangingBlock.FACING); } diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/block/WindowBlock.java b/src/main/java/zone/moddev/mc/ironagearchitecture/block/WindowBlock.java index 65da8f1..1c189ea 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/block/WindowBlock.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/block/WindowBlock.java @@ -64,7 +64,7 @@ RayTraceResult trace(BlockPos pos,Vec3d start,Vec3d end,List boxe if(!world.isRemote) WindowController.toggle(world,pos,player);return true; } private static boolean allowsPlacement(@Nullable ItemStack held) { - return held != null && (held.getItem() instanceof WindowItem || held.getItem() instanceof TileHangingItem); + return held != null && (held.getItem() instanceof WindowItem || held.getItem() instanceof TileHangingItem || held.getItem() instanceof SmartWallItem); } @Override public void neighborChanged(IBlockState state,World world,BlockPos pos,Block changed){if(!world.isRemote) WindowController.neighbor(world,pos);} @Override public void onBlockAdded(World world,BlockPos pos,IBlockState state){if(!world.isRemote) WindowController.neighbor(world,pos);} 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 567fa45..2ba5723 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/client/ClientProxy.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/client/ClientProxy.java @@ -30,7 +30,45 @@ /** Models are registered before baking and never referenced by common classes. */ public final class ClientProxy extends CommonProxy { + private zone.moddev.mc.ironagearchitecture.BuildingMode localBuildingMode; + @SubscribeEvent public void disconnected(net.minecraftforge.fml.common.network.FMLNetworkEvent.ClientDisconnectionFromServerEvent event) { + Minecraft.getMinecraft().addScheduledTask(()->applyBuildingMode(localBuildingMode)); + } + private void applyBuildingMode(zone.moddev.mc.ironagearchitecture.BuildingMode mode) { + if(mode==zone.moddev.mc.ironagearchitecture.ArchitectureConfig.buildingMode) return; + zone.moddev.mc.ironagearchitecture.ArchitectureConfig.buildingMode=mode; + net.minecraft.item.crafting.CraftingManager.getInstance().getRecipeList().removeIf(recipe->{ + net.minecraft.item.ItemStack output=recipe.getRecipeOutput(); + return output!=null&&"ironagearchitecture".equals(output.getItem().getRegistryName().getResourceDomain()); + }); + zone.moddev.mc.ironagearchitecture.ArchitectureRecipes.register(); + } + @SubscribeEvent public void smartChunkUnloaded(net.minecraftforge.event.world.ChunkEvent.Unload event) { + if(event.getWorld().isRemote) + zone.moddev.mc.ironagearchitecture.block.SmartWallData.get(event.getWorld()).clearChunk(event.getChunk().xPosition,event.getChunk().zPosition); + } + @Override public int smartLayout(net.minecraft.world.IBlockAccess access,net.minecraft.util.math.BlockPos pos) { + if(access instanceof net.minecraft.world.World) return super.smartLayout(access,pos); + net.minecraft.world.World world=Minecraft.getMinecraft().world; + return world==null?0:zone.moddev.mc.ironagearchitecture.block.SmartWallData.get(world).code(pos); + } + @Override public void smartSnapshot(zone.moddev.mc.ironagearchitecture.SmartWallNetwork.Packet packet) { + Minecraft.getMinecraft().addScheduledTask(()->{ + net.minecraft.world.World world=Minecraft.getMinecraft().world; + if(world==null||world.provider.getDimension()!=packet.dimension) return; + applyBuildingMode(zone.moddev.mc.ironagearchitecture.BuildingMode.values()[packet.mode]); + zone.moddev.mc.ironagearchitecture.block.SmartWallData data=zone.moddev.mc.ironagearchitecture.block.SmartWallData.get(world); + if(packet.replace) data.clearChunk(packet.x,packet.z); + for(java.util.Map.Entry entry:packet.cells.entrySet()) { + net.minecraft.util.math.BlockPos at=net.minecraft.util.math.BlockPos.fromLong(entry.getKey()); + if((at.getX()>>4)==packet.x&&(at.getZ()>>4)==packet.z) data.put(at,entry.getValue()); + } + world.markBlockRangeForRenderUpdate(packet.x*16-16,0,packet.z*16-16,packet.x*16+31,255,packet.z*16+31); + }); + } @Override public void preInit() { + localBuildingMode=zone.moddev.mc.ironagearchitecture.ArchitectureConfig.buildingMode; + net.minecraftforge.fml.common.FMLCommonHandler.instance().bus().register(this); super.preInit(); MinecraftForge.EVENT_BUS.register(this); for (Block block : ArchitectureBlocks.VISIBLE) { @@ -128,7 +166,10 @@ private static GutterItem[] gutterItems() { } } @SubscribeEvent public void loadWorld(WorldEvent.Load event) { - if(event.getWorld().isRemote) event.getWorld().addEventListener(new WallRenderListener()); + if(event.getWorld().isRemote) { + zone.moddev.mc.ironagearchitecture.block.SmartWallData.get(event.getWorld()); + event.getWorld().addEventListener(new WallRenderListener()); + } } @SuppressWarnings({"rawtypes", "unchecked"}) private static String variant(IBlockState state) { diff --git a/src/main/java/zone/moddev/mc/ironagearchitecture/client/WallBakedModel.java b/src/main/java/zone/moddev/mc/ironagearchitecture/client/WallBakedModel.java index d35787e..8720d2f 100644 --- a/src/main/java/zone/moddev/mc/ironagearchitecture/client/WallBakedModel.java +++ b/src/main/java/zone/moddev/mc/ironagearchitecture/client/WallBakedModel.java @@ -20,6 +20,7 @@ import net.minecraftforge.client.model.pipeline.UnpackedBakedQuad; import net.minecraftforge.common.property.IExtendedBlockState; import zone.moddev.mc.ironagearchitecture.block.FrameBlock; +import zone.moddev.mc.ironagearchitecture.block.SmartWallBlock; import zone.moddev.mc.ironagearchitecture.block.JettyFloorBlock; import zone.moddev.mc.ironagearchitecture.block.SurfaceGeometry; import zone.moddev.mc.ironagearchitecture.block.TimberCorners; @@ -46,11 +47,15 @@ public WallBakedModel(IBakedModel base,FrameBlock block,IBlockState state) { if(side!=null) return Collections.emptyList(); Integer mask=extended instanceof IExtendedBlockState?((IExtendedBlockState)extended).getValue(FrameBlock.CONNECTIONS):null; Integer patch=extended instanceof IExtendedBlockState?((IExtendedBlockState)extended).getValue(TimberCorners.PROPERTY):null; + if(block instanceof SmartWallBlock) { + patch=mask; + mask=extended instanceof IExtendedBlockState?((IExtendedBlockState)extended).getValue(SmartWallBlock.LAYOUT):0; + } int joins=mask==null?0:mask,corners=patch==null?0:patch;long key=(joins&0xffffffffL)|((long)corners<<32); List found=cache.get(key); if(found!=null) return found; List quads=new ArrayList<>(); - for(SurfaceGeometry.Face face:SurfaceGeometry.faces(block.parts(state,joins,corners),state.getValue(FrameBlock.FACING))) { + for(SurfaceGeometry.Face face:SurfaceGeometry.faces(block.parts(state,joins,corners),(block instanceof SmartWallBlock?EnumFacing.NORTH:state.getValue(FrameBlock.FACING)))) { double[][] p=face.points; if(p.length==4) add(quads,face.part,p); else for(int i=1;i quads,Part part,double[][] points) { - EnumFacing facing=state.getValue(BlockHorizontal.FACING); + EnumFacing facing=block instanceof SmartWallBlock?EnumFacing.NORTH:state.getValue(BlockHorizontal.FACING); double[][] positions=new double[4][3]; for(int v=0;v<4;v++) { double[] point=points[part.horizontal?3-v:v]; diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_acacia.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_acacia.json new file mode 100644 index 0000000..bef08ff --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_acacia.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_birch.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_birch.json new file mode 100644 index 0000000..d5910a6 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_birch.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_dark_oak.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_dark_oak.json new file mode 100644 index 0000000..fadfa75 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_dark_oak.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_jungle.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_jungle.json new file mode 100644 index 0000000..570b7c7 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_jungle.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_oak.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_oak.json new file mode 100644 index 0000000..cd35279 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_oak.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_spruce.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_spruce.json new file mode 100644 index 0000000..19df1ea --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_corner_spruce.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_acacia.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_acacia.json new file mode 100644 index 0000000..bef08ff --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_acacia.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_birch.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_birch.json new file mode 100644 index 0000000..d5910a6 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_birch.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_dark_oak.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_dark_oak.json new file mode 100644 index 0000000..fadfa75 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_dark_oak.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_jungle.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_jungle.json new file mode 100644 index 0000000..570b7c7 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_jungle.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_oak.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_oak.json new file mode 100644 index 0000000..cd35279 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_oak.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_spruce.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_spruce.json new file mode 100644 index 0000000..19df1ea --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_full_spruce.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_acacia.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_acacia.json new file mode 100644 index 0000000..bef08ff --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_acacia.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_acacia" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_birch.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_birch.json new file mode 100644 index 0000000..d5910a6 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_birch.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_birch" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_dark_oak.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_dark_oak.json new file mode 100644 index 0000000..fadfa75 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_dark_oak.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_dark_oak" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_jungle.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_jungle.json new file mode 100644 index 0000000..570b7c7 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_jungle.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_jungle" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_oak.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_oak.json new file mode 100644 index 0000000..cd35279 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_oak.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_oak" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_spruce.json b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_spruce.json new file mode 100644 index 0000000..19df1ea --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/blockstates/smart_daub_thin_spruce.json @@ -0,0 +1,52 @@ +{ + "variants": { + "facing=north,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=north,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=east,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=south,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=0": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=1": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=2": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + }, + "facing=west,panel=3": { + "model": "ironagearchitecture:smart_daub_mesh_spruce" + } + } +} diff --git a/src/main/resources/assets/ironagearchitecture/lang/en_GB.lang b/src/main/resources/assets/ironagearchitecture/lang/en_GB.lang index 2f07330..d8a53c4 100644 --- a/src/main/resources/assets/ironagearchitecture/lang/en_GB.lang +++ b/src/main/resources/assets/ironagearchitecture/lang/en_GB.lang @@ -491,6 +491,34 @@ 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 +tile.ironagearchitecture.smart_daub_full_oak.0.name=Oak Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_oak.1.name=Limewashed Oak Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_oak.0.name=Oak Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_oak.1.name=Limewashed Oak Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_spruce.0.name=Spruce Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_spruce.1.name=Limewashed Spruce Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_spruce.0.name=Spruce Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_spruce.1.name=Limewashed Spruce Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_birch.0.name=Birch Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_birch.1.name=Limewashed Birch Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_birch.0.name=Birch Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_birch.1.name=Limewashed Birch Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_jungle.0.name=Jungle Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_jungle.1.name=Limewashed Jungle Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_jungle.0.name=Jungle Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_jungle.1.name=Limewashed Jungle Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_acacia.0.name=Acacia Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_acacia.1.name=Limewashed Acacia Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_acacia.0.name=Acacia Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_acacia.1.name=Limewashed Acacia Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_dark_oak.0.name=Dark Oak Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_dark_oak.1.name=Limewashed Dark Oak Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_dark_oak.0.name=Dark Oak Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_dark_oak.1.name=Limewashed Dark Oak Smart Thin Wattle and Daub +hint.ironagearchitecture.smart.place=Build the wall; its posts, rails and braces follow the facade and openings. +hint.ironagearchitecture.smart.corner=Sneak-use a thin panel on another thin smart wall to form a corner. +hint.ironagearchitecture.smart.craft=Hybrid: matching post panel + stick. Cut and rejoin full and thin panels. +hint.ironagearchitecture.smart.craft.auto=Four matching planks around full daub make four smart full walls. tile.ironagearchitecture.fireplace_brick_hearth.name=Brick Hearth tile.ironagearchitecture.fireplace_brick_firebox.name=Brick Firebox tile.ironagearchitecture.fireplace_brick_jamb.name=Brick Fireplace Jamb diff --git a/src/main/resources/assets/ironagearchitecture/lang/en_US.lang b/src/main/resources/assets/ironagearchitecture/lang/en_US.lang index 2f07330..d8a53c4 100644 --- a/src/main/resources/assets/ironagearchitecture/lang/en_US.lang +++ b/src/main/resources/assets/ironagearchitecture/lang/en_US.lang @@ -491,6 +491,34 @@ 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 +tile.ironagearchitecture.smart_daub_full_oak.0.name=Oak Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_oak.1.name=Limewashed Oak Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_oak.0.name=Oak Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_oak.1.name=Limewashed Oak Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_spruce.0.name=Spruce Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_spruce.1.name=Limewashed Spruce Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_spruce.0.name=Spruce Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_spruce.1.name=Limewashed Spruce Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_birch.0.name=Birch Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_birch.1.name=Limewashed Birch Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_birch.0.name=Birch Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_birch.1.name=Limewashed Birch Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_jungle.0.name=Jungle Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_jungle.1.name=Limewashed Jungle Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_jungle.0.name=Jungle Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_jungle.1.name=Limewashed Jungle Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_acacia.0.name=Acacia Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_acacia.1.name=Limewashed Acacia Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_acacia.0.name=Acacia Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_acacia.1.name=Limewashed Acacia Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_full_dark_oak.0.name=Dark Oak Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_full_dark_oak.1.name=Limewashed Dark Oak Smart Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_dark_oak.0.name=Dark Oak Smart Thin Wattle and Daub +tile.ironagearchitecture.smart_daub_thin_dark_oak.1.name=Limewashed Dark Oak Smart Thin Wattle and Daub +hint.ironagearchitecture.smart.place=Build the wall; its posts, rails and braces follow the facade and openings. +hint.ironagearchitecture.smart.corner=Sneak-use a thin panel on another thin smart wall to form a corner. +hint.ironagearchitecture.smart.craft=Hybrid: matching post panel + stick. Cut and rejoin full and thin panels. +hint.ironagearchitecture.smart.craft.auto=Four matching planks around full daub make four smart full walls. tile.ironagearchitecture.fireplace_brick_hearth.name=Brick Hearth tile.ironagearchitecture.fireplace_brick_firebox.name=Brick Firebox tile.ironagearchitecture.fireplace_brick_jamb.name=Brick Fireplace Jamb diff --git a/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_acacia.json b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_acacia.json new file mode 100644 index 0000000..44b880d --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_acacia.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "ironagearchitecture:blocks/limewashed_cob" + }, + "elements": [] +} diff --git a/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_birch.json b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_birch.json new file mode 100644 index 0000000..44b880d --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_birch.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "ironagearchitecture:blocks/limewashed_cob" + }, + "elements": [] +} diff --git a/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_dark_oak.json b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_dark_oak.json new file mode 100644 index 0000000..44b880d --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_dark_oak.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "ironagearchitecture:blocks/limewashed_cob" + }, + "elements": [] +} diff --git a/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_jungle.json b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_jungle.json new file mode 100644 index 0000000..44b880d --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_jungle.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "ironagearchitecture:blocks/limewashed_cob" + }, + "elements": [] +} diff --git a/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_oak.json b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_oak.json new file mode 100644 index 0000000..44b880d --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_oak.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "ironagearchitecture:blocks/limewashed_cob" + }, + "elements": [] +} diff --git a/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_spruce.json b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_spruce.json new file mode 100644 index 0000000..44b880d --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/block/smart_daub_mesh_spruce.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/block", + "textures": { + "particle": "ironagearchitecture:blocks/limewashed_cob" + }, + "elements": [] +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_acacia_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_acacia_0.json new file mode 100644 index 0000000..c6a5a57 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_acacia_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_acacia" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_acacia_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_acacia_1.json new file mode 100644 index 0000000..c6a5a57 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_acacia_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_acacia" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_birch_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_birch_0.json new file mode 100644 index 0000000..1551d98 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_birch_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_birch" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_birch_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_birch_1.json new file mode 100644 index 0000000..1551d98 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_birch_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_birch" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_dark_oak_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_dark_oak_0.json new file mode 100644 index 0000000..b27e57a --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_dark_oak_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_dark_oak" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_dark_oak_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_dark_oak_1.json new file mode 100644 index 0000000..b27e57a --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_dark_oak_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_dark_oak" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_jungle_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_jungle_0.json new file mode 100644 index 0000000..6cfa413 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_jungle_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_jungle" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_jungle_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_jungle_1.json new file mode 100644 index 0000000..6cfa413 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_jungle_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_jungle" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_oak_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_oak_0.json new file mode 100644 index 0000000..213401f --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_oak_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_oak" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_oak_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_oak_1.json new file mode 100644 index 0000000..213401f --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_oak_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_oak" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_spruce_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_spruce_0.json new file mode 100644 index 0000000..7dbae37 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_spruce_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_spruce" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_spruce_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_spruce_1.json new file mode 100644 index 0000000..7dbae37 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_full_spruce_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_spruce" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_acacia_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_acacia_0.json new file mode 100644 index 0000000..c6a5a57 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_acacia_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_acacia" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_acacia_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_acacia_1.json new file mode 100644 index 0000000..c6a5a57 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_acacia_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_acacia" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_birch_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_birch_0.json new file mode 100644 index 0000000..1551d98 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_birch_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_birch" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_birch_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_birch_1.json new file mode 100644 index 0000000..1551d98 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_birch_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_birch" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_dark_oak_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_dark_oak_0.json new file mode 100644 index 0000000..b27e57a --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_dark_oak_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_dark_oak" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_dark_oak_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_dark_oak_1.json new file mode 100644 index 0000000..b27e57a --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_dark_oak_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_dark_oak" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_jungle_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_jungle_0.json new file mode 100644 index 0000000..6cfa413 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_jungle_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_jungle" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_jungle_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_jungle_1.json new file mode 100644 index 0000000..6cfa413 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_jungle_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_jungle" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_oak_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_oak_0.json new file mode 100644 index 0000000..213401f --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_oak_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_oak" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_oak_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_oak_1.json new file mode 100644 index 0000000..213401f --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_oak_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_oak" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_spruce_0.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_spruce_0.json new file mode 100644 index 0000000..7dbae37 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_spruce_0.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_spruce" +} diff --git a/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_spruce_1.json b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_spruce_1.json new file mode 100644 index 0000000..7dbae37 --- /dev/null +++ b/src/main/resources/assets/ironagearchitecture/models/item/smart_daub_thin_spruce_1.json @@ -0,0 +1,3 @@ +{ + "parent": "ironagearchitecture:block/smart_daub_mesh_spruce" +} 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 1d015f6..1e1bc3e 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.13.110021]") + dependencies="required-after:ironagearchitecture@[0.1.14.110021]") public final class ArchitectureProbe { @SidedProxy(clientSide="zone.moddev.mc.ironagearchitecture.probe.ClientProbe",serverSide="zone.moddev.mc.ironagearchitecture.probe.ProbeProxy") public static ProbeProxy proxy; @@ -55,6 +55,28 @@ public final class ArchitectureProbe { if(!Boolean.getBoolean("iaa.probe")) return; MinecraftServer server=FMLCommonHandler.instance().getMinecraftServerInstance(); WorldServer world=server.worldServerForDimension(0); + if(Boolean.getBoolean("iaa.legacySmart")) { + WallRegistrationProbe.verify(); + require(ArchitectureConfig.retainLegacyWallIds,"upgraded config retains old walls"); + BlockPos corner=new BlockPos(-15,15,-1),thin=new BlockPos(-14,15,-1),anchor=new BlockPos(2,15,-1); + require(world.getBlockState(corner).getBlock()==Block.getBlockFromName("ironagearchitecture:smart_daub_corner_spruce"),"Old smart corner identity"); + require(((SmartWallBlock)world.getBlockState(corner).getBlock()).mask(world.getBlockState(corner))==14,"Old smart corner footprint"); + require(world.getBlockState(thin).getBlock()==ArchitectureBlocks.findSmart("spruce",1),"Old smart thin identity"); + require(world.getBlockState(anchor).getBlock()==ArchitectureBlocks.findWall("dark_oak",FrameFamily.POST),"Old manual anchor"); + require(SmartWallData.get(world).code(new BlockPos(-25,10,0))!=0,"Old saved framing"); + server.saveAllWorlds(false); + finish(server,"{\"passed\":true,\"legacySmart\":true,\"blockIds\":"+ArchitectureBlocks.REGISTERED.size()+",\"runtimeSha256\":\""+System.getProperty("iaa.runtimeSha256")+"\"}\n"); + return; + } + if (Boolean.getBoolean("iaa.legacyFireplaces")) { + BlockPos lower=new BlockPos(-38,10,4),upper=new BlockPos(-39,14,4); + require(world.getBlockState(lower).getBlock()==ArchitectureBlocks.fireplace(FireplaceBlock.Masonry.STONE,FireplaceBlock.Kind.FIREBOX),"Legacy downstairs firebox"); + require(world.getBlockState(upper).getBlock()==ArchitectureBlocks.fireplace(FireplaceBlock.Masonry.STONE,FireplaceBlock.Kind.FIREBOX),"Legacy upstairs firebox"); + for(BlockPos outlet:new BlockPos[]{new BlockPos(-38,22,4),new BlockPos(-39,22,4)}) + require(FireplaceDraft.smokeOutlet(world,outlet,world.getBlockState(outlet))!=null,"Legacy independent draft"); + finish(server,"{\"passed\":true,\"legacyFireplaces\":true,\"runtimeSha256\":\""+System.getProperty("iaa.runtimeSha256")+"\"}\n"); + return; + } if (Boolean.getBoolean("iaa.legacyFascia")) { int bareFascias = GutterProbe.verifyOldPreview(world); finish(server, "{\"passed\":true,\"bareFascias\":" + bareFascias @@ -62,6 +84,7 @@ public final class ArchitectureProbe { return; } verifyConfigMatrix(); + WallRegistrationProbe.verify(); if(Boolean.getBoolean("iaa.eraMatrix")) EraProbe.verify(world); if(Boolean.getBoolean("iaa.recipeMatrix")) RecipeProbe.verify(world); if(Boolean.getBoolean("iaa.legacy")) verifyLegacy(world); @@ -77,7 +100,7 @@ public final class ArchitectureProbe { if(Boolean.getBoolean("iaa.legacyJetties")) { int index=0; for(Block block:savedBlocks) { - if (ClayTileProbe.isClay(block) || block instanceof FireplaceBlock) continue; + if (ClayTileProbe.isClay(block) || block instanceof FireplaceBlock || block instanceof SmartWallBlock) continue; for(int meta=0;meta<16;meta++) { require(world.getBlockState(new BlockPos(100+index,70,100)).equals(block.getStateFromMeta(meta)),"0.1.2 saved state "+index); index++; @@ -95,6 +118,7 @@ public final class ArchitectureProbe { 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()): + block instanceof SmartWallBlock?Item.getItemFromBlock(ArchitectureBlocks.findSmart(((SmartWallBlock)block).wood,((SmartWallBlock)block).profile(state)==2?1:((SmartWallBlock)block).profile(state))): block instanceof FasciaBlock?Item.getItemFromBlock(ArchitectureBlocks.CLAY_FASCIA):Item.getItemFromBlock(block); int dropCount = 1; if (block instanceof JettyFloorBlock && ((JettyFloorBlock)block).bracket != null) dropCount++; @@ -126,7 +150,7 @@ public final class ArchitectureProbe { require(actual!=null&&ItemStack.areItemStacksEqual(actual,output),"Recipe shadowed "+output); recipes++; } - if(variants>=460) { + if(ArchitectureConfig.frames && ArchitectureConfig.buildingMode.manual()) { require(recipes==EraProbe.expectedRecipes(),"Recipe catalog changed "+recipes); verifyRoofs(world); verifyWalls(world); @@ -147,19 +171,21 @@ public final class ArchitectureProbe { require(total==2,"Double slab quantity"); } } + if(ArchitectureConfig.windows && !ArchitectureConfig.buildingMode.manual()) WindowProbe.verify(world,reload); ClayTileProbe.verify(world); GutterProbe.verify(world); FireplaceProbe.verify(world, reload); - if (!reload && clayClient) RyeTilePreview.build(world); + SmartWallProbe.verify(world,reload); + if (!reload && clayClient) SmartWallPreview.build(world); if (Boolean.getBoolean("iaa.clayOnly")) { 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"); world.getGameRules().setOrCreateGameRule("spawnRadius","0"); - world.setSpawnPoint(clayClient ? RyeTilePreview.SPAWN : new BlockPos(7,64,-9)); + world.setSpawnPoint(clayClient ? SmartWallPreview.SPAWN : new BlockPos(7,64,-9)); server.saveAllWorlds(false); - String result="{\"passed\":true,\"variants\":"+variants+",\"recipes\":"+recipes+",\"states\":"+states+",\"reload\":"+reload + String result="{\"passed\":true,\"blockIds\":"+ArchitectureBlocks.REGISTERED.size()+",\"variants\":"+variants+",\"recipes\":"+recipes+",\"states\":"+states+",\"reload\":"+reload +",\"legacy\":"+Boolean.getBoolean("iaa.legacy")+",\"legacyDaub\":"+Boolean.getBoolean("iaa.legacyDaub") +",\"legacyJetties\":"+Boolean.getBoolean("iaa.legacyJetties")+",\"eras\":"+new com.google.gson.Gson().toJson(zone.moddev.mc.ironagearchitecture.ArchitectureConfig.survivalEras.allowed) +",\"eraMatrix\":"+Boolean.getBoolean("iaa.eraMatrix")+",\"recipeMatrix\":"+Boolean.getBoolean("iaa.recipeMatrix")+",\"runtimeSha256\":\""+System.getProperty("iaa.runtimeSha256","development")+"\"}\n"; @@ -174,6 +200,8 @@ private static void finish(MinecraftServer server, String result) throws java.io private void verifyConfigMatrix() throws Exception { boolean cob=ArchitectureConfig.cob,frames=ArchitectureConfig.frames,thatch=ArchitectureConfig.thatch,slate=ArchitectureConfig.slate,windows=ArchitectureConfig.windows; boolean clayTiles = ArchitectureConfig.clayTiles, fireplaces = ArchitectureConfig.fireplaces; + boolean retain=ArchitectureConfig.retainLegacyWallIds; + zone.moddev.mc.ironagearchitecture.BuildingMode mode=ArchitectureConfig.buildingMode; zone.moddev.mc.ironagearchitecture.EraSelection eras=ArchitectureConfig.survivalEras; java.nio.file.Path directory=Paths.get("config-matrix");Files.createDirectories(directory); try { @@ -187,7 +215,7 @@ private void verifyConfigMatrix() throws Exception { &&ArchitectureConfig.thatch==((mask&4)!=0)&&ArchitectureConfig.slate==((mask&8)!=0)&&ArchitectureConfig.windows==((mask&19)==19) &&ArchitectureConfig.clayTiles==((mask&32)!=0)&&ArchitectureConfig.fireplaces==((mask&64)!=0),"Config mask "+mask); } - } finally {ArchitectureConfig.cob=cob;ArchitectureConfig.frames=frames;ArchitectureConfig.thatch=thatch;ArchitectureConfig.slate=slate;ArchitectureConfig.windows=windows;ArchitectureConfig.clayTiles=clayTiles;ArchitectureConfig.fireplaces=fireplaces;ArchitectureConfig.survivalEras=eras;} + } finally {ArchitectureConfig.cob=cob;ArchitectureConfig.frames=frames;ArchitectureConfig.thatch=thatch;ArchitectureConfig.slate=slate;ArchitectureConfig.windows=windows;ArchitectureConfig.clayTiles=clayTiles;ArchitectureConfig.fireplaces=fireplaces;ArchitectureConfig.survivalEras=eras;ArchitectureConfig.buildingMode=mode;ArchitectureConfig.retainLegacyWallIds=retain;} } private void verifyWallHeads(WorldServer world) { BlockPos pos=new BlockPos(95,90,95); diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClayTileProbe.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClayTileProbe.java index 881e0e9..5ad9a8e 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClayTileProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClayTileProbe.java @@ -62,7 +62,7 @@ static void verify(WorldServer world) { JettyFloorBlock floor = ArchitectureBlocks.findFloor("oak", FloorShape.FULL, null); IBlockState deck = floor.getDefaultState().withProperty(FrameBlock.FACING, facing); require((floor.connections(deck, world, pos.up()) >> 16) == 3, "Tile wall supports wrong deck quarters " + facing); - PartialWallBlock wall = ArchitectureBlocks.findPartial("oak", FrameFamily.POST, WallProfile.THIN); + FrameBlock wall = ArchitectureConfig.buildingMode.manual()?ArchitectureBlocks.findPartial("oak", FrameFamily.POST, WallProfile.THIN):ArchitectureBlocks.findSmart("oak",1); IBlockState placed = wall.getStateForPlacement(world, pos.up(), EnumFacing.UP, .5F, 1, .5F, 0, player); require(placed.getValue(FrameBlock.FACING) == facing, "Timber did not inherit the tile plane"); world.setBlockState(pos, placed, 2); 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 29acdaf..9e1b808 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClientProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/ClientProbe.java @@ -48,7 +48,7 @@ public final class ClientProbe extends ProbeProxy { mc.gameSettings.pauseOnLostFocus=false; phase=1;ticks=0; String preview = System.getProperty("iaa.previewName", - ArchitectureConfig.fireplaces ? "Rye Fireplace Preview" : RyeTilePreview.NAME); + "Smart Walls Preview"); if (new java.io.File(mc.mcDataDir, "saves/" + preview).exists()) throw new IllegalStateException("Use a fresh world for the native preview"); mc.launchIntegratedServer(preview,preview,new WorldSettings(137,GameType.CREATIVE,false,false,WorldType.FLAT)); @@ -59,7 +59,7 @@ public final class ClientProbe extends ProbeProxy { mc.setIngameNotInFocus(); mc.player.capabilities.allowFlying=true;mc.player.capabilities.isFlying=true; if (ArchitectureConfig.clayTiles && ArchitectureConfig.frames) - lookAt(mc, -32, 9.5, -12.4, -39.5, 17.5, 3.6); + lookAt(mc, -10, 12, -24, -9, 16, 0); else lookAt(mc,48,102,-85,48,73,4); } else if(phase==2&&ticks>260) { for(RoofBlock roof:ArchitectureBlocks.ROOFS) for(IBlockState state:roof.getBlockState().getValidStates()) { @@ -104,7 +104,7 @@ public final class ClientProbe extends ProbeProxy { IBakedModel soffit = mc.getRenderItem().getItemModelMesher().getItemModel(new ItemStack(ArchitectureBlocks.SOFFIT)); if (soffit.getParticleTexture().getIconName().contains("missingno")) throw new IllegalStateException("Missing soffit item model"); - phase=ArchitectureConfig.fireplaces ? 70 : 60;ticks=0; + phase=80;ticks=0; if (phase == 70) { setPreviewGameType(mc, GameType.SPECTATOR); setPreviewDoor(mc, true); @@ -158,6 +158,8 @@ public final class ClientProbe extends ProbeProxy { setPreviewRain(mc, false); checkPalettes(mc); mc.displayGuiScreen(new PaletteGui(mc,Era.IRON_AGE,true));phase=8;ticks=0; + } else if (phase >= 80 && phase <= 87) { + captureSmartWalls(mc); } else if (phase >= 70 && phase <= 75) { captureFireplacePreview(mc); } else if(phase==8) { @@ -172,6 +174,51 @@ public final class ClientProbe extends ProbeProxy { } } } + private void captureSmartWalls(Minecraft mc) { + double[][] cameras={{-12,10,-25,-10,15,0},{-20,10,-10,-21.5,14,0}, + {-11,10,-9,-10.5,15,-.5},{12,11,-11,2,16,-.3},{10,12,6,4,16,-.5},{-17,10.2,1.2,-19.2,12.4,4}}; + String[] names={"smart-walls-street.png","smart-small-cottage.png","smart-half-jetty.png","smart-full-jetty.png","smart-corners-and-side.png","smart-l-shaped-return.png"}; + if(Boolean.getBoolean("iaa.surfacePreview")) { + cameras=java.util.Arrays.copyOf(cameras,8); + cameras[6]=new double[]{-20,10.1,11.2,-20,12.1,14.5}; + cameras[7]=new double[]{-10.1,12.5,-2.3,-10.6,15.2,-.7}; + names=java.util.Arrays.copyOf(names,8); + names[6]="manual-brace-end-surfaces.png";names[7]="smart-jetty-brace-surfaces.png"; + } + double[] camera=cameras[phase-80]; + lookAt(mc,camera[0],camera[1],camera[2],camera[3],camera[4],camera[5]); + if(ticks<=150) return; + if(phase==80) { + net.minecraft.world.World server=mc.getIntegratedServer().worldServerForDimension(0); + BlockPos at=new BlockPos(-11,15,-1); + int expected=zone.moddev.mc.ironagearchitecture.block.SmartWallData.get(server).code(at); + int actual=zone.moddev.mc.ironagearchitecture.block.SmartWallData.get(mc.world).code(at); + if(expected==0||expected!=actual) throw new IllegalStateException("Smart layout did not reach the client"); + System.out.println("IAA_SMART_CLIENT_PASS serverLayout=true matchingClient=true"); + } + capture(mc,names[phase-80]); + if(phase<80+names.length-1){phase++;ticks=0;return;} + mc.getIntegratedServer().addScheduledTask(()->{ + WorldServer world=mc.getIntegratedServer().worldServerForDimension(0); + for(EntityPlayer player:world.playerEntities) { + BlockPos spawn=SmartWallPreview.SPAWN; + player.setPositionAndUpdate(spawn.getX()+.5,spawn.getY(),spawn.getZ()+.5); + player.inventory.setInventorySlotContents(0,new ItemStack(ArchitectureBlocks.findSmart("spruce",0),64,1)); + player.inventory.setInventorySlotContents(1,new ItemStack(ArchitectureBlocks.findSmart("spruce",1),64,1)); + player.inventory.setInventorySlotContents(2,new ItemStack(ArchitectureBlocks.findWindow("spruce",1,true,true,false),32)); + player.inventory.setInventorySlotContents(3,new ItemStack(ArchitectureBlocks.findFloor("spruce",zone.moddev.mc.ironagearchitecture.block.FloorShape.FULL,null),64)); + player.inventory.setInventorySlotContents(4,new ItemStack(net.minecraft.block.Block.getBlockFromName("ironagearchitecture:clay_tile_roof"),64)); + player.inventory.setInventorySlotContents(5,ArchitectureConfig.buildingMode.manual() + ?new ItemStack(ArchitectureBlocks.findWall("spruce",zone.moddev.mc.ironagearchitecture.block.FrameFamily.POST),64,2) + :new ItemStack(ArchitectureBlocks.findSmart("spruce",0),64,1)); + player.inventory.setInventorySlotContents(6,new ItemStack(ArchitectureBlocks.findSmart("oak",0),64)); + player.inventory.setInventorySlotContents(7,new ItemStack(ArchitectureBlocks.findSmart("dark_oak",0),64,1)); + } + }); + checkPalettes(mc); + mc.displayGuiScreen(new PaletteGui(mc,Era.IRON_AGE,true));phase=8;ticks=0; + } + private void captureFireplacePreview(Minecraft mc) { double[][] cameras = { {-37.5, 10.03, 1.4, -37.5, 11.1, 4.0}, @@ -291,6 +338,9 @@ private void checkPalettes(Minecraft mc) { for(Era era:Era.values()) { java.util.List items=new java.util.ArrayList<>();EraTabs.tab(era).displayAllRelevantItems(items); int expected=era==Era.IRON_AGE?14:EraProbe.expectedVariants(); + if(era==Era.LATE_MEDIEVAL_TUDOR&&ArchitectureConfig.buildingMode.automatic() + &&!(items.get(0).getItem() instanceof zone.moddev.mc.ironagearchitecture.block.SmartWallItem)) + throw new IllegalStateException("Smart walls should be first in the palette"); if(items.size()!=expected) throw new IllegalStateException("Creative tab "+era+" expected "+expected+" got "+items.size()); for(ItemStack item:items) if(!zone.moddev.mc.ironagearchitecture.EraCatalog.eras(item.getItem()).contains(era)) throw new IllegalStateException("Wrong era item "+item); for(ItemStack item:items) { 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 6af3071..d5f0438 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/EraProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/EraProbe.java @@ -15,11 +15,14 @@ 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?20:0)+fireplaceParts(): + return ArchitectureConfig.survivalEras.allowed.contains(Era.LATE_MEDIEVAL_TUDOR)?(ArchitectureConfig.buildingMode==BuildingMode.AUTOMATIC?(ArchitectureConfig.windows?306:159):(ArchitectureConfig.windows?2010:1863))+(ArchitectureConfig.clayTiles?20:0)+fireplaceParts()+smartRecipes(): ArchitectureConfig.survivalEras.allowed.contains(Era.IRON_AGE)?36:0; } static int expectedVariants() { - return (ArchitectureConfig.windows ? 510 : 460) + (ArchitectureConfig.clayTiles ? 16 : 0) + fireplaceParts(); + return (ArchitectureConfig.windows ? 510 : 460) + (ArchitectureConfig.clayTiles ? 16 : 0) + fireplaceParts() - (ArchitectureConfig.buildingMode==BuildingMode.AUTOMATIC?408:0) + (ArchitectureConfig.frames && ArchitectureConfig.buildingMode.automatic() ? 24 : 0); + } + private static int smartRecipes() { + return ArchitectureConfig.frames && ArchitectureConfig.buildingMode==BuildingMode.HYBRID ? 72+(ArchitectureConfig.windows?48:0) : 0; } private static int fireplaceParts() { return ArchitectureConfig.fireplaces ? (ArchitectureConfig.clayTiles ? 16 : 14) : 0; @@ -47,6 +50,8 @@ static void verify(World world) throws Exception { EraSelection selected=ArchitectureConfig.survivalEras; boolean cob=ArchitectureConfig.cob,frames=ArchitectureConfig.frames,thatch=ArchitectureConfig.thatch,slate=ArchitectureConfig.slate,windows=ArchitectureConfig.windows; boolean clayTiles = ArchitectureConfig.clayTiles, fireplaces = ArchitectureConfig.fireplaces; + boolean retain=ArchitectureConfig.retainLegacyWallIds; + BuildingMode mode=ArchitectureConfig.buildingMode; String[][] cases={{"all"},{"iron_age"},{"late_medieval_tudor"},{"iron_age","late_medieval_tudor"},{},{"typo"},{"iron_age","typo"},{" IRON_AGE ","iron_age"}}; try { recipes.removeIf(EraProbe::ours);ArchitectureConfig.survivalEras=EraSelection.parse("all");ArchitectureRecipes.register(); @@ -55,7 +60,7 @@ static void verify(World world) throws Exception { Files.createDirectories(Paths.get("era-matrix")); for(int c=0;c\n}\n"; + String text="blocks {\n B:leadedWindows="+windows+"\n B:clayTiles="+clayTiles+"\n B:fireplaces="+fireplaces+"\n}\nbuilding {\n S:mode="+mode.name().toLowerCase(java.util.Locale.ROOT)+"\n}\nrecipes {\n S:survivalEras <\n"+String.join("\n",cases[c])+"\n >\n}\n"; Files.write(file,text.getBytes(StandardCharsets.UTF_8));ArchitectureConfig.load(file.toFile()); recipes.removeIf(EraProbe::ours);ArchitectureRecipes.register(); int count=0;for(IRecipe recipe:recipes) if(ours(recipe)) {require(EraCatalog.canCraft(recipe.getRecipeOutput()),"Prohibited registered output");count++;} @@ -81,6 +86,8 @@ static void verify(World world) throws Exception { ArchitectureConfig.cob=cob;ArchitectureConfig.frames=frames;ArchitectureConfig.thatch=thatch;ArchitectureConfig.slate=slate;ArchitectureConfig.windows=windows; ArchitectureConfig.clayTiles = clayTiles; ArchitectureConfig.fireplaces = fireplaces; + ArchitectureConfig.buildingMode = mode; + ArchitectureConfig.retainLegacyWallIds = retain; } } } diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/RecipeProbe.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/RecipeProbe.java index b06f935..1ac6bf1 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/RecipeProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/RecipeProbe.java @@ -33,7 +33,10 @@ private static void crafts(World world,ItemStack expected,ItemStack... slots) { static void verify(World world) { if(ArchitectureConfig.frames) for(int wood=0;wood<6;wood++) for(int finish=0;finish<2;finish++) { ItemStack plank=new ItemStack(Blocks.PLANKS,1,wood),infill=new ItemStack(ArchitectureBlocks.DAUB,1,finish); - crafts(world,new ItemStack(ArchitectureBlocks.FRAMES.get(wood),4,finish*2),null,plank,null,plank,infill,plank,null,plank,null); + ItemStack wall=ArchitectureConfig.buildingMode==BuildingMode.AUTOMATIC + ?new ItemStack(ArchitectureBlocks.findSmart(ArchitectureBlocks.WOODS[wood],0),4,finish) + :new ItemStack(ArchitectureBlocks.FRAMES.get(wood),4,finish*2); + crafts(world,wall,null,plank,null,plank,infill,plank,null,plank,null); ItemStack other=new ItemStack(Blocks.PLANKS,1,(wood+1)%6); crafts(world,null,null,other,null,plank,infill,plank,null,plank,null); } @@ -42,8 +45,10 @@ static void verify(World world) { crafts(world,null,glazed,null,null,glazed); // Full/thin wall cutting must not multiply glazing. crafts(world,null,glazed,glazed); // Rejoining glazed frames is deliberately unavailable too. crafts(world,null,glazed,new ItemStack(ArchitectureBlocks.LEADED_PANE,1,window.diamond?0:1)); - FrameBlock panel=window.thin?ArchitectureBlocks.findPartial(window.wood,FrameFamily.POST,WallProfile.THIN):ArchitectureBlocks.findWall(window.wood,FrameFamily.POST); - crafts(world,null,new ItemStack(panel,1,window.finish*2+1),new ItemStack(ArchitectureBlocks.LEADED_PANE,1,window.diamond?0:1)); + if(ArchitectureConfig.buildingMode.manual()) { + FrameBlock panel=window.thin?ArchitectureBlocks.findPartial(window.wood,FrameFamily.POST,WallProfile.THIN):ArchitectureBlocks.findWall(window.wood,FrameFamily.POST); + crafts(world,null,new ItemStack(panel,1,window.finish*2+1),new ItemStack(ArchitectureBlocks.LEADED_PANE,1,window.diamond?0:1)); + } if(window.finish==0) { String otherWood=window.wood.equals("oak")?"spruce":"oak"; for(WindowBlock other:new WindowBlock[]{ArchitectureBlocks.findWindow(otherWood,0,window.thin,window.diamond,false),ArchitectureBlocks.findWindow(window.wood,1,window.thin,window.diamond,false),ArchitectureBlocks.findWindow(window.wood,0,!window.thin,window.diamond,false),ArchitectureBlocks.findWindow(window.wood,0,window.thin,!window.diamond,false)}) @@ -125,6 +130,7 @@ private static int units(ItemStack stack) { Item item=stack.getItem();if(item instanceof JettyBracketItem) return ((JettyBracketItem)item).fullReach?2:1; Block block=Block.getBlockFromItem(item); if(block instanceof JettyFloorBlock) return Integer.bitCount(((JettyFloorBlock)block).shape.mask); + if(block instanceof SmartWallBlock) return ((SmartWallBlock)block).profile==0?4:2; if(block instanceof PartialWallBlock) {WallProfile p=((PartialWallBlock)block).profile;return p.corner?p.quarters():(p==WallProfile.THIN?2:1);} if(ArchitectureBlocks.SLABS.contains(block)) return 2; if(ArchitectureBlocks.STAIRS.contains(block)) return 3; diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/SmartWallPreview.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/SmartWallPreview.java new file mode 100644 index 0000000..89fda88 --- /dev/null +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/SmartWallPreview.java @@ -0,0 +1,139 @@ +package zone.moddev.mc.ironagearchitecture.probe; + +import net.minecraft.block.*; +import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.WorldServer; +import zone.moddev.mc.ironagearchitecture.*; +import zone.moddev.mc.ironagearchitecture.block.*; + +/** The same smart item builds a cottage, half-depth jetty and full projecting townhouse. */ +final class SmartWallPreview { + static final BlockPos SPAWN=new BlockPos(-6,10,-8); + private SmartWallPreview() { } + + static void build(WorldServer world) { + for(int x=-35;x<=18;x++) for(int z=-36;z<=18;z++) { + for(int y=3;y<9;y++) world.setBlockState(new BlockPos(x,y,z),Blocks.DIRT.getDefaultState(),2); + world.setBlockState(new BlockPos(x,9,z),(z<0?Blocks.COBBLESTONE:Blocks.GRASS).getDefaultState(),2); + } + house(world,-25,7,7,3,0,"oak",0); + annex(world); + house(world,-15,9,9,4,1,"spruce",1); + house(world,-3,11,9,4,2,"dark_oak",1); + if(Boolean.getBoolean("iaa.surfacePreview")) surfaceSamples(world); + for(int i=0;i<100&&SmartWallEvents.pending(world);i++) SmartWallEvents.flush(world); + } + + private static void surfaceSamples(WorldServer world) { + // Separate suspended samples expose the otherwise buried brace end caps. + FrameBlock[] samples={ArchitectureBlocks.findWall("oak",FrameFamily.BRACE), + ArchitectureBlocks.findWall("spruce",FrameFamily.POST), + ArchitectureBlocks.findPartial("birch",FrameFamily.BRACE,WallProfile.THIN), + ArchitectureBlocks.findPartial("dark_oak",FrameFamily.BRACE,WallProfile.SHORT_LEFT)}; + for(int i=0;i=9?2:1);dx++) for(int y=1;y0,"planned facade braces, origin="+Integer.toHexString(data.code(origin))+", pending="+SmartWallEvents.pending(world)+", survey="+(SmartWallPlanner.survey(world,origin,EnumFacing.NORTH)!=null)); + world.setBlockState(origin.add(6,1,0),opening(),3); + for(int i=0;i<100&&SmartWallEvents.pending(world);i++) SmartWallEvents.flush(world); + require(data.code(origin.add(4,1,0))!=before||((SmartWallGeometry.face(data.code(origin.add(5,1,0)),EnumFacing.NORTH)&SmartFacade.RIGHT)!=0), + "opening edit reframes neighbouring bay"); + NBTTagCompound saved=data.writeToNBT(new NBTTagCompound()); + SmartWallData restored=new SmartWallData();restored.readFromNBT(saved); + require(restored.code(origin)==data.code(origin),"NBT layout round trip"); + SmartWallNetwork.Packet packet=new SmartWallNetwork.Packet(); + packet.dimension=0;packet.x=origin.getX()>>4;packet.z=origin.getZ()>>4;packet.replace=true; + packet.cells=Collections.singletonMap(origin.toLong(),data.code(origin)); + io.netty.buffer.ByteBuf bytes=io.netty.buffer.Unpooled.buffer(); + try { + packet.toBytes(bytes); + SmartWallNetwork.Packet decoded=new SmartWallNetwork.Packet();decoded.fromBytes(bytes); + require(decoded.cells.equals(packet.cells)&&decoded.mode==ArchitectureConfig.buildingMode.ordinal(),"layout packet and mode"); + } finally {bytes.release();} + FakePlayer player=FakePlayerFactory.getMinecraft(world); + player.setPosition(35,100,75);player.setSneaking(true); + try { + SmartWallBlock thin=ArchitectureBlocks.findSmart("oak",1); + BlockPos corner=origin.north(4); + IBlockState first=thin.getDefaultState().withProperty(FrameBlock.FACING,EnumFacing.NORTH); + world.setBlockState(corner,first,3); + ItemStack held=new ItemStack(thin,2,0); + EnumActionResult result=held.getItem().onItemUse(held,player,world,corner,EnumHand.MAIN_HAND,EnumFacing.WEST,.2F,.5F,.5F); + require(result==EnumActionResult.SUCCESS,"thin corner placement"); + IBlockState placed=world.getBlockState(corner); + require(placed.getBlock() instanceof SmartWallBlock&&((SmartWallBlock)placed.getBlock()).profile(placed)==2,"joined L footprint"); + List drops=placed.getBlock().getDrops(world,corner,placed,0); + require(drops.size()==1&&drops.get(0).stackSize==2&&drops.get(0).getItem()==Item.getItemFromBlock(thin),"corner recovers two panels"); + for(EnumFacing facing:EnumFacing.HORIZONTALS) { + IBlockState state=thin.getDefaultState().withProperty(FrameBlock.FACING,facing); + int occupied=thin.mask(state); + require(Integer.bitCount(occupied)==2,"thin facing"); + double volume=0;for(AxisAlignedBB box:SmartWallGeometry.boxes(occupied)) volume+=(box.maxX-box.minX)*(box.maxZ-box.minZ); + require(Math.abs(volume-.5)<1e-8,"fixed thin collision"); + world.setBlockState(corner,state,2); + JettyFloorBlock floor=ArchitectureBlocks.findFloor("oak",FloorShape.FULL,null); + IBlockState deck=floor.getDefaultState(); + world.setBlockState(corner.up(),deck,2); + require((floor.connections(deck,world,corner.up())>>>16)==occupied,"jetty support follows occupied half"); + for(int q=0;q<4;q++) { + double cx=q%2*.5+.25,cz=q/2*.5+.25; + Vec3d start=new Vec3d(corner.getX()+cx,corner.getY()-.1,corner.getZ()+cz); + Vec3d end=new Vec3d(corner.getX()+cx,corner.getY()+1.1,corner.getZ()+cz); + boolean hit=thin.collisionRayTrace(state,world,corner,start,end)!=null; + require(hit==((occupied&(1< drops=world.getEntitiesWithinAABB(net.minecraft.entity.item.EntityItem.class,new AxisAlignedBB(mined).expand(1,1,1)); + drops.removeIf(drop->drop.isDead); + require(drops.size()==1&&drops.get(0).getEntityItem().getItem()==Item.getItemFromBlock(wall) + &&drops.get(0).getEntityItem().getMetadata()==finish,"silk harvest keeps wood and finish "+wall+" "+facing+" "+finish+" drops="+drops.size()); + for(net.minecraft.entity.item.EntityItem drop:drops) world.removeEntity(drop); + } + // The normal server/client path uses exactly the persisted descriptor format. + System.out.println("IAA_SMART_WALL_PASS braces="+braces+" chunkBorder=true edit=true save=true corners=true facings=4 mode="+ArchitectureConfig.buildingMode); + } + private static IBlockState opening() { + return ArchitectureConfig.windows?ArchitectureBlocks.findWindow("oak",0,false,true,false).getDefaultState():Blocks.AIR.getDefaultState(); + } +} diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/WallRegistrationProbe.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/WallRegistrationProbe.java new file mode 100644 index 0000000..8ab0d27 --- /dev/null +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/WallRegistrationProbe.java @@ -0,0 +1,72 @@ +package zone.moddev.mc.ironagearchitecture.probe; + +import java.util.*; +import java.nio.file.*; +import java.nio.charset.StandardCharsets; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import zone.moddev.mc.ironagearchitecture.*; +import zone.moddev.mc.ironagearchitecture.block.*; + +/** Counts real registry entries, not creative items or baked model variants. */ +final class WallRegistrationProbe { + private static void require(boolean ok,String message) {if(!ok) throw new IllegalStateException("Wall registration: "+message);} + static void verify() throws Exception { + int registered=0; + for(Block block:Block.REGISTRY) if(IronAgeArchitecture.MODID.equals(block.getRegistryName().getResourceDomain())) registered++; + require(registered==ArchitectureBlocks.REGISTERED.size(),"registry count"); + if(ArchitectureConfig.frames) { + for(String wood:ArchitectureBlocks.WOODS) { + for(FrameFamily family:FrameFamily.values()) if(family!=FrameFamily.INFILL) { + require(present(family.registryPrefix+"_"+wood)==ArchitectureConfig.registerManualWalls(),"manual family "+wood+" "+family); + for(WallProfile profile:WallProfile.values()) if(!profile.corner||family==FrameFamily.TERMINAL) + require(present("partial_"+profile.key()+"_"+family.registryPrefix+"_"+wood)==ArchitectureConfig.registerManualWalls(),"manual profile "+wood+" "+profile); + } + require(present("smart_daub_corner_"+wood)==ArchitectureConfig.retainLegacyWallIds,"legacy corner "+wood); + SmartWallBlock full=ArchitectureBlocks.findSmart(wood,0),thin=ArchitectureBlocks.findSmart(wood,1); + Set states=new HashSet<>(); + for(int meta=0;meta<16;meta++) { + IBlockState state=full.getStateFromMeta(meta);states.add(state); + require(full.getMetaFromState(state)==meta,"full/corner metadata round trip"); + require(full.profile(state)==(meta<8?0:2),"full/corner footprint bit"); + require(full.finish(state.getValue(FrameBlock.PANEL))==((meta>>2)&1),"finish bit"); + require(full.mask(state)==SmartWallGeometry.mask(meta<8?0:2,EnumFacing.getHorizontal(meta&3)),"facing bits"); + require(full.isFullCube(state)==(meta<8),"corner cube classification"); + require(full.getUseNeighborBrightness(state)==(meta>=8),"corner neighbour lighting"); + IBlockState oldThin=thin.getStateFromMeta(meta); + require(thin.getMetaFromState(oldThin)==(meta&7)&&thin.profile(oldThin)==1,"unchanged thin metadata"); + } + require(states.size()==16,"all sixteen full/corner states used"); + } + } + verifyConfigUpgrade(); + System.out.println("IAA_WALL_REGISTRY_PASS blockIds="+registered+" manualIds="+(ArchitectureConfig.frames&&ArchitectureConfig.registerManualWalls()?144:0) + +" smartIds="+ArchitectureBlocks.SMART_WALLS.size()+" packedStates=16 retainLegacy="+ArchitectureConfig.retainLegacyWallIds); + } + private static boolean present(String name) {return Block.REGISTRY.containsKey(new ResourceLocation(IronAgeArchitecture.MODID,name));} + private static void verifyConfigUpgrade() throws Exception { + boolean cob=ArchitectureConfig.cob,frames=ArchitectureConfig.frames,thatch=ArchitectureConfig.thatch,slate=ArchitectureConfig.slate, + windows=ArchitectureConfig.windows,clay=ArchitectureConfig.clayTiles,fireplaces=ArchitectureConfig.fireplaces,retain=ArchitectureConfig.retainLegacyWallIds; + BuildingMode mode=ArchitectureConfig.buildingMode;EraSelection eras=ArchitectureConfig.survivalEras; + Path directory=Paths.get("wall-config-matrix",UUID.randomUUID().toString());Files.createDirectories(directory); + try { + Path fresh=directory.resolve("fresh.cfg");ArchitectureConfig.load(fresh.toFile()); + require(ArchitectureConfig.buildingMode==BuildingMode.AUTOMATIC&&!ArchitectureConfig.retainLegacyWallIds&&!ArchitectureConfig.registerManualWalls(),"fresh automatic defaults"); + ArchitectureConfig.load(fresh.toFile());require(!ArchitectureConfig.retainLegacyWallIds,"fresh config reload stays compact"); + Path old=directory.resolve("old-alpha.cfg");Files.write(old,"blocks {\n B:timberFrames=true\n}\n".getBytes(StandardCharsets.UTF_8)); + ArchitectureConfig.load(old.toFile());require(ArchitectureConfig.buildingMode==BuildingMode.AUTOMATIC&&ArchitectureConfig.retainLegacyWallIds,"old config keeps IDs"); + for(BuildingMode selected:BuildingMode.values()) { + Path explicit=directory.resolve(selected.name()+".cfg"); + Files.write(explicit,("building {\n S:mode="+selected.name()+"\n B:retainLegacyWallIds=false\n}\n").getBytes(StandardCharsets.UTF_8)); + ArchitectureConfig.load(explicit.toFile()); + require(ArchitectureConfig.buildingMode==selected&&!ArchitectureConfig.retainLegacyWallIds&&ArchitectureConfig.registerManualWalls()==selected.manual(),"explicit mode "+selected); + } + } finally { + ArchitectureConfig.cob=cob;ArchitectureConfig.frames=frames;ArchitectureConfig.thatch=thatch;ArchitectureConfig.slate=slate; + ArchitectureConfig.windows=windows;ArchitectureConfig.clayTiles=clay;ArchitectureConfig.fireplaces=fireplaces; + ArchitectureConfig.retainLegacyWallIds=retain;ArchitectureConfig.buildingMode=mode;ArchitectureConfig.survivalEras=eras; + } + } +} diff --git a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/WindowProbe.java b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/WindowProbe.java index b49e3a1..4e73bcd 100644 --- a/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/WindowProbe.java +++ b/src/probe/java/zone/moddev/mc/ironagearchitecture/probe/WindowProbe.java @@ -148,7 +148,8 @@ private static void placement(WorldServer world,BlockPos pos,FakePlayer player) clear(world,pos);player.setHeldItem(EnumHand.MAIN_HAND,null); } for(EnumFacing facing:EnumFacing.HORIZONTALS) for(boolean interior:new boolean[]{false,true}) { - clear(world,pos);PartialWallBlock wall=ArchitectureBlocks.findPartial("oak",FrameFamily.POST,WallProfile.THIN); + clear(world,pos);FrameBlock wall=zone.moddev.mc.ironagearchitecture.ArchitectureConfig.buildingMode.manual() + ?ArchitectureBlocks.findPartial("oak",FrameFamily.POST,WallProfile.THIN):ArchitectureBlocks.findSmart("oak",1); IBlockState support=wall.getDefaultState().withProperty(FrameBlock.FACING,facing);world.setBlockState(pos.down(),support,2); Vec3d standing=point(pos,8,0,interior?48:-32,facing);player.setPosition(standing.xCoord,standing.yCoord,standing.zCoord);player.rotationYaw=facing.getOpposite().getHorizontalIndex()*90; ItemStack stack=new ItemStack(ArchitectureBlocks.findWindow("oak",0,true,true,false)); diff --git a/src/test/java/zone/moddev/mc/ironagearchitecture/SmartFacadeTest.java b/src/test/java/zone/moddev/mc/ironagearchitecture/SmartFacadeTest.java new file mode 100644 index 0000000..e600e63 --- /dev/null +++ b/src/test/java/zone/moddev/mc/ironagearchitecture/SmartFacadeTest.java @@ -0,0 +1,59 @@ +package zone.moddev.mc.ironagearchitecture; + +import java.util.Arrays; +import org.junit.Test; +import static org.junit.Assert.*; +import zone.moddev.mc.ironagearchitecture.block.SmartFacade; + +public class SmartFacadeTest { + @Test public void layoutsAreStableAndKeepBracesOutOfOpenings() { + for(int width=7;width<=15;width++) for(int height=3;height<=5;height++) { + int[][] grid=new int[height][width]; + for(int[] row:grid) Arrays.fill(row,SmartFacade.WALL); + for(int y=1;y0); + grid[1][width-2]=SmartFacade.OPENING; + assertFalse(Arrays.deepEquals(result,SmartFacade.arrange(grid))); + } + } + @Test public void incompleteAndSteppedFacadesRemainBounded() { + int[][] grid={{1,1,1,1,1,1,1},{1,1,2,2,1,1,1},{1,1,1,1,1,1,1}, + {0,1,1,1,1,1,0},{0,0,1,1,1,0,0},{0,0,0,1,0,0,0}}; + int[][] result=SmartFacade.arrange(grid); + for(int y=0;y=0&&result[y][x]<=255); + if(grid[y][x]==0) assertEquals(0,result[y][x]); + } + } + @Test public void unglazedOpeningsHaveTheSameFramingAsWindows() { + int[][] empty=new int[4][9],glazed=new int[4][9]; + for(int y=0;y<4;y++){Arrays.fill(empty[y],SmartFacade.WALL);Arrays.fill(glazed[y],SmartFacade.WALL);} + for(int y=0;y<3;y++) {empty[y][4]=0;glazed[y][4]=SmartFacade.OPENING;} + for(int y=1;y<3;y++) {empty[y][1]=0;glazed[y][1]=SmartFacade.OPENING;} + assertTrue(Arrays.deepEquals(SmartFacade.arrange(empty),SmartFacade.arrange(glazed))); + } + @Test public void manualRailsAndBracesGuideSmartPanels() { + int[][] cells=new int[4][9]; + for(int[] row:cells) Arrays.fill(row,SmartFacade.WALL); + cells[1][4]=SmartFacade.HEAD; + int[][] rail=SmartFacade.arrange(cells); + assertTrue((rail[1][1]&SmartFacade.TOP)!=0); + cells[1][4]=SmartFacade.RISING; + int[][] brace=SmartFacade.arrange(cells); + assertTrue((brace[2][5]&SmartFacade.RISE)!=0); + } + @Test public void modeParsingDefaultsToAutomatic() { + assertEquals(BuildingMode.AUTOMATIC,BuildingMode.parse("misspelled")); + assertEquals(BuildingMode.AUTOMATIC,BuildingMode.parse(" Automatic ")); + assertTrue(BuildingMode.HYBRID.manual()); + assertTrue(BuildingMode.HYBRID.automatic()); + } +} diff --git a/src/test/java/zone/moddev/mc/ironagearchitecture/SmartGeometryTest.java b/src/test/java/zone/moddev/mc/ironagearchitecture/SmartGeometryTest.java new file mode 100644 index 0000000..5e52cb5 --- /dev/null +++ b/src/test/java/zone/moddev/mc/ironagearchitecture/SmartGeometryTest.java @@ -0,0 +1,25 @@ +package zone.moddev.mc.ironagearchitecture; + +import org.junit.Test; +import static org.junit.Assert.*; +import net.minecraft.util.EnumFacing; +import zone.moddev.mc.ironagearchitecture.block.*; + +public class SmartGeometryTest { + @Test public void joinedHalfPanelsFillTheSharedPlasterBoundary() { + int coverage=3<<(EnumFacing.EAST.getHorizontalIndex()*4); + boolean meets=false; + for(WallGeometry.Part part:SmartWallGeometry.parts(3,SmartWallGeometry.FALLBACK,coverage)) + if(part.grain==WallGeometry.Grain.DAUB&&part.bounds(EnumFacing.NORTH).maxX==1) meets=true; + assertTrue(meets); + } + @Test public void allPhysicalProfilesKeepMeshesInsideTheirCell() { + for(int mask=1;mask<=15;mask++) for(int code:new int[]{SmartWallGeometry.FALLBACK,0xffffffff,0x80808080}) { + for(WallGeometry.Part part:SmartWallGeometry.parts(mask,code,0)) { + net.minecraft.util.math.AxisAlignedBB bounds=part.bounds(EnumFacing.NORTH); + assertTrue(bounds.minX>=0&&bounds.minY>=0&&bounds.minZ>=0); + assertTrue(bounds.maxX<=1&&bounds.maxY<=1&&bounds.maxZ<=1); + } + } + } +} diff --git a/src/test/java/zone/moddev/mc/ironagearchitecture/SurfaceGeometryTest.java b/src/test/java/zone/moddev/mc/ironagearchitecture/SurfaceGeometryTest.java index ad4330c..89e04e4 100644 --- a/src/test/java/zone/moddev/mc/ironagearchitecture/SurfaceGeometryTest.java +++ b/src/test/java/zone/moddev/mc/ironagearchitecture/SurfaceGeometryTest.java @@ -17,6 +17,82 @@ private static double area(List parts) { WallGeometry.box(parts,0,0,0,16,16,16,Grain.VERTICAL); assertEquals(6*256,area(parts),1e-4); } + @Test public void diagonalBraceEndsReplaceTheDaubSurfaceTheyOccupy() { + for(EnumFacing facing:EnumFacing.HORIZONTALS) for(int panel=0;panel<4;panel++) { + List parts=WallGeometry.parts(FrameFamily.BRACE,panel,facing,0); + double bottom=0; + for(SurfaceGeometry.Face face:SurfaceGeometry.faces(parts,facing)) { + boolean onBottom=true; + for(double[] point:face.points) + if(Math.abs(face.part.transform(point[0],point[1],point[2],facing)[1])>1e-6) onBottom=false; + if(onBottom) bottom+=SurfaceGeometry.area(face.points); + } + double inset=(panel&1)==0?.25:.375; + double expected=16*(16-2*WallGeometry.RECESS)+2*1.25*(WallGeometry.RECESS-inset); + assertEquals("Duplicate wood/daub skin at "+facing+" panel "+panel,expected,bottom,1e-4); + } + } + @Test public void identicalSlopingPrismsHaveOneOutsideSkin() { + double[][] outline={{0,0},{2,0},{16,14},{16,16},{14,16},{0,2}}; + for(EnumFacing facing:EnumFacing.HORIZONTALS) for(boolean horizontal:new boolean[]{false,true}) { + Part timber=new Part(outline,.25,1.25,Grain.RISING,facing,horizontal); + Part daub=new Part(outline,.25,1.25,Grain.DAUB,facing,horizontal); + assertEquals(area(Collections.singletonList(timber)),area(Arrays.asList(daub,timber)),1e-4); + } + } + @Test public void manualAndSmartWallsHaveNoCoplanarTimberDaubOverlap() { + for(EnumFacing facing:EnumFacing.HORIZONTALS) for(FrameFamily family:FrameFamily.values()) for(int panel=0;panel<4;panel++) { + noTimberDaubOverlap(WallGeometry.parts(family,panel,facing,0xfff),facing); + for(WallProfile profile:WallProfile.values()) + noTimberDaubOverlap(PartialWallGeometry.parts(profile,family,panel,facing,0xfff),facing); + } + for(int mask=1;mask<=15;mask++) for(int code:new int[]{SmartWallGeometry.FALLBACK,0xffffffff,0x80808080}) + noTimberDaubOverlap(SmartWallGeometry.parts(mask,code,0),EnumFacing.NORTH); + } + /** Independently intersect the finished polygons in 2D, after their world transforms. */ + private static void noTimberDaubOverlap(List parts,EnumFacing facing) { + List faces=SurfaceGeometry.faces(parts,facing); + for(SurfaceGeometry.Face wood:faces) { + if(wood.part.grain==Grain.DAUB) continue; + double[][] a=worldPoints(wood,facing); + double[] n=new double[3]; + for(int i=0;iMath.abs(n[1])?0:1;if(Math.abs(n[2])>Math.abs(n[drop])) drop=2; + for(SurfaceGeometry.Face daub:faces) { + if(daub.part.grain!=Grain.DAUB) continue; + double[][] b=worldPoints(daub,facing);boolean coplanar=true; + for(double[] p:b) if(Math.abs(n[0]*(p[0]-a[0][0])+n[1]*(p[1]-a[0][1])+n[2]*(p[2]-a[0][2]))>1e-6) coplanar=false; + if(!coplanar) continue; + java.awt.geom.Area overlap=project(a,drop);overlap.intersect(project(b,drop)); + java.awt.geom.PathIterator path=overlap.getPathIterator(null);double[] point=new double[6]; + double firstX=0,firstY=0,lastX=0,lastY=0,total=0; + while(!path.isDone()) { + int kind=path.currentSegment(point); + if(kind==java.awt.geom.PathIterator.SEG_MOVETO) {firstX=lastX=point[0];firstY=lastY=point[1];} + else if(kind==java.awt.geom.PathIterator.SEG_LINETO) {total+=lastX*point[1]-lastY*point[0];lastX=point[0];lastY=point[1];} + else if(kind==java.awt.geom.PathIterator.SEG_CLOSE) total+=lastX*firstY-lastY*firstX; + path.next(); + } + assertEquals("Overlapping plaster and "+wood.part.grain+" at "+facing,0,Math.abs(total)/2,1e-5); + } + } + } + private static double[][] worldPoints(SurfaceGeometry.Face face,EnumFacing facing) { + double[][] result=new double[face.points.length][]; + for(int i=0;i parts=new ArrayList<>(); WallGeometry.box(parts,0,0,0,8,16,16,Grain.EAST_WEST);