111 Commits
v1.2 ... master

Author SHA1 Message Date
c80d3c4167 Merge pull request 'Further visual fixes' (#26) from dev into master
All checks were successful
deploy / deploy (push) Successful in 4m59s
Reviewed-on: #26
2025-05-14 18:21:15 +02:00
fd26f5da4a feat: new version for publish 2025-05-14 18:15:00 +02:00
dbcb79f992 improved: removed unnecessary check + inner cull improvement 2025-05-14 18:13:35 +02:00
8d8a0e3654 fix: broken inner collision due to bad model 2025-05-14 18:13:18 +02:00
5c6fbff52e fix: visual issue always showing the suffocation texture 2025-05-14 18:13:06 +02:00
17f5c378cd Merge pull request 'Visual fixes' (#25) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m54s
Reviewed-on: #25
2025-05-14 02:49:34 +02:00
2f9cf1e57d feat: new version for publish 2025-05-14 02:45:42 +02:00
a470724d81 fix: render issue with water and reframed blocks + missing mixin in mixin config + shading improvement prep 2025-05-14 02:43:46 +02:00
b825959626 fix: render issue with non collidable reframed + culling issue with vanilla blocks + Fixed #22 2025-05-14 02:25:15 +02:00
083d39562a fix: added additional check for placing event see #19 2025-04-13 20:09:08 +02:00
9e40de85e3 Merge pull request 'new block and better additivity' (#24) from dev into master
All checks were successful
deploy / deploy (push) Successful in 6m2s
Reviewed-on: #24
2024-07-01 23:32:00 +02:00
e592db1ea2 feat: updated version 2024-07-01 23:31:09 +02:00
1fa55064c7 feat: added slabs layer block 2024-07-01 23:29:18 +02:00
e8c06a7fb3 feat: better additivity support 2024-07-01 22:31:52 +02:00
d7ae1f646f fix: bad rotating / mirroring logic on corner double frame 2024-07-01 15:16:50 +02:00
e3dac8a77f fix: wrong Step Cross shape 2024-07-01 02:19:34 +02:00
5281f84a2a fix: useAO out of bounds because not using model id 2024-07-01 02:11:49 +02:00
abc7031989 Merge pull request 'More merging logic + 11 new block + Fix server / client sync' (#23) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m39s
Reviewed-on: #23
2024-06-19 14:42:54 +02:00
eac948bf5d feat: updated version 2024-06-19 14:36:00 +02:00
12924579a5 feat: added half slab and half slabs slab 2024-06-19 14:26:43 +02:00
48c7c4e538 fix: made frames properly update on every client when using blueprint/screwdriver/hammer 2024-06-19 13:20:12 +02:00
d325e3c5a5 feat: added 3 blocks and some refactoring 2024-06-19 02:42:03 +02:00
4d353bab27 refactor: changed method to non depreciated version 2024-06-18 15:23:59 +02:00
6f8304d638 feat: added HalfStairsCubeStair and HalfStairsStepStair 2024-06-18 14:56:46 +02:00
3bf9fc2268 feat: added StepCross 2024-06-17 21:10:57 +02:00
5812812bd9 feat: added missing models 2024-06-17 19:19:36 +02:00
1e6cab04cc feat: added SlabsInnerStair and SlabsOuterStair with additive logic + depreciation warning suppressing on AbstractBlock Methods 2024-06-17 19:19:13 +02:00
d7ad6ed0c1 feat: added and improved logic to merge step with slabs 2024-06-17 10:52:21 +02:00
ef76408b80 Merge pull request 'fix and new block' (#22) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m32s
Reviewed-on: #22
2024-06-16 18:22:10 +02:00
8a9fbd9109 feat: added SlabsStair block 2024-06-16 18:17:04 +02:00
a76714d54d fix: render issue with items in axiom 2024-06-16 15:17:48 +02:00
b99f315c6f docs: updated objectives for publish 2024-06-14 17:46:47 +02:00
73bf27bdda Merge pull request 'fix: more checks on ThemedEntity Class (Always more to fix)' (#21) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m8s
Reviewed-on: #21
2024-06-14 17:17:37 +02:00
7df46a4b76 fix: more checks on ThemedEntity Class 2024-06-14 17:16:53 +02:00
402caaf549 Merge pull request 'fix: more injection fix + bad culling' (#20) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m5s
Reviewed-on: #20
2024-06-14 16:53:17 +02:00
42049047f7 fix: more injection fix + bad culling 2024-06-14 16:51:25 +02:00
4eddb32190 Merge pull request 'fix: injection remapping issues fixed + additional check' (#19) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m11s
Reviewed-on: #19
2024-06-14 00:37:13 +02:00
ddffd45a44 fix: injection remapping issues fixed + additional check 2024-06-14 00:35:13 +02:00
53f962da1b Merge pull request 'changed version' (#18) from dev into master
All checks were successful
deploy / deploy (push) Successful in 6m17s
Reviewed-on: #18
2024-06-13 23:10:51 +02:00
fcf02b68e6 feat: version was missing in previous commit 2024-06-13 22:55:51 +02:00
5517d813e7 Merge pull request 'added new blocks and further axiom support' (#17) from dev into master
Some checks failed
deploy / deploy (push) Has been cancelled
Reviewed-on: #17
2024-06-13 22:55:09 +02:00
b88a4abfe5 feat: more axiom support (connected textures and culling in selections) + newer version 2024-06-13 22:51:05 +02:00
ce650abc76 feat: added connected texture support on Axiom selection (includes blueprint previews etc) 2024-06-13 02:50:16 +02:00
535fd6151e feat: added post fence and postfence frames 2024-06-13 01:28:28 +02:00
1061431af7 feat: added button frame 2024-06-12 18:59:08 +02:00
520430d5bc feat: Axiom now renders frames in selections properly 2024-06-12 00:13:38 +02:00
52fb6840ad fix: added check in injections and fixed modifiers interactions and Layer drops 2024-06-12 00:12:07 +02:00
f6b403d852 Merge pull request 'feat: added support for ReFramed block entities with axiom' (#16) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m16s
Reviewed-on: #16
2024-05-23 12:34:22 +02:00
5430016be4 feat: added support for ReFramed block entities with axiom 2024-05-23 12:33:54 +02:00
aa3cf6d511 Merge pull request 'fix: falling block entities calling can replace crashing logic side' (#15) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m59s
Reviewed-on: #15
2024-05-23 00:47:20 +02:00
15a8f80210 fix: falling block entities calling can replace crashing logic side 2024-05-23 00:45:52 +02:00
cfdbfd3a6a Merge pull request 'Improvement for cache fixing issues + 4 new blocks' (#14) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m39s
Reviewed-on: #14
2024-05-14 15:53:28 +02:00
c49a978aa9 updated minor version for publishing 2024-05-14 15:27:34 +02:00
6b2ee1dc83 added added Door, Trapdoor and Pane 2024-05-14 15:21:18 +02:00
752ee956eb added support for rotation and mirroring 2024-05-13 12:58:42 +02:00
d5369823d9 added pillars wall and fixed issues related to multimodel self culling when using multiple models for the same state + unseless caching removed + still no dyn ao :( 2024-04-28 23:19:00 +02:00
c6f2244826 w.i.p. Dynamic AO 2024-04-28 15:27:00 +02:00
72c9c3511b fabric version update 2024-04-25 13:23:06 +02:00
ac2f7def0c Merge pull request '1.5.8 cache fix' (#13) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m13s
Reviewed-on: #13
2024-04-16 19:21:50 +02:00
fa44408836 Updated to v1.5.8 2024-04-16 19:20:52 +02:00
78b8b6f607 cleanup + fixed wall collision when using chorus 2024-04-16 00:24:16 +02:00
6318fdece0 improved caching to use less per-model space and changed cache class for retextured elements 2024-04-16 00:10:53 +02:00
44f03b2c96 Merge pull request 'New minor version with fix for caching on dynamic models' (#12) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m25s
Reviewed-on: #12
2024-04-15 20:58:47 +02:00
c3e0ab16b7 fixed cache issue with dynamic models from continuity and athena. Finished wall logic + updated to minor version 1.5.7 2024-04-15 20:57:03 +02:00
d8e4153754 added all wall models 2024-04-12 19:23:35 +02:00
572182e9f0 imports cleanup 2024-04-12 01:06:11 +02:00
6f394bbe6f wip wall logic and models + moved generation for block into separate classes for better readability 2024-04-12 01:03:47 +02:00
b05c0ebd42 removed unused imports 2024-04-11 00:06:22 +02:00
4309310c02 added missing receipe cleaned up model names creating W.I.P. wall + typo 2024-04-11 00:05:47 +02:00
74a290fd16 improved additivity of blocks and added pillar 2024-03-28 19:01:33 +01:00
a430a8e62b moved tools to tool crafting tab 2024-03-27 23:24:33 +01:00
3afee9e501 added continuity to optional modrinth dependencies + made blocks addition possible by placing correct shape on existing frame 2024-03-27 23:14:24 +01:00
319d247e4c added continuity to optional modrinth dependencies 2024-03-25 17:12:05 +01:00
f1af00738e Merge pull request 'version 1.5.6 fix for server imports + support for continuity and MoreCulling compat' (#11) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m44s
Reviewed-on: #11
2024-03-25 16:33:09 +01:00
3048f53878 removed necessary implementation and updated to minor version 2024-03-25 16:27:58 +01:00
f222e4dc24 fixed incompatibility with MoreCulling + fixed server crash with clientside classes import + continuity connected textures support 2024-03-25 15:22:28 +01:00
5c064176b2 complied with FramedBlock assets License by making this project Multi-Licensed. In addition, wrote a README.md + cache reload fix on resource reload + items for hammer, blueprint and screwdriver + updated to v1.5.5 2024-03-15 23:30:10 +01:00
186cca3e27 Merge pull request 'v1.5 new shapes + self culling + caching + coding tools/cleanup' (#10) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m42s
Reviewed-on: #10
2024-03-14 22:30:29 +01:00
08bde119b2 updated version for publish 2024-03-14 22:28:45 +01:00
6922dcc9d9 improved naming + added new voxel list builder + Half Stairs Slab + Half Stairs Stair + Layer + coherence in model orientations 2024-03-14 22:05:14 +01:00
b1ba784ee3 made stairs connect when 2 different instance are used + organized todos 2024-03-10 15:43:06 +01:00
c207fb5ae6 renamed blocks for more clarity and expandability 2024-03-10 15:26:29 +01:00
5fcb983592 fixed small cube and caching for dynamic models items because items rerender often 2024-03-09 15:26:50 +01:00
bd412bfccd added missing shape 2024-03-08 23:50:50 +01:00
96f08bdd3e w.i.p more shapes + more coherent properties name (yes changing again) 2024-03-08 23:50:17 +01:00
394c75f9ed fix error + cleanup 2024-03-08 20:45:11 +01:00
e6c9ee63cd self culling !! 2024-03-08 20:38:14 +01:00
239802ce7b cleanup 2024-03-06 22:16:43 +01:00
5f87489939 better caching for both appearances and meshed models 2024-03-06 22:10:11 +01:00
d43d9456ee Merge pull request 'moved and created data generation for all elements other than models + removed block that aren't working' (#9) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m48s
Reviewed-on: #9
2024-03-05 21:56:03 +01:00
de7acfee50 moved and created data generation for all elements other than models + removed block that aren't working + cleanup 2024-03-05 21:54:36 +01:00
2957a899db Merge pull request 'Sound support + OffHand camo.' (#8) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m6s
Reviewed-on: #8
2024-03-04 17:04:13 +01:00
6ca266b676 patched sounds on the frames and made it so that it will take the texture of the block in offhand 2024-03-04 16:57:18 +01:00
Adrien1106
4d87ce9753 Merge pull request #1 from quentinlegot/CI-fix
Fix CI while fethcing wrapper-validation-action
2024-03-03 22:54:55 +01:00
e196975f1d Fix CI while fethcing wrapper-validation-action 2024-03-03 22:53:30 +01:00
e8ce990955 removed w.i.p. mixins 2024-03-03 21:57:22 +01:00
c3e2e626c8 removed useless element in github workflow 2024-03-03 21:49:09 +01:00
f3578a4da5 Merge pull request 'Indium suppor + github runner' (#7) from dev into master
Some checks failed
deploy / deploy (push) Failing after 5m3s
Reviewed-on: #7
2024-03-03 21:34:52 +01:00
dccc01ef49 Added Modrinth Auto Publisher on github workflow 2024-03-03 19:33:25 +01:00
961165104d added support for indium/sodium 2024-03-03 17:43:53 +01:00
28ae110e14 Merge pull request 'Double frames' (#6) from dev into master
All checks were successful
deploy / deploy (push) Successful in 5m14s
Reviewed-on: #6
2024-03-02 02:02:47 +01:00
13539fd91d added some todos 2024-03-02 01:57:25 +01:00
9c6e436fe3 fixed culling error and updated version to 1.3 2024-03-02 01:29:08 +01:00
53107d090b added double stair and found a culling error 2024-03-02 01:14:28 +01:00
a3ffe17a7e fixed connected texture (might still have bugs) 2024-03-01 23:43:28 +01:00
87616ae930 removed unused mixin 2024-03-01 21:58:57 +01:00
ce216d1f9f some rework / cleanup with full rework of culling to work by parts. no self culling yet? still missing work on connected textures 2024-03-01 21:56:22 +01:00
6a84a14f3e cleanup to prepare for double camos 2024-02-28 21:21:28 +01:00
fcd5d77b26 added item render support for athena blocks and some improved readability 2024-02-28 00:31:42 +01:00
0a050a2abc removed old changelog 2024-02-26 21:12:04 +01:00
5824d5d60c fixed chipped gradle import 2024-02-26 21:10:59 +01:00
416 changed files with 17546 additions and 5114 deletions

28
.github/workflows/publish.yml vendored Normal file
View 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
View File

@@ -33,3 +33,4 @@ local.properties
*-autosave.kra *-autosave.kra
*.kra~ *.kra~
/src/generated/ /src/generated/
/libs/

View File

@@ -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
View File

@@ -1,15 +1 @@
Copyright (c) 2019 B0undarybreaker (Meredith Espinosa) MIT AND LGPL-3.0
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.

165
LICENSE.LGPL-3.0 Normal file
View 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
View 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.

34
README.md Normal file
View File

@@ -0,0 +1,34 @@
# 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:
- Pressure Plate
- 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`

View File

@@ -3,7 +3,8 @@ import fr.altarik.CreateTag
plugins { plugins {
id 'fabric-loom' version '1.5-SNAPSHOT' id "com.modrinth.minotaur" version "2.+"
id 'fabric-loom' version '1.6-SNAPSHOT'
id 'maven-publish' id 'maven-publish'
} }
@@ -72,6 +73,20 @@ repositories {
maven { maven {
url "https://maven.resourcefulbees.com/repository/maven-public/" url "https://maven.resourcefulbees.com/repository/maven-public/"
} }
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
filter {
includeGroup "maven.modrinth"
}
}
maven {
url = "https://jitpack.io"
}
mavenCentral() mavenCentral()
// Add repositories to retrieve artifacts from in here. // Add repositories to retrieve artifacts from in here.
@@ -88,15 +103,29 @@ dependencies {
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Athena for connected texture // 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 textures
modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}" modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
modRuntimeOnly "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 connected textures
modCompileOnly "maven.modrinth:continuity:${project.continuity_version}"
modRuntimeOnly "maven.modrinth:continuity:${project.continuity_version}"
// Chipped to test athena implementation // Chipped to test athena implementation
modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7" modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7"
// modRuntimeOnly "earth.terrarium.chipped:chipped-fabric-${project.minecraft_version}:3.1.2" TODO not working for some reasons modRuntimeOnly "earth.terrarium.chipped:Chipped-fabric-${project.minecraft_version}:3.1.2"
// Fabric API. This is technically optional, but you probably want it anyway. // Axiom for blueprint support
modCompileOnly "maven.modrinth:N6n5dqoA:YxeYxQyz"
modCompileOnly "com.github.moulberry:AxiomClientAPI:1.0.5.3"
// Fabric API.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
} }
@@ -106,6 +135,9 @@ processResources {
inputs.property "loader_version", project.loader_version inputs.property "loader_version", project.loader_version
inputs.property "mod_id", project.mod_id inputs.property "mod_id", project.mod_id
inputs.property "athena_version", project.athena_version 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" filteringCharset "UTF-8"
filesMatching("fabric.mod.json") { filesMatching("fabric.mod.json") {
@@ -171,6 +203,24 @@ publishing {
} }
} }
// configure modrinth publication
modrinth {
token = getEnv("MODRINTH_TOKEN", local.getProperty("modrinth_token"))
projectId = project.modrinth_id
versionNumber = "${project.mod_version}-${project.minecraft_version}"
versionName = "${project.archives_base_name} ${project.mod_version}-${project.minecraft_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}"
optional.version "1IjD5062", "${project.continuity_version}"
}
}
tasks.register("reportToDiscord", ReportDiscord) { tasks.register("reportToDiscord", ReportDiscord) {
config.set(reportConfig) config.set(reportConfig)
} }

View File

@@ -5,19 +5,23 @@ org.gradle.jvmargs=-Xmx1G
# check these on https://modmuss50.me/fabric.html # check these on https://modmuss50.me/fabric.html
minecraft_version=1.20.4 minecraft_version=1.20.4
yarn_mappings=1.20.4+build.3 yarn_mappings=1.20.4+build.3
loader_version=0.15.6 loader_version=0.15.11
# Mod Properties # Mod Properties
mod_version = 1.2 modrinth_id = jCpoCBpn
mod_version = 1.6.8
maven_group = fr.adrien1106 maven_group = fr.adrien1106
archives_base_name = ReFramed archives_base_name = ReFramed
mod_id = reframed mod_id = reframed
# Dependencies # Dependencies
# check this on https://modmuss50.me/fabric.html # check this on https://modmuss50.me/fabric.html
fabric_version=0.95.4+1.20.4 fabric_version=0.97.0+1.20.4
git_owner=Altarik git_owner=Altarik
git_repo=ReFramed git_repo=ReFramed
athena_version=3.3.0 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

View File

@@ -1,20 +1,20 @@
package fr.adrien1106.reframed; package fr.adrien1106.reframed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import fr.adrien1106.reframed.block.*; 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.api.ModInitializer;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup; 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.AbstractBlock;
import net.minecraft.block.Block; import net.minecraft.block.Block;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntityType; import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.BlockItem; import net.minecraft.item.*;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; import net.minecraft.registry.Registry;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -22,62 +22,134 @@ import net.minecraft.world.World;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.function.BiConsumer; 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 multiple camos * TODO Dynamic Ambient Occlusion -> not scheduled
* TODO better connected textures -> not scheduled
* TODO support continuity overlays -> not scheduled
* TODO slopes -> thinking about it
*/ */
public class ReFramed implements ModInitializer { public class ReFramed implements ModInitializer {
public static final String MODID = "reframed"; public static final String MODID = "reframed";
public static final ArrayList<Block> BLOCKS = new ArrayList<>(); 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 ReFramedBlock
CUBE,
SMALL_CUBE, SMALL_CUBES_STEP,
STAIR, STAIRS_CUBE,
HALF_STAIR, HALF_STAIRS_SLAB, HALF_STAIRS_STAIR, HALF_STAIRS_CUBE_STAIR, HALF_STAIRS_STEP_STAIR,
SLAB, SLABS_CUBE, SLABS_STAIR, SLABS_OUTER_STAIR, SLABS_INNER_STAIR, SLABS_HALF_LAYER, SLABS_LAYER,
HALF_SLAB, HALF_SLABS_SLAB,
STEP, STEPS_SLAB, STEPS_CROSS, STEPS_HALF_LAYER,
LAYER, HALF_LAYER,
PILLAR, PILLARS_WALL, WALL,
PANE, TRAPDOOR, DOOR,
BUTTON,
POST, POST_FENCE, FENCE;
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<ReFramedEntity> REFRAMED_BLOCK_ENTITY;
public static BlockEntityType<ReFramedDoubleEntity> REFRAMED_DOUBLE_BLOCK_ENTITY;
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {}; public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
@Override @Override
public void onInitialize() { public void onInitialize() {
//registerReFramed mutates FRAMES as a side effect, which is a List, so order is preserved CUBE = registerBlock("cube" , new ReFramedBlock(cp(Blocks.OAK_PLANKS)));
//the ordering is used in the creative tab, so they're roughly sorted by encounter order of the SMALL_CUBE = registerBlock("small_cube" , new ReFramedSmallCubeBlock(cp(Blocks.OAK_PLANKS)));
//corresponding vanilla block in the "search" creative tab... with the non-vanilla "post" and SMALL_CUBES_STEP = registerBlock("small_cubes_step" , new ReFramedSmallCubesStepBlock(cp(Blocks.OAK_PLANKS)));
//"vertical slab" inserted where they fit ...and i moved the lever way up next to the pressureplate STAIR = registerBlock("stair" , new ReFramedStairBlock(cp(Blocks.OAK_STAIRS)));
//and button, because they're redstoney... hopefully this ordering makes sense lol STAIRS_CUBE = registerBlock("stairs_cube" , new ReFramedStairsCubeBlock(cp(Blocks.OAK_STAIRS)));
CUBE = registerReFramed("cube" , new ReFramedBlock(ReFramedInteractionUtil.makeSettings())); HALF_STAIR = registerBlock("half_stair" , new ReFramedHalfStairBlock(cp(Blocks.OAK_STAIRS)));
STAIRS = registerReFramed("stairs" , new ReFramedStairsBlock(cp(Blocks.OAK_STAIRS))); HALF_STAIRS_SLAB = registerBlock("half_stairs_slab" , new ReFramedHalfStairsSlabBlock(cp(Blocks.OAK_STAIRS)));
SLAB = registerReFramed("slab" , new ReFramedSlabBlock(cp(Blocks.OAK_SLAB))); HALF_STAIRS_STAIR = registerBlock("half_stairs_stair" , new ReFramedHalfStairsStairBlock(cp(Blocks.OAK_STAIRS)));
POST = registerReFramed("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE))); HALF_STAIRS_CUBE_STAIR = registerBlock("half_stairs_cube_stair" , new ReFramedHalfStairsCubeStairBlock(cp(Blocks.OAK_STAIRS)));
FENCE = registerReFramed("fence" , new ReFramedFenceBlock(cp(Blocks.OAK_FENCE))); HALF_STAIRS_STEP_STAIR = registerBlock("half_stairs_step_stair" , new ReFramedHalfStairsStepStairBlock(cp(Blocks.OAK_STAIRS)));
FENCE_GATE = registerReFramed("fence_gate" , new ReFramedFenceGateBlock(cp(Blocks.OAK_FENCE_GATE))); LAYER = registerBlock("layer" , new ReFramedLayerBlock(cp(Blocks.OAK_SLAB)));
DOOR = registerReFramed("door" , new ReFramedDoorBlock(cp(Blocks.OAK_DOOR), BlockSetType.OAK)); HALF_LAYER = registerBlock("half_layer" , new ReFramedHalfLayerBlock(cp(Blocks.OAK_SLAB)));
TRAPDOOR = registerReFramed("trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.OAK_TRAPDOOR), BlockSetType.OAK)); SLAB = registerBlock("slab" , new ReFramedSlabBlock(cp(Blocks.OAK_SLAB)));
IRON_DOOR = registerReFramed("iron_door" , new ReFramedDoorBlock(cp(Blocks.IRON_DOOR), BlockSetType.IRON)); SLABS_CUBE = registerBlock("slabs_cube" , new ReFramedSlabsCubeBlock(cp(Blocks.OAK_SLAB)));
IRON_TRAPDOOR = registerReFramed("iron_trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.IRON_TRAPDOOR), BlockSetType.IRON)); SLABS_STAIR = registerBlock("slabs_stair" , new ReFramedSlabsStairBlock(cp(Blocks.OAK_STAIRS)));
PRESSURE_PLATE = registerReFramed("pressure_plate", new ReFramedPressurePlateBlock(cp(Blocks.OAK_PRESSURE_PLATE))); SLABS_OUTER_STAIR = registerBlock("slabs_outer_stair" , new ReFramedSlabsOuterStairBlock(cp(Blocks.OAK_STAIRS)));
BUTTON = registerReFramed("button" , new ReFramedButtonBlock(cp(Blocks.OAK_BUTTON))); SLABS_INNER_STAIR = registerBlock("slabs_inner_stair" , new ReFramedSlabsInnerStairBlock(cp(Blocks.OAK_STAIRS)));
LEVER = registerReFramed("lever" , new ReFramedLeverBlock(cp(Blocks.LEVER))); SLABS_HALF_LAYER = registerBlock("slabs_half_layer" , new ReFramedSlabsHalfLayerBlock(cp(Blocks.OAK_SLAB)));
WALL = registerReFramed("wall" , new ReFramedWallBlock(ReFramedInteractionUtil.makeSettings())); SLABS_LAYER = registerBlock("slabs_layer" , new ReFramedSlabsLayerBlock(cp(Blocks.OAK_SLAB)));
CARPET = registerReFramed("carpet" , new ReFramedCarpetBlock(cp(Blocks.WHITE_CARPET))); HALF_SLAB = registerBlock("half_slab" , new ReFramedHalfSlabBlock(cp(Blocks.OAK_SLAB)));
PANE = registerReFramed("pane" , new ReFramedPaneBlock(cp(Blocks.GLASS_PANE))); HALF_SLABS_SLAB = registerBlock("half_slabs_slab" , new ReFramedHalfSlabsSlabBlock(cp(Blocks.OAK_SLAB)));
CANDLE = registerReFramed("candle" , new ReFramedCandleBlock(ReFramedCandleBlock.configureSettings(cp(Blocks.CANDLE)))); STEP = registerBlock("step" , new ReFramedStepBlock(cp(Blocks.OAK_SLAB)));
STEPS_SLAB = registerBlock("steps_slab" , new ReFramedStepsSlabBlock(cp(Blocks.OAK_SLAB)));
STEPS_CROSS = registerBlock("steps_cross" , new ReFramedStepsCrossBlock(cp(Blocks.OAK_SLAB)));
STEPS_HALF_LAYER = registerBlock("steps_half_layer" , new ReFramedStepsHalfLayerBlock(cp(Blocks.OAK_SLAB)));
PILLAR = registerBlock("pillar" , new ReFramedPillarBlock(cp(Blocks.OAK_FENCE)));
PILLARS_WALL = registerBlock("pillars_wall" , new ReFramedPillarsWallBlock(cp(Blocks.OAK_FENCE)));
WALL = registerBlock("wall" , new ReFramedWallBlock(cp(Blocks.OAK_FENCE)));
PANE = registerBlock("pane" , new ReFramedPaneBlock(cp(Blocks.OAK_FENCE)));
TRAPDOOR = registerBlock("trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.OAK_TRAPDOOR)));
DOOR = registerBlock("door" , new ReFramedDoorBlock(cp(Blocks.OAK_DOOR)));
BUTTON = registerBlock("button" , new ReFramedButtonBlock(cp(Blocks.OAK_BUTTON)));
POST = registerBlock("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE)));
FENCE = registerBlock("fence" , new ReFramedFenceBlock(cp(Blocks.OAK_FENCE)));
POST_FENCE = registerBlock("post_fence" , new ReFramedPostFenceBlock(cp(Blocks.OAK_FENCE)));
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"), 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) BlockEntityType.Builder.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"),
BlockEntityType.Builder.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")) .displayName(Text.translatable("itemGroup.reframed.tab"))
.icon(() -> new ItemStack(SLAB)) .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) { 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)
.sounds(BlockSoundGroup.WOOD)
.hardness(0.2f)
.suffocates(Blocks::never)
.solidBlock(Blocks::always);
// .blockVision(Blocks::always);
}
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 registerReFramed(String path, B block) { private static <B extends Block> B registerBlock(String path, B block) {
Identifier id = id(path); Identifier id = id(path);
Registry.register(Registries.BLOCK, id, block); Registry.register(Registries.BLOCK, id, block);

View File

@@ -0,0 +1,100 @@
package fr.adrien1106.reframed.block;
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.BooleanProperty;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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 net.minecraft.world.WorldAccess;
import org.apache.commons.lang3.function.TriFunction;
import org.jetbrains.annotations.Nullable;
import static net.minecraft.state.property.Properties.*;
import static net.minecraft.state.property.Properties.SOUTH;
public abstract class ConnectingReFramedBlock extends WaterloggableReFramedBlock {
public ConnectingReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(EAST, false)
.with(NORTH, false)
.with(WEST, false)
.with(SOUTH, false)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST));
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) {
BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved);
if (dir == Direction.DOWN) return new_state;
return placementState(new_state, world, pos, this::connectsTo);
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
return placementState(state, world, pos, this::connectsTo);
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getConnectionProperty(rotation.rotate(dir)), state.get(getConnectionProperty(dir)))
, (prev, next) -> next);
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getConnectionProperty(mirror.apply(dir)), state.get(getConnectionProperty(dir)))
, (prev, next) -> next);
}
protected abstract boolean connectsTo(BlockState state, boolean fs, Direction dir);
@Override
@SuppressWarnings("deprecation")
public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context);
public static BooleanProperty getConnectionProperty(Direction dir) {
return switch (dir) {
case NORTH -> NORTH;
case EAST -> EAST;
case SOUTH -> SOUTH;
case WEST -> WEST;
default -> null;
};
}
public static BlockState placementState(BlockState state, BlockView world, BlockPos pos, TriFunction<BlockState, Boolean, Direction, Boolean> connectsTo) {
for (Direction dir: Direction.Type.HORIZONTAL) {
BlockState neighbor = world.getBlockState(pos.offset(dir));
state = state.with(getConnectionProperty(dir), connectsTo.apply(
neighbor,
neighbor.isSideSolidFullSquare(world, pos.offset(dir), dir.getOpposite()),
dir
));
}
return state;
}
}

View File

@@ -0,0 +1,70 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Corner;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.blocks.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER_FACE;
public abstract class CornerDoubleReFramedBlock extends WaterloggableReFramedDoubleBlock {
public CornerDoubleReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_EAST_DOWN).with(CORNER_FACE, 0));
}
@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
@SuppressWarnings("deprecation")
public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context);
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
Corner corner = state.get(CORNER);
Direction face = corner.getDirection(state.get(CORNER_FACE));
corner = corner.rotate(rotation);
return state
.with(CORNER, corner)
.with(CORNER_FACE, corner.getDirectionIndex(rotation.rotate(face)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
Corner corner = state.get(CORNER);
Direction face = corner.getDirection(state.get(CORNER_FACE));
corner = corner.mirror(mirror);
return state
.with(CORNER, corner)
.with(CORNER_FACE, corner.getDirectionIndex(mirror.apply(face)));
}
@Override
public abstract VoxelShape getShape(BlockState state, int i);
}

View File

@@ -0,0 +1,64 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Edge;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.blocks.BlockProperties.EDGE;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE_FACE;
public abstract class EdgeDoubleReFramedBlock extends WaterloggableReFramedDoubleBlock {
public EdgeDoubleReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(EDGE_FACE, 0));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE, EDGE_FACE));
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
Edge edge = BlockHelper.getPlacementEdge(ctx);
return super.getPlacementState(ctx)
.with(EDGE, edge)
.with(EDGE_FACE, edge.getDirectionIndex(ctx.getSide().getOpposite()));
}
@Override
@SuppressWarnings("deprecation")
public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context);
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
Edge edge = state.get(EDGE).rotate(rotation);
Direction face = state.get(EDGE).getDirection(state.get(EDGE_FACE));
return state.with(EDGE, edge).with(EDGE_FACE, edge.getDirectionIndex(rotation.rotate(face)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
Edge edge = state.get(EDGE).mirror(mirror);
Direction face = state.get(EDGE).getDirection(state.get(EDGE_FACE));
return state.with(EDGE, edge).with(EDGE_FACE, edge.getDirectionIndex(mirror.apply(face)));
}
@Override
public abstract VoxelShape getShape(BlockState state, int i);
}

View File

@@ -0,0 +1,57 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.AbstractBlock;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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 net.minecraft.state.property.Properties.FACING;
public abstract class FacingDoubleReFramedBlock extends WaterloggableReFramedDoubleBlock {
public FacingDoubleReFramedBlock(AbstractBlock.Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
}
@Override
protected void appendProperties(StateManager.Builder< Block, BlockState > builder) {
super.appendProperties(builder.add(FACING));
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
return super.getPlacementState(ctx)
.with(FACING, ctx.getSide().getOpposite());
}
@Override
@SuppressWarnings("deprecation")
public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context);
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state
.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(FACING, mirror.apply(state.get(FACING)));
}
@Override
public abstract VoxelShape getShape(BlockState state, int i);
}

View File

@@ -0,0 +1,46 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.context.LootContextParameterSet;
import net.minecraft.state.StateManager;
import java.util.List;
import static net.minecraft.state.property.Properties.LAYERS;
public abstract class HalfLayerDoubleReFramedBlock extends EdgeDoubleReFramedBlock {
public HalfLayerDoubleReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(LAYERS, 1));
}
@Override
@SuppressWarnings("deprecation")
public List<ItemStack> getDroppedStacks(BlockState state, LootContextParameterSet.Builder builder) {
List<ItemStack> drops = super.getDroppedStacks(state, builder);
if (state.get(LAYERS) > 1)
drops.add(new ItemStack(ReFramed.HALF_LAYER, state.get(LAYERS)-1));
return drops;
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(LAYERS));
}
@Override
@SuppressWarnings("deprecation")
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null
|| context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
) return false;
return block_item.getBlock() == ReFramed.HALF_LAYER && state.get(LAYERS) < 8;
}
}

View File

@@ -0,0 +1,64 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.context.LootContextParameterSet;
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 java.util.List;
import static net.minecraft.state.property.Properties.LAYERS;
public abstract class LayeredReFramedBlock extends WaterloggableReFramedBlock {
public LayeredReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(LAYERS, 1));
}
@Override
@SuppressWarnings("deprecation")
public List<ItemStack> getDroppedStacks(BlockState state, LootContextParameterSet.Builder builder) {
List<ItemStack> drops = super.getDroppedStacks(state, builder);
drops.forEach((stack) -> {
if (stack.getItem() instanceof BlockItem bi && bi.getBlock() instanceof LayeredReFramedBlock)
stack.setCount(state.get(LAYERS));
});
return drops;
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(LAYERS));
}
@Override
@SuppressWarnings("deprecation")
public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context);
@Override
@SuppressWarnings("deprecation")
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null) return false;
return !(
context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
|| !(block_item.getBlock() == this && 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);
}
}

View File

@@ -0,0 +1,49 @@
package fr.adrien1106.reframed.block;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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 net.minecraft.state.property.Properties.AXIS;
public abstract class PillarReFramedBlock extends WaterloggableReFramedBlock {
public PillarReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
}
@Override
protected void appendProperties(StateManager.Builder< Block, BlockState > builder) {
super.appendProperties(builder.add(AXIS));
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
return super.getPlacementState(ctx).with(AXIS, ctx.getSide().getAxis());
}
@Override
@SuppressWarnings("deprecation")
public abstract VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context);
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(AXIS, mirror.apply(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
}
}

View File

@@ -1,33 +1,68 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.*; import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.registry.Registries;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.ItemScatterer;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.GameRules;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.LIGHT;
public class ReFramedBlock extends Block implements BlockEntityProvider { public class ReFramedBlock extends Block implements BlockEntityProvider {
public ReFramedBlock(Settings settings) { public ReFramedBlock(Settings settings) {
super(settings); super(settings.dynamicBounds());
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState())); setDefaultState(getDefaultState().with(LIGHT, false));
} }
//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 @Override
//Just make your own BlockEntityType, it's fine, you can even use the same ReFramedEntity class @SuppressWarnings("deprecation")
public int getOpacity(BlockState state, BlockView world, BlockPos pos) {
if (state.get(LIGHT)) return 0;
if (!(world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity)
|| frame_entity.getTheme(0).isOpaque())
return world.getMaxLightLevel();
return 0;
}
@Override
@SuppressWarnings("deprecation")
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return false;
}
@Override @Override
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
@@ -35,51 +70,175 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder)); super.appendProperties(builder.add(LIGHT));
} }
@Nullable @Nullable
@Override @Override
public BlockState getPlacementState(ItemPlacementContext ctx) { public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx); return ReFramedEntity.getNbtLightLevel(super.getPlacementState(ctx), ctx.getStack());
} }
@Override @Override
@SuppressWarnings("deprecation")
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { 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 (!canUse(world, pos, player)) return ActionResult.PASS;
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit); ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
return r; if (result.isAccepted()) return result;
return BlockHelper.useCamo(state, world, pos, player, hand, hit, 1);
}
protected boolean canUse(World world, BlockPos pos, PlayerEntity player) {
return player.canModifyBlocks() && world.canPlayerModifyAt(player, pos);
} }
@Override @Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { @SuppressWarnings("deprecation")
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved); public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved); if (!new_state.isOf(state.getBlock())) world.removeBlockEntity(pos);
}
if(!(new_state.getBlock() instanceof ReFramedBlock) &&
@Override world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity &&
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack); ) {
super.onPlaced(world, pos, state, placer, stack); 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()));
});
ItemScatterer.spawn(world, pos, drops);
}
super.onStateReplaced(state, world, pos, new_state, moved);
} }
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack, BlockState old_state, BlockEntity old_entity) {
if (!(world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity)) {
onPlaced(world, pos, state, placer, stack);
return;
}
// apply state change keeping the old information
if (old_state.getBlock() instanceof ReFramedBlock old_frame_block
&& old_entity instanceof ReFramedEntity old_frame_entity) {
Map<Integer, Integer> theme_map = old_frame_block.getThemeMap(old_state, state);
theme_map.forEach((self, other) ->
frame_entity.setTheme(old_frame_entity.getTheme(self), other)
);
// apply any changes needed to keep previous properties
if (old_frame_entity.emitsLight() && !frame_entity.emitsLight()) {
frame_entity.toggleLight();
world.setBlockState(pos, state.with(LIGHT, true));
}
if (old_frame_entity.emitsRedstone() && !frame_entity.emitsRedstone()) {
frame_entity.toggleRedstone();
world.updateNeighbors(pos, this);
}
if (old_frame_entity.isSolid() && !frame_entity.isSolid()) frame_entity.toggleSolidity();
// apply themes from item
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
if(tag != null) {
// determine a list of themes than can be used
Iterator<Integer> free_themes = IntStream
.rangeClosed(1, frame_entity.getThemes().size())
.filter(value -> !theme_map.containsValue(value))
.iterator();
// apply all the themes possible from item
for (int i = 1; tag.contains(BLOCKSTATE_KEY + i) && free_themes.hasNext(); i++) {
BlockState theme = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound(BLOCKSTATE_KEY + i));
if (theme == null || theme.getBlock() == Blocks.AIR) continue;
frame_entity.setTheme(theme, free_themes.next());
}
}
} else if(world.isClient) { // prevents flashing with default texture before server sends the update
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
if(tag != null) frame_entity.readNbt(tag);
}
onPlaced(world, pos, state, placer, stack);
}
public boolean matchesShape(Vec3d hit, BlockPos pos, BlockState state) {
return matchesShape(hit, pos, state, 0);
}
public boolean matchesShape(Vec3d hit, BlockPos pos, BlockState state, int i) {
Vec3d rel = BlockHelper.getRelativePos(hit, pos);
return matchesShape(rel, getShape(state, i));
}
public boolean matchesShape(Vec3d rel_hit, VoxelShape shape) {
return BlockHelper.cursorMatchesFace(shape, rel_hit);
}
@Override @Override
@SuppressWarnings("deprecation")
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { 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); @Override
@SuppressWarnings("deprecation")
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return isGhost(view, pos)
? VoxelShapes.empty()
: super.getCullingShape(state, view, pos);
}
@SuppressWarnings("deprecation")
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 @Override
@SuppressWarnings("deprecation")
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { 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 @Override
@SuppressWarnings("deprecation")
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir); return getWeakRedstonePower(state, view, pos, dir);
}
/**
* @param state - the block state to get the top theme index from
* @return the index of the top theme to use for the block
*/
public int getTopThemeIndex(BlockState state) {
return 1;
}
/**
* @param state - the block state of the block that is being replaced
* @param new_state - the block state of the block that is replacing the block
* @return a map of the theme indexes to map when changing state so that the themes are preserved
*/
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
return Map.of();
}
public VoxelShape getShadingShape(BlockState state, BlockView world, BlockPos pos) {
if (!(world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)) return this.getCollisionShape(state, world, pos, ShapeContext.absent());
AtomicInteger i = new AtomicInteger(1);
return framed_entity.getThemes().stream().map((theme) -> {
int index = i.getAndIncrement();
return theme.isTransparent(world, pos) ? VoxelShapes.empty() : this.getShape(state, index);
}).reduce(
VoxelShapes.empty(),
(prev, current) -> VoxelShapes.combine(prev, current, BooleanBiFunction.OR)
);
} }
} }

View File

@@ -1,95 +1,245 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects; import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.ReFramed; import net.minecraft.block.*;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil; import net.minecraft.block.enums.BlockFace;
import net.minecraft.block.Block; import net.minecraft.entity.Entity;
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.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.PersistentProjectileEntity;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.WorldView;
import net.minecraft.world.event.GameEvent;
import net.minecraft.world.explosion.Explosion;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class ReFramedButtonBlock extends ButtonBlock implements BlockEntityProvider { import java.util.function.BiConsumer;
public ReFramedButtonBlock(Settings settings) {
this(settings, BlockSetType.OAK, 30); import static net.minecraft.state.property.Properties.*;
}
public class ReFramedButtonBlock extends WaterloggableReFramedBlock {
public ReFramedButtonBlock(Settings settings, BlockSetType blockSetType, int i) {
super(blockSetType, i, settings); public static final VoxelShape[] BUTTON_VOXELS;
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
} public ReFramedButtonBlock(Settings settings) {
super(settings);
@Nullable setDefaultState(getDefaultState()
@Override .with(HORIZONTAL_FACING, Direction.NORTH)
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { .with(BLOCK_FACE, BlockFace.WALL)
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); .with(POWERED, false)
} );
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { @Override
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder)); protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
} super.appendProperties(builder.add(HORIZONTAL_FACING, BLOCK_FACE, POWERED));
}
@Nullable
@Override @Override
public BlockState getPlacementState(ItemPlacementContext ctx) { @SuppressWarnings("deprecation")
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx); public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
} return canPlaceAt(world, pos, getDirection(state).getOpposite());
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { public static boolean canPlaceAt(WorldView world, BlockPos pos, Direction direction) {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit); BlockPos other_pos = pos.offset(direction);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit); return world.getBlockState(other_pos).isSideSolidFullSquare(world, other_pos, direction.getOpposite());
return r; }
}
@Override
@Override public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { BlockState state = super.getPlacementState(ctx);
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved); Direction side = ctx.getSide();
super.onStateReplaced(state, world, pos, newState, moved); return state
} .with(HORIZONTAL_FACING, side.getAxis() == Direction.Axis.Y
? ctx.getHorizontalPlayerFacing()
@Override : side
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { )
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack); .with(BLOCK_FACE, side.getAxis() != Direction.Axis.Y
} ? BlockFace.WALL
: side == Direction.UP ? BlockFace.FLOOR : BlockFace.CEILING
@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 BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos other_pos) {
@Override return getDirection(state).getOpposite() == direction && !state.canPlaceAt(world, pos)
public boolean emitsRedstonePower(BlockState state) { ? Blocks.AIR.getDefaultState()
return super.emitsRedstonePower(state); : super.getStateForNeighborUpdate(state, direction, other_state, world, pos, other_pos);
} }
@Override @Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
boolean a = 0 != super.getWeakRedstonePower(state, view, pos, dir); ActionResult result = super.onUse(state, world, pos, player, hand, hit);
boolean b = 0 != ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir); if (result.isAccepted()) return result;
return (a ^ b) ? 15 : 0;
} if (state.get(POWERED)) return ActionResult.CONSUME;
powerOn(state, world, pos);
@Override playClickSound(player, world, pos, true);
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { world.emitGameEvent(player, GameEvent.BLOCK_ACTIVATE, pos);
if(getDirection(state) != dir) return 0;
else return getWeakRedstonePower(state, view, pos, dir); return ActionResult.success(world.isClient);
} }
@Override
@SuppressWarnings("deprecation")
public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stackMerger) {
if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK && !world.isClient() && !(Boolean)state.get(POWERED)) {
powerOn(state, world, pos);
}
super.onExploded(state, world, pos, explosion, stackMerger);
}
public void powerOn(BlockState state, World world, BlockPos pos) {
world.setBlockState(pos, state.with(POWERED, true), 3);
updateNeighbors(state, world, pos);
world.scheduleBlockTick(pos, this, 30);
}
protected void playClickSound(@Nullable PlayerEntity player, WorldAccess world, BlockPos pos, boolean powered) {
world.playSound(
powered ? player : null,
pos,
powered ? BlockSetType.OAK.buttonClickOn() : BlockSetType.OAK.buttonClickOff(),
SoundCategory.BLOCKS
);
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return BUTTON_VOXELS[
(state.get(POWERED) ? 12 : 0) +
(4 * state.get(BLOCK_FACE).ordinal()) +
state.get(HORIZONTAL_FACING).ordinal() - 2
];
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(HORIZONTAL_FACING, rotation.rotate(state.get(HORIZONTAL_FACING)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(HORIZONTAL_FACING, mirror.apply(state.get(HORIZONTAL_FACING)));
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState new_state, boolean moved) {
super.onStateReplaced(state, world, pos, new_state, false);
if(!state.isOf(new_state.getBlock())) {
if (!moved && state.get(POWERED)) updateNeighbors(state, world, pos);
}
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return state.get(POWERED) ? 15 : super.getWeakRedstonePower(state, view, pos, dir);
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return dir == getDirection(state) ? state.getWeakRedstonePower(view, pos, dir) : 0;
}
@Override
@SuppressWarnings("deprecation")
public boolean emitsRedstonePower(BlockState state) {
return true;
}
@Override
@SuppressWarnings("deprecation")
public void scheduledTick(BlockState state, ServerWorld world, BlockPos pos, Random random) {
if (state.get(POWERED)) tryPowerWithProjectiles(state, world, pos);
}
@Override
@SuppressWarnings("deprecation")
public void onEntityCollision(BlockState state, World world, BlockPos pos, Entity entity) {
if (!world.isClient && !state.get(POWERED)) tryPowerWithProjectiles(state, world, pos);
}
protected void tryPowerWithProjectiles(BlockState state, World world, BlockPos pos) {
PersistentProjectileEntity projectile = world.getNonSpectatingEntities(
PersistentProjectileEntity.class,
state.getOutlineShape(world, pos).getBoundingBox().offset(pos)
).stream().findFirst().orElse(null);
boolean has_projectile = projectile != null;
if (has_projectile != state.get(POWERED)) {
world.setBlockState(pos, state.with(POWERED, has_projectile), 3);
this.updateNeighbors(state, world, pos);
this.playClickSound(null, world, pos, has_projectile);
world.emitGameEvent(projectile, has_projectile ? GameEvent.BLOCK_ACTIVATE : GameEvent.BLOCK_DEACTIVATE, pos);
}
if (has_projectile) {
world.scheduleBlockTick(pos, this, 30);
}
}
private void updateNeighbors(BlockState state, World world, BlockPos pos) {
world.updateNeighborsAlways(pos, this);
world.updateNeighborsAlways(pos.offset(getDirection(state).getOpposite()), this);
}
protected static Direction getDirection(BlockState state) {
return switch (state.get(BLOCK_FACE)) {
case CEILING -> Direction.DOWN;
case FLOOR -> Direction.UP;
default -> state.get(HORIZONTAL_FACING);
};
}
static {
VoxelShape SHAPE = createCuboidShape(5, 0, 6, 11, 2, 10);
VoxelShape POWERED_SHAPE = createCuboidShape(5, 0, 6, 11, 1, 10);
BUTTON_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 24)
.add()
.add(0, VoxelHelper::rotateY)
.add()
.add(VoxelHelper::rotateZ, VoxelHelper::rotateY)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.add(0, VoxelHelper::mirrorY)
.add()
.add(2, VoxelHelper::mirrorY)
.add()
.add(POWERED_SHAPE)
.add()
.add(12, VoxelHelper::rotateY)
.add()
.add(VoxelHelper::rotateZ, VoxelHelper::rotateY)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.add(12, VoxelHelper::mirrorY)
.add()
.add(13, VoxelHelper::mirrorY)
.add()
.build();
}
} }

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -1,89 +1,274 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects; import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil; import net.minecraft.block.*;
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.block.entity.BlockEntity;
import net.minecraft.block.enums.DoorHinge;
import net.minecraft.block.enums.DoubleBlockHalf;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundCategory;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.WorldView;
import net.minecraft.world.event.GameEvent;
import net.minecraft.world.explosion.Explosion;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class ReFramedDoorBlock extends DoorBlock implements BlockEntityProvider { import java.util.function.BiConsumer;
public ReFramedDoorBlock(Settings settings, BlockSetType blockSetType) {
super(blockSetType, settings); import static net.minecraft.state.property.Properties.*;
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
} public class ReFramedDoorBlock extends WaterloggableReFramedBlock {
@Nullable public static final VoxelShape[] DOOR_VOXELS;
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { public ReFramedDoorBlock(Settings settings) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); super(settings);
} setDefaultState(getDefaultState()
.with(HORIZONTAL_FACING, Direction.NORTH)
@Override .with(DOOR_HINGE, DoorHinge.LEFT)
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { .with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER)
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder)); .with(OPEN, false)
} .with(POWERED, false)
);
@Nullable }
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) { @Override
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx); protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
} super.appendProperties(builder.add(HORIZONTAL_FACING, DOOR_HINGE, DOUBLE_BLOCK_HALF, OPEN, POWERED));
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { @Override
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit); @SuppressWarnings("deprecation")
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit); public boolean canPlaceAt(BlockState state, WorldView world, BlockPos pos) {
return r; BlockPos pos_down = pos.down();
} BlockState state_down = world.getBlockState(pos_down);
return state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER ? !state_down.isAir() : state_down.isOf(this);
@Override }
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved); @Override
super.onStateReplaced(state, world, pos, newState, moved); @SuppressWarnings("deprecation")
} public void neighborUpdate(BlockState state, World world, BlockPos pos, Block source, BlockPos sourcePos, boolean notify) {
if (world.isClient) return;
@Override boolean powered = world.isReceivingRedstonePower(pos)
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { || world.isReceivingRedstonePower(
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack); state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER
super.onPlaced(world, pos, state, placer, stack); ? pos.up()
} : pos.down()
);
@Override if (!getDefaultState().isOf(source) && powered != state.get(POWERED)) {
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { if (state.get(OPEN) != powered)
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx)); playToggleSound(null, world, pos, powered);
}
world.setBlockState(pos, state.with(POWERED, powered).with(OPEN, powered), 2);
@Override if (state.get(WATERLOGGED)) {
public boolean emitsRedstonePower(BlockState state) { world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
return ReFramedInteractionUtil.emitsRedstonePower(state); }
} }
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { @Override
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir); public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
} World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
@Override Direction facing = ctx.getHorizontalPlayerFacing().getOpposite();
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { if (pos.getY() >= world.getTopY() - 1 || !world.getBlockState(pos.up()).canReplace(ctx)) return null;
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir); BlockState state = super.getPlacementState(ctx)
} .with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER)
} .with(HORIZONTAL_FACING, facing);
if (world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.up()))
state = state.with(OPEN, true).with(POWERED, true);
return state.with(DOOR_HINGE, getHinge(facing, pos, world, BlockHelper.getRelativePos(ctx.getHitPos(), pos)));
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack, BlockState old_state, BlockEntity old_entity) {
world.setBlockState(
pos.up(),
state
.with(DOUBLE_BLOCK_HALF, DoubleBlockHalf.UPPER)
.with(WATERLOGGED, world.getFluidState(pos.up()).isOf(Fluids.WATER)),
3
);
}
@Override
public BlockState onBreak(World world, BlockPos pos, BlockState state, PlayerEntity player) {
if (!world.isClient() && (player.isCreative() || player.canHarvest(state))) {
DoubleBlockHalf half = state.get(DOUBLE_BLOCK_HALF);
BlockPos other_pos = half == DoubleBlockHalf.LOWER ? pos.up() : pos.down();
BlockState other_state = world.getBlockState(other_pos);
if (other_state.isOf(this) && other_state.get(DOUBLE_BLOCK_HALF) != half) {
world.setBlockState(other_pos, other_state.get(WATERLOGGED) ? Blocks.WATER.getDefaultState(): Blocks.AIR.getDefaultState(), 35);
world.syncWorldEvent(player, 2001, other_pos, Block.getRawIdFromState(other_state));
}
}
return super.onBreak(world, pos, state, player);
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState other, WorldAccess world, BlockPos pos, BlockPos moved) {
if (direction.getAxis() == Direction.Axis.Y
&& other.isOf(this)
&& other.get(DOUBLE_BLOCK_HALF) != state.get(DOUBLE_BLOCK_HALF)
&& other.get(OPEN) != state.get(OPEN)
) return state.cycle(OPEN);
Direction facing = state.get(HORIZONTAL_FACING);
if (direction == (
state.get(DOOR_HINGE) == DoorHinge.RIGHT
? facing.rotateYClockwise()
: facing.rotateYCounterclockwise())
&& other.isOf(this)
&& other.get(DOUBLE_BLOCK_HALF) == state.get(DOUBLE_BLOCK_HALF)
&& other.get(DOOR_HINGE) != state.get(DOOR_HINGE)
&& !state.get(POWERED)
) return state.with(OPEN, other.get(OPEN));
return super.getStateForNeighborUpdate(state, direction, other, world, pos, moved);
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult result = super.onUse(state, world, pos, player, hand, hit);
if (result.isAccepted()) return result;
flip(state, world, pos, player);
return ActionResult.success(world.isClient);
}
@Override
@SuppressWarnings("deprecation")
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return switch (type) {
case LAND, AIR -> state.get(OPEN);
case WATER -> false;
};
}
@Override
@SuppressWarnings("deprecation")
public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stack_merger) {
if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK
&& !world.isClient()
&& !state.get(POWERED)
) flip(state, world, pos, null);
super.onExploded(state, world, pos, explosion, stack_merger);
}
private void flip(BlockState state, World world, BlockPos pos, @Nullable PlayerEntity player) {
state = state.cycle(OPEN);
world.setBlockState(pos, state, 10);
this.playToggleSound(player, world, pos, state.get(OPEN));
}
protected void playToggleSound(@Nullable PlayerEntity player, World world, BlockPos pos, boolean open) {
world.playSound(player, pos, open ? BlockSetType.OAK.doorOpen() : BlockSetType.OAK.doorClose(), SoundCategory.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F);
world.emitGameEvent(player, open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
Direction direction = state.get(HORIZONTAL_FACING);
if (state.get(OPEN)) direction = switch (state.get(DOOR_HINGE)) {
case RIGHT -> direction.rotateYCounterclockwise();
case LEFT -> direction.rotateYClockwise();
};
return DOOR_VOXELS[direction.ordinal() - 2];
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(HORIZONTAL_FACING, rotation.rotate(state.get(HORIZONTAL_FACING)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return mirror == BlockMirror.NONE ? state : state.with(HORIZONTAL_FACING, mirror.apply(state.get(HORIZONTAL_FACING))).cycle(DOOR_HINGE);
}
@Override
@SuppressWarnings("deprecation")
public long getRenderingSeed(BlockState state, BlockPos pos) {
return MathHelper.hashCode(pos.getX(), pos.down(state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER ? 0 : 1).getY(), pos.getZ());
}
private DoorHinge getHinge(Direction facing, BlockPos pos, World world, Vec3d hit_pos) {
Direction left = facing.rotateYClockwise();
BlockPos left_pos = pos.offset(left);
BlockState left_state = world.getBlockState(left_pos);
Direction right = facing.rotateYCounterclockwise();
BlockPos right_pos = pos.offset(right);
BlockState right_state = world.getBlockState(right_pos);
DoorHinge hinge = null;
if (left_state.isSideSolidFullSquare(world, left_pos, right))
hinge = DoorHinge.LEFT;
if (right_state.isSideSolidFullSquare(world, right_pos, left))
hinge = hinge == DoorHinge.LEFT ? null : DoorHinge.RIGHT;
if (hinge != null) return hinge;
if (left_state.isOf(this)
&& left_state.get(HORIZONTAL_FACING) == facing
&& left_state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER
&& left_state.get(DOOR_HINGE) == DoorHinge.LEFT
) hinge = DoorHinge.RIGHT;
if (right_state.isOf(this)
&& right_state.get(HORIZONTAL_FACING) == facing
&& right_state.get(DOUBLE_BLOCK_HALF) == DoubleBlockHalf.LOWER
&& right_state.get(DOOR_HINGE) == DoorHinge.RIGHT
) hinge = hinge == DoorHinge.RIGHT ? null : DoorHinge.LEFT;
if (hinge != null) return hinge;
return switch (facing.getAxis()) {
case Z -> {
if (left.getDirection() == Direction.AxisDirection.POSITIVE)
yield hit_pos.getX() < 0.5 ? DoorHinge.RIGHT : DoorHinge.LEFT;
else // left.getDirection() == Direction.AxisDirection.NEGATIVE
yield hit_pos.getX() < 0.5 ? DoorHinge.LEFT : DoorHinge.RIGHT;
}
case X -> {
if (left.getDirection() == Direction.AxisDirection.POSITIVE)
yield hit_pos.getZ() < 0.5 ? DoorHinge.RIGHT : DoorHinge.LEFT;
else // left.getDirection() == Direction.AxisDirection.NEGATIVE
yield hit_pos.getZ() < 0.5 ? DoorHinge.LEFT : DoorHinge.RIGHT;
}
default -> DoorHinge.LEFT;
};
}
static {
VoxelShape SHAPE = createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 3.0);
DOOR_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 4)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.build();
}
}

View File

@@ -0,0 +1,78 @@
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;
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 isGhost(view, pos) ? empty() : getOutlineShape(state, view, pos, ctx);
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return getCollisionShape(state, view, pos, ShapeContext.absent());
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if (!canUse(world, pos, player)) return ActionResult.PASS;
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));
}
}

View File

@@ -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);
nbt.put(BLOCKSTATE_KEY + 2, NbtHelper.fromBlockState(second_state));
}
}

View File

@@ -1,8 +1,8 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed; import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil; import fr.adrien1106.reframed.util.blocks.BlockProperties;
import fr.adrien1106.reframed.util.ThemeableBlockEntity; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
@@ -21,172 +21,141 @@ import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects; import java.util.Objects;
//Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic, //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. //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 { public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity {
protected BlockState renderedState = Blocks.AIR.getDefaultState(); protected BlockState first_state = Blocks.AIR.getDefaultState();
protected byte bitfield = DEFAULT_BITFIELD; protected byte bit_field = SOLIDITY_MASK;
protected static final int SPENT_GLOWSTONE_DUST_MASK = 0b00000001; public static final byte LIGHT_MASK = 0b001;
protected static final int SPENT_REDSTONE_TORCH_MASK = 0b00000010; public static final byte REDSTONE_MASK = 0b010;
protected static final int SPENT_POPPED_CHORUS_MASK = 0b00000100; public static final byte SOLIDITY_MASK = 0b100;
protected static final int EMITS_REDSTONE_MASK = 0b00001000;
protected static final int IS_SOLID_MASK = 0b00010000; public static final String BLOCKSTATE_KEY = "s";
protected static final byte DEFAULT_BITFIELD = IS_SOLID_MASK; //brand-new frames shall be solid public static final String BITFIELD_KEY = "b";
//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";
protected static final String BITFIELD_KEY = "b";
public ReFramedEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) { public ReFramedEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state); super(type, pos, state);
} }
@Override @Override
public void readNbt(NbtCompound tag) { public void readNbt(NbtCompound nbt) {
super.readNbt(tag); 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 // Force a chunk remesh on the client if the displayed blockstate has changed
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState")); if(world != null && world.isClient && !Objects.equals(rendered_state, first_state))
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)) {
ReFramed.chunkRerenderProxy.accept(world, pos); ReFramed.chunkRerenderProxy.accept(world, pos);
}
} }
@Override @Override
public void writeNbt(NbtCompound tag) { public void writeNbt(NbtCompound nbt) {
super.writeNbt(tag); super.writeNbt(nbt);
if(renderedState != Blocks.AIR.getDefaultState()) tag.put(BLOCKSTATE_KEY, NbtHelper.fromBlockState(renderedState)); nbt.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(first_state));
if(bitfield != DEFAULT_BITFIELD) tag.putByte(BITFIELD_KEY, bitfield); if(bit_field != SOLIDITY_MASK) nbt.putByte(BITFIELD_KEY, bit_field);
} }
public static @NotNull BlockState readStateFromItem(ItemStack stack) { public static @NotNull BlockState readStateFromItem(ItemStack stack, int state) {
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack); NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
if(blockEntityTag == null) return Blocks.AIR.getDefaultState(); if(nbt == null) return Blocks.AIR.getDefaultState();
//slightly paranoid NBT handling cause you never know what mysteries are afoot with items //slightly paranoid NBT handling cause you never know what mysteries are afoot with items
NbtElement subElement; NbtElement element;
if(blockEntityTag.contains(BLOCKSTATE_KEY)) subElement = blockEntityTag.get(BLOCKSTATE_KEY); //2.0.5 if(nbt.contains(BLOCKSTATE_KEY + state)) element = nbt.get(BLOCKSTATE_KEY + state);
else if(blockEntityTag.contains("BlockState")) subElement = blockEntityTag.get("BlockState"); //old 2.0.4 items
else return Blocks.AIR.getDefaultState(); else return Blocks.AIR.getDefaultState();
if(!(subElement instanceof NbtCompound subCompound)) return Blocks.AIR.getDefaultState(); if(!(element instanceof NbtCompound compound)) return Blocks.AIR.getDefaultState();
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), compound);
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), subCompound);
} }
//Awkward: usually the BlockState is the source of truth for things like the "emits light" blockstate, but if you //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 //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 //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" //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; if(state == null || stack == null) return state;
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack); NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
if(blockEntityTag == null) return state; if(nbt == null) return state;
if(state.contains(ReFramedInteractionUtil.LIGHT)) { if(state.contains(BlockProperties.LIGHT)) {
state = state.with(ReFramedInteractionUtil.LIGHT, state = state.with(BlockProperties.LIGHT,
blockEntityTag.getBoolean("spentglow") || //2.0.4 ((nbt.contains(BITFIELD_KEY)
((blockEntityTag.contains(BITFIELD_KEY) ? blockEntityTag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD) & SPENT_GLOWSTONE_DUST_MASK) != 0 || //2.0.5 ? nbt.getByte(BITFIELD_KEY)
readStateFromItem(stack).getLuminance() != 0 //glowstone dust wasn't manually added, the block just emits light : SOLIDITY_MASK)
& LIGHT_MASK) != 0
); );
} }
return state; return state;
} }
//RenderAttachmentBlockEntity impl. Note that ThemeableBlockEntity depends on this returning a BlockState object.
@Override @Override
public BlockState getRenderAttachmentData() { public BlockState getTheme(int i) {
return renderedState; return first_state;
} }
public void setRenderedState(BlockState newState) { @Override
if(!Objects.equals(renderedState, newState)) { public List<BlockState> getThemes() {
renderedState = newState; 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) && i == 1) {
first_state = new_state;
markDirtyAndDispatch(); markDirtyAndDispatch();
} }
} }
public boolean hasSpentGlowstoneDust() { /* --------------------------------------------------- ADDONS --------------------------------------------------- */
return (bitfield & SPENT_GLOWSTONE_DUST_MASK) != 0; public boolean emitsLight() {
return (bit_field & LIGHT_MASK) != 0;
} }
public void spentGlowstoneDust() { public void toggleLight() {
bitfield |= SPENT_GLOWSTONE_DUST_MASK; if (emitsLight()) bit_field &= ~LIGHT_MASK;
else bit_field |= LIGHT_MASK;
markDirtyAndDispatch(); markDirtyAndDispatch();
} }
public boolean hasSpentRedstoneTorch() { public void toggleRedstone() {
return (bitfield & SPENT_REDSTONE_TORCH_MASK) != 0; if (emitsRedstone()) bit_field &= ~REDSTONE_MASK;
} else bit_field |= REDSTONE_MASK;
public void spentRedstoneTorch() { if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
bitfield |= SPENT_REDSTONE_TORCH_MASK;
markDirtyAndDispatch(); markDirtyAndDispatch();
} }
public boolean hasSpentPoppedChorus() {
return (bitfield & SPENT_POPPED_CHORUS_MASK) != 0;
}
public void spentPoppedChorus() {
bitfield |= SPENT_POPPED_CHORUS_MASK;
markDirtyAndDispatch();
}
public boolean emitsRedstone() { public boolean emitsRedstone() {
return (bitfield & EMITS_REDSTONE_MASK) != 0; return (bit_field & REDSTONE_MASK) != 0;
} }
public void setEmitsRedstone(boolean nextEmitsRedstone) { public void toggleSolidity() {
boolean currentlyEmitsRedstone = emitsRedstone(); if (isSolid()) bit_field &= ~SOLIDITY_MASK;
else bit_field |= SOLIDITY_MASK;
if(currentlyEmitsRedstone != nextEmitsRedstone) {
if(currentlyEmitsRedstone) bitfield &= ~EMITS_REDSTONE_MASK; if(world != null) {
else bitfield |= EMITS_REDSTONE_MASK; world.setBlockState(pos, getCachedState());
markDirtyAndDispatch(); ReFramed.chunkRerenderProxy.accept(world, pos);
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
} }
markDirtyAndDispatch();
} }
public boolean isSolid() { 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 @Nullable
@Override @Override
public Packet<ClientPlayPacketListener> toUpdatePacket() { public Packet<ClientPlayPacketListener> toUpdatePacket() {
@@ -195,8 +164,6 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
@Override @Override
public NbtCompound toInitialChunkDataNbt() { 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(); return createNbt();
} }
@@ -208,5 +175,4 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
markDirty(); markDirty();
dispatch(); dispatch();
} }
//</standard blockentity boilerplate>
} }

View File

@@ -1,87 +1,108 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects; import fr.adrien1106.reframed.util.VoxelHelper;
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.BlockState;
import net.minecraft.block.FenceBlock; import net.minecraft.block.FenceGateBlock;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager; import net.minecraft.item.Items;
import net.minecraft.item.LeadItem;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class ReFramedFenceBlock extends FenceBlock implements BlockEntityProvider { public class ReFramedFenceBlock extends ConnectingReFramedBlock {
public ReFramedFenceBlock(Settings settings) {
super(settings); public static final VoxelShape[] FENCE_VOXELS;
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
} public ReFramedFenceBlock(Settings settings) {
super(settings);
@Nullable }
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { @Override
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); protected boolean connectsTo(BlockState state, boolean fs, Direction dir) {
} return fs || state.isIn(BlockTags.FENCES)
|| (state.getBlock() instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, dir));
@Override }
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder)); @Override
} public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
VoxelShape shape = FENCE_VOXELS[0];
@Nullable for (Direction dir: Direction.Type.HORIZONTAL) {
@Override if (state.get(getConnectionProperty(dir)))
public BlockState getPlacementState(ItemPlacementContext ctx) { shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]);
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx); }
} return shape;
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { @Override
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit); public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit); VoxelShape shape = FENCE_VOXELS[5];
return r; for (Direction dir: Direction.Type.HORIZONTAL) {
} if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() + 4]);
@Override }
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { return shape;
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved); }
super.onStateReplaced(state, world, pos, newState, moved);
} @Override
@SuppressWarnings("deprecation")
@Override public VoxelShape getCameraCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { return getOutlineShape(state, world, pos, context);
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack); }
}
@Override
@Override public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { return getOutlineShape(state, view, pos, ShapeContext.absent());
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx)); }
}
@Override
@Override @SuppressWarnings("deprecation")
public boolean emitsRedstonePower(BlockState state) { public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return ReFramedInteractionUtil.emitsRedstonePower(state); return false;
} }
@Override @Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir); ActionResult result = super.onUse(state, world, pos, player, hand, hit);
} if (result.isAccepted()) return result;
if (world.isClient) {
@Override ItemStack itemStack = player.getStackInHand(hand);
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { return itemStack.isOf(Items.LEAD) ? ActionResult.SUCCESS : ActionResult.PASS;
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir); } else {
} return LeadItem.attachHeldMobsToBlock(player, world, pos);
}
}
static {
VoxelShape POST = createCuboidShape(6, 0, 6, 10, 16, 10);
VoxelShape POST_COLLISION = createCuboidShape(6, 0, 6, 10, 24, 10);
VoxelShape SIDE = VoxelShapes.combineAndSimplify(
createCuboidShape(7, 12, 0, 9, 15, 6),
createCuboidShape(7, 6, 0, 9, 9, 6),
BooleanBiFunction.OR
);
VoxelShape SIDE_COLLISION = createCuboidShape(7, 0, 0, 9, 24, 6);
FENCE_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 5)
.add(SIDE)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.add(POST_COLLISION)
.add(SIDE_COLLISION)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.build();
}
} }

View File

@@ -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;
}
}

View File

@@ -0,0 +1,188 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Edge;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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 java.util.Map;
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.EDGE_FACE;
import static net.minecraft.state.property.Properties.*;
public class ReFramedHalfLayerBlock extends LayeredReFramedBlock {
public static final VoxelShape[] HALF_LAYER_VOXELS;
public ReFramedHalfLayerBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(EDGE_FACE, 0));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE, EDGE_FACE));
}
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (super.canReplace(state, context)) return true;
if (context.getPlayer() == null
|| context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
) return false;
Edge edge = state.get(EDGE);
Direction face = edge.getDirection(state.get(EDGE_FACE));
if (block_item.getBlock() == ReFramed.SLAB)
return ReFramed.SLAB
.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.SLAB.getDefaultState().with(FACING, edge.getOtherDirection(face).getOpposite())
);
if (block_item.getBlock() == ReFramed.STEP)
return ReFramed.STEP
.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.STEP.getDefaultState().with(EDGE, edge.getOpposite(face))
);
return false;
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getHalfLayerShape(
state.get(EDGE),
state.get(EDGE_FACE),
state.get(LAYERS)
);
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState previous = ctx.getWorld().getBlockState(ctx.getBlockPos());
BlockState state = super.getPlacementState(ctx);
if (previous.isOf(this))
return state;
if (previous.isOf(ReFramed.SLABS_HALF_LAYER) || previous.isOf(ReFramed.STEPS_HALF_LAYER))
return previous.with(LAYERS, Math.min(8, previous.get(LAYERS) + 1));
if (previous.isOf(ReFramed.SLAB)) {
Direction face = previous.get(FACING);
Edge edge;
if (face.getAxis() == ctx.getSide().getAxis()) {
edge = BlockHelper.getPlacementEdge(ctx);
if (face == ctx.getSide()) edge = edge.getOpposite(edge.getOtherDirection(ctx.getSide()));
} else edge = Edge.getByDirections(face, ctx.getSide().getOpposite());
return ReFramed.SLABS_HALF_LAYER.getDefaultState()
.with(EDGE, edge)
.with(EDGE_FACE, edge.getDirectionIndex(face))
.with(WATERLOGGED, previous.get(WATERLOGGED));
}
if (previous.isOf(ReFramed.STEP)) {
int face_index = 0;
Edge edge = previous.get(EDGE);
if (!ReFramed.STEP.matchesShape(
ctx.getHitPos(),
ctx.getBlockPos(),
ReFramed.STEP.getDefaultState().with(EDGE, edge.getOpposite(1))
)) face_index = 1;
return ReFramed.STEPS_HALF_LAYER.getDefaultState()
.with(EDGE, edge)
.with(EDGE_FACE, face_index)
.with(WATERLOGGED, previous.get(WATERLOGGED));
}
Edge edge = BlockHelper.getPlacementEdge(ctx);
return state.with(EDGE, edge).with(EDGE_FACE, edge.getDirectionIndex(ctx.getSide().getOpposite()));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
Edge edge = state.get(EDGE);
Direction face = rotation.rotate(edge.getDirection(state.get(EDGE_FACE)));
edge = edge.rotate(rotation);
return state.with(EDGE, edge).with(EDGE_FACE, edge.getDirectionIndex(face));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
Edge edge = state.get(EDGE);
Direction face = mirror.apply(edge.getDirection(state.get(EDGE_FACE)));
edge = edge.mirror(mirror);
return state.with(EDGE, edge).with(EDGE_FACE, edge.getDirectionIndex(face));
}
@Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
if (new_state.isOf(ReFramed.SLABS_HALF_LAYER)
|| new_state.isOf(ReFramed.STEPS_HALF_LAYER)
) return Map.of(1, 2);
return super.getThemeMap(state, new_state);
}
public static VoxelShape getHalfLayerShape(Edge edge, int face, int layer) {
return HALF_LAYER_VOXELS[edge.ordinal() * 16 + face * 8 + layer - 1];
}
static {
VoxelListBuilder builder = VoxelListBuilder.create(createCuboidShape(0, 0, 0, 16, 8, 2), 192)
.add(createCuboidShape(0, 0, 0, 16, 8, 4))
.add(createCuboidShape(0, 0, 0, 16, 8, 6))
.add(createCuboidShape(0, 0, 0, 16, 8, 8))
.add(createCuboidShape(0, 0, 0, 16, 8, 10))
.add(createCuboidShape(0, 0, 0, 16, 8, 12))
.add(createCuboidShape(0, 0, 0, 16, 8, 14))
.add(createCuboidShape(0, 0, 0, 16, 8, 16));
for (int i = 0; i < 8; i++) {
builder.add(i, VoxelHelper::rotateCX, VoxelHelper::mirrorZ);
}
for (int i = 0; i < 48; i++) {
builder.add(i, VoxelHelper::rotateCX);
}
for (int i = 0; i < 64; i++) {
builder.add(i, VoxelHelper::rotateCY);
}
for (int i = 64; i < 80; i++) {
builder.add(i, VoxelHelper::rotateX);
}
for (int i = 80; i < 96; i++) {
builder.add(i, VoxelHelper::rotateX);
}
for (int i = 96; i < 112; i++) {
builder.add(i, VoxelHelper::rotateX);
}
for (int i = 112; i < 128; i++) {
builder.add(i, VoxelHelper::rotateX);
}
HALF_LAYER_VOXELS = builder.build();
}
}

View File

@@ -0,0 +1,86 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
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 java.util.Map;
import static net.minecraft.state.property.Properties.*;
public class ReFramedHalfSlabBlock extends ReFramedSlabBlock {
public static VoxelShape[] HALF_SLAB_SHAPES;
public ReFramedHalfSlabBlock(Settings settings) {
super(settings);
}
@Override
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null
|| context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
) return false;
// allow replacing with slab, step, small cube and half stair
Block block = block_item.getBlock();
if (block != this) return false;
// check if the player is clicking on the inner part of the block
return ReFramed.HALF_SLABS_SLAB
.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.HALF_SLABS_SLAB.getDefaultState().with(FACING, state.get(FACING)),
2
);
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState current_state = ctx.getWorld().getBlockState(ctx.getBlockPos());
if (current_state.isOf(this))
return ReFramed.HALF_SLABS_SLAB.getDefaultState()
.with(FACING, current_state.get(FACING))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
return super.getPlacementState(ctx).with(FACING, ctx.getSide().getOpposite());
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getHalfSlabShape(state.get(FACING));
}
public static VoxelShape getHalfSlabShape(Direction direction) {
return HALF_SLAB_SHAPES[direction.getId()];
}
@Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
if (new_state.isOf(ReFramed.HALF_SLABS_SLAB)) return Map.of(1, 1);
return super.getThemeMap(state, new_state);
}
static {
HALF_SLAB_SHAPES = VoxelHelper.VoxelListBuilder.create(createCuboidShape(0, 0, 0, 16, 4, 16),6)
.add(createCuboidShape(0, 12, 0, 16, 16, 16))
.add(createCuboidShape(0, 0, 0, 16, 16, 4))
.add(createCuboidShape(0, 0, 12, 16, 16, 16))
.add(createCuboidShape(0, 0, 0, 4, 16, 16))
.add(createCuboidShape(12, 0, 0, 16, 16, 16))
.build();
}
}

View File

@@ -0,0 +1,45 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import static fr.adrien1106.reframed.block.ReFramedHalfSlabBlock.getHalfSlabShape;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
import static net.minecraft.state.property.Properties.FACING;
public class ReFramedHalfSlabsSlabBlock extends FacingDoubleReFramedBlock {
public static VoxelShape[] HALF_SLAB_COMP_SHAPES;
public ReFramedHalfSlabsSlabBlock(Settings settings) {
super(settings);
}
@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) {
Direction face = state.get(FACING);
return i == 2
? HALF_SLAB_COMP_SHAPES[face.getId()]
: getHalfSlabShape(face);
}
static {
HALF_SLAB_COMP_SHAPES = VoxelHelper.VoxelListBuilder.create(createCuboidShape(0, 4, 0, 16, 8, 16),6)
.add(createCuboidShape(0, 8, 0, 16, 12, 16))
.add(createCuboidShape(0, 0, 4, 16, 16, 8))
.add(createCuboidShape(0, 0, 8, 16, 16, 12))
.add(createCuboidShape(4, 0, 0, 8, 16, 16))
.add(createCuboidShape(8, 0, 0, 12, 16, 16))
.build();
}
}

View File

@@ -0,0 +1,199 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Corner;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.jetbrains.annotations.Nullable;
import java.util.Map;
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.*;
import static fr.adrien1106.reframed.util.blocks.Corner.*;
import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.state.property.Properties.WATERLOGGED;
public class ReFramedHalfStairBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] HALF_STAIR_VOXELS;
public ReFramedHalfStairBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN).with(CORNER_FACE, 0));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER,CORNER_FACE));
}
@Override
@SuppressWarnings("deprecation")
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null
|| context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
) return false;
// allow replacing with slab, step, small cube and half stair
Block block = block_item.getBlock();
Corner corner = state.get(CORNER);
Direction dir = corner.getDirection(state.get(CORNER_FACE));
if (block == this || block == ReFramed.STEP)
return ReFramed.HALF_STAIRS_STAIR.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.HALF_STAIRS_STAIR.getDefaultState().with(EDGE, corner.getEdge(dir)),
dir.getDirection() == Direction.AxisDirection.POSITIVE ? 1 : 2
);
if (block == ReFramed.SMALL_CUBE)
return ReFramed.SMALL_CUBE.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.SMALL_CUBE.getDefaultState().with(CORNER, corner.change(dir))
) || ReFramed.SMALL_CUBE.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.SMALL_CUBE.getDefaultState().with(CORNER, corner.getOpposite(dir))
);
if (block == ReFramed.SLAB)
return ReFramed.SLAB.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.SLAB.getDefaultState().with(FACING, dir.getOpposite())
);
return false;
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState current_state = ctx.getWorld().getBlockState(ctx.getBlockPos());
if (current_state.isOf(ReFramed.SMALL_CUBE)) {
Corner corner = current_state.get(CORNER).getOpposite(ctx.getSide().getOpposite());
return ReFramed.HALF_STAIRS_SLAB.getDefaultState()
.with(CORNER, corner)
.with(CORNER_FACE, corner.getDirectionIndex(ctx.getSide().getOpposite()))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
if (current_state.isOf(this))
return ReFramed.HALF_STAIRS_STAIR.getDefaultState()
.with(EDGE, current_state.get(CORNER).getEdge(current_state.get(CORNER).getDirection(current_state.get(CORNER_FACE))))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
if (current_state.isOf(ReFramed.SLAB)) {
Corner corner = BlockHelper.getPlacementCorner(ctx);
Direction face = current_state.get(FACING);
if (!corner.hasDirection(face)) corner = corner.change(face.getOpposite());
return ReFramed.SLABS_INNER_STAIR.getDefaultState()
.with(CORNER, corner)
.with(CORNER_FACE, corner.getDirectionIndex(face))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
Corner corner = BlockHelper.getPlacementCorner(ctx);
return super.getPlacementState(ctx)
.with(CORNER, corner)
.with(CORNER_FACE, corner.getDirectionIndex(ctx.getSide().getOpposite()));
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getHalfStairShape(state.get(CORNER), state.get(CORNER_FACE));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
Corner corner = state.get(CORNER).rotate(rotation);
Direction face = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return state.with(CORNER, corner).with(CORNER_FACE, corner.getDirectionIndex(rotation.rotate(face)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
Corner corner = state.get(CORNER).mirror(mirror);
Direction face = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return state.with(CORNER, corner).with(CORNER_FACE, corner.getDirectionIndex(mirror.apply(face)));
}
@Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
if (new_state.isOf(ReFramed.HALF_STAIRS_SLAB)
|| new_state.isOf(ReFramed.HALF_STAIRS_CUBE_STAIR)
|| new_state.isOf(ReFramed.HALF_STAIRS_STEP_STAIR)
) return Map.of(1, 1);
if (new_state.isOf(ReFramed.SLABS_INNER_STAIR)) return Map.of(1, 2);
if (new_state.isOf(ReFramed.HALF_STAIRS_STAIR))
return Map.of(
1,
state.get(CORNER)
.getDirection(state.get(CORNER_FACE))
.getDirection() == Direction.AxisDirection.POSITIVE ? 2 : 1
);
return super.getThemeMap(state, new_state);
}
public static VoxelShape getHalfStairShape(Corner corner, int face) {
return HALF_STAIR_VOXELS[face + corner.getID() * 3];
}
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();
}
}

View File

@@ -0,0 +1,48 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.Corner;
import fr.adrien1106.reframed.util.blocks.Edge;
import fr.adrien1106.reframed.util.blocks.StairShape;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import static fr.adrien1106.reframed.block.ReFramedHalfStairBlock.getHalfStairShape;
import static fr.adrien1106.reframed.block.ReFramedSmallCubeBlock.getSmallCubeShape;
import static fr.adrien1106.reframed.block.ReFramedStairBlock.getStairShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER_FACE;
public class ReFramedHalfStairsCubeStairBlock extends CornerDoubleReFramedBlock {
public ReFramedHalfStairsCubeStairBlock(Settings settings) {
super(settings);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
Corner corner = state.get(CORNER);
Direction face = corner.getDirection(state.get(CORNER_FACE));
Edge edge = corner.getEdge(face);
return getStairShape(
edge,
face.getDirection() == Direction.AxisDirection.POSITIVE
? StairShape.OUTER_LEFT
: StairShape.OUTER_RIGHT
);
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Corner corner = state.get(CORNER);
Direction face = corner.getDirection(state.get(CORNER_FACE));
if (i == 2) corner = corner.change(face);
return i == 2
? getSmallCubeShape(corner)
: getHalfStairShape(corner, state.get(CORNER_FACE));
}
}

View File

@@ -0,0 +1,75 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Corner;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.getHalfStairShape;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
import static fr.adrien1106.reframed.block.ReFramedSmallCubeBlock.getSmallCubeShape;
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;
public class ReFramedHalfStairsSlabBlock extends WaterloggableReFramedDoubleBlock {
public ReFramedHalfStairsSlabBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN).with(CORNER_FACE, 0));
}
@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
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getSlabShape(state.get(CORNER).getDirection(state.get(CORNER_FACE)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
Corner corner = state.get(CORNER).rotate(rotation);
Direction face = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return state.with(CORNER, corner).with(CORNER_FACE, corner.getDirectionIndex(rotation.rotate(face)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
Corner corner = state.get(CORNER).mirror(mirror);
Direction face = state.get(CORNER).getDirection(state.get(CORNER_FACE));
return state.with(CORNER, corner).with(CORNER_FACE, corner.getDirectionIndex(mirror.apply(face)));
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Corner corner = state.get(CORNER);
int face = state.get(CORNER_FACE);
return i == 2
? getSmallCubeShape(corner.getOpposite(face))
: getHalfStairShape(corner, face);
}
}

View File

@@ -0,0 +1,79 @@
package fr.adrien1106.reframed.block;
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.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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.getHalfStairShape;
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.*;
public class ReFramedHalfStairsStairBlock extends WaterloggableReFramedDoubleBlock {
public ReFramedHalfStairsStairBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(EDGE, NORTH_DOWN));
}
@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
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getStairShape(state.get(EDGE), StairShape.STRAIGHT);
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(EDGE, state.get(EDGE).rotate(rotation));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(EDGE, state.get(EDGE).mirror(mirror));
}
@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 getHalfStairShape(corner, corner.getDirectionIndex(side));
}
@Override
public int getTopThemeIndex(BlockState state) {
return 2;
}
}

View File

@@ -0,0 +1,76 @@
package fr.adrien1106.reframed.block;
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.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.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.getHalfStairShape;
import static fr.adrien1106.reframed.block.ReFramedStairBlock.getStairShape;
import static fr.adrien1106.reframed.block.ReFramedStepBlock.getStepShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.*;
public class ReFramedHalfStairsStepStairBlock extends CornerDoubleReFramedBlock {
public ReFramedHalfStairsStepStairBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(CORNER_FEATURE, 0));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER_FEATURE));
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
Corner corner = state.get(CORNER);
int face_index = state.get(CORNER_FACE);
Direction face = corner.getDirection(face_index);
face = BlockHelper.getPlacementEdge(ctx).getOtherDirection(face);
int feature_index = corner.getDirectionIndex(face);
return state.with(CORNER_FEATURE, feature_index > face_index ? feature_index - 1 : feature_index);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
Corner corner = state.get(CORNER);
int feature_index = state.get(CORNER_FEATURE), face_index = state.get(CORNER_FACE);
Direction feature_face = corner.getDirection(feature_index >= face_index ? feature_index + 1 : feature_index);
Direction face = corner.getDirection(face_index);
Edge edge = Edge.getByDirections(feature_face, face);
return getStairShape(
edge,
corner.getOtherDirection(edge).getDirection() == Direction.AxisDirection.POSITIVE
? edge.getDirectionIndex(face) == 0
? StairShape.FIRST_OUTER_LEFT
: StairShape.SECOND_OUTER_LEFT
: edge.getDirectionIndex(face) == 0
? StairShape.FIRST_OUTER_RIGHT
: StairShape.SECOND_OUTER_RIGHT
);
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Corner corner = state.get(CORNER);
int feature_index = state.get(CORNER_FEATURE), face_index = state.get(CORNER_FACE);
Direction feature_face = corner.getDirection(feature_index >= face_index ? feature_index + 1 : feature_index);
Direction face = corner.getDirection(face_index);
return i == 2
? getStepShape(Edge.getByDirections(face.getOpposite(), feature_face))
: getHalfStairShape(corner, face_index);
}
}

View File

@@ -0,0 +1,102 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.VoxelHelper;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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 fr.adrien1106.reframed.util.blocks.BlockProperties.HALF_LAYERS;
import static net.minecraft.state.property.Properties.*;
public class ReFramedLayerBlock extends LayeredReFramedBlock {
public static final VoxelShape[] LAYER_VOXELS;
public ReFramedLayerBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(FACING));
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getLayerShape(state.get(FACING), state.get(LAYERS));
}
public static VoxelShape getLayerShape(Direction facing, int layers) {
return LAYER_VOXELS[facing.getId() * 8 + layers - 1];
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState previous = ctx.getWorld().getBlockState(ctx.getBlockPos());
BlockState state = super.getPlacementState(ctx);
if (previous.isOf(this)) return state;
if (previous.isOf(ReFramed.SLAB))
return ReFramed.SLABS_LAYER.getDefaultState()
.with(FACING, previous.get(FACING))
.with(WATERLOGGED, previous.get(WATERLOGGED));
if (previous.isOf(ReFramed.SLABS_LAYER))
return previous.with(HALF_LAYERS, previous.get(HALF_LAYERS) + 1);
return state.with(FACING, ctx.getSide().getOpposite());
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(FACING, mirror.apply(state.get(FACING)));
}
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();
}
}

View File

@@ -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);
}
}

View File

@@ -1,87 +1,47 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects; import fr.adrien1106.reframed.util.VoxelHelper;
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.BlockState;
import net.minecraft.block.PaneBlock; import net.minecraft.block.PaneBlock;
import net.minecraft.block.ShapeContext; import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.registry.tag.BlockTags;
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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class ReFramedPaneBlock extends PaneBlock implements BlockEntityProvider { public class ReFramedPaneBlock extends ConnectingReFramedBlock {
public ReFramedPaneBlock(Settings settings) {
super(settings); public static final VoxelShape[] PANE_VOXELS;
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
} public ReFramedPaneBlock(Settings settings) {
super(settings);
@Override }
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); @Override
} public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
VoxelShape shape = PANE_VOXELS[0];
@Nullable for (Direction dir: Direction.Type.HORIZONTAL) {
@Override if (state.get(getConnectionProperty(dir)))
public BlockState getPlacementState(ItemPlacementContext ctx) { shape = VoxelShapes.union(shape, PANE_VOXELS[dir.ordinal() - 1]);
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx); }
} return shape;
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { @Override
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder)); protected boolean connectsTo(BlockState state, boolean fs, Direction dir) {
} return !cannotConnect(state) && fs || state.getBlock() instanceof PaneBlock || state.isIn(BlockTags.WALLS);
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { static {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit); VoxelShape POST = createCuboidShape(7, 0, 7, 9, 16, 9);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit); VoxelShape SIDE = createCuboidShape(7, 0, 0, 9, 16, 7);
return r; PANE_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 5)
} .add(SIDE)
.add(VoxelHelper::mirrorZ)
@Override .add(VoxelHelper::rotateY)
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { .add(VoxelHelper::mirrorX)
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved); .build();
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);
}
} }

View File

@@ -0,0 +1,37 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedPillarBlock extends PillarReFramedBlock {
public static final VoxelShape[] PILLAR_VOXELS;
public ReFramedPillarBlock(Settings settings) {
super(settings);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getPillarShape(state.get(AXIS));
}
public static VoxelShape getPillarShape(Direction.Axis axis) {
return PILLAR_VOXELS[axis.ordinal()];
}
static {
final VoxelShape PILLAR = createCuboidShape(0, 4, 4, 16, 12, 12);
PILLAR_VOXELS = VoxelHelper.VoxelListBuilder.create(PILLAR, 3)
.add(VoxelHelper::rotateZ)
.add(VoxelHelper::rotateX)
.build();
}
}

View File

@@ -0,0 +1,129 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.enums.WallShape;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.Map;
import java.util.stream.Collectors;
import static fr.adrien1106.reframed.block.ReFramedWallBlock.*;
import static net.minecraft.state.property.Properties.*;
import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedPillarsWallBlock extends WaterloggableReFramedDoubleBlock {
public ReFramedPillarsWallBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(EAST_WALL_SHAPE, WallShape.NONE)
.with(NORTH_WALL_SHAPE, WallShape.NONE)
.with(WEST_WALL_SHAPE, WallShape.NONE)
.with(SOUTH_WALL_SHAPE, WallShape.NONE)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EAST_WALL_SHAPE, NORTH_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE));
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) {
BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved);
if (dir == Direction.DOWN) return new_state;
BlockState top_state = dir == Direction.UP? other_state: world.getBlockState(pos.up());
boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN);
VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN);
Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
.collect(Collectors.toMap(d -> d, d -> {
if (d == dir) return other_state;
return world.getBlockState(pos.offset(d));
}));
return getWallState(new_state, top_state, neighbors, top_shape, fs, world, pos);
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
BlockState top_state = world.getBlockState(pos.up());
boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN);
VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN);
Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
.collect(Collectors.toMap(d -> d, d -> world.getBlockState(pos.offset(d))));
return getWallState(state, top_state, neighbors, top_shape, fs, world, pos);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
if (isGhost(view, pos)) return empty();
VoxelShape shape = WALL_VOXELS[9];
for (Direction dir : Direction.Type.HORIZONTAL) {
if (state.get(getWallShape(dir)) != WallShape.NONE)
shape = VoxelShapes.union(shape, WALL_VOXELS[8 + dir.ordinal()]);
}
return shape;
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return isGhost(view, pos) ? empty(): getOutlineShape(state, view, pos, ShapeContext.absent());
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
VoxelShape shape = WALL_VOXELS[0];
for (Direction dir: Direction.Type.HORIZONTAL) {
WallShape wall_shape = state.get(getWallShape(dir));
if (wall_shape != WallShape.NONE)
shape = VoxelShapes.union(shape, WALL_VOXELS[1 + (wall_shape.ordinal()-1) * 4 + (dir.ordinal() - 2)]);
}
return shape;
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getWallShape(rotation.rotate(dir)), state.get(getWallShape(dir)))
, (prev, next) -> next);
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getWallShape(mirror.apply(dir)), state.get(getWallShape(dir)))
, (prev, next) -> next);
}
@Override
public VoxelShape getShape(BlockState state, int i) {
if (i == 1) return WALL_VOXELS[0];
VoxelShape shape = VoxelShapes.empty();
for (Direction dir: Direction.Type.HORIZONTAL) {
WallShape wall_shape = state.get(getWallShape(dir));
if (wall_shape != WallShape.NONE)
shape = VoxelShapes.union(shape, WALL_VOXELS[1 + (wall_shape.ordinal()-1) * 4 + (dir.ordinal() - 2)]);
}
return shape;
}
}

View File

@@ -1,57 +1,37 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects; import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext; 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.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
public class ReFramedPostBlock extends WaterloggableReFramedBlock { import static net.minecraft.state.property.Properties.AXIS;
public ReFramedPostBlock(Settings settings) {
super(settings); public class ReFramedPostBlock extends PillarReFramedBlock {
setDefaultState(getDefaultState().with(Properties.AXIS, Direction.Axis.Y)); public static final VoxelShape[] POST_VOXELS;
}
public ReFramedPostBlock(Settings settings) {
@Override super(settings);
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { }
super.appendProperties(builder.add(Properties.AXIS));
} @Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
@Override return getPillarShape(state.get(AXIS));
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) { }
BlockState sup = super.getPlacementState(ctx);
if(sup != null) sup = sup.with(Properties.AXIS, ctx.getSide().getAxis()); public static VoxelShape getPillarShape(Direction.Axis axis) {
return sup; return POST_VOXELS[axis.ordinal()];
} }
protected static final VoxelShape SHAPE_X = createCuboidShape(0, 6, 6, 16, 10, 10); static {
protected static final VoxelShape SHAPE_Y = createCuboidShape(6, 0, 6, 10, 16, 10); final VoxelShape POST = createCuboidShape(0, 6, 6, 16, 10, 10);
protected static final VoxelShape SHAPE_Z = createCuboidShape(6, 6, 0, 10, 10, 16); POST_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 3)
.add(VoxelHelper::rotateZ)
protected VoxelShape shap(BlockState state) { .add(VoxelHelper::rotateX)
return switch(state.get(Properties.AXIS)) { .build();
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);
}
} }

View File

@@ -0,0 +1,150 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.FenceGateBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.entity.ai.pathing.NavigationType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.item.LeadItem;
import net.minecraft.registry.tag.BlockTags;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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 net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
import static fr.adrien1106.reframed.block.ConnectingReFramedBlock.getConnectionProperty;
import static fr.adrien1106.reframed.block.ConnectingReFramedBlock.placementState;
import static fr.adrien1106.reframed.block.ReFramedFenceBlock.FENCE_VOXELS;
import static net.minecraft.state.property.Properties.*;
import static net.minecraft.state.property.Properties.WEST;
public class ReFramedPostFenceBlock extends WaterloggableReFramedDoubleBlock {
public ReFramedPostFenceBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState()
.with(EAST, false)
.with(NORTH, false)
.with(WEST, false)
.with(SOUTH, false)
);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EAST, NORTH, SOUTH, WEST));
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) {
BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved);
if (dir == Direction.DOWN) return new_state;
return placementState(new_state, world, pos, this::connectsTo);
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
return placementState(state, world, pos, this::connectsTo);
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getConnectionProperty(rotation.rotate(dir)), state.get(getConnectionProperty(dir)))
, (prev, next) -> next);
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
s.with(getConnectionProperty(mirror.apply(dir)), state.get(getConnectionProperty(dir)))
, (prev, next) -> next);
}
private boolean connectsTo(BlockState state, boolean fs, Direction dir) {
return fs || state.isIn(BlockTags.FENCES)
|| (state.getBlock() instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, dir));
}
@Override
public VoxelShape getShape(BlockState state, int i) {
if (i == 1) return FENCE_VOXELS[0];
VoxelShape shape = VoxelShapes.empty();
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]);
}
return shape;
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
VoxelShape shape = FENCE_VOXELS[0];
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() - 1]);
}
return shape;
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
VoxelShape shape = FENCE_VOXELS[5];
for (Direction dir: Direction.Type.HORIZONTAL) {
if (state.get(getConnectionProperty(dir)))
shape = VoxelShapes.union(shape, FENCE_VOXELS[dir.ordinal() + 4]);
}
return shape;
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getCameraCollisionShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getOutlineShape(state, world, pos, context);
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return getOutlineShape(state, view, pos, ShapeContext.absent());
}
@Override
@SuppressWarnings("deprecation")
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return false;
}
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult result = super.onUse(state, world, pos, player, hand, hit);
if (result.isAccepted()) return result;
if (world.isClient) {
ItemStack itemStack = player.getStackInHand(hand);
return itemStack.isOf(Items.LEAD) ? ActionResult.SUCCESS : ActionResult.PASS;
} else {
return LeadItem.attachHeldMobsToBlock(player, world, pos);
}
}
}

View File

@@ -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;
}
}

View File

@@ -1,11 +1,17 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.generator.MultipartBlockStateProvider; import fr.adrien1106.reframed.ReFramed;
import net.minecraft.block.*; import fr.adrien1106.reframed.util.blocks.BlockHelper;
import net.minecraft.data.client.MultipartBlockStateSupplier; import fr.adrien1106.reframed.util.blocks.Corner;
import fr.adrien1106.reframed.util.blocks.Edge;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.state.property.Properties; import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
@@ -13,34 +19,165 @@ import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements MultipartBlockStateProvider { import java.util.Map;
private static final VoxelShape DOWN = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.5f, 1f); import static fr.adrien1106.reframed.util.blocks.BlockProperties.*;
private static final VoxelShape UP = VoxelShapes.cuboid(0f, 0.5f, 0f, 1f, 1f, 1f); import static net.minecraft.state.property.Properties.*;
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); public class ReFramedSlabBlock extends WaterloggableReFramedBlock {
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); 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) { public ReFramedSlabBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(Properties.FACING, Direction.DOWN)); setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
} }
@Override @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { 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());
} }
@Override @Override
@SuppressWarnings("deprecation")
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null
|| context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
) return false;
// allow replacing with slab, step, small cube and half stair
Block block = block_item.getBlock();
if (block != this
&& block != ReFramed.STEP
&& block != ReFramed.SMALL_CUBE
&& block != ReFramed.HALF_STAIR
&& block != ReFramed.HALF_LAYER
&& block != ReFramed.LAYER
) return false;
// check if the player is clicking on the inner part of the block
return matchesShape(
context.getHitPos(),
context.getBlockPos(),
state.with(FACING, state.get(FACING).getOpposite())
);
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState current_state = ctx.getWorld().getBlockState(ctx.getBlockPos());
if (current_state.isOf(this))
return ReFramed.SLABS_CUBE.getDefaultState()
.with(AXIS, current_state.get(FACING).getAxis());
if (current_state.isOf(ReFramed.HALF_LAYER)) {
Edge edge = current_state.get(EDGE);
Direction face = edge.getDirection(current_state.get(EDGE_FACE));
edge = edge.getOpposite(face);
return ReFramed.SLABS_HALF_LAYER.getDefaultState()
.with(EDGE, edge)
.with(EDGE_FACE, edge.getDirectionIndex(edge.getOtherDirection(face)))
.with(LAYERS, current_state.get(LAYERS))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
if (current_state.isOf(ReFramed.HALF_STAIR)) {
Corner corner = current_state.get(CORNER);
Direction face = corner.getDirection(current_state.get(CORNER_FACE));
corner = corner.change(face);
return ReFramed.SLABS_INNER_STAIR.getDefaultState()
.with(CORNER, corner)
.with(CORNER_FACE, corner.getDirectionIndex(face.getOpposite()))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
if (current_state.isOf(ReFramed.STEP)) {
Edge edge = current_state.get(EDGE),
placed = BlockHelper.getPlacementEdge(ctx),
new_edge;
Direction face = edge.getFirstDirection(),
other = edge.getSecondDirection().getOpposite();
if (!ReFramed.STEP.matchesShape(
ctx.getHitPos(),
ctx.getBlockPos(),
current_state.with(EDGE, new_edge = edge.getOpposite(face))
)
&& ctx.getSide() != other.getOpposite()
&& (placed.getAxis() == edge.getAxis()
|| ctx.getSide() == other
|| !placed.hasDirection(other))
) {
new_edge = edge.getOpposite(edge.getSecondDirection());
other = edge.getFirstDirection().getOpposite();
}
return ReFramed.SLABS_STAIR.getDefaultState()
.with(EDGE, new_edge)
.with(EDGE_FACE, new_edge.getDirectionIndex(other))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
if (current_state.isOf(ReFramed.SMALL_CUBE)) {
Corner corner = current_state.get(CORNER);
Edge placed = BlockHelper.getPlacementEdge(ctx);
Direction face;
Corner new_corner;
if (!corner.hasDirection(face = ctx.getSide())) {
int i = 0;
do {
face = corner.getDirection(i);
new_corner = corner.change(face);
} while (!ReFramed.SMALL_CUBE.matchesShape(
ctx.getHitPos(),
ctx.getBlockPos(),
current_state.with(CORNER, new_corner)
) && ++i < 3);
if (i == 3) {
face = placed.getOtherDirection(corner.getMatchingDirection(placed)).getOpposite();
new_corner = corner.change(face);
}
} else new_corner = corner.change(face);
return ReFramed.SLABS_OUTER_STAIR.getDefaultState()
.with(CORNER, new_corner)
.with(CORNER_FACE, new_corner.getDirectionIndex(face.getOpposite()))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
return super.getPlacementState(ctx).with(FACING, ctx.getSide().getOpposite());
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) { public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return switch (state.get(Properties.FACING)) { return getSlabShape(state.get(FACING));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(FACING, mirror.apply(state.get(FACING)));
}
public static VoxelShape getSlabShape(Direction side) {
return switch (side) {
case DOWN -> DOWN; case DOWN -> DOWN;
case UP -> UP; case UP -> UP;
case NORTH -> NORTH; case NORTH -> NORTH;
@@ -51,7 +188,13 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements Mul
} }
@Override @Override
public MultipartBlockStateSupplier getMultipart() { public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
return null; if (new_state.isOf(ReFramed.SLABS_STAIR)
|| new_state.isOf(ReFramed.SLABS_OUTER_STAIR)
|| new_state.isOf(ReFramed.SLABS_INNER_STAIR)
|| new_state.isOf(ReFramed.SLABS_HALF_LAYER)
) return Map.of(1, 1);
if (new_state.isOf(ReFramed.SLABS_CUBE)) return Map.of(1, state.get(FACING).getDirection() == Direction.AxisDirection.POSITIVE ? 2 : 1);
return super.getThemeMap(state, new_state);
} }
} }

View File

@@ -0,0 +1,59 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.state.property.Properties.AXIS;
public class ReFramedSlabsCubeBlock extends ReFramedDoubleBlock {
public ReFramedSlabsCubeBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(AXIS, Direction.Axis.Y));
}
@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
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(AXIS, mirror.apply(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis());
}
@Override
public VoxelShape getShape(BlockState state, int i) {
return switch (state.get(AXIS)) {
case Y -> i == 2 ? UP : DOWN;
case Z -> i == 2 ? SOUTH : NORTH;
case X -> i == 2 ? EAST : WEST;
};
}
@Override
public int getTopThemeIndex(BlockState state) {
return 2;
}
}

View File

@@ -0,0 +1,62 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.Edge;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
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 static fr.adrien1106.reframed.block.ReFramedHalfLayerBlock.getHalfLayerShape;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE_FACE;
import static net.minecraft.state.property.Properties.LAYERS;
public class ReFramedSlabsHalfLayerBlock extends HalfLayerDoubleReFramedBlock {
private static final VoxelShape[] SLABS_HALF_LAYER_VOXELS = new VoxelShape[196];
public ReFramedSlabsHalfLayerBlock(Settings settings) {
super(settings);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getSlabsHalfLayerShape(state.get(EDGE), state.get(EDGE_FACE), state.get(LAYERS));
}
public static VoxelShape getSlabsHalfLayerShape(Edge edge, int face, int layers) {
int i = edge.ordinal() * 16 + face * 8 + layers - 1;
VoxelShape shape = SLABS_HALF_LAYER_VOXELS[i];
if (shape == null) {
shape = VoxelShapes.combineAndSimplify(
getShape(edge, edge.getDirection(face), layers, 1),
getShape(edge, edge.getDirection(face), layers, 2),
BooleanBiFunction.OR
);
SLABS_HALF_LAYER_VOXELS[i] = shape;
}
return shape;
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Edge edge = state.get(EDGE);
Direction face = edge.getDirection(state.get(EDGE_FACE));
return getShape(edge, face, state.get(LAYERS), i);
}
private static VoxelShape getShape(Edge edge, Direction face, int layers, int i) {
if (i == 2) {
face = edge.getOtherDirection(face);
edge = edge.getOpposite(face);
}
return i == 2
? getHalfLayerShape(edge, edge.getDirectionIndex(face), layers)
: getSlabShape(face);
}
}

View File

@@ -0,0 +1,48 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.Corner;
import fr.adrien1106.reframed.util.blocks.Edge;
import fr.adrien1106.reframed.util.blocks.StairShape;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import static fr.adrien1106.reframed.block.ReFramedHalfStairBlock.getHalfStairShape;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
import static fr.adrien1106.reframed.block.ReFramedStairBlock.getStairShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER_FACE;
public class ReFramedSlabsInnerStairBlock extends CornerDoubleReFramedBlock {
public ReFramedSlabsInnerStairBlock(Settings settings) {
super(settings);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
Corner corner = state.get(CORNER);
Direction face = corner.getDirection(state.get(CORNER_FACE));
Edge edge = corner.getEdge(face);
return getStairShape(
edge,
face.getDirection() == Direction.AxisDirection.POSITIVE
? StairShape.INNER_LEFT
: StairShape.INNER_RIGHT
);
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Corner corner = state.get(CORNER);
Direction face = corner.getDirection(state.get(CORNER_FACE));
if (i == 2) corner = corner.change(face);
return i == 2
? getHalfStairShape(corner, corner.getDirectionIndex(face.getOpposite()))
: getSlabShape(face);
}
}

View File

@@ -0,0 +1,96 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.VoxelHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.loot.context.LootContextParameterSet;
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.BlockView;
import java.util.List;
import static fr.adrien1106.reframed.block.ReFramedLayerBlock.getLayerShape;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.HALF_LAYERS;
import static net.minecraft.state.property.Properties.FACING;
public class ReFramedSlabsLayerBlock extends FacingDoubleReFramedBlock {
public static VoxelShape[] HALF_LAYER_SHAPES;
public ReFramedSlabsLayerBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(HALF_LAYERS, 1));
}
@Override
@SuppressWarnings("deprecation")
public List<ItemStack> getDroppedStacks(BlockState state, LootContextParameterSet.Builder builder) {
List<ItemStack> drops = super.getDroppedStacks(state, builder);
if (state.get(HALF_LAYERS) > 1)
drops.add(new ItemStack(ReFramed.LAYER, state.get(HALF_LAYERS)-1));
return drops;
}
@Override
@SuppressWarnings("deprecation")
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null
|| context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
) return false;
return block_item.getBlock() == ReFramed.LAYER && state.get(HALF_LAYERS) < 4;
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(HALF_LAYERS));
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getLayerShape(state.get(FACING), state.get(HALF_LAYERS) + 4);
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Direction face = state.get(FACING);
return i == 2
? HALF_LAYER_SHAPES[face.getId() * 4 + state.get(HALF_LAYERS)-1]
: getSlabShape(face);
}
static {
VoxelHelper.VoxelListBuilder builder = VoxelHelper.VoxelListBuilder.create(createCuboidShape(0, 8, 0, 16, 10, 16), 24)
.add(createCuboidShape(0, 8, 0, 16, 12, 16))
.add(createCuboidShape(0, 8, 0, 16, 14, 16))
.add(createCuboidShape(0, 8, 0, 16, 16, 16));
for (int i = 0; i < 4; i++) {
builder.add(i, VoxelHelper::mirrorY);
}
for (int i = 0; i < 4; i++) {
builder.add(i, VoxelHelper::rotateX);
}
for (int i = 0; i < 4; i++) {
builder.add(i, VoxelHelper::rotateCX);
}
for (int i = 0; i < 4; i++) {
builder.add(i, VoxelHelper::rotateCZ);
}
for (int i = 0; i < 4; i++) {
builder.add(i, VoxelHelper::rotateZ);
}
HALF_LAYER_SHAPES = builder.build();
}
}

View File

@@ -0,0 +1,51 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.Corner;
import fr.adrien1106.reframed.util.blocks.Edge;
import fr.adrien1106.reframed.util.blocks.StairShape;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
import static fr.adrien1106.reframed.block.ReFramedSmallCubeBlock.getSmallCubeShape;
import static fr.adrien1106.reframed.block.ReFramedStairBlock.getStairShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER_FACE;
public class ReFramedSlabsOuterStairBlock extends CornerDoubleReFramedBlock {
public ReFramedSlabsOuterStairBlock(Settings settings) {
super(settings);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
Corner corner = state.get(CORNER);
Direction face = corner.getDirection(state.get(CORNER_FACE));
Edge edge = corner.getEdgeWith(face);
return getStairShape(
edge,
corner.getOtherDirection(edge).getDirection() == Direction.AxisDirection.POSITIVE
? edge.getDirectionIndex(face) == 1
? StairShape.FIRST_OUTER_LEFT
: StairShape.SECOND_OUTER_LEFT
: edge.getDirectionIndex(face) == 1
? StairShape.FIRST_OUTER_RIGHT
: StairShape.SECOND_OUTER_RIGHT
);
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Corner corner = state.get(CORNER);
Direction face = corner.getDirection(state.get(CORNER_FACE));
return i == 2
? getSmallCubeShape(corner.change(face))
: getSlabShape(face);
}
}

View File

@@ -0,0 +1,37 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.Edge;
import fr.adrien1106.reframed.util.blocks.StairShape;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.getSlabShape;
import static fr.adrien1106.reframed.block.ReFramedStairBlock.getStairShape;
import static fr.adrien1106.reframed.block.ReFramedStepBlock.getStepShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE_FACE;
public class ReFramedSlabsStairBlock extends EdgeDoubleReFramedBlock {
public ReFramedSlabsStairBlock(Settings settings) {
super(settings);
}
@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 face = edge.getDirection(state.get(EDGE_FACE));
return i == 2
? getStepShape(edge.getOpposite(edge.getOtherDirection(face)))
: getSlabShape(face);
}
}

View File

@@ -0,0 +1,199 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.VoxelHelper;
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.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.*;
import static fr.adrien1106.reframed.util.blocks.Corner.*;
import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.state.property.Properties.WATERLOGGED;
public class ReFramedSmallCubeBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] SMALL_CUBE_VOXELS;
public ReFramedSmallCubeBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(CORNER, NORTH_EAST_DOWN));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER));
}
@Override
@SuppressWarnings("deprecation")
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null
|| context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
) return false;
Block block = block_item.getBlock();
Corner corner = state.get(CORNER);
if (block == this)
return matchesShape(
context.getHitPos(),
context.getBlockPos(),
getDefaultState().with(CORNER, corner.change(corner.getFirstDirection()))
) || matchesShape(
context.getHitPos(),
context.getBlockPos(),
getDefaultState().with(CORNER, corner.change(corner.getSecondDirection()))
) || matchesShape(
context.getHitPos(),
context.getBlockPos(),
getDefaultState().with(CORNER, corner.change(corner.getThirdDirection()))
);
if (block == ReFramed.HALF_STAIR || block == ReFramed.SLAB) {
corner = corner.getOpposite();
Direction face = corner.getFirstDirection();
Edge edge = corner.getEdge(face);
if (ReFramed.STAIR.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.STAIR.getDefaultState()
.with(EDGE, edge)
.with(
STAIR_SHAPE,
face.getDirection() == Direction.AxisDirection.POSITIVE
? StairShape.INNER_LEFT
: StairShape.INNER_RIGHT
)
)) return block == ReFramed.SLAB || !matchesShape(
context.getHitPos(),
context.getBlockPos(),
getDefaultState().with(CORNER, corner)
);
}
return false;
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockPos pos = ctx.getBlockPos();
BlockState current_state = ctx.getWorld().getBlockState(pos);
if (current_state.isOf(ReFramed.HALF_STAIR)) {
BlockState new_state;
Direction face = current_state.get(CORNER).getDirection(current_state.get(CORNER_FACE));
if (matchesShape(
ctx.getHitPos(), pos,
getDefaultState().with(CORNER, current_state.get(CORNER).change(face))
)) new_state = ReFramed.HALF_STAIRS_CUBE_STAIR.getDefaultState();
else new_state = ReFramed.HALF_STAIRS_SLAB.getDefaultState();
return new_state
.with(CORNER, current_state.get(CORNER))
.with(CORNER_FACE, current_state.get(CORNER_FACE))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
if (current_state.isOf(this)) {
Vec3d hit = ctx.getHitPos();
Corner corner = current_state.get(CORNER);
ReFramedSmallCubesStepBlock block = ((ReFramedSmallCubesStepBlock) ReFramed.SMALL_CUBES_STEP);
BlockState state = block.getDefaultState()
.with(EDGE, corner.getEdge(corner.getFirstDirection()))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
if (block.matchesShape(
hit, pos, state,
corner.getFirstDirection().getDirection() == Direction.AxisDirection.POSITIVE ? 1 : 2
)) return state;
state = state.with(EDGE, corner.getEdge(corner.getSecondDirection()));
if (block.matchesShape(
hit, pos, state,
corner.getSecondDirection().getDirection() == Direction.AxisDirection.POSITIVE ? 1 : 2
)) return state;
return state.with(EDGE, corner.getEdge(corner.getThirdDirection()));
}
if (current_state.isOf(ReFramed.SLAB)) {
Corner corner = BlockHelper.getPlacementCorner(ctx);
Direction face = current_state.get(FACING);
if (!corner.hasDirection(face)) corner = corner.change(face.getOpposite());
return ReFramed.SLABS_OUTER_STAIR.getDefaultState()
.with(CORNER, corner)
.with(CORNER_FACE, corner.getDirectionIndex(face))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
return super.getPlacementState(ctx).with(CORNER, BlockHelper.getPlacementCorner(ctx));
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getSmallCubeShape(state.get(CORNER));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(CORNER, state.get(CORNER).rotate(rotation));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(CORNER, state.get(CORNER).mirror(mirror));
}
@Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
if (new_state.isOf(ReFramed.HALF_STAIRS_SLAB)
|| new_state.isOf(ReFramed.SLABS_OUTER_STAIR)
) return Map.of(1, 2);
if (new_state.isOf(ReFramed.SMALL_CUBES_STEP))
return Map.of(
1,
state.get(CORNER)
.getOtherDirection(new_state.get(EDGE))
.getDirection() == Direction.AxisDirection.POSITIVE ? 2 : 1
);
return super.getThemeMap(state, new_state);
}
public static VoxelShape getSmallCubeShape(Corner corner) {
return SMALL_CUBE_VOXELS[corner.getID()];
}
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();
}
}

View File

@@ -0,0 +1,74 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Corner;
import fr.adrien1106.reframed.util.blocks.Edge;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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;
public class ReFramedSmallCubesStepBlock extends WaterloggableReFramedDoubleBlock {
public ReFramedSmallCubesStepBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
}
@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
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getStepShape(state.get(EDGE));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(EDGE, state.get(EDGE).rotate(rotation));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(EDGE, state.get(EDGE).mirror(mirror));
}
@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;
}
}

View File

@@ -0,0 +1,227 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
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.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.WorldAccess;
import org.jetbrains.annotations.Nullable;
import java.util.Map;
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.StairShape.*;
public class ReFramedStairBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] STAIR_VOXELS;
public ReFramedStairBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE, STAIR_SHAPE));
}
@Override
@SuppressWarnings("deprecation")
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null) return false;
return !(
context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
|| !(
block_item.getBlock() == ReFramed.STEP
&& ReFramed.STAIRS_CUBE
.matchesShape(
context.getHitPos(),
context.getBlockPos(),
state,
2
)
)
);
}
@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) {
BlockState current_state = ctx.getWorld().getBlockState(ctx.getBlockPos());
if (current_state.isOf(ReFramed.STEP)) {
Edge edge = current_state.get(EDGE).opposite();
StairShape shape = BlockHelper.getStairsShape(edge, ctx.getWorld(), ctx.getBlockPos());
return ReFramed.STAIRS_CUBE.getDefaultState()
.with(EDGE, edge)
.with(STAIR_SHAPE, shape);
}
Edge edge = BlockHelper.getPlacementEdge(ctx);
StairShape shape = BlockHelper.getStairsShape(edge, ctx.getWorld(), ctx.getBlockPos());
return super.getPlacementState(ctx).with(EDGE, edge).with(STAIR_SHAPE, shape);
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getStairShape(state.get(EDGE), state.get(STAIR_SHAPE));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
Edge prev_edge = state.get(EDGE);
Edge edge = prev_edge.rotate(rotation);
if (prev_edge.getAxis() == Direction.Axis.Y) return state.with(EDGE, edge);
if (prev_edge.getFace().getDirection() == edge.getFace().getDirection()) // 90° rotations
state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).mirror());
else state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).flip());
if (prev_edge.getAxis() == edge.getAxis()) // 180° rotation
state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).mirror());
return state.with(EDGE, edge);
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
Edge prev_edge = state.get(EDGE);
Edge edge = prev_edge.mirror(mirror);
return state
.with(STAIR_SHAPE, prev_edge == edge ? state.get(STAIR_SHAPE).mirror() : state.get(STAIR_SHAPE).flip())
.with(EDGE, edge);
}
public static VoxelShape getStairShape(Edge edge, StairShape shape) {
return STAIR_VOXELS[edge.getID() * 9 + shape.getID()];
}
@Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
if (new_state.isOf(ReFramed.STAIRS_CUBE)) return Map.of(1, 1);
return super.getThemeMap(state, new_state);
}
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(1).add(10)
.add(3).add(12)
.add(5).add(16)
.add(8, VoxelHelper::rotateCY).add(7, VoxelHelper::rotateCY)
// DOWN_EAST
.add(36, VoxelHelper::rotateZ)
.add(2).add(11)
.add(4).add(13)
.add(41, VoxelHelper::rotateZ).add(42, VoxelHelper::rotateZ)
.add(6).add(17)
// EAST_UP
.add(45, VoxelHelper::rotateZ)
.add(29).add(20)
.add(31).add(22)
.add(35).add(24)
.add(52, VoxelHelper::rotateZ).add(53, VoxelHelper::rotateZ)
// UP_WEST
.add(54, VoxelHelper::rotateZ)
.add(28).add(19)
.add(30).add(21)
.add(59, VoxelHelper::rotateZ).add(60, VoxelHelper::rotateZ)
.add(34).add(23)
// WEST_NORTH
.add(0, VoxelHelper::rotateCZ)
.add(1).add(28)
.add(3).add(30)
.add(7).add(32)
.add(43).add(68)
// NORTH_EAST
.add(72, VoxelHelper::rotateY)
.add(2).add(29)
.add(4).add(31)
.add(50).add(61)
.add(8).add(33)
// EAST_SOUTH
.add(81, VoxelHelper::rotateY)
.add(11).add(20)
.add(13).add(22)
.add(15).add(26)
.add(51).add(62)
// SOUTH_WEST
.add(90, VoxelHelper::rotateY)
.add(10).add(19)
.add(12).add(21)
.add(44).add(69)
.add(14).add(25)
.build();
}
}

View File

@@ -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 TOP */
.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, R180))
.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, R180, R270));
}
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));
}
}

View File

@@ -0,0 +1,87 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Edge;
import fr.adrien1106.reframed.util.blocks.StairShape;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
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 {
private static final VoxelShape[] STAIRS_CUBE_VOXELS = VoxelListBuilder.buildFrom(STAIR_VOXELS);
public ReFramedStairsCubeBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN).with(STAIR_SHAPE, StairShape.STRAIGHT));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE, STAIR_SHAPE));
}
@Override
@SuppressWarnings("deprecation")
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
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
Edge prev_edge = state.get(EDGE);
Edge edge = prev_edge.rotate(rotation);
if (prev_edge.getAxis() == Direction.Axis.Y) return state.with(EDGE, edge);
if (prev_edge.getFace().getDirection() == edge.getFace().getDirection()) // 90° rotations
state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).mirror());
else state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).flip());
if (prev_edge.getAxis() == edge.getAxis()) // 180° rotation
state = state.with(STAIR_SHAPE, state.get(STAIR_SHAPE).mirror());
return state.with(EDGE, edge);
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
Edge prev_edge = state.get(EDGE);
Edge edge = prev_edge.mirror(mirror);
return state
.with(STAIR_SHAPE, prev_edge == edge ? state.get(STAIR_SHAPE).mirror() : state.get(STAIR_SHAPE).flip())
.with(EDGE, edge);
}
@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);
}
}

View File

@@ -0,0 +1,223 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Corner;
import fr.adrien1106.reframed.util.blocks.Edge;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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 org.jetbrains.annotations.Nullable;
import java.util.Map;
import static fr.adrien1106.reframed.util.VoxelHelper.VoxelListBuilder;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.*;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.STAIR_SHAPE;
import static net.minecraft.state.property.Properties.*;
public class ReFramedStepBlock extends WaterloggableReFramedBlock {
public static final VoxelShape[] STEP_VOXELS;
public ReFramedStepBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE));
}
@Override
@SuppressWarnings("deprecation")
public boolean canReplace(BlockState state, ItemPlacementContext context) {
if (context.getPlayer() == null
|| context.getPlayer().isSneaking()
|| !(context.getStack().getItem() instanceof BlockItem block_item)
) return false;
Block block = block_item.getBlock();
Edge edge = state.get(EDGE);
if (block == ReFramed.HALF_LAYER)
return matchesShape(
context.getHitPos(),
context.getBlockPos(),
getDefaultState().with(EDGE, edge.getOpposite(edge.getFirstDirection()))
) || matchesShape(
context.getHitPos(),
context.getBlockPos(),
getDefaultState().with(EDGE, edge.getOpposite(edge.getSecondDirection()))
);
// allow replacing with stair
if (block != this
&& block != ReFramed.STAIR
&& block != ReFramed.SLAB
) return false;
return ReFramed.STAIR
.matchesShape(
context.getHitPos(),
context.getBlockPos(),
ReFramed.STAIRS_CUBE.getDefaultState().with(EDGE, edge.opposite())
);
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
BlockPos pos = ctx.getBlockPos();
BlockState current_state = ctx.getWorld().getBlockState(pos);
if (current_state.isOf(ReFramed.STAIR))
return ReFramed.STAIRS_CUBE.getDefaultState()
.with(EDGE, current_state.get(EDGE))
.with(STAIR_SHAPE, current_state.get(STAIR_SHAPE));
if (current_state.isOf(this)) {
Vec3d hit = ctx.getHitPos();
Edge edge = current_state.get(EDGE);
// Steps Slab
if (matchesShape(hit, pos,
current_state.with(EDGE, edge.getOpposite(edge.getFirstDirection()))
)) return ReFramed.STEPS_SLAB.getDefaultState()
.with(FACING, edge.getFirstDirection())
.with(AXIS, edge.getSecondDirection().getAxis())
.with(WATERLOGGED, current_state.get(WATERLOGGED));
else if (matchesShape(hit, pos,
current_state.with(EDGE, edge.getOpposite(edge.getSecondDirection()))
)) return ReFramed.STEPS_SLAB.getDefaultState()
.with(FACING, edge.getSecondDirection())
.with(AXIS, edge.getFirstDirection().getAxis())
.with(WATERLOGGED, current_state.get(WATERLOGGED));
// Steps Cross
return ReFramed.STEPS_CROSS.getDefaultState()
.with(EDGE, edge)
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
if (current_state.isOf(ReFramed.SLAB)) {
Direction facing = current_state.get(FACING);
Edge edge;
// Slabs Stair
if (ctx.getSide() == facing || ctx.getSide() == facing.getOpposite())
edge = BlockHelper.getPlacementEdge(ctx);
else
edge = Edge.getByDirections(facing, ctx.getSide().getOpposite());
return ReFramed.SLABS_STAIR.getDefaultState()
.with(EDGE, edge)
.with(EDGE_FACE, edge.getDirectionIndex(facing));
}
if (current_state.isOf(ReFramed.HALF_STAIR)) {
Corner corner = current_state.get(CORNER);
int face_index = current_state.get(CORNER_FACE), feature_index;
Direction face = corner.getDirection(current_state.get(CORNER_FACE));
Direction side = ctx.getSide().getOpposite();
if (side.getAxis() == face.getAxis())
side = BlockHelper.getPlacementEdge(ctx).getOtherDirection(face == side ? face : face.getOpposite());
if (side.getAxis() != face.getAxis() && !corner.hasDirection(side))
side = corner.getOtherDirection(Edge.getByDirections(face, side.getOpposite()));
feature_index = corner.getDirectionIndex(side);
return ReFramed.HALF_STAIRS_STEP_STAIR.getDefaultState()
.with(CORNER, corner)
.with(CORNER_FACE, face_index)
.with(CORNER_FEATURE, feature_index > face_index ? feature_index - 1 : feature_index)
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
if (current_state.isOf(ReFramed.HALF_LAYER)) {
Edge edge = current_state.get(EDGE);
Direction face = edge.getDirection(current_state.get(EDGE_FACE));
edge = edge.getOpposite(face);
return ReFramed.STEPS_HALF_LAYER.getDefaultState()
.with(EDGE, edge)
.with(EDGE_FACE, edge.getDirectionIndex(edge.getOtherDirection(face)))
.with(LAYERS, current_state.get(LAYERS))
.with(WATERLOGGED, current_state.get(WATERLOGGED));
}
return super.getPlacementState(ctx).with(EDGE, BlockHelper.getPlacementEdge(ctx));
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getStepShape(state.get(EDGE));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(EDGE, state.get(EDGE).rotate(rotation));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(EDGE, state.get(EDGE).mirror(mirror));
}
public static VoxelShape getStepShape(Edge edge) {
return STEP_VOXELS[edge.getID()];
}
@Override
public Map<Integer, Integer> getThemeMap(BlockState state, BlockState new_state) {
if (new_state.isOf(ReFramed.STEPS_CROSS)
|| new_state.isOf(ReFramed.STEPS_HALF_LAYER)
) return Map.of(1, 1);
if (new_state.isOf(ReFramed.STAIRS_CUBE)
|| new_state.isOf(ReFramed.SLABS_STAIR)) return Map.of(1, 2);
if (new_state.isOf(ReFramed.STEPS_SLAB))
return Map.of(
1,
state.get(EDGE)
.getOtherDirection(new_state.get(FACING))
.getDirection() == Direction.AxisDirection.POSITIVE ? 2 : 1
);
return super.getThemeMap(state, new_state);
}
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();
}
}

View File

@@ -0,0 +1,86 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Edge;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.block.ReFramedStepBlock.getStepShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
public class ReFramedStepsCrossBlock extends WaterloggableReFramedDoubleBlock {
public static VoxelShape[] STEP_CROSS_VOXELS;
public ReFramedStepsCrossBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(EDGE, Edge.NORTH_DOWN));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(EDGE));
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
Edge edge = BlockHelper.getPlacementCorner(ctx).getEdge(ctx.getSide().getOpposite());
return super.getPlacementState(ctx).with(EDGE, edge);
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return STEP_CROSS_VOXELS[state.get(EDGE).ordinal()];
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(EDGE, state.get(EDGE).rotate(rotation));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(EDGE, state.get(EDGE).mirror(mirror));
}
@Override
public VoxelShape getShape(BlockState state, int i) {
return getStepShape(i == 1 ? state.get(EDGE): state.get(EDGE).opposite());
}
static {
VoxelShape STEP_CROSS = VoxelShapes.combineAndSimplify(
getStepShape(Edge.NORTH_DOWN),
getStepShape(Edge.SOUTH_UP),
BooleanBiFunction.OR
);
STEP_CROSS_VOXELS = VoxelHelper.VoxelListBuilder.create(STEP_CROSS, 12)
.add(VoxelHelper::rotateX)
.add(VoxelHelper::rotateX)
.add(VoxelHelper::rotateX)
.add(0, VoxelHelper::rotateCY)
.add(VoxelHelper::rotateZ)
.add(VoxelHelper::rotateZ)
.add(VoxelHelper::rotateZ)
.add(0, VoxelHelper::rotateCZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::rotateY)
.build();
}
}

View File

@@ -0,0 +1,62 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.Edge;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
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 static fr.adrien1106.reframed.block.ReFramedHalfLayerBlock.getHalfLayerShape;
import static fr.adrien1106.reframed.block.ReFramedStepBlock.getStepShape;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.EDGE_FACE;
import static net.minecraft.state.property.Properties.LAYERS;
public class ReFramedStepsHalfLayerBlock extends HalfLayerDoubleReFramedBlock {
private static final VoxelShape[] SLABS_HALF_LAYER_VOXELS = new VoxelShape[196];
public ReFramedStepsHalfLayerBlock(Settings settings) {
super(settings);
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getStepsHalfLayerShape(state.get(EDGE), state.get(EDGE_FACE), state.get(LAYERS));
}
public static VoxelShape getStepsHalfLayerShape(Edge edge, int face, int layers) {
int i = edge.ordinal() * 16 + face * 8 + layers - 1;
VoxelShape shape = SLABS_HALF_LAYER_VOXELS[i];
if (shape == null) {
shape = VoxelShapes.combineAndSimplify(
getShape(edge, edge.getDirection(face), layers, 1),
getShape(edge, edge.getDirection(face), layers, 2),
BooleanBiFunction.OR
);
SLABS_HALF_LAYER_VOXELS[i] = shape;
}
return shape;
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Edge edge = state.get(EDGE);
Direction face = edge.getDirection(state.get(EDGE_FACE));
return getShape(edge, face, state.get(LAYERS), i);
}
private static VoxelShape getShape(Edge edge, Direction face, int layers, int i) {
if (i == 2) {
face = edge.getOtherDirection(face);
edge = edge.getOpposite(face);
}
return i == 2
? getHalfLayerShape(edge, edge.getDirectionIndex(face), layers)
: getStepShape(edge);
}
}

View File

@@ -0,0 +1,85 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.util.blocks.BlockHelper;
import fr.adrien1106.reframed.util.blocks.Edge;
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.util.BlockMirror;
import net.minecraft.util.BlockRotation;
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.state.property.Properties.AXIS;
import static net.minecraft.state.property.Properties.FACING;
public class ReFramedStepsSlabBlock extends WaterloggableReFramedDoubleBlock {
public ReFramedStepsSlabBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(FACING, Direction.DOWN).with(AXIS, Axis.X));
}
@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
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getSlabShape(state.get(FACING));
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state
.with(AXIS, rotation.rotate(Direction.get(Direction.AxisDirection.POSITIVE, state.get(AXIS))).getAxis())
.with(FACING, rotation.rotate(state.get(FACING)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
if (state.get(FACING).getAxis() != Axis.Y)
return state.with(FACING, mirror.apply(state.get(FACING)));
return state;
}
@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.NORTH : Direction.SOUTH;
}
));
}
@Override
public int getTopThemeIndex(BlockState state) {
return 2;
}
}

View File

@@ -1,22 +1,18 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects; import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.ReFramed; import net.minecraft.block.*;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil; import net.minecraft.block.enums.BlockHalf;
import fr.adrien1106.reframed.util.ReframedInteractible; import net.minecraft.entity.ai.pathing.NavigationType;
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.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.sound.SoundCategory;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.BlockMirror;
import net.minecraft.util.BlockRotation;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
@@ -24,71 +20,139 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.event.GameEvent;
import net.minecraft.world.explosion.Explosion;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
public class ReFramedTrapdoorBlock extends TrapdoorBlock implements BlockEntityProvider, ReframedInteractible { import java.util.function.BiConsumer;
public ReFramedTrapdoorBlock(Settings settings, BlockSetType woodType) {
super(woodType, settings); import static net.minecraft.state.property.Properties.*;
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
} public class ReFramedTrapdoorBlock extends WaterloggableReFramedBlock {
@Override public static final VoxelShape[] TRAPDOOR_VOXELS;
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); public ReFramedTrapdoorBlock(Settings settings) {
} super(settings);
setDefaultState(getDefaultState()
@Override .with(HORIZONTAL_FACING, Direction.NORTH)
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { .with(BLOCK_HALF, BlockHalf.BOTTOM)
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder)); .with(OPEN, false)
} .with(POWERED, false)
);
@Nullable }
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) { @Override
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx); protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
} super.appendProperties(builder.add(HORIZONTAL_FACING, BLOCK_HALF, OPEN, POWERED));
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { @Override
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit); @SuppressWarnings("deprecation")
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit); public void neighborUpdate(BlockState state, World world, BlockPos pos, Block source, BlockPos sourcePos, boolean notify) {
return r; if (world.isClient) return;
} boolean powered = world.isReceivingRedstonePower(pos);
if (powered != state.get(POWERED)) {
@Override if (state.get(OPEN) != powered) {
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { state = state.with(OPEN, powered);
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved); playToggleSound(null, world, pos, powered);
super.onStateReplaced(state, world, pos, newState, moved); }
}
world.setBlockState(pos, state.with(POWERED, powered), 2);
@Override if (state.get(WATERLOGGED)) {
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack); }
super.onPlaced(world, pos, state, placer, stack); }
} }
@Override @Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx)); BlockState state = super.getPlacementState(ctx);
} World world = ctx.getWorld();
BlockPos pos = ctx.getBlockPos();
@Override Direction side = ctx.getSide();
public boolean emitsRedstonePower(BlockState state) {
return ReFramedInteractionUtil.emitsRedstonePower(state); if (side.getAxis().isVertical()) state = state
} .with(HORIZONTAL_FACING, ctx.getHorizontalPlayerFacing().getOpposite())
.with(BLOCK_HALF, side == Direction.UP ? BlockHalf.BOTTOM : BlockHalf.TOP);
@Override else state = state
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { .with(HORIZONTAL_FACING, side)
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir); .with(BLOCK_HALF, ctx.getHitPos().getY() - pos.getY() > 0.5 ? BlockHalf.TOP : BlockHalf.BOTTOM);
}
if (world.isReceivingRedstonePower(pos)) state = state.with(OPEN, true).with(POWERED, true);
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { return state;
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir); }
}
@Override
@Override public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
public boolean canAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) { ActionResult result = super.onUse(state, world, pos, player, hand, hit);
return false; if (result.isAccepted()) return result;
} flip(state, world, pos, player);
return ActionResult.success(world.isClient);
}
@Override
@SuppressWarnings("deprecation")
public boolean canPathfindThrough(BlockState state, BlockView world, BlockPos pos, NavigationType type) {
return switch (type) {
case LAND, AIR -> state.get(OPEN);
case WATER -> state.get(WATERLOGGED);
};
}
@Override
@SuppressWarnings("deprecation")
public void onExploded(BlockState state, World world, BlockPos pos, Explosion explosion, BiConsumer<ItemStack, BlockPos> stack_merger) {
if (explosion.getDestructionType() == Explosion.DestructionType.TRIGGER_BLOCK
&& !world.isClient()
&& !state.get(POWERED)
) flip(state, world, pos, null);
super.onExploded(state, world, pos, explosion, stack_merger);
}
private void flip(BlockState state, World world, BlockPos pos, @Nullable PlayerEntity player) {
state = state.cycle(OPEN);
world.setBlockState(pos, state, 2);
this.playToggleSound(player, world, pos, state.get(OPEN));
}
protected void playToggleSound(@Nullable PlayerEntity player, World world, BlockPos pos, boolean open) {
world.playSound(player, pos, open ? BlockSetType.OAK.trapdoorOpen() : BlockSetType.OAK.trapdoorClose(), SoundCategory.BLOCKS, 1.0F, world.getRandom().nextFloat() * 0.1F + 0.9F);
world.emitGameEvent(player, open ? GameEvent.BLOCK_OPEN : GameEvent.BLOCK_CLOSE, pos);
}
@Override
@SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
int index;
if (!state.get(OPEN)) index = state.get(BLOCK_HALF) == BlockHalf.BOTTOM ? 0 : 1;
else index = state.get(HORIZONTAL_FACING).ordinal();
return TRAPDOOR_VOXELS[index];
}
@Override
@SuppressWarnings("deprecation")
public BlockState rotate(BlockState state, BlockRotation rotation) {
return state.with(HORIZONTAL_FACING, rotation.rotate(state.get(HORIZONTAL_FACING)));
}
@Override
@SuppressWarnings("deprecation")
public BlockState mirror(BlockState state, BlockMirror mirror) {
return state.with(HORIZONTAL_FACING, mirror.apply(state.get(HORIZONTAL_FACING)));
}
static {
VoxelShape SHAPE = createCuboidShape(0, 0, 0, 16, 3, 16);
TRAPDOOR_VOXELS = VoxelHelper.VoxelListBuilder.create(SHAPE, 6)
.add(VoxelHelper::mirrorY)
.add(VoxelHelper::rotateX)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.build();
}
} }

View File

@@ -1,130 +1,208 @@
package fr.adrien1106.reframed.block; package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects; import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.ReFramed; import net.minecraft.block.*;
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.block.enums.WallShape;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack; import net.minecraft.registry.tag.BlockTags;
import net.minecraft.state.StateManager; import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult; import net.minecraft.state.property.Property;
import net.minecraft.util.Hand; import net.minecraft.util.BlockMirror;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.BlockRotation;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape; import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes; import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView; import net.minecraft.world.BlockView;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldAccess;
import net.minecraft.world.WorldView;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class ReFramedWallBlock extends WallBlock implements BlockEntityProvider { import static net.minecraft.state.property.Properties.*;
public ReFramedWallBlock(Settings settings) {
super(settings); public class ReFramedWallBlock extends WaterloggableReFramedBlock {
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
public static final VoxelShape[] WALL_VOXELS;
initNewShapemaps(); //WallBlock specific haxx
} public ReFramedWallBlock(Settings settings) {
super(settings);
@Nullable setDefaultState(getDefaultState()
@Override .with(UP, true)
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { .with(EAST_WALL_SHAPE, WallShape.NONE)
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state); .with(NORTH_WALL_SHAPE, WallShape.NONE)
} .with(WEST_WALL_SHAPE, WallShape.NONE)
.with(SOUTH_WALL_SHAPE, WallShape.NONE)
@Override );
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { }
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
} @Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
@Nullable super.appendProperties(builder.add(UP, EAST_WALL_SHAPE, NORTH_WALL_SHAPE, SOUTH_WALL_SHAPE, WEST_WALL_SHAPE));
@Override }
public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx); @Override
} public BlockState getStateForNeighborUpdate(BlockState state, Direction dir, BlockState other_state, WorldAccess world, BlockPos pos, BlockPos moved) {
BlockState new_state = super.getStateForNeighborUpdate(state, dir, other_state, world, pos, moved);
@Override if (dir == Direction.DOWN) return new_state;
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) { BlockState top_state = dir == Direction.UP? other_state: world.getBlockState(pos.up());
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit); boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit); VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN);
return r;
} Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
.collect(Collectors.toMap(d -> d, d -> {
@Override if (d == dir) return other_state;
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) { return world.getBlockState(pos.offset(d));
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved); }));
super.onStateReplaced(state, world, pos, newState, moved); new_state = getWallState(new_state, top_state, neighbors, top_shape, fs, world, pos);
} return new_state.with(UP, shouldHavePost(new_state, top_state, top_shape));
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) { @Override
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack); public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
} BlockState state = super.getPlacementState(ctx);
World world = ctx.getWorld();
@Override BlockPos pos = ctx.getBlockPos();
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), getNewShape(state, newCollisionShapeMap)); BlockState top_state = world.getBlockState(pos.up());
} boolean fs = top_state.isSideSolidFullSquare(world, pos.up(), Direction.DOWN);
VoxelShape top_shape = fs ? null : top_state.getCollisionShape(world, pos.up()).getFace(Direction.DOWN);
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) { Map<Direction, BlockState> neighbors = Direction.Type.HORIZONTAL.stream()
return getNewShape(state, newShapeMap); .collect(Collectors.toMap(d -> d, d -> world.getBlockState(pos.offset(d))));
} state = getWallState(state, top_state, neighbors, top_shape, fs, world, pos);
return state.with(UP, shouldHavePost(state, top_state, top_shape));
@Override }
public boolean emitsRedstonePower(BlockState state) {
return ReFramedInteractionUtil.emitsRedstonePower(state); @Override
} @SuppressWarnings("deprecation")
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
@Override VoxelShape shape = state.get(UP) ? WALL_VOXELS[0]: VoxelShapes.empty();
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { for (Direction dir : Direction.Type.HORIZONTAL) {
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir); WallShape wall_shape = state.get(getWallShape(dir));
} if (wall_shape != WallShape.NONE)
shape = VoxelShapes.union(shape, WALL_VOXELS[1 + (wall_shape.ordinal()-1) * 4 + (dir.ordinal() - 2)]);
@Override }
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) { return shape;
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir); }
}
@Override
//Shapemap heck (WallBlock has a map keyed on BlockState, but since we add more blockstates most of those map lookups fail) public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
if (isGhost(view, pos)) return VoxelShapes.empty();
protected record ShapeKey(boolean up, WallShape north, WallShape east, WallShape south, WallShape west) { VoxelShape shape = state.get(UP) ? WALL_VOXELS[9]: VoxelShapes.empty();
static ShapeKey fromBlockstate(BlockState state) { for (Direction dir : Direction.Type.HORIZONTAL) {
return new ShapeKey( if (state.get(getWallShape(dir)) != WallShape.NONE)
state.get(WallBlock.UP), shape = VoxelShapes.union(shape, WALL_VOXELS[8 + dir.ordinal()]);
state.get(WallBlock.NORTH_SHAPE), }
state.get(WallBlock.EAST_SHAPE), return shape;
state.get(WallBlock.SOUTH_SHAPE), }
state.get(WallBlock.WEST_SHAPE)
); @Override
} @SuppressWarnings("deprecation")
} public BlockState rotate(BlockState state, BlockRotation rotation) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
protected final Map<ShapeKey, VoxelShape> newShapeMap = new HashMap<>(); s.with(getWallShape(rotation.rotate(dir)), state.get(getWallShape(dir)))
protected final Map<ShapeKey, VoxelShape> newCollisionShapeMap = new HashMap<>(); , (prev, next) -> next);
}
protected void initNewShapemaps() {
initNewShapemap(((WallBlockAccessor) this).getShapeMap(), newShapeMap); @Override
initNewShapemap(((WallBlockAccessor) this).getCollisionShapeMap(), newCollisionShapeMap); @SuppressWarnings("deprecation")
} public BlockState mirror(BlockState state, BlockMirror mirror) {
return Direction.Type.HORIZONTAL.stream().reduce(state, (s, dir) ->
protected void initNewShapemap(Map<BlockState, VoxelShape> oldShapeMap, Map<ShapeKey, VoxelShape> newShapeMap) { s.with(getWallShape(mirror.apply(dir)), state.get(getWallShape(dir)))
oldShapeMap.forEach((state, shape) -> newShapeMap.putIfAbsent(ShapeKey.fromBlockstate(state), shape)); , (prev, next) -> next);
} }
protected VoxelShape getNewShape(BlockState state, Map<ShapeKey, VoxelShape> shapes) { public static BlockState getWallState(BlockState state, BlockState top_state, Map<Direction, BlockState> neighbors, VoxelShape top_shape, boolean fs, WorldView world, BlockPos pos) {
return shapes.getOrDefault(ShapeKey.fromBlockstate(state), VoxelShapes.empty()); for (Direction dir : Direction.Type.HORIZONTAL) {
} BlockPos offset = pos.offset(dir);
BlockState neighbor = neighbors.get(dir);
boolean side_full = neighbor.isSideSolidFullSquare(world, offset, dir.getOpposite());
Property<WallShape> wall_shape = getWallShape(dir);
if (shouldConnectTo(neighbor, side_full, dir.getOpposite())) {
state = state.with(
wall_shape,
fs
|| (top_state.contains(wall_shape) && top_state.get(wall_shape) != WallShape.NONE)
|| shouldUseTall(WALL_VOXELS[dir.ordinal() + 3], top_shape)
? WallShape.TALL
: WallShape.LOW
);
} else state = state.with(wall_shape, WallShape.NONE);
}
return state;
}
public static boolean shouldHavePost(BlockState state, BlockState top_state, VoxelShape top_shape) {
// above has post
if ((top_state.contains(UP) && top_state.get(UP)) || (!top_state.contains(UP) && top_state.contains(NORTH_WALL_SHAPE))) return true;
if (Stream.of(Direction.SOUTH, Direction.EAST) // Opposites are different
.anyMatch(dir -> state.get(getWallShape(dir)) != state.get(getWallShape(dir.getOpposite())))
) return true;
// no sides
if (Direction.Type.HORIZONTAL.stream().allMatch(dir -> state.get(getWallShape(dir)) == WallShape.NONE))
return true;
// Matching sides
if (Stream.of(Direction.SOUTH, Direction.EAST)
.anyMatch(dir ->
state.get(getWallShape(dir)) == state.get(getWallShape(dir.getOpposite()))
)) return false;
return top_state.isIn(BlockTags.WALL_POST_OVERRIDE) || top_shape == null || shouldUseTall(WALL_VOXELS[0], top_shape);
}
public static boolean shouldConnectTo(BlockState state, boolean side_full, Direction side) {
Block block = state.getBlock();
boolean bl = block instanceof FenceGateBlock && FenceGateBlock.canWallConnect(state, side);
return state.isIn(BlockTags.WALLS) || !WallBlock.cannotConnect(state) && side_full || block instanceof PaneBlock || bl;
}
public static boolean shouldUseTall(VoxelShape self_shape, VoxelShape other_shape) {
return !VoxelShapes.matchesAnywhere(
self_shape,
other_shape,
BooleanBiFunction.ONLY_FIRST
);
}
public static Property<WallShape> getWallShape(Direction dir) {
return switch (dir) {
case EAST -> EAST_WALL_SHAPE;
case NORTH -> NORTH_WALL_SHAPE;
case WEST -> WEST_WALL_SHAPE;
case SOUTH -> SOUTH_WALL_SHAPE;
default -> null;
};
}
static {
VoxelShape POST = createCuboidShape(4, 0, 4, 12, 16, 12);
VoxelShape POST_COLLISION = createCuboidShape(4, 0, 4, 12, 24, 12);
VoxelShape LOW = createCuboidShape(5, 0, 0, 11, 14, 8);
VoxelShape TALL = createCuboidShape(5, 0, 0, 11, 16, 8);
VoxelShape SIDE_COLLISION = createCuboidShape(5, 0, 0, 11, 24, 8);
WALL_VOXELS = VoxelHelper.VoxelListBuilder.create(POST, 14)
.add(LOW)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.add(TALL)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.add(POST_COLLISION)
.add(SIDE_COLLISION)
.add(VoxelHelper::mirrorZ)
.add(VoxelHelper::rotateY)
.add(VoxelHelper::mirrorX)
.build();
}
} }

View File

@@ -16,7 +16,6 @@ import org.jetbrains.annotations.Nullable;
public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterloggable { public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterloggable {
public WaterloggableReFramedBlock(Settings settings) { public WaterloggableReFramedBlock(Settings settings) {
super(settings); super(settings);
setDefaultState(getDefaultState().with(Properties.WATERLOGGED, false)); setDefaultState(getDefaultState().with(Properties.WATERLOGGED, false));
} }
@@ -24,7 +23,13 @@ public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterlo
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) { protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(Properties.WATERLOGGED)); super.appendProperties(builder.add(Properties.WATERLOGGED));
} }
@Override
@SuppressWarnings("deprecation")
public boolean hasSidedTransparency(BlockState state) {
return true;
}
@Nullable @Nullable
@Override @Override
public BlockState getPlacementState(ItemPlacementContext ctx) { public BlockState getPlacementState(ItemPlacementContext ctx) {
@@ -34,10 +39,13 @@ public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterlo
} }
@Override @Override
@SuppressWarnings("deprecation")
public FluidState getFluidState(BlockState state) { public FluidState getFluidState(BlockState state) {
return state.get(Properties.WATERLOGGED) ? Fluids.WATER.getStill(false) : super.getFluidState(state); return state.get(Properties.WATERLOGGED) ? Fluids.WATER.getStill(false) : super.getFluidState(state);
} }
@Override
@SuppressWarnings("deprecation")
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState otherState, WorldAccess world, BlockPos pos, BlockPos moved) { 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)); if(state.get(Properties.WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
return super.getStateForNeighborUpdate(state, direction, otherState, world, pos, moved); return super.getStateForNeighborUpdate(state, direction, otherState, world, pos, moved);

View File

@@ -0,0 +1,47 @@
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);
setDefaultState(getDefaultState().with(Properties.WATERLOGGED, false));
}
@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
@SuppressWarnings("deprecation")
public FluidState getFluidState(BlockState state) {
return state.get(Properties.WATERLOGGED) ? Fluids.WATER.getStill(false) : super.getFluidState(state);
}
@Override
@SuppressWarnings("deprecation")
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);
}
}

View File

@@ -25,77 +25,237 @@ public class ReFramedClient implements ClientModInitializer {
//all frames mustn't be on the SOLID layer because they are not opaque! //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])); BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0]));
HELPER.addReFramedModel(ReFramed.id("button_special") , HELPER.auto(new Identifier("block/button"))); // CUBE
HELPER.addReFramedModel(ReFramed.id("button_pressed_special") , HELPER.auto(new Identifier("block/button_pressed"))); HELPER.addReFramedModel("cube" , HELPER.auto(new Identifier("block/cube")));
HELPER.addReFramedModel(ReFramed.id("one_candle_special") , HELPER.auto(new Identifier("block/template_candle"))); // SMALL_CUBE
HELPER.addReFramedModel(ReFramed.id("two_candles_special") , HELPER.auto(new Identifier("block/template_two_candles"))); HELPER.addReFramedModel("small_cube" , HELPER.auto(ReFramed.id("block/small_cube/base")));
HELPER.addReFramedModel(ReFramed.id("three_candles_special") , HELPER.auto(new Identifier("block/template_three_candles"))); // SMALL_CUBES_STEP
HELPER.addReFramedModel(ReFramed.id("four_candles_special") , HELPER.auto(new Identifier("block/template_four_candles"))); HELPER.addReFramedModel("small_cubes_step" , HELPER.autoDouble(ReFramed.id("block/small_cube/base"), ReFramed.id("block/small_cube/step/base")));
HELPER.addReFramedModel(ReFramed.id("carpet_special") , HELPER.auto(new Identifier("block/carpet"))); HELPER.addReFramedModel("small_cubes_step_reverse" , HELPER.autoDouble(ReFramed.id("block/small_cube/step/base"), ReFramed.id("block/small_cube/base")));
HELPER.addReFramedModel(ReFramed.id("cube_special") , HELPER.auto(new Identifier("block/cube"))); // SLAB
HELPER.addReFramedModel(ReFramed.id("door_bottom_left_special") , HELPER.auto(new Identifier("block/door_bottom_left"))); HELPER.addReFramedModel("slab" , HELPER.auto(new Identifier("block/slab")));
HELPER.addReFramedModel(ReFramed.id("door_bottom_right_special") , HELPER.auto(new Identifier("block/door_bottom_right"))); // SLAB_CUBE
HELPER.addReFramedModel(ReFramed.id("door_top_left_special") , HELPER.auto(new Identifier("block/door_top_left"))); HELPER.addReFramedModel("double_slab" , HELPER.autoDouble(new Identifier("block/slab"), new Identifier("block/slab_top")));
HELPER.addReFramedModel(ReFramed.id("door_top_right_special") , HELPER.auto(new Identifier("block/door_top_right"))); // STAIR
HELPER.addReFramedModel(ReFramed.id("door_bottom_left_open_special"), HELPER.auto(new Identifier("block/door_bottom_left_open"))); HELPER.addReFramedModel("stair" , HELPER.auto(ReFramed.id("block/stair/straight")));
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("outers_stair" , HELPER.auto(ReFramed.id("block/stair/double_outer")));
HELPER.addReFramedModel(ReFramed.id("door_top_left_open_special") , HELPER.auto(new Identifier("block/door_top_left_open"))); HELPER.addReFramedModel("inner_stair" , HELPER.auto(ReFramed.id("block/stair/inner")));
HELPER.addReFramedModel(ReFramed.id("door_top_right_open_special") , HELPER.auto(new Identifier("block/door_top_right_open"))); HELPER.addReFramedModel("outer_stair" , HELPER.auto(ReFramed.id("block/stair/outer")));
HELPER.addReFramedModel(ReFramed.id("fence_post_special") , HELPER.auto(new Identifier("block/fence_post"))); HELPER.addReFramedModel("outer_side_stair" , HELPER.auto(ReFramed.id("block/stair/outer_side")));
HELPER.addReFramedModel(ReFramed.id("fence_gate_special") , HELPER.auto(new Identifier("block/template_fence_gate"))); // STAIRS_CUBE
HELPER.addReFramedModel(ReFramed.id("fence_gate_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_open"))); HELPER.addReFramedModel("stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/straight"), ReFramed.id("block/stair/cube/straight")));
HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall"))); HELPER.addReFramedModel("outers_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/double_outer"), ReFramed.id("block/stair/cube/double_outer")));
HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall_open"))); HELPER.addReFramedModel("inner_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/inner"), ReFramed.id("block/stair/cube/inner")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_post_special") , HELPER.auto(new Identifier("block/glass_pane_post"))); HELPER.addReFramedModel("outer_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/outer"), ReFramed.id("block/stair/cube/outer")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_special") , HELPER.auto(new Identifier("block/glass_pane_noside"))); HELPER.addReFramedModel("outer_side_stairs_cube" , HELPER.autoDouble(ReFramed.id("block/stair/outer_side"), ReFramed.id("block/stair/cube/outer_side")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_alt_special"), HELPER.auto(new Identifier("block/glass_pane_noside_alt"))); // HALF_STAIR
HELPER.addReFramedModel(ReFramed.id("pressure_plate_up_special") , HELPER.auto(new Identifier("block/pressure_plate_up"))); HELPER.addReFramedModel("half_stair_down" , HELPER.auto(ReFramed.id("block/half_stair/down")));
HELPER.addReFramedModel(ReFramed.id("pressure_plate_down_special") , HELPER.auto(new Identifier("block/pressure_plate_down"))); HELPER.addReFramedModel("half_stair_side" , HELPER.auto(ReFramed.id("block/half_stair/side")));
HELPER.addReFramedModel(ReFramed.id("slab_special") , HELPER.auto(new Identifier("block/slab"))); // HALF_STAIRS_SLAB
HELPER.addReFramedModel(ReFramed.id("stairs_special") , HELPER.auto(ReFramed.id("block/stairs"))); HELPER.addReFramedModel("half_stairs_slab_down" , HELPER.autoDouble(ReFramed.id("block/half_stair/down"), ReFramed.id("block/half_stair/slab/down")));
HELPER.addReFramedModel(ReFramed.id("double_outer_stairs_special") , HELPER.auto(ReFramed.id("block/double_outer_stairs"))); HELPER.addReFramedModel("half_stairs_slab_side" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/slab/side")));
HELPER.addReFramedModel(ReFramed.id("inner_stairs_special") , HELPER.auto(ReFramed.id("block/inner_stairs"))); // HALF_STAIRS_STAIR
HELPER.addReFramedModel(ReFramed.id("outer_stairs_special") , HELPER.auto(ReFramed.id("block/outer_stairs"))); HELPER.addReFramedModel("half_stairs_stair_down" , HELPER.autoDouble(ReFramed.id("block/half_stair/down"), ReFramed.id("block/half_stair/stair/down")));
HELPER.addReFramedModel(ReFramed.id("outer_side_stairs_special") , HELPER.auto(ReFramed.id("block/outer_side_stairs"))); HELPER.addReFramedModel("half_stairs_stair_side" , HELPER.autoDouble(ReFramed.id("block/half_stair/side"), ReFramed.id("block/half_stair/stair/side")));
HELPER.addReFramedModel(ReFramed.id("trapdoor_bottom_special") , HELPER.auto(new Identifier("block/template_trapdoor_bottom"))); HELPER.addReFramedModel("half_stairs_stair_reverse" , HELPER.autoDouble(ReFramed.id("block/half_stair/stair/side"), ReFramed.id("block/half_stair/side")));
HELPER.addReFramedModel(ReFramed.id("trapdoor_top_special") , HELPER.auto(new Identifier("block/template_trapdoor_top"))); // STEP
HELPER.addReFramedModel(ReFramed.id("wall_post_special") , HELPER.auto(new Identifier("block/template_wall_post"))); HELPER.addReFramedModel("step" , HELPER.auto(ReFramed.id("block/step/down")));
// STEPS_SLAB
//vanilla style models (using "special-sprite replacement" method) HELPER.addReFramedModel("steps_slab" , HELPER.autoDouble(ReFramed.id("block/step/down"), ReFramed.id("block/step/slab/down")));
HELPER.addReFramedModel(ReFramed.id("lever_special") , HELPER.json(ReFramed.id("block/lever"))); HELPER.addReFramedModel("steps_slab_side" , HELPER.autoDouble(ReFramed.id("block/step/side"), ReFramed.id("block/step/slab/side")));
HELPER.addReFramedModel(ReFramed.id("trapdoor_open_special") , HELPER.json(ReFramed.id("block/trapdoor_open"))); // LAYER
HELPER.addReFramedModel(ReFramed.id("lever_on_special") , HELPER.json(ReFramed.id("block/lever_on"))); HELPER.addReFramedModel("layer_1" , HELPER.auto(new Identifier("block/snow_height2")));
//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("layer_2" , HELPER.auto(new Identifier("block/snow_height4")));
HELPER.addReFramedModel(ReFramed.id("fence_side_special") , HELPER.json(ReFramed.id("block/fence_side"))); HELPER.addReFramedModel("layer_3" , HELPER.auto(new Identifier("block/snow_height6")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_side_special") , HELPER.json(ReFramed.id("block/glass_pane_side"))); HELPER.addReFramedModel("layer_4" , HELPER.auto(new Identifier("block/snow_height8")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_side_alt_special") , HELPER.json(ReFramed.id("block/glass_pane_side_alt"))); HELPER.addReFramedModel("layer_5" , HELPER.auto(new Identifier("block/snow_height10")));
HELPER.addReFramedModel(ReFramed.id("wall_side_special") , HELPER.json(ReFramed.id("block/wall_side"))); HELPER.addReFramedModel("layer_6" , HELPER.auto(new Identifier("block/snow_height12")));
HELPER.addReFramedModel(ReFramed.id("wall_side_tall_special") , HELPER.json(ReFramed.id("block/wall_side_tall"))); HELPER.addReFramedModel("layer_7" , HELPER.auto(new Identifier("block/snow_height14")));
HELPER.addReFramedModel("layer_8" , HELPER.auto(new Identifier("block/cube")));
//item only models // PILLAR
HELPER.addReFramedModel(ReFramed.id("button_inventory_special") , HELPER.auto(new Identifier("block/button_inventory"))); HELPER.addReFramedModel("pillar" , HELPER.auto(ReFramed.id("block/pillar")));
HELPER.addReFramedModel(ReFramed.id("fence_inventory_special") , HELPER.auto(new Identifier("block/fence_inventory"))); // WALL
HELPER.addReFramedModel(ReFramed.id("fence_post_inventory_special") , HELPER.auto(ReFramed.id("block/fence_post_inventory"))); HELPER.addReFramedModel("wall_inventory" , HELPER.auto(ReFramed.id("block/wall/inventory/default")));
HELPER.addReFramedModel(ReFramed.id("wall_inventory_special") , HELPER.auto(new Identifier("block/wall_inventory"))); // --------------------- pillar
HELPER.addReFramedModel("wall_core" , HELPER.auto(ReFramed.id("block/wall/pillar/core")));
//item model assignments (in lieu of models/item/___.json) HELPER.addReFramedModel("wall_pillar_low" , HELPER.auto(ReFramed.id("block/wall/pillar/low")));
HELPER.assignItemModel(ReFramed.id("button_inventory_special") , ReFramed.BUTTON); HELPER.addReFramedModel("wall_pillar_tall" , HELPER.auto(ReFramed.id("block/wall/pillar/tall")));
HELPER.assignItemModel(ReFramed.id("carpet_special") , ReFramed.CARPET); HELPER.addReFramedModel("wall_pillar_none" , HELPER.auto(ReFramed.id("block/wall/pillar/none")));
HELPER.assignItemModel(ReFramed.id("cube_special") , ReFramed.CUBE); // --------------------- side
HELPER.assignItemModel(ReFramed.id("fence_inventory_special") , ReFramed.FENCE); HELPER.addReFramedModel("wall_side_low" , HELPER.auto(ReFramed.id("block/wall/side/low")));
HELPER.assignItemModel(ReFramed.id("fence_gate_special") , ReFramed.FENCE_GATE); HELPER.addReFramedModel("wall_side_tall" , HELPER.auto(ReFramed.id("block/wall/side/tall")));
HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.IRON_TRAPDOOR); // --------------------- junction
HELPER.assignItemModel(ReFramed.id("fence_post_inventory_special") , ReFramed.POST); HELPER.addReFramedModel("wall_low_e" , HELPER.auto(ReFramed.id("block/wall/junction/low")));
HELPER.assignItemModel(ReFramed.id("pressure_plate_up_special") , ReFramed.PRESSURE_PLATE); HELPER.addReFramedModel("wall_tall_e" , HELPER.auto(ReFramed.id("block/wall/junction/tall")));
HELPER.assignItemModel(ReFramed.id("slab_special") , ReFramed.SLAB); // --------------------- junction_i
HELPER.assignItemModel(ReFramed.id("stairs_special") , ReFramed.STAIRS); HELPER.addReFramedModel("wall_low_i" , HELPER.auto(ReFramed.id("block/wall/junction/low_i")));
HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.TRAPDOOR); HELPER.addReFramedModel("wall_tall_i" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i")));
HELPER.assignItemModel(ReFramed.id("wall_inventory_special") , ReFramed.WALL); HELPER.addReFramedModel("wall_low_tall_i" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_i")));
// --------------------- junction_c
HELPER.addReFramedModel("wall_low_c" , HELPER.auto(ReFramed.id("block/wall/junction/low_c")));
HELPER.addReFramedModel("wall_tall_c" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c")));
HELPER.addReFramedModel("wall_low_tall_c" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_c")));
HELPER.addReFramedModel("wall_tall_low_c" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_c")));
// --------------------- junction_t
HELPER.addReFramedModel("wall_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_t")));
HELPER.addReFramedModel("wall_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_t")));
HELPER.addReFramedModel("wall_tall_low_c_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_c_t")));
HELPER.addReFramedModel("wall_tall_i_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i_low_t")));
HELPER.addReFramedModel("wall_low_i_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_i_tall_t")));
HELPER.addReFramedModel("wall_low_tall_c_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_tall_c_t")));
HELPER.addReFramedModel("wall_low_c_tall_t" , HELPER.auto(ReFramed.id("block/wall/junction/low_c_tall_t")));
HELPER.addReFramedModel("wall_tall_c_low_t" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c_low_t")));
// --------------------- junction_x
HELPER.addReFramedModel("wall_low_x" , HELPER.auto(ReFramed.id("block/wall/junction/low_x")));
HELPER.addReFramedModel("wall_tall_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_x")));
HELPER.addReFramedModel("wall_tall_i_low_i_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_i_low_i_x")));
HELPER.addReFramedModel("wall_tall_low_t_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_low_t_x")));
HELPER.addReFramedModel("wall_tall_c_low_c_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_c_low_c_x")));
HELPER.addReFramedModel("wall_tall_t_low_x" , HELPER.auto(ReFramed.id("block/wall/junction/tall_t_low_x")));
// PILLAR WALL
HELPER.addReFramedModel("pillars_wall_inventory" , HELPER.autoDouble(ReFramed.id("block/pillar"), ReFramed.id("block/wall/full/inventory/sides")));
HELPER.addReFramedModel("pillars_wall_low" , HELPER.autoDouble(ReFramed.id("block/wall/full/pillar/low"), ReFramed.id("block/wall/full/side/low")));
HELPER.addReFramedModel("pillars_wall_tall" , HELPER.autoDouble(ReFramed.id("block/wall/full/pillar/tall"), ReFramed.id("block/wall/full/side/tall")));
// PANE
HELPER.addReFramedModel("pane_inventory" , HELPER.auto(ReFramed.id("block/pane")));
HELPER.addReFramedModel("pane_post" , HELPER.auto(new Identifier("block/glass_pane_post")));
HELPER.addReFramedModel("pane_side" , HELPER.auto(new Identifier("block/glass_pane_side")));
HELPER.addReFramedModel("pane_side_alt" , HELPER.auto(new Identifier("block/glass_pane_side_alt")));
HELPER.addReFramedModel("pane_noside" , HELPER.auto(new Identifier("block/glass_pane_noside")));
HELPER.addReFramedModel("pane_noside_alt" , HELPER.auto(new Identifier("block/glass_pane_noside_alt")));
// TRAPDOOR
HELPER.addReFramedModel("trapdoor_open" , HELPER.auto(new Identifier("block/oak_trapdoor_open")));
HELPER.addReFramedModel("trapdoor_bottom" , HELPER.auto(new Identifier("block/oak_trapdoor_bottom")));
HELPER.addReFramedModel("trapdoor_top" , HELPER.auto(new Identifier("block/oak_trapdoor_top")));
// DOOR
HELPER.addReFramedModel("door_inventory" , HELPER.auto(ReFramed.id("block/door")));
// BUTTON
HELPER.addReFramedModel("button_inventory" , HELPER.auto(new Identifier("block/button_inventory")));
HELPER.addReFramedModel("button" , HELPER.auto(new Identifier("block/button")));
HELPER.addReFramedModel("button_pressed" , HELPER.auto(new Identifier("block/button_pressed")));
// POST
HELPER.addReFramedModel("post" , HELPER.auto(ReFramed.id("block/post")));
// FENCE
HELPER.addReFramedModel("fence_inventory" , HELPER.auto(ReFramed.id("block/fence/inventory")));
HELPER.addReFramedModel("fence_core" , HELPER.auto(ReFramed.id("block/fence/core")));
HELPER.addReFramedModel("fence_side_off" , HELPER.auto(ReFramed.id("block/fence/side_off")));
HELPER.addReFramedModel("fence_side_on" , HELPER.auto(ReFramed.id("block/fence/side_on")));
// POST FENCE
HELPER.addReFramedModel("post_fence_inventory" , HELPER.autoDouble(ReFramed.id("block/post"), ReFramed.id("block/fence/full/inventory")));
HELPER.addReFramedModel("post_fence_side" , HELPER.autoDouble(ReFramed.id("block/fence/full/side_core"), ReFramed.id("block/fence/full/side_bars")));
// SLABS STAIR
HELPER.addReFramedModel("slabs_stair" , HELPER.autoDouble(ReFramed.id("block/slabs_stair/slab"), ReFramed.id("block/slabs_stair/step")));
HELPER.addReFramedModel("slabs_stair_side" , HELPER.autoDouble(ReFramed.id("block/slabs_stair/side/slab"), ReFramed.id("block/slabs_stair/side/step")));
// SLABS OUTER STAIR
HELPER.addReFramedModel("slabs_outer_stair" , HELPER.autoDouble(ReFramed.id("block/slabs_stair/outer/slab"), ReFramed.id("block/slabs_stair/outer/cube")));
HELPER.addReFramedModel("slabs_outer_stair_side" , HELPER.autoDouble(ReFramed.id("block/slabs_stair/outer/side/slab"), ReFramed.id("block/slabs_stair/outer/side/cube")));
// SLABS OUTER STAIR
HELPER.addReFramedModel("slabs_inner_stair" , HELPER.autoDouble(ReFramed.id("block/slabs_stair/inner/slab"), ReFramed.id("block/slabs_stair/inner/half_stair")));
HELPER.addReFramedModel("slabs_inner_stair_side" , HELPER.autoDouble(ReFramed.id("block/slabs_stair/inner/side/slab"), ReFramed.id("block/slabs_stair/inner/side/half_stair")));
// SLABS OUTER STAIR
HELPER.addReFramedModel("steps_cross" , HELPER.autoDouble(ReFramed.id("block/step/down"), ReFramed.id("block/step/cross")));
// HALF STAIRS CUBE STAIR
HELPER.addReFramedModel("half_stairs_cube_stair" , HELPER.autoDouble(ReFramed.id("block/half_stair/base"), ReFramed.id("block/half_stair/stair/cube")));
HELPER.addReFramedModel("half_stairs_cube_stair_side" , HELPER.autoDouble(ReFramed.id("block/half_stair/base_side"), ReFramed.id("block/half_stair/stair/cube_side")));
// HALF STAIRS STEP STAIR
HELPER.addReFramedModel("half_stairs_step_stair_1" , HELPER.autoDouble(ReFramed.id("block/half_stair/base"), ReFramed.id("block/half_stair/stair/step_1")));
HELPER.addReFramedModel("half_stairs_step_stair_side_1", HELPER.autoDouble(ReFramed.id("block/half_stair/base_side"), ReFramed.id("block/half_stair/stair/step_side_1")));
HELPER.addReFramedModel("half_stairs_step_stair_2" , HELPER.autoDouble(ReFramed.id("block/half_stair/base"), ReFramed.id("block/half_stair/stair/step_2")));
HELPER.addReFramedModel("half_stairs_step_stair_side_2", HELPER.autoDouble(ReFramed.id("block/half_stair/base_side"), ReFramed.id("block/half_stair/stair/step_side_2")));
// HALF LAYER
// --------------------- east
HELPER.addReFramedModel("half_layer_2" , HELPER.auto(ReFramed.id("block/half_layer/east/layer_2")));
HELPER.addReFramedModel("half_layer_4" , HELPER.auto(ReFramed.id("block/half_layer/east/layer_4")));
HELPER.addReFramedModel("half_layer_6" , HELPER.auto(ReFramed.id("block/half_layer/east/layer_6")));
HELPER.addReFramedModel("half_layer_8" , HELPER.auto(ReFramed.id("block/half_layer/east/layer_8")));
HELPER.addReFramedModel("half_layer_10" , HELPER.auto(ReFramed.id("block/half_layer/east/layer_10")));
HELPER.addReFramedModel("half_layer_12" , HELPER.auto(ReFramed.id("block/half_layer/east/layer_12")));
HELPER.addReFramedModel("half_layer_14" , HELPER.auto(ReFramed.id("block/half_layer/east/layer_14")));
HELPER.addReFramedModel("half_layer_16" , HELPER.auto(ReFramed.id("block/half_layer/east/layer_16")));
// --------------------- side
HELPER.addReFramedModel("half_layer_side_2" , HELPER.auto(ReFramed.id("block/half_layer/side/layer_2")));
HELPER.addReFramedModel("half_layer_side_4" , HELPER.auto(ReFramed.id("block/half_layer/side/layer_4")));
HELPER.addReFramedModel("half_layer_side_6" , HELPER.auto(ReFramed.id("block/half_layer/side/layer_6")));
HELPER.addReFramedModel("half_layer_side_8" , HELPER.auto(ReFramed.id("block/half_layer/side/layer_8")));
HELPER.addReFramedModel("half_layer_side_10" , HELPER.auto(ReFramed.id("block/half_layer/side/layer_10")));
HELPER.addReFramedModel("half_layer_side_12" , HELPER.auto(ReFramed.id("block/half_layer/side/layer_12")));
HELPER.addReFramedModel("half_layer_side_14" , HELPER.auto(ReFramed.id("block/half_layer/side/layer_14")));
HELPER.addReFramedModel("half_layer_side_16" , HELPER.auto(ReFramed.id("block/half_layer/side/layer_16")));
// SLAB HALF LAYER
HELPER.addReFramedModel("slabs_half_inventory" , HELPER.autoDouble(new Identifier("block/slab"), ReFramed.id("block/half_layer/slab/east/layer_4")));
// STEP HALF LAYER
HELPER.addReFramedModel("steps_half_inventory" , HELPER.autoDouble(ReFramed.id("block/step/down"), ReFramed.id("block/half_layer/slab/east/layer_4")));
// --------------------- east
HELPER.addReFramedModel("second_half_layer_2" , HELPER.auto(ReFramed.id("block/half_layer/slab/east/layer_2")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_4" , HELPER.auto(ReFramed.id("block/half_layer/slab/east/layer_4")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_6" , HELPER.auto(ReFramed.id("block/half_layer/slab/east/layer_6")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_8" , HELPER.auto(ReFramed.id("block/half_layer/slab/east/layer_8")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_10" , HELPER.auto(ReFramed.id("block/half_layer/slab/east/layer_10")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_12" , HELPER.auto(ReFramed.id("block/half_layer/slab/east/layer_12")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_14" , HELPER.auto(ReFramed.id("block/half_layer/slab/east/layer_14")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_16" , HELPER.auto(ReFramed.id("block/half_layer/slab/east/layer_16")).setThemeIndex(2));
// --------------------- side
HELPER.addReFramedModel("second_half_layer_side_2" , HELPER.auto(ReFramed.id("block/half_layer/slab/side/layer_2")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_side_4" , HELPER.auto(ReFramed.id("block/half_layer/slab/side/layer_4")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_side_6" , HELPER.auto(ReFramed.id("block/half_layer/slab/side/layer_6")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_side_8" , HELPER.auto(ReFramed.id("block/half_layer/slab/side/layer_8")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_side_10" , HELPER.auto(ReFramed.id("block/half_layer/slab/side/layer_10")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_side_12" , HELPER.auto(ReFramed.id("block/half_layer/slab/side/layer_12")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_side_14" , HELPER.auto(ReFramed.id("block/half_layer/slab/side/layer_14")).setThemeIndex(2));
HELPER.addReFramedModel("second_half_layer_side_16" , HELPER.auto(ReFramed.id("block/half_layer/slab/side/layer_16")).setThemeIndex(2));
// HALF SLAB
HELPER.addReFramedModel("half_slab" , HELPER.auto(ReFramed.id("block/half_slab/default")));
// HALF SLABS SLAB
HELPER.addReFramedModel("half_slabs_slab" , HELPER.autoDouble(ReFramed.id("block/half_slab/default"), ReFramed.id("block/half_slab/complement")));
// SLABS LAYER
HELPER.addReFramedModel("slabs_layer_2" , HELPER.autoDouble(new Identifier("block/slab"), ReFramed.id("block/layer_top/layer_2")));
HELPER.addReFramedModel("slabs_layer_4" , HELPER.autoDouble(new Identifier("block/slab"), ReFramed.id("block/layer_top/layer_4")));
HELPER.addReFramedModel("slabs_layer_6" , HELPER.autoDouble(new Identifier("block/slab"), ReFramed.id("block/layer_top/layer_6")));
HELPER.addReFramedModel("slabs_layer_8" , HELPER.autoDouble(new Identifier("block/slab"), ReFramed.id("block/layer_top/layer_8")));
// item model assignments (in lieu of models/item/___.json)
HELPER.assignItemModel("cube" , ReFramed.CUBE);
HELPER.assignItemModel("small_cube" , ReFramed.SMALL_CUBE);
HELPER.assignItemModel("small_cubes_step" , ReFramed.SMALL_CUBES_STEP);
HELPER.assignItemModel("slab" , ReFramed.SLAB);
HELPER.assignItemModel("double_slab" , ReFramed.SLABS_CUBE);
HELPER.assignItemModel("stair" , ReFramed.STAIR);
HELPER.assignItemModel("stairs_cube" , ReFramed.STAIRS_CUBE);
HELPER.assignItemModel("half_stair_down" , ReFramed.HALF_STAIR);
HELPER.assignItemModel("half_stairs_slab_down" , ReFramed.HALF_STAIRS_SLAB);
HELPER.assignItemModel("half_stairs_stair_down" , ReFramed.HALF_STAIRS_STAIR);
HELPER.assignItemModel("step" , ReFramed.STEP);
HELPER.assignItemModel("steps_slab" , ReFramed.STEPS_SLAB);
HELPER.assignItemModel("layer_1" , ReFramed.LAYER);
HELPER.assignItemModel("pillar" , ReFramed.PILLAR);
HELPER.assignItemModel("pillars_wall_inventory" , ReFramed.PILLARS_WALL);
HELPER.assignItemModel("wall_inventory" , ReFramed.WALL);
HELPER.assignItemModel("pane_inventory" , ReFramed.PANE);
HELPER.assignItemModel("trapdoor_bottom" , ReFramed.TRAPDOOR);
HELPER.assignItemModel("door_inventory" , ReFramed.DOOR);
HELPER.assignItemModel("button_inventory" , ReFramed.BUTTON);
HELPER.assignItemModel("post" , ReFramed.POST);
HELPER.assignItemModel("fence_inventory" , ReFramed.FENCE);
HELPER.assignItemModel("post_fence_inventory" , ReFramed.POST_FENCE);
HELPER.assignItemModel("slabs_stair" , ReFramed.SLABS_STAIR);
HELPER.assignItemModel("slabs_outer_stair" , ReFramed.SLABS_OUTER_STAIR);
HELPER.assignItemModel("slabs_inner_stair" , ReFramed.SLABS_INNER_STAIR);
HELPER.assignItemModel("steps_cross" , ReFramed.STEPS_CROSS);
HELPER.assignItemModel("half_stairs_cube_stair" , ReFramed.HALF_STAIRS_CUBE_STAIR);
HELPER.assignItemModel("half_stairs_step_stair_1", ReFramed.HALF_STAIRS_STEP_STAIR);
HELPER.assignItemModel("half_layer_2" , ReFramed.HALF_LAYER);
HELPER.assignItemModel("slabs_half_inventory" , ReFramed.SLABS_HALF_LAYER);
HELPER.assignItemModel("steps_half_inventory" , ReFramed.STEPS_HALF_LAYER);
HELPER.assignItemModel("half_slab" , ReFramed.HALF_SLAB);
HELPER.assignItemModel("half_slabs_slab" , ReFramed.HALF_SLABS_SLAB);
HELPER.assignItemModel("slabs_layer_2" , ReFramed.SLABS_LAYER);
} }
private void privateInit() { private void privateInit() {
//set up some magic to force chunk rerenders when you change a template (see TemplateEntity) //set up some magic to force chunk re-renders when you change a template (see TemplateEntity)
ReFramed.chunkRerenderProxy = (world, pos) -> { ReFramed.chunkRerenderProxy = (world, pos) -> {
if(world == MinecraftClient.getInstance().world) { if(world == MinecraftClient.getInstance().world) {
MinecraftClient.getInstance().worldRenderer.scheduleBlockRender( MinecraftClient.getInstance().worldRenderer.scheduleBlockRender(
@@ -109,7 +269,7 @@ public class ReFramedClient implements ClientModInitializer {
//supporting code for the TemplatesModelProvider //supporting code for the TemplatesModelProvider
ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> PROVIDER); //block models ModelLoadingRegistry.INSTANCE.registerResourceProvider(rm -> PROVIDER); //block models
ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> PROVIDER); //item models ModelLoadingRegistry.INSTANCE.registerVariantProvider(rm -> PROVIDER); //item models
ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(new SimpleSynchronousResourceReloadListener() { ResourceManagerHelper.get(ResourceType.CLIENT_RESOURCES).registerReloadListener(new SimpleSynchronousResourceReloadListener() {
@Override public Identifier getFabricId() { return ReFramed.id("dump-caches"); } @Override public Identifier getFabricId() { return ReFramed.id("dump-caches"); }
@Override public void reload(ResourceManager blah) { PROVIDER.dumpCache(); } @Override public void reload(ResourceManager blah) { PROVIDER.dumpCache(); }

View File

@@ -1,8 +1,11 @@
package fr.adrien1106.reframed.client; 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.UnbakedAutoRetexturedModel;
import fr.adrien1106.reframed.client.model.UnbakedDoubleRetexturedModel;
import fr.adrien1106.reframed.client.model.UnbakedJsonRetexturedModel; 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.Renderer;
import net.fabricmc.fabric.api.renderer.v1.RendererAccess; import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
import net.fabricmc.loader.api.FabricLoader; import net.fabricmc.loader.api.FabricLoader;
@@ -23,24 +26,32 @@ public class ReFramedClientHelper {
private final ReFramedModelProvider prov; private final ReFramedModelProvider prov;
public UnbakedModel auto(Identifier parent) {
public UnbakedRetexturedModel auto(Identifier parent) {
return new UnbakedAutoRetexturedModel(parent); return new UnbakedAutoRetexturedModel(parent);
} }
public UnbakedModel json(Identifier parent) { public UnbakedRetexturedModel json(Identifier parent) {
return new UnbakedJsonRetexturedModel(parent); return new UnbakedJsonRetexturedModel(parent);
} }
public void addReFramedModel(Identifier id, UnbakedModel unbaked) { public UnbakedModel autoDouble(Identifier first, Identifier second) {
prov.addReFramedModel(id, unbaked); return new UnbakedDoubleRetexturedModel(
auto(first),
auto(second)
);
} }
public void assignItemModel(Identifier model_id, ItemConvertible... item_convertibles) { public void addReFramedModel(String id, UnbakedModel unbaked) {
prov.assignItemModel(model_id, item_convertibles); prov.addReFramedModel(ReFramed.id(id + "_special"), unbaked);
} }
public CamoAppearanceManager getCamoApperanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) { public void assignItemModel(String id, ItemConvertible... item_convertibles) {
return prov.getCamoApperanceManager(spriteLookup); prov.assignItemModel(ReFramed.id(id + "_special"), item_convertibles);
}
public CamoAppearanceManager getCamoAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
return prov.getCamoAppearanceManager(spriteLookup);
} }
public @NotNull Renderer getFabricRenderer() { public @NotNull Renderer getFabricRenderer() {

View File

@@ -37,14 +37,14 @@ 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. //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. //So, instead, we use a ModelVariantProvider to redirect attempts to load the item:id#inventory model.
@Override @Override
public @Nullable UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) { public @Nullable UnbakedModel loadModelVariant(ModelIdentifier model, ModelProviderContext context) {
Identifier customModelId = itemAssignments.get(modelId); Identifier custom_model = itemAssignments.get(model);
return customModelId == null ? null : loadModelResource(customModelId, context); return custom_model == null ? null : loadModelResource(custom_model, context);
} }
/// camo appearance manager cache /// camo appearance manager cache
public CamoAppearanceManager getCamoApperanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) { public CamoAppearanceManager getCamoAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
//This is kind of needlessly sketchy using the "volatile double checked locking" pattern. //This is kind of needlessly sketchy using the "volatile double checked locking" pattern.
//I'd like all frame models to use the same CamoApperanceManager, despite the model //I'd like all frame models to use the same CamoApperanceManager, despite the model
//baking process happening concurrently on several threads, but I also don't want to //baking process happening concurrently on several threads, but I also don't want to
@@ -70,6 +70,7 @@ public class ReFramedModelProvider implements ModelResourceProvider, ModelVarian
} }
public void dumpCache() { public void dumpCache() {
CamoAppearanceManager.dumpCahe();
appearanceManager = null; //volatile write appearanceManager = null; //volatile write
} }

View File

@@ -0,0 +1,63 @@
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.render.model.BakedQuad;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
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.ArrayList;
import java.util.List;
import java.util.function.Supplier;
@Environment(EnvType.CLIENT)
public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements MultiRetexturableModel {
private final RetexturingBakedModel model_1, model_2;
public DoubleRetexturingBakedModel(RetexturingBakedModel model_1, RetexturingBakedModel 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 List<BakedQuad> getQuads(BlockState blockState, Direction face, Random rand) {
List<BakedQuad> quads = new ArrayList<>(model_1.getQuads(blockState, face, rand));
quads.addAll(model_2.getQuads(blockState, face, rand));
return quads;
}
@Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
model_1.emitBlockQuads(world, state, pos, randomSupplier, context);
model_2.emitBlockQuads(world, state, pos, randomSupplier, 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<RetexturingBakedModel> models() {
return List.of(model_1, model_2);
}
}

View File

@@ -4,7 +4,8 @@ import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import org.jetbrains.annotations.Nullable;
public interface DynamicBakedModel { public interface DynamicBakedModel {
BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos); BakedModel computeQuads(@Nullable BlockRenderView level, BlockState origin_state, @Nullable BlockPos pos, int theme_index);
} }

View File

@@ -1,83 +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, RetexturingBakedModel.RetexturingTransformer transform) {
MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder();
QuadEmitter emitter = builder.getEmitter();
mesh.forEach(quad -> {
int i = -1;
do {
emitter.copyFrom(quad);
i = transform.transform(emitter, i);
} while (i > 0);
});
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;
};
}
}

View File

@@ -0,0 +1,8 @@
package fr.adrien1106.reframed.client.model;
import java.util.List;
public interface MultiRetexturableModel {
List<RetexturingBakedModel> models();
}

View File

@@ -1,11 +1,14 @@
package fr.adrien1106.reframed.client.model; 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.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import org.joml.Vector3f; 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 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) { public static QuadPosBounds read(QuadView quad) {
@@ -76,4 +79,15 @@ public record QuadPosBounds(float min_x, float max_x, float min_y, float max_y,
quad.pos(i, pos); 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);
}
} }

View File

@@ -1,10 +1,13 @@
package fr.adrien1106.reframed.client.model; 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.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadView;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@Environment(EnvType.CLIENT)
public record QuadUvBounds(float minU, float maxU, float minV, float maxV) { public record QuadUvBounds(float minU, float maxU, float minV, float maxV) {
public static QuadUvBounds read(QuadView quad) { public static QuadUvBounds read(QuadView quad) {
float u0 = quad.u(0), u1 = quad.u(1), u2 = quad.u(2), u3 = quad.u(3); float u0 = quad.u(0), u1 = quad.u(1), u2 = quad.u(2), u3 = quad.u(3);

View File

@@ -1,20 +1,24 @@
package fr.adrien1106.reframed.client.model; package fr.adrien1106.reframed.client.model;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import fr.adrien1106.reframed.block.ReFramedEntity; import fr.adrien1106.reframed.block.ReFramedEntity;
import fr.adrien1106.reframed.client.model.apperance.SpriteProperties; import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.mixin.MinecraftAccessor; import fr.adrien1106.reframed.mixin.MinecraftAccessor;
import fr.adrien1106.reframed.client.model.apperance.CamoAppearance; import fr.adrien1106.reframed.client.model.apperance.CamoAppearance;
import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager; import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance; import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance;
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh; import fr.adrien1106.reframed.util.blocks.ThemeableBlockEntity;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView; import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.*;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel; import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.fabricmc.fabric.api.renderer.v1.model.ModelHelper;
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext; import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
@@ -24,35 +28,85 @@ import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier; import java.util.function.Supplier;
public abstract class RetexturingBakedModel extends ForwardingBakedModel { public abstract class RetexturingBakedModel extends ForwardingBakedModel {
public RetexturingBakedModel(BakedModel baseModel, CamoAppearanceManager tam, ModelBakeSettings settings, BlockState itemModelState, boolean ao) { public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state) {
this.wrapped = baseModel; //field from the superclass; vanilla getQuads etc. will delegate through to this this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this
this.tam = tam; this.appearance_manager = tam;
this.uvlock = settings.isUvLocked(); this.theme_index = theme_index;
this.itemModelState = itemModelState; this.uv_lock = settings.isUvLocked();
this.ao = ao; this.item_state = item_state;
} }
protected final CamoAppearanceManager tam; protected final CamoAppearanceManager appearance_manager;
protected final boolean uvlock; protected final int theme_index;
protected final BlockState itemModelState; protected final boolean uv_lock;
protected final boolean ao; protected final BlockState item_state;
protected record MeshCacheKey(BlockState state, TransformCacheKey transform) {} protected record MeshCacheKey(Object state_key, CamoAppearance appearance, int model_id) {}
protected final ConcurrentMap<MeshCacheKey, Mesh> retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache /** cache that store retextured models */
protected record TransformCacheKey(CamoAppearance appearance, int model_id) {} // self culling cache of the models not made thread local so that it is only computed once
protected final ConcurrentMap<TransformCacheKey, RetexturingTransformer> retextured_transforms = new ConcurrentHashMap<>(); protected final Cache<MeshCacheKey, Mesh> RETEXTURED_MESH_CACHE = CacheBuilder.newBuilder().maximumSize(256).build();
protected static final Direction[] DIRECTIONS = Direction.values(); /** cache that stores the base meshes which has the size of the amount of models */
protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1]; protected final Object2ObjectLinkedOpenHashMap<Object, Mesh> BASE_MESH_CACHE =
static { System.arraycopy(DIRECTIONS, 0, DIRECTIONS_AND_NULL, 0, DIRECTIONS.length); } new Object2ObjectLinkedOpenHashMap<>(2, 0.25f) {
@Override
protected abstract Mesh getBaseMesh(BlockState state); protected void rehash(int v) {}
};
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;
}
private List<BakedQuad>[] quads = null;
@Override
public List<BakedQuad> getQuads(BlockState state, Direction face, Random rand) {
if (quads == null) {
quads = ModelHelper.toQuadLists(
getRetexturedMesh(
new MeshCacheKey(
hashCode(),
appearance_manager.getDefaultAppearance(theme_index),
0
),
state == null ? item_state : state
)
);
}
return quads[ModelHelper.toFaceIndex(face)];
}
public void setCamo(BlockRenderView world, BlockState state, BlockPos pos) {
if (state == null || state.isAir()) {
quads = null;
return;
}
CamoAppearance camo = appearance_manager.getCamoAppearance(world, state, pos, theme_index, false);
MeshCacheKey key = new MeshCacheKey(
hashCode(),
camo,
0
);
quads = ModelHelper.toQuadLists(camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state));
}
protected abstract Mesh convertModel(BlockState state);
@Override @Override
public boolean isVanillaAdapter() { public boolean isVanillaAdapter() {
@@ -61,39 +115,47 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
@Override @Override
public Sprite getParticleSprite() { public Sprite getParticleSprite() {
return tam.getDefaultAppearance().getSprites(Direction.UP, 0).get(0).sprite(); return appearance_manager.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite();
} }
public int getThemeIndex() {
return theme_index;
}
@Override @Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) { public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
BlockState theme = (world.getBlockEntityRenderData(pos) instanceof BlockState s) ? s : null; BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null;
QuadEmitter quad_emitter = context.getEmitter(); QuadEmitter quad_emitter = context.getEmitter();
if(theme == null || theme.isAir()) { if(theme == null || theme.isAir()) {
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(), 0)), 0).outputTo(quad_emitter); getRetexturedMesh(
new MeshCacheKey(
hashCode(),
appearance_manager.getDefaultAppearance(theme_index),
0
),
state
).outputTo(quad_emitter);
return; return;
} }
if(theme.getBlock() == Blocks.BARRIER) return; if(theme.getBlock() == Blocks.BARRIER) return;
CamoAppearance camo = tam.getCamoAppearance(world, theme, pos); CamoAppearance camo = appearance_manager.getCamoAppearance(world, theme, pos, theme_index, false);
long seed = theme.getRenderingSeed(pos); long seed = theme.getRenderingSeed(pos);
int model_id = 0; int model_id = 0;
if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed); if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, world, pos, 0); int tint = 0xFF000000 | MinecraftClient.getInstance().getBlockColors().getColor(theme, world, pos, 0);
Mesh untintedMesh = getUntintedRetexturedMesh( MeshCacheKey key = new MeshCacheKey(hashCode(), camo, model_id);
new MeshCacheKey( // do not clutter the cache with single-use meshes
state, Mesh untintedMesh = camo.hashCode() == -1 ? transformMesh(key, state) : getRetexturedMesh(key, state);
new TransformCacheKey(camo, model_id)
),
seed
);
//The specific tint might vary a lot; imagine grass color smoothly changing. Trying to bake the tint into //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. //the cached mesh will pollute it with a ton of single-use meshes with only slightly different colors.
if(tint == 0xFFFFFFFF) { if(tint == 0xFFFFFFFF) {
untintedMesh.outputTo(quad_emitter); untintedMesh.outputTo(quad_emitter);
} else { } else {
context.pushTransform(new TintingTransformer(camo, tint, seed)); context.pushTransform(new TintingTransformer(camo, model_id, tint));
untintedMesh.outputTo(quad_emitter); untintedMesh.outputTo(quad_emitter);
context.popTransform(); context.popTransform();
} }
@@ -103,106 +165,84 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) { 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 //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 //none of this is accessible unless you're in creative mode doing ctrl-pick btw
CamoAppearance nbtAppearance; CamoAppearance appearance;
int tint; int tint;
BlockState theme = ReFramedEntity.readStateFromItem(stack); BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index);
if(!theme.isAir()) { if(!theme.isAir()) {
nbtAppearance = tam.getCamoAppearance(null, theme, null); appearance = appearance_manager.getCamoAppearance(null, theme, null, theme_index, true);
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0); tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
} else { } else {
nbtAppearance = tam.getDefaultAppearance(); appearance = appearance_manager.getDefaultAppearance(theme_index);
tint = 0xFFFFFFFF; 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(); QuadEmitter quad_emitter = context.getEmitter();
if(tint == 0xFFFFFFFF) { if(tint == 0xFFFFFFFF) {
untintedMesh.outputTo(quad_emitter); untintedMesh.outputTo(quad_emitter);
} else { } else {
context.pushTransform(new TintingTransformer(nbtAppearance, tint, 0)); context.pushTransform(new TintingTransformer(appearance, 0, tint));
untintedMesh.outputTo(quad_emitter); untintedMesh.outputTo(quad_emitter);
context.popTransform(); context.popTransform();
} }
} }
protected Mesh getUntintedRetexturedMesh(MeshCacheKey key, long seed) { public boolean useAmbientOcclusion(BlockRenderView view, BlockPos pos) {
return retextured_meshes.computeIfAbsent(key, (k) -> createUntintedRetexturedMesh(k, seed)); if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)) return false;
BlockState theme = frame_entity.getTheme(theme_index);
CamoAppearance appearance = appearance_manager
.getCamoAppearance(view, theme, pos, theme_index, false);
long seed = theme.getRenderingSeed(pos);
int model_id = 0;
if (appearance instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
return appearance.getAO(model_id);
}
protected Mesh getRetexturedMesh(MeshCacheKey key, BlockState state) {
if (RETEXTURED_MESH_CACHE.asMap().containsKey(key)) return RETEXTURED_MESH_CACHE.getIfPresent(key);
Mesh mesh = transformMesh(key, state);
RETEXTURED_MESH_CACHE.put(key, mesh);
return mesh;
} }
protected Mesh createUntintedRetexturedMesh(MeshCacheKey key, long seed) { protected Mesh transformMesh(MeshCacheKey key, BlockState state) {
RetexturingTransformer transformer = retextured_transforms.computeIfAbsent(key.transform, (k) -> new RetexturingTransformer(k.appearance, seed)); MeshBuilder builder = ReFramedClient.HELPER.getFabricRenderer().meshBuilder();
return MeshTransformUtil.pretransformMesh(getBaseMesh(key.state), transformer); 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, 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();
} }
public class RetexturingTransformer {
private final long seed;
protected RetexturingTransformer(CamoAppearance ta, long seed) {
this.ta = ta;
this.seed = seed;
}
protected final CamoAppearance ta;
public int transform(QuadEmitter quad, int i) {
if(quad.tag() == 0) return 0; //Pass the quad through unmodified.
Direction direction = quad.nominalFace();
List<SpriteProperties> sprites = ta.getSprites(direction, seed);
if (i == -1) i = sprites.size();
SpriteProperties properties = sprites.get(sprites.size() - i);
i--;
QuadPosBounds bounds = properties.bounds();
if (bounds == null) { // sprite applies anywhere e.g. default behaviour
quad.material(ta.getRenderMaterial(ao));
quad.spriteBake(
properties.sprite(),
MutableQuadView.BAKE_NORMALIZED
| properties.flags()
| (uvlock ? MutableQuadView.BAKE_LOCK_UV : 0)
);
quad.tag(i+1);
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(ta.getRenderMaterial(ao));
bounds.intersection(origin_bounds, direction.getAxis()).apply(quad, origin_bounds);
quad.spriteBake( // TODO check if the flags are usefull because it seems to be braking the functioning of it
properties.sprite(),
MutableQuadView.BAKE_NORMALIZED
| MutableQuadView.BAKE_LOCK_UV
);
quad.tag(i+1);
quad.emit();
return i;
}
}
protected static class TintingTransformer implements RenderContext.QuadTransform { protected static class TintingTransformer implements RenderContext.QuadTransform {
private final long seed; private final CamoAppearance appearance;
protected TintingTransformer(CamoAppearance ta, int tint, long seed) { private final int model_id;
this.ta = ta; private final int tint;
protected TintingTransformer(CamoAppearance appearance, int model_id, int tint) {
this.appearance = appearance;
this.model_id = model_id;
this.tint = tint; this.tint = tint;
this.seed = seed;
} }
protected final CamoAppearance ta;
protected final int tint;
@Override @Override
public boolean transform(MutableQuadView quad) { public boolean transform(MutableQuadView quad) {
if(quad.tag() == 0) return true; int camo_quad_index = quad.tag() - ((quad.tag() >>> 8) << 8);
if(camo_quad_index == 0) return true;
if(appearance.hasColor(quad.nominalFace(), model_id, camo_quad_index)) quad.color(tint, tint, tint, tint);
if(ta.hasColor(quad.nominalFace(), seed, quad.tag())) quad.color(tint, tint, tint, tint);
return true; return true;
} }
} }

View File

@@ -12,7 +12,6 @@ import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.Baker; import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.ModelBakeSettings; import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
@@ -20,54 +19,30 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import org.jetbrains.annotations.Nullable; 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; import java.util.function.Function;
public class UnbakedAutoRetexturedModel implements UnbakedModel { public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel {
public UnbakedAutoRetexturedModel(Identifier parent) { public UnbakedAutoRetexturedModel(Identifier parent) {
this.parent = parent; super(parent);
} item_state = Blocks.AIR.getDefaultState();
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);
} }
@Nullable @Nullable
@Override @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( return new RetexturingBakedModel(
baker.bake(parent, modelBakeSettings), baker.bake(parent, bake_settings),
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup), ReFramedClient.HELPER.getCamoAppearanceManager(texture_getter),
modelBakeSettings, theme_index,
itemModelState, bake_settings,
ao item_state
) { ) {
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>(); protected Mesh convertModel(BlockState state) {
@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) {
Renderer r = ReFramedClient.HELPER.getFabricRenderer(); Renderer r = ReFramedClient.HELPER.getFabricRenderer();
MeshBuilder builder = r.meshBuilder(); MeshBuilder builder = r.meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();
RenderMaterial mat = tam.getCachedMaterial(state, false); RenderMaterial mat = appearance_manager.getCachedMaterial(state, false);
Random rand = Random.create(42); Random rand = Random.create(42);

View File

@@ -0,0 +1,47 @@
package fr.adrien1106.reframed.client.model;
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(
(RetexturingBakedModel) model_1.bake(baker, texture_getter, model_bake_settings, identifier),
(RetexturingBakedModel) model_2.bake(baker, texture_getter, model_bake_settings, identifier)
);
}
}

View File

@@ -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.MeshBuilder;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter; import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.minecraft.block.BlockState; 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.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier; import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.screen.PlayerScreenHandler; import net.minecraft.screen.PlayerScreenHandler;
@@ -17,65 +20,37 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random; import net.minecraft.util.math.random.Random;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function; import java.util.function.Function;
public class UnbakedJsonRetexturedModel implements UnbakedModel { public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
public UnbakedJsonRetexturedModel(Identifier parent) { public UnbakedJsonRetexturedModel(Identifier parent) {
this.parent = parent; super(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);
} }
@Nullable @Nullable
@Override @Override
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) { public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings bake_settings, Identifier identifier) {
Direction[] DIRECTIONS = RetexturingBakedModel.DIRECTIONS; Direction[] directions = Direction.values();
Sprite[] specialSprites = new Sprite[DIRECTIONS.length]; Sprite[] sprites = new Sprite[directions.length];
for(int i = 0; i < DIRECTIONS.length; i++) { for(int i = 0; i < directions.length; i++) {
SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + DIRECTIONS[i].getName())); 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 + " !"); sprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
} }
BakedModel model = baker.bake(parent, modelBakeSettings);
return new RetexturingBakedModel( return new RetexturingBakedModel(
model, baker.bake(parent, bake_settings),
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup), ReFramedClient.HELPER.getCamoAppearanceManager(spriteLookup),
modelBakeSettings, theme_index,
itemModelState, bake_settings,
ao item_state
) { ) {
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>(); protected Mesh convertModel(BlockState state) {
@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) {
Renderer r = ReFramedClient.HELPER.getFabricRenderer(); Renderer r = ReFramedClient.HELPER.getFabricRenderer();
MeshBuilder builder = r.meshBuilder(); MeshBuilder builder = r.meshBuilder();
QuadEmitter emitter = builder.getEmitter(); QuadEmitter emitter = builder.getEmitter();
RenderMaterial mat = tam.getCachedMaterial(state, false); RenderMaterial mat = appearance_manager.getCachedMaterial(state, false);
Random rand = Random.create(42); Random rand = Random.create(42);
@@ -84,9 +59,9 @@ public class UnbakedJsonRetexturedModel implements UnbakedModel {
emitter.fromVanilla(quad, mat, cullFace); emitter.fromVanilla(quad, mat, cullFace);
QuadUvBounds bounds = QuadUvBounds.read(emitter); QuadUvBounds bounds = QuadUvBounds.read(emitter);
for(int i = 0; i < specialSprites.length; i++) { for(int i = 0; i < sprites.length; i++) {
if(bounds.displaysSprite(specialSprites[i])) { if(bounds.displaysSprite(sprites[i])) {
bounds.normalizeUv(emitter, specialSprites[i]); bounds.normalizeUv(emitter, sprites[i]);
emitter.tag(i + 1); emitter.tag(i + 1);
break; break;
} }

View File

@@ -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;
public UnbakedRetexturedModel(Identifier parent) {
this.parent = parent;
}
public UnbakedRetexturedModel setThemeIndex(int theme_index) {
this.theme_index = theme_index;
return this;
}
@Override
public Collection<Identifier> getModelDependencies() {
return List.of(parent);
}
@Override
public void setParents(Function<Identifier, UnbakedModel> function) {
function.apply(parent).setParents(function);
}
}

View File

@@ -5,4 +5,4 @@ import net.minecraft.util.math.Direction;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public record Appearance(Map<Direction, List<SpriteProperties>> sprites) {} public record Appearance(Map<Direction, List<SpriteProperties>> sprites, boolean use_ao) {}

View File

@@ -1,13 +1,81 @@
package fr.adrien1106.reframed.client.model.apperance; 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.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
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 net.minecraft.util.math.Direction;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public interface CamoAppearance { @Environment(EnvType.CLIENT)
@NotNull RenderMaterial getRenderMaterial(boolean ao); public abstract class CamoAppearance {
@NotNull List<SpriteProperties> getSprites(Direction dir, long seed); protected final int id;
boolean hasColor(Direction dir, long seed, int index); 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 abstract boolean getAO(int model_id);
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 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(getAO(model_id)));
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(getAO(model_id)));
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;
}
} }

View File

@@ -1,10 +1,15 @@
package fr.adrien1106.reframed.client.model.apperance; 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.ReFramed;
import fr.adrien1106.reframed.client.ReFramedClient; import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.client.model.DynamicBakedModel; import fr.adrien1106.reframed.client.model.DynamicBakedModel;
import fr.adrien1106.reframed.client.model.QuadPosBounds; import fr.adrien1106.reframed.client.model.QuadPosBounds;
import fr.adrien1106.reframed.compat.RebakedModel;
import fr.adrien1106.reframed.mixin.model.WeightedBakedModelAccessor; 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.Renderer;
import net.fabricmc.fabric.api.renderer.v1.material.BlendMode; import net.fabricmc.fabric.api.renderer.v1.material.BlendMode;
import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder; import net.fabricmc.fabric.api.renderer.v1.material.MaterialFinder;
@@ -29,58 +34,83 @@ import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView; import net.minecraft.world.BlockRenderView;
import java.util.*; import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function; import java.util.function.Function;
@Environment(EnvType.CLIENT)
public class CamoAppearanceManager { 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) { public CamoAppearanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
MaterialFinder finder = ReFramedClient.HELPER.getFabricRenderer().materialFinder(); MaterialFinder finder = ReFramedClient.HELPER.getFabricRenderer().materialFinder();
for(BlendMode blend : BlendMode.values()) { for(BlendMode blend : BlendMode.values()) {
finder.clear().disableDiffuse(false).blendMode(blend); finder.clear().disableDiffuse(false).blendMode(blend);
materialsWithoutAo.put(blend, finder.ambientOcclusion(TriState.FALSE).find()); materials.put(blend, finder.ambientOcclusion(TriState.TRUE).find());
materialsWithAo.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO 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); Sprite sprite = spriteLookup.apply(DEFAULT_SPRITE_MAIN);
if(defaultSprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_ID + " !"); if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
this.defaultAppearance = new SingleSpriteAppearance(defaultSprite, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement()); this.default_appearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
Sprite barrier = spriteLookup.apply(BARRIER_SPRITE_ID); sprite = spriteLookup.apply(DEFAULT_SPRITE_SECONDARY);
if(barrier == null) barrier = defaultSprite; //eh if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
this.barrierItemAppearance = new SingleSpriteAppearance(barrier, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement()); this.accent_appearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
sprite = spriteLookup.apply(BARRIER_SPRITE_ID);
this.barrier_appearance = new SingleSpriteAppearance(sprite, materials.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
}
private final CamoAppearance default_appearance;
private final CamoAppearance accent_appearance;
private final CamoAppearance barrier_appearance;
private final AtomicInteger serial_number = new AtomicInteger(0); //Mutable
private final EnumMap<BlendMode, RenderMaterial> ao_materials = new EnumMap<>(BlendMode.class);
private final EnumMap<BlendMode, RenderMaterial> materials = new EnumMap<>(BlendMode.class); //Immutable contents
public static void dumpCahe() {
APPEARANCE_CACHE.invalidateAll();
} }
protected static final SpriteIdentifier DEFAULT_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block")); public CamoAppearance getDefaultAppearance(int appearance) {
private static final SpriteIdentifier BARRIER_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:item/barrier")); return appearance == 2 ? accent_appearance: default_appearance;
private final CamoAppearance defaultAppearance;
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 EnumMap<BlendMode, RenderMaterial> materialsWithAo = new EnumMap<>(BlendMode.class);
private final EnumMap<BlendMode, RenderMaterial> materialsWithoutAo = new EnumMap<>(BlendMode.class); //Immutable contents
public CamoAppearance getDefaultAppearance() {
return defaultAppearance;
} }
public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos) { public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index, boolean item) {
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state); BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
// add support for connected textures and more generally any compatible models injected so that they return baked quads // add support for connected textures that uses dynamic baking
if (model instanceof DynamicBakedModel dynamic_model) { if (model instanceof DynamicBakedModel dynamic_model) {
return computeAppearance(dynamic_model.computeQuads(world, state, pos), state); // 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, !item);
if (item) APPEARANCE_CACHE.put(state, appearance);
return appearance;
}
} }
return appearanceCache.computeIfAbsent(state, block_state -> computeAppearance(model, block_state));
// refresh cache
if (APPEARANCE_CACHE.asMap().containsKey(state)) return APPEARANCE_CACHE.getIfPresent(state);
CamoAppearance appearance = computeAppearance(model, state, false);
APPEARANCE_CACHE.put(state, appearance);
return appearance;
} }
public RenderMaterial getCachedMaterial(BlockState state, boolean ao) { 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))); return m.get(BlendMode.fromRenderLayer(RenderLayers.getBlockLayer(state)));
} }
@@ -88,15 +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 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). // 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. // 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(BakedModel model, BlockState state) { private CamoAppearance computeAppearance(BakedModel model, BlockState state, boolean is_dynamic) {
if(state.getBlock() == Blocks.BARRIER) return barrierItemAppearance; if(state.getBlock() == Blocks.BARRIER) return barrier_appearance;
if (!(model instanceof WeightedBakedModelAccessor weighted_model)) { if (!(model instanceof WeightedBakedModelAccessor weighted_model)) {
return new ComputedAppearance( return new ComputedAppearance(
getAppearance(model), getAppearance(model),
getCachedMaterial(state, true), getCachedMaterial(state, true),
getCachedMaterial(state, false), getCachedMaterial(state, false),
serialNumber.getAndIncrement() is_dynamic ? -1 : serial_number.getAndIncrement()
); );
} }
List<Weighted.Present<Appearance>> appearances = weighted_model.getModels().stream() List<Weighted.Present<Appearance>> appearances = weighted_model.getModels().stream()
@@ -107,7 +137,7 @@ public class CamoAppearanceManager {
appearances, appearances,
getCachedMaterial(state, true), getCachedMaterial(state, true),
getCachedMaterial(state, false), getCachedMaterial(state, false),
serialNumber.getAndIncrement() is_dynamic ? -1 : serial_number.getAndIncrement()
); );
} }
@@ -124,7 +154,7 @@ public class CamoAppearanceManager {
Arrays.stream(Direction.values()).forEach(direction -> { Arrays.stream(Direction.values()).forEach(direction -> {
List<BakedQuad> quads = model.getQuads(null, direction, random); List<BakedQuad> quads = model.getQuads(null, direction, random);
if(quads.isEmpty()) { // add default appearance if none present if(quads.isEmpty()) { // add default appearance if none present
sprites.put(direction, defaultAppearance.getSprites(direction, 0)); sprites.put(direction, default_appearance.getSprites(direction, 0));
return; return;
} }
@@ -145,7 +175,7 @@ public class CamoAppearanceManager {
}); });
}); });
return new Appearance(sprites); return new Appearance(sprites, model.useAmbientOcclusion());
} }
private static int getBakeFlags(QuadEmitter emitter, Sprite sprite) { private static int getBakeFlags(QuadEmitter emitter, Sprite sprite) {

View File

@@ -1,52 +1,43 @@
package fr.adrien1106.reframed.client.model.apperance; 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.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class ComputedAppearance implements CamoAppearance { @Environment(EnvType.CLIENT)
public class ComputedAppearance extends CamoAppearance {
private final Appearance appearance; 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.appearance = appearance;
this.id = id;
this.matWithAo = withAo;
this.matWithoutAo = withoutAo;
} }
@Override @Override
public @NotNull RenderMaterial getRenderMaterial(boolean ao) { public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
return ao ? matWithAo : matWithoutAo;
}
@Override
public @NotNull List<SpriteProperties> getSprites(Direction dir, long seed) {
return appearance.sprites().get(dir); return appearance.sprites().get(dir);
} }
@Override @Override
public boolean hasColor(Direction dir, long seed, int index) { public boolean hasColor(Direction dir, int model_id, int index) {
List<SpriteProperties> properties = getSprites(dir, seed); List<SpriteProperties> properties = getSprites(dir, model_id);
if (index != 0) index = properties.size() - index; if (index != 0) index = properties.size() - index;
return properties.get(index).has_colors(); return properties.get(index).has_colors();
} }
@Override @Override
public boolean equals(Object o) { public boolean getAO(int model_id) {
if(this == o) return true; return appearance.use_ao();
if(o == null || getClass() != o.getClass()) return false;
ComputedAppearance that = (ComputedAppearance) o;
return id == that.id;
} }
@Override @Override
public int hashCode() { public boolean equals(Object o) {
return id; if(this == o) return true;
if(!(o instanceof ComputedAppearance that)) return false;
return id == that.id;
} }
} }

View File

@@ -1,5 +1,7 @@
package fr.adrien1106.reframed.client.model.apperance; 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.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.minecraft.client.texture.Sprite; import net.minecraft.client.texture.Sprite;
import net.minecraft.util.math.Direction; import net.minecraft.util.math.Direction;
@@ -7,47 +9,40 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; import java.util.List;
public class SingleSpriteAppearance implements CamoAppearance { @Environment(EnvType.CLIENT)
public class SingleSpriteAppearance extends CamoAppearance {
private final @NotNull Sprite defaultSprite; private final @NotNull Sprite defaultSprite;
private final RenderMaterial mat;
private final int id;
public SingleSpriteAppearance(@NotNull Sprite defaultSprite, RenderMaterial mat, int id) { public SingleSpriteAppearance(@NotNull Sprite defaultSprite, RenderMaterial mat, int id) {
super(null, mat, id);
this.defaultSprite = defaultSprite; this.defaultSprite = defaultSprite;
this.mat = mat;
this.id = id;
} }
@Override @Override
public @NotNull RenderMaterial getRenderMaterial(boolean ao) { public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
return mat;
}
@Override
public @NotNull List<SpriteProperties> getSprites(Direction dir, long seed) {
return List.of(new SpriteProperties(defaultSprite, 0, null, false)); return List.of(new SpriteProperties(defaultSprite, 0, null, false));
} }
@Override @Override
public boolean hasColor(Direction dir, long seed, int index) { public boolean hasColor(Direction dir, int model_id, int index) {
return false; return false;
} }
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if(this == o) return true; if(this == o) return true;
if(o == null || getClass() != o.getClass()) return false; if(!(o instanceof SingleSpriteAppearance that)) return false;
SingleSpriteAppearance that = (SingleSpriteAppearance) o;
return id == that.id; return id == that.id;
} }
@Override @Override
public int hashCode() { public boolean getAO(int model_id) {
return id; return false;
} }
@Override @Override
public String toString() { 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);
} }
} }

View File

@@ -1,5 +1,7 @@
package fr.adrien1106.reframed.client.model.apperance; 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.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
import net.minecraft.util.collection.Weighted; import net.minecraft.util.collection.Weighted;
import net.minecraft.util.collection.Weighting; import net.minecraft.util.collection.Weighting;
@@ -9,25 +11,15 @@ import org.jetbrains.annotations.NotNull;
import java.util.List; 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 List<Weighted.Present<Appearance>> appearances;
private final int total_weight; 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.appearances = appearances;
this.total_weight = Weighting.getWeightSum(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;
} }
@@ -37,35 +29,33 @@ public class WeightedComputedAppearance implements CamoAppearance {
.map(appearances::indexOf).orElse(0); .map(appearances::indexOf).orElse(0);
} }
private Appearance getAppearance(long seed) { private Appearance getAppearance(int model_id) {
Random random = Random.create(seed); return appearances.get(model_id).getData();
return Weighting.getAt(appearances, Math.abs((int)random.nextLong()) % total_weight)
.map(Weighted.Present::getData).get();
} }
@Override @Override
public @NotNull List<SpriteProperties> getSprites(Direction dir, long seed) { public @NotNull List<SpriteProperties> getSprites(Direction dir, int model_id) {
return getAppearance(seed).sprites().get(dir); return getAppearance(model_id).sprites().get(dir);
} }
@Override @Override
public boolean hasColor(Direction dir, long seed, int index) { public boolean hasColor(Direction dir, int model_id, int index) {
List<SpriteProperties> properties = getSprites(dir, seed); List<SpriteProperties> properties = getSprites(dir, model_id);
if (index != 0) index = properties.size() - index; if (index != 0) index = properties.size() - index;
return properties.get(index).has_colors(); return properties.get(index).has_colors();
} }
@Override @Override
public boolean equals(Object o) { public boolean getAO(int model_id) {
if(this == o) return true; return getAppearance(model_id).use_ao();
if(o == null || getClass() != o.getClass()) return false;
WeightedComputedAppearance that = (WeightedComputedAppearance) o;
return id == that.id;
} }
@Override @Override
public int hashCode() { public boolean equals(Object o) {
return id; if(this == o) return true;
if(!(o instanceof WeightedComputedAppearance that)) return false;
return id == that.id;
} }
} }

View File

@@ -0,0 +1,183 @@
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.client.model.RetexturingBakedModel;
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.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().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
* @param hash - the hash of main model
*/
public static void computeInnerCull(BlockState state, List<RetexturingBakedModel> models, int hash) {
if (!(state.getBlock() instanceof ReFramedBlock frame_block)) return;
if (INNER_CULL_MAP.asMap().containsKey(new CullElement(frame_block, hash, 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(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, hash, self_id), cull_array);
}
}
public static boolean shouldDrawInnerFace(BlockState state, BlockRenderView view, BlockPos pos, int quad_index, int theme_index, int hash) {
if (!(state.getBlock() instanceof ReFramedBlock frame_block)
|| !(view.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity)
) return true;
CullElement key = new CullElement(frame_block, hash, 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);
try {
if (self_theme.isSideInvisible(other_theme, null)) return false;
} catch (NullPointerException e) { // this can happen if mod haven't thought about inner faces
return true;
}
return self_theme.isOpaque() != other_theme.isOpaque() && self_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) {
BlockState other_state = world.getBlockState(other_pos);
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e
&& self_state.getBlock() instanceof ReFramedBlock
? e : null;
ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e
&& other_state.getBlock() instanceof ReFramedBlock
? e : null;
// normal behaviour
if ((theme_index == 0 && self != null) || (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.isSolid() || self_state.isTransparent(world ,pos))))
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() && (!self.isSolid() || (other.isSolid() == self.isSolid()))))
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;
}
}

View File

@@ -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();
}

View File

@@ -12,11 +12,13 @@ import net.minecraft.util.math.random.Random;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
public class RebakedAthenaModel implements BakedModel { public class RebakedModel implements BakedModel {
protected final Map<Direction, List<BakedQuad>> face_quads; protected final Map<Direction, List<BakedQuad>> face_quads;
protected boolean ambient_occlusion;
public RebakedAthenaModel(Map<Direction, List<BakedQuad>> face_quads) { public RebakedModel(Map<Direction, List<BakedQuad>> face_quads, boolean ambient_occlusion) {
this.face_quads = face_quads; this.face_quads = face_quads;
this.ambient_occlusion = ambient_occlusion;
} }
@Override @Override
@@ -26,7 +28,7 @@ public class RebakedAthenaModel implements BakedModel {
@Override @Override
public boolean useAmbientOcclusion() { public boolean useAmbientOcclusion() {
return false; return ambient_occlusion;
} }
@Override @Override
@@ -51,11 +53,11 @@ public class RebakedAthenaModel implements BakedModel {
@Override @Override
public ModelTransformation getTransformation() { public ModelTransformation getTransformation() {
return null; return ModelTransformation.NONE;
} }
@Override @Override
public ModelOverrideList getOverrides() { public ModelOverrideList getOverrides() {
return null; return ModelOverrideList.EMPTY;
} }
} }

View File

@@ -0,0 +1,8 @@
package fr.adrien1106.reframed.generator;
import net.minecraft.block.Block;
import net.minecraft.data.client.BlockStateSupplier;
public interface BlockStateProvider {
BlockStateSupplier getMultipart(Block block);
}

View File

@@ -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");
}
}

View File

@@ -1,5 +1,6 @@
package fr.adrien1106.reframed.generator; 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.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
@@ -12,6 +13,6 @@ public class GBlockLoot extends FabricBlockLootTableProvider {
@Override @Override
public void generate() { 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))));
} }
} }

View File

@@ -0,0 +1,39 @@
package fr.adrien1106.reframed.generator;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.block.*;
import fr.adrien1106.reframed.generator.block.*;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider.BlockTagProvider;
import net.minecraft.block.Block;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
import net.minecraft.registry.tag.BlockTags;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
public class GBlockTag extends BlockTagProvider {
private static final Map<Class<? extends Block>, TagGetter> providers = new HashMap<>();
static {
providers.put(ReFramedPillarsWallBlock.class, new PillarsWall());
providers.put(ReFramedWallBlock.class, new Wall());
providers.put(ReFramedPaneBlock.class, new Pane());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
}
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((block) -> {
if (providers.containsKey(block.getClass()))
providers.get(block.getClass()).getTags().forEach((tag) -> getOrCreateTagBuilder(tag).add(block));
builder.add(block);
});
}
}

View File

@@ -1,14 +1,59 @@
package fr.adrien1106.reframed.generator; package fr.adrien1106.reframed.generator;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.block.*;
import fr.adrien1106.reframed.generator.block.*;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
import net.minecraft.block.Block;
import net.minecraft.data.client.*; import net.minecraft.data.client.*;
import net.minecraft.state.property.Property; import net.minecraft.state.property.Property;
import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import static net.minecraft.data.client.VariantSettings.Rotation.R0;
public class GBlockstate extends FabricModelProvider { public class GBlockstate extends FabricModelProvider {
private static final Map<Class<? extends Block>, BlockStateProvider> providers = new HashMap<>();
static {
providers.put(ReFramedHalfStairBlock.class, new HalfStair());
providers.put(ReFramedHalfStairsSlabBlock.class, new HalfStairsSlab());
providers.put(ReFramedHalfStairsStairBlock.class, new HalfStairsStair());
providers.put(ReFramedHalfStairsCubeStairBlock.class, new HalfStairsCubeStair());
providers.put(ReFramedHalfStairsStepStairBlock.class, new HalfStairsStepStair());
providers.put(ReFramedLayerBlock.class, new Layer());
providers.put(ReFramedHalfLayerBlock.class, new HalfLayer());
providers.put(ReFramedPillarBlock.class, new Pillar());
providers.put(ReFramedSlabBlock.class, new Slab());
providers.put(ReFramedSlabsCubeBlock.class, new SlabsCube());
providers.put(ReFramedSlabsStairBlock.class, new SlabsStair());
providers.put(ReFramedSlabsOuterStairBlock.class, new SlabsOuterStair());
providers.put(ReFramedSlabsInnerStairBlock.class, new SlabsInnerStair());
providers.put(ReFramedSlabsHalfLayerBlock.class, new SlabsHalfLayer());
providers.put(ReFramedSlabsLayerBlock.class, new SlabsLayer());
providers.put(ReFramedHalfSlabBlock.class, new HalfSlab());
providers.put(ReFramedHalfSlabsSlabBlock.class, new HalfSlabsSlab());
providers.put(ReFramedSmallCubeBlock.class, new SmallCube());
providers.put(ReFramedSmallCubesStepBlock.class, new SmallCubesStep());
providers.put(ReFramedStairBlock.class, new Stair());
providers.put(ReFramedStairsCubeBlock.class, new StairsCube());
providers.put(ReFramedStepBlock.class, new Step());
providers.put(ReFramedStepsSlabBlock.class, new StepsSlab());
providers.put(ReFramedStepsCrossBlock.class, new StepsCross());
providers.put(ReFramedStepsHalfLayerBlock.class, new StepsHalfLayer());
providers.put(ReFramedPillarsWallBlock.class, new PillarsWall());
providers.put(ReFramedWallBlock.class, new Wall());
providers.put(ReFramedPaneBlock.class, new Pane());
providers.put(ReFramedTrapdoorBlock.class, new Trapdoor());
providers.put(ReFramedDoorBlock.class, new Door());
providers.put(ReFramedButtonBlock.class, new Button());
providers.put(ReFramedPostBlock.class, new Post());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
}
public GBlockstate(FabricDataOutput output) { public GBlockstate(FabricDataOutput output) {
super(output); super(output);
@@ -16,22 +61,34 @@ public class GBlockstate extends FabricModelProvider {
@Override @Override
public void generateBlockStateModels(BlockStateModelGenerator model_generator) { public void generateBlockStateModels(BlockStateModelGenerator model_generator) {
Generator.BLOCKS ReFramed.BLOCKS
.forEach(model_generator::excludeFromSimpleItemModelGeneration); .forEach(model_generator::excludeFromSimpleItemModelGeneration);
Generator.BLOCKS.stream() ReFramed.BLOCKS.stream()
.map(block -> block instanceof MultipartBlockStateProvider multipart_block ? multipart_block.getMultipart(): null) .map(block -> {
if (providers.containsKey(block.getClass())) return providers.get(block.getClass()).getMultipart(block);
return VariantsBlockStateSupplier.create(
block,
GBlockstate.variant(
ReFramed.id("cube_special"),
true,
R0, R0
)
);
})
.filter(Objects::nonNull) .filter(Objects::nonNull)
.forEach(model_generator.blockStateCollector); .forEach(model_generator.blockStateCollector);
} }
@Override @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) { public static BlockStateVariant variant(Identifier model, boolean uv_lock, VariantSettings.Rotation x, VariantSettings.Rotation y) {
BlockStateVariant variant = BlockStateVariant.create().put(VariantSettings.MODEL, model); BlockStateVariant variant = BlockStateVariant.create().put(VariantSettings.MODEL, model);
if (uv_lock) variant.put(VariantSettings.UVLOCK, uv_lock); if (uv_lock) variant.put(VariantSettings.UVLOCK, uv_lock);
if (!x.equals(VariantSettings.Rotation.R0)) variant.put(VariantSettings.X, x); if (!x.equals(R0)) variant.put(VariantSettings.X, x);
if (!y.equals(VariantSettings.Rotation.R0)) variant.put(VariantSettings.Y, y); if (!y.equals(R0)) variant.put(VariantSettings.Y, y);
return variant; return variant;
} }
@@ -39,11 +96,57 @@ public class GBlockstate extends FabricModelProvider {
return When.create().set(property_1, value_1); return When.create().set(property_1, value_1);
} }
public static <T extends Comparable<T>, U extends Comparable<U>> When when(Property<T> property_1, T value_1, Property<U> property_2, U value_2) { public static <T extends Comparable<T>,
return When.allOf(when(property_1, value_1), when(property_2, value_2)); U extends Comparable<U>> When when(Property<T> property_1, T value_1,
Property<U> property_2, U value_2) {
return When.allOf(
when(property_1, value_1),
when(property_2, value_2)
);
} }
public static <T extends Comparable<T>, U extends Comparable<U>, V extends Comparable<V>> When when(Property<T> property_1, T value_1, Property<U> property_2, U value_2, Property<V> property_3, V value_3) { public static <T extends Comparable<T>,
return When.allOf(when(property_1, value_1), when(property_2, value_2), when(property_3, value_3)); U extends Comparable<U>,
V extends Comparable<V>> When when(Property<T> property_1, T value_1,
Property<U> property_2, U value_2,
Property<V> property_3, V value_3) {
return When.allOf(
when(property_1, value_1),
when(property_2, value_2),
when(property_3, value_3)
);
}
public static <T extends Comparable<T>,
U extends Comparable<U>,
V extends Comparable<V>,
W extends Comparable<W>> When when(Property<T> property_1, T value_1,
Property<U> property_2, U value_2,
Property<V> property_3, V value_3,
Property<W> property_4, W value_4) {
return When.allOf(
when(property_1, value_1),
when(property_2, value_2),
when(property_3, value_3),
when(property_4, value_4)
);
}
public static <T extends Comparable<T>,
U extends Comparable<U>,
V extends Comparable<V>,
W extends Comparable<W>,
X extends Comparable<X>> When when(Property<T> property_1, T value_1,
Property<U> property_2, U value_2,
Property<V> property_3, V value_3,
Property<W> property_4, W value_4,
Property<X> property_5, X value_5) {
return When.allOf(
when(property_1, value_1),
when(property_2, value_2),
when(property_3, value_3),
when(property_4, value_4),
when(property_5, value_5)
);
} }
} }

View File

@@ -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(" "));
}
}

View File

@@ -0,0 +1,76 @@
package fr.adrien1106.reframed.generator;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.block.*;
import fr.adrien1106.reframed.generator.block.*;
import fr.adrien1106.reframed.generator.item.Blueprint;
import fr.adrien1106.reframed.generator.item.Hammer;
import fr.adrien1106.reframed.generator.item.Screwdriver;
import fr.adrien1106.reframed.item.ReFramedBlueprintItem;
import fr.adrien1106.reframed.item.ReFramedHammerItem;
import fr.adrien1106.reframed.item.ReFramedScrewdriverItem;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.item.ItemConvertible;
import java.util.HashMap;
import java.util.Map;
public class GRecipe extends FabricRecipeProvider {
private static final Map<Class<? extends ItemConvertible>, RecipeSetter> providers = new HashMap<>();
static {
providers.put(ReFramedBlock.class, new Cube());
providers.put(ReFramedHalfStairBlock.class, new HalfStair());
providers.put(ReFramedHalfStairsSlabBlock.class, new HalfStairsSlab());
providers.put(ReFramedHalfStairsStairBlock.class, new HalfStairsStair());
providers.put(ReFramedHalfStairsCubeStairBlock.class, new HalfStairsCubeStair());
providers.put(ReFramedHalfStairsStepStairBlock.class, new HalfStairsStepStair());
providers.put(ReFramedLayerBlock.class, new Layer());
providers.put(ReFramedHalfLayerBlock.class, new HalfLayer());
providers.put(ReFramedPillarBlock.class, new Pillar());
providers.put(ReFramedSlabBlock.class, new Slab());
providers.put(ReFramedSlabsCubeBlock.class, new SlabsCube());
providers.put(ReFramedSlabsStairBlock.class, new SlabsStair());
providers.put(ReFramedSlabsOuterStairBlock.class, new SlabsOuterStair());
providers.put(ReFramedSlabsInnerStairBlock.class, new SlabsInnerStair());
providers.put(ReFramedSlabsHalfLayerBlock.class, new SlabsHalfLayer());
providers.put(ReFramedSlabsLayerBlock.class, new SlabsLayer());
providers.put(ReFramedHalfSlabBlock.class, new HalfSlab());
providers.put(ReFramedHalfSlabsSlabBlock.class, new HalfSlabsSlab());
providers.put(ReFramedSmallCubeBlock.class, new SmallCube());
providers.put(ReFramedSmallCubesStepBlock.class, new SmallCubesStep());
providers.put(ReFramedStairBlock.class, new Stair());
providers.put(ReFramedStairsCubeBlock.class, new StairsCube());
providers.put(ReFramedStepBlock.class, new Step());
providers.put(ReFramedStepsSlabBlock.class, new StepsSlab());
providers.put(ReFramedStepsCrossBlock.class, new StepsCross());
providers.put(ReFramedStepsHalfLayerBlock.class, new StepsHalfLayer());
providers.put(ReFramedPillarsWallBlock.class, new PillarsWall());
providers.put(ReFramedWallBlock.class, new Wall());
providers.put(ReFramedPaneBlock.class, new Pane());
providers.put(ReFramedTrapdoorBlock.class, new Trapdoor());
providers.put(ReFramedDoorBlock.class, new Door());
providers.put(ReFramedButtonBlock.class, new Button());
providers.put(ReFramedPostBlock.class, new Post());
providers.put(ReFramedFenceBlock.class, new Fence());
providers.put(ReFramedPostFenceBlock.class, new PostFence());
providers.put(ReFramedBlueprintItem.class, new Blueprint());
providers.put(ReFramedHammerItem.class, new Hammer());
providers.put(ReFramedScrewdriverItem.class, new Screwdriver());
}
public GRecipe(FabricDataOutput output) {
super(output);
}
@Override
public void generate(RecipeExporter exporter) {
ReFramed.BLOCKS.forEach(block -> {
if (providers.containsKey(block.getClass())) providers.get(block.getClass()).setRecipe(exporter, block);
});
ReFramed.ITEMS.forEach(item -> {
if (providers.containsKey(item.getClass())) providers.get(item.getClass()).setRecipe(exporter, item);
});
}
}

View File

@@ -2,22 +2,17 @@ package fr.adrien1106.reframed.generator;
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; 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 { 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 @Override
public void onInitializeDataGenerator(FabricDataGenerator data_generator) { public void onInitializeDataGenerator(FabricDataGenerator data_generator) {
FabricDataGenerator.Pack myPack = data_generator.createPack(); FabricDataGenerator.Pack my_pack = data_generator.createPack();
myPack.addProvider(GBlockLoot::new); my_pack.addProvider(GBlockLoot::new);
myPack.addProvider(GBlockstate::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);
} }
} }

View File

@@ -1,7 +0,0 @@
package fr.adrien1106.reframed.generator;
import net.minecraft.data.client.MultipartBlockStateSupplier;
public interface MultipartBlockStateProvider {
MultipartBlockStateSupplier getMultipart();
}

View File

@@ -0,0 +1,9 @@
package fr.adrien1106.reframed.generator;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.item.ItemConvertible;
public interface RecipeSetter {
void setRecipe(RecipeExporter exporter, ItemConvertible convertible);
}

View File

@@ -0,0 +1,11 @@
package fr.adrien1106.reframed.generator;
import net.minecraft.block.Block;
import net.minecraft.registry.tag.TagKey;
import java.util.List;
public interface TagGetter {
List<TagKey<Block>> getTags();
}

View File

@@ -0,0 +1,95 @@
package fr.adrien1106.reframed.generator.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.RecipeSetter;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.enums.BlockFace;
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.ItemConvertible;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.*;
public class Button implements RecipeSetter, BlockStateProvider {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, convertible, ReFramed.CUBE, 8);
ShapelessRecipeJsonBuilder.create(RecipeCategory.BUILDING_BLOCKS, convertible, 1)
.input(ReFramed.CUBE, 1)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(convertible), FabricRecipeProvider.conditionsFromItem(convertible))
.offerTo(exporter);
}
@Override
public BlockStateSupplier getMultipart(Block block) {
Identifier button = ReFramed.id("button_special");
Identifier button_pressed = ReFramed.id("button_pressed_special");
return MultipartBlockStateSupplier.create(block)
// FLOOR OFF
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button, true, R0, R0))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button, true, R0, R90))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button, true, R0, R180))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button, true, R0, R270))
// CEILING OFF
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button, true, R180, R0))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button, true, R180, R90))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button, true, R180, R180))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button, true, R180, R270))
// WALL OFF
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button, true, R90, R0))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button, true, R90, R90))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button, true, R90, R180))
.with(GBlockstate.when(POWERED, false, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button, true, R90, R270))
// FLOOR ON
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button_pressed, true, R0, R0))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button_pressed, true, R0, R90))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button_pressed, true, R0, R180))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.FLOOR),
GBlockstate.variant(button_pressed, true, R0, R270))
// CEILING ON
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button_pressed, true, R180, R0))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button_pressed, true, R180, R90))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button_pressed, true, R180, R180))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.CEILING),
GBlockstate.variant(button_pressed, true, R180, R270))
// WALL ON
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.NORTH, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button_pressed, true, R90, R0))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.EAST, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button_pressed, true, R90, R90))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.SOUTH, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button_pressed, true, R90, R180))
.with(GBlockstate.when(POWERED, true, HORIZONTAL_FACING, Direction.WEST, BLOCK_FACE, BlockFace.WALL),
GBlockstate.variant(button_pressed, true, R90, R270));
}
}

View File

@@ -0,0 +1,77 @@
package fr.adrien1106.reframed.generator.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.util.blocks.Corner;
import net.minecraft.block.Block;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.util.Identifier;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.blocks.BlockProperties.CORNER_FACE;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
public class CornerDouble {
public static MultipartBlockStateSupplier getMultipart(Block block, String model_name) {
Identifier model_id = ReFramed.id(model_name + "_special");
Identifier side_id = ReFramed.id(model_name + "_side_special");
return MultipartBlockStateSupplier.create(block)
// BOTTOM
.with(GBlockstate.when(CORNER, Corner.EAST_SOUTH_DOWN, CORNER_FACE, 2),
GBlockstate.variant(model_id, true, R0, R0))
.with(GBlockstate.when(CORNER, Corner.SOUTH_WEST_DOWN, CORNER_FACE, 2),
GBlockstate.variant(model_id, true, R0, R90))
.with(GBlockstate.when(CORNER, Corner.WEST_NORTH_DOWN, CORNER_FACE, 2),
GBlockstate.variant(model_id, true, R0, R180))
.with(GBlockstate.when(CORNER, Corner.NORTH_EAST_DOWN, CORNER_FACE, 2),
GBlockstate.variant(model_id, true, R0, R270))
// TOP
.with(GBlockstate.when(CORNER, Corner.NORTH_EAST_UP, CORNER_FACE, 2),
GBlockstate.variant(model_id, true, R180, R0))
.with(GBlockstate.when(CORNER, Corner.EAST_SOUTH_UP, CORNER_FACE, 2),
GBlockstate.variant(model_id, true, R180, R90))
.with(GBlockstate.when(CORNER, Corner.SOUTH_WEST_UP, CORNER_FACE, 2),
GBlockstate.variant(model_id, true, R180, R180))
.with(GBlockstate.when(CORNER, Corner.WEST_NORTH_UP, CORNER_FACE, 2),
GBlockstate.variant(model_id, true, R180, R270))
// EAST
.with(GBlockstate.when(CORNER, Corner.EAST_SOUTH_DOWN, CORNER_FACE, 0),
GBlockstate.variant(side_id, true, R0, R0))
.with(GBlockstate.when(CORNER, Corner.EAST_SOUTH_UP, CORNER_FACE, 0),
GBlockstate.variant(side_id, true, R90, R0))
.with(GBlockstate.when(CORNER, Corner.NORTH_EAST_UP, CORNER_FACE, 1),
GBlockstate.variant(side_id, true, R180, R0))
.with(GBlockstate.when(CORNER, Corner.NORTH_EAST_DOWN, CORNER_FACE, 1),
GBlockstate.variant(side_id, true, R270, R0))
// SOUTH
.with(GBlockstate.when(CORNER, Corner.SOUTH_WEST_DOWN, CORNER_FACE, 0),
GBlockstate.variant(side_id, true, R0, R90))
.with(GBlockstate.when(CORNER, Corner.SOUTH_WEST_UP, CORNER_FACE, 0),
GBlockstate.variant(side_id, true, R90, R90))
.with(GBlockstate.when(CORNER, Corner.EAST_SOUTH_UP, CORNER_FACE, 1),
GBlockstate.variant(side_id, true, R180, R90))
.with(GBlockstate.when(CORNER, Corner.EAST_SOUTH_DOWN, CORNER_FACE, 1),
GBlockstate.variant(side_id, true, R270, R90))
// WEST
.with(GBlockstate.when(CORNER, Corner.WEST_NORTH_DOWN, CORNER_FACE, 0),
GBlockstate.variant(side_id, true, R0, R180))
.with(GBlockstate.when(CORNER, Corner.WEST_NORTH_UP, CORNER_FACE, 0),
GBlockstate.variant(side_id, true, R90, R180))
.with(GBlockstate.when(CORNER, Corner.SOUTH_WEST_UP, CORNER_FACE, 1),
GBlockstate.variant(side_id, true, R180, R180))
.with(GBlockstate.when(CORNER, Corner.SOUTH_WEST_DOWN, CORNER_FACE, 1),
GBlockstate.variant(side_id, true, R270, R180))
// NORTH
.with(GBlockstate.when(CORNER, Corner.NORTH_EAST_DOWN, CORNER_FACE, 0),
GBlockstate.variant(side_id, true, R0, R270))
.with(GBlockstate.when(CORNER, Corner.NORTH_EAST_UP, CORNER_FACE, 0),
GBlockstate.variant(side_id, true, R90, R270))
.with(GBlockstate.when(CORNER, Corner.WEST_NORTH_UP, CORNER_FACE, 1),
GBlockstate.variant(side_id, true, R180, R270))
.with(GBlockstate.when(CORNER, Corner.WEST_NORTH_DOWN, CORNER_FACE, 1),
GBlockstate.variant(side_id, true, R270, R270))
;
}
}

View File

@@ -0,0 +1,26 @@
package fr.adrien1106.reframed.generator.block;
import fr.adrien1106.reframed.generator.RecipeSetter;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
import net.minecraft.item.ItemConvertible;
import net.minecraft.item.Items;
import net.minecraft.recipe.book.RecipeCategory;
public class Cube implements RecipeSetter {
@Override
public void setRecipe(RecipeExporter exporter, ItemConvertible convertible) {
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, convertible)
.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(convertible), FabricRecipeProvider.conditionsFromItem(convertible))
.offerTo(exporter);
}
}

Some files were not shown because too many files have changed in this diff Show More