Compare commits
47 Commits
v1.0-SNAPS
...
v1.5.6
| Author | SHA1 | Date | |
|---|---|---|---|
| f1af00738e | |||
| 3048f53878 | |||
| f222e4dc24 | |||
| 5c064176b2 | |||
| 186cca3e27 | |||
| 08bde119b2 | |||
| 6922dcc9d9 | |||
| b1ba784ee3 | |||
| c207fb5ae6 | |||
| 5fcb983592 | |||
| bd412bfccd | |||
| 96f08bdd3e | |||
| 394c75f9ed | |||
| e6c9ee63cd | |||
| 239802ce7b | |||
| 5f87489939 | |||
| d43d9456ee | |||
| de7acfee50 | |||
| 2957a899db | |||
| 6ca266b676 | |||
|
|
4d87ce9753 | ||
| e196975f1d | |||
| e8ce990955 | |||
| c3e2e626c8 | |||
| f3578a4da5 | |||
| dccc01ef49 | |||
| 961165104d | |||
| 28ae110e14 | |||
| 13539fd91d | |||
| 9c6e436fe3 | |||
| 53107d090b | |||
| a3ffe17a7e | |||
| 87616ae930 | |||
| ce216d1f9f | |||
| 6a84a14f3e | |||
| fcd5d77b26 | |||
| 0a050a2abc | |||
| 5824d5d60c | |||
| 6dc0cf7fdf | |||
| 5779aa7bee | |||
| 58d9687ff8 | |||
| 4ec8f4903a | |||
| bba83206a2 | |||
| b30177a5c7 | |||
| aef1cfbf43 | |||
| 1e132c71ef | |||
| 467b4f2031 |
@@ -28,6 +28,8 @@ jobs:
|
||||
run: |
|
||||
chmod +x ./gradlew
|
||||
touch local.properties
|
||||
- name: Generate data
|
||||
run: ./gradlew runDatagen
|
||||
- name: deploy
|
||||
run: ./gradlew publish
|
||||
- name: create tag
|
||||
|
||||
28
.github/workflows/publish.yml
vendored
Normal file
28
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Publish mod on Modrinth
|
||||
|
||||
on: [ push, workflow_dispatch ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout repository
|
||||
uses: actions/checkout@v3
|
||||
- name: validate gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@v2
|
||||
- name: setup jdk
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'oracle'
|
||||
- name: make gradle wrapper executable
|
||||
if: ${{ runner.os != 'Windows' }}
|
||||
run: |
|
||||
chmod +x ./gradlew
|
||||
touch local.properties
|
||||
- name: Generate data
|
||||
run: ./gradlew runDatagen
|
||||
- name: Publish Modrinth
|
||||
run: ./gradlew modrinth
|
||||
env:
|
||||
MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -33,3 +33,4 @@ local.properties
|
||||
*-autosave.kra
|
||||
*.kra~
|
||||
/src/generated/
|
||||
/libs/
|
||||
|
||||
55
CHANGELOG.md
55
CHANGELOG.md
@@ -1,55 +0,0 @@
|
||||
Versions before 2.1.2 have been backfilled; I gotta be more on top of changelogs.
|
||||
|
||||
# next version (unreleased)
|
||||
|
||||
* Start sketching out a proper API, accessible through `TemplatesClientApi.getInstance()`.
|
||||
* Not everything has been moved into the API package yet for ABI reasons.
|
||||
* Code cleanups that hopefully didn't break ABI compat
|
||||
* Remove some unused stuff from the jar
|
||||
* Vertical slab placement no longer completely sucks
|
||||
* Fix a bug where templates that look like blocks with randomized models, such as stone, could reroll their blockstate on every resource load.
|
||||
* Forgot to specify a random seed.
|
||||
* All templated blocks still use the *same* model, so templated stone will still not be randomly rotated/flipped, but at least it's now the *same* same model.
|
||||
|
||||
road map:
|
||||
|
||||
* want to fix texture orientation on vertical slabs/doors
|
||||
* really want to fix the way vertical slabs place lmao (it's so buggy)
|
||||
* clean up `StairShapeMaker`
|
||||
|
||||
# 2.1.1 (Aug 2, 2023)
|
||||
|
||||
Enable ambient-occlusion ("smooth lighting") on all Templates except for the slopes, which are still bugged
|
||||
|
||||
# 2.1.0 (Jul 31, 2023)
|
||||
|
||||
* Add a vertical slab template
|
||||
* Add a "tiny slope" template
|
||||
* Change the block entity NBT format to be much smaller
|
||||
* Reduce memory footprint of the block entity
|
||||
* Respect `doTileDrops`
|
||||
* Improve creative ctrl-pick behavior on glowing Templates
|
||||
* Adding a Barrier block to a Template makes it remove its model (not unbreakable)
|
||||
|
||||
# 2.0.4 (Jul 25, 2023)
|
||||
|
||||
* Apply more block tags
|
||||
* Apply item tags
|
||||
|
||||
# 2.0.3 (Jul 23, 2023)
|
||||
|
||||
* add Door and Iron Door templates
|
||||
* cool rivulet
|
||||
|
||||
# 2.0.2 (Jul 20, 2023)
|
||||
|
||||
* Add an Iron Trapdoor template
|
||||
* Add some more mod metadata (change name to "Templates 2", add authors, fix sources link)
|
||||
|
||||
# 2.0.1 (Jul 11, 2023)
|
||||
|
||||
Fix a duplication glitch with the Stair Template, which was retaining its block entity after being broken.
|
||||
|
||||
# 2.0.0 (Jul 11, 2023)
|
||||
|
||||
Initial release
|
||||
16
LICENSE
16
LICENSE
@@ -1,15 +1 @@
|
||||
Copyright (c) 2019 B0undarybreaker (Meredith Espinosa)
|
||||
Copyright (c) 2024 Adrien1106
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
MIT AND LGPL-3.0
|
||||
165
LICENSE.LGPL-3.0
Normal file
165
LICENSE.LGPL-3.0
Normal file
@@ -0,0 +1,165 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
15
LICENSE.MIT
Normal file
15
LICENSE.MIT
Normal file
@@ -0,0 +1,15 @@
|
||||
Copyright (c) 2019 B0undarybreaker (Meredith Espinosa)\
|
||||
Copyright (c) 2024 Adrien1106
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
43
README.md
Normal file
43
README.md
Normal file
@@ -0,0 +1,43 @@
|
||||
# ReFramed
|
||||
## Overview
|
||||
This Project is an unofficial port of [FramedBlocks](https://github.com/XFactHD/FramedBlocks)
|
||||
forked from [Templates 2](https://github.com/quat1024/templates-mod).
|
||||
It aims to add the same mechanics as [FramedBlocks](https://github.com/XFactHD/FramedBlocks) into Fabric.
|
||||
|
||||
For a complete Feature list please have a look at the [Modrinth](https://modrinth.com/mod/reframed) page.
|
||||
|
||||
## Information
|
||||
### Where to get it?
|
||||
The mod can be downloaded on [Modrinth](https://modrinth.com/mod/reframed).
|
||||
|
||||
### Issues/requests
|
||||
Any issues/requests may be addressed on this repository in the [Issues](https://github.com/DriHut/ReFramed/issues) section.
|
||||
|
||||
### Out of date?
|
||||
I will be adding new shapes as time passes, but I am currently not planing on keeping up with the newer versions of the game.
|
||||
That said this mod is under permissive licenses and make it easy for anyone to create their own fork.
|
||||
If anyone in the future plans to do so, I will gladly either link it from here and the modrinth page,
|
||||
or based on preferences add the person(s) to the project
|
||||
|
||||
### What Shapes are planed to be added
|
||||
Currently, the list of shapes to be added is pretty simple as the mod is still under development:
|
||||
- Wall
|
||||
- Fence
|
||||
- Pane
|
||||
- Button
|
||||
- Pressure Plate
|
||||
- Trapdoor
|
||||
- Door
|
||||
- Carpet
|
||||
- Post
|
||||
- Half Slab (maybe redundant with Layer)
|
||||
- Slabs Stair (a stair with one end being of a second theme, might be done in multiple blocks)
|
||||
|
||||
Any Ideas feel free to make a suggestion [here](https://github.com/DriHut/ReFramed/issues).
|
||||
|
||||
## License
|
||||
This work is dual-licensed under MIT and LGPL 3.0 both applying to different part of this project:
|
||||
- The LGPL 3.0 license applies to all the textures that can be found in [`assets/reframed/textures`](src/main/resources/assets/reframed/textures)
|
||||
- The MIT license applied to everything else so including all the code present within this project
|
||||
|
||||
`SPDX-License-Identifier: MIT AND LGPL-3.0`
|
||||
59
build.gradle
59
build.gradle
@@ -3,6 +3,7 @@ import fr.altarik.CreateTag
|
||||
|
||||
|
||||
plugins {
|
||||
id "com.modrinth.minotaur" version "2.+"
|
||||
id 'fabric-loom' version '1.5-SNAPSHOT'
|
||||
id 'maven-publish'
|
||||
}
|
||||
@@ -66,6 +67,23 @@ repositories {
|
||||
name 'altarik-releases'
|
||||
url 'https://repo.altarik.fr/releases/'
|
||||
}
|
||||
maven {
|
||||
url "https://maven.teamresourceful.com/repository/maven-public/"
|
||||
}
|
||||
maven {
|
||||
url "https://maven.resourcefulbees.com/repository/maven-public/"
|
||||
}
|
||||
exclusiveContent {
|
||||
forRepository {
|
||||
maven {
|
||||
name = "Modrinth"
|
||||
url = "https://api.modrinth.com/maven"
|
||||
}
|
||||
}
|
||||
filter {
|
||||
includeGroup "maven.modrinth"
|
||||
}
|
||||
}
|
||||
mavenCentral()
|
||||
|
||||
// Add repositories to retrieve artifacts from in here.
|
||||
@@ -82,7 +100,25 @@ dependencies {
|
||||
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
||||
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
|
||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||
// Indium and sodium for sodium support
|
||||
modCompileOnly "maven.modrinth:indium:${project.indium_version}+mc${project.minecraft_version}"
|
||||
modCompileOnly "maven.modrinth:sodium:mc${project.minecraft_version}-${project.sodium_version}"
|
||||
// modRuntimeOnly "maven.modrinth:indium:${project.indium_version}+mc${project.minecraft_version}"
|
||||
// modRuntimeOnly "maven.modrinth:sodium:mc${project.minecraft_version}-${project.sodium_version}"
|
||||
|
||||
// Athena for connected texture
|
||||
modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
|
||||
modRuntimeOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
|
||||
|
||||
// Continuity for connectedTextures
|
||||
modCompileOnly "maven.modrinth:continuity:${project.continuity_version}"
|
||||
modRuntimeOnly "maven.modrinth:continuity:${project.continuity_version}"
|
||||
|
||||
// Chipped to test athena implementation
|
||||
modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7"
|
||||
modRuntimeOnly "earth.terrarium.chipped:Chipped-fabric-${project.minecraft_version}:3.1.2"
|
||||
|
||||
// Fabric API.
|
||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||
}
|
||||
|
||||
@@ -91,6 +127,10 @@ processResources {
|
||||
inputs.property "minecraft_version", project.minecraft_version
|
||||
inputs.property "loader_version", project.loader_version
|
||||
inputs.property "mod_id", project.mod_id
|
||||
inputs.property "athena_version", project.athena_version
|
||||
inputs.property "indium_version", project.indium_version
|
||||
inputs.property "sodium_version", project.sodium_version
|
||||
inputs.property "continuity_version", project.continuity_version
|
||||
filteringCharset "UTF-8"
|
||||
|
||||
filesMatching("fabric.mod.json") {
|
||||
@@ -156,6 +196,23 @@ publishing {
|
||||
}
|
||||
}
|
||||
|
||||
// configure modrinth publication
|
||||
modrinth {
|
||||
token = getEnv("MODRINTH_TOKEN", local.getProperty("modrinth_token"))
|
||||
projectId = project.modrinth_id
|
||||
versionNumber = project.mod_version
|
||||
versionName = "${project.archives_base_name} ${project.mod_version}"
|
||||
versionType = project.mod_version.endsWith('SNAPSHOT') ? 'beta' : 'release'
|
||||
uploadFile = remapJar
|
||||
gameVersions = [project.minecraft_version]
|
||||
loaders = ["fabric"]
|
||||
dependencies {
|
||||
required.project "fabric-api"
|
||||
optional.version "b1ZV3DIJ", "${project.athena_version}"
|
||||
optional.version "Orvt0mRa", "${project.indium_version}+mc${project.minecraft_version}"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("reportToDiscord", ReportDiscord) {
|
||||
config.set(reportConfig)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,8 @@ yarn_mappings=1.20.4+build.3
|
||||
loader_version=0.15.6
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.0-SNAPSHOT
|
||||
modrinth_id = jCpoCBpn
|
||||
mod_version = 1.5.6
|
||||
maven_group = fr.adrien1106
|
||||
archives_base_name = ReFramed
|
||||
mod_id = reframed
|
||||
@@ -19,3 +20,8 @@ fabric_version=0.95.4+1.20.4
|
||||
|
||||
git_owner=Altarik
|
||||
git_repo=ReFramed
|
||||
|
||||
athena_version=3.3.0
|
||||
sodium_version=0.5.8
|
||||
indium_version=1.0.30
|
||||
continuity_version=3.0.0-beta.4+1.20.2
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
package fr.adrien1106.reframed;
|
||||
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import fr.adrien1106.reframed.block.*;
|
||||
import fr.adrien1106.reframed.item.ReFramedHammerItem;
|
||||
import fr.adrien1106.reframed.item.ReFramedBlueprintItem;
|
||||
import fr.adrien1106.reframed.item.ReFramedBlueprintWrittenItem;
|
||||
import fr.adrien1106.reframed.item.ReFramedScrewdriverItem;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
|
||||
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.registry.Registry;
|
||||
import net.minecraft.sound.BlockSoundGroup;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -22,62 +23,100 @@ import net.minecraft.world.World;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
|
||||
|
||||
/**
|
||||
* TODO handle grass side, multiple camos
|
||||
* TODO make block pairable by right click -> for v1.6
|
||||
* TODO Dynamic Ambient Occlusion -> for v1.6
|
||||
* TODO add minecraft models like wall fence etc -> for v1.6
|
||||
* TODO better connected textures -> maybe v1.6 ?
|
||||
* TODO support continuity overlays -> not scheduled
|
||||
*/
|
||||
public class ReFramed implements ModInitializer {
|
||||
public static final String MODID = "reframed";
|
||||
|
||||
public static final ArrayList<Block> BLOCKS = new ArrayList<>();
|
||||
public static Block CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, DOOR, TRAPDOOR, IRON_DOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE, CANDLE;
|
||||
public static Block CUBE, SMALL_CUBE, SMALL_CUBES_STEP, STAIR, HALF_STAIR, STAIRS_CUBE, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, SLAB, SLABS_CUBE, STEP, STEPS_SLAB, LAYER;
|
||||
|
||||
public static final ArrayList<Item> ITEMS = new ArrayList<>();
|
||||
public static Item HAMMER, SCREWDRIVER, BLUEPRINT, BLUEPRINT_WRITTEN;
|
||||
|
||||
public static ItemGroup ITEM_GROUP;
|
||||
|
||||
public static BlockEntityType<ReFramedEntity> REFRAMED_BLOCK_ENTITY;
|
||||
public static BlockEntityType<ReFramedDoubleEntity> REFRAMED_DOUBLE_BLOCK_ENTITY;
|
||||
|
||||
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
//registerReFramed mutates FRAMES as a side effect, which is a List, so order is preserved
|
||||
//the ordering is used in the creative tab, so they're roughly sorted by encounter order of the
|
||||
//corresponding vanilla block in the "search" creative tab... with the non-vanilla "post" and
|
||||
//"vertical slab" inserted where they fit ...and i moved the lever way up next to the pressureplate
|
||||
//and button, because they're redstoney... hopefully this ordering makes sense lol
|
||||
CUBE = registerReFramed("cube" , new ReFramedBlock(ReFramedInteractionUtil.makeSettings()));
|
||||
STAIRS = registerReFramed("stairs" , new ReFramedStairsBlock(cp(Blocks.OAK_STAIRS)));
|
||||
SLAB = registerReFramed("slab" , new ReFramedSlabBlock(cp(Blocks.OAK_SLAB)));
|
||||
POST = registerReFramed("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE)));
|
||||
FENCE = registerReFramed("fence" , new ReFramedFenceBlock(cp(Blocks.OAK_FENCE)));
|
||||
FENCE_GATE = registerReFramed("fence_gate" , new ReFramedFenceGateBlock(cp(Blocks.OAK_FENCE_GATE)));
|
||||
DOOR = registerReFramed("door" , new ReFramedDoorBlock(cp(Blocks.OAK_DOOR), BlockSetType.OAK));
|
||||
TRAPDOOR = registerReFramed("trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.OAK_TRAPDOOR), BlockSetType.OAK));
|
||||
IRON_DOOR = registerReFramed("iron_door" , new ReFramedDoorBlock(cp(Blocks.IRON_DOOR), BlockSetType.IRON));
|
||||
IRON_TRAPDOOR = registerReFramed("iron_trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.IRON_TRAPDOOR), BlockSetType.IRON));
|
||||
PRESSURE_PLATE = registerReFramed("pressure_plate", new ReFramedPressurePlateBlock(cp(Blocks.OAK_PRESSURE_PLATE)));
|
||||
BUTTON = registerReFramed("button" , new ReFramedButtonBlock(cp(Blocks.OAK_BUTTON)));
|
||||
LEVER = registerReFramed("lever" , new ReFramedLeverBlock(cp(Blocks.LEVER)));
|
||||
WALL = registerReFramed("wall" , new ReFramedWallBlock(ReFramedInteractionUtil.makeSettings()));
|
||||
CARPET = registerReFramed("carpet" , new ReFramedCarpetBlock(cp(Blocks.WHITE_CARPET)));
|
||||
PANE = registerReFramed("pane" , new ReFramedPaneBlock(cp(Blocks.GLASS_PANE)));
|
||||
CANDLE = registerReFramed("candle" , new ReFramedCandleBlock(ReFramedCandleBlock.configureSettings(cp(Blocks.CANDLE))));
|
||||
CUBE = registerBlock("cube" , new ReFramedBlock(cp(Blocks.OAK_PLANKS)));
|
||||
SMALL_CUBE = registerBlock("small_cube" , new ReFramedSmallCubeBlock(cp(Blocks.OAK_PLANKS)));
|
||||
SMALL_CUBES_STEP = registerBlock("small_cubes_step" , new ReFramedSmallCubesStepBlock(cp(Blocks.OAK_PLANKS)));
|
||||
STAIR = registerBlock("stair" , new ReFramedStairBlock(cp(Blocks.OAK_STAIRS)));
|
||||
STAIRS_CUBE = registerBlock("stairs_cube" , new ReFramedStairsCubeBlock(cp(Blocks.OAK_STAIRS)));
|
||||
HALF_STAIR = registerBlock("half_stair" , new ReFramedHalfStairBlock(cp(Blocks.OAK_STAIRS)));
|
||||
HALF_STAIRS_SLAB = registerBlock("half_stairs_slab" , new ReFramedHalfStairsSlabBlock(cp(Blocks.OAK_STAIRS)));
|
||||
HALF_STAIRS_STAIR = registerBlock("half_stairs_stair" , new ReFramedHalfStairsStairBlock(cp(Blocks.OAK_STAIRS)));
|
||||
LAYER = registerBlock("layer" , new ReFramedLayerBlock(cp(Blocks.OAK_SLAB)));
|
||||
SLAB = registerBlock("slab" , new ReFramedSlabBlock(cp(Blocks.OAK_SLAB)));
|
||||
SLABS_CUBE = registerBlock("slabs_cube" , new ReFramedSlabsCubeBlock(cp(Blocks.OAK_SLAB)));
|
||||
STEP = registerBlock("step" , new ReFramedStepBlock(cp(Blocks.OAK_SLAB)));
|
||||
STEPS_SLAB = registerBlock("steps_slab" , new ReFramedStepsSlabBlock(cp(Blocks.OAK_SLAB)));
|
||||
|
||||
HAMMER = registerItem("hammer" , new ReFramedHammerItem(new Item.Settings().maxCount(1)));
|
||||
SCREWDRIVER = registerItem("screwdriver" , new ReFramedScrewdriverItem(new Item.Settings().maxCount(1)));
|
||||
BLUEPRINT = registerItem("blueprint" , new ReFramedBlueprintItem(new Item.Settings()));
|
||||
BLUEPRINT_WRITTEN = registerItem("blueprint_written" , new ReFramedBlueprintWrittenItem(new Item.Settings().maxCount(1)));
|
||||
|
||||
REFRAMED_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("camo"),
|
||||
FabricBlockEntityTypeBuilder.create((pos, state) -> new ReFramedEntity(REFRAMED_BLOCK_ENTITY, pos, state), BLOCKS.toArray(new Block[0])).build(null)
|
||||
FabricBlockEntityTypeBuilder.create(
|
||||
(pos, state) -> new ReFramedEntity(REFRAMED_BLOCK_ENTITY, pos, state),
|
||||
BLOCKS.stream()
|
||||
.filter(block -> !(block instanceof ReFramedDoubleBlock))
|
||||
.toArray(Block[]::new)).build(null)
|
||||
);
|
||||
|
||||
Registry.register(Registries.ITEM_GROUP, id("tab"), FabricItemGroup.builder()
|
||||
REFRAMED_DOUBLE_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("double_camo"),
|
||||
FabricBlockEntityTypeBuilder.create(
|
||||
(pos, state) -> new ReFramedDoubleEntity(REFRAMED_DOUBLE_BLOCK_ENTITY, pos, state),
|
||||
BLOCKS.stream()
|
||||
.filter(block -> block instanceof ReFramedDoubleBlock)
|
||||
.toArray(Block[]::new)).build(null)
|
||||
);
|
||||
|
||||
ITEM_GROUP = Registry.register(Registries.ITEM_GROUP, id("tab"), FabricItemGroup.builder()
|
||||
.displayName(Text.translatable("itemGroup.reframed.tab"))
|
||||
.icon(() -> new ItemStack(SLAB))
|
||||
.entries((ctx, e) -> e.addAll(BLOCKS.stream().map(ItemStack::new).collect(Collectors.toList()))).build()
|
||||
.entries((ctx, e) -> e.addAll(
|
||||
Stream.concat(
|
||||
ITEMS.stream().filter(item -> item != BLUEPRINT_WRITTEN),
|
||||
BLOCKS.stream().map(Block::asItem)
|
||||
).map(Item::getDefaultStack).toList())
|
||||
).build()
|
||||
);
|
||||
}
|
||||
|
||||
private static AbstractBlock.Settings cp(Block base) {
|
||||
return ReFramedInteractionUtil.configureSettings(AbstractBlock.Settings.copy(base));
|
||||
return AbstractBlock.Settings.copy(base)
|
||||
.luminance(state -> state.contains(LIGHT) && state.get(LIGHT) ? 15 : 0)
|
||||
.nonOpaque()
|
||||
.sounds(BlockSoundGroup.WOOD)
|
||||
.hardness(0.2f)
|
||||
.suffocates((a,b,c) -> false)
|
||||
.blockVision((a,b,c) -> false);
|
||||
}
|
||||
|
||||
private static <B extends Block> B registerReFramed(String path, B block) {
|
||||
private static <I extends Item> I registerItem(String path, I item) {
|
||||
Identifier id = id(path);
|
||||
Registry.register(Registries.ITEM, id, item);
|
||||
ITEMS.add(item);
|
||||
return item;
|
||||
}
|
||||
|
||||
private static <B extends Block> B registerBlock(String path, B block) {
|
||||
Identifier id = id(path);
|
||||
|
||||
Registry.register(Registries.BLOCK, id, block);
|
||||
|
||||
@@ -1,29 +1,62 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import fr.adrien1106.reframed.generator.RecipeSetter;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.ItemScatterer;
|
||||
import net.minecraft.util.collection.DefaultedList;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.GameRules;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedBlock extends Block implements BlockEntityProvider {
|
||||
import java.util.List;
|
||||
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
|
||||
|
||||
public class ReFramedBlock extends Block implements BlockEntityProvider, RecipeSetter {
|
||||
|
||||
public ReFramedBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
setDefaultState(getDefaultState().with(LIGHT, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a record for the key so that it replaces the blockstate
|
||||
* which may have states that returns same models
|
||||
* @param state - the state_key to generate the key from
|
||||
* @return a cache key with only relevant properties
|
||||
*/
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the amount of models the block can have prevents allocating too much space for a model
|
||||
*/
|
||||
public int getModelStateCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
//For addon devs: override this so your blocks don't end up trying to place my block entity, my BlockEntityType only handles blocks internal to the mod
|
||||
@@ -35,51 +68,118 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
super.appendProperties(builder.add(LIGHT));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
return ReFramedEntity.getNbtLightLevel(super.getPlacementState(ctx), ctx.getStack());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit);
|
||||
ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
|
||||
if (result.isAccepted()) return result;
|
||||
return BlockHelper.useCamo(state, world, pos, player, hand, hit, 1);
|
||||
|
||||
}
|
||||
|
||||
// don't like this but might be useful
|
||||
protected ActionResult superUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
return super.onUse(state, world, pos, player, hand, hit);
|
||||
}
|
||||
|
||||
protected boolean canUse(World world, BlockPos pos, PlayerEntity player) {
|
||||
return player.canModifyBlocks() && world.canPlayerModifyAt(player, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
if(!state.isOf(newState.getBlock()) &&
|
||||
world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity &&
|
||||
world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
|
||||
) {
|
||||
DefaultedList<ItemStack> drops = DefaultedList.of();
|
||||
|
||||
List<BlockState> themes = frame_entity.getThemes();
|
||||
themes.forEach(theme -> {
|
||||
if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock()));
|
||||
});
|
||||
|
||||
if(frame_entity.emitsRedstone()
|
||||
&& themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) != 0))
|
||||
drops.add(new ItemStack(Items.REDSTONE_TORCH));
|
||||
if(frame_entity.emitsLight()
|
||||
&& themes.stream().noneMatch(theme -> theme.getLuminance() != 0))
|
||||
drops.add(new ItemStack(Items.GLOWSTONE_DUST));
|
||||
if(!frame_entity.isSolid()
|
||||
&& themes.stream().anyMatch(AbstractBlockState::isSolid))
|
||||
drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
|
||||
|
||||
ItemScatterer.spawn(world, pos, drops);
|
||||
}
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
if(world.isClient && world.getBlockEntity(pos) instanceof ReFramedEntity be) {
|
||||
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
|
||||
if(tag != null) be.readNbt(tag);
|
||||
}
|
||||
super.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
return isGhost(view, pos)
|
||||
? VoxelShapes.empty()
|
||||
: super.getCollisionShape(state, view, pos, ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
|
||||
return isGhost(view, pos)
|
||||
? VoxelShapes.empty()
|
||||
: super.getCullingShape(state, view, pos);
|
||||
}
|
||||
|
||||
public VoxelShape getShape(BlockState state, int i) {
|
||||
// assuming the shape don't need the world and position
|
||||
return getOutlineShape(state, null, null, null);
|
||||
}
|
||||
|
||||
public boolean isGhost(BlockView view, BlockPos pos) {
|
||||
return view.getBlockEntity(pos) instanceof ReFramedEntity be && !be.isSolid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
return getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
public int getTopThemeIndex(BlockState state) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
ShapedRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.pattern("III")
|
||||
.pattern("I~I")
|
||||
.pattern("III")
|
||||
.input('I', Items.BAMBOO)
|
||||
.input('~', Items.STRING)
|
||||
.criterion(FabricRecipeProvider.hasItem(Items.BAMBOO), FabricRecipeProvider.conditionsFromItem(Items.BAMBOO))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ButtonBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedButtonBlock extends ButtonBlock implements BlockEntityProvider {
|
||||
public ReFramedButtonBlock(Settings settings) {
|
||||
this(settings, BlockSetType.OAK, 30);
|
||||
}
|
||||
|
||||
public ReFramedButtonBlock(Settings settings, BlockSetType blockSetType, int i) {
|
||||
super(blockSetType, i, settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return super.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
boolean a = 0 != super.getWeakRedstonePower(state, view, pos, dir);
|
||||
boolean b = 0 != ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
return (a ^ b) ? 15 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
if(getDirection(state) != dir) return 0;
|
||||
else return getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.CandleBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedCandleBlock extends CandleBlock implements BlockEntityProvider {
|
||||
public ReFramedCandleBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
public static AbstractBlock.Settings configureSettings(AbstractBlock.Settings in) {
|
||||
return in.luminance(state -> Math.max(ReFramedInteractionUtil.luminance(state), CandleBlock.STATE_TO_LUMINANCE.applyAsInt(state)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
super.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.CarpetBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedCarpetBlock extends CarpetBlock implements BlockEntityProvider {
|
||||
public ReFramedCarpetBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.DoorBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedDoorBlock extends DoorBlock implements BlockEntityProvider {
|
||||
public ReFramedDoorBlock(Settings settings, BlockSetType blockSetType) {
|
||||
super(blockSetType, settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
super.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static net.minecraft.util.shape.VoxelShapes.empty;
|
||||
import static net.minecraft.util.shape.VoxelShapes.fullCube;
|
||||
|
||||
public abstract class ReFramedDoubleBlock extends ReFramedBlock {
|
||||
public ReFramedDoubleBlock(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_DOUBLE_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
public int getHitShape(BlockState state, BlockHitResult hit) {
|
||||
return getHitShape(state, hit.getPos(), hit.getBlockPos(), hit.getSide());
|
||||
}
|
||||
|
||||
public int getHitShape(BlockState state, Vec3d hit, BlockPos pos, Direction side) {
|
||||
VoxelShape first_shape = getShape(state, 1);
|
||||
VoxelShape second_shape = getShape(state, 2);
|
||||
|
||||
// Determine if any of the two shape is covering the side entirely
|
||||
if (isFaceFullSquare(first_shape, side)) return 1;
|
||||
if (isFaceFullSquare(second_shape, side)) return 2;
|
||||
|
||||
Vec3d rel = BlockHelper.getRelativePos(hit, pos);
|
||||
if (BlockHelper.cursorMatchesFace(first_shape, rel)) return 1;
|
||||
if (BlockHelper.cursorMatchesFace(second_shape, rel)) return 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTransparent(BlockState state, BlockView world, BlockPos pos) {
|
||||
return world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity
|
||||
&& framed_entity.getThemes().stream().allMatch(theme -> theme.isTransparent(world, pos));
|
||||
}
|
||||
|
||||
public VoxelShape getRenderOutline(BlockState state, BlockHitResult hit) {
|
||||
return getShape(state, getHitShape(state, hit));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return getCullingShape(state, view, pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
|
||||
return isGhost(view, pos) ? empty() : fullCube();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit);
|
||||
ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
|
||||
if (result.isAccepted()) return result;
|
||||
return BlockHelper.useCamo(state, world, pos, player, hand, hit, getHitShape(state, hit));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtHelper;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ReFramedDoubleEntity extends ReFramedEntity {
|
||||
|
||||
protected BlockState second_state = Blocks.AIR.getDefaultState();
|
||||
|
||||
public ReFramedDoubleEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||
super(type, pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getTheme(int i) {
|
||||
return i == 2 ? second_state : super.getTheme(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BlockState> getThemes() {
|
||||
List<BlockState> themes = super.getThemes();
|
||||
themes.add(second_state);
|
||||
return themes;
|
||||
}
|
||||
|
||||
public void setTheme(BlockState new_state, int i) {
|
||||
if(i == 2) {
|
||||
if (Objects.equals(second_state, new_state)) return;
|
||||
second_state = new_state;
|
||||
markDirtyAndDispatch();
|
||||
} else super.setTheme(new_state, i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNbt(NbtCompound nbt) {
|
||||
super.readNbt(nbt);
|
||||
|
||||
BlockState rendered_state = second_state;// keep previous state_key to check if rerender is needed
|
||||
second_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 2));
|
||||
|
||||
// Force a chunk remesh on the client if the displayed blockstate has changed
|
||||
if(world != null && world.isClient && !Objects.equals(rendered_state, second_state)) {
|
||||
ReFramed.chunkRerenderProxy.accept(world, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNbt(NbtCompound nbt) {
|
||||
super.writeNbt(nbt);
|
||||
|
||||
if(second_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 2, NbtHelper.fromBlockState(second_state));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockProperties;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
@@ -21,26 +21,22 @@ import net.minecraft.util.math.BlockPos;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
//Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic,
|
||||
//is pretty important since players might place a lot of them. There were tons and tons of these at Blanketcon.
|
||||
//To that end, most of the state has been crammed into a bitfield.
|
||||
//To that end, most of the state_key has been crammed into a bitfield.
|
||||
public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity {
|
||||
protected BlockState renderedState = Blocks.AIR.getDefaultState();
|
||||
protected byte bitfield = DEFAULT_BITFIELD;
|
||||
protected BlockState first_state = Blocks.AIR.getDefaultState();
|
||||
protected byte bit_field = SOLIDITY_MASK;
|
||||
|
||||
protected static final int SPENT_GLOWSTONE_DUST_MASK = 0b00000001;
|
||||
protected static final int SPENT_REDSTONE_TORCH_MASK = 0b00000010;
|
||||
protected static final int SPENT_POPPED_CHORUS_MASK = 0b00000100;
|
||||
protected static final int EMITS_REDSTONE_MASK = 0b00001000;
|
||||
protected static final int IS_SOLID_MASK = 0b00010000;
|
||||
protected static final byte DEFAULT_BITFIELD = IS_SOLID_MASK; //brand-new frames shall be solid
|
||||
protected static final byte LIGHT_MASK = 0b001;
|
||||
protected static final byte REDSTONE_MASK = 0b010;
|
||||
protected static final byte SOLIDITY_MASK = 0b100;
|
||||
|
||||
//Using one-character names is a little brash, like, what if there's a mod that adds crap to the NBT of every
|
||||
//block entity, and uses short names for the same reason I am (because there are lots and lots of block entities)?
|
||||
//Kinda doubt it?
|
||||
protected static final String BLOCKSTATE_KEY = "s";
|
||||
public static final String BLOCKSTATE_KEY = "s";
|
||||
protected static final String BITFIELD_KEY = "b";
|
||||
|
||||
public ReFramedEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||
@@ -48,145 +44,116 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readNbt(NbtCompound tag) {
|
||||
super.readNbt(tag);
|
||||
public void readNbt(NbtCompound nbt) {
|
||||
super.readNbt(nbt);
|
||||
|
||||
BlockState lastRenderedState = renderedState;
|
||||
BlockState rendered_state = first_state; // keep previous state_key to check if rerender is needed
|
||||
first_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 1));
|
||||
if (nbt.contains(BITFIELD_KEY)) bit_field = nbt.getByte(BITFIELD_KEY);
|
||||
|
||||
if(tag.contains("BlockState")) { //2.0.4 and earlier
|
||||
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState"));
|
||||
|
||||
if(tag.getBoolean("spentglow")) spentGlowstoneDust();
|
||||
if(tag.getBoolean("spentredst")) spentRedstoneTorch();
|
||||
if(tag.getBoolean("spentchor")) spentPoppedChorus();
|
||||
setEmitsRedstone(tag.getBoolean("emitsredst"));
|
||||
setSolidity(!tag.contains("solid") || tag.getBoolean("solid")); //default to "true" if it's nonexistent
|
||||
} else {
|
||||
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound(BLOCKSTATE_KEY));
|
||||
bitfield = tag.contains(BITFIELD_KEY) ? tag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD;
|
||||
}
|
||||
|
||||
//Force a chunk remesh on the client if the displayed blockstate has changed
|
||||
if(world != null && world.isClient && !Objects.equals(lastRenderedState, renderedState)) {
|
||||
// Force a chunk remesh on the client if the displayed blockstate has changed
|
||||
if(world != null && world.isClient && !Objects.equals(rendered_state, first_state)) {
|
||||
ReFramed.chunkRerenderProxy.accept(world, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeNbt(NbtCompound tag) {
|
||||
super.writeNbt(tag);
|
||||
public void writeNbt(NbtCompound nbt) {
|
||||
super.writeNbt(nbt);
|
||||
|
||||
if(renderedState != Blocks.AIR.getDefaultState()) tag.put(BLOCKSTATE_KEY, NbtHelper.fromBlockState(renderedState));
|
||||
if(bitfield != DEFAULT_BITFIELD) tag.putByte(BITFIELD_KEY, bitfield);
|
||||
if(first_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(first_state));
|
||||
if(bit_field != SOLIDITY_MASK) nbt.putByte(BITFIELD_KEY, bit_field);
|
||||
}
|
||||
|
||||
public static @NotNull BlockState readStateFromItem(ItemStack stack) {
|
||||
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
|
||||
if(blockEntityTag == null) return Blocks.AIR.getDefaultState();
|
||||
public static @NotNull BlockState readStateFromItem(ItemStack stack, int state) {
|
||||
NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
|
||||
if(nbt == null) return Blocks.AIR.getDefaultState();
|
||||
|
||||
//slightly paranoid NBT handling cause you never know what mysteries are afoot with items
|
||||
NbtElement subElement;
|
||||
if(blockEntityTag.contains(BLOCKSTATE_KEY)) subElement = blockEntityTag.get(BLOCKSTATE_KEY); //2.0.5
|
||||
else if(blockEntityTag.contains("BlockState")) subElement = blockEntityTag.get("BlockState"); //old 2.0.4 items
|
||||
NbtElement element;
|
||||
if(nbt.contains(BLOCKSTATE_KEY + state)) element = nbt.get(BLOCKSTATE_KEY + state);
|
||||
else return Blocks.AIR.getDefaultState();
|
||||
|
||||
if(!(subElement instanceof NbtCompound subCompound)) return Blocks.AIR.getDefaultState();
|
||||
|
||||
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), subCompound);
|
||||
if(!(element instanceof NbtCompound compound)) return Blocks.AIR.getDefaultState();
|
||||
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), compound);
|
||||
}
|
||||
|
||||
//Awkward: usually the BlockState is the source of truth for things like the "emits light" blockstate, but if you
|
||||
//ctrl-pick a glowing block and place it, it should still be glowing. This is some hacky shit that guesses the value of
|
||||
//the LIGHT blockstate based off information in the NBT tag, and also prevents bugginess like "the blockstate is not
|
||||
//glowing but the copied NBT thinks glowstone dust was already added, so it refuses to accept more dust"
|
||||
public static @Nullable BlockState weirdNbtLightLevelStuff(@Nullable BlockState state, ItemStack stack) {
|
||||
public static @Nullable BlockState getNbtLightLevel(@Nullable BlockState state, ItemStack stack) {
|
||||
if(state == null || stack == null) return state;
|
||||
|
||||
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
|
||||
if(blockEntityTag == null) return state;
|
||||
NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
|
||||
if(nbt == null) return state;
|
||||
|
||||
if(state.contains(ReFramedInteractionUtil.LIGHT)) {
|
||||
state = state.with(ReFramedInteractionUtil.LIGHT,
|
||||
blockEntityTag.getBoolean("spentglow") || //2.0.4
|
||||
((blockEntityTag.contains(BITFIELD_KEY) ? blockEntityTag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD) & SPENT_GLOWSTONE_DUST_MASK) != 0 || //2.0.5
|
||||
readStateFromItem(stack).getLuminance() != 0 //glowstone dust wasn't manually added, the block just emits light
|
||||
if(state.contains(BlockProperties.LIGHT)) {
|
||||
state = state.with(BlockProperties.LIGHT,
|
||||
((nbt.contains(BITFIELD_KEY)
|
||||
? nbt.getByte(BITFIELD_KEY)
|
||||
: SOLIDITY_MASK)
|
||||
& LIGHT_MASK) != 0
|
||||
);
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
//RenderAttachmentBlockEntity impl. Note that ThemeableBlockEntity depends on this returning a BlockState object.
|
||||
@Override
|
||||
public BlockState getRenderAttachmentData() {
|
||||
return renderedState;
|
||||
public BlockState getTheme(int i) {
|
||||
return first_state;
|
||||
}
|
||||
|
||||
public void setRenderedState(BlockState newState) {
|
||||
if(!Objects.equals(renderedState, newState)) {
|
||||
renderedState = newState;
|
||||
@Override
|
||||
public List<BlockState> getThemes() {
|
||||
List<BlockState> themes = new ArrayList<>();
|
||||
themes.add(first_state);
|
||||
return themes;
|
||||
}
|
||||
|
||||
public void setTheme(BlockState new_state, int i) {
|
||||
if(!Objects.equals(first_state, new_state)) {
|
||||
first_state = new_state;
|
||||
markDirtyAndDispatch();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSpentGlowstoneDust() {
|
||||
return (bitfield & SPENT_GLOWSTONE_DUST_MASK) != 0;
|
||||
/* --------------------------------------------------- ADDONS --------------------------------------------------- */
|
||||
public boolean emitsLight() {
|
||||
return (bit_field & LIGHT_MASK) != 0;
|
||||
}
|
||||
|
||||
public void spentGlowstoneDust() {
|
||||
bitfield |= SPENT_GLOWSTONE_DUST_MASK;
|
||||
public void toggleLight() {
|
||||
if (emitsLight()) bit_field &= ~LIGHT_MASK;
|
||||
else bit_field |= LIGHT_MASK;
|
||||
markDirtyAndDispatch();
|
||||
}
|
||||
|
||||
public boolean hasSpentRedstoneTorch() {
|
||||
return (bitfield & SPENT_REDSTONE_TORCH_MASK) != 0;
|
||||
}
|
||||
public void toggleRedstone() {
|
||||
if (emitsRedstone()) bit_field &= ~REDSTONE_MASK;
|
||||
else bit_field |= REDSTONE_MASK;
|
||||
|
||||
public void spentRedstoneTorch() {
|
||||
bitfield |= SPENT_REDSTONE_TORCH_MASK;
|
||||
markDirtyAndDispatch();
|
||||
}
|
||||
|
||||
public boolean hasSpentPoppedChorus() {
|
||||
return (bitfield & SPENT_POPPED_CHORUS_MASK) != 0;
|
||||
}
|
||||
|
||||
public void spentPoppedChorus() {
|
||||
bitfield |= SPENT_POPPED_CHORUS_MASK;
|
||||
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
|
||||
markDirtyAndDispatch();
|
||||
}
|
||||
|
||||
public boolean emitsRedstone() {
|
||||
return (bitfield & EMITS_REDSTONE_MASK) != 0;
|
||||
return (bit_field & REDSTONE_MASK) != 0;
|
||||
}
|
||||
|
||||
public void setEmitsRedstone(boolean nextEmitsRedstone) {
|
||||
boolean currentlyEmitsRedstone = emitsRedstone();
|
||||
public void toggleSolidity() {
|
||||
if (isSolid()) bit_field &= ~SOLIDITY_MASK;
|
||||
else bit_field |= SOLIDITY_MASK;
|
||||
|
||||
if(currentlyEmitsRedstone != nextEmitsRedstone) {
|
||||
if(currentlyEmitsRedstone) bitfield &= ~EMITS_REDSTONE_MASK;
|
||||
else bitfield |= EMITS_REDSTONE_MASK;
|
||||
markDirtyAndDispatch();
|
||||
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
|
||||
}
|
||||
if(world != null) world.setBlockState(pos, getCachedState());
|
||||
markDirtyAndDispatch();
|
||||
}
|
||||
|
||||
public boolean isSolid() {
|
||||
return (bitfield & IS_SOLID_MASK) != 0;
|
||||
return (bit_field & SOLIDITY_MASK) != 0;
|
||||
}
|
||||
|
||||
public void setSolidity(boolean nextSolid) {
|
||||
boolean currentlySolid = isSolid();
|
||||
|
||||
if(currentlySolid != nextSolid) {
|
||||
if(currentlySolid) bitfield &= ~IS_SOLID_MASK;
|
||||
else bitfield |= IS_SOLID_MASK;
|
||||
markDirtyAndDispatch();
|
||||
if(world != null) world.setBlockState(pos, getCachedState()); //do i need to invalidate any shape caches or something
|
||||
}
|
||||
}
|
||||
|
||||
//<standard blockentity boilerplate>
|
||||
@Nullable
|
||||
@Override
|
||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
||||
@@ -195,8 +162,6 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
||||
|
||||
@Override
|
||||
public NbtCompound toInitialChunkDataNbt() {
|
||||
//TERRIBLE yarn name, this is "getUpdateTag", it's the nbt that will be sent to clients
|
||||
//and it just calls "writeNbt"
|
||||
return createNbt();
|
||||
}
|
||||
|
||||
@@ -208,5 +173,4 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
||||
markDirty();
|
||||
dispatch();
|
||||
}
|
||||
//</standard blockentity boilerplate>
|
||||
}
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.FenceBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedFenceBlock extends FenceBlock implements BlockEntityProvider {
|
||||
public ReFramedFenceBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import fr.adrien1106.reframed.util.ReframedInteractible;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.FenceGateBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.WoodType;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedFenceGateBlock extends FenceGateBlock implements BlockEntityProvider, ReframedInteractible {
|
||||
public ReFramedFenceGateBlock(Settings settings, WoodType woodType) {
|
||||
super(woodType, settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
public ReFramedFenceGateBlock(Settings settings) {
|
||||
this(settings, WoodType.OAK);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.util.VoxelHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.Corner;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.BlockStateSupplier;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.function.BooleanBiFunction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER_FACE;
|
||||
import static fr.adrien1106.reframed.util.blocks.Corner.*;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
|
||||
public class ReFramedHalfStairBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
|
||||
|
||||
public static final VoxelShape[] HALF_STAIR_VOXELS;
|
||||
|
||||
private record ModelCacheKey(Corner corner, int face) {}
|
||||
|
||||
public ReFramedHalfStairBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN).with(CORNER_FACE, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return new ModelCacheKey(state.get(CORNER), state.get(CORNER_FACE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 24;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(CORNER,CORNER_FACE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
Corner corner = BlockHelper.getPlacementCorner(ctx);
|
||||
return super.getPlacementState(ctx)
|
||||
.with(CORNER, corner)
|
||||
.with(CORNER_FACE, corner.getDirectionIndex(ctx.getSide().getOpposite()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return HALF_STAIR_VOXELS[state.get(CORNER_FACE) + state.get(CORNER).getID() * 3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateSupplier getMultipart() {
|
||||
return getHalfStairMultipart(
|
||||
this,
|
||||
ReFramed.id("half_stair_down_special"),
|
||||
ReFramed.id("half_stair_side_special")
|
||||
);
|
||||
}
|
||||
|
||||
public static BlockStateSupplier getHalfStairMultipart(Block block, Identifier model_down, Identifier model_side) {
|
||||
return MultipartBlockStateSupplier.create(block)
|
||||
.with(GBlockstate.when(CORNER, NORTH_EAST_DOWN, CORNER_FACE, 1),
|
||||
GBlockstate.variant(model_side, true, R0, R0))
|
||||
.with(GBlockstate.when(CORNER, NORTH_EAST_DOWN, CORNER_FACE, 0),
|
||||
GBlockstate.variant(model_side, true, R90, R270))
|
||||
.with(GBlockstate.when(CORNER, NORTH_EAST_DOWN, CORNER_FACE, 2),
|
||||
GBlockstate.variant(model_down, true, R0, R0))
|
||||
|
||||
.with(GBlockstate.when(CORNER, EAST_SOUTH_DOWN, CORNER_FACE, 1),
|
||||
GBlockstate.variant(model_side, true, R0, R90))
|
||||
.with(GBlockstate.when(CORNER, EAST_SOUTH_DOWN, CORNER_FACE, 0),
|
||||
GBlockstate.variant(model_side, true, R90, R0))
|
||||
.with(GBlockstate.when(CORNER, EAST_SOUTH_DOWN, CORNER_FACE, 2),
|
||||
GBlockstate.variant(model_down, true, R0, R90))
|
||||
|
||||
.with(GBlockstate.when(CORNER, SOUTH_WEST_DOWN, CORNER_FACE, 1),
|
||||
GBlockstate.variant(model_side, true, R0, R180))
|
||||
.with(GBlockstate.when(CORNER, SOUTH_WEST_DOWN, CORNER_FACE, 0),
|
||||
GBlockstate.variant(model_side, true, R90, R90))
|
||||
.with(GBlockstate.when(CORNER, SOUTH_WEST_DOWN, CORNER_FACE, 2),
|
||||
GBlockstate.variant(model_down, true, R0, R180))
|
||||
|
||||
.with(GBlockstate.when(CORNER, WEST_NORTH_DOWN, CORNER_FACE, 1),
|
||||
GBlockstate.variant(model_side, true, R0, R270))
|
||||
.with(GBlockstate.when(CORNER, WEST_NORTH_DOWN, CORNER_FACE, 0),
|
||||
GBlockstate.variant(model_side, true, R90, R180))
|
||||
.with(GBlockstate.when(CORNER, WEST_NORTH_DOWN, CORNER_FACE, 2),
|
||||
GBlockstate.variant(model_down, true, R0, R270))
|
||||
|
||||
.with(GBlockstate.when(CORNER, EAST_SOUTH_UP, CORNER_FACE, 0),
|
||||
GBlockstate.variant(model_side, true, R180, R0))
|
||||
.with(GBlockstate.when(CORNER, EAST_SOUTH_UP, CORNER_FACE, 1),
|
||||
GBlockstate.variant(model_side, true, R270, R90))
|
||||
.with(GBlockstate.when(CORNER, EAST_SOUTH_UP, CORNER_FACE, 2),
|
||||
GBlockstate.variant(model_down, true, R180, R0))
|
||||
|
||||
.with(GBlockstate.when(CORNER, SOUTH_WEST_UP, CORNER_FACE, 0),
|
||||
GBlockstate.variant(model_side, true, R180, R90))
|
||||
.with(GBlockstate.when(CORNER, SOUTH_WEST_UP, CORNER_FACE, 1),
|
||||
GBlockstate.variant(model_side, true, R270, R180))
|
||||
.with(GBlockstate.when(CORNER, SOUTH_WEST_UP, CORNER_FACE, 2),
|
||||
GBlockstate.variant(model_down, true, R180, R90))
|
||||
|
||||
.with(GBlockstate.when(CORNER, WEST_NORTH_UP, CORNER_FACE, 0),
|
||||
GBlockstate.variant(model_side, true, R180, R180))
|
||||
.with(GBlockstate.when(CORNER, WEST_NORTH_UP, CORNER_FACE, 1),
|
||||
GBlockstate.variant(model_side, true, R270, R270))
|
||||
.with(GBlockstate.when(CORNER, WEST_NORTH_UP, CORNER_FACE, 2),
|
||||
GBlockstate.variant(model_down, true, R180, R180))
|
||||
|
||||
.with(GBlockstate.when(CORNER, NORTH_EAST_UP, CORNER_FACE, 0),
|
||||
GBlockstate.variant(model_side, true, R180, R270))
|
||||
.with(GBlockstate.when(CORNER, NORTH_EAST_UP, CORNER_FACE, 1),
|
||||
GBlockstate.variant(model_side, true, R270, R0))
|
||||
.with(GBlockstate.when(CORNER, NORTH_EAST_UP, CORNER_FACE, 2),
|
||||
GBlockstate.variant(model_down, true, R180, R270));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 2);
|
||||
ShapedRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this, 4)
|
||||
.pattern("I ")
|
||||
.pattern("II ")
|
||||
.input('I', ReFramed.CUBE)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
|
||||
static {
|
||||
final VoxelShape HALF_STAIR = VoxelShapes.combineAndSimplify(
|
||||
createCuboidShape(8, 0, 0, 16, 16, 8),
|
||||
createCuboidShape(0, 0, 0, 8, 8, 8),
|
||||
BooleanBiFunction.OR
|
||||
);
|
||||
HALF_STAIR_VOXELS = VoxelListBuilder.create(HALF_STAIR, 24)
|
||||
.add(0 , VoxelHelper::rotateY, VoxelHelper::mirrorZ)
|
||||
.add(0 , VoxelHelper::rotateCX, VoxelHelper::mirrorZ)
|
||||
|
||||
.add(0 , VoxelHelper::rotateY)
|
||||
.add(1 , VoxelHelper::rotateY)
|
||||
.add(2 , VoxelHelper::rotateY)
|
||||
|
||||
.add(3 , VoxelHelper::rotateY)
|
||||
.add(4 , VoxelHelper::rotateY)
|
||||
.add(5 , VoxelHelper::rotateY)
|
||||
|
||||
.add(6 , VoxelHelper::rotateY)
|
||||
.add(7 , VoxelHelper::rotateY)
|
||||
.add(8 , VoxelHelper::rotateY)
|
||||
|
||||
.add(0 , VoxelHelper::mirrorY)
|
||||
.add(1 , VoxelHelper::mirrorY)
|
||||
.add(2 , VoxelHelper::mirrorY)
|
||||
|
||||
.add(12, VoxelHelper::rotateY)
|
||||
.add(13, VoxelHelper::rotateY)
|
||||
.add(14, VoxelHelper::rotateY)
|
||||
|
||||
.add(15, VoxelHelper::rotateY)
|
||||
.add(16, VoxelHelper::rotateY)
|
||||
.add(17, VoxelHelper::rotateY)
|
||||
|
||||
.add(18, VoxelHelper::rotateY)
|
||||
.add(19, VoxelHelper::rotateY)
|
||||
.add(20, VoxelHelper::rotateY)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.Corner;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.BlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.block.ReFramedHalfStairBlock.HALF_STAIR_VOXELS;
|
||||
import static fr.adrien1106.reframed.block.ReFramedHalfStairBlock.getHalfStairMultipart;
|
||||
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
|
||||
import static fr.adrien1106.reframed.block.ReFramedSmallCubeBlock.SMALL_CUBE_VOXELS;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER_FACE;
|
||||
import static fr.adrien1106.reframed.util.blocks.Corner.NORTH_EAST_DOWN;
|
||||
import static net.minecraft.util.shape.VoxelShapes.empty;
|
||||
|
||||
public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider {
|
||||
|
||||
private record ModelCacheKey(Corner corner, int face) {}
|
||||
|
||||
public ReFramedHalfStairsSlabBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN).with(CORNER_FACE, 0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return new ModelCacheKey(state.get(CORNER), state.get(CORNER_FACE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 24;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(CORNER,CORNER_FACE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
Corner corner = BlockHelper.getPlacementCorner(ctx);
|
||||
return super.getPlacementState(ctx)
|
||||
.with(CORNER, corner)
|
||||
.with(CORNER_FACE, corner.getDirectionIndex(ctx.getSide().getOpposite()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
|
||||
return isGhost(view, pos) ? empty(): getSlabShape(state.get(CORNER).getDirection(state.get(CORNER_FACE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return getSlabShape(state.get(CORNER).getDirection(state.get(CORNER_FACE)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, int i) {
|
||||
Corner corner = state.get(CORNER);
|
||||
int face = state.get(CORNER_FACE);
|
||||
return i == 2
|
||||
? SMALL_CUBE_VOXELS[corner.getOpposite(face).getID()]
|
||||
: HALF_STAIR_VOXELS[face + corner.getID() * 3];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateSupplier getMultipart() {
|
||||
return getHalfStairMultipart(
|
||||
this,
|
||||
ReFramed.id("half_stairs_slab_down_special"),
|
||||
ReFramed.id("half_stairs_slab_side_special")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 2);
|
||||
ShapelessRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.input(ReFramed.HALF_STAIR)
|
||||
.input(ReFramed.SMALL_CUBE)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.Corner;
|
||||
import fr.adrien1106.reframed.util.blocks.Edge;
|
||||
import fr.adrien1106.reframed.util.blocks.StairShape;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.BlockStateSupplier;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.block.ReFramedHalfStairBlock.HALF_STAIR_VOXELS;
|
||||
import static fr.adrien1106.reframed.block.ReFramedStairBlock.getStairShape;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
|
||||
import static fr.adrien1106.reframed.util.blocks.Edge.*;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
import static net.minecraft.util.shape.VoxelShapes.empty;
|
||||
|
||||
public class ReFramedHalfStairsStairBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider {
|
||||
public ReFramedHalfStairsStairBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(EDGE, NORTH_DOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return state.get(EDGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(EDGE));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return super.getPlacementState(ctx).with(EDGE, BlockHelper.getPlacementEdge(ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
|
||||
return isGhost(view, pos) ? empty(): getStairShape(state.get(EDGE), StairShape.STRAIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return getStairShape(state.get(EDGE), StairShape.STRAIGHT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, int i) {
|
||||
Edge edge = state.get(EDGE);
|
||||
Direction side = i == 1
|
||||
? edge.getRightDirection()
|
||||
: edge.getLeftDirection();
|
||||
Corner corner = Corner.getByDirections(
|
||||
edge.getFirstDirection(),
|
||||
edge.getSecondDirection(),
|
||||
side
|
||||
|
||||
);
|
||||
return HALF_STAIR_VOXELS[corner.getID() * 3 + corner.getDirectionIndex(side)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTopThemeIndex(BlockState state) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateSupplier getMultipart() {
|
||||
Identifier model_id = ReFramed.id("half_stairs_stair_down_special");
|
||||
Identifier side_model_id = ReFramed.id("half_stairs_stair_side_special");
|
||||
Identifier reverse_model_id = ReFramed.id("half_stairs_stair_reverse_special");
|
||||
return MultipartBlockStateSupplier.create(this)
|
||||
/* X AXIS */
|
||||
.with(GBlockstate.when(EDGE, NORTH_DOWN),
|
||||
GBlockstate.variant(side_model_id, true, R90, R180))
|
||||
.with(GBlockstate.when(EDGE, DOWN_SOUTH),
|
||||
GBlockstate.variant(side_model_id, true, R0, R180))
|
||||
.with(GBlockstate.when(EDGE, SOUTH_UP),
|
||||
GBlockstate.variant(side_model_id, true, R270, R180))
|
||||
.with(GBlockstate.when(EDGE, UP_NORTH),
|
||||
GBlockstate.variant(side_model_id, true, R180, R180))
|
||||
/* Y AXIS */
|
||||
.with(GBlockstate.when(EDGE, NORTH_EAST),
|
||||
GBlockstate.variant(model_id, true, R0, R0))
|
||||
.with(GBlockstate.when(EDGE, EAST_SOUTH),
|
||||
GBlockstate.variant(model_id, true, R0, R90))
|
||||
.with(GBlockstate.when(EDGE, SOUTH_WEST),
|
||||
GBlockstate.variant(model_id, true, R0, R180))
|
||||
.with(GBlockstate.when(EDGE, WEST_NORTH),
|
||||
GBlockstate.variant(model_id, true, R0, R270))
|
||||
/* Z AXIS */
|
||||
.with(GBlockstate.when(EDGE, DOWN_EAST),
|
||||
GBlockstate.variant(side_model_id, true, R0, R90))
|
||||
.with(GBlockstate.when(EDGE, EAST_UP),
|
||||
GBlockstate.variant(reverse_model_id, true, R180, R270))
|
||||
.with(GBlockstate.when(EDGE, UP_WEST),
|
||||
GBlockstate.variant(side_model_id, true, R180, R90))
|
||||
.with(GBlockstate.when(EDGE, WEST_DOWN),
|
||||
GBlockstate.variant(reverse_model_id, true, R0, R270));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 2);
|
||||
ShapelessRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.input(ReFramed.HALF_STAIR, 2)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.util.VoxelHelper;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
import static net.minecraft.state.property.Properties.FACING;
|
||||
import static net.minecraft.state.property.Properties.LAYERS;
|
||||
|
||||
public class ReFramedLayerBlock extends ReFramedSlabBlock {
|
||||
|
||||
public static final VoxelShape[] LAYER_VOXELS;
|
||||
private record ModelCacheKey(Direction face, int layer) {}
|
||||
|
||||
public ReFramedLayerBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(LAYERS, 1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return new ModelCacheKey(state.get(FACING), state.get(LAYERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 48;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(LAYERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return LAYER_VOXELS[state.get(FACING).getId() * 8 + state.get(LAYERS) - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canReplace(BlockState state, ItemPlacementContext ctx) {
|
||||
return !(!state.isOf(this) || ctx.getPlayer().isSneaking() || state.get(LAYERS) == 8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
BlockState previous = ctx.getWorld().getBlockState(ctx.getBlockPos());
|
||||
if (!previous.isOf(this)) return super.getPlacementState(ctx);
|
||||
return previous.with(LAYERS, previous.get(LAYERS) + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipartBlockStateSupplier getMultipart() {
|
||||
String model_pattern = "layer_x_special";
|
||||
MultipartBlockStateSupplier supplier = MultipartBlockStateSupplier.create(this);
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
Identifier model = ReFramed.id(model_pattern.replace("x", i + ""));
|
||||
supplier
|
||||
.with(GBlockstate.when(FACING, Direction.DOWN, LAYERS, i),
|
||||
GBlockstate.variant(model, true, R0, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.SOUTH, LAYERS, i),
|
||||
GBlockstate.variant(model, true, R90, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.UP, LAYERS, i),
|
||||
GBlockstate.variant(model, true, R180, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.NORTH, LAYERS, i),
|
||||
GBlockstate.variant(model, true, R270, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.WEST, LAYERS, i),
|
||||
GBlockstate.variant(model, true, R90, R90))
|
||||
.with(GBlockstate.when(FACING, Direction.EAST, LAYERS, i),
|
||||
GBlockstate.variant(model, true, R90, R270));
|
||||
}
|
||||
return supplier;
|
||||
}
|
||||
|
||||
static {
|
||||
VoxelListBuilder builder = VoxelListBuilder.create(createCuboidShape(0, 0, 0, 16, 2, 16), 48)
|
||||
.add(createCuboidShape(0, 0, 0, 16, 4, 16))
|
||||
.add(createCuboidShape(0, 0, 0, 16, 6, 16))
|
||||
.add(createCuboidShape(0, 0, 0, 16, 8, 16))
|
||||
.add(createCuboidShape(0, 0, 0, 16, 10, 16))
|
||||
.add(createCuboidShape(0, 0, 0, 16, 12, 16))
|
||||
.add(createCuboidShape(0, 0, 0, 16, 14, 16))
|
||||
.add(createCuboidShape(0, 0, 0, 16, 16, 16));
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
builder.add(i, VoxelHelper::mirrorY);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
builder.add(i, VoxelHelper::rotateX);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
builder.add(i, VoxelHelper::rotateCX);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
builder.add(i, VoxelHelper::rotateCZ);
|
||||
}
|
||||
for (int i = 0; i < 8; i++) {
|
||||
builder.add(i, VoxelHelper::rotateZ);
|
||||
}
|
||||
|
||||
LAYER_VOXELS = builder.build();
|
||||
}
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.LeverBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedLeverBlock extends LeverBlock implements BlockEntityProvider {
|
||||
public ReFramedLeverBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return super.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
boolean a = 0 != super.getWeakRedstonePower(state, view, pos, dir);
|
||||
boolean b = 0 != ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
return (a ^ b) ? 15 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
if(getDirection(state) != dir) return 0;
|
||||
else return getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.PaneBlock;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedPaneBlock extends PaneBlock implements BlockEntityProvider {
|
||||
public ReFramedPaneBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
super.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedPostBlock extends WaterloggableReFramedBlock {
|
||||
public ReFramedPostBlock(Settings settings) {
|
||||
super(settings);
|
||||
|
||||
setDefaultState(getDefaultState().with(Properties.AXIS, Direction.Axis.Y));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(Properties.AXIS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
BlockState sup = super.getPlacementState(ctx);
|
||||
if(sup != null) sup = sup.with(Properties.AXIS, ctx.getSide().getAxis());
|
||||
return sup;
|
||||
}
|
||||
|
||||
protected static final VoxelShape SHAPE_X = createCuboidShape(0, 6, 6, 16, 10, 10);
|
||||
protected static final VoxelShape SHAPE_Y = createCuboidShape(6, 0, 6, 10, 16, 10);
|
||||
protected static final VoxelShape SHAPE_Z = createCuboidShape(6, 6, 0, 10, 10, 16);
|
||||
|
||||
protected VoxelShape shap(BlockState state) {
|
||||
return switch(state.get(Properties.AXIS)) {
|
||||
case X -> SHAPE_X;
|
||||
case Y -> SHAPE_Y;
|
||||
case Z -> SHAPE_Z;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), shap(state));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return shap(state);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedPressurePlateBlock extends PressurePlateBlock implements BlockEntityProvider {
|
||||
public ReFramedPressurePlateBlock(Settings settings, BlockSetType blockSetType) {
|
||||
super(blockSetType, settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
public ReFramedPressurePlateBlock(Settings settings) {
|
||||
this(settings, BlockSetType.OAK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
super.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return super.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
boolean a = 0 != super.getWeakRedstonePower(state, view, pos, dir);
|
||||
boolean b = 0 != ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
return (a ^ b) ? 15 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return dir == Direction.UP ? getWeakRedstonePower(state, view, pos, dir) : 0;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.generator.MultipartBlockStateProvider;
|
||||
import net.minecraft.block.*;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
@@ -13,34 +22,51 @@ import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements MultipartBlockStateProvider {
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
import static net.minecraft.state.property.Properties.FACING;
|
||||
|
||||
private static final VoxelShape DOWN = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.5f, 1f);
|
||||
private static final VoxelShape UP = VoxelShapes.cuboid(0f, 0.5f, 0f, 1f, 1f, 1f);
|
||||
private static final VoxelShape NORTH = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 1f, 0.5f);
|
||||
private static final VoxelShape SOUTH = VoxelShapes.cuboid(0f, 0f, 0.5f, 1f, 1f, 1f);
|
||||
private static final VoxelShape EAST = VoxelShapes.cuboid(0.5f, 0f, 0f, 1f, 1f, 1f);
|
||||
private static final VoxelShape WEST = VoxelShapes.cuboid(0f, 0f, 0f, 0.5f, 1f, 1f);
|
||||
public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
|
||||
|
||||
protected static final VoxelShape DOWN = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.5f, 1f);
|
||||
protected static final VoxelShape UP = VoxelShapes.cuboid(0f, 0.5f, 0f, 1f, 1f, 1f);
|
||||
protected static final VoxelShape NORTH = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 1f, 0.5f);
|
||||
protected static final VoxelShape SOUTH = VoxelShapes.cuboid(0f, 0f, 0.5f, 1f, 1f, 1f);
|
||||
protected static final VoxelShape EAST = VoxelShapes.cuboid(0.5f, 0f, 0f, 1f, 1f, 1f);
|
||||
protected static final VoxelShape WEST = VoxelShapes.cuboid(0f, 0f, 0f, 0.5f, 1f, 1f);
|
||||
|
||||
public ReFramedSlabBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(Properties.FACING, Direction.DOWN));
|
||||
setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return state.get(FACING);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 6;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(Properties.FACING));
|
||||
super.appendProperties(builder.add(FACING));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return super.getPlacementState(ctx).with(Properties.FACING, ctx.getSide().getOpposite());
|
||||
return super.getPlacementState(ctx).with(FACING, ctx.getSide().getOpposite());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return switch (state.get(Properties.FACING)) {
|
||||
return getSlabShape(state.get(FACING));
|
||||
}
|
||||
|
||||
public static VoxelShape getSlabShape(Direction side) {
|
||||
return switch (side) {
|
||||
case DOWN -> DOWN;
|
||||
case UP -> UP;
|
||||
case NORTH -> NORTH;
|
||||
@@ -52,6 +78,31 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements Mul
|
||||
|
||||
@Override
|
||||
public MultipartBlockStateSupplier getMultipart() {
|
||||
return null;
|
||||
Identifier model_id = ReFramed.id("slab_special");
|
||||
return MultipartBlockStateSupplier.create(this)
|
||||
.with(GBlockstate.when(FACING, Direction.DOWN),
|
||||
GBlockstate.variant(model_id, true, R0, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.SOUTH),
|
||||
GBlockstate.variant(model_id, true, R90, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.UP),
|
||||
GBlockstate.variant(model_id, true, R180, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.NORTH),
|
||||
GBlockstate.variant(model_id, true, R270, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.WEST),
|
||||
GBlockstate.variant(model_id, true, R90, R90))
|
||||
.with(GBlockstate.when(FACING, Direction.EAST),
|
||||
GBlockstate.variant(model_id, true, R90, R270));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 2);
|
||||
ShapedRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this, 6)
|
||||
.pattern("III")
|
||||
.input('I', ReFramed.CUBE)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.*;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.R0;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.R90;
|
||||
import static net.minecraft.state.property.Properties.AXIS;
|
||||
|
||||
public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock implements BlockStateProvider {
|
||||
|
||||
public ReFramedSlabsCubeBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return state.get(AXIS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(AXIS));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, int i) {
|
||||
return switch (state.get(AXIS)) {
|
||||
case Y -> i == 2 ? UP : DOWN;
|
||||
case Z -> i == 2 ? NORTH : SOUTH;
|
||||
case X -> i == 2 ? EAST : WEST;
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTopThemeIndex(BlockState state) {
|
||||
// when the side is shared just return one
|
||||
return state.get(AXIS) == Direction.Axis.Y ? 2: super.getTopThemeIndex(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipartBlockStateSupplier getMultipart() {
|
||||
Identifier model_id = ReFramed.id("double_slab_special");
|
||||
return MultipartBlockStateSupplier.create(this)
|
||||
.with(GBlockstate.when(AXIS, Direction.Axis.Y),
|
||||
GBlockstate.variant(model_id, true, R0, R0))
|
||||
.with(GBlockstate.when(AXIS, Direction.Axis.Z),
|
||||
GBlockstate.variant(model_id, true, R90, R0))
|
||||
.with(GBlockstate.when(AXIS, Direction.Axis.X),
|
||||
GBlockstate.variant(model_id, true, R90, R90));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE);
|
||||
ShapelessRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.input(ReFramed.SLAB, 2)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.util.VoxelHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.BlockStateSupplier;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
|
||||
import static fr.adrien1106.reframed.util.blocks.Corner.*;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
|
||||
public class ReFramedSmallCubeBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
|
||||
|
||||
public static final VoxelShape[] SMALL_CUBE_VOXELS;
|
||||
|
||||
public ReFramedSmallCubeBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return state.get(CORNER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(CORNER));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return super.getPlacementState(ctx).with(CORNER, BlockHelper.getPlacementCorner(ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return SMALL_CUBE_VOXELS[state.get(CORNER).getID()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateSupplier getMultipart() {
|
||||
Identifier small_cube_id = ReFramed.id("small_cube_special");
|
||||
return MultipartBlockStateSupplier.create(this)
|
||||
.with(GBlockstate.when(CORNER, NORTH_EAST_DOWN),
|
||||
GBlockstate.variant(small_cube_id, true, R0, R0))
|
||||
.with(GBlockstate.when(CORNER, EAST_SOUTH_DOWN),
|
||||
GBlockstate.variant(small_cube_id, true, R0, R90))
|
||||
.with(GBlockstate.when(CORNER, SOUTH_WEST_DOWN),
|
||||
GBlockstate.variant(small_cube_id, true, R0, R180))
|
||||
.with(GBlockstate.when(CORNER, WEST_NORTH_DOWN),
|
||||
GBlockstate.variant(small_cube_id, true, R0, R270))
|
||||
.with(GBlockstate.when(CORNER, EAST_SOUTH_UP),
|
||||
GBlockstate.variant(small_cube_id, true, R180, R0))
|
||||
.with(GBlockstate.when(CORNER, SOUTH_WEST_UP),
|
||||
GBlockstate.variant(small_cube_id, true, R180, R90))
|
||||
.with(GBlockstate.when(CORNER, WEST_NORTH_UP),
|
||||
GBlockstate.variant(small_cube_id, true, R180, R180))
|
||||
.with(GBlockstate.when(CORNER, NORTH_EAST_UP),
|
||||
GBlockstate.variant(small_cube_id, true, R180, R270));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 8);
|
||||
ShapelessRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this, 8)
|
||||
.input(ReFramed.CUBE)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
|
||||
static {
|
||||
final VoxelShape SMALL_CUBE = VoxelShapes.cuboid(.5f, 0f, 0f, 1f, .5f, .5f);
|
||||
|
||||
SMALL_CUBE_VOXELS = VoxelListBuilder.create(SMALL_CUBE, 8)
|
||||
.add(VoxelHelper::rotateY)
|
||||
.add(VoxelHelper::rotateY)
|
||||
.add(VoxelHelper::rotateY)
|
||||
|
||||
.add(SMALL_CUBE, VoxelHelper::mirrorY)
|
||||
.add(VoxelHelper::rotateY)
|
||||
.add(VoxelHelper::rotateY)
|
||||
.add(VoxelHelper::rotateY)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.Corner;
|
||||
import fr.adrien1106.reframed.util.blocks.Edge;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.BlockStateSupplier;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.block.ReFramedSmallCubeBlock.SMALL_CUBE_VOXELS;
|
||||
import static fr.adrien1106.reframed.block.ReFramedStepBlock.getStepShape;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
|
||||
import static fr.adrien1106.reframed.util.blocks.Edge.*;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
import static net.minecraft.util.shape.VoxelShapes.empty;
|
||||
|
||||
public class ReFramedSmallCubesStepBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider {
|
||||
|
||||
public ReFramedSmallCubesStepBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return state.get(EDGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(EDGE));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return super.getPlacementState(ctx).with(EDGE, BlockHelper.getPlacementEdge(ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
|
||||
return isGhost(view, pos) ? empty(): getStepShape(state.get(EDGE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return getStepShape(state.get(EDGE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, int i) {
|
||||
Edge edge = state.get(EDGE);
|
||||
return SMALL_CUBE_VOXELS[Corner.getByDirections(
|
||||
edge.getFirstDirection(),
|
||||
edge.getSecondDirection(),
|
||||
i == 1
|
||||
? edge.getRightDirection()
|
||||
: edge.getLeftDirection()
|
||||
).getID()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTopThemeIndex(BlockState state) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateSupplier getMultipart() {
|
||||
Identifier model_id = ReFramed.id("small_cubes_step_special");
|
||||
Identifier reverse_model_id = ReFramed.id("small_cubes_step_reverse_special");
|
||||
return MultipartBlockStateSupplier.create(this)
|
||||
/* X AXIS */
|
||||
.with(GBlockstate.when(EDGE, DOWN_EAST),
|
||||
GBlockstate.variant(reverse_model_id, true, R0, R0))
|
||||
.with(GBlockstate.when(EDGE, EAST_UP),
|
||||
GBlockstate.variant(model_id, true, R180, R0))
|
||||
.with(GBlockstate.when(EDGE, UP_WEST),
|
||||
GBlockstate.variant(reverse_model_id, true, R180, R180))
|
||||
.with(GBlockstate.when(EDGE, WEST_DOWN),
|
||||
GBlockstate.variant(model_id, true, R0, R180))
|
||||
/* Y AXIS */
|
||||
.with(GBlockstate.when(EDGE, EAST_SOUTH),
|
||||
GBlockstate.variant(model_id, true, R90, R0))
|
||||
.with(GBlockstate.when(EDGE, SOUTH_WEST),
|
||||
GBlockstate.variant(model_id, true, R90, R90))
|
||||
.with(GBlockstate.when(EDGE, WEST_NORTH),
|
||||
GBlockstate.variant(model_id, true, R90, R180))
|
||||
.with(GBlockstate.when(EDGE, NORTH_EAST),
|
||||
GBlockstate.variant(model_id, true, R90, R270))
|
||||
/* Z AXIS */
|
||||
.with(GBlockstate.when(EDGE, DOWN_SOUTH),
|
||||
GBlockstate.variant(reverse_model_id, true, R0, R90))
|
||||
.with(GBlockstate.when(EDGE, NORTH_DOWN),
|
||||
GBlockstate.variant(model_id, true, R0, R270))
|
||||
.with(GBlockstate.when(EDGE, UP_NORTH),
|
||||
GBlockstate.variant(reverse_model_id, true, R180, R270))
|
||||
.with(GBlockstate.when(EDGE, SOUTH_UP),
|
||||
GBlockstate.variant(model_id, true, R180, R90));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 4);
|
||||
ShapelessRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.input(ReFramed.SMALL_CUBE, 2)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,433 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.util.VoxelHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.Edge;
|
||||
import fr.adrien1106.reframed.util.blocks.StairShape;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.client.When;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.function.BooleanBiFunction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.STAIR_SHAPE;
|
||||
import static fr.adrien1106.reframed.util.blocks.Edge.*;
|
||||
import static fr.adrien1106.reframed.util.blocks.StairShape.*;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
|
||||
public class ReFramedStairBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
|
||||
public static final VoxelShape[] STAIR_VOXELS;
|
||||
private record ModelCacheKey(Edge edge, StairShape shape) {}
|
||||
|
||||
public ReFramedStairBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return new ModelCacheKey(state.get(EDGE), state.get(STAIR_SHAPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 108; // Has 12 * 9 state combination and 52 models still reduces cache size
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(EDGE, STAIR_SHAPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) {
|
||||
return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved)
|
||||
.with(STAIR_SHAPE, BlockHelper.getStairsShape(state.get(EDGE), world, pos));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override // Pretty happy of how clean it is (also got it on first try :) )
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
Edge face = BlockHelper.getPlacementEdge(ctx);
|
||||
StairShape shape = BlockHelper.getStairsShape(face, ctx.getWorld(), ctx.getBlockPos());
|
||||
return super.getPlacementState(ctx).with(EDGE, face).with(STAIR_SHAPE, shape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
|
||||
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return getStairShape(state.get(EDGE), state.get(STAIR_SHAPE));
|
||||
}
|
||||
|
||||
public static VoxelShape getStairShape(Edge edge, StairShape shape) {
|
||||
return STAIR_VOXELS[edge.getID() * 9 + shape.getID()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipartBlockStateSupplier getMultipart() {
|
||||
return getStairMultipart(this, false);
|
||||
}
|
||||
|
||||
public static MultipartBlockStateSupplier getStairMultipart(Block block, boolean is_double) {
|
||||
String infix = is_double ? "s_cube" : "";
|
||||
Identifier straight_id = ReFramed.id("stair" + infix + "_special");
|
||||
Identifier double_outer_id = ReFramed.id("outers_stair" + infix + "_special");
|
||||
Identifier inner_id = ReFramed.id("inner_stair" + infix + "_special");
|
||||
Identifier outer_id = ReFramed.id("outer_stair" + infix + "_special");
|
||||
Identifier outer_side_id = ReFramed.id("outer_side_stair" + infix + "_special");
|
||||
return MultipartBlockStateSupplier.create(block)
|
||||
/* STRAIGHT X AXIS */
|
||||
.with(GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R0, R0))
|
||||
.with(GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R180, R0))
|
||||
.with(GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R180, R180))
|
||||
.with(GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R0, R180))
|
||||
/* STRAIGHT Y AXIS */
|
||||
.with(GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R90, R0))
|
||||
.with(GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R90, R90))
|
||||
.with(GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R90, R180))
|
||||
.with(GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R90, R270))
|
||||
/* STRAIGHT Z AXIS */
|
||||
.with(GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R0, R90))
|
||||
.with(GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R0, R270))
|
||||
.with(GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R180, R270))
|
||||
.with(GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R180, R90))
|
||||
/* INNER BOTTOM */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, INNER_LEFT)),
|
||||
GBlockstate.variant(inner_id, true, R0, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, INNER_LEFT)),
|
||||
GBlockstate.variant(inner_id, true, R0, R270))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, INNER_RIGHT)),
|
||||
GBlockstate.variant(inner_id, true, R0, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, INNER_RIGHT)),
|
||||
GBlockstate.variant(inner_id, true, R0, R90))
|
||||
/* INNER TOP */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, INNER_LEFT)),
|
||||
GBlockstate.variant(inner_id, true, R180, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, INNER_LEFT)),
|
||||
GBlockstate.variant(inner_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, INNER_RIGHT)),
|
||||
GBlockstate.variant(inner_id, true, R180, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, INNER_LEFT)),
|
||||
GBlockstate.variant(inner_id, true, R180, R270))
|
||||
/* OUTER BOTTOM */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_id, true, R0, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_id, true, R0, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_id, true, R0, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_id, true, R0, R270))
|
||||
/* OUTER TOP */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_id, true, R180, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_id, true, R180, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_id, true, R180, R270))
|
||||
/* OUTER EAST */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R0, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R90, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R180, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R270, R0))
|
||||
/* OUTER SOUTH */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R0, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R90, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R270, R90))
|
||||
/* OUTER WEST */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R0, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R90, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R180, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R270, R180))
|
||||
/* OUTER NORTH */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R0, R270))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R90, R270))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R180, R270))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R270, R270))
|
||||
/* OUTER BOTTOM */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, OUTER_RIGHT)),
|
||||
GBlockstate.variant(double_outer_id, true, R0, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, DOWN_SOUTH, STAIR_SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, OUTER_RIGHT)),
|
||||
GBlockstate.variant(double_outer_id, true, R0, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, WEST_DOWN, STAIR_SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, OUTER_RIGHT)),
|
||||
GBlockstate.variant(double_outer_id, true, R0, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, NORTH_DOWN, STAIR_SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, DOWN_EAST, STAIR_SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, OUTER_RIGHT)),
|
||||
GBlockstate.variant(double_outer_id, true, R0, R270))
|
||||
/* OUTER TOP */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, NORTH_EAST, STAIR_SHAPE, OUTER_LEFT)),
|
||||
GBlockstate.variant(double_outer_id, true, R180, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, EAST_UP, STAIR_SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, EAST_SOUTH, STAIR_SHAPE, OUTER_LEFT)),
|
||||
GBlockstate.variant(double_outer_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, SOUTH_UP, STAIR_SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, SOUTH_WEST, STAIR_SHAPE, OUTER_LEFT)),
|
||||
GBlockstate.variant(double_outer_id, true, R180, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(EDGE, UP_NORTH, STAIR_SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(EDGE, UP_WEST, STAIR_SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(EDGE, WEST_NORTH, STAIR_SHAPE, OUTER_LEFT)),
|
||||
GBlockstate.variant(double_outer_id, true, R180, R270));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE);
|
||||
ShapedRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this, 4)
|
||||
.pattern("I ")
|
||||
.pattern("II ")
|
||||
.pattern("III")
|
||||
.input('I', ReFramed.CUBE)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
|
||||
static {
|
||||
final VoxelShape STRAIGHT = VoxelShapes.combineAndSimplify(
|
||||
createCuboidShape(0, 8, 0, 16, 16, 8),
|
||||
createCuboidShape(0, 0, 0, 16, 8, 16),
|
||||
BooleanBiFunction.OR
|
||||
);
|
||||
final VoxelShape INNER = Stream.of(
|
||||
createCuboidShape(0, 8, 0, 16, 16, 8),
|
||||
createCuboidShape(0, 8, 8, 8, 16, 16),
|
||||
createCuboidShape(0, 0, 0, 16, 8, 16)
|
||||
).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
|
||||
final VoxelShape OUTER = Stream.of(
|
||||
createCuboidShape(0, 8, 0, 8, 16, 8),
|
||||
createCuboidShape(8, 0, 0, 16, 8, 8),
|
||||
createCuboidShape(0, 0, 0, 8, 8, 16)
|
||||
).reduce((v1, v2) -> VoxelShapes.combineAndSimplify(v1, v2, BooleanBiFunction.OR)).get();
|
||||
final VoxelShape JUNCTION = VoxelShapes.combineAndSimplify(
|
||||
createCuboidShape(0, 8, 0, 8, 16, 8),
|
||||
createCuboidShape(0, 0, 0, 16, 8, 16),
|
||||
BooleanBiFunction.OR
|
||||
);
|
||||
|
||||
STAIR_VOXELS = VoxelListBuilder.create(STRAIGHT, 108)
|
||||
.add(INNER).add(VoxelHelper::rotateY)
|
||||
.add(OUTER).add(VoxelHelper::rotateY)
|
||||
.add(JUNCTION).add(VoxelHelper::rotateY)
|
||||
.add(JUNCTION, VoxelHelper::rotateX, VoxelHelper::rotateZ).add(VoxelHelper::rotateZ)
|
||||
// DOWN_SOUTH
|
||||
.add(0, VoxelHelper::rotateCX)
|
||||
.add(1, VoxelHelper::rotateCX).add(2, VoxelHelper::rotateCX)
|
||||
.add(3, VoxelHelper::rotateCX).add(4, VoxelHelper::rotateCX)
|
||||
.add(5, VoxelHelper::rotateCX).add(6, VoxelHelper::rotateCX)
|
||||
.add(7, VoxelHelper::rotateCX).add(8, VoxelHelper::rotateCX)
|
||||
// SOUTH_UP
|
||||
.add(9, VoxelHelper::rotateCX)
|
||||
.add(10, VoxelHelper::rotateCX).add(11, VoxelHelper::rotateCX)
|
||||
.add(12, VoxelHelper::rotateCX).add(13, VoxelHelper::rotateCX)
|
||||
.add(14, VoxelHelper::rotateCX).add(15, VoxelHelper::rotateCX)
|
||||
.add(16, VoxelHelper::rotateCX).add(17, VoxelHelper::rotateCX)
|
||||
// UP_NORTH
|
||||
.add(18, VoxelHelper::rotateCX)
|
||||
.add(19, VoxelHelper::rotateCX).add(20, VoxelHelper::rotateCX)
|
||||
.add(21, VoxelHelper::rotateCX).add(22, VoxelHelper::rotateCX)
|
||||
.add(23, VoxelHelper::rotateCX).add(24, VoxelHelper::rotateCX)
|
||||
.add(25, VoxelHelper::rotateCX).add(26, VoxelHelper::rotateCX)
|
||||
// WEST_DOWN
|
||||
.add(0, VoxelHelper::rotateCY)
|
||||
.add(10).add(1)
|
||||
.add(12).add(3)
|
||||
.add(16).add(5)
|
||||
.add(7, VoxelHelper::rotateCY).add(8, VoxelHelper::rotateCY)
|
||||
// DOWN_EAST
|
||||
.add(36, VoxelHelper::rotateZ)
|
||||
.add(11).add(2)
|
||||
.add(13).add(4)
|
||||
.add(41, VoxelHelper::rotateZ).add(42, VoxelHelper::rotateZ)
|
||||
.add(17).add(6)
|
||||
// EAST_UP
|
||||
.add(45, VoxelHelper::rotateZ)
|
||||
.add(20).add(29)
|
||||
.add(22).add(31)
|
||||
.add(24).add(35)
|
||||
.add(52, VoxelHelper::rotateZ).add(53, VoxelHelper::rotateZ)
|
||||
// UP_WEST
|
||||
.add(54, VoxelHelper::rotateZ)
|
||||
.add(19).add(28)
|
||||
.add(21).add(30)
|
||||
.add(59, VoxelHelper::rotateZ).add(60, VoxelHelper::rotateZ)
|
||||
.add(23).add(34)
|
||||
// WEST_NORTH
|
||||
.add(0, VoxelHelper::rotateCZ)
|
||||
.add(1).add(28)
|
||||
.add(3).add(30)
|
||||
.add(7).add(32)
|
||||
.add(44).add(69)
|
||||
// NORTH_EAST
|
||||
.add(72, VoxelHelper::rotateY)
|
||||
.add(2).add(29)
|
||||
.add(4).add(31)
|
||||
.add(51).add(62)
|
||||
.add(8).add(33)
|
||||
// EAST_SOUTH
|
||||
.add(81, VoxelHelper::rotateY)
|
||||
.add(11).add(20)
|
||||
.add(13).add(22)
|
||||
.add(15).add(26)
|
||||
.add(50).add(61)
|
||||
// SOUTH_WEST
|
||||
.add(90, VoxelHelper::rotateY)
|
||||
.add(10).add(19)
|
||||
.add(12).add(21)
|
||||
.add(43).add(68)
|
||||
.add(14).add(25)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,571 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.generator.MultipartBlockStateProvider;
|
||||
import fr.adrien1106.reframed.util.VoxelHelper;
|
||||
import fr.adrien1106.reframed.util.property.StairDirection;
|
||||
import fr.adrien1106.reframed.util.property.StairShape;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.*;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.EnumProperty;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.function.BooleanBiFunction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Direction.Axis;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static fr.adrien1106.reframed.util.property.StairShape.*;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
import static fr.adrien1106.reframed.util.property.StairDirection.*;
|
||||
|
||||
public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements MultipartBlockStateProvider {
|
||||
|
||||
public static final EnumProperty<StairDirection> FACING = EnumProperty.of("facing", StairDirection.class);
|
||||
public static final EnumProperty<StairShape> SHAPE = EnumProperty.of("shape", StairShape.class);
|
||||
private static final List<VoxelShape> VOXEL_LIST = new ArrayList<>(52);
|
||||
|
||||
public ReFramedStairsBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(FACING, StairDirection.NORTH_DOWN).with(SHAPE, STRAIGHT));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(FACING).add(SHAPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) {
|
||||
return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved)
|
||||
.with(SHAPE, getPlacementShape(state.get(FACING), world, pos));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override // Pretty happy of how clean it is (also got it on first try :) )
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
BlockState state = super.getPlacementState(ctx);
|
||||
Direction side = ctx.getSide().getOpposite();
|
||||
BlockPos block_pos = ctx.getBlockPos();
|
||||
Vec3d hit_pos = ctx.getHitPos();
|
||||
Vec3d pos = new Vec3d(
|
||||
hit_pos.getX() - block_pos.getX() - .5d,
|
||||
hit_pos.getY() - block_pos.getY() - .5d,
|
||||
hit_pos.getZ() - block_pos.getZ() - .5d
|
||||
);
|
||||
|
||||
Stream<Axis> axes = Stream.of(Axis.values()).filter(axis -> !axis.equals(side.getAxis()));
|
||||
Axis axis = axes.reduce((axis_1, axis_2) ->
|
||||
Math.abs(axis_1.choose(pos.x, pos.y, pos.z)) > Math.abs(axis_2.choose(pos.x, pos.y, pos.z))
|
||||
? axis_1
|
||||
: axis_2
|
||||
).get();
|
||||
|
||||
Direction part_direction = Direction.from(
|
||||
axis,
|
||||
axis.choose(pos.x, pos.y, pos.z) > 0
|
||||
? Direction.AxisDirection.POSITIVE
|
||||
: Direction.AxisDirection.NEGATIVE
|
||||
);
|
||||
StairDirection face = StairDirection.getByDirections(side, part_direction);
|
||||
|
||||
return state.with(FACING, face).with(SHAPE, getPlacementShape(face, ctx.getWorld(), block_pos));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
|
||||
//StairsBlock onStateReplaced is Weird! it doesn't delegate to regular block onStateReplaced!
|
||||
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
|
||||
}
|
||||
|
||||
/* ---------------------------------- DON'T GO FURTHER IF YOU LIKE HAVING EYES ---------------------------------- */
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
StairShape shape = state.get(SHAPE);
|
||||
StairDirection direction = state.get(FACING);
|
||||
return switch (shape) {
|
||||
case STRAIGHT ->
|
||||
switch (direction) {
|
||||
case DOWN_SOUTH -> VOXEL_LIST.get(0);
|
||||
case NORTH_DOWN -> VOXEL_LIST.get(1);
|
||||
case UP_NORTH -> VOXEL_LIST.get(2);
|
||||
case SOUTH_UP -> VOXEL_LIST.get(3);
|
||||
case DOWN_EAST -> VOXEL_LIST.get(4);
|
||||
case WEST_DOWN -> VOXEL_LIST.get(5);
|
||||
case UP_WEST -> VOXEL_LIST.get(6);
|
||||
case EAST_UP -> VOXEL_LIST.get(7);
|
||||
case NORTH_EAST -> VOXEL_LIST.get(8);
|
||||
case EAST_SOUTH -> VOXEL_LIST.get(9);
|
||||
case SOUTH_WEST -> VOXEL_LIST.get(10);
|
||||
case WEST_NORTH -> VOXEL_LIST.get(11);
|
||||
};
|
||||
case INNER_LEFT ->
|
||||
switch (direction) {
|
||||
case WEST_DOWN, NORTH_DOWN -> VOXEL_LIST.get(44);
|
||||
case DOWN_EAST -> VOXEL_LIST.get(45);
|
||||
case DOWN_SOUTH -> VOXEL_LIST.get(47);
|
||||
case UP_WEST, UP_NORTH, WEST_NORTH -> VOXEL_LIST.get(48);
|
||||
case EAST_UP, NORTH_EAST -> VOXEL_LIST.get(49);
|
||||
case EAST_SOUTH -> VOXEL_LIST.get(50);
|
||||
case SOUTH_UP, SOUTH_WEST -> VOXEL_LIST.get(51);
|
||||
};
|
||||
case INNER_RIGHT ->
|
||||
switch (direction) {
|
||||
case WEST_NORTH -> VOXEL_LIST.get(44);
|
||||
case NORTH_DOWN, NORTH_EAST -> VOXEL_LIST.get(45);
|
||||
case DOWN_EAST, DOWN_SOUTH, EAST_SOUTH -> VOXEL_LIST.get(46);
|
||||
case WEST_DOWN, SOUTH_WEST -> VOXEL_LIST.get(47);
|
||||
case UP_NORTH -> VOXEL_LIST.get(49);
|
||||
case EAST_UP, SOUTH_UP -> VOXEL_LIST.get(50);
|
||||
case UP_WEST -> VOXEL_LIST.get(51);
|
||||
};
|
||||
case OUTER_LEFT ->
|
||||
switch (direction) {
|
||||
case DOWN_EAST -> VOXEL_LIST.get(43);
|
||||
case WEST_DOWN, NORTH_DOWN -> VOXEL_LIST.get(42);
|
||||
case DOWN_SOUTH -> VOXEL_LIST.get(41);
|
||||
case EAST_UP, NORTH_EAST -> VOXEL_LIST.get(39);
|
||||
case UP_WEST, UP_NORTH, WEST_NORTH -> VOXEL_LIST.get(38);
|
||||
case SOUTH_UP, SOUTH_WEST -> VOXEL_LIST.get(37);
|
||||
case EAST_SOUTH -> VOXEL_LIST.get(36);
|
||||
};
|
||||
case OUTER_RIGHT ->
|
||||
switch (direction) {
|
||||
case NORTH_DOWN, NORTH_EAST -> VOXEL_LIST.get(43);
|
||||
case WEST_NORTH -> VOXEL_LIST.get(42);
|
||||
case WEST_DOWN, SOUTH_WEST -> VOXEL_LIST.get(41);
|
||||
case DOWN_EAST, DOWN_SOUTH, EAST_SOUTH -> VOXEL_LIST.get(40);
|
||||
case UP_NORTH -> VOXEL_LIST.get(39);
|
||||
case UP_WEST -> VOXEL_LIST.get(37);
|
||||
case EAST_UP, SOUTH_UP -> VOXEL_LIST.get(36);
|
||||
};
|
||||
case FIRST_OUTER_LEFT ->
|
||||
switch (direction) {
|
||||
case WEST_DOWN, NORTH_DOWN -> VOXEL_LIST.get(14);
|
||||
case SOUTH_UP -> VOXEL_LIST.get(17);
|
||||
case EAST_UP -> VOXEL_LIST.get(19);
|
||||
case EAST_SOUTH -> VOXEL_LIST.get(20);
|
||||
case DOWN_SOUTH -> VOXEL_LIST.get(22);
|
||||
case UP_NORTH, WEST_NORTH -> VOXEL_LIST.get(25);
|
||||
case SOUTH_WEST -> VOXEL_LIST.get(28);
|
||||
case UP_WEST -> VOXEL_LIST.get(31);
|
||||
case DOWN_EAST -> VOXEL_LIST.get(34);
|
||||
case NORTH_EAST -> VOXEL_LIST.get(35);
|
||||
};
|
||||
case FIRST_OUTER_RIGHT ->
|
||||
switch (direction) {
|
||||
case NORTH_DOWN -> VOXEL_LIST.get(15);
|
||||
case SOUTH_UP, EAST_UP -> VOXEL_LIST.get(16);
|
||||
case WEST_DOWN -> VOXEL_LIST.get(13);
|
||||
case DOWN_SOUTH, EAST_SOUTH -> VOXEL_LIST.get(23);
|
||||
case UP_NORTH -> VOXEL_LIST.get(24);
|
||||
case WEST_NORTH -> VOXEL_LIST.get(26);
|
||||
case UP_WEST -> VOXEL_LIST.get(28);
|
||||
case SOUTH_WEST -> VOXEL_LIST.get(29);
|
||||
case DOWN_EAST -> VOXEL_LIST.get(33);
|
||||
case NORTH_EAST -> VOXEL_LIST.get(34);
|
||||
};
|
||||
case SECOND_OUTER_LEFT ->
|
||||
switch (direction) {
|
||||
case DOWN_EAST -> VOXEL_LIST.get(15);
|
||||
case DOWN_SOUTH -> VOXEL_LIST.get(13);
|
||||
case UP_WEST, UP_NORTH -> VOXEL_LIST.get(18);
|
||||
case SOUTH_UP, SOUTH_WEST -> VOXEL_LIST.get(21);
|
||||
case NORTH_EAST -> VOXEL_LIST.get(24);
|
||||
case NORTH_DOWN -> VOXEL_LIST.get(26);
|
||||
case WEST_DOWN -> VOXEL_LIST.get(30);
|
||||
case WEST_NORTH -> VOXEL_LIST.get(31);
|
||||
case EAST_SOUTH -> VOXEL_LIST.get(32);
|
||||
case EAST_UP -> VOXEL_LIST.get(35);
|
||||
};
|
||||
case SECOND_OUTER_RIGHT ->
|
||||
switch (direction) {
|
||||
case DOWN_SOUTH, DOWN_EAST -> VOXEL_LIST.get(12);
|
||||
case UP_WEST -> VOXEL_LIST.get(17);
|
||||
case UP_NORTH -> VOXEL_LIST.get(19);
|
||||
case SOUTH_UP -> VOXEL_LIST.get(20);
|
||||
case SOUTH_WEST -> VOXEL_LIST.get(22);
|
||||
case NORTH_EAST, NORTH_DOWN -> VOXEL_LIST.get(27);
|
||||
case WEST_DOWN -> VOXEL_LIST.get(29);
|
||||
case WEST_NORTH -> VOXEL_LIST.get(30);
|
||||
case EAST_UP -> VOXEL_LIST.get(32);
|
||||
case EAST_SOUTH -> VOXEL_LIST.get(33);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
private static String getNeighborPos(StairDirection face, Direction direction, Boolean reverse, Direction reference, BlockView world, BlockPos pos) {
|
||||
BlockState block_state = world.getBlockState(
|
||||
pos.offset(reverse ? direction.getOpposite() : direction)
|
||||
);
|
||||
|
||||
if (block_state.getBlock() instanceof ReFramedStairsBlock && block_state.get(FACING).hasDirection(reference)) {
|
||||
if (block_state.get(FACING).hasDirection(face.getLeftDirection())) return "left";
|
||||
else if (block_state.get(FACING).hasDirection(face.getRightDirection())) return "right";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static StairShape getPlacementShape(StairDirection face, BlockView world, BlockPos pos) {
|
||||
StairShape shape = STRAIGHT;
|
||||
|
||||
String sol = getNeighborPos(face, face.getFirstDirection(), true, face.getSecondDirection(), world, pos);
|
||||
switch (sol) {
|
||||
case "right": return INNER_RIGHT;
|
||||
case "left": return INNER_LEFT;
|
||||
}
|
||||
|
||||
sol = getNeighborPos(face, face.getSecondDirection(), true, face.getFirstDirection(), world, pos);
|
||||
switch (sol) {
|
||||
case "right": return INNER_RIGHT;
|
||||
case "left": return INNER_LEFT;
|
||||
}
|
||||
|
||||
sol = getNeighborPos(face, face.getFirstDirection(), false, face.getSecondDirection(), world, pos);
|
||||
switch (sol) {
|
||||
case "right" -> shape = FIRST_OUTER_RIGHT;
|
||||
case "left" -> shape = FIRST_OUTER_LEFT;
|
||||
}
|
||||
|
||||
sol = getNeighborPos(face, face.getSecondDirection(), false, face.getFirstDirection(), world, pos);
|
||||
switch (sol) {
|
||||
case "right" -> {
|
||||
if (shape.equals(STRAIGHT)) shape = SECOND_OUTER_RIGHT;
|
||||
else if (shape.equals(FIRST_OUTER_RIGHT)) shape = OUTER_RIGHT;
|
||||
}
|
||||
case "left" -> {
|
||||
if (shape.equals(STRAIGHT)) shape = SECOND_OUTER_LEFT;
|
||||
else if (shape.equals(FIRST_OUTER_LEFT)) shape = OUTER_LEFT;
|
||||
}
|
||||
}
|
||||
|
||||
return shape;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipartBlockStateSupplier getMultipart() {
|
||||
Identifier straight_id = ReFramed.id("stairs_special");
|
||||
Identifier double_outer_id = ReFramed.id("double_outer_stairs_special");
|
||||
Identifier inner_id = ReFramed.id("inner_stairs_special");
|
||||
Identifier outer_id = ReFramed.id("outer_stairs_special");
|
||||
Identifier outer_side_id = ReFramed.id("outer_side_stairs_special");
|
||||
return MultipartBlockStateSupplier.create(this)
|
||||
/* STRAIGHT X AXIS */
|
||||
.with(GBlockstate.when(FACING, DOWN_EAST, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R0, R0))
|
||||
.with(GBlockstate.when(FACING, EAST_UP, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R180, R0))
|
||||
.with(GBlockstate.when(FACING, UP_WEST, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R180, R180))
|
||||
.with(GBlockstate.when(FACING, WEST_DOWN, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R0, R180))
|
||||
/* STRAIGHT Y AXIS */
|
||||
.with(GBlockstate.when(FACING, EAST_SOUTH, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R90, R0))
|
||||
.with(GBlockstate.when(FACING, SOUTH_WEST, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R90, R90))
|
||||
.with(GBlockstate.when(FACING, WEST_NORTH, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R90, R180))
|
||||
.with(GBlockstate.when(FACING, NORTH_EAST, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R90, R270))
|
||||
/* STRAIGHT Z AXIS */
|
||||
.with(GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R0, R90))
|
||||
.with(GBlockstate.when(FACING, NORTH_DOWN, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R0, R270))
|
||||
.with(GBlockstate.when(FACING, UP_NORTH, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R180, R270))
|
||||
.with(GBlockstate.when(FACING, SOUTH_UP, SHAPE, STRAIGHT),
|
||||
GBlockstate.variant(straight_id, true, R180, R90))
|
||||
/* INNER BOTTOM */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_DOWN, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, WEST_NORTH, SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(FACING, WEST_DOWN, SHAPE, INNER_LEFT)),
|
||||
GBlockstate.variant(inner_id, true, R0, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_DOWN, SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(FACING, NORTH_EAST, SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_EAST, SHAPE, INNER_LEFT)),
|
||||
GBlockstate.variant(inner_id, true, R0, R270))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(FACING, EAST_SOUTH, SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_EAST, SHAPE, INNER_RIGHT)),
|
||||
GBlockstate.variant(inner_id, true, R0, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, SOUTH_WEST, SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(FACING, WEST_DOWN, SHAPE, INNER_RIGHT)),
|
||||
GBlockstate.variant(inner_id, true, R0, R90))
|
||||
/* INNER TOP */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, EAST_UP, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, NORTH_EAST, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, UP_NORTH, SHAPE, INNER_RIGHT)),
|
||||
GBlockstate.variant(inner_id, true, R180, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, EAST_UP, SHAPE, INNER_RIGHT),
|
||||
GBlockstate.when(FACING, EAST_SOUTH, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, SOUTH_UP, SHAPE, INNER_RIGHT)),
|
||||
GBlockstate.variant(inner_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_UP, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, SOUTH_WEST, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, UP_WEST, SHAPE, INNER_RIGHT)),
|
||||
GBlockstate.variant(inner_id, true, R180, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, UP_NORTH, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, WEST_NORTH, SHAPE, INNER_LEFT),
|
||||
GBlockstate.when(FACING, UP_WEST, SHAPE, INNER_LEFT)),
|
||||
GBlockstate.variant(inner_id, true, R180, R270))
|
||||
/* OUTER BOTTOM */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_EAST, SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_id, true, R0, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, WEST_DOWN, SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_id, true, R0, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_DOWN, SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, WEST_DOWN, SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_id, true, R0, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_DOWN, SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_EAST, SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_id, true, R0, R270))
|
||||
/* OUTER TOP */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, UP_NORTH, SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, EAST_UP, SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_id, true, R180, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_UP, SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, EAST_UP, SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_UP, SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, UP_WEST, SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_id, true, R180, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, UP_NORTH, SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, UP_WEST, SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_id, true, R180, R270))
|
||||
/* OUTER EAST */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, EAST_SOUTH, SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_EAST, SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R0, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, EAST_SOUTH, SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, EAST_UP, SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R90, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_EAST, SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, EAST_UP, SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R180, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_EAST, SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_EAST, SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R270, R0))
|
||||
/* OUTER SOUTH */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_WEST, SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R0, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_WEST, SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, SOUTH_UP, SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R90, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, EAST_SOUTH, SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, SOUTH_UP, SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, EAST_SOUTH, SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R270, R90))
|
||||
/* OUTER WEST */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, WEST_NORTH, SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, WEST_DOWN, SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R0, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, WEST_NORTH, SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, UP_WEST, SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R90, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_WEST, SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, UP_WEST, SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R180, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_WEST, SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, WEST_DOWN, SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R270, R180))
|
||||
/* OUTER NORTH */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_EAST, SHAPE, SECOND_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, NORTH_DOWN, SHAPE, SECOND_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R0, R270))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_EAST, SHAPE, SECOND_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, UP_NORTH, SHAPE, FIRST_OUTER_RIGHT)),
|
||||
GBlockstate.variant(outer_side_id, true, R90, R270))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, WEST_NORTH, SHAPE, FIRST_OUTER_LEFT),
|
||||
GBlockstate.when(FACING, UP_NORTH, SHAPE, FIRST_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R180, R270))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, WEST_NORTH, SHAPE, FIRST_OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, NORTH_DOWN, SHAPE, SECOND_OUTER_LEFT)),
|
||||
GBlockstate.variant(outer_side_id, true, R270, R270))
|
||||
/* OUTER BOTTOM */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_EAST, SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, EAST_SOUTH, SHAPE, OUTER_RIGHT)),
|
||||
GBlockstate.variant(double_outer_id, true, R0, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(FACING, WEST_DOWN, SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, SOUTH_WEST, SHAPE, OUTER_RIGHT)),
|
||||
GBlockstate.variant(double_outer_id, true, R0, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_DOWN, SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(FACING, WEST_DOWN, SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(FACING, WEST_NORTH, SHAPE, OUTER_RIGHT)),
|
||||
GBlockstate.variant(double_outer_id, true, R0, R180))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, NORTH_DOWN, SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, DOWN_EAST, SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(FACING, NORTH_EAST, SHAPE, OUTER_RIGHT)),
|
||||
GBlockstate.variant(double_outer_id, true, R0, R270))
|
||||
/* OUTER BOTTOM */
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, UP_NORTH, SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, EAST_UP, SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(FACING, NORTH_EAST, SHAPE, OUTER_LEFT)),
|
||||
GBlockstate.variant(double_outer_id, true, R180, R0))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_UP, SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, EAST_UP, SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, EAST_SOUTH, SHAPE, OUTER_LEFT)),
|
||||
GBlockstate.variant(double_outer_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, SOUTH_UP, SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(FACING, UP_WEST, SHAPE, OUTER_RIGHT),
|
||||
GBlockstate.when(FACING, SOUTH_WEST, SHAPE, OUTER_LEFT)),
|
||||
GBlockstate.variant(double_outer_id, true, R180, R90))
|
||||
.with(When.anyOf(
|
||||
GBlockstate.when(FACING, UP_NORTH, SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(FACING, UP_WEST, SHAPE, OUTER_LEFT),
|
||||
GBlockstate.when(FACING, WEST_NORTH, SHAPE, OUTER_LEFT)),
|
||||
GBlockstate.variant(double_outer_id, true, R270, R90));
|
||||
}
|
||||
|
||||
static {
|
||||
final VoxelShape STRAIGHT = Stream.of(
|
||||
VoxelShapes.cuboid(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f),
|
||||
VoxelShapes.cuboid(0.0f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f)
|
||||
).reduce((previous, current) -> VoxelShapes.combineAndSimplify(previous, current, BooleanBiFunction.OR)).get();
|
||||
final VoxelShape JUNCTION = Stream.of(
|
||||
VoxelShapes.cuboid(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f),
|
||||
VoxelShapes.cuboid(0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f)
|
||||
).reduce((previous, current) -> VoxelShapes.combineAndSimplify(previous, current, BooleanBiFunction.OR)).get();
|
||||
final VoxelShape OUTER = Stream.of(
|
||||
VoxelShapes.cuboid(0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f),
|
||||
VoxelShapes.cuboid(0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.5f),
|
||||
VoxelShapes.cuboid(0.0f, 0.5f, 0.5f, 0.5f, 1.0f, 1.0f),
|
||||
VoxelShapes.cuboid(0.5f, 0.0f, 0.5f, 1.0f, 0.5f, 1.0f)
|
||||
).reduce((previous, current) -> VoxelShapes.combineAndSimplify(previous, current, BooleanBiFunction.OR)).get();
|
||||
final VoxelShape INNER = Stream.of(
|
||||
VoxelShapes.cuboid(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f),
|
||||
VoxelShapes.cuboid(0.0f, 0.5f, 0.0f, 0.5f, 1.0f, 1.0f),
|
||||
VoxelShapes.cuboid(0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.5f)
|
||||
).reduce((previous, current) -> VoxelShapes.combineAndSimplify(previous, current, BooleanBiFunction.OR)).get();
|
||||
|
||||
VOXEL_LIST.add(STRAIGHT);
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(STRAIGHT, Axis.Z));
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.X), Axis.Z));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.X));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.Y), Axis.X));
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.Y), Axis.Z), Axis.X));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(STRAIGHT, Axis.Y), Axis.Z));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateClockwise(STRAIGHT, Axis.Z), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(STRAIGHT, Axis.Z));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(STRAIGHT, Axis.Z), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(STRAIGHT, Axis.Z), Axis.Y), Axis.Z));
|
||||
|
||||
VOXEL_LIST.add(JUNCTION);
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(JUNCTION, Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(JUNCTION, Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.Y));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(JUNCTION, Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(JUNCTION, Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.mirror(JUNCTION, Axis.Y), Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(JUNCTION, Axis.Y), Axis.Y));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z), Axis.Z));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z), Axis.Z));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z), Axis.Z), Axis.Z));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Z), Axis.Z));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X), Axis.X));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X), Axis.X));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X), Axis.X), Axis.X));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(JUNCTION, Axis.X), Axis.Y), Axis.X), Axis.X));
|
||||
|
||||
VOXEL_LIST.add(OUTER);
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(OUTER, Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(OUTER, Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(OUTER, Axis.Y));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(OUTER, Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(OUTER, Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(OUTER, Axis.Y), Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(OUTER, Axis.Y), Axis.Y));
|
||||
|
||||
VOXEL_LIST.add(INNER);
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(INNER, Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(INNER, Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(INNER, Axis.Y));
|
||||
|
||||
VOXEL_LIST.add(VoxelHelper.mirror(INNER, Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateClockwise(VoxelHelper.mirror(INNER, Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(INNER, Axis.Y), Axis.Y), Axis.Y));
|
||||
VOXEL_LIST.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.mirror(INNER, Axis.Y), Axis.Y));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.Edge;
|
||||
import fr.adrien1106.reframed.util.blocks.StairShape;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.block.ReFramedStairBlock.*;
|
||||
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.STAIR_SHAPE;
|
||||
|
||||
public class ReFramedStairsCubeBlock extends ReFramedDoubleBlock implements BlockStateProvider {
|
||||
|
||||
private static final VoxelShape[] STAIRS_CUBE_VOXELS = VoxelListBuilder.buildFrom(STAIR_VOXELS);
|
||||
private record ModelCacheKey(Edge edge, StairShape shape) {}
|
||||
|
||||
public ReFramedStairsCubeBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(STAIR_SHAPE, StairShape.STRAIGHT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return new ModelCacheKey(state.get(EDGE), state.get(STAIR_SHAPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 108; // Has 12 * 9 state combination and 52 models still reduces cache size
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(EDGE, STAIR_SHAPE));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) {
|
||||
return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved)
|
||||
.with(STAIR_SHAPE, BlockHelper.getStairsShape(state.get(EDGE), world, pos));
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
Edge face = BlockHelper.getPlacementEdge(ctx);
|
||||
StairShape shape = BlockHelper.getStairsShape(face, ctx.getWorld(), ctx.getBlockPos());
|
||||
return super.getPlacementState(ctx).with(EDGE, face).with(STAIR_SHAPE, shape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
|
||||
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, int i) {
|
||||
Edge edge = state.get(EDGE);
|
||||
StairShape shape = state.get(STAIR_SHAPE);
|
||||
return i == 2 ? STAIRS_CUBE_VOXELS[edge.getID() * 9 + shape.getID()] : getStairShape(edge, shape);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipartBlockStateSupplier getMultipart() {
|
||||
return getStairMultipart(this, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE);
|
||||
ShapelessRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.input(ReFramed.STAIR)
|
||||
.input(ReFramed.STEP)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.util.VoxelHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.Edge;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.BlockStateSupplier;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
|
||||
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
|
||||
import static fr.adrien1106.reframed.util.blocks.Edge.*;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
|
||||
public class ReFramedStepBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
|
||||
|
||||
public static final VoxelShape[] STEP_VOXELS;
|
||||
|
||||
public ReFramedStepBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return state.get(EDGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 12;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(EDGE));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return super.getPlacementState(ctx).with(EDGE, BlockHelper.getPlacementEdge(ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return getStepShape(state.get(EDGE));
|
||||
}
|
||||
|
||||
public static VoxelShape getStepShape(Edge edge) {
|
||||
return STEP_VOXELS[edge.getID()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockStateSupplier getMultipart() {
|
||||
Identifier model_id = ReFramed.id("step_special");
|
||||
return MultipartBlockStateSupplier.create(this)
|
||||
/* X AXIS */
|
||||
.with(GBlockstate.when(EDGE, DOWN_EAST),
|
||||
GBlockstate.variant(model_id, true, R0, R0))
|
||||
.with(GBlockstate.when(EDGE, EAST_UP),
|
||||
GBlockstate.variant(model_id, true, R180, R0))
|
||||
.with(GBlockstate.when(EDGE, UP_WEST),
|
||||
GBlockstate.variant(model_id, true, R180, R180))
|
||||
.with(GBlockstate.when(EDGE, WEST_DOWN),
|
||||
GBlockstate.variant(model_id, true, R0, R180))
|
||||
/* Y AXIS */
|
||||
.with(GBlockstate.when(EDGE, EAST_SOUTH),
|
||||
GBlockstate.variant(model_id, true, R90, R0))
|
||||
.with(GBlockstate.when(EDGE, SOUTH_WEST),
|
||||
GBlockstate.variant(model_id, true, R90, R90))
|
||||
.with(GBlockstate.when(EDGE, WEST_NORTH),
|
||||
GBlockstate.variant(model_id, true, R90, R180))
|
||||
.with(GBlockstate.when(EDGE, NORTH_EAST),
|
||||
GBlockstate.variant(model_id, true, R90, R270))
|
||||
/* Z AXIS */
|
||||
.with(GBlockstate.when(EDGE, DOWN_SOUTH),
|
||||
GBlockstate.variant(model_id, true, R0, R90))
|
||||
.with(GBlockstate.when(EDGE, NORTH_DOWN),
|
||||
GBlockstate.variant(model_id, true, R0, R270))
|
||||
.with(GBlockstate.when(EDGE, UP_NORTH),
|
||||
GBlockstate.variant(model_id, true, R180, R270))
|
||||
.with(GBlockstate.when(EDGE, SOUTH_UP),
|
||||
GBlockstate.variant(model_id, true, R180, R90));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 4);
|
||||
ShapedRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this, 8)
|
||||
.pattern("II")
|
||||
.input('I', ReFramed.CUBE)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
|
||||
static {
|
||||
final VoxelShape STEP = createCuboidShape(0, 0, 0, 16, 8, 8);
|
||||
|
||||
STEP_VOXELS = VoxelListBuilder.create(STEP, 12)
|
||||
.add(VoxelHelper::rotateCX)
|
||||
.add(VoxelHelper::rotateCX)
|
||||
.add(VoxelHelper::rotateCX)
|
||||
|
||||
.add(STEP, VoxelHelper::rotateCY)
|
||||
.add(VoxelHelper::rotateZ)
|
||||
.add(VoxelHelper::rotateZ)
|
||||
.add(VoxelHelper::rotateZ)
|
||||
|
||||
.add(STEP, VoxelHelper::rotateCZ)
|
||||
.add(VoxelHelper::rotateY)
|
||||
.add(VoxelHelper::rotateY)
|
||||
.add(VoxelHelper::rotateY)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.generator.BlockStateProvider;
|
||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
||||
import fr.adrien1106.reframed.util.blocks.BlockHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.Edge;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.RecipeProvider;
|
||||
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Direction.Axis;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
|
||||
import static fr.adrien1106.reframed.block.ReFramedStepBlock.getStepShape;
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
||||
import static net.minecraft.state.property.Properties.AXIS;
|
||||
import static net.minecraft.state.property.Properties.FACING;
|
||||
import static net.minecraft.util.shape.VoxelShapes.empty;
|
||||
|
||||
public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider {
|
||||
private record ModelCacheKey(Direction facing, Axis axis) {}
|
||||
|
||||
public ReFramedStepsSlabBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(getDefaultState().with(FACING, Direction.DOWN).with(AXIS, Axis.X));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getModelCacheKey(BlockState state) {
|
||||
return new ModelCacheKey(state.get(FACING), state.get(AXIS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModelStateCount() {
|
||||
return 18;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(FACING, AXIS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
Vec3d pos = BlockHelper.getHitPos(ctx.getHitPos(), ctx.getBlockPos());
|
||||
return super.getPlacementState(ctx)
|
||||
.with(FACING, ctx.getSide().getOpposite())
|
||||
.with(AXIS, BlockHelper.getHitAxis(pos, ctx.getSide()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
|
||||
return isGhost(view, pos) ? empty() : getSlabShape(state.get(FACING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||
return getSlabShape(state.get(FACING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getShape(BlockState state, int i) {
|
||||
Axis axis = state.get(AXIS);
|
||||
return getStepShape(Edge.getByDirections(
|
||||
state.get(FACING),
|
||||
switch (axis) {
|
||||
case X -> i == 1 ? Direction.WEST : Direction.EAST;
|
||||
case Y -> i == 1 ? Direction.DOWN : Direction.UP;
|
||||
case Z -> i == 1 ? Direction.SOUTH : Direction.NORTH;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTopThemeIndex(BlockState state) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MultipartBlockStateSupplier getMultipart() {
|
||||
Identifier step_id = ReFramed.id("steps_slab_special");
|
||||
Identifier step_side_id = ReFramed.id("steps_slab_side_special");
|
||||
return MultipartBlockStateSupplier.create(this)
|
||||
.with(GBlockstate.when(FACING, Direction.DOWN, AXIS, Axis.X),
|
||||
GBlockstate.variant(step_id, true, R0, R180))
|
||||
.with(GBlockstate.when(FACING, Direction.DOWN, AXIS, Axis.Z),
|
||||
GBlockstate.variant(step_id, true, R0, R90))
|
||||
.with(GBlockstate.when(FACING, Direction.UP, AXIS, Axis.X),
|
||||
GBlockstate.variant(step_id, true, R180, R180))
|
||||
.with(GBlockstate.when(FACING, Direction.UP, AXIS, Axis.Z),
|
||||
GBlockstate.variant(step_id, true, R180, R90))
|
||||
|
||||
.with(GBlockstate.when(FACING, Direction.EAST, AXIS, Axis.Z),
|
||||
GBlockstate.variant(step_side_id, true, R180, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.EAST, AXIS, Axis.Y),
|
||||
GBlockstate.variant(step_side_id, true, R90, R0))
|
||||
.with(GBlockstate.when(FACING, Direction.SOUTH, AXIS, Axis.X),
|
||||
GBlockstate.variant(step_side_id, true, R180, R90))
|
||||
.with(GBlockstate.when(FACING, Direction.SOUTH, AXIS, Axis.Y),
|
||||
GBlockstate.variant(step_side_id, true, R90, R90))
|
||||
.with(GBlockstate.when(FACING, Direction.WEST, AXIS, Axis.Z),
|
||||
GBlockstate.variant(step_side_id, true, R0, R180))
|
||||
.with(GBlockstate.when(FACING, Direction.WEST, AXIS, Axis.Y),
|
||||
GBlockstate.variant(step_side_id, true, R90, R180))
|
||||
.with(GBlockstate.when(FACING, Direction.NORTH, AXIS, Axis.X),
|
||||
GBlockstate.variant(step_side_id, true, R0, R270))
|
||||
.with(GBlockstate.when(FACING, Direction.NORTH, AXIS, Axis.Y),
|
||||
GBlockstate.variant(step_side_id, true, R90, R270));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 2);
|
||||
ShapelessRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.input(ReFramed.STEP, 2)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import fr.adrien1106.reframed.util.ReframedInteractible;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockSetType;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.TrapdoorBlock;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReFramedTrapdoorBlock extends TrapdoorBlock implements BlockEntityProvider, ReframedInteractible {
|
||||
public ReFramedTrapdoorBlock(Settings settings, BlockSetType woodType) {
|
||||
super(woodType, settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
super.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||
import fr.adrien1106.reframed.mixin.WallBlockAccessor;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ShapeContext;
|
||||
import net.minecraft.block.WallBlock;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.enums.WallShape;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ReFramedWallBlock extends WallBlock implements BlockEntityProvider {
|
||||
public ReFramedWallBlock(Settings settings) {
|
||||
super(settings);
|
||||
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||
|
||||
initNewShapemaps(); //WallBlock specific haxx
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
|
||||
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||
return r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||
super.onStateReplaced(state, world, pos, newState, moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), getNewShape(state, newCollisionShapeMap));
|
||||
}
|
||||
|
||||
@Override
|
||||
public VoxelShape getOutlineShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||
return getNewShape(state, newShapeMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean emitsRedstonePower(BlockState state) {
|
||||
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||
}
|
||||
|
||||
//Shapemap heck (WallBlock has a map keyed on BlockState, but since we add more blockstates most of those map lookups fail)
|
||||
|
||||
protected record ShapeKey(boolean up, WallShape north, WallShape east, WallShape south, WallShape west) {
|
||||
static ShapeKey fromBlockstate(BlockState state) {
|
||||
return new ShapeKey(
|
||||
state.get(WallBlock.UP),
|
||||
state.get(WallBlock.NORTH_SHAPE),
|
||||
state.get(WallBlock.EAST_SHAPE),
|
||||
state.get(WallBlock.SOUTH_SHAPE),
|
||||
state.get(WallBlock.WEST_SHAPE)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected final Map<ShapeKey, VoxelShape> newShapeMap = new HashMap<>();
|
||||
protected final Map<ShapeKey, VoxelShape> newCollisionShapeMap = new HashMap<>();
|
||||
|
||||
protected void initNewShapemaps() {
|
||||
initNewShapemap(((WallBlockAccessor) this).getShapeMap(), newShapeMap);
|
||||
initNewShapemap(((WallBlockAccessor) this).getCollisionShapeMap(), newCollisionShapeMap);
|
||||
}
|
||||
|
||||
protected void initNewShapemap(Map<BlockState, VoxelShape> oldShapeMap, Map<ShapeKey, VoxelShape> newShapeMap) {
|
||||
oldShapeMap.forEach((state, shape) -> newShapeMap.putIfAbsent(ShapeKey.fromBlockstate(state), shape));
|
||||
}
|
||||
|
||||
protected VoxelShape getNewShape(BlockState state, Map<ShapeKey, VoxelShape> shapes) {
|
||||
return shapes.getOrDefault(ShapeKey.fromBlockstate(state), VoxelShapes.empty());
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterloggable {
|
||||
public WaterloggableReFramedBlock(Settings settings) {
|
||||
super(settings);
|
||||
|
||||
setDefaultState(getDefaultState().with(Properties.WATERLOGGED, false));
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package fr.adrien1106.reframed.block;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Waterloggable;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.fluid.Fluids;
|
||||
import net.minecraft.item.ItemPlacementContext;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.WorldAccess;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class WaterloggableReFramedDoubleBlock extends ReFramedDoubleBlock implements Waterloggable {
|
||||
public WaterloggableReFramedDoubleBlock(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||
super.appendProperties(builder.add(Properties.WATERLOGGED));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||
BlockState sup = super.getPlacementState(ctx);
|
||||
if(sup != null) sup = sup.with(Properties.WATERLOGGED, ctx.getWorld().getFluidState(ctx.getBlockPos()).isOf(Fluids.WATER));
|
||||
return sup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockState state) {
|
||||
return state.get(Properties.WATERLOGGED) ? Fluids.WATER.getStill(false) : super.getFluidState(state);
|
||||
}
|
||||
|
||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState otherState, WorldAccess world, BlockPos pos, BlockPos moved) {
|
||||
if(state.get(Properties.WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
|
||||
return super.getStateForNeighborUpdate(state, direction, otherState, world, pos, moved);
|
||||
}
|
||||
}
|
||||
@@ -26,72 +26,68 @@ public class ReFramedClient implements ClientModInitializer {
|
||||
//all frames mustn't be on the SOLID layer because they are not opaque!
|
||||
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0]));
|
||||
|
||||
HELPER.addReFramedModel(ReFramed.id("button_special") , HELPER.auto(new Identifier("block/button")));
|
||||
HELPER.addReFramedModel(ReFramed.id("button_pressed_special") , HELPER.auto(new Identifier("block/button_pressed")));
|
||||
HELPER.addReFramedModel(ReFramed.id("one_candle_special") , HELPER.auto(new Identifier("block/template_candle")));
|
||||
HELPER.addReFramedModel(ReFramed.id("two_candles_special") , HELPER.auto(new Identifier("block/template_two_candles")));
|
||||
HELPER.addReFramedModel(ReFramed.id("three_candles_special") , HELPER.auto(new Identifier("block/template_three_candles")));
|
||||
HELPER.addReFramedModel(ReFramed.id("four_candles_special") , HELPER.auto(new Identifier("block/template_four_candles")));
|
||||
HELPER.addReFramedModel(ReFramed.id("carpet_special") , HELPER.auto(new Identifier("block/carpet")));
|
||||
HELPER.addReFramedModel(ReFramed.id("cube_special") , HELPER.auto(new Identifier("block/cube")));
|
||||
HELPER.addReFramedModel(ReFramed.id("door_bottom_left_special") , HELPER.auto(new Identifier("block/door_bottom_left")));
|
||||
HELPER.addReFramedModel(ReFramed.id("door_bottom_right_special") , HELPER.auto(new Identifier("block/door_bottom_right")));
|
||||
HELPER.addReFramedModel(ReFramed.id("door_top_left_special") , HELPER.auto(new Identifier("block/door_top_left")));
|
||||
HELPER.addReFramedModel(ReFramed.id("door_top_right_special") , HELPER.auto(new Identifier("block/door_top_right")));
|
||||
HELPER.addReFramedModel(ReFramed.id("door_bottom_left_open_special"), HELPER.auto(new Identifier("block/door_bottom_left_open")));
|
||||
HELPER.addReFramedModel(ReFramed.id("door_bottom_right_open_special"), HELPER.auto(new Identifier("block/door_bottom_right_open"))); //This is why we dont format code as tables kids
|
||||
HELPER.addReFramedModel(ReFramed.id("door_top_left_open_special") , HELPER.auto(new Identifier("block/door_top_left_open")));
|
||||
HELPER.addReFramedModel(ReFramed.id("door_top_right_open_special") , HELPER.auto(new Identifier("block/door_top_right_open")));
|
||||
HELPER.addReFramedModel(ReFramed.id("fence_post_special") , HELPER.auto(new Identifier("block/fence_post")));
|
||||
HELPER.addReFramedModel(ReFramed.id("fence_gate_special") , HELPER.auto(new Identifier("block/template_fence_gate")));
|
||||
HELPER.addReFramedModel(ReFramed.id("fence_gate_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_open")));
|
||||
HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall")));
|
||||
HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall_open")));
|
||||
HELPER.addReFramedModel(ReFramed.id("glass_pane_post_special") , HELPER.auto(new Identifier("block/glass_pane_post")));
|
||||
HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_special") , HELPER.auto(new Identifier("block/glass_pane_noside")));
|
||||
HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_alt_special"), HELPER.auto(new Identifier("block/glass_pane_noside_alt")));
|
||||
HELPER.addReFramedModel(ReFramed.id("pressure_plate_up_special") , HELPER.auto(new Identifier("block/pressure_plate_up")));
|
||||
HELPER.addReFramedModel(ReFramed.id("pressure_plate_down_special") , HELPER.auto(new Identifier("block/pressure_plate_down")));
|
||||
HELPER.addReFramedModel(ReFramed.id("slab_special") , HELPER.auto(new Identifier("block/slab")));
|
||||
HELPER.addReFramedModel(ReFramed.id("stairs_special") , HELPER.auto(new Identifier("block/stairs")));
|
||||
HELPER.addReFramedModel(ReFramed.id("double_outer_stairs_special") , HELPER.auto(ReFramed.id("block/double_outer_stairs")));
|
||||
HELPER.addReFramedModel(ReFramed.id("inner_stairs_special") , HELPER.auto(new Identifier("block/inner_stairs")));
|
||||
HELPER.addReFramedModel(ReFramed.id("outer_stairs_special") , HELPER.auto(new Identifier("block/outer_stairs")));
|
||||
HELPER.addReFramedModel(ReFramed.id("outer_side_stairs_special") , HELPER.auto(ReFramed.id("block/outer_side_stairs")));
|
||||
HELPER.addReFramedModel(ReFramed.id("trapdoor_bottom_special") , HELPER.auto(new Identifier("block/template_trapdoor_bottom")));
|
||||
HELPER.addReFramedModel(ReFramed.id("trapdoor_top_special") , HELPER.auto(new Identifier("block/template_trapdoor_top")));
|
||||
HELPER.addReFramedModel(ReFramed.id("wall_post_special") , HELPER.auto(new Identifier("block/template_wall_post")));
|
||||
|
||||
//vanilla style models (using "special-sprite replacement" method)
|
||||
HELPER.addReFramedModel(ReFramed.id("lever_special") , HELPER.json(ReFramed.id("block/lever")));
|
||||
HELPER.addReFramedModel(ReFramed.id("trapdoor_open_special") , HELPER.json(ReFramed.id("block/trapdoor_open")));
|
||||
HELPER.addReFramedModel(ReFramed.id("lever_on_special") , HELPER.json(ReFramed.id("block/lever_on")));
|
||||
//these next five only exist because AutoRetexturedModels don't seem to rotate their textures the right way when rotated from a multipart blockstate
|
||||
HELPER.addReFramedModel(ReFramed.id("fence_side_special") , HELPER.json(ReFramed.id("block/fence_side")));
|
||||
HELPER.addReFramedModel(ReFramed.id("glass_pane_side_special") , HELPER.json(ReFramed.id("block/glass_pane_side")));
|
||||
HELPER.addReFramedModel(ReFramed.id("glass_pane_side_alt_special") , HELPER.json(ReFramed.id("block/glass_pane_side_alt")));
|
||||
HELPER.addReFramedModel(ReFramed.id("wall_side_special") , HELPER.json(ReFramed.id("block/wall_side")));
|
||||
HELPER.addReFramedModel(ReFramed.id("wall_side_tall_special") , HELPER.json(ReFramed.id("block/wall_side_tall")));
|
||||
|
||||
//item only models
|
||||
HELPER.addReFramedModel(ReFramed.id("button_inventory_special") , HELPER.auto(new Identifier("block/button_inventory")));
|
||||
HELPER.addReFramedModel(ReFramed.id("fence_inventory_special") , HELPER.auto(new Identifier("block/fence_inventory")));
|
||||
HELPER.addReFramedModel(ReFramed.id("fence_post_inventory_special") , HELPER.auto(ReFramed.id("block/fence_post_inventory")));
|
||||
HELPER.addReFramedModel(ReFramed.id("wall_inventory_special") , HELPER.auto(new Identifier("block/wall_inventory")));
|
||||
// CUBE
|
||||
HELPER.addReFramedModel("cube_special" , HELPER.auto(new Identifier("block/cube")));
|
||||
// SMALL_CUBE
|
||||
HELPER.addReFramedModel("small_cube_special" , HELPER.auto(ReFramed.id("block/small_cube/base")));
|
||||
// SMALL_CUBES_STEP
|
||||
HELPER.addReFramedModel("small_cubes_step_special" , HELPER.autoDouble(ReFramed.id("block/small_cube/base"), ReFramed.id("block/small_cube/step/base")));
|
||||
HELPER.addReFramedModel("small_cubes_step_reverse_special" , HELPER.autoDouble(ReFramed.id("block/small_cube/step/base"), ReFramed.id("block/small_cube/base")));
|
||||
// SLAB
|
||||
HELPER.addReFramedModel("slab_special" , HELPER.auto(new Identifier("block/slab")));
|
||||
// SLAB_CUBE
|
||||
HELPER.addReFramedModel("double_slab_special" , HELPER.autoDouble(new Identifier("block/slab"), new Identifier("block/slab_top")));
|
||||
// STAIR
|
||||
HELPER.addReFramedModel("stair_special" , HELPER.auto(ReFramed.id("block/stair/straight")));
|
||||
HELPER.addReFramedModel("outers_stair_special" , HELPER.auto(ReFramed.id("block/stair/double_outer")));
|
||||
HELPER.addReFramedModel("inner_stair_special" , HELPER.auto(ReFramed.id("block/stair/inner")));
|
||||
HELPER.addReFramedModel("outer_stair_special" , HELPER.auto(ReFramed.id("block/stair/outer")));
|
||||
HELPER.addReFramedModel("outer_side_stair_special" , HELPER.auto(ReFramed.id("block/stair/outer_side")));
|
||||
// STAIRS_CUBE
|
||||
HELPER.addReFramedModel("stairs_cube_special" , HELPER.autoDouble(ReFramed.id("block/stair/straight"), ReFramed.id("block/stair/cube/straight")));
|
||||
HELPER.addReFramedModel("outers_stairs_cube_special" , HELPER.autoDouble(ReFramed.id("block/stair/double_outer"), ReFramed.id("block/stair/cube/double_outer")));
|
||||
HELPER.addReFramedModel("inner_stairs_cube_special" , HELPER.autoDouble(ReFramed.id("block/stair/inner"), ReFramed.id("block/stair/cube/inner")));
|
||||
HELPER.addReFramedModel("outer_stairs_cube_special" , HELPER.autoDouble(ReFramed.id("block/stair/outer"), ReFramed.id("block/stair/cube/outer")));
|
||||
HELPER.addReFramedModel("outer_side_stairs_cube_special" , HELPER.autoDouble(ReFramed.id("block/stair/outer_side"), ReFramed.id("block/stair/cube/outer_side")));
|
||||
// HALF_STAIR
|
||||
HELPER.addReFramedModel("half_stair_down_special" , HELPER.auto(ReFramed.id("block/half_stair/down")));
|
||||
HELPER.addReFramedModel("half_stair_side_special" , HELPER.auto(ReFramed.id("block/half_stair/side")));
|
||||
// HALF_STAIRS_SLAB
|
||||
HELPER.addReFramedModel("half_stairs_slab_down_special" , HELPER.autoDouble(ReFramed.id("block/half_stair/down"), ReFramed.id("block/half_stair/slab/down")));
|
||||
HELPER.addReFramedModel("half_stairs_slab_side_special" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/slab/side")));
|
||||
// HALF_STAIRS_STAIR
|
||||
HELPER.addReFramedModel("half_stairs_stair_down_special" , HELPER.autoDouble(ReFramed.id("block/half_stair/down"), ReFramed.id("block/half_stair/stair/down")));
|
||||
HELPER.addReFramedModel("half_stairs_stair_side_special" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/stair/side")));
|
||||
HELPER.addReFramedModel("half_stairs_stair_reverse_special" , HELPER.autoDouble(ReFramed.id("block/half_stair/stair/side"), ReFramed.id("block/half_stair/side")));
|
||||
// STEP
|
||||
HELPER.addReFramedModel("step_special" , HELPER.auto(ReFramed.id("block/step/down")));
|
||||
// STEPS_SLAB
|
||||
HELPER.addReFramedModel("steps_slab_special" , HELPER.autoDouble(ReFramed.id("block/step/down"), ReFramed.id("block/step/slab/down")));
|
||||
HELPER.addReFramedModel("steps_slab_side_special" , HELPER.autoDouble(ReFramed.id("block/step/side"), ReFramed.id("block/step/slab/side")));
|
||||
// LAYER
|
||||
HELPER.addReFramedModel("layer_1_special" , HELPER.auto(new Identifier("block/snow_height2")));
|
||||
HELPER.addReFramedModel("layer_2_special" , HELPER.auto(new Identifier("block/snow_height4")));
|
||||
HELPER.addReFramedModel("layer_3_special" , HELPER.auto(new Identifier("block/snow_height6")));
|
||||
HELPER.addReFramedModel("layer_4_special" , HELPER.auto(new Identifier("block/snow_height8")));
|
||||
HELPER.addReFramedModel("layer_5_special" , HELPER.auto(new Identifier("block/snow_height10")));
|
||||
HELPER.addReFramedModel("layer_6_special" , HELPER.auto(new Identifier("block/snow_height12")));
|
||||
HELPER.addReFramedModel("layer_7_special" , HELPER.auto(new Identifier("block/snow_height14")));
|
||||
HELPER.addReFramedModel("layer_8_special" , HELPER.auto(new Identifier("block/cube")));
|
||||
|
||||
//item model assignments (in lieu of models/item/___.json)
|
||||
HELPER.assignItemModel(ReFramed.id("button_inventory_special") , ReFramed.BUTTON);
|
||||
HELPER.assignItemModel(ReFramed.id("carpet_special") , ReFramed.CARPET);
|
||||
HELPER.assignItemModel(ReFramed.id("cube_special") , ReFramed.CUBE);
|
||||
HELPER.assignItemModel(ReFramed.id("fence_inventory_special") , ReFramed.FENCE);
|
||||
HELPER.assignItemModel(ReFramed.id("fence_gate_special") , ReFramed.FENCE_GATE);
|
||||
HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.IRON_TRAPDOOR);
|
||||
HELPER.assignItemModel(ReFramed.id("fence_post_inventory_special") , ReFramed.POST);
|
||||
HELPER.assignItemModel(ReFramed.id("pressure_plate_up_special") , ReFramed.PRESSURE_PLATE);
|
||||
HELPER.assignItemModel(ReFramed.id("slab_special") , ReFramed.SLAB);
|
||||
HELPER.assignItemModel(ReFramed.id("stairs_special") , ReFramed.STAIRS);
|
||||
HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.TRAPDOOR);
|
||||
HELPER.assignItemModel(ReFramed.id("wall_inventory_special") , ReFramed.WALL);
|
||||
HELPER.assignItemModel("cube_special" , ReFramed.CUBE);
|
||||
HELPER.assignItemModel("small_cube_special" , ReFramed.SMALL_CUBE);
|
||||
HELPER.assignItemModel("small_cubes_step_special" , ReFramed.SMALL_CUBES_STEP);
|
||||
HELPER.assignItemModel("slab_special" , ReFramed.SLAB);
|
||||
HELPER.assignItemModel("double_slab_special" , ReFramed.SLABS_CUBE);
|
||||
HELPER.assignItemModel("stair_special" , ReFramed.STAIR);
|
||||
HELPER.assignItemModel("stairs_cube_special" , ReFramed.STAIRS_CUBE);
|
||||
HELPER.assignItemModel("half_stair_down_special" , ReFramed.HALF_STAIR);
|
||||
HELPER.assignItemModel("half_stairs_slab_down_special" , ReFramed.HALF_STAIRS_SLAB);
|
||||
HELPER.assignItemModel("half_stairs_stair_down_special", ReFramed.HALF_STAIRS_STAIR);
|
||||
HELPER.assignItemModel("step_special" , ReFramed.STEP);
|
||||
HELPER.assignItemModel("steps_slab_special" , ReFramed.STEPS_SLAB);
|
||||
HELPER.assignItemModel("layer_1_special" , ReFramed.LAYER);
|
||||
}
|
||||
|
||||
private void privateInit() {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package fr.adrien1106.reframed.client;
|
||||
|
||||
import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.client.model.UnbakedAutoRetexturedModel;
|
||||
import fr.adrien1106.reframed.client.model.UnbakedDoubleRetexturedModel;
|
||||
import fr.adrien1106.reframed.client.model.UnbakedJsonRetexturedModel;
|
||||
import fr.adrien1106.reframed.client.model.UnbakedRetexturedModel;
|
||||
import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
@@ -23,20 +26,25 @@ public class ReFramedClientHelper {
|
||||
|
||||
private final ReFramedModelProvider prov;
|
||||
|
||||
public UnbakedModel auto(Identifier parent) {
|
||||
|
||||
public UnbakedRetexturedModel auto(Identifier parent) {
|
||||
return new UnbakedAutoRetexturedModel(parent);
|
||||
}
|
||||
|
||||
public UnbakedModel json(Identifier parent) {
|
||||
public UnbakedRetexturedModel json(Identifier parent) {
|
||||
return new UnbakedJsonRetexturedModel(parent);
|
||||
}
|
||||
|
||||
public void addReFramedModel(Identifier id, UnbakedModel unbaked) {
|
||||
prov.addReFramedModel(id, unbaked);
|
||||
public UnbakedModel autoDouble(Identifier first, Identifier second) {
|
||||
return new UnbakedDoubleRetexturedModel(auto(first), auto(second));
|
||||
}
|
||||
|
||||
public void assignItemModel(Identifier model_id, ItemConvertible... item_convertibles) {
|
||||
prov.assignItemModel(model_id, item_convertibles);
|
||||
public void addReFramedModel(String id, UnbakedModel unbaked) {
|
||||
prov.addReFramedModel(ReFramed.id(id), unbaked);
|
||||
}
|
||||
|
||||
public void assignItemModel(String id, ItemConvertible... item_convertibles) {
|
||||
prov.assignItemModel(ReFramed.id(id), item_convertibles);
|
||||
}
|
||||
|
||||
public CamoAppearanceManager getCamoApperanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
|
||||
|
||||
@@ -37,9 +37,9 @@ public class ReFramedModelProvider implements ModelResourceProvider, ModelVarian
|
||||
//but json models are never allowed to have non-json models as a parent, and frame unbaked models are not json models. Ah well.
|
||||
//So, instead, we use a ModelVariantProvider to redirect attempts to load the item:id#inventory model.
|
||||
@Override
|
||||
public @Nullable UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) {
|
||||
Identifier customModelId = itemAssignments.get(modelId);
|
||||
return customModelId == null ? null : loadModelResource(customModelId, context);
|
||||
public @Nullable UnbakedModel loadModelVariant(ModelIdentifier model, ModelProviderContext context) {
|
||||
Identifier custom_model = itemAssignments.get(model);
|
||||
return custom_model == null ? null : loadModelResource(custom_model, context);
|
||||
}
|
||||
|
||||
/// camo appearance manager cache
|
||||
@@ -70,6 +70,7 @@ public class ReFramedModelProvider implements ModelResourceProvider, ModelVarian
|
||||
}
|
||||
|
||||
public void dumpCache() {
|
||||
CamoAppearanceManager.dumpCahe();
|
||||
appearanceManager = null; //volatile write
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements MultiRetexturableModel {
|
||||
|
||||
private final ForwardingBakedModel model_1, model_2;
|
||||
public DoubleRetexturingBakedModel(ForwardingBakedModel model_1, ForwardingBakedModel model_2) {
|
||||
this.wrapped = model_1.getWrappedModel();
|
||||
this.model_1 = model_1;
|
||||
this.model_2 = model_2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanillaAdapter() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sprite getParticleSprite() {
|
||||
return model_1.getParticleSprite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {}
|
||||
|
||||
@Override // models are emitted here because no checks are done on items
|
||||
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
model_1.emitItemQuads(stack, randomSupplier, context);
|
||||
model_2.emitItemQuads(stack, randomSupplier, context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ForwardingBakedModel> models() {
|
||||
return List.of(model_1, model_2);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public interface DynamicBakedModel {
|
||||
BakedModel computeQuads(@Nullable BlockRenderView level, BlockState origin_state, @Nullable BlockPos pos, int theme_index);
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class MeshTransformUtil {
|
||||
public static Mesh pretransformMesh(Mesh mesh, RenderContext.QuadTransform transform) {
|
||||
MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder();
|
||||
QuadEmitter emitter = builder.getEmitter();
|
||||
|
||||
mesh.forEach(quad -> {
|
||||
emitter.copyFrom(quad);
|
||||
if(transform.transform(emitter)) emitter.emit();
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static Map<Direction, Direction> facePermutation(Matrix4f mat) {
|
||||
Map<Direction, Direction> facePermutation = new EnumMap<>(Direction.class);
|
||||
for(Direction input : Direction.values()) {
|
||||
Direction output = Direction.transform(mat, input);
|
||||
facePermutation.put(input, output);
|
||||
}
|
||||
return facePermutation;
|
||||
}
|
||||
|
||||
public static RenderContext.QuadTransform applyAffine(ModelBakeSettings settings) {
|
||||
return applyMatrix(settings.getRotation().getMatrix());
|
||||
}
|
||||
|
||||
public static RenderContext.QuadTransform applyMatrix(Matrix4f mat) {
|
||||
Map<Direction, Direction> facePermutation = facePermutation(mat);
|
||||
Vector3f pos3 = new Vector3f();
|
||||
Vector4f pos4 = new Vector4f();
|
||||
|
||||
return quad -> {
|
||||
//For each vertex:
|
||||
for(int i = 0; i < 4; i++) {
|
||||
//Copy pos into a vec3, then a vec4. the w component is set to 0 since this is a point, not a normal
|
||||
quad.copyPos(i, pos3);
|
||||
pos3.add(-0.5f, -0.5f, -0.5f);
|
||||
pos4.set(pos3, 0);
|
||||
|
||||
//Compute the matrix-vector product. This function mutates the vec4 in-place.
|
||||
//Note that `transformAffine` has the same purpose as `transform`; the difference is it
|
||||
//assumes (without checking) that the last row of the matrix is 0,0,0,1, as an optimization
|
||||
mat.transform(pos4);
|
||||
|
||||
//Manually copy the data back onto the vertex
|
||||
quad.pos(i, pos4.x + 0.5f, pos4.y + 0.5f, pos4.z + 0.5f);
|
||||
}
|
||||
|
||||
//permute tags
|
||||
int tag = quad.tag();
|
||||
if(tag != 0) quad.tag(facePermutation.get(RetexturingBakedModel.DIRECTIONS[tag - 1]).ordinal() + 1);
|
||||
|
||||
//permute lighting face (?)
|
||||
quad.nominalFace(facePermutation.get(quad.lightFace()));
|
||||
|
||||
//permute cullface
|
||||
Direction cull = quad.cullFace();
|
||||
if(cull != null) quad.cullFace(facePermutation.get(cull));
|
||||
|
||||
//Output the quad
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface MultiRetexturableModel {
|
||||
|
||||
List<ForwardingBakedModel> models();
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import org.joml.Vector3f;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public record QuadPosBounds(float min_x, float max_x, float min_y, float max_y, float min_z, float max_z) {
|
||||
|
||||
public static QuadPosBounds read(QuadView quad) {
|
||||
return read(quad, true);
|
||||
}
|
||||
|
||||
public static QuadPosBounds read(QuadView quad, boolean check_full) {
|
||||
float x0 = quad.x(0), x1 = quad.x(1), x2 = quad.x(2), x3 = quad.x(3);
|
||||
float y0 = quad.y(0), y1 = quad.y(1), y2 = quad.y(2), y3 = quad.y(3);
|
||||
float z0 = quad.z(0), z1 = quad.z(1), z2 = quad.z(2), z3 = quad.z(3);
|
||||
|
||||
// Checks if the Dimensions are either 0 or 1 except for the Axis dimension
|
||||
Direction.Axis axis = quad.nominalFace().getAxis();
|
||||
if (check_full && (axis == Direction.Axis.X || (
|
||||
(MathHelper.approximatelyEquals(x0, 0) || MathHelper.approximatelyEquals(x0, 1))
|
||||
&& (MathHelper.approximatelyEquals(x1, 0) || MathHelper.approximatelyEquals(x1, 1))
|
||||
&& (MathHelper.approximatelyEquals(x2, 0) || MathHelper.approximatelyEquals(x2, 1))
|
||||
&& (MathHelper.approximatelyEquals(x3, 0) || MathHelper.approximatelyEquals(x3, 1))
|
||||
)) && (axis == Direction.Axis.Y || (
|
||||
(MathHelper.approximatelyEquals(y0, 0) || MathHelper.approximatelyEquals(y0, 1))
|
||||
&& (MathHelper.approximatelyEquals(y1, 0) || MathHelper.approximatelyEquals(y1, 1))
|
||||
&& (MathHelper.approximatelyEquals(y2, 0) || MathHelper.approximatelyEquals(y2, 1))
|
||||
&& (MathHelper.approximatelyEquals(y3, 0) || MathHelper.approximatelyEquals(y3, 1))
|
||||
)) & (axis == Direction.Axis.Z || (
|
||||
(MathHelper.approximatelyEquals(z0, 0) || MathHelper.approximatelyEquals(z0, 1))
|
||||
&& (MathHelper.approximatelyEquals(z1, 0) || MathHelper.approximatelyEquals(z1, 1))
|
||||
&& (MathHelper.approximatelyEquals(z2, 0) || MathHelper.approximatelyEquals(z2, 1))
|
||||
&& (MathHelper.approximatelyEquals(z3, 0) || MathHelper.approximatelyEquals(z3, 1))
|
||||
))
|
||||
) return null;
|
||||
|
||||
return new QuadPosBounds(
|
||||
Math.min(Math.min(x0, x1), Math.min(x2, x3)),
|
||||
Math.max(Math.max(x0, x1), Math.max(x2, x3)),
|
||||
Math.min(Math.min(y0, y1), Math.min(y2, y3)),
|
||||
Math.max(Math.max(y0, y1), Math.max(y2, y3)),
|
||||
Math.min(Math.min(z0, z1), Math.min(z2, z3)),
|
||||
Math.max(Math.max(z0, z1), Math.max(z2, z3))
|
||||
);
|
||||
}
|
||||
|
||||
public boolean matches(QuadPosBounds other_bounds) {
|
||||
return !(
|
||||
(min_x != max_x && (min_x >= other_bounds.max_x || max_x <= other_bounds.min_x))
|
||||
|| (min_y != max_y && (min_y >= other_bounds.max_y || max_y <= other_bounds.min_y))
|
||||
|| (min_z != max_z && (min_z >= other_bounds.max_z || max_z <= other_bounds.min_z))
|
||||
);
|
||||
}
|
||||
|
||||
public QuadPosBounds intersection(QuadPosBounds other_bounds, Direction.Axis axis) {
|
||||
return new QuadPosBounds(
|
||||
axis.equals(Direction.Axis.X) ? other_bounds.min_x: Math.max(min_x, other_bounds.min_x),
|
||||
axis.equals(Direction.Axis.X) ? other_bounds.max_x: Math.min(max_x, other_bounds.max_x),
|
||||
axis.equals(Direction.Axis.Y) ? other_bounds.min_y: Math.max(min_y, other_bounds.min_y),
|
||||
axis.equals(Direction.Axis.Y) ? other_bounds.max_y: Math.min(max_y, other_bounds.max_y),
|
||||
axis.equals(Direction.Axis.Z) ? other_bounds.min_z: Math.max(min_z, other_bounds.min_z),
|
||||
axis.equals(Direction.Axis.Z) ? other_bounds.max_z: Math.min(max_z, other_bounds.max_z)
|
||||
);
|
||||
}
|
||||
|
||||
public void apply(MutableQuadView quad, QuadPosBounds origin_bounds) {
|
||||
Vector3f pos = new Vector3f();
|
||||
for (int i = 0; i < 4; i++) {
|
||||
quad.copyPos(i, pos);
|
||||
pos.x = MathHelper.approximatelyEquals(pos.x, origin_bounds.min_x)? min_x: max_x;
|
||||
pos.y = MathHelper.approximatelyEquals(pos.y, origin_bounds.min_y)? min_y: max_y;
|
||||
pos.z = MathHelper.approximatelyEquals(pos.z, origin_bounds.min_z)? min_z: max_z;
|
||||
quad.pos(i, pos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof QuadPosBounds other)) return false;
|
||||
return MathHelper.approximatelyEquals(min_x, other.min_x)
|
||||
&& MathHelper.approximatelyEquals(min_y, other.min_y)
|
||||
&& MathHelper.approximatelyEquals(min_z, other.min_z)
|
||||
&& MathHelper.approximatelyEquals(max_x, other.max_x)
|
||||
&& MathHelper.approximatelyEquals(max_y, other.max_y)
|
||||
&& MathHelper.approximatelyEquals(max_z, other.max_z);
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
record QuadUvBounds(float minU, float maxU, float minV, float maxV) {
|
||||
static QuadUvBounds read(QuadView quad) {
|
||||
float u0 = quad.u(0); float u1 = quad.u(1); float u2 = quad.u(2); float u3 = quad.u(3);
|
||||
float v0 = quad.v(0); float v1 = quad.v(1); float v2 = quad.v(2); float v3 = quad.v(3);
|
||||
@Environment(EnvType.CLIENT)
|
||||
public record QuadUvBounds(float minU, float maxU, float minV, float maxV) {
|
||||
public static QuadUvBounds read(QuadView quad) {
|
||||
float u0 = quad.u(0), u1 = quad.u(1), u2 = quad.u(2), u3 = quad.u(3);
|
||||
float v0 = quad.v(0), v1 = quad.v(1), v2 = quad.v(2), v3 = quad.v(3);
|
||||
return new QuadUvBounds(
|
||||
Math.min(Math.min(u0, u1), Math.min(u2, u3)),
|
||||
Math.max(Math.max(u0, u1), Math.max(u2, u3)),
|
||||
@@ -37,8 +40,4 @@ record QuadUvBounds(float minU, float maxU, float minV, float maxV) {
|
||||
return (value2 - low) / (high - low);
|
||||
}
|
||||
|
||||
//static float rangeRemap(float value, float low1, float high1, float low2, float high2) {
|
||||
// float value2 = MathHelper.clamp(value, low1, high1);
|
||||
// return low2 + (value2 - low1) * (high2 - low2) / (high1 - low1);
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
||||
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||
import fr.adrien1106.reframed.mixin.MinecraftAccessor;
|
||||
import fr.adrien1106.reframed.client.model.apperance.CamoAppearance;
|
||||
import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
|
||||
import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.*;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.block.BlockState;
|
||||
@@ -22,35 +24,60 @@ import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
||||
public RetexturingBakedModel(BakedModel baseModel, CamoAppearanceManager tam, ModelBakeSettings settings, BlockState itemModelState, boolean ao) {
|
||||
this.wrapped = baseModel; //field from the superclass; vanilla getQuads etc. will delegate through to this
|
||||
public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state, boolean ao) {
|
||||
this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this
|
||||
|
||||
this.tam = tam;
|
||||
this.uvlock = settings.isUvLocked();
|
||||
this.itemModelState = itemModelState;
|
||||
this.appearance_manager = tam;
|
||||
this.theme_index = theme_index;
|
||||
this.uv_lock = settings.isUvLocked();
|
||||
this.item_state = item_state;
|
||||
this.ao = ao;
|
||||
|
||||
int cache_size = 64; // default is 64 why don't ask me and it should get overwritten
|
||||
if (item_state.getBlock() instanceof ReFramedBlock frame_block) cache_size = frame_block.getModelStateCount() + 1;
|
||||
BASE_MESH_CACHE = new Object2ObjectLinkedOpenHashMap<>(cache_size, 0.25f) {
|
||||
@Override
|
||||
protected void rehash(int v) {}
|
||||
};
|
||||
}
|
||||
|
||||
protected final CamoAppearanceManager tam;
|
||||
protected final boolean uvlock;
|
||||
protected final BlockState itemModelState;
|
||||
protected final CamoAppearanceManager appearance_manager;
|
||||
protected final int theme_index;
|
||||
protected final boolean uv_lock;
|
||||
protected final boolean ao;
|
||||
protected final BlockState item_state;
|
||||
|
||||
protected record MeshCacheKey(BlockState state, TransformCacheKey transform) {}
|
||||
protected final ConcurrentMap<MeshCacheKey, Mesh> retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache
|
||||
protected record TransformCacheKey(CamoAppearance appearance, int model_id) {}
|
||||
protected final ConcurrentMap<TransformCacheKey, RetexturingTransformer> retextured_transforms = new ConcurrentHashMap<>();
|
||||
protected record MeshCacheKey(Object state_key, CamoAppearance appearance, int model_id) {}
|
||||
/** cache that store retextured models */
|
||||
protected final Object2ObjectLinkedOpenHashMap<MeshCacheKey, Mesh> RETEXTURED_MESH_CACHE =
|
||||
new Object2ObjectLinkedOpenHashMap<>(128, 0.25f) {
|
||||
@Override
|
||||
protected void rehash(int v) {}
|
||||
};
|
||||
|
||||
protected static final Direction[] DIRECTIONS = Direction.values();
|
||||
protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1];
|
||||
static { System.arraycopy(DIRECTIONS, 0, DIRECTIONS_AND_NULL, 0, DIRECTIONS.length); }
|
||||
/** cache that stores the base meshes which has the size of the amount of models */
|
||||
protected final Object2ObjectLinkedOpenHashMap<Object, Mesh> BASE_MESH_CACHE;
|
||||
|
||||
protected abstract Mesh getBaseMesh(BlockState state);
|
||||
protected static final Direction[] DIRECTIONS_AND_NULL;
|
||||
static {
|
||||
Direction[] values = Direction.values();
|
||||
DIRECTIONS_AND_NULL = new Direction[values.length + 1];
|
||||
System.arraycopy(values, 0, DIRECTIONS_AND_NULL, 0, values.length);
|
||||
}
|
||||
|
||||
protected Mesh getBaseMesh(Object key, BlockState state) {
|
||||
//Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate
|
||||
if (BASE_MESH_CACHE.containsKey(key)) return BASE_MESH_CACHE.getAndMoveToFirst(key);
|
||||
Mesh mesh = convertModel(state);
|
||||
BASE_MESH_CACHE.putAndMoveToFirst(key, mesh);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
protected abstract Mesh convertModel(BlockState state);
|
||||
|
||||
@Override
|
||||
public boolean isVanillaAdapter() {
|
||||
@@ -59,39 +86,42 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
||||
|
||||
@Override
|
||||
public Sprite getParticleSprite() {
|
||||
return tam.getDefaultAppearance().getSprite(Direction.UP, 0);
|
||||
return appearance_manager.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emitBlockQuads(BlockRenderView blockView, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
BlockState theme = (blockView.getBlockEntityRenderData(pos) instanceof BlockState s) ? s : null;
|
||||
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
// skip render if block not a frame (which should always be the case
|
||||
if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return;
|
||||
BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null;
|
||||
QuadEmitter quad_emitter = context.getEmitter();
|
||||
if(theme == null || theme.isAir()) {
|
||||
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(), 0)), 0).outputTo(quad_emitter);
|
||||
getRetexturedMesh(
|
||||
new MeshCacheKey(
|
||||
frame_block.getModelCacheKey(state),
|
||||
appearance_manager.getDefaultAppearance(theme_index),
|
||||
0
|
||||
),
|
||||
state
|
||||
).outputTo(quad_emitter);
|
||||
return;
|
||||
}
|
||||
if(theme.getBlock() == Blocks.BARRIER) return;
|
||||
|
||||
CamoAppearance camo = tam.getCamoAppearance(theme);
|
||||
CamoAppearance camo = appearance_manager.getCamoAppearance(world, theme, pos, theme_index, false);
|
||||
long seed = theme.getRenderingSeed(pos);
|
||||
int model_id = 0;
|
||||
if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
|
||||
|
||||
int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, blockView, pos, 0);
|
||||
Mesh untintedMesh = getUntintedRetexturedMesh(
|
||||
new MeshCacheKey(
|
||||
state,
|
||||
new TransformCacheKey(camo, model_id)
|
||||
),
|
||||
seed
|
||||
);
|
||||
int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, world, pos, 0);
|
||||
Mesh untintedMesh = getRetexturedMesh(new MeshCacheKey(frame_block.getModelCacheKey(state), camo, model_id), state);
|
||||
|
||||
//The specific tint might vary a lot; imagine grass color smoothly changing. Trying to bake the tint into
|
||||
//the cached mesh will pollute it with a ton of single-use meshes with only slightly different colors.
|
||||
if(tint == 0xFFFFFFFF) {
|
||||
untintedMesh.outputTo(quad_emitter);
|
||||
} else {
|
||||
context.pushTransform(new TintingTransformer(camo, tint, seed));
|
||||
context.pushTransform(new TintingTransformer(camo, model_id, tint));
|
||||
untintedMesh.outputTo(quad_emitter);
|
||||
context.popTransform();
|
||||
}
|
||||
@@ -101,78 +131,71 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
||||
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||
//cheeky: if the item has NBT data, pluck out the blockstate from it & look up the item color provider
|
||||
//none of this is accessible unless you're in creative mode doing ctrl-pick btw
|
||||
CamoAppearance nbtAppearance;
|
||||
CamoAppearance appearance;
|
||||
int tint;
|
||||
BlockState theme = ReFramedEntity.readStateFromItem(stack);
|
||||
BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index);
|
||||
if(!theme.isAir()) {
|
||||
nbtAppearance = tam.getCamoAppearance(theme);
|
||||
appearance = appearance_manager.getCamoAppearance(null, theme, null, theme_index, true);
|
||||
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
|
||||
} else {
|
||||
nbtAppearance = tam.getDefaultAppearance();
|
||||
appearance = appearance_manager.getDefaultAppearance(theme_index);
|
||||
tint = 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(itemModelState, new TransformCacheKey(nbtAppearance, 0)), 0);
|
||||
Mesh untintedMesh = getRetexturedMesh(new MeshCacheKey("I", appearance, 0), item_state);
|
||||
|
||||
QuadEmitter quad_emitter = context.getEmitter();
|
||||
if(tint == 0xFFFFFFFF) {
|
||||
untintedMesh.outputTo(quad_emitter);
|
||||
} else {
|
||||
context.pushTransform(new TintingTransformer(nbtAppearance, tint, 0));
|
||||
context.pushTransform(new TintingTransformer(appearance, 0, tint));
|
||||
untintedMesh.outputTo(quad_emitter);
|
||||
context.popTransform();
|
||||
}
|
||||
}
|
||||
|
||||
protected Mesh getUntintedRetexturedMesh(MeshCacheKey key, long seed) {
|
||||
return retextured_meshes.computeIfAbsent(key, (k) -> createUntintedRetexturedMesh(k, seed));
|
||||
protected Mesh getRetexturedMesh(MeshCacheKey key, BlockState state) {
|
||||
if (RETEXTURED_MESH_CACHE.containsKey(key)) return RETEXTURED_MESH_CACHE.getAndMoveToFirst(key);
|
||||
Mesh mesh = transformMesh(key, state);
|
||||
RETEXTURED_MESH_CACHE.putAndMoveToFirst(key, mesh);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
protected Mesh createUntintedRetexturedMesh(MeshCacheKey key, long seed) {
|
||||
RetexturingTransformer transformer = retextured_transforms.computeIfAbsent(key.transform, (k) -> new RetexturingTransformer(k.appearance, seed));
|
||||
return MeshTransformUtil.pretransformMesh(getBaseMesh(key.state), transformer);
|
||||
protected Mesh transformMesh(MeshCacheKey key, BlockState state) {
|
||||
MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder();
|
||||
QuadEmitter emitter = builder.getEmitter();
|
||||
|
||||
AtomicInteger quad_index = new AtomicInteger();
|
||||
getBaseMesh(key.state_key, state).forEach(quad -> {
|
||||
int i = -1;
|
||||
do {
|
||||
emitter.copyFrom(quad);
|
||||
i = key.appearance.transformQuad(emitter, i, quad_index.get(), key.model_id, ao, uv_lock);
|
||||
} while (i > 0);
|
||||
// kinda weird to do it like that but other directions don't use the quad_index so it doesn't matter
|
||||
if (quad.cullFace() == null) quad_index.getAndIncrement();
|
||||
});
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
protected class RetexturingTransformer implements RenderContext.QuadTransform {
|
||||
private final long seed;
|
||||
protected RetexturingTransformer(CamoAppearance ta, long seed) {
|
||||
this.ta = ta;
|
||||
this.seed = seed;
|
||||
}
|
||||
protected static class TintingTransformer implements RenderContext.QuadTransform {
|
||||
private final CamoAppearance appearance;
|
||||
private final int model_id;
|
||||
private final int tint;
|
||||
|
||||
protected final CamoAppearance ta;
|
||||
|
||||
@Override
|
||||
public boolean transform(MutableQuadView quad) {
|
||||
quad.material(ta.getRenderMaterial(ao));
|
||||
|
||||
int tag = quad.tag();
|
||||
if(tag == 0) return true; //Pass the quad through unmodified.
|
||||
|
||||
//The quad tag numbers were selected so this magic trick works:
|
||||
Direction direction = quad.nominalFace();
|
||||
quad.spriteBake(ta.getSprite(direction, seed), MutableQuadView.BAKE_NORMALIZED | ta.getBakeFlags(direction, seed) | (uvlock ? MutableQuadView.BAKE_LOCK_UV : 0));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected class TintingTransformer implements RenderContext.QuadTransform {
|
||||
private final long seed;
|
||||
protected TintingTransformer(CamoAppearance ta, int tint, long seed) {
|
||||
this.ta = ta;
|
||||
protected TintingTransformer(CamoAppearance appearance, int model_id, int tint) {
|
||||
this.appearance = appearance;
|
||||
this.model_id = model_id;
|
||||
this.tint = tint;
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
protected final CamoAppearance ta;
|
||||
protected final int tint;
|
||||
|
||||
@Override
|
||||
public boolean transform(MutableQuadView quad) {
|
||||
int tag = quad.tag();
|
||||
if(tag == 0) return true;
|
||||
int camo_quad_index = quad.tag() - ((quad.tag() >>> 8) << 8);
|
||||
if(camo_quad_index == 0) return true;
|
||||
|
||||
if(ta.hasColor(quad.nominalFace(), seed)) quad.color(tint, tint, tint, tint);
|
||||
if(appearance.hasColor(quad.nominalFace(), model_id, camo_quad_index)) quad.color(tint, tint, tint, tint);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
@@ -20,54 +19,31 @@ import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UnbakedAutoRetexturedModel implements UnbakedModel {
|
||||
public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel {
|
||||
|
||||
public UnbakedAutoRetexturedModel(Identifier parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
protected final Identifier parent;
|
||||
protected BlockState itemModelState = Blocks.AIR.getDefaultState();
|
||||
protected boolean ao = true;
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return Collections.singletonList(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> function) {
|
||||
function.apply(parent).setParents(function);
|
||||
super(parent);
|
||||
item_state = Blocks.AIR.getDefaultState();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings bake_settings, Identifier identifier) {
|
||||
return new RetexturingBakedModel(
|
||||
baker.bake(parent, modelBakeSettings),
|
||||
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
|
||||
modelBakeSettings,
|
||||
itemModelState,
|
||||
baker.bake(parent, bake_settings),
|
||||
ReFramedClient.HELPER.getCamoApperanceManager(texture_getter),
|
||||
theme_index,
|
||||
bake_settings,
|
||||
item_state,
|
||||
ao
|
||||
) {
|
||||
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
protected Mesh getBaseMesh(BlockState state) {
|
||||
//Convert models to retexturable Meshes lazily, the first time we encounter each blockstate
|
||||
return jsonToMesh.computeIfAbsent(state, this::convertModel);
|
||||
}
|
||||
|
||||
private Mesh convertModel(BlockState state) {
|
||||
protected Mesh convertModel(BlockState state) {
|
||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||
MeshBuilder builder = r.meshBuilder();
|
||||
QuadEmitter emitter = builder.getEmitter();
|
||||
RenderMaterial mat = tam.getCachedMaterial(state, false);
|
||||
RenderMaterial mat = appearance_manager.getCachedMaterial(state, false);
|
||||
|
||||
Random rand = Random.create(42);
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.util.Identifier;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UnbakedDoubleRetexturedModel implements UnbakedModel {
|
||||
|
||||
protected final UnbakedModel model_1;
|
||||
protected final UnbakedModel model_2;
|
||||
|
||||
public UnbakedDoubleRetexturedModel(UnbakedRetexturedModel model_1, UnbakedRetexturedModel model_2) {
|
||||
this.model_1 = model_1;
|
||||
this.model_2 = model_2;
|
||||
model_2.setThemeIndex(2);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return List.of(((List<Identifier>) model_1.getModelDependencies()).get(0), ((List<Identifier>) model_2.getModelDependencies()).get(0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> function) {
|
||||
model_1.setParents(function);
|
||||
model_2.setParents(function);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings model_bake_settings, Identifier identifier) {
|
||||
return new DoubleRetexturingBakedModel(
|
||||
(ForwardingBakedModel) model_1.bake(baker, texture_getter, model_bake_settings, identifier),
|
||||
(ForwardingBakedModel) model_2.bake(baker, texture_getter, model_bake_settings, identifier)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,10 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.*;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.render.model.Baker;
|
||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.screen.PlayerScreenHandler;
|
||||
@@ -17,65 +20,38 @@ import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class UnbakedJsonRetexturedModel implements UnbakedModel {
|
||||
public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
|
||||
public UnbakedJsonRetexturedModel(Identifier parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
protected final Identifier parent;
|
||||
protected BlockState itemModelState;
|
||||
protected boolean ao = true;
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return Collections.singletonList(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> function) {
|
||||
function.apply(parent).setParents(function);
|
||||
super(parent);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
||||
Direction[] DIRECTIONS = RetexturingBakedModel.DIRECTIONS;
|
||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings bake_settings, Identifier identifier) {
|
||||
Direction[] directions = Direction.values();
|
||||
|
||||
Sprite[] specialSprites = new Sprite[DIRECTIONS.length];
|
||||
for(int i = 0; i < DIRECTIONS.length; i++) {
|
||||
SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + DIRECTIONS[i].getName()));
|
||||
specialSprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
|
||||
Sprite[] sprites = new Sprite[directions.length];
|
||||
for(int i = 0; i < directions.length; i++) {
|
||||
SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + directions[i].getName()));
|
||||
sprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
|
||||
}
|
||||
|
||||
BakedModel model = baker.bake(parent, modelBakeSettings);
|
||||
|
||||
return new RetexturingBakedModel(
|
||||
model,
|
||||
baker.bake(parent, bake_settings),
|
||||
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
|
||||
modelBakeSettings,
|
||||
itemModelState,
|
||||
theme_index,
|
||||
bake_settings,
|
||||
item_state,
|
||||
ao
|
||||
) {
|
||||
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
protected Mesh getBaseMesh(BlockState state) {
|
||||
//Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate
|
||||
return jsonToMesh.computeIfAbsent(state, this::convertModel);
|
||||
}
|
||||
|
||||
private Mesh convertModel(BlockState state) {
|
||||
protected Mesh convertModel(BlockState state) {
|
||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||
MeshBuilder builder = r.meshBuilder();
|
||||
QuadEmitter emitter = builder.getEmitter();
|
||||
RenderMaterial mat = tam.getCachedMaterial(state, false);
|
||||
RenderMaterial mat = appearance_manager.getCachedMaterial(state, false);
|
||||
|
||||
Random rand = Random.create(42);
|
||||
|
||||
@@ -84,9 +60,9 @@ public class UnbakedJsonRetexturedModel implements UnbakedModel {
|
||||
emitter.fromVanilla(quad, mat, cullFace);
|
||||
|
||||
QuadUvBounds bounds = QuadUvBounds.read(emitter);
|
||||
for(int i = 0; i < specialSprites.length; i++) {
|
||||
if(bounds.displaysSprite(specialSprites[i])) {
|
||||
bounds.normalizeUv(emitter, specialSprites[i]);
|
||||
for(int i = 0; i < sprites.length; i++) {
|
||||
if(bounds.displaysSprite(sprites[i])) {
|
||||
bounds.normalizeUv(emitter, sprites[i]);
|
||||
emitter.tag(i + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package fr.adrien1106.reframed.client.model;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.UnbakedModel;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class UnbakedRetexturedModel implements UnbakedModel {
|
||||
|
||||
protected final Identifier parent;
|
||||
|
||||
protected int theme_index = 1;
|
||||
protected BlockState item_state;
|
||||
protected final boolean ao = true;
|
||||
|
||||
public UnbakedRetexturedModel(Identifier parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public void setThemeIndex(int theme_index) {
|
||||
this.theme_index = theme_index;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<Identifier> getModelDependencies() {
|
||||
return List.of(parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setParents(Function<Identifier, UnbakedModel> function) {
|
||||
function.apply(parent).setParents(function);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package fr.adrien1106.reframed.client.model.apperance;
|
||||
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.Direction;
|
||||
|
||||
public record Appearance(Sprite[] sprites, int[] flags, byte color_mask) {}
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public record Appearance(Map<Direction, List<SpriteProperties>> sprites) {}
|
||||
|
||||
@@ -1,13 +1,80 @@
|
||||
package fr.adrien1106.reframed.client.model.apperance;
|
||||
|
||||
import fr.adrien1106.reframed.client.model.QuadPosBounds;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface CamoAppearance {
|
||||
@NotNull RenderMaterial getRenderMaterial(boolean ao);
|
||||
@NotNull Sprite getSprite(Direction dir, long seed);
|
||||
int getBakeFlags(Direction dir, long seed);
|
||||
boolean hasColor(Direction dir, long seed);
|
||||
import java.util.List;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public abstract class CamoAppearance {
|
||||
protected final int id;
|
||||
protected final RenderMaterial ao_material;
|
||||
protected final RenderMaterial material;
|
||||
|
||||
protected CamoAppearance(RenderMaterial ao_material, RenderMaterial material, int id) {
|
||||
this.id = id;
|
||||
|
||||
this.ao_material = ao_material;
|
||||
this.material = material;
|
||||
}
|
||||
|
||||
public abstract @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id);
|
||||
public abstract boolean hasColor(Direction dir, int model_id, int index);
|
||||
|
||||
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
||||
return ao && ao_material != null? ao_material : material;
|
||||
}
|
||||
|
||||
public int transformQuad(QuadEmitter quad, int i, int quad_index, int model_id, boolean ao, boolean uv_lock) {
|
||||
if(quad.tag() == 0) return 0; // Pass the quad through unmodified.
|
||||
|
||||
Direction direction = quad.nominalFace();
|
||||
List<SpriteProperties> sprites = getSprites(direction, model_id);
|
||||
if (i == -1) i = sprites.size();
|
||||
|
||||
SpriteProperties properties = sprites.get(sprites.size() - i);
|
||||
int tag = i + (quad_index << 8);
|
||||
i--;
|
||||
QuadPosBounds bounds = properties.bounds();
|
||||
|
||||
if (bounds == null) { // sprite applies anywhere e.g. default behaviour
|
||||
quad.material(getRenderMaterial(ao));
|
||||
quad.spriteBake(
|
||||
properties.sprite(),
|
||||
MutableQuadView.BAKE_NORMALIZED
|
||||
| properties.flags()
|
||||
| (uv_lock ? MutableQuadView.BAKE_LOCK_UV : 0)
|
||||
);
|
||||
quad.tag(tag);
|
||||
quad.emit();
|
||||
return i;
|
||||
}
|
||||
|
||||
// verify if sprite covers the current quad and apply the new size
|
||||
QuadPosBounds origin_bounds = QuadPosBounds.read(quad, false);
|
||||
if (!bounds.matches(origin_bounds)) return i;
|
||||
|
||||
// apply new quad shape
|
||||
quad.material(getRenderMaterial(ao));
|
||||
bounds.intersection(origin_bounds, direction.getAxis()).apply(quad, origin_bounds);
|
||||
quad.spriteBake( // seems to work without the flags and break with it
|
||||
properties.sprite(),
|
||||
MutableQuadView.BAKE_NORMALIZED
|
||||
| MutableQuadView.BAKE_LOCK_UV
|
||||
);
|
||||
quad.tag(tag);
|
||||
quad.emit();
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
package fr.adrien1106.reframed.client.model.apperance;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||
import fr.adrien1106.reframed.client.model.DynamicBakedModel;
|
||||
import fr.adrien1106.reframed.client.model.QuadPosBounds;
|
||||
import fr.adrien1106.reframed.compat.RebakedModel;
|
||||
import fr.adrien1106.reframed.mixin.model.WeightedBakedModelAccessor;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
|
||||
@@ -21,59 +28,89 @@ import net.minecraft.client.util.SpriteIdentifier;
|
||||
import net.minecraft.screen.PlayerScreenHandler;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.collection.Weighted;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class CamoAppearanceManager {
|
||||
|
||||
|
||||
protected static final SpriteIdentifier DEFAULT_SPRITE_MAIN = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block"));
|
||||
protected static final SpriteIdentifier DEFAULT_SPRITE_SECONDARY = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_accent_block"));
|
||||
private static final SpriteIdentifier BARRIER_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:item/barrier"));
|
||||
private static final Cache<BlockState, CamoAppearance> APPEARANCE_CACHE = CacheBuilder.newBuilder().maximumSize(2048).build();
|
||||
|
||||
public CamoAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
|
||||
MaterialFinder finder = ReFramedClient.HELPER.getFabricRenderer().materialFinder();
|
||||
for(BlendMode blend : BlendMode.values()) {
|
||||
finder.clear().disableDiffuse(false).blendMode(blend);
|
||||
|
||||
materialsWithoutAo.put(blend, finder.ambientOcclusion(TriState.FALSE).find());
|
||||
materialsWithAo.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO
|
||||
materials.put(blend, finder.ambientOcclusion(TriState.FALSE).find());
|
||||
ao_materials.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO
|
||||
}
|
||||
|
||||
Sprite defaultSprite = spriteLookup.apply(DEFAULT_SPRITE_ID);
|
||||
if(defaultSprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_ID + " !");
|
||||
this.defaultAppearance = new SingleSpriteAppearance(defaultSprite, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement());
|
||||
Sprite sprite = spriteLookup.apply(DEFAULT_SPRITE_MAIN);
|
||||
if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
|
||||
this.default_appearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
|
||||
|
||||
Sprite barrier = spriteLookup.apply(BARRIER_SPRITE_ID);
|
||||
if(barrier == null) barrier = defaultSprite; //eh
|
||||
this.barrierItemAppearance = new SingleSpriteAppearance(barrier, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement());
|
||||
sprite = spriteLookup.apply(DEFAULT_SPRITE_SECONDARY);
|
||||
if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
|
||||
this.accent_appearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
|
||||
|
||||
sprite = spriteLookup.apply(BARRIER_SPRITE_ID);
|
||||
this.barrierItemAppearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
|
||||
}
|
||||
|
||||
protected static final SpriteIdentifier DEFAULT_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block"));
|
||||
private static final SpriteIdentifier BARRIER_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:item/barrier"));
|
||||
|
||||
private final CamoAppearance defaultAppearance;
|
||||
private final CamoAppearance default_appearance;
|
||||
private final CamoAppearance accent_appearance;
|
||||
private final CamoAppearance barrierItemAppearance;
|
||||
|
||||
private final ConcurrentHashMap<BlockState, CamoAppearance> appearanceCache = new ConcurrentHashMap<>(); //Mutable, append-only cache
|
||||
private final AtomicInteger serialNumber = new AtomicInteger(0); //Mutable
|
||||
private final AtomicInteger serial_number = new AtomicInteger(0); //Mutable
|
||||
|
||||
private final EnumMap<BlendMode, RenderMaterial> materialsWithAo = new EnumMap<>(BlendMode.class);
|
||||
private final EnumMap<BlendMode, RenderMaterial> materialsWithoutAo = new EnumMap<>(BlendMode.class); //Immutable contents
|
||||
private final EnumMap<BlendMode, RenderMaterial> ao_materials = new EnumMap<>(BlendMode.class);
|
||||
private final EnumMap<BlendMode, RenderMaterial> materials = new EnumMap<>(BlendMode.class); //Immutable contents
|
||||
|
||||
public CamoAppearance getDefaultAppearance() {
|
||||
return defaultAppearance;
|
||||
public static void dumpCahe() {
|
||||
APPEARANCE_CACHE.invalidateAll();
|
||||
}
|
||||
|
||||
public CamoAppearance getCamoAppearance(BlockState state) {
|
||||
return appearanceCache.computeIfAbsent(state, this::computeAppearance);
|
||||
public CamoAppearance getDefaultAppearance(int appearance) {
|
||||
return appearance == 2 ? accent_appearance: default_appearance;
|
||||
}
|
||||
|
||||
public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index, boolean item) {
|
||||
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
|
||||
|
||||
// add support for connected textures that uses dynamic baking
|
||||
if (model instanceof DynamicBakedModel dynamic_model) {
|
||||
// cache items as they get rendered more often
|
||||
if (item && APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state);
|
||||
|
||||
model = dynamic_model.computeQuads(world, state, pos, theme_index);
|
||||
// if model isn't rebaked its just wrapped (i.e. not dynamic and may be cached)
|
||||
if (model instanceof RebakedModel) {
|
||||
CamoAppearance appearance = computeAppearance(model, state);
|
||||
if (item) APPEARANCE_CACHE.put(state, appearance);
|
||||
return appearance;
|
||||
}
|
||||
}
|
||||
|
||||
// refresh cache
|
||||
if (APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state);
|
||||
|
||||
CamoAppearance appearance = computeAppearance(model, state);
|
||||
APPEARANCE_CACHE.put(state, appearance);
|
||||
return appearance;
|
||||
}
|
||||
|
||||
public RenderMaterial getCachedMaterial(BlockState state, boolean ao) {
|
||||
Map<BlendMode, RenderMaterial> m = ao ? materialsWithAo : materialsWithoutAo;
|
||||
Map<BlendMode, RenderMaterial> m = ao ? ao_materials : materials;
|
||||
return m.get(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state)));
|
||||
}
|
||||
|
||||
@@ -81,16 +118,15 @@ public class CamoAppearanceManager {
|
||||
// The computeIfAbsent map update will work without corrupting the map, but there will be some "wasted effort" computing the value twice.
|
||||
// The results are going to be the same, apart from their serialNumbers differing (= their equals & hashCode differing).
|
||||
// Tiny amount of wasted space in some caches if CamoAppearances are used as a map key, then. IMO it's not a critical issue.
|
||||
private CamoAppearance computeAppearance(BlockState state) {
|
||||
private CamoAppearance computeAppearance(BakedModel model, BlockState state) {
|
||||
if(state.getBlock() == Blocks.BARRIER) return barrierItemAppearance;
|
||||
|
||||
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
|
||||
if (!(model instanceof WeightedBakedModelAccessor weighted_model)) {
|
||||
return new ComputedAppearance(
|
||||
getAppearance(model),
|
||||
getCachedMaterial(state, true),
|
||||
getCachedMaterial(state, false),
|
||||
serialNumber.getAndIncrement()
|
||||
serial_number.getAndIncrement()
|
||||
);
|
||||
}
|
||||
List<Weighted.Present<Appearance>> appearances = weighted_model.getModels().stream()
|
||||
@@ -101,7 +137,7 @@ public class CamoAppearanceManager {
|
||||
appearances,
|
||||
getCachedMaterial(state, true),
|
||||
getCachedMaterial(state, false),
|
||||
serialNumber.getAndIncrement()
|
||||
serial_number.getAndIncrement()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -112,42 +148,34 @@ public class CamoAppearanceManager {
|
||||
RenderMaterial material = r.materialFinder().clear().find();
|
||||
Random random = Random.create();
|
||||
|
||||
Sprite[] sprites = new Sprite[6];
|
||||
int[] flags = new int[6];
|
||||
byte[] color_mask = {0b000000};
|
||||
Map<Direction, List<SpriteProperties>> sprites = new EnumMap<>(Direction.class);
|
||||
|
||||
//Read quads off the model by their `cullface`
|
||||
Arrays.stream(Direction.values()).forEach(direction -> {
|
||||
List<BakedQuad> quads = model.getQuads(null, direction, random);
|
||||
if(quads.isEmpty() || quads.get(0) == null) {
|
||||
sprites[direction.ordinal()] = defaultAppearance.getSprite(direction, 0); // make sure direction has a sprite
|
||||
if(quads.isEmpty()) { // add default appearance if none present
|
||||
sprites.put(direction, default_appearance.getSprites(direction, 0));
|
||||
return;
|
||||
}
|
||||
|
||||
BakedQuad quad = quads.get(0);
|
||||
if(quad.hasColor()) color_mask[0] |= (byte) (1 << direction.ordinal());
|
||||
|
||||
Sprite sprite = quad.getSprite();
|
||||
if(sprite == null) return;
|
||||
sprites[direction.ordinal()] = sprite;
|
||||
|
||||
//Problem: Some models (eg. pistons, stone, glazed terracotta) have their UV coordinates permuted in
|
||||
//non-standard ways. The actual png image appears sideways, but the original model's UVs rotate it right way up again.
|
||||
//If I simply display the texture on my model, it will appear sideways, like it does in the png.
|
||||
//If I can discover the pattern of rotations and flips on the original model, I can "un-rotate" the texture back
|
||||
//into a standard orientation.
|
||||
//
|
||||
//Solution: Look at the first and second vertices of the orignial quad, and decide whether their UV coordinates
|
||||
//are "low" or "high" compared to the middle of the sprite. The first two vertices uniquely determine the pattern
|
||||
//of the other two (since UV coordinates are unique and shouldn't cross). There are 16 possibilities so this information
|
||||
//is easily summarized in a bitfield, and the correct fabric rendering API "bake flags" to un-transform the sprite
|
||||
//are looked up with a simple table.
|
||||
quad_emitter.fromVanilla(quad, material, direction);
|
||||
|
||||
flags[direction.ordinal()] = getBakeFlags(quad_emitter, sprite);
|
||||
sprites.put(direction, new ArrayList<>());
|
||||
quads.forEach(quad -> {
|
||||
Sprite sprite = quad.getSprite();
|
||||
if(sprite == null) return;
|
||||
sprites.computeIfPresent(direction, (dir, pairs) -> {
|
||||
quad_emitter.fromVanilla(quad, material, direction);
|
||||
pairs.add(new SpriteProperties(
|
||||
sprite,
|
||||
getBakeFlags(quad_emitter, sprite),
|
||||
QuadPosBounds.read(quad_emitter),
|
||||
quad.hasColor())
|
||||
);
|
||||
return pairs;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return new Appearance(sprites, flags, color_mask[0]);
|
||||
return new Appearance(sprites);
|
||||
}
|
||||
|
||||
private static int getBakeFlags(QuadEmitter emitter, Sprite sprite) {
|
||||
|
||||
@@ -1,62 +1,38 @@
|
||||
package fr.adrien1106.reframed.client.model.apperance;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ComputedAppearance implements CamoAppearance {
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class ComputedAppearance extends CamoAppearance {
|
||||
private final Appearance appearance;
|
||||
private final int id;
|
||||
private final RenderMaterial matWithAo;
|
||||
private final RenderMaterial matWithoutAo;
|
||||
|
||||
public ComputedAppearance(@NotNull Appearance appearance, RenderMaterial withAo, RenderMaterial withoutAo, int id) {
|
||||
public ComputedAppearance(@NotNull Appearance appearance, RenderMaterial ao_material, RenderMaterial material, int id) {
|
||||
super(ao_material, material, id);
|
||||
this.appearance = appearance;
|
||||
this.id = id;
|
||||
|
||||
this.matWithAo = withAo;
|
||||
this.matWithoutAo = withoutAo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
||||
return ao ? matWithAo : matWithoutAo;
|
||||
public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
|
||||
return appearance.sprites().get(dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Sprite getSprite(Direction dir, long seed) {
|
||||
return appearance.sprites()[dir.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBakeFlags(Direction dir, long seed) {
|
||||
return appearance.flags()[dir.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasColor(Direction dir, long seed) {
|
||||
return (appearance.color_mask() & (1 << dir.ordinal())) != 0;
|
||||
public boolean hasColor(Direction dir, int model_id, int index) {
|
||||
List<SpriteProperties> properties = getSprites(dir, model_id);
|
||||
if (index != 0) index = properties.size() - index;
|
||||
return properties.get(index).has_colors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(this == o) return true;
|
||||
if(o == null || getClass() != o.getClass()) return false;
|
||||
ComputedAppearance that = (ComputedAppearance) o;
|
||||
if(!(o instanceof ComputedAppearance that)) return false;
|
||||
return id == that.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ComputedApperance{sprites=%s, bakeFlags=%s, hasColorMask=%s, matWithoutAo=%s, matWithAo=%s, id=%d}"
|
||||
.formatted(Arrays.toString(appearance.sprites()), Arrays.toString(appearance.flags()), appearance.color_mask(), matWithoutAo, matWithAo, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +1,42 @@
|
||||
package fr.adrien1106.reframed.client.model.apperance;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SingleSpriteAppearance implements CamoAppearance {
|
||||
import java.util.List;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class SingleSpriteAppearance extends CamoAppearance {
|
||||
private final @NotNull Sprite defaultSprite;
|
||||
private final RenderMaterial mat;
|
||||
private final int id;
|
||||
|
||||
public SingleSpriteAppearance(@NotNull Sprite defaultSprite, RenderMaterial mat, int id) {
|
||||
super(null, mat, id);
|
||||
this.defaultSprite = defaultSprite;
|
||||
this.mat = mat;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
||||
return mat;
|
||||
public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
|
||||
return List.of(new SpriteProperties(defaultSprite, 0, null, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Sprite getSprite(Direction dir, long seed) {
|
||||
return defaultSprite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBakeFlags(Direction dir, long seed) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasColor(Direction dir, long seed) {
|
||||
public boolean hasColor(Direction dir, int model_id, int index) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(this == o) return true;
|
||||
if(o == null || getClass() != o.getClass()) return false;
|
||||
SingleSpriteAppearance that = (SingleSpriteAppearance) o;
|
||||
if(!(o instanceof SingleSpriteAppearance that)) return false;
|
||||
return id == that.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "SingleSpriteAppearance[defaultSprite=%s, mat=%s, id=%d]".formatted(defaultSprite, mat, id);
|
||||
return "SingleSpriteAppearance[defaultSprite=%s, mat=%s, id=%d]".formatted(defaultSprite, material, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package fr.adrien1106.reframed.client.model.apperance;
|
||||
|
||||
import fr.adrien1106.reframed.client.model.QuadPosBounds;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
|
||||
public record SpriteProperties(Sprite sprite, int flags, QuadPosBounds bounds, boolean has_colors) {
|
||||
}
|
||||
@@ -1,35 +1,25 @@
|
||||
package fr.adrien1106.reframed.client.model.apperance;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.collection.Weighted;
|
||||
import net.minecraft.util.collection.Weighting;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class WeightedComputedAppearance implements CamoAppearance {
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class WeightedComputedAppearance extends CamoAppearance {
|
||||
private final List<Weighted.Present<Appearance>> appearances;
|
||||
private final int total_weight;
|
||||
private final int id;
|
||||
private final RenderMaterial matWithAo;
|
||||
private final RenderMaterial matWithoutAo;
|
||||
|
||||
public WeightedComputedAppearance(@NotNull List<Weighted.Present<Appearance>> appearances, RenderMaterial withAo, RenderMaterial withoutAo, int id) {
|
||||
public WeightedComputedAppearance(@NotNull List<Weighted.Present<Appearance>> appearances, RenderMaterial ao_material, RenderMaterial material, int id) {
|
||||
super(ao_material, material, id);
|
||||
this.appearances = appearances;
|
||||
this.total_weight = Weighting.getWeightSum(appearances);
|
||||
this.id = id;
|
||||
|
||||
this.matWithAo = withAo;
|
||||
this.matWithoutAo = withoutAo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RenderMaterial getRenderMaterial(boolean ao) {
|
||||
return ao ? matWithAo : matWithoutAo;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,44 +29,27 @@ public class WeightedComputedAppearance implements CamoAppearance {
|
||||
.map(appearances::indexOf).orElse(0);
|
||||
}
|
||||
|
||||
private Appearance getAppearance(long seed) {
|
||||
Random random = Random.create(seed);
|
||||
return Weighting.getAt(appearances, Math.abs((int)random.nextLong()) % total_weight)
|
||||
.map(Weighted.Present::getData).get();
|
||||
private Appearance getAppearance(int model_id) {
|
||||
return appearances.get(model_id).getData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Sprite getSprite(Direction dir, long seed) {
|
||||
return getAppearance(seed).sprites()[dir.ordinal()];
|
||||
public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
|
||||
return getAppearance(model_id).sprites().get(dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBakeFlags(Direction dir, long seed) {
|
||||
return getAppearance(seed).flags()[dir.ordinal()];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasColor(Direction dir, long seed) {
|
||||
return (getAppearance(seed).color_mask() & (1 << dir.ordinal())) != 0;
|
||||
public boolean hasColor(Direction dir, int model_id, int index) {
|
||||
List<SpriteProperties> properties = getSprites(dir, model_id);
|
||||
if (index != 0) index = properties.size() - index;
|
||||
return properties.get(index).has_colors();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(this == o) return true;
|
||||
if(o == null || getClass() != o.getClass()) return false;
|
||||
WeightedComputedAppearance that = (WeightedComputedAppearance) o;
|
||||
if(!(o instanceof WeightedComputedAppearance that)) return false;
|
||||
return id == that.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Appearance appearance = appearances.get(0).getData();
|
||||
return "ComputedApperance{sprites=%s, bakeFlags=%s, hasColorMask=%s, matWithoutAo=%s, matWithAo=%s, id=%d}"
|
||||
.formatted(Arrays.toString(appearance.sprites()), Arrays.toString(appearance.flags()), appearance.color_mask(), matWithoutAo, matWithAo, id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,177 @@
|
||||
package fr.adrien1106.reframed.client.util;
|
||||
|
||||
import com.google.common.cache.Cache;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||
import fr.adrien1106.reframed.client.model.QuadPosBounds;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.function.BooleanBiFunction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import net.minecraft.world.BlockView;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static net.minecraft.util.shape.VoxelShapes.combine;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class RenderHelper {
|
||||
|
||||
|
||||
// self culling cache of the models not made thread local so that it is only computed once
|
||||
private static final Cache<CullElement, Integer[]> INNER_CULL_MAP = CacheBuilder.newBuilder().maximumSize(1024).build();
|
||||
private record CullElement(Block block, Object state_key, int model) {}
|
||||
|
||||
/**
|
||||
* compute which quad might cull with another model quad
|
||||
* @param state - the state of the model
|
||||
* @param models - list of models on the same block
|
||||
*/
|
||||
public static void computeInnerCull(BlockState state, List<ForwardingBakedModel> models) {
|
||||
if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return;
|
||||
Object key = frame_block.getModelCacheKey(state);
|
||||
if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, key, 1))) return;
|
||||
|
||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||
QuadEmitter quad_emitter = r.meshBuilder().getEmitter();
|
||||
RenderMaterial material = r.materialFinder().clear().find();
|
||||
Random random = Random.create();
|
||||
|
||||
List<List<QuadPosBounds>> model_bounds = models.stream()
|
||||
.map(ForwardingBakedModel::getWrappedModel)
|
||||
.filter(Objects::nonNull)
|
||||
.map(wrapped -> wrapped.getQuads(state, null, random))
|
||||
.map(quads -> quads.stream().map(quad -> {
|
||||
quad_emitter.fromVanilla(quad, material, null);
|
||||
return QuadPosBounds.read(quad_emitter, false);
|
||||
}).toList()).toList();
|
||||
|
||||
Integer[] cull_array;
|
||||
for(int self_id = 1; self_id <= model_bounds.size(); self_id++) {
|
||||
List<QuadPosBounds> self_bounds = model_bounds.get(self_id - 1);
|
||||
cull_array = new Integer[self_bounds.size()];
|
||||
for (int self_quad = 0; self_quad < cull_array.length; self_quad++) {
|
||||
QuadPosBounds self_bound = self_bounds.get(self_quad);
|
||||
for(int other_id = 1; other_id <= model_bounds.size(); other_id++) {
|
||||
if (other_id == self_id) continue;
|
||||
if (model_bounds.get(other_id - 1).stream().anyMatch(other_bound -> other_bound.equals(self_bound))) {
|
||||
cull_array[self_quad] = other_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
INNER_CULL_MAP.put(new CullElement(frame_block, key, self_id), cull_array);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean shouldDrawInnerFace(BlockState state, BlockRenderView view, BlockPos pos, int quad_index, int theme_index) {
|
||||
if ( !(state.getBlock() instanceof ReFramedBlock frame_block)
|
||||
|| !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)
|
||||
) return true;
|
||||
CullElement key = new CullElement(frame_block, frame_block.getModelCacheKey(state), theme_index);
|
||||
if (!INNER_CULL_MAP.asMap().containsKey(key)) return true;
|
||||
|
||||
// needs to be Integer object because array is initialized with null not 0
|
||||
Integer cull_theme = Objects.requireNonNull(INNER_CULL_MAP.getIfPresent(key))[quad_index];
|
||||
if (cull_theme == null) return true; // no culling possible
|
||||
|
||||
BlockState self_theme = frame_entity.getTheme(theme_index);
|
||||
BlockState other_theme = frame_entity.getTheme(cull_theme);
|
||||
|
||||
if (self_theme.isSideInvisible(other_theme, null)) return false;
|
||||
return !self_theme.isOpaque() || !other_theme.isOpaque();
|
||||
}
|
||||
|
||||
// Doing this method from scratch as it is simpler to do than injecting everywhere
|
||||
public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) {
|
||||
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null;
|
||||
ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e ? e : null;
|
||||
BlockState other_state = world.getBlockState(other_pos);
|
||||
|
||||
// normal behaviour
|
||||
if (self == null && other == null) return Block.shouldDrawSide(self_state, world, pos, side, other_pos);
|
||||
|
||||
// self is a normal Block
|
||||
if (self == null && other_state.getBlock() instanceof ReFramedBlock other_block) {
|
||||
VoxelShape self_shape = self_state.getCullingShape(world, pos);
|
||||
if (self_shape.isEmpty()) return true;
|
||||
|
||||
int i = 0;
|
||||
VoxelShape other_shape = VoxelShapes.empty();
|
||||
for (BlockState s: other.getThemes()) {
|
||||
i++;
|
||||
if (self_state.isSideInvisible(s, side) || s.isOpaque())
|
||||
other_shape = combine(
|
||||
other_shape,
|
||||
other_block
|
||||
.getShape(other_state, i)
|
||||
.getFace(side.getOpposite()),
|
||||
BooleanBiFunction.OR
|
||||
);
|
||||
}
|
||||
|
||||
// determine if side needs to be rendered
|
||||
return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST);
|
||||
}
|
||||
|
||||
BlockState self_theme = self.getTheme(theme_index);
|
||||
// other is normal Block
|
||||
if (other == null && self_state.getBlock() instanceof ReFramedBlock self_block) {
|
||||
// Transparent is simple if self and the neighbor are invisible don't render side (like default)
|
||||
if (self_theme.isSideInvisible(other_state, side)) return false;
|
||||
|
||||
// Opaque is also simple as each model are rendered one by one
|
||||
if (other_state.isOpaque()) {
|
||||
// no cache section :( because it differs between each instance of the frame
|
||||
VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side);
|
||||
if (self_shape.isEmpty()) return true;
|
||||
VoxelShape other_shape = other_state.getCullingFace(world, other_pos, side.getOpposite());
|
||||
|
||||
// determine if side needs to be rendered
|
||||
return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Both are frames
|
||||
// here both are computed in the same zone as there will necessarily a shape comparison
|
||||
if (self_state.getBlock() instanceof ReFramedBlock self_block && other_state.getBlock() instanceof ReFramedBlock other_block) {
|
||||
VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side);
|
||||
if (self_shape.isEmpty()) return true;
|
||||
|
||||
int i = 0;
|
||||
VoxelShape other_shape = VoxelShapes.empty();
|
||||
for (BlockState s: other.getThemes()) {
|
||||
i++;
|
||||
if (self_theme.isSideInvisible(s, side) || s.isOpaque())
|
||||
other_shape = combine(
|
||||
other_shape,
|
||||
other_block
|
||||
.getShape(other_state, i)
|
||||
.getFace(side.getOpposite()),
|
||||
BooleanBiFunction.OR
|
||||
);
|
||||
}
|
||||
|
||||
// determine if side needs to be rendered
|
||||
return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package fr.adrien1106.reframed.compat;
|
||||
|
||||
import me.pepperbell.continuity.client.model.QuadProcessors;
|
||||
import me.pepperbell.continuity.impl.client.ProcessingContextImpl;
|
||||
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public interface ICTMQuadTransform extends RenderContext.QuadTransform {
|
||||
|
||||
void invokePrepare(BlockRenderView view, BlockState state, BlockPos pos, Supplier<Random> random, boolean manual_culling, Function<Sprite, QuadProcessors.Slice> slice);
|
||||
|
||||
ProcessingContextImpl getProcessingContext();
|
||||
|
||||
void invokeReset();
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package fr.adrien1106.reframed.compat;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.render.model.json.ModelOverrideList;
|
||||
import net.minecraft.client.render.model.json.ModelTransformation;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class RebakedModel implements BakedModel {
|
||||
protected final Map<Direction, List<BakedQuad>> face_quads;
|
||||
|
||||
public RebakedModel(Map<Direction, List<BakedQuad>> face_quads) {
|
||||
this.face_quads = face_quads;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<BakedQuad> getQuads(BlockState state, Direction direction, Random random) {
|
||||
return face_quads.getOrDefault(direction, List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useAmbientOcclusion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasDepth() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSideLit() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBuiltin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Sprite getParticleSprite() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelTransformation getTransformation() {
|
||||
return ModelTransformation.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelOverrideList getOverrides() {
|
||||
return ModelOverrideList.EMPTY;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import net.minecraft.data.client.BlockStateSupplier;
|
||||
|
||||
public interface BlockStateProvider {
|
||||
BlockStateSupplier getMultipart();
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricAdvancementProvider;
|
||||
import net.minecraft.advancement.Advancement;
|
||||
import net.minecraft.advancement.AdvancementEntry;
|
||||
import net.minecraft.advancement.AdvancementFrame;
|
||||
import net.minecraft.advancement.AdvancementRewards;
|
||||
import net.minecraft.advancement.criterion.InventoryChangedCriterion;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class GAdvancement extends FabricAdvancementProvider {
|
||||
protected GAdvancement(FabricDataOutput output) {
|
||||
super(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateAdvancement(Consumer<AdvancementEntry> consumer) {
|
||||
Advancement.Builder builder = Advancement.Builder.create()
|
||||
.display(
|
||||
Items.CAKE,
|
||||
Text.literal("Is Everything A Lie ?"),
|
||||
Text.translatable("advancements.reframed.description"),
|
||||
new Identifier("textures/gui/advancements/backgrounds/adventure.png"),
|
||||
AdvancementFrame.TASK,
|
||||
true,
|
||||
true,
|
||||
false
|
||||
).rewards(AdvancementRewards.Builder.experience(1000));
|
||||
ReFramed.BLOCKS.forEach(block ->
|
||||
builder.criterion(
|
||||
"get_" + Registries.BLOCK.getId(block).getPath(),
|
||||
InventoryChangedCriterion.Conditions.items(block)
|
||||
)
|
||||
);
|
||||
builder.build(consumer, ReFramed.MODID + "/root");
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
|
||||
import net.minecraft.registry.Registries;
|
||||
@@ -12,6 +13,6 @@ public class GBlockLoot extends FabricBlockLootTableProvider {
|
||||
|
||||
@Override
|
||||
public void generate() {
|
||||
Generator.BLOCKS.forEach(block -> addDrop(block, Registries.ITEM.get(Registries.BLOCK.getId(block))));
|
||||
ReFramed.BLOCKS.forEach(block -> addDrop(block, Registries.ITEM.get(Registries.BLOCK.getId(block))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider.BlockTagProvider;
|
||||
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
|
||||
import net.minecraft.registry.tag.BlockTags;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class GBlockTag extends BlockTagProvider {
|
||||
|
||||
public GBlockTag(FabricDataOutput output, CompletableFuture<WrapperLookup> registries) {
|
||||
super(output, registries);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configure(WrapperLookup arg) {
|
||||
FabricTagBuilder builder = getOrCreateTagBuilder(BlockTags.AXE_MINEABLE);
|
||||
ReFramed.BLOCKS.forEach(builder::add);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
|
||||
import net.minecraft.data.client.*;
|
||||
@@ -8,6 +9,8 @@ import net.minecraft.util.Identifier;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import static net.minecraft.data.client.VariantSettings.Rotation.R0;
|
||||
|
||||
public class GBlockstate extends FabricModelProvider {
|
||||
|
||||
public GBlockstate(FabricDataOutput output) {
|
||||
@@ -16,22 +19,34 @@ public class GBlockstate extends FabricModelProvider {
|
||||
|
||||
@Override
|
||||
public void generateBlockStateModels(BlockStateModelGenerator model_generator) {
|
||||
Generator.BLOCKS
|
||||
ReFramed.BLOCKS
|
||||
.forEach(model_generator::excludeFromSimpleItemModelGeneration);
|
||||
Generator.BLOCKS.stream()
|
||||
.map(block -> block instanceof MultipartBlockStateProvider multipart_block ? multipart_block.getMultipart(): null)
|
||||
ReFramed.BLOCKS.stream()
|
||||
.map(block -> {
|
||||
if (block instanceof BlockStateProvider multipart_block) return multipart_block.getMultipart();
|
||||
return VariantsBlockStateSupplier.create(
|
||||
block,
|
||||
GBlockstate.variant(
|
||||
ReFramed.id("cube_special"),
|
||||
true,
|
||||
R0, R0
|
||||
)
|
||||
);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(model_generator.blockStateCollector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateItemModels(ItemModelGenerator itemModelGenerator) {}
|
||||
public void generateItemModels(ItemModelGenerator model_generator) {
|
||||
ReFramed.ITEMS.forEach(item -> model_generator.register(item, Models.GENERATED));
|
||||
}
|
||||
|
||||
public static BlockStateVariant variant(Identifier model, boolean uv_lock, VariantSettings.Rotation x, VariantSettings.Rotation y) {
|
||||
BlockStateVariant variant = BlockStateVariant.create().put(VariantSettings.MODEL, model);
|
||||
if (uv_lock) variant.put(VariantSettings.UVLOCK, uv_lock);
|
||||
if (!x.equals(VariantSettings.Rotation.R0)) variant.put(VariantSettings.X, x);
|
||||
if (!y.equals(VariantSettings.Rotation.R0)) variant.put(VariantSettings.Y, y);
|
||||
if (!x.equals(R0)) variant.put(VariantSettings.X, x);
|
||||
if (!y.equals(R0)) variant.put(VariantSettings.Y, y);
|
||||
return variant;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricLanguageProvider;
|
||||
import net.minecraft.registry.Registries;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class GLanguage extends FabricLanguageProvider {
|
||||
protected GLanguage(FabricDataOutput dataOutput) {
|
||||
super(dataOutput, "en_us");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generateTranslations(TranslationBuilder builder) {
|
||||
builder.add(Registries.ITEM_GROUP.getKey(ReFramed.ITEM_GROUP).get(), "Frames");
|
||||
builder.add("advancements.reframed.description", "Get all the frame types.");
|
||||
ReFramed.BLOCKS.forEach(block -> builder.add(block, beautify(Registries.BLOCK.getId(block).getPath()) + " Frame"));
|
||||
ReFramed.ITEMS.forEach(block -> builder.add(block, beautify(Registries.ITEM.getId(block).getPath())));
|
||||
}
|
||||
|
||||
private static String beautify(String name) {
|
||||
return Arrays.stream(name.split("_"))
|
||||
.map(word -> word.substring(0, 1).toUpperCase() + word.substring(1))
|
||||
.collect(Collectors.joining(" "));
|
||||
}
|
||||
}
|
||||
22
src/main/java/fr/adrien1106/reframed/generator/GRecipe.java
Normal file
22
src/main/java/fr/adrien1106/reframed/generator/GRecipe.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
|
||||
public class GRecipe extends FabricRecipeProvider {
|
||||
public GRecipe(FabricDataOutput output) {
|
||||
super(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(RecipeExporter exporter) {
|
||||
ReFramed.BLOCKS.forEach(block -> {
|
||||
if (block instanceof RecipeSetter provider) provider.setRecipe(exporter);
|
||||
});
|
||||
ReFramed.ITEMS.forEach(item -> {
|
||||
if (item instanceof RecipeSetter provider) provider.setRecipe(exporter);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -2,22 +2,17 @@ package fr.adrien1106.reframed.generator;
|
||||
|
||||
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
|
||||
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
|
||||
import net.minecraft.block.Block;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static fr.adrien1106.reframed.ReFramed.*;
|
||||
|
||||
public class Generator implements DataGeneratorEntrypoint {
|
||||
|
||||
/**
|
||||
* missing DOOR, IRON_DOOR, CANDLE
|
||||
*/
|
||||
public static List<Block> BLOCKS = List.of(CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, TRAPDOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE);
|
||||
@Override
|
||||
public void onInitializeDataGenerator(FabricDataGenerator data_generator) {
|
||||
FabricDataGenerator.Pack myPack = data_generator.createPack();
|
||||
myPack.addProvider(GBlockLoot::new);
|
||||
myPack.addProvider(GBlockstate::new);
|
||||
FabricDataGenerator.Pack my_pack = data_generator.createPack();
|
||||
my_pack.addProvider(GBlockLoot::new);
|
||||
my_pack.addProvider(GBlockstate::new);
|
||||
my_pack.addProvider(GLanguage::new);
|
||||
my_pack.addProvider(GBlockTag::new);
|
||||
my_pack.addProvider(GRecipe::new);
|
||||
my_pack.addProvider(GAdvancement::new);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||
|
||||
public interface MultipartBlockStateProvider {
|
||||
MultipartBlockStateSupplier getMultipart();
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package fr.adrien1106.reframed.generator;
|
||||
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
|
||||
public interface RecipeSetter {
|
||||
|
||||
void setRecipe(RecipeExporter exporter);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package fr.adrien1106.reframed.item;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||
import fr.adrien1106.reframed.generator.RecipeSetter;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUsageContext;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ReFramedBlueprintItem extends Item implements RecipeSetter {
|
||||
public ReFramedBlueprintItem(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult useOnBlock(ItemUsageContext context) {
|
||||
BlockPos pos = context.getBlockPos();
|
||||
World world = context.getWorld();
|
||||
if (!(world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity)
|
||||
|| frame_entity.getThemes().stream().noneMatch(state -> state.getBlock() != Blocks.AIR)
|
||||
) return ActionResult.PASS;
|
||||
|
||||
context.getStack().decrement(1);
|
||||
ItemStack stack = ReFramed.BLUEPRINT_WRITTEN.getDefaultStack();
|
||||
frame_entity.setStackNbt(stack);
|
||||
context.getPlayer().giveItemStack(stack);
|
||||
world.playSound(context.getPlayer(), context.getPlayer().getBlockPos(), SoundEvents.ITEM_BOOK_PUT, SoundCategory.PLAYERS);
|
||||
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
ShapedRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this, 3)
|
||||
.pattern("PI")
|
||||
.pattern("PP")
|
||||
.input('P', Items.PAPER)
|
||||
.input('I', Items.INK_SAC)
|
||||
.criterion(FabricRecipeProvider.hasItem(Items.PAPER), FabricRecipeProvider.conditionsFromItem(Items.PAPER))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package fr.adrien1106.reframed.item;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.client.item.TooltipContext;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUsageContext;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtHelper;
|
||||
import net.minecraft.registry.Registries;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.TypedActionResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
|
||||
|
||||
public class ReFramedBlueprintWrittenItem extends Item {
|
||||
public ReFramedBlueprintWrittenItem(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) {
|
||||
ItemStack stack = player.getStackInHand(hand);
|
||||
if (!player.isSneaking() || !stack.hasNbt()) return super.use(world, player, hand);
|
||||
stack.decrement(1);
|
||||
player.giveItemStack(ReFramed.BLUEPRINT.getDefaultStack());
|
||||
world.playSound(player, player.getBlockPos(), SoundEvents.ITEM_BOOK_PUT, SoundCategory.PLAYERS);
|
||||
|
||||
return TypedActionResult.success(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult useOnBlock(ItemUsageContext context) {
|
||||
BlockPos pos = context.getBlockPos();
|
||||
World world = context.getWorld();
|
||||
if (!(world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity)
|
||||
|| frame_entity.getThemes().stream().anyMatch(state -> state.getBlock() != Blocks.AIR)
|
||||
|| !context.getStack().hasNbt()
|
||||
) return ActionResult.PASS;
|
||||
|
||||
NbtCompound tag = BlockItem.getBlockEntityNbt(context.getStack());
|
||||
if(tag == null) return ActionResult.FAIL;
|
||||
|
||||
PlayerEntity player = context.getPlayer();
|
||||
if (!player.isCreative()) { // verify player has blocks and remove them
|
||||
PlayerInventory inventory = player.getInventory();
|
||||
List<ItemStack> stacks = getBlockStates(tag).values().stream()
|
||||
.map(AbstractBlock.AbstractBlockState::getBlock)
|
||||
.map(Block::asItem)
|
||||
.map(Item::getDefaultStack)
|
||||
.toList();
|
||||
if (stacks.stream().anyMatch(stack -> !inventory.contains(stack)))
|
||||
return ActionResult.FAIL;
|
||||
stacks.stream().map(inventory::getSlotWithStack).forEach(index -> inventory.removeStack(index, 1));
|
||||
player.playSound(SoundEvents.ENTITY_ITEM_PICKUP, 0.5f, 0.5f);
|
||||
}
|
||||
frame_entity.readNbt(tag);
|
||||
world.playSound(player, player.getBlockPos(), SoundEvents.ITEM_BOOK_PAGE_TURN, SoundCategory.PLAYERS);
|
||||
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> tooltip, TooltipContext context) {
|
||||
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
|
||||
if(tag == null) return;
|
||||
|
||||
Map<Integer, BlockState> states = getBlockStates(tag);
|
||||
states.forEach((index, state) -> tooltip.add(
|
||||
Text.literal("Theme " + index + ": ")
|
||||
.append(
|
||||
Text.translatable(state.getBlock().getTranslationKey())
|
||||
.formatted(Formatting.GRAY)
|
||||
)
|
||||
));
|
||||
super.appendTooltip(stack, world, tooltip, context);
|
||||
}
|
||||
|
||||
private static Map<Integer, BlockState> getBlockStates(NbtCompound tag) {
|
||||
return tag.getKeys().stream()
|
||||
.filter(key ->
|
||||
key.startsWith(BLOCKSTATE_KEY)
|
||||
&& key.replace(BLOCKSTATE_KEY,"").chars().allMatch(Character::isDigit)
|
||||
)
|
||||
.collect(Collectors.toMap(
|
||||
key -> Integer.parseInt(key.substring(BLOCKSTATE_KEY.length())),
|
||||
key -> NbtHelper.toBlockState(
|
||||
Registries.BLOCK.getReadOnlyWrapper(),
|
||||
tag.getCompound(key)
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package fr.adrien1106.reframed.item;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.block.ReFramedDoubleBlock;
|
||||
import fr.adrien1106.reframed.generator.RecipeSetter;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemUsageContext;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ReFramedHammerItem extends Item implements RecipeSetter {
|
||||
public ReFramedHammerItem(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult useOnBlock(ItemUsageContext context) {
|
||||
World world = context.getWorld();
|
||||
BlockPos pos = context.getBlockPos();
|
||||
if (!(world.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)) return ActionResult.PASS;
|
||||
BlockState state = world.getBlockState(pos);
|
||||
PlayerEntity player = context.getPlayer();
|
||||
int theme_index = state.getBlock() instanceof ReFramedDoubleBlock b
|
||||
? b.getHitShape(
|
||||
state,
|
||||
context.getHitPos(),
|
||||
context.getBlockPos(),
|
||||
context.getSide()
|
||||
)
|
||||
: 1;
|
||||
|
||||
if (frame_entity.getTheme(theme_index).getBlock() == Blocks.AIR) return ActionResult.PASS;
|
||||
|
||||
if (!player.isCreative()) {
|
||||
player.giveItemStack(new ItemStack(frame_entity.getTheme(theme_index).getBlock()));
|
||||
world.playSound(player, pos, SoundEvents.ENTITY_ITEM_PICKUP, SoundCategory.BLOCKS, 1f, 1.1f);
|
||||
}
|
||||
frame_entity.setTheme(Blocks.AIR.getDefaultState(), theme_index);
|
||||
ReFramed.chunkRerenderProxy.accept(world, pos);
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
ShapedRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.pattern(" CI")
|
||||
.pattern(" ~C")
|
||||
.pattern("~ ")
|
||||
.input('I', Items.IRON_INGOT)
|
||||
.input('C', ReFramed.CUBE)
|
||||
.input('~', Items.STICK)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package fr.adrien1106.reframed.item;
|
||||
|
||||
import fr.adrien1106.reframed.ReFramed;
|
||||
import fr.adrien1106.reframed.block.ReFramedDoubleBlock;
|
||||
import fr.adrien1106.reframed.generator.RecipeSetter;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.data.server.recipe.RecipeExporter;
|
||||
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemUsageContext;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.recipe.book.RecipeCategory;
|
||||
import net.minecraft.sound.BlockSoundGroup;
|
||||
import net.minecraft.sound.SoundCategory;
|
||||
import net.minecraft.state.property.Properties;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ReFramedScrewdriverItem extends Item implements RecipeSetter {
|
||||
|
||||
public ReFramedScrewdriverItem(Settings settings) {
|
||||
super(settings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult useOnBlock(ItemUsageContext context) {
|
||||
World world = context.getWorld();
|
||||
BlockPos pos = context.getBlockPos();
|
||||
if (!(world.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)) return ActionResult.PASS;
|
||||
BlockState state = world.getBlockState(pos);
|
||||
PlayerEntity player = context.getPlayer();
|
||||
int theme_index = state.getBlock() instanceof ReFramedDoubleBlock b
|
||||
? b.getHitShape(
|
||||
state,
|
||||
context.getHitPos(),
|
||||
context.getBlockPos(),
|
||||
context.getSide()
|
||||
)
|
||||
: 1;
|
||||
|
||||
|
||||
BlockState theme = frame_entity.getTheme(theme_index);
|
||||
if (!theme.contains(Properties.AXIS)) return ActionResult.PASS;
|
||||
|
||||
Direction.Axis axis = theme.get(Properties.AXIS);
|
||||
BlockSoundGroup group = theme.getSoundGroup();
|
||||
world.playSound(player, pos, group.getPlaceSound(), SoundCategory.BLOCKS, group.getVolume(), group.getPitch());
|
||||
frame_entity.setTheme(theme.with(
|
||||
Properties.AXIS,
|
||||
switch (axis) {
|
||||
case X -> Direction.Axis.Y;
|
||||
case Y -> Direction.Axis.Z;
|
||||
case Z -> Direction.Axis.X;
|
||||
}
|
||||
), theme_index);
|
||||
ReFramed.chunkRerenderProxy.accept(world, pos);
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRecipe(RecipeExporter exporter) {
|
||||
ShapedRecipeJsonBuilder
|
||||
.create(RecipeCategory.BUILDING_BLOCKS, this)
|
||||
.pattern(" I")
|
||||
.pattern(" I ")
|
||||
.pattern("C ")
|
||||
.input('I', Items.IRON_INGOT)
|
||||
.input('C', ReFramed.CUBE)
|
||||
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
|
||||
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
|
||||
.offerTo(exporter);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package fr.adrien1106.reframed.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockEntityProvider;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtHelper;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
|
||||
|
||||
@Mixin(BlockItem.class)
|
||||
public class BlockItemMixin {
|
||||
|
||||
@Inject(
|
||||
method = "writeNbtToBlockEntity",
|
||||
at = @At(
|
||||
value = "INVOKE_ASSIGN",
|
||||
target = "Lnet/minecraft/item/BlockItem;getBlockEntityNbt(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/nbt/NbtCompound;",
|
||||
shift = At.Shift.AFTER
|
||||
)
|
||||
)
|
||||
private static void placeBlockWithOffHandCamo(World world, PlayerEntity player, BlockPos pos, ItemStack stack, CallbackInfoReturnable<Boolean> cir, @Local LocalRef<NbtCompound> compound) {
|
||||
if (compound.get() != null
|
||||
|| player.getOffHandStack().isEmpty()
|
||||
|| !(player.getOffHandStack().getItem() instanceof BlockItem block)
|
||||
|| block.getBlock() instanceof BlockEntityProvider
|
||||
|| !Block.isShapeFullCube(block.getBlock().getDefaultState().getCollisionShape(world, pos))
|
||||
) return;
|
||||
NbtCompound new_comp = new NbtCompound();
|
||||
player.getOffHandStack().decrement(1);
|
||||
new_comp.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(block.getBlock().getDefaultState()));
|
||||
compound.set(new_comp);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package fr.adrien1106.reframed.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.util.function.BooleanBiFunction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.shape.VoxelShape;
|
||||
import net.minecraft.util.shape.VoxelShapes;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.*;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(Block.class)
|
||||
public class BlockMixin {
|
||||
|
||||
@Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isOpaque()Z"))
|
||||
private static boolean isNeighborCamoOpaque(BlockState state, @Local(argsOnly = true) BlockView world, @Local(ordinal = 1, argsOnly = true) BlockPos pos) {
|
||||
BlockEntity block_entity = world.getBlockEntity(pos);
|
||||
if (!(block_entity instanceof ReFramedEntity frame_entity)) return state.isOpaque();
|
||||
return frame_entity.getThemeState().isOpaque();
|
||||
}
|
||||
|
||||
@Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isSideInvisible(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/Direction;)Z"))
|
||||
private static boolean isCamoInvisible(BlockState state, BlockState other_state, Direction direction, @Local(argsOnly = true) BlockView world, @Local(ordinal = 0, argsOnly = true) BlockPos pos, @Local(ordinal = 1, argsOnly = true) BlockPos other_pos) {
|
||||
if (world.getBlockEntity(other_pos) instanceof ReFramedEntity entity) other_state = entity.getThemeState();
|
||||
if (world.getBlockEntity(pos) instanceof ReFramedEntity entity) state = entity.getThemeState();
|
||||
return state.isSideInvisible(other_state, direction);
|
||||
}
|
||||
|
||||
@Inject(method = "shouldDrawSide", at = @At(value = "RETURN", ordinal = 0), cancellable = true)
|
||||
private static void shouldDrawGlassCamoSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, CallbackInfoReturnable<Boolean> cir, @Local(ordinal = 1) BlockState neighbor) {
|
||||
if (!(world.getBlockEntity(pos) instanceof ReFramedEntity) && !(world.getBlockEntity(other_pos) instanceof ReFramedEntity)) return;
|
||||
VoxelShape voxel_block = state.getCullingFace(world, pos, side);
|
||||
if (voxel_block.isEmpty()) {
|
||||
cir.setReturnValue(true);
|
||||
return;
|
||||
}
|
||||
VoxelShape voxel_neighbor = neighbor.getCullingFace(world, pos, side.getOpposite());
|
||||
cir.setReturnValue(VoxelShapes.matchesAnywhere(voxel_block, voxel_neighbor, BooleanBiFunction.ONLY_FIRST));
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
package fr.adrien1106.reframed.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(BlockRenderInfo.class)
|
||||
public abstract class BlockRenderInfoMixin {
|
||||
|
||||
@Shadow public BlockPos blockPos;
|
||||
|
||||
@Shadow public BlockState blockState;
|
||||
|
||||
@Shadow public BlockRenderView blockView;
|
||||
|
||||
@Shadow @Final private BlockPos.Mutable searchPos;
|
||||
|
||||
@ModifyArg(method = "prepareForBlock",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
|
||||
"getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;"))
|
||||
public BlockState prepareCamoLayer(BlockState state, @Local(argsOnly = true) BlockPos pos) {
|
||||
BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(pos);
|
||||
if (!(block_entity instanceof ReFramedEntity frame_entity)) return state;
|
||||
return frame_entity.getThemeState();
|
||||
}
|
||||
|
||||
@Inject(method = "shouldDrawFace",
|
||||
at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/util/math/Direction;getId()I"),
|
||||
cancellable = true)
|
||||
private void shouldDrawCamoFace(Direction face, CallbackInfoReturnable<Boolean> cir) {
|
||||
BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(blockPos);
|
||||
if (!(block_entity instanceof ReFramedEntity)) return;
|
||||
cir.setReturnValue(Block.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package fr.adrien1106.reframed.mixin;
|
||||
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import org.objectweb.asm.tree.ClassNode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
|
||||
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class CompatMixinPlugin implements IMixinConfigPlugin {
|
||||
|
||||
private static final FabricLoader LOADER = FabricLoader.getInstance();
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("ReFramed MIXIN");
|
||||
private static final List<String> COMPAT_MOD = List.of("athena", "indium", "sodium", "special-model-loader", "continuity");
|
||||
private static final Map<String, Supplier<Boolean>> CONDITIONS = new HashMap<>();
|
||||
static {
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.render.AbstractBlockRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.IndiumAbstractBlockRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityConnectionPredicateMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityCTMQuadTransformMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
|
||||
CONDITIONS.put("fr.adrien1106.reframed.mixin.compat.ContinuityModelWrappingHandlerMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(4)));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onLoad(String mixin_package) {}
|
||||
|
||||
@Override
|
||||
public String getRefMapperConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldApplyMixin(String target_class, String mixin_class) {
|
||||
return CONDITIONS.getOrDefault(mixin_class, () -> true).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptTargets(Set<String> mine, Set<String> others) {}
|
||||
|
||||
@Override
|
||||
public List<String> getMixins() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void preApply(String target_class_name, ClassNode target_class, String mixin_class_name, IMixinInfo mixin_info) {}
|
||||
|
||||
@Override
|
||||
public void postApply(String target_class, ClassNode target, String mixin_class, IMixinInfo mixin_info) {
|
||||
String mixin_class_name = mixin_class.substring(mixin_class.lastIndexOf('.') + 1);
|
||||
COMPAT_MOD.forEach(mod -> {
|
||||
if (mixin_class_name.toLowerCase().startsWith(mod))
|
||||
LOGGER.info("Loaded compatibility mixin class for mod \"" + mod + "\" (class: " + target_class + ")");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import earth.terrarium.athena.api.client.fabric.AthenaBakedModel;
|
||||
import earth.terrarium.athena.api.client.fabric.WrappedGetter;
|
||||
import earth.terrarium.athena.api.client.models.AthenaBlockModel;
|
||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||
import fr.adrien1106.reframed.client.model.DynamicBakedModel;
|
||||
import fr.adrien1106.reframed.compat.RebakedModel;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Mixin(AthenaBakedModel.class)
|
||||
public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedModel {
|
||||
|
||||
@Shadow(remap = false) @Final private AthenaBlockModel model;
|
||||
|
||||
@Shadow(remap = false) @Final private Int2ObjectMap<Sprite> textures;
|
||||
|
||||
/**
|
||||
* Reuses the emitQuad method to compute the quads to be used by the frame
|
||||
*
|
||||
* @param level - the world
|
||||
* @param origin_state - the current block camo
|
||||
* @param pos - the block position
|
||||
* @return - the rebakedmodel containing the computed quads
|
||||
*/
|
||||
@Override
|
||||
public BakedModel computeQuads(@Nullable BlockRenderView level, BlockState origin_state, @Nullable BlockPos pos, int theme_index) {
|
||||
Map<Direction, List<BakedQuad>> face_quads = new HashMap<>();
|
||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||
QuadEmitter emitter = r.meshBuilder().getEmitter();
|
||||
|
||||
WrappedGetter getter = new WrappedGetter(level);
|
||||
BlockState state = level != null && pos != null
|
||||
&& level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity
|
||||
? framed_entity.getTheme(theme_index)
|
||||
: origin_state;
|
||||
Arrays.stream(Direction.values()).forEach(direction -> {
|
||||
face_quads.put(direction, new ArrayList<>());
|
||||
|
||||
(level == null || pos == null
|
||||
? model.getDefaultQuads(direction).get(direction)
|
||||
: model.getQuads(getter, state, pos, direction)
|
||||
).forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> {
|
||||
Sprite texture = textures.get(sprite.sprite());
|
||||
if (texture == null) return quads;
|
||||
emitter.square(direction, sprite.left(), sprite.bottom(), sprite.right(), sprite.top(), sprite.depth());
|
||||
|
||||
int flag = MutableQuadView.BAKE_LOCK_UV;
|
||||
|
||||
switch (sprite.rotation()) {
|
||||
case CLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_90;
|
||||
case CLOCKWISE_180 -> flag |= MutableQuadView.BAKE_ROTATE_180;
|
||||
case COUNTERCLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_270;
|
||||
}
|
||||
|
||||
emitter.spriteBake(texture, flag);
|
||||
emitter.color(-1, -1, -1, -1);
|
||||
quads.add(emitter.toBakedQuad(texture));
|
||||
return quads;
|
||||
}));
|
||||
});
|
||||
|
||||
return new RebakedModel(face_quads);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import earth.terrarium.athena.api.client.utils.AppearanceAndTintGetter;
|
||||
import earth.terrarium.athena.api.client.utils.CtmUtils;
|
||||
import earth.terrarium.athena.impl.client.models.ConnectedBlockModel;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(ConnectedBlockModel.class)
|
||||
public class AthenaConnectedBlockModelMixin {
|
||||
|
||||
@Redirect(method = "getQuads",
|
||||
at = @At(value = "INVOKE", target = "Learth/terrarium/athena/api/client/utils/CtmUtils;" +
|
||||
"checkRelative(Learth/terrarium/athena/api/client/utils/AppearanceAndTintGetter;" +
|
||||
"Lnet/minecraft/block/BlockState;" +
|
||||
"Lnet/minecraft/util/math/BlockPos;" +
|
||||
"Lnet/minecraft/util/math/Direction;)Z"))
|
||||
private boolean checkForCull(AppearanceAndTintGetter level, BlockState state, BlockPos pos, Direction direction) {
|
||||
// Always get all the textures unless its another block then use default behaviour
|
||||
if (level.getBlockEntity(pos) instanceof ThemeableBlockEntity
|
||||
|| level.getBlockEntity(pos.offset(direction)) instanceof ThemeableBlockEntity)
|
||||
return false;
|
||||
return CtmUtils.checkRelative(level, state, pos, direction);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import earth.terrarium.athena.api.client.fabric.WrappedGetter;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(WrappedGetter.class)
|
||||
public class AthenaWrappedGetterMixin {
|
||||
|
||||
@Redirect(
|
||||
method = "query",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/BlockRenderView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;"
|
||||
)
|
||||
) // TODO better connected textures
|
||||
private BlockState queryCamoState(BlockRenderView world, BlockPos pos, @Local(argsOnly = true) BlockState reference_state) {
|
||||
// get Any that will connect or return any other (/!\ isOf is an uncertain check)
|
||||
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)
|
||||
return framed_entity.getThemes()
|
||||
.stream()
|
||||
.filter(state -> reference_state.isOf(state.getBlock()))
|
||||
.findAny()
|
||||
.orElse(framed_entity.getTheme(1));
|
||||
return world.getBlockState(pos);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||
import fr.adrien1106.reframed.client.model.DynamicBakedModel;
|
||||
import fr.adrien1106.reframed.compat.ICTMQuadTransform;
|
||||
import fr.adrien1106.reframed.compat.RebakedModel;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import me.pepperbell.continuity.client.config.ContinuityConfig;
|
||||
import me.pepperbell.continuity.client.model.CTMBakedModel;
|
||||
import me.pepperbell.continuity.client.model.ModelObjectsContainer;
|
||||
import me.pepperbell.continuity.client.model.QuadProcessors;
|
||||
import me.pepperbell.continuity.client.util.RenderUtil;
|
||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import net.minecraft.client.render.model.BakedQuad;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(CTMBakedModel.class)
|
||||
public abstract class ContinuityCTMBakedModelMixin extends ForwardingBakedModel implements DynamicBakedModel {
|
||||
|
||||
@Shadow protected abstract Function<Sprite, QuadProcessors.Slice> getSliceFunc(BlockState state);
|
||||
|
||||
@Override
|
||||
public BakedModel computeQuads(@Nullable BlockRenderView level, BlockState origin_state, @Nullable BlockPos pos, int theme_index) {
|
||||
if (wrapped instanceof DynamicBakedModel wrapped_dynamic) // support wrap of dynamic models
|
||||
return wrapped_dynamic.computeQuads(level, origin_state, pos, theme_index);
|
||||
|
||||
|
||||
ModelObjectsContainer container = ModelObjectsContainer.get();
|
||||
// normally baked model / wrapped or feature disabled or item (i.e. no need to compute quads)
|
||||
if (level == null || pos == null
|
||||
|| !ContinuityConfig.INSTANCE.connectedTextures.get()
|
||||
|| !container.featureStates.getConnectedTexturesState().isEnabled()
|
||||
) return wrapped;
|
||||
|
||||
Map<Direction, List<BakedQuad>> face_quads = new HashMap<>();
|
||||
|
||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||
QuadEmitter emitter = r.meshBuilder().getEmitter();
|
||||
|
||||
// get applicable state
|
||||
BlockState state = level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity
|
||||
? framed_entity.getTheme(theme_index)
|
||||
: origin_state;
|
||||
|
||||
// get random supplier
|
||||
Random random = Random.create();
|
||||
Supplier<Random> random_supplier = () -> {
|
||||
random.setSeed(state.getRenderingSeed(pos));
|
||||
return random;
|
||||
};
|
||||
|
||||
// get quad transform and prepare
|
||||
ICTMQuadTransform transform = ((ICTMQuadTransform) container.ctmQuadTransform);
|
||||
transform.invokePrepare(
|
||||
level,
|
||||
state,
|
||||
pos,
|
||||
random_supplier,
|
||||
ContinuityConfig.INSTANCE.useManualCulling.get(),
|
||||
getSliceFunc(state)
|
||||
);
|
||||
Arrays.stream(Direction.values()).forEach(direction -> {
|
||||
face_quads.put(direction, new ArrayList<>());
|
||||
|
||||
wrapped.getQuads(state, direction, random_supplier.get()).forEach(quad -> face_quads.computeIfPresent(direction, (d, quads) -> {
|
||||
emitter.fromVanilla(quad, emitter.material(), direction);
|
||||
transform.transform(emitter);
|
||||
quads.add(emitter.toBakedQuad(RenderUtil.getSpriteFinder().find(emitter)));
|
||||
return quads;
|
||||
}));
|
||||
// transform.getProcessingContext().getExtraQuadEmitter(); // TODO start here for overlay support
|
||||
});
|
||||
|
||||
transform.getProcessingContext().reset(); // reset instead of outputting to emitter
|
||||
transform.invokeReset();
|
||||
|
||||
return new RebakedModel(face_quads);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import fr.adrien1106.reframed.compat.ICTMQuadTransform;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import me.pepperbell.continuity.client.model.CullingCache;
|
||||
import me.pepperbell.continuity.client.model.QuadProcessors;
|
||||
import me.pepperbell.continuity.impl.client.ProcessingContextImpl;
|
||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@Mixin(targets = "me.pepperbell.continuity.client.model.CTMBakedModel$CTMQuadTransform")
|
||||
public abstract class ContinuityCTMQuadTransformMixin implements ICTMQuadTransform {
|
||||
@Shadow(remap = false) @Final protected ProcessingContextImpl processingContext;
|
||||
|
||||
@Shadow public abstract void prepare(BlockRenderView view, BlockState state, BlockPos pos, Supplier<Random> random, boolean manual_culling, Function<Sprite, QuadProcessors.Slice> slice);
|
||||
@Shadow(remap = false) public abstract void reset();
|
||||
|
||||
@Redirect(
|
||||
method = "transform",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lme/pepperbell/continuity/client/model/CullingCache;shouldCull(Lnet/fabricmc/fabric/api/renderer/v1/mesh/QuadView;Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)Z"
|
||||
)
|
||||
)
|
||||
private boolean camo_replacement(CullingCache cache, QuadView quad, BlockRenderView view, BlockPos pos, BlockState state) {
|
||||
if (view.getBlockEntity(pos) instanceof ThemeableBlockEntity) return false;
|
||||
return cache.shouldCull(quad, view, pos, state);
|
||||
}
|
||||
|
||||
// uses this because invoker did not want to work for some reason
|
||||
public void invokePrepare(BlockRenderView view, BlockState state, BlockPos pos, Supplier<Random> random, boolean manual_culling, Function<Sprite, QuadProcessors.Slice> slice) {
|
||||
prepare(view, state, pos, random, manual_culling, slice);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProcessingContextImpl getProcessingContext() {
|
||||
return processingContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invokeReset() {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import me.pepperbell.continuity.client.processor.ConnectionPredicate;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(ConnectionPredicate.class)
|
||||
public interface ContinuityConnectionPredicateMixin {
|
||||
|
||||
@Redirect(
|
||||
method = "shouldConnect(Lnet/minecraft/world/BlockRenderView;Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/client/texture/Sprite;)Z",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/world/BlockRenderView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;"
|
||||
)
|
||||
) // TODO better connected textures
|
||||
private BlockState getBlockState(BlockRenderView view, BlockPos pos, @Local(argsOnly = true) BlockState state) {
|
||||
if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)) return view.getBlockState(pos);
|
||||
return frame_entity.getThemes()
|
||||
.stream()
|
||||
.filter(theme -> theme.getBlock() == state.getBlock())
|
||||
.findFirst()
|
||||
.orElse(frame_entity.getTheme(0));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
||||
import me.pepperbell.continuity.client.resource.ModelWrappingHandler;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.registry.DefaultedRegistry;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
@Mixin(ModelWrappingHandler.class)
|
||||
public class ContinuityModelWrappingHandlerMixin {
|
||||
|
||||
@Redirect(
|
||||
method = "createBlockStateModelIdMap",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/registry/DefaultedRegistry;iterator()Ljava/util/Iterator;"
|
||||
)
|
||||
)
|
||||
private static Iterator<Block> filterFrames(DefaultedRegistry<Block> registry) {
|
||||
return registry
|
||||
.stream()
|
||||
.filter(block -> !(block instanceof ReFramedBlock))
|
||||
.iterator();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||
import link.infra.indium.renderer.mesh.MutableQuadViewImpl;
|
||||
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
|
||||
import link.infra.indium.renderer.render.BlockRenderInfo;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(AbstractBlockRenderContext.class)
|
||||
public abstract class IndiumAbstractBlockRenderContextMixin {
|
||||
|
||||
@Shadow(remap = false) protected BlockRenderInfo blockInfo;
|
||||
|
||||
@Shadow public abstract boolean isFaceCulled(@Nullable Direction face);
|
||||
|
||||
@Redirect(method = "renderQuad", at = @At(value = "INVOKE", target = "Llink/infra/indium/renderer/render/AbstractBlockRenderContext;isFaceCulled(Lnet/minecraft/util/math/Direction;)Z"))
|
||||
private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) {
|
||||
if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face);
|
||||
|
||||
return !RenderHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import link.infra.indium.renderer.render.BlockRenderInfo;
|
||||
import link.infra.indium.renderer.render.TerrainBlockRenderInfo;
|
||||
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(TerrainBlockRenderInfo.class)
|
||||
public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin {
|
||||
|
||||
@Unique private int theme_index = 0;
|
||||
|
||||
@Redirect(
|
||||
method = "shouldDrawFaceInner",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lme/jellysquid/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Z"
|
||||
)
|
||||
)
|
||||
private boolean shouldDrawCamoSide(BlockOcclusionCache instance, BlockState state, BlockView view, BlockPos pos, Direction face) {
|
||||
BlockPos other_pos = pos.offset(face);
|
||||
if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity
|
||||
|| view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity))
|
||||
return instance.shouldDrawSide(state, view, pos, face);
|
||||
return RenderHelper.shouldDrawSide(state, view, pos, face, other_pos, theme_index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo, int theme_index) {
|
||||
this.theme_index = theme_index;
|
||||
prepareForBlock(blockState, blockPos, seed, modelAo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThemeIndex() {
|
||||
return theme_index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
|
||||
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||
import fr.adrien1106.reframed.util.mixin.IMultipartBakedModelMixin;
|
||||
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
|
||||
import link.infra.indium.renderer.render.TerrainRenderContext;
|
||||
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockRenderContext;
|
||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||
import net.minecraft.client.render.model.BakedModel;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@Mixin(TerrainRenderContext.class)
|
||||
public abstract class IndiumTerrainRenderContextMixin extends AbstractBlockRenderContext {
|
||||
|
||||
@Inject(
|
||||
method = "tessellateBlock",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Llink/infra/indium/renderer/aocalc/AoCalculator;clear()V"
|
||||
), remap = false,
|
||||
cancellable = true)
|
||||
private void renderMultipleModels(BlockRenderContext ctx, CallbackInfo ci) {
|
||||
if (!(ctx.model() instanceof IMultipartBakedModelMixin wrapped)
|
||||
|| !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return;
|
||||
|
||||
List<ForwardingBakedModel> models = retexturing_model.models();
|
||||
RenderHelper.computeInnerCull(ctx.state(), models);
|
||||
int i = 0;
|
||||
for (BakedModel model : models) {
|
||||
i++;
|
||||
aoCalc.clear();
|
||||
((IBlockRenderInfoMixin) blockInfo).prepareForBlock(ctx.state(), ctx.pos(), ctx.seed(), model.useAmbientOcclusion(), i);
|
||||
model.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
|
||||
}
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package fr.adrien1106.reframed.mixin.compat;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
@Mixin(BlockOcclusionCache.class)
|
||||
public class SodiumBlockOcclusionCacheMixin {
|
||||
|
||||
@Inject(
|
||||
method = "shouldDrawSide",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/util/math/BlockPos$Mutable;set(III)Lnet/minecraft/util/math/BlockPos$Mutable;",
|
||||
shift = At.Shift.AFTER
|
||||
), cancellable = true)
|
||||
private void shouldDrawFrameNeighborSide(BlockState self_state, BlockView view, BlockPos self_pos, Direction face, CallbackInfoReturnable<Boolean> cir, @Local BlockPos.Mutable other_pos) {
|
||||
if (!(view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity)) return;
|
||||
cir.setReturnValue(RenderHelper.shouldDrawSide(self_state, view, self_pos, face, other_pos, 0));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package fr.adrien1106.reframed.mixin.particles;
|
||||
|
||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.client.particle.BlockDustParticle;
|
||||
@@ -20,8 +21,11 @@ public class MixinBlockDustParticle {
|
||||
)
|
||||
void modifyParticleSprite(ClientWorld clientWorld, double d, double e, double f, double g, double h, double i, BlockState state, BlockPos pos, CallbackInfo ci) {
|
||||
AccessorParticle a = (AccessorParticle) this;
|
||||
if(a.getRandom().nextBoolean() && clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable) {
|
||||
BlockState theme = themeable.getThemeState();
|
||||
if(a.getRandom().nextBoolean()
|
||||
&& clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable
|
||||
&& state.getBlock() instanceof ReFramedBlock block
|
||||
) {
|
||||
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
|
||||
if(theme == null || theme.isAir()) return;
|
||||
|
||||
Sprite replacement = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(theme);
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
package fr.adrien1106.reframed.mixin.particles;
|
||||
|
||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class MixinEntity {
|
||||
@Shadow @Deprecated public abstract BlockPos getLandingPos(); //TODO, somewhat expensive method
|
||||
|
||||
@ModifyArg(
|
||||
method = "spawnSprintingParticles",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;<init>(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V")
|
||||
)
|
||||
private BlockState modifyParticleState(BlockState origState) {
|
||||
private BlockState modifyParticleState(BlockState state, @Local(ordinal = 0) BlockPos landing_pos) {
|
||||
World world = ((Entity) (Object) this).getWorld();
|
||||
|
||||
if(world.getBlockEntity(getLandingPos()) instanceof ThemeableBlockEntity themeable) {
|
||||
BlockState theme = themeable.getThemeState();
|
||||
if(!theme.isAir()) return theme;
|
||||
if(world.getBlockEntity(landing_pos) instanceof ThemeableBlockEntity themeable
|
||||
&& state.getBlock() instanceof ReFramedBlock block) {
|
||||
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
|
||||
if(!theme.isAir()) return theme;
|
||||
}
|
||||
|
||||
return origState;
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +1,33 @@
|
||||
package fr.adrien1106.reframed.mixin.particles;
|
||||
|
||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(LivingEntity.class)
|
||||
public class MixinLivingEntity {
|
||||
@Unique private BlockPos lastFallCheckPos;
|
||||
|
||||
@Inject(method = "fall", at = @At("HEAD"))
|
||||
private void onFall(double d, boolean bl, BlockState blockState, BlockPos blockPos, CallbackInfo ci) {
|
||||
lastFallCheckPos = blockPos;
|
||||
}
|
||||
|
||||
@ModifyArg(
|
||||
method = "fall",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;<init>(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V")
|
||||
)
|
||||
private BlockState modifyParticleState(BlockState origState) {
|
||||
private BlockState modifyParticleState(BlockState state, @Local(ordinal = 0, argsOnly = true) BlockPos land_pos) {
|
||||
World world = ((Entity) (Object) this).getWorld();
|
||||
|
||||
if(lastFallCheckPos != null && world.getBlockEntity(lastFallCheckPos) instanceof ThemeableBlockEntity themeable) {
|
||||
BlockState theme = themeable.getThemeState();
|
||||
if(!theme.isAir()) return theme;
|
||||
if(world.getBlockEntity(land_pos) instanceof ThemeableBlockEntity themeable
|
||||
&& state.getBlock() instanceof ReFramedBlock block) {
|
||||
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
|
||||
if(!theme.isAir()) return theme;
|
||||
}
|
||||
|
||||
return origState;
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package fr.adrien1106.reframed.mixin.render;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.mesh.MutableQuadViewImpl;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(AbstractBlockRenderContext.class)
|
||||
public abstract class AbstractBlockRenderContextMixin {
|
||||
|
||||
@Shadow public abstract boolean isFaceCulled(@Nullable Direction face);
|
||||
|
||||
@Shadow(remap = false) @Final protected BlockRenderInfo blockInfo;
|
||||
|
||||
@Redirect(
|
||||
method = "renderQuad",
|
||||
at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/fabricmc/fabric/impl/client/indigo/renderer/render/AbstractBlockRenderContext;isFaceCulled(Lnet/minecraft/util/math/Direction;)Z"
|
||||
)
|
||||
)
|
||||
private boolean shouldDrawInnerQuad(AbstractBlockRenderContext instance, Direction face, @Local(argsOnly = true) MutableQuadViewImpl quad) {
|
||||
if (face != null || quad.tag() == 0 || !(blockInfo instanceof IBlockRenderInfoMixin info) || info.getThemeIndex() == 0) return isFaceCulled(face);
|
||||
|
||||
return !RenderHelper.shouldDrawInnerFace(blockInfo.blockState, blockInfo.blockView, blockInfo.blockPos, quad.tag() >>> 8, info.getThemeIndex());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package fr.adrien1106.reframed.mixin.render;
|
||||
|
||||
import com.llamalad7.mixinextras.sugar.Local;
|
||||
import fr.adrien1106.reframed.client.util.RenderHelper;
|
||||
import fr.adrien1106.reframed.util.mixin.IBlockRenderInfoMixin;
|
||||
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
|
||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||
|
||||
@Mixin(BlockRenderInfo.class)
|
||||
public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
|
||||
|
||||
@Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo);
|
||||
|
||||
@Shadow public BlockRenderView blockView;
|
||||
@Unique private int theme_index = 0;
|
||||
|
||||
@ModifyArg(method = "prepareForBlock",
|
||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
|
||||
"getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;"))
|
||||
public BlockState prepareCamoLayer(BlockState state, @Local(argsOnly = true) BlockPos pos) {
|
||||
BlockEntity block_entity = blockView.getBlockEntity(pos);
|
||||
if (!(block_entity instanceof ThemeableBlockEntity frame_entity)) return state;
|
||||
return frame_entity.getTheme(theme_index);
|
||||
}
|
||||
|
||||
@Redirect(method = "shouldDrawFace", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/BlockPos;)Z"))
|
||||
private boolean shouldDrawAdjacentCamoSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos) {
|
||||
return RenderHelper.shouldDrawSide(state, world, pos, side, other_pos, theme_index);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Unique
|
||||
public void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index) {
|
||||
this.theme_index = theme_index;
|
||||
prepareForBlock(state, pos, ao);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getThemeIndex() {
|
||||
return theme_index;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user