22 Commits
v1.2 ... v1.4

Author SHA1 Message Date
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
174 changed files with 2938 additions and 4402 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 }}

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

View File

@@ -3,6 +3,7 @@ import fr.altarik.CreateTag
plugins {
id "com.modrinth.minotaur" version "2.+"
id 'fabric-loom' version '1.5-SNAPSHOT'
id 'maven-publish'
}
@@ -72,6 +73,17 @@ repositories {
maven {
url "https://maven.resourcefulbees.com/repository/maven-public/"
}
exclusiveContent {
forRepository {
maven {
name = "Modrinth"
url = "https://api.modrinth.com/maven"
}
}
filter {
includeGroup "maven.modrinth"
}
}
mavenCentral()
// Add repositories to retrieve artifacts from in here.
@@ -88,13 +100,19 @@ dependencies {
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
// Indium and sodium for sodium support
modCompileOnly "maven.modrinth:indium:${project.indium_version}+mc${project.minecraft_version}"
modCompileOnly "maven.modrinth:sodium:mc${project.minecraft_version}-${project.sodium_version}"
// modRuntimeOnly "maven.modrinth:indium:${project.indium_version}+mc${project.minecraft_version}"
// modRuntimeOnly "maven.modrinth:sodium:mc${project.minecraft_version}-${project.sodium_version}"
// Athena for connected texture
modCompileOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
modRuntimeOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
// Chipped to test athena implementation
modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7"
// modRuntimeOnly "earth.terrarium.chipped:chipped-fabric-${project.minecraft_version}:3.1.2" 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.
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
@@ -106,6 +124,8 @@ processResources {
inputs.property "loader_version", project.loader_version
inputs.property "mod_id", project.mod_id
inputs.property "athena_version", project.athena_version
inputs.property "indium_version", project.indium_version
inputs.property "sodium_version", project.sodium_version
filteringCharset "UTF-8"
filesMatching("fabric.mod.json") {
@@ -171,6 +191,23 @@ publishing {
}
}
// configure modrinth publication
modrinth {
token = getEnv("MODRINTH_TOKEN", local.getProperty("modrinth_token"))
projectId = project.modrinth_id
versionNumber = project.mod_version
versionName = "${project.archives_base_name} ${project.mod_version}"
versionType = project.mod_version.endsWith('SNAPSHOT') ? 'beta' : 'release'
uploadFile = remapJar
gameVersions = [project.minecraft_version]
loaders = ["fabric"]
dependencies {
required.project "fabric-api"
optional.version "b1ZV3DIJ", "${project.athena_version}"
optional.version "Orvt0mRa", "${project.indium_version}+mc${project.minecraft_version}"
}
}
tasks.register("reportToDiscord", ReportDiscord) {
config.set(reportConfig)
}

View File

@@ -8,7 +8,8 @@ yarn_mappings=1.20.4+build.3
loader_version=0.15.6
# Mod Properties
mod_version = 1.2
modrinth_id = jCpoCBpn
mod_version = 1.4
maven_group = fr.adrien1106
archives_base_name = ReFramed
mod_id = reframed
@@ -21,3 +22,5 @@ git_owner=Altarik
git_repo=ReFramed
athena_version=3.3.0
sodium_version=0.5.8
indium_version=1.0.30

View File

@@ -1,20 +1,21 @@
package fr.adrien1106.reframed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import fr.adrien1106.reframed.block.*;
import fr.adrien1106.reframed.util.BlockHelper;
import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.itemgroup.v1.FabricItemGroup;
import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.Blocks;
import net.minecraft.block.entity.BlockEntityType;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
@@ -25,15 +26,17 @@ import java.util.function.BiConsumer;
import java.util.stream.Collectors;
/**
* TODO multiple camos
* TODO self culling, fix other models, better connected textures, preload items, better cache
*/
public class ReFramed implements ModInitializer {
public static final String MODID = "reframed";
public static final ArrayList<Block> BLOCKS = new ArrayList<>();
public static Block CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, DOOR, TRAPDOOR, IRON_DOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE, CANDLE;
public static Block CUBE, STAIRS, DOUBLE_STAIRS, SLAB, DOUBLE_SLAB, STEP, DOUBLE_STEP;
public static ItemGroup ITEM_GROUP;
public static BlockEntityType<ReFramedEntity> REFRAMED_BLOCK_ENTITY;
public static BlockEntityType<ReFramedDoubleEntity> REFRAMED_DOUBLE_BLOCK_ENTITY;
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
@@ -44,29 +47,31 @@ public class ReFramed implements ModInitializer {
//corresponding vanilla block in the "search" creative tab... with the non-vanilla "post" and
//"vertical slab" inserted where they fit ...and i moved the lever way up next to the pressureplate
//and button, because they're redstoney... hopefully this ordering makes sense lol
CUBE = registerReFramed("cube" , new ReFramedBlock(ReFramedInteractionUtil.makeSettings()));
CUBE = registerReFramed("cube" , new ReFramedBlock(cp(Blocks.OAK_PLANKS)));
STAIRS = registerReFramed("stairs" , new ReFramedStairsBlock(cp(Blocks.OAK_STAIRS)));
DOUBLE_STAIRS = registerReFramed("double_stairs" , new ReFramedDoubleStairsBlock(cp(Blocks.OAK_STAIRS)));
SLAB = registerReFramed("slab" , new ReFramedSlabBlock(cp(Blocks.OAK_SLAB)));
POST = registerReFramed("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE)));
FENCE = registerReFramed("fence" , new ReFramedFenceBlock(cp(Blocks.OAK_FENCE)));
FENCE_GATE = registerReFramed("fence_gate" , new ReFramedFenceGateBlock(cp(Blocks.OAK_FENCE_GATE)));
DOOR = registerReFramed("door" , new ReFramedDoorBlock(cp(Blocks.OAK_DOOR), BlockSetType.OAK));
TRAPDOOR = registerReFramed("trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.OAK_TRAPDOOR), BlockSetType.OAK));
IRON_DOOR = registerReFramed("iron_door" , new ReFramedDoorBlock(cp(Blocks.IRON_DOOR), BlockSetType.IRON));
IRON_TRAPDOOR = registerReFramed("iron_trapdoor" , new ReFramedTrapdoorBlock(cp(Blocks.IRON_TRAPDOOR), BlockSetType.IRON));
PRESSURE_PLATE = registerReFramed("pressure_plate", new ReFramedPressurePlateBlock(cp(Blocks.OAK_PRESSURE_PLATE)));
BUTTON = registerReFramed("button" , new ReFramedButtonBlock(cp(Blocks.OAK_BUTTON)));
LEVER = registerReFramed("lever" , new ReFramedLeverBlock(cp(Blocks.LEVER)));
WALL = registerReFramed("wall" , new ReFramedWallBlock(ReFramedInteractionUtil.makeSettings()));
CARPET = registerReFramed("carpet" , new ReFramedCarpetBlock(cp(Blocks.WHITE_CARPET)));
PANE = registerReFramed("pane" , new ReFramedPaneBlock(cp(Blocks.GLASS_PANE)));
CANDLE = registerReFramed("candle" , new ReFramedCandleBlock(ReFramedCandleBlock.configureSettings(cp(Blocks.CANDLE))));
DOUBLE_SLAB = registerReFramed("double_slab" , new ReFramedDoubleSlabBlock(cp(Blocks.OAK_SLAB)));
STEP = registerReFramed("step" , new ReFramedStepBlock(cp(Blocks.OAK_SLAB)));
DOUBLE_STEP = registerReFramed("double_step" , new ReFramedDoubleStepBlock(cp(Blocks.OAK_SLAB)));
REFRAMED_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("camo"),
FabricBlockEntityTypeBuilder.create((pos, state) -> new ReFramedEntity(REFRAMED_BLOCK_ENTITY, pos, state), BLOCKS.toArray(new Block[0])).build(null)
FabricBlockEntityTypeBuilder.create(
(pos, state) -> new ReFramedEntity(REFRAMED_BLOCK_ENTITY, pos, state),
BLOCKS.stream()
.filter(block -> !(block instanceof ReFramedDoubleBlock))
.toArray(Block[]::new)).build(null)
);
REFRAMED_DOUBLE_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("double_camo"),
FabricBlockEntityTypeBuilder.create(
(pos, state) -> new ReFramedDoubleEntity(REFRAMED_DOUBLE_BLOCK_ENTITY, pos, state),
BLOCKS.stream()
.filter(block -> block instanceof ReFramedDoubleBlock)
.toArray(Block[]::new)).build(null)
);
Registry.register(Registries.ITEM_GROUP, id("tab"), FabricItemGroup.builder()
ITEM_GROUP = Registry.register(Registries.ITEM_GROUP, id("tab"), FabricItemGroup.builder()
.displayName(Text.translatable("itemGroup.reframed.tab"))
.icon(() -> new ItemStack(SLAB))
.entries((ctx, e) -> e.addAll(BLOCKS.stream().map(ItemStack::new).collect(Collectors.toList()))).build()
@@ -74,7 +79,13 @@ public class ReFramed implements ModInitializer {
}
private static AbstractBlock.Settings cp(Block base) {
return ReFramedInteractionUtil.configureSettings(AbstractBlock.Settings.copy(base));
return AbstractBlock.Settings.copy(base)
.luminance(BlockHelper::luminance)
.nonOpaque()
.sounds(BlockSoundGroup.WOOD)
.hardness(0.2f)
.suffocates((a,b,c) -> false)
.blockVision((a,b,c) -> false);
}
private static <B extends Block> B registerReFramed(String path, B block) {

View File

@@ -1,29 +1,45 @@
package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import fr.adrien1106.reframed.generator.RecipeSetter;
import fr.adrien1106.reframed.util.BlockHelper;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.*;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.ItemScatterer;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class ReFramedBlock extends Block implements BlockEntityProvider {
import java.util.List;
import static fr.adrien1106.reframed.util.BlockProperties.LIGHT;
public class ReFramedBlock extends Block implements BlockEntityProvider, RecipeSetter {
public ReFramedBlock(Settings settings) {
super(settings);
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
setDefaultState(getDefaultState().with(LIGHT, false));
}
//For addon devs: override this so your blocks don't end up trying to place my block entity, my BlockEntityType only handles blocks internal to the mod
@@ -35,51 +51,118 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
super.appendProperties(builder.add(LIGHT));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
return ReFramedEntity.getNbtLightLevel(super.getPlacementState(ctx), ctx.getStack());
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
return r;
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit);
ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
if (result.isAccepted()) return result;
return BlockHelper.useCamo(state, world, pos, player, hand, hit, 1);
}
// don't like this but might be useful
protected ActionResult superUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
return super.onUse(state, world, pos, player, hand, hit);
}
protected boolean canUse(World world, BlockPos pos, PlayerEntity player) {
return player.canModifyBlocks() && world.canPlayerModifyAt(player, pos);
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
if(!state.isOf(newState.getBlock()) &&
world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity &&
world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
) {
DefaultedList<ItemStack> drops = DefaultedList.of();
List<BlockState> themes = frame_entity.getThemes();
themes.forEach(theme -> {
if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock()));
});
if(frame_entity.emitsRedstone()
&& themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) != 0))
drops.add(new ItemStack(Items.REDSTONE_TORCH));
if(frame_entity.emitsLight()
&& themes.stream().noneMatch(theme -> theme.getLuminance() != 0))
drops.add(new ItemStack(Items.GLOWSTONE_DUST));
if(!frame_entity.isSolid()
&& themes.stream().anyMatch(theme -> theme.isSolid()))
drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
ItemScatterer.spawn(world, pos, drops);
}
super.onStateReplaced(state, world, pos, newState, moved);
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
if(world.isClient && world.getBlockEntity(pos) instanceof ReFramedEntity be) {
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
if(tag != null) be.readNbt(tag);
}
super.onPlaced(world, pos, state, placer, stack);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
return isGhost(view, pos)
? VoxelShapes.empty()
: super.getCollisionShape(state, view, pos, ctx);
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return ReFramedInteractionUtil.emitsRedstonePower(state);
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return isGhost(view, pos)
? VoxelShapes.empty()
: super.getCullingShape(state, view, pos);
}
public VoxelShape getShape(BlockState state, int i) {
// assuming the shape don't need the world and position
return getOutlineShape(state, null, null, null);
}
public boolean isGhost(BlockView view, BlockPos pos) {
return view.getBlockEntity(pos) instanceof ReFramedEntity be && !be.isSolid();
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
return getWeakRedstonePower(state, view, pos, dir);
}
public int getTopThemeIndex(BlockState state) {
return 1;
}
@Override
public void setRecipe(RecipeExporter exporter) {
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, this)
.pattern("III")
.pattern("I~I")
.pattern("III")
.input('I', Items.BAMBOO)
.input('~', Items.STRING)
.criterion(FabricRecipeProvider.hasItem(Items.BAMBOO), FabricRecipeProvider.conditionsFromItem(Items.BAMBOO))
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
.offerTo(exporter);
}
}

View File

@@ -1,95 +0,0 @@
package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.BlockState;
import net.minecraft.block.ButtonBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class ReFramedButtonBlock extends ButtonBlock implements BlockEntityProvider {
public ReFramedButtonBlock(Settings settings) {
this(settings, BlockSetType.OAK, 30);
}
public ReFramedButtonBlock(Settings settings, BlockSetType blockSetType, int i) {
super(blockSetType, i, settings);
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
}
@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
return r;
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, newState, moved);
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return super.emitsRedstonePower(state);
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
boolean a = 0 != super.getWeakRedstonePower(state, view, pos, dir);
boolean b = 0 != ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
return (a ^ b) ? 15 : 0;
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
if(getDirection(state) != dir) return 0;
else return getWeakRedstonePower(state, view, pos, dir);
}
}

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 +0,0 @@
package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.BlockState;
import net.minecraft.block.DoorBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class ReFramedDoorBlock extends DoorBlock implements BlockEntityProvider {
public ReFramedDoorBlock(Settings settings, BlockSetType blockSetType) {
super(blockSetType, settings);
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
}
@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
return r;
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, newState, moved);
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
super.onPlaced(world, pos, state, placer, stack);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return ReFramedInteractionUtil.emitsRedstonePower(state);
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
}
}

View File

@@ -0,0 +1,77 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.BlockHelper;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import static net.minecraft.util.shape.VoxelShapes.empty;
import static net.minecraft.util.shape.VoxelShapes.fullCube;
public abstract class ReFramedDoubleBlock extends ReFramedBlock {
public ReFramedDoubleBlock(Settings settings) {
super(settings);
}
@Override
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_DOUBLE_BLOCK_ENTITY.instantiate(pos, state);
}
protected int getHitShape(BlockState state, BlockHitResult hit) {
Direction side = hit.getSide();
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 pos = BlockHelper.getRelativePos(hit.getPos(), hit.getBlockPos());
// System.out.println(side.getAxis().choose(hit.getPos().x, hit.getPos().y, hit.getPos().z));
if (BlockHelper.cursorMatchesFace(first_shape, pos)) return 1;
if (BlockHelper.cursorMatchesFace(second_shape, pos)) return 2;
return 0;
}
@Override
public boolean isTransparent(BlockState state, BlockView world, BlockPos pos) {
return world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity
&& framed_entity.getThemes().stream().allMatch(theme -> theme.isTransparent(world, pos));
}
public VoxelShape getRenderOutline(BlockState state, BlockHitResult hit) {
return getShape(state, getHitShape(state, hit));
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return getCullingShape(state, view, pos);
}
@Override
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return isGhost(view, pos) ? empty() : fullCube();
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit);
ActionResult result = BlockHelper.useUpgrade(state, world, pos, player, hand);
if (result.isAccepted()) return result;
return BlockHelper.useCamo(state, world, pos, player, hand, hit, getHitShape(state, hit));
}
}

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 to check if rerender is needed
second_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 2));
// Force a chunk remesh on the client if the displayed blockstate has changed
if(world != null && world.isClient && !Objects.equals(rendered_state, second_state)) {
ReFramed.chunkRerenderProxy.accept(world, pos);
}
}
@Override
public void writeNbt(NbtCompound nbt) {
super.writeNbt(nbt);
if(second_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 2, NbtHelper.fromBlockState(second_state));
}
}

View File

@@ -0,0 +1,81 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.generator.GBlockstate;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.Properties;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import org.jetbrains.annotations.Nullable;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.*;
import static net.minecraft.data.client.VariantSettings.Rotation.R0;
import static net.minecraft.data.client.VariantSettings.Rotation.R90;
import static net.minecraft.state.property.Properties.AXIS;
public class ReFramedDoubleSlabBlock extends ReFramedDoubleBlock implements BlockStateProvider {
public ReFramedDoubleSlabBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(Properties.AXIS, Direction.Axis.Y));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(Properties.AXIS));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return super.getPlacementState(ctx).with(Properties.AXIS, ctx.getSide().getAxis());
}
@Override
public VoxelShape getShape(BlockState state, int i) {
return switch (state.get(Properties.AXIS)) {
case Y -> i == 2 ? UP : DOWN;
case Z -> i == 2 ? NORTH : SOUTH;
case X -> i == 2 ? EAST : WEST;
};
}
@Override
public int getTopThemeIndex(BlockState state) {
// when the side is shared just return one
return state.get(AXIS) == Direction.Axis.Y ? 2: super.getTopThemeIndex(state);
}
@Override
public MultipartBlockStateSupplier getMultipart() {
Identifier model_id = ReFramed.id("double_slab_special");
return MultipartBlockStateSupplier.create(this)
.with(GBlockstate.when(AXIS, Direction.Axis.Y),
GBlockstate.variant(model_id, true, R0, R0))
.with(GBlockstate.when(AXIS, Direction.Axis.Z),
GBlockstate.variant(model_id, true, R90, R0))
.with(GBlockstate.when(AXIS, Direction.Axis.X),
GBlockstate.variant(model_id, true, R90, R90));
}
@Override
public void setRecipe(RecipeExporter exporter) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE);
ShapelessRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, this)
.input(ReFramed.SLAB, 2)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
.offerTo(exporter);
}
}

View File

@@ -0,0 +1,210 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.util.BlockHelper;
import fr.adrien1106.reframed.util.property.Corner;
import fr.adrien1106.reframed.util.property.StairShape;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.state.StateManager;
import net.minecraft.util.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.World;
import net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import static fr.adrien1106.reframed.block.ReFramedStairsBlock.*;
import static fr.adrien1106.reframed.util.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.BlockProperties.STAIR_SHAPE;
import static fr.adrien1106.reframed.util.property.StairShape.STRAIGHT;
public class ReFramedDoubleStairsBlock extends ReFramedDoubleBlock implements BlockStateProvider {
private static final List<VoxelShape> COMPLEMENT_LIST = new ArrayList<>(52);
public ReFramedDoubleStairsBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER, STAIR_SHAPE));
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) {
return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved)
.with(STAIR_SHAPE, BlockHelper.getStairsShape(this, state.get(CORNER), world, pos));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
Corner face = BlockHelper.getPlacementCorner(ctx);
StairShape shape = BlockHelper.getStairsShape(this, face, ctx.getWorld(), ctx.getBlockPos());
return super.getPlacementState(ctx).with(CORNER, face).with(STAIR_SHAPE, shape);
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
}
@Override
public VoxelShape getShape(BlockState state, int i) {
return i == 2 ? getComplementOutline(state) : getOutline(state);
}
private VoxelShape getComplementOutline(BlockState state) {
StairShape shape = state.get(STAIR_SHAPE);
Corner direction = state.get(CORNER);
return switch (shape) {
case STRAIGHT ->
switch (direction) {
case DOWN_SOUTH -> COMPLEMENT_LIST.get(0);
case NORTH_DOWN -> COMPLEMENT_LIST.get(1);
case UP_NORTH -> COMPLEMENT_LIST.get(2);
case SOUTH_UP -> COMPLEMENT_LIST.get(3);
case DOWN_EAST -> COMPLEMENT_LIST.get(4);
case WEST_DOWN -> COMPLEMENT_LIST.get(5);
case UP_WEST -> COMPLEMENT_LIST.get(6);
case EAST_UP -> COMPLEMENT_LIST.get(7);
case NORTH_EAST -> COMPLEMENT_LIST.get(8);
case EAST_SOUTH -> COMPLEMENT_LIST.get(9);
case SOUTH_WEST -> COMPLEMENT_LIST.get(10);
case WEST_NORTH -> COMPLEMENT_LIST.get(11);
};
case INNER_LEFT ->
switch (direction) {
case WEST_DOWN, NORTH_DOWN -> COMPLEMENT_LIST.get(44);
case DOWN_EAST -> COMPLEMENT_LIST.get(45);
case DOWN_SOUTH -> COMPLEMENT_LIST.get(47);
case UP_WEST, UP_NORTH, WEST_NORTH -> COMPLEMENT_LIST.get(48);
case EAST_UP, NORTH_EAST -> COMPLEMENT_LIST.get(49);
case EAST_SOUTH -> COMPLEMENT_LIST.get(50);
case SOUTH_UP, SOUTH_WEST -> COMPLEMENT_LIST.get(51);
};
case INNER_RIGHT ->
switch (direction) {
case WEST_NORTH -> COMPLEMENT_LIST.get(44);
case NORTH_DOWN, NORTH_EAST -> COMPLEMENT_LIST.get(45);
case DOWN_EAST, DOWN_SOUTH, EAST_SOUTH -> COMPLEMENT_LIST.get(46);
case WEST_DOWN, SOUTH_WEST -> COMPLEMENT_LIST.get(47);
case UP_NORTH -> COMPLEMENT_LIST.get(49);
case EAST_UP, SOUTH_UP -> COMPLEMENT_LIST.get(50);
case UP_WEST -> COMPLEMENT_LIST.get(51);
};
case OUTER_LEFT ->
switch (direction) {
case DOWN_EAST -> COMPLEMENT_LIST.get(43);
case WEST_DOWN, NORTH_DOWN -> COMPLEMENT_LIST.get(42);
case DOWN_SOUTH -> COMPLEMENT_LIST.get(41);
case EAST_UP, NORTH_EAST -> COMPLEMENT_LIST.get(39);
case UP_WEST, UP_NORTH, WEST_NORTH -> COMPLEMENT_LIST.get(38);
case SOUTH_UP, SOUTH_WEST -> COMPLEMENT_LIST.get(37);
case EAST_SOUTH -> COMPLEMENT_LIST.get(36);
};
case OUTER_RIGHT ->
switch (direction) {
case NORTH_DOWN, NORTH_EAST -> COMPLEMENT_LIST.get(43);
case WEST_NORTH -> COMPLEMENT_LIST.get(42);
case WEST_DOWN, SOUTH_WEST -> COMPLEMENT_LIST.get(41);
case DOWN_EAST, DOWN_SOUTH, EAST_SOUTH -> COMPLEMENT_LIST.get(40);
case UP_NORTH -> COMPLEMENT_LIST.get(39);
case UP_WEST -> COMPLEMENT_LIST.get(37);
case EAST_UP, SOUTH_UP -> COMPLEMENT_LIST.get(36);
};
case FIRST_OUTER_LEFT ->
switch (direction) {
case WEST_DOWN, NORTH_DOWN -> COMPLEMENT_LIST.get(14);
case SOUTH_UP -> COMPLEMENT_LIST.get(17);
case EAST_UP -> COMPLEMENT_LIST.get(19);
case EAST_SOUTH -> COMPLEMENT_LIST.get(20);
case DOWN_SOUTH -> COMPLEMENT_LIST.get(22);
case UP_NORTH, WEST_NORTH -> COMPLEMENT_LIST.get(25);
case SOUTH_WEST -> COMPLEMENT_LIST.get(28);
case UP_WEST -> COMPLEMENT_LIST.get(31);
case DOWN_EAST -> COMPLEMENT_LIST.get(34);
case NORTH_EAST -> COMPLEMENT_LIST.get(35);
};
case FIRST_OUTER_RIGHT ->
switch (direction) {
case NORTH_DOWN -> COMPLEMENT_LIST.get(15);
case SOUTH_UP, EAST_UP -> COMPLEMENT_LIST.get(16);
case WEST_DOWN -> COMPLEMENT_LIST.get(13);
case DOWN_SOUTH, EAST_SOUTH -> COMPLEMENT_LIST.get(23);
case UP_NORTH -> COMPLEMENT_LIST.get(24);
case WEST_NORTH -> COMPLEMENT_LIST.get(26);
case UP_WEST -> COMPLEMENT_LIST.get(28);
case SOUTH_WEST -> COMPLEMENT_LIST.get(29);
case DOWN_EAST -> COMPLEMENT_LIST.get(33);
case NORTH_EAST -> COMPLEMENT_LIST.get(34);
};
case SECOND_OUTER_LEFT ->
switch (direction) {
case DOWN_EAST -> COMPLEMENT_LIST.get(15);
case DOWN_SOUTH -> COMPLEMENT_LIST.get(13);
case UP_WEST, UP_NORTH -> COMPLEMENT_LIST.get(18);
case SOUTH_UP, SOUTH_WEST -> COMPLEMENT_LIST.get(21);
case NORTH_EAST -> COMPLEMENT_LIST.get(24);
case NORTH_DOWN -> COMPLEMENT_LIST.get(26);
case WEST_DOWN -> COMPLEMENT_LIST.get(30);
case WEST_NORTH -> COMPLEMENT_LIST.get(31);
case EAST_SOUTH -> COMPLEMENT_LIST.get(32);
case EAST_UP -> COMPLEMENT_LIST.get(35);
};
case SECOND_OUTER_RIGHT ->
switch (direction) {
case DOWN_SOUTH, DOWN_EAST -> COMPLEMENT_LIST.get(12);
case UP_WEST -> COMPLEMENT_LIST.get(17);
case UP_NORTH -> COMPLEMENT_LIST.get(19);
case SOUTH_UP -> COMPLEMENT_LIST.get(20);
case SOUTH_WEST -> COMPLEMENT_LIST.get(22);
case NORTH_EAST, NORTH_DOWN -> COMPLEMENT_LIST.get(27);
case WEST_DOWN -> COMPLEMENT_LIST.get(29);
case WEST_NORTH -> COMPLEMENT_LIST.get(30);
case EAST_UP -> COMPLEMENT_LIST.get(32);
case EAST_SOUTH -> COMPLEMENT_LIST.get(33);
};
};
}
@Override
public MultipartBlockStateSupplier getMultipart() {
return getStairMultipart(this, true);
}
@Override
public void setRecipe(RecipeExporter exporter) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE);
ShapelessRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, this)
.input(ReFramed.STAIRS)
.input(ReFramed.STEP)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
.offerTo(exporter);
}
static {
VOXEL_LIST.forEach(shape -> COMPLEMENT_LIST.add(VoxelShapes.combineAndSimplify(VoxelShapes.fullCube(), shape, BooleanBiFunction.ONLY_FIRST)));
}
}

View File

@@ -0,0 +1,118 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.util.BlockHelper;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapelessRecipeJsonBuilder;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.state.StateManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Direction.Axis;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
import static fr.adrien1106.reframed.block.ReFramedSlabBlock.*;
import static fr.adrien1106.reframed.block.ReFramedStepBlock.STEP_VOXELS;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.FACING;
import static net.minecraft.state.property.Properties.AXIS;
import static net.minecraft.util.shape.VoxelShapes.empty;
public class ReFramedDoubleStepBlock extends WaterloggableReFramedDoubleBlock implements BlockStateProvider {
public ReFramedDoubleStepBlock(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
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
return isGhost(view, pos) ? empty() : getSlabShape(state.get(FACING));
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return getSlabShape(state.get(FACING));
}
@Override
public VoxelShape getShape(BlockState state, int i) {
Axis axis = state.get(AXIS);
return STEP_VOXELS.get(switch (state.get(FACING)) {
case DOWN -> axis == Axis.Z ? (i == 1 ? 0 : 1): (i == 1 ? 4 : 5 );
case UP -> axis == Axis.Z ? (i == 1 ? 3 : 2): (i == 1 ? 7 : 6 );
case NORTH -> axis == Axis.Y ? (i == 1 ? 1 : 2): (i == 1 ? 11 : 8 );
case SOUTH -> axis == Axis.Y ? (i == 1 ? 0 : 3): (i == 1 ? 9 : 10);
case EAST -> axis == Axis.Y ? (i == 1 ? 4 : 7): (i == 1 ? 8 : 9 );
case WEST -> axis == Axis.Y ? (i == 1 ? 5 : 6): (i == 1 ? 10 : 11);
});
}
@Override
public MultipartBlockStateSupplier getMultipart() {
Identifier step_id = ReFramed.id("double_step_special");
Identifier step_side_id = ReFramed.id("double_step_side_special");
return MultipartBlockStateSupplier.create(this)
.with(GBlockstate.when(FACING, Direction.DOWN, AXIS, Axis.X),
GBlockstate.variant(step_id, true, R0, R0))
.with(GBlockstate.when(FACING, Direction.DOWN, AXIS, Axis.Z),
GBlockstate.variant(step_id, true, R0, R90))
.with(GBlockstate.when(FACING, Direction.UP, AXIS, Axis.X),
GBlockstate.variant(step_id, true, R180, R0))
.with(GBlockstate.when(FACING, Direction.UP, AXIS, Axis.Z),
GBlockstate.variant(step_id, true, R180, R90))
.with(GBlockstate.when(FACING, Direction.EAST, AXIS, Axis.Z),
GBlockstate.variant(step_side_id, true, R0, R0))
.with(GBlockstate.when(FACING, Direction.EAST, AXIS, Axis.Y),
GBlockstate.variant(step_side_id, true, R90, R0))
.with(GBlockstate.when(FACING, Direction.SOUTH, AXIS, Axis.X),
GBlockstate.variant(step_side_id, true, R0, R90))
.with(GBlockstate.when(FACING, Direction.SOUTH, AXIS, Axis.Y),
GBlockstate.variant(step_side_id, true, R90, R90))
.with(GBlockstate.when(FACING, Direction.WEST, AXIS, Axis.Z),
GBlockstate.variant(step_side_id, true, R0, R180))
.with(GBlockstate.when(FACING, Direction.WEST, AXIS, Axis.Y),
GBlockstate.variant(step_side_id, true, R90, R180))
.with(GBlockstate.when(FACING, Direction.NORTH, AXIS, Axis.X),
GBlockstate.variant(step_side_id, true, R0, R270))
.with(GBlockstate.when(FACING, Direction.NORTH, AXIS, Axis.Y),
GBlockstate.variant(step_side_id, true, R90, R270));
}
@Override
public void setRecipe(RecipeExporter exporter) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 2);
ShapelessRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, this)
.input(ReFramed.STEP, 2)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
.offerTo(exporter);
}
}

View File

@@ -1,7 +1,7 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import fr.adrien1106.reframed.util.BlockProperties;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
@@ -21,26 +21,22 @@ import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
//Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic,
//is pretty important since players might place a lot of them. There were tons and tons of these at Blanketcon.
//To that end, most of the state has been crammed into a bitfield.
public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity {
protected BlockState renderedState = Blocks.AIR.getDefaultState();
protected byte bitfield = DEFAULT_BITFIELD;
protected BlockState first_state = Blocks.AIR.getDefaultState();
protected byte bit_field = SOLIDITY_MASK;
protected static final int SPENT_GLOWSTONE_DUST_MASK = 0b00000001;
protected static final int SPENT_REDSTONE_TORCH_MASK = 0b00000010;
protected static final int SPENT_POPPED_CHORUS_MASK = 0b00000100;
protected static final int EMITS_REDSTONE_MASK = 0b00001000;
protected static final int IS_SOLID_MASK = 0b00010000;
protected static final byte DEFAULT_BITFIELD = IS_SOLID_MASK; //brand-new frames shall be solid
//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 byte LIGHT_MASK = 0b001;
protected static final byte REDSTONE_MASK = 0b010;
protected static final byte SOLIDITY_MASK = 0b100;
public static final String BLOCKSTATE_KEY = "s";
protected static final String BITFIELD_KEY = "b";
public ReFramedEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
@@ -48,145 +44,116 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
}
@Override
public void readNbt(NbtCompound tag) {
super.readNbt(tag);
public void readNbt(NbtCompound nbt) {
super.readNbt(nbt);
BlockState lastRenderedState = renderedState;
BlockState rendered_state = first_state; // keep previous state to check if rerender is needed
first_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 1));
if (nbt.contains(BITFIELD_KEY)) bit_field = nbt.getByte(BITFIELD_KEY);
if(tag.contains("BlockState")) { //2.0.4 and earlier
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState"));
if(tag.getBoolean("spentglow")) spentGlowstoneDust();
if(tag.getBoolean("spentredst")) spentRedstoneTorch();
if(tag.getBoolean("spentchor")) spentPoppedChorus();
setEmitsRedstone(tag.getBoolean("emitsredst"));
setSolidity(!tag.contains("solid") || tag.getBoolean("solid")); //default to "true" if it's nonexistent
} else {
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound(BLOCKSTATE_KEY));
bitfield = tag.contains(BITFIELD_KEY) ? tag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD;
}
//Force a chunk remesh on the client if the displayed blockstate has changed
if(world != null && world.isClient && !Objects.equals(lastRenderedState, renderedState)) {
// Force a chunk remesh on the client if the displayed blockstate has changed
if(world != null && world.isClient && !Objects.equals(rendered_state, first_state)) {
ReFramed.chunkRerenderProxy.accept(world, pos);
}
}
@Override
public void writeNbt(NbtCompound tag) {
super.writeNbt(tag);
public void writeNbt(NbtCompound nbt) {
super.writeNbt(nbt);
if(renderedState != Blocks.AIR.getDefaultState()) tag.put(BLOCKSTATE_KEY, NbtHelper.fromBlockState(renderedState));
if(bitfield != DEFAULT_BITFIELD) tag.putByte(BITFIELD_KEY, bitfield);
if(first_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(first_state));
if(bit_field != SOLIDITY_MASK) nbt.putByte(BITFIELD_KEY, bit_field);
}
public static @NotNull BlockState readStateFromItem(ItemStack stack) {
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
if(blockEntityTag == null) return Blocks.AIR.getDefaultState();
public static @NotNull BlockState readStateFromItem(ItemStack stack, int state) {
NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
if(nbt == null) return Blocks.AIR.getDefaultState();
//slightly paranoid NBT handling cause you never know what mysteries are afoot with items
NbtElement subElement;
if(blockEntityTag.contains(BLOCKSTATE_KEY)) subElement = blockEntityTag.get(BLOCKSTATE_KEY); //2.0.5
else if(blockEntityTag.contains("BlockState")) subElement = blockEntityTag.get("BlockState"); //old 2.0.4 items
NbtElement element;
if(nbt.contains(BLOCKSTATE_KEY + state)) element = nbt.get(BLOCKSTATE_KEY + state);
else return Blocks.AIR.getDefaultState();
if(!(subElement instanceof NbtCompound subCompound)) return Blocks.AIR.getDefaultState();
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), subCompound);
if(!(element instanceof NbtCompound compound)) return Blocks.AIR.getDefaultState();
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), compound);
}
//Awkward: usually the BlockState is the source of truth for things like the "emits light" blockstate, but if you
//ctrl-pick a glowing block and place it, it should still be glowing. This is some hacky shit that guesses the value of
//the LIGHT blockstate based off information in the NBT tag, and also prevents bugginess like "the blockstate is not
//glowing but the copied NBT thinks glowstone dust was already added, so it refuses to accept more dust"
public static @Nullable BlockState weirdNbtLightLevelStuff(@Nullable BlockState state, ItemStack stack) {
public static @Nullable BlockState getNbtLightLevel(@Nullable BlockState state, ItemStack stack) {
if(state == null || stack == null) return state;
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
if(blockEntityTag == null) return state;
NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
if(nbt == null) return state;
if(state.contains(ReFramedInteractionUtil.LIGHT)) {
state = state.with(ReFramedInteractionUtil.LIGHT,
blockEntityTag.getBoolean("spentglow") || //2.0.4
((blockEntityTag.contains(BITFIELD_KEY) ? blockEntityTag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD) & SPENT_GLOWSTONE_DUST_MASK) != 0 || //2.0.5
readStateFromItem(stack).getLuminance() != 0 //glowstone dust wasn't manually added, the block just emits light
if(state.contains(BlockProperties.LIGHT)) {
state = state.with(BlockProperties.LIGHT,
((nbt.contains(BITFIELD_KEY)
? nbt.getByte(BITFIELD_KEY)
: SOLIDITY_MASK)
& LIGHT_MASK) != 0
);
}
return state;
}
//RenderAttachmentBlockEntity impl. Note that ThemeableBlockEntity depends on this returning a BlockState object.
@Override
public BlockState getRenderAttachmentData() {
return renderedState;
public BlockState getTheme(int i) {
return first_state;
}
public void setRenderedState(BlockState newState) {
if(!Objects.equals(renderedState, newState)) {
renderedState = newState;
@Override
public List<BlockState> getThemes() {
List<BlockState> themes = new ArrayList<>();
themes.add(first_state);
return themes;
}
public void setTheme(BlockState new_state, int i) {
if(!Objects.equals(first_state, new_state)) {
first_state = new_state;
markDirtyAndDispatch();
}
}
public boolean hasSpentGlowstoneDust() {
return (bitfield & SPENT_GLOWSTONE_DUST_MASK) != 0;
/* --------------------------------------------------- ADDONS --------------------------------------------------- */
public boolean emitsLight() {
return (bit_field & LIGHT_MASK) != 0;
}
public void spentGlowstoneDust() {
bitfield |= SPENT_GLOWSTONE_DUST_MASK;
public void toggleLight() {
if (emitsLight()) bit_field &= ~LIGHT_MASK;
else bit_field |= LIGHT_MASK;
markDirtyAndDispatch();
}
public boolean hasSpentRedstoneTorch() {
return (bitfield & SPENT_REDSTONE_TORCH_MASK) != 0;
}
public void spentRedstoneTorch() {
bitfield |= SPENT_REDSTONE_TORCH_MASK;
public void toggleRedstone() {
if (emitsRedstone()) bit_field &= ~REDSTONE_MASK;
else bit_field |= REDSTONE_MASK;
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
markDirtyAndDispatch();
}
public boolean hasSpentPoppedChorus() {
return (bitfield & SPENT_POPPED_CHORUS_MASK) != 0;
}
public void spentPoppedChorus() {
bitfield |= SPENT_POPPED_CHORUS_MASK;
markDirtyAndDispatch();
}
public boolean emitsRedstone() {
return (bitfield & EMITS_REDSTONE_MASK) != 0;
return (bit_field & REDSTONE_MASK) != 0;
}
public void setEmitsRedstone(boolean nextEmitsRedstone) {
boolean currentlyEmitsRedstone = emitsRedstone();
if(currentlyEmitsRedstone != nextEmitsRedstone) {
if(currentlyEmitsRedstone) bitfield &= ~EMITS_REDSTONE_MASK;
else bitfield |= EMITS_REDSTONE_MASK;
markDirtyAndDispatch();
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
}
public void toggleSolidity() {
if (isSolid()) bit_field &= ~SOLIDITY_MASK;
else bit_field |= SOLIDITY_MASK;
if(world != null) world.setBlockState(pos, getCachedState());
markDirtyAndDispatch();
}
public boolean isSolid() {
return (bitfield & IS_SOLID_MASK) != 0;
return (bit_field & SOLIDITY_MASK) != 0;
}
public void setSolidity(boolean nextSolid) {
boolean currentlySolid = isSolid();
if(currentlySolid != nextSolid) {
if(currentlySolid) bitfield &= ~IS_SOLID_MASK;
else bitfield |= IS_SOLID_MASK;
markDirtyAndDispatch();
if(world != null) world.setBlockState(pos, getCachedState()); //do i need to invalidate any shape caches or something
}
}
//<standard blockentity boilerplate>
@Nullable
@Override
public Packet<ClientPlayPacketListener> toUpdatePacket() {
@@ -195,8 +162,6 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
@Override
public NbtCompound toInitialChunkDataNbt() {
//TERRIBLE yarn name, this is "getUpdateTag", it's the nbt that will be sent to clients
//and it just calls "writeNbt"
return createNbt();
}
@@ -208,5 +173,4 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
markDirty();
dispatch();
}
//</standard blockentity boilerplate>
}

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.FenceBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class ReFramedFenceBlock extends FenceBlock implements BlockEntityProvider {
public ReFramedFenceBlock(Settings settings) {
super(settings);
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
}
@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
return r;
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, newState, moved);
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return ReFramedInteractionUtil.emitsRedstonePower(state);
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
}
}

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

@@ -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 +0,0 @@
package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.PaneBlock;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class ReFramedPaneBlock extends PaneBlock implements BlockEntityProvider {
public ReFramedPaneBlock(Settings settings) {
super(settings);
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
}
@Override
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
return r;
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, newState, moved);
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
super.onPlaced(world, pos, state, placer, stack);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return ReFramedInteractionUtil.emitsRedstonePower(state);
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
}
}

View File

@@ -1,57 +0,0 @@
package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
public class ReFramedPostBlock extends WaterloggableReFramedBlock {
public ReFramedPostBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(Properties.AXIS, Direction.Axis.Y));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(Properties.AXIS));
}
@Override
public @Nullable BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState sup = super.getPlacementState(ctx);
if(sup != null) sup = sup.with(Properties.AXIS, ctx.getSide().getAxis());
return sup;
}
protected static final VoxelShape SHAPE_X = createCuboidShape(0, 6, 6, 16, 10, 10);
protected static final VoxelShape SHAPE_Y = createCuboidShape(6, 0, 6, 10, 16, 10);
protected static final VoxelShape SHAPE_Z = createCuboidShape(6, 6, 0, 10, 10, 16);
protected VoxelShape shap(BlockState state) {
return switch(state.get(Properties.AXIS)) {
case X -> SHAPE_X;
case Y -> SHAPE_Y;
case Z -> SHAPE_Z;
};
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), shap(state));
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return shap(state);
}
}

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,20 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.generator.MultipartBlockStateProvider;
import net.minecraft.block.*;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.generator.GBlockstate;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.Properties;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
@@ -13,34 +22,41 @@ import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import org.jetbrains.annotations.Nullable;
public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements MultipartBlockStateProvider {
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static net.minecraft.state.property.Properties.FACING;
private static final VoxelShape DOWN = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.5f, 1f);
private static final VoxelShape UP = VoxelShapes.cuboid(0f, 0.5f, 0f, 1f, 1f, 1f);
private static final VoxelShape NORTH = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 1f, 0.5f);
private static final VoxelShape SOUTH = VoxelShapes.cuboid(0f, 0f, 0.5f, 1f, 1f, 1f);
private static final VoxelShape EAST = VoxelShapes.cuboid(0.5f, 0f, 0f, 1f, 1f, 1f);
private static final VoxelShape WEST = VoxelShapes.cuboid(0f, 0f, 0f, 0.5f, 1f, 1f);
public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
protected static final VoxelShape DOWN = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.5f, 1f);
protected static final VoxelShape UP = VoxelShapes.cuboid(0f, 0.5f, 0f, 1f, 1f, 1f);
protected static final VoxelShape NORTH = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 1f, 0.5f);
protected static final VoxelShape SOUTH = VoxelShapes.cuboid(0f, 0f, 0.5f, 1f, 1f, 1f);
protected static final VoxelShape EAST = VoxelShapes.cuboid(0.5f, 0f, 0f, 1f, 1f, 1f);
protected static final VoxelShape WEST = VoxelShapes.cuboid(0f, 0f, 0f, 0.5f, 1f, 1f);
public ReFramedSlabBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(Properties.FACING, Direction.DOWN));
setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(Properties.FACING));
super.appendProperties(builder.add(FACING));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return super.getPlacementState(ctx).with(Properties.FACING, ctx.getSide().getOpposite());
return super.getPlacementState(ctx).with(FACING, ctx.getSide().getOpposite());
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return switch (state.get(Properties.FACING)) {
return getSlabShape(state.get(FACING));
}
public static VoxelShape getSlabShape(Direction side) {
return switch (side) {
case DOWN -> DOWN;
case UP -> UP;
case NORTH -> NORTH;
@@ -52,6 +68,31 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements Mul
@Override
public MultipartBlockStateSupplier getMultipart() {
return null;
Identifier model_id = ReFramed.id("slab_special");
return MultipartBlockStateSupplier.create(this)
.with(GBlockstate.when(FACING, Direction.DOWN),
GBlockstate.variant(model_id, true, R0, R0))
.with(GBlockstate.when(FACING, Direction.SOUTH),
GBlockstate.variant(model_id, true, R90, R0))
.with(GBlockstate.when(FACING, Direction.UP),
GBlockstate.variant(model_id, true, R180, R0))
.with(GBlockstate.when(FACING, Direction.NORTH),
GBlockstate.variant(model_id, true, R270, R0))
.with(GBlockstate.when(FACING, Direction.WEST),
GBlockstate.variant(model_id, true, R90, R90))
.with(GBlockstate.when(FACING, Direction.EAST),
GBlockstate.variant(model_id, true, R90, R270));
}
@Override
public void setRecipe(RecipeExporter exporter) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 2);
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, this, 6)
.pattern("III")
.input('I', ReFramed.CUBE)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
.offerTo(exporter);
}
}

View File

@@ -2,23 +2,27 @@ 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.generator.BlockStateProvider;
import fr.adrien1106.reframed.util.BlockHelper;
import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.property.StairDirection;
import fr.adrien1106.reframed.util.property.Corner;
import fr.adrien1106.reframed.util.property.StairShape;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.data.client.*;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.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;
@@ -30,76 +34,55 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
import static fr.adrien1106.reframed.util.BlockProperties.*;
import static fr.adrien1106.reframed.util.property.StairShape.*;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
import static fr.adrien1106.reframed.util.property.StairDirection.*;
import static fr.adrien1106.reframed.util.property.Corner.*;
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 class ReFramedStairsBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
public 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));
setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN).with(STAIR_SHAPE, STRAIGHT));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(FACING).add(SHAPE));
super.appendProperties(builder.add(CORNER, STAIR_SHAPE));
}
@Override
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) {
return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved)
.with(SHAPE, getPlacementShape(state.get(FACING), world, pos));
.with(STAIR_SHAPE, BlockHelper.getStairsShape(state.getBlock(), state.get(CORNER), 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));
Corner face = BlockHelper.getPlacementCorner(ctx);
StairShape shape = BlockHelper.getStairsShape(this, face, ctx.getWorld(), ctx.getBlockPos());
return super.getPlacementState(ctx).with(CORNER, face).with(STAIR_SHAPE, shape);
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
super.onStateReplaced(state, world, pos, newState, moved);
//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 getOutline(state);
}
public static VoxelShape getOutline(BlockState state) {
StairShape shape = state.get(STAIR_SHAPE);
Corner direction = state.get(CORNER);
return switch (shape) {
case STRAIGHT ->
switch (direction) {
@@ -211,277 +194,248 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M
};
}
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)
return getStairMultipart(this, false);
}
public static MultipartBlockStateSupplier getStairMultipart(Block block, boolean is_double) {
String prefix = is_double ? "double_" : "";
Identifier straight_id = ReFramed.id(prefix + "stairs_special");
Identifier double_outer_id = ReFramed.id(prefix + "outers_stairs_special");
Identifier inner_id = ReFramed.id(prefix + "inner_stairs_special");
Identifier outer_id = ReFramed.id(prefix + "outer_stairs_special");
Identifier outer_side_id = ReFramed.id(prefix + "outer_side_stairs_special");
return MultipartBlockStateSupplier.create(block)
/* STRAIGHT X AXIS */
.with(GBlockstate.when(FACING, DOWN_EAST, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, DOWN_EAST, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R0, R0))
.with(GBlockstate.when(FACING, EAST_UP, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, EAST_UP, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R180, R0))
.with(GBlockstate.when(FACING, UP_WEST, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, UP_WEST, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R180, R180))
.with(GBlockstate.when(FACING, WEST_DOWN, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, WEST_DOWN, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R0, R180))
/* STRAIGHT Y AXIS */
.with(GBlockstate.when(FACING, EAST_SOUTH, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, EAST_SOUTH, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R90, R0))
.with(GBlockstate.when(FACING, SOUTH_WEST, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, SOUTH_WEST, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R90, R90))
.with(GBlockstate.when(FACING, WEST_NORTH, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, WEST_NORTH, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R90, R180))
.with(GBlockstate.when(FACING, NORTH_EAST, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, NORTH_EAST, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R90, R270))
/* STRAIGHT Z AXIS */
.with(GBlockstate.when(FACING, DOWN_SOUTH, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, DOWN_SOUTH, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R0, R90))
.with(GBlockstate.when(FACING, NORTH_DOWN, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, NORTH_DOWN, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R0, R270))
.with(GBlockstate.when(FACING, UP_NORTH, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, UP_NORTH, STAIR_SHAPE, STRAIGHT),
GBlockstate.variant(straight_id, true, R180, R270))
.with(GBlockstate.when(FACING, SOUTH_UP, SHAPE, STRAIGHT),
.with(GBlockstate.when(CORNER, SOUTH_UP, STAIR_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.when(CORNER, NORTH_DOWN, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, WEST_NORTH, STAIR_SHAPE, INNER_RIGHT),
GBlockstate.when(CORNER, WEST_DOWN, STAIR_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.when(CORNER, NORTH_DOWN, STAIR_SHAPE, INNER_RIGHT),
GBlockstate.when(CORNER, NORTH_EAST, STAIR_SHAPE, INNER_RIGHT),
GBlockstate.when(CORNER, DOWN_EAST, STAIR_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.when(CORNER, DOWN_SOUTH, STAIR_SHAPE, INNER_RIGHT),
GBlockstate.when(CORNER, EAST_SOUTH, STAIR_SHAPE, INNER_RIGHT),
GBlockstate.when(CORNER, DOWN_EAST, STAIR_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.when(CORNER, DOWN_SOUTH, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, SOUTH_WEST, STAIR_SHAPE, INNER_RIGHT),
GBlockstate.when(CORNER, WEST_DOWN, STAIR_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.when(CORNER, EAST_UP, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, NORTH_EAST, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, UP_NORTH, STAIR_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.when(CORNER, EAST_UP, STAIR_SHAPE, INNER_RIGHT),
GBlockstate.when(CORNER, EAST_SOUTH, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, SOUTH_UP, STAIR_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.when(CORNER, SOUTH_UP, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, SOUTH_WEST, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, UP_WEST, STAIR_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.when(CORNER, UP_NORTH, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, WEST_NORTH, STAIR_SHAPE, INNER_LEFT),
GBlockstate.when(CORNER, UP_WEST, STAIR_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.when(CORNER, DOWN_SOUTH, STAIR_SHAPE, SECOND_OUTER_RIGHT),
GBlockstate.when(CORNER, DOWN_EAST, STAIR_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.when(CORNER, DOWN_SOUTH, STAIR_SHAPE, SECOND_OUTER_LEFT),
GBlockstate.when(CORNER, WEST_DOWN, STAIR_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.when(CORNER, NORTH_DOWN, STAIR_SHAPE, FIRST_OUTER_LEFT),
GBlockstate.when(CORNER, WEST_DOWN, STAIR_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.when(CORNER, NORTH_DOWN, STAIR_SHAPE, FIRST_OUTER_RIGHT),
GBlockstate.when(CORNER, DOWN_EAST, STAIR_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.when(CORNER, UP_NORTH, STAIR_SHAPE, SECOND_OUTER_RIGHT),
GBlockstate.when(CORNER, EAST_UP, STAIR_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.when(CORNER, SOUTH_UP, STAIR_SHAPE, FIRST_OUTER_RIGHT),
GBlockstate.when(CORNER, EAST_UP, STAIR_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.when(CORNER, SOUTH_UP, STAIR_SHAPE, FIRST_OUTER_LEFT),
GBlockstate.when(CORNER, UP_WEST, STAIR_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.when(CORNER, UP_NORTH, STAIR_SHAPE, SECOND_OUTER_LEFT),
GBlockstate.when(CORNER, UP_WEST, STAIR_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.when(CORNER, EAST_SOUTH, STAIR_SHAPE, SECOND_OUTER_RIGHT),
GBlockstate.when(CORNER, DOWN_EAST, STAIR_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.when(CORNER, EAST_SOUTH, STAIR_SHAPE, SECOND_OUTER_LEFT),
GBlockstate.when(CORNER, EAST_UP, STAIR_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.when(CORNER, NORTH_EAST, STAIR_SHAPE, FIRST_OUTER_LEFT),
GBlockstate.when(CORNER, EAST_UP, STAIR_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.when(CORNER, NORTH_EAST, STAIR_SHAPE, FIRST_OUTER_RIGHT),
GBlockstate.when(CORNER, DOWN_EAST, STAIR_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.when(CORNER, SOUTH_WEST, STAIR_SHAPE, SECOND_OUTER_RIGHT),
GBlockstate.when(CORNER, DOWN_SOUTH, STAIR_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.when(CORNER, SOUTH_WEST, STAIR_SHAPE, SECOND_OUTER_LEFT),
GBlockstate.when(CORNER, SOUTH_UP, STAIR_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.when(CORNER, EAST_SOUTH, STAIR_SHAPE, FIRST_OUTER_LEFT),
GBlockstate.when(CORNER, SOUTH_UP, STAIR_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.when(CORNER, EAST_SOUTH, STAIR_SHAPE, FIRST_OUTER_RIGHT),
GBlockstate.when(CORNER, DOWN_SOUTH, STAIR_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.when(CORNER, WEST_NORTH, STAIR_SHAPE, SECOND_OUTER_RIGHT),
GBlockstate.when(CORNER, WEST_DOWN, STAIR_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.when(CORNER, WEST_NORTH, STAIR_SHAPE, SECOND_OUTER_LEFT),
GBlockstate.when(CORNER, UP_WEST, STAIR_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.when(CORNER, SOUTH_WEST, STAIR_SHAPE, FIRST_OUTER_LEFT),
GBlockstate.when(CORNER, UP_WEST, STAIR_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.when(CORNER, SOUTH_WEST, STAIR_SHAPE, FIRST_OUTER_RIGHT),
GBlockstate.when(CORNER, WEST_DOWN, STAIR_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.when(CORNER, NORTH_EAST, STAIR_SHAPE, SECOND_OUTER_RIGHT),
GBlockstate.when(CORNER, NORTH_DOWN, STAIR_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.when(CORNER, NORTH_EAST, STAIR_SHAPE, SECOND_OUTER_LEFT),
GBlockstate.when(CORNER, UP_NORTH, STAIR_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.when(CORNER, WEST_NORTH, STAIR_SHAPE, FIRST_OUTER_LEFT),
GBlockstate.when(CORNER, UP_NORTH, STAIR_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.when(CORNER, WEST_NORTH, STAIR_SHAPE, FIRST_OUTER_RIGHT),
GBlockstate.when(CORNER, NORTH_DOWN, STAIR_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.when(CORNER, DOWN_SOUTH, STAIR_SHAPE, OUTER_RIGHT),
GBlockstate.when(CORNER, DOWN_EAST, STAIR_SHAPE, OUTER_RIGHT),
GBlockstate.when(CORNER, EAST_SOUTH, STAIR_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.when(CORNER, DOWN_SOUTH, STAIR_SHAPE, OUTER_LEFT),
GBlockstate.when(CORNER, WEST_DOWN, STAIR_SHAPE, OUTER_RIGHT),
GBlockstate.when(CORNER, SOUTH_WEST, STAIR_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.when(CORNER, NORTH_DOWN, STAIR_SHAPE, OUTER_LEFT),
GBlockstate.when(CORNER, WEST_DOWN, STAIR_SHAPE, OUTER_LEFT),
GBlockstate.when(CORNER, WEST_NORTH, STAIR_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.when(CORNER, NORTH_DOWN, STAIR_SHAPE, OUTER_RIGHT),
GBlockstate.when(CORNER, DOWN_EAST, STAIR_SHAPE, OUTER_LEFT),
GBlockstate.when(CORNER, NORTH_EAST, STAIR_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.when(CORNER, UP_NORTH, STAIR_SHAPE, OUTER_RIGHT),
GBlockstate.when(CORNER, EAST_UP, STAIR_SHAPE, OUTER_LEFT),
GBlockstate.when(CORNER, NORTH_EAST, STAIR_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.when(CORNER, SOUTH_UP, STAIR_SHAPE, OUTER_RIGHT),
GBlockstate.when(CORNER, EAST_UP, STAIR_SHAPE, OUTER_RIGHT),
GBlockstate.when(CORNER, EAST_SOUTH, STAIR_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.when(CORNER, SOUTH_UP, STAIR_SHAPE, OUTER_LEFT),
GBlockstate.when(CORNER, UP_WEST, STAIR_SHAPE, OUTER_RIGHT),
GBlockstate.when(CORNER, SOUTH_WEST, STAIR_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.when(CORNER, UP_NORTH, STAIR_SHAPE, OUTER_LEFT),
GBlockstate.when(CORNER, UP_WEST, STAIR_SHAPE, OUTER_LEFT),
GBlockstate.when(CORNER, WEST_NORTH, STAIR_SHAPE, OUTER_LEFT)),
GBlockstate.variant(double_outer_id, true, R180, R270));
}
@Override
public void setRecipe(RecipeExporter exporter) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE);
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, this, 4)
.pattern("I ")
.pattern("II ")
.pattern("III")
.input('I', ReFramed.CUBE)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
.offerTo(exporter);
}
static {
final VoxelShape STRAIGHT = Stream.of(
VoxelShapes.cuboid(0.0f, 0.0f, 0.0f, 1.0f, 0.5f, 1.0f),

View File

@@ -0,0 +1,136 @@
package fr.adrien1106.reframed.block;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.generator.GBlockstate;
import fr.adrien1106.reframed.generator.BlockStateProvider;
import fr.adrien1106.reframed.util.BlockHelper;
import fr.adrien1106.reframed.util.VoxelHelper;
import fr.adrien1106.reframed.util.property.Corner;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.data.client.MultipartBlockStateSupplier;
import net.minecraft.data.server.recipe.RecipeExporter;
import net.minecraft.data.server.recipe.RecipeProvider;
import net.minecraft.data.server.recipe.ShapedRecipeJsonBuilder;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.recipe.book.RecipeCategory;
import net.minecraft.state.StateManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.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.ArrayList;
import java.util.List;
import static fr.adrien1106.reframed.util.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.property.Corner.*;
import static net.minecraft.data.client.VariantSettings.Rotation.*;
public class ReFramedStepBlock extends WaterloggableReFramedBlock implements BlockStateProvider {
public static final List<VoxelShape> STEP_VOXELS = new ArrayList<>(12);
public ReFramedStepBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(CORNER, Corner.NORTH_DOWN));
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(CORNER));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return super.getPlacementState(ctx).with(CORNER, BlockHelper.getPlacementCorner(ctx));
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
return switch (state.get(CORNER)) {
case DOWN_SOUTH -> STEP_VOXELS.get(0);
case NORTH_DOWN -> STEP_VOXELS.get(1);
case UP_NORTH -> STEP_VOXELS.get(2);
case SOUTH_UP -> STEP_VOXELS.get(3);
case DOWN_EAST -> STEP_VOXELS.get(4);
case WEST_DOWN -> STEP_VOXELS.get(5);
case UP_WEST -> STEP_VOXELS.get(6);
case EAST_UP -> STEP_VOXELS.get(7);
case NORTH_EAST -> STEP_VOXELS.get(8);
case EAST_SOUTH -> STEP_VOXELS.get(9);
case SOUTH_WEST -> STEP_VOXELS.get(10);
case WEST_NORTH -> STEP_VOXELS.get(11);
};
}
@Override
public MultipartBlockStateSupplier getMultipart() {
Identifier step_id = ReFramed.id("step_special");
return MultipartBlockStateSupplier.create(this)
/* X AXIS */
.with(GBlockstate.when(CORNER, DOWN_EAST),
GBlockstate.variant(step_id, true, R0, R0))
.with(GBlockstate.when(CORNER, EAST_UP),
GBlockstate.variant(step_id, true, R180, R0))
.with(GBlockstate.when(CORNER, UP_WEST),
GBlockstate.variant(step_id, true, R180, R180))
.with(GBlockstate.when(CORNER, WEST_DOWN),
GBlockstate.variant(step_id, true, R0, R180))
/* Y AXIS */
.with(GBlockstate.when(CORNER, EAST_SOUTH),
GBlockstate.variant(step_id, true, R90, R0))
.with(GBlockstate.when(CORNER, SOUTH_WEST),
GBlockstate.variant(step_id, true, R90, R90))
.with(GBlockstate.when(CORNER, WEST_NORTH),
GBlockstate.variant(step_id, true, R90, R180))
.with(GBlockstate.when(CORNER, NORTH_EAST),
GBlockstate.variant(step_id, true, R90, R270))
/* Z AXIS */
.with(GBlockstate.when(CORNER, DOWN_SOUTH),
GBlockstate.variant(step_id, true, R0, R90))
.with(GBlockstate.when(CORNER, NORTH_DOWN),
GBlockstate.variant(step_id, true, R0, R270))
.with(GBlockstate.when(CORNER, UP_NORTH),
GBlockstate.variant(step_id, true, R180, R270))
.with(GBlockstate.when(CORNER, SOUTH_UP),
GBlockstate.variant(step_id, true, R180, R90));
}
@Override
public void setRecipe(RecipeExporter exporter) {
RecipeProvider.offerStonecuttingRecipe(exporter, RecipeCategory.BUILDING_BLOCKS, this, ReFramed.CUBE, 4);
ShapedRecipeJsonBuilder
.create(RecipeCategory.BUILDING_BLOCKS, this, 8)
.pattern("II")
.input('I', ReFramed.CUBE)
.criterion(FabricRecipeProvider.hasItem(ReFramed.CUBE), FabricRecipeProvider.conditionsFromItem(ReFramed.CUBE))
.criterion(FabricRecipeProvider.hasItem(this), FabricRecipeProvider.conditionsFromItem(this))
.offerTo(exporter);
}
static {
final VoxelShape STEP = VoxelShapes.cuboid(0f, 0f, .5f, 1f, .5f, 1f);
STEP_VOXELS.add(STEP);
STEP_VOXELS.add(VoxelHelper.mirror(STEP, Direction.Axis.Z));
STEP_VOXELS.add(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(STEP, Direction.Axis.X), Direction.Axis.Z));
STEP_VOXELS.add(VoxelHelper.rotateCounterClockwise(STEP, Direction.Axis.X));
STEP_VOXELS.add(VoxelHelper.rotateCounterClockwise(STEP, Direction.Axis.Y));
STEP_VOXELS.add(VoxelHelper.mirror(VoxelHelper.rotateCounterClockwise(STEP, Direction.Axis.Y), Direction.Axis.X));
STEP_VOXELS.add(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(STEP, Direction.Axis.Y), Direction.Axis.Z), Direction.Axis.X));
STEP_VOXELS.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateCounterClockwise(STEP, Direction.Axis.Y), Direction.Axis.Z));
STEP_VOXELS.add(VoxelHelper.rotateCounterClockwise(VoxelHelper.rotateClockwise(STEP, Direction.Axis.Z), Direction.Axis.Y));
STEP_VOXELS.add(VoxelHelper.rotateClockwise(STEP, Direction.Axis.Z));
STEP_VOXELS.add(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(STEP, Direction.Axis.Z), Direction.Axis.Y));
STEP_VOXELS.add(VoxelHelper.mirror(VoxelHelper.rotateClockwise(VoxelHelper.rotateClockwise(STEP, Direction.Axis.Z), Direction.Axis.Y), Direction.Axis.Z));
}
}

View File

@@ -1,94 +0,0 @@
package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import fr.adrien1106.reframed.util.ReframedInteractible;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.TrapdoorBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
public class ReFramedTrapdoorBlock extends TrapdoorBlock implements BlockEntityProvider, ReframedInteractible {
public ReFramedTrapdoorBlock(Settings settings, BlockSetType woodType) {
super(woodType, settings);
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
}
@Override
public @Nullable BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
return r;
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, newState, moved);
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
super.onPlaced(world, pos, state, placer, stack);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return ReFramedInteractionUtil.emitsRedstonePower(state);
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
}
@Override
public boolean canAddRedstoneEmission(BlockState state, BlockView view, BlockPos pos) {
return false;
}
}

View File

@@ -1,130 +0,0 @@
package fr.adrien1106.reframed.block;
import com.google.common.base.MoreObjects;
import fr.adrien1106.reframed.ReFramed;
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
import fr.adrien1106.reframed.mixin.WallBlockAccessor;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.block.WallBlock;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.block.enums.WallShape;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.state.StateManager;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
public class ReFramedWallBlock extends WallBlock implements BlockEntityProvider {
public ReFramedWallBlock(Settings settings) {
super(settings);
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
initNewShapemaps(); //WallBlock specific haxx
}
@Nullable
@Override
public BlockEntity createBlockEntity(BlockPos pos, BlockState state) {
return ReFramed.REFRAMED_BLOCK_ENTITY.instantiate(pos, state);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
}
@Override
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
return r;
}
@Override
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
super.onStateReplaced(state, world, pos, newState, moved);
}
@Override
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), getNewShape(state, newCollisionShapeMap));
}
@Override
public VoxelShape getOutlineShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return getNewShape(state, newShapeMap);
}
@Override
public boolean emitsRedstonePower(BlockState state) {
return ReFramedInteractionUtil.emitsRedstonePower(state);
}
@Override
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
}
@Override
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
}
//Shapemap heck (WallBlock has a map keyed on BlockState, but since we add more blockstates most of those map lookups fail)
protected record ShapeKey(boolean up, WallShape north, WallShape east, WallShape south, WallShape west) {
static ShapeKey fromBlockstate(BlockState state) {
return new ShapeKey(
state.get(WallBlock.UP),
state.get(WallBlock.NORTH_SHAPE),
state.get(WallBlock.EAST_SHAPE),
state.get(WallBlock.SOUTH_SHAPE),
state.get(WallBlock.WEST_SHAPE)
);
}
}
protected final Map<ShapeKey, VoxelShape> newShapeMap = new HashMap<>();
protected final Map<ShapeKey, VoxelShape> newCollisionShapeMap = new HashMap<>();
protected void initNewShapemaps() {
initNewShapemap(((WallBlockAccessor) this).getShapeMap(), newShapeMap);
initNewShapemap(((WallBlockAccessor) this).getCollisionShapeMap(), newCollisionShapeMap);
}
protected void initNewShapemap(Map<BlockState, VoxelShape> oldShapeMap, Map<ShapeKey, VoxelShape> newShapeMap) {
oldShapeMap.forEach((state, shape) -> newShapeMap.putIfAbsent(ShapeKey.fromBlockstate(state), shape));
}
protected VoxelShape getNewShape(BlockState state, Map<ShapeKey, VoxelShape> shapes) {
return shapes.getOrDefault(ShapeKey.fromBlockstate(state), VoxelShapes.empty());
}
}

View File

@@ -16,7 +16,6 @@ import org.jetbrains.annotations.Nullable;
public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterloggable {
public WaterloggableReFramedBlock(Settings settings) {
super(settings);
setDefaultState(getDefaultState().with(Properties.WATERLOGGED, false));
}

View File

@@ -0,0 +1,43 @@
package fr.adrien1106.reframed.block;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.Waterloggable;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.Properties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.WorldAccess;
import org.jetbrains.annotations.Nullable;
public class WaterloggableReFramedDoubleBlock extends ReFramedDoubleBlock implements Waterloggable {
public WaterloggableReFramedDoubleBlock(Settings settings) {
super(settings);
}
@Override
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
super.appendProperties(builder.add(Properties.WATERLOGGED));
}
@Nullable
@Override
public BlockState getPlacementState(ItemPlacementContext ctx) {
BlockState sup = super.getPlacementState(ctx);
if(sup != null) sup = sup.with(Properties.WATERLOGGED, ctx.getWorld().getFluidState(ctx.getBlockPos()).isOf(Fluids.WATER));
return sup;
}
@Override
public FluidState getFluidState(BlockState state) {
return state.get(Properties.WATERLOGGED) ? Fluids.WATER.getStill(false) : super.getFluidState(state);
}
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState otherState, WorldAccess world, BlockPos pos, BlockPos moved) {
if(state.get(Properties.WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world));
return super.getStateForNeighborUpdate(state, direction, otherState, world, pos, moved);
}
}

View File

@@ -25,73 +25,32 @@ public class ReFramedClient implements ClientModInitializer {
//all frames mustn't be on the SOLID layer because they are not opaque!
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0]));
HELPER.addReFramedModel(ReFramed.id("button_special") , HELPER.auto(new Identifier("block/button")));
HELPER.addReFramedModel(ReFramed.id("button_pressed_special") , HELPER.auto(new Identifier("block/button_pressed")));
HELPER.addReFramedModel(ReFramed.id("one_candle_special") , HELPER.auto(new Identifier("block/template_candle")));
HELPER.addReFramedModel(ReFramed.id("two_candles_special") , HELPER.auto(new Identifier("block/template_two_candles")));
HELPER.addReFramedModel(ReFramed.id("three_candles_special") , HELPER.auto(new Identifier("block/template_three_candles")));
HELPER.addReFramedModel(ReFramed.id("four_candles_special") , HELPER.auto(new Identifier("block/template_four_candles")));
HELPER.addReFramedModel(ReFramed.id("carpet_special") , HELPER.auto(new Identifier("block/carpet")));
HELPER.addReFramedModel(ReFramed.id("cube_special") , HELPER.auto(new Identifier("block/cube")));
HELPER.addReFramedModel(ReFramed.id("door_bottom_left_special") , HELPER.auto(new Identifier("block/door_bottom_left")));
HELPER.addReFramedModel(ReFramed.id("door_bottom_right_special") , HELPER.auto(new Identifier("block/door_bottom_right")));
HELPER.addReFramedModel(ReFramed.id("door_top_left_special") , HELPER.auto(new Identifier("block/door_top_left")));
HELPER.addReFramedModel(ReFramed.id("door_top_right_special") , HELPER.auto(new Identifier("block/door_top_right")));
HELPER.addReFramedModel(ReFramed.id("door_bottom_left_open_special"), HELPER.auto(new Identifier("block/door_bottom_left_open")));
HELPER.addReFramedModel(ReFramed.id("door_bottom_right_open_special"), HELPER.auto(new Identifier("block/door_bottom_right_open"))); //This is why we dont format code as tables kids
HELPER.addReFramedModel(ReFramed.id("door_top_left_open_special") , HELPER.auto(new Identifier("block/door_top_left_open")));
HELPER.addReFramedModel(ReFramed.id("door_top_right_open_special") , HELPER.auto(new Identifier("block/door_top_right_open")));
HELPER.addReFramedModel(ReFramed.id("fence_post_special") , HELPER.auto(new Identifier("block/fence_post")));
HELPER.addReFramedModel(ReFramed.id("fence_gate_special") , HELPER.auto(new Identifier("block/template_fence_gate")));
HELPER.addReFramedModel(ReFramed.id("fence_gate_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_open")));
HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall")));
HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall_open")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_post_special") , HELPER.auto(new Identifier("block/glass_pane_post")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_special") , HELPER.auto(new Identifier("block/glass_pane_noside")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_alt_special"), HELPER.auto(new Identifier("block/glass_pane_noside_alt")));
HELPER.addReFramedModel(ReFramed.id("pressure_plate_up_special") , HELPER.auto(new Identifier("block/pressure_plate_up")));
HELPER.addReFramedModel(ReFramed.id("pressure_plate_down_special") , HELPER.auto(new Identifier("block/pressure_plate_down")));
HELPER.addReFramedModel(ReFramed.id("slab_special") , HELPER.auto(new Identifier("block/slab")));
HELPER.addReFramedModel(ReFramed.id("stairs_special") , HELPER.auto(ReFramed.id("block/stairs")));
HELPER.addReFramedModel(ReFramed.id("double_outer_stairs_special") , HELPER.auto(ReFramed.id("block/double_outer_stairs")));
HELPER.addReFramedModel(ReFramed.id("inner_stairs_special") , HELPER.auto(ReFramed.id("block/inner_stairs")));
HELPER.addReFramedModel(ReFramed.id("outer_stairs_special") , HELPER.auto(ReFramed.id("block/outer_stairs")));
HELPER.addReFramedModel(ReFramed.id("outer_side_stairs_special") , HELPER.auto(ReFramed.id("block/outer_side_stairs")));
HELPER.addReFramedModel(ReFramed.id("trapdoor_bottom_special") , HELPER.auto(new Identifier("block/template_trapdoor_bottom")));
HELPER.addReFramedModel(ReFramed.id("trapdoor_top_special") , HELPER.auto(new Identifier("block/template_trapdoor_top")));
HELPER.addReFramedModel(ReFramed.id("wall_post_special") , HELPER.auto(new Identifier("block/template_wall_post")));
//vanilla style models (using "special-sprite replacement" method)
HELPER.addReFramedModel(ReFramed.id("lever_special") , HELPER.json(ReFramed.id("block/lever")));
HELPER.addReFramedModel(ReFramed.id("trapdoor_open_special") , HELPER.json(ReFramed.id("block/trapdoor_open")));
HELPER.addReFramedModel(ReFramed.id("lever_on_special") , HELPER.json(ReFramed.id("block/lever_on")));
//these next five only exist because AutoRetexturedModels don't seem to rotate their textures the right way when rotated from a multipart blockstate
HELPER.addReFramedModel(ReFramed.id("fence_side_special") , HELPER.json(ReFramed.id("block/fence_side")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_side_special") , HELPER.json(ReFramed.id("block/glass_pane_side")));
HELPER.addReFramedModel(ReFramed.id("glass_pane_side_alt_special") , HELPER.json(ReFramed.id("block/glass_pane_side_alt")));
HELPER.addReFramedModel(ReFramed.id("wall_side_special") , HELPER.json(ReFramed.id("block/wall_side")));
HELPER.addReFramedModel(ReFramed.id("wall_side_tall_special") , HELPER.json(ReFramed.id("block/wall_side_tall")));
//item only models
HELPER.addReFramedModel(ReFramed.id("button_inventory_special") , HELPER.auto(new Identifier("block/button_inventory")));
HELPER.addReFramedModel(ReFramed.id("fence_inventory_special") , HELPER.auto(new Identifier("block/fence_inventory")));
HELPER.addReFramedModel(ReFramed.id("fence_post_inventory_special") , HELPER.auto(ReFramed.id("block/fence_post_inventory")));
HELPER.addReFramedModel(ReFramed.id("wall_inventory_special") , HELPER.auto(new Identifier("block/wall_inventory")));
HELPER.addReFramedModel("cube_special" , HELPER.auto(new Identifier("block/cube")));
HELPER.addReFramedModel("slab_special" , HELPER.auto(new Identifier("block/slab")));
HELPER.addReFramedModel("double_slab_special" , HELPER.autoDouble(new Identifier("block/slab"), new Identifier("block/slab_top")));
HELPER.addReFramedModel("stairs_special" , HELPER.auto(ReFramed.id("block/stairs")));
HELPER.addReFramedModel("outers_stairs_special" , HELPER.auto(ReFramed.id("block/double_outer_stairs")));
HELPER.addReFramedModel("inner_stairs_special" , HELPER.auto(ReFramed.id("block/inner_stairs")));
HELPER.addReFramedModel("outer_stairs_special" , HELPER.auto(ReFramed.id("block/outer_stairs")));
HELPER.addReFramedModel("outer_side_stairs_special" , HELPER.auto(ReFramed.id("block/outer_side_stairs")));
HELPER.addReFramedModel("double_stairs_special" , HELPER.autoDouble(ReFramed.id("block/stairs"), ReFramed.id("block/stairs_complement")));
HELPER.addReFramedModel("double_outers_stairs_special" , HELPER.autoDouble(ReFramed.id("block/double_outer_stairs"), ReFramed.id("block/double_outer_stairs_complement")));
HELPER.addReFramedModel("double_inner_stairs_special" , HELPER.autoDouble(ReFramed.id("block/inner_stairs"), ReFramed.id("block/inner_stairs_complement")));
HELPER.addReFramedModel("double_outer_stairs_special" , HELPER.autoDouble(ReFramed.id("block/outer_stairs"), ReFramed.id("block/outer_stairs_complement")));
HELPER.addReFramedModel("double_outer_side_stairs_special", HELPER.autoDouble(ReFramed.id("block/outer_side_stairs"), ReFramed.id("block/outer_side_stairs_complement")));
HELPER.addReFramedModel("step_special" , HELPER.auto(ReFramed.id("block/step")));
HELPER.addReFramedModel("double_step_special" , HELPER.autoDouble(ReFramed.id("block/step"), ReFramed.id("block/step_complement_slab")));
HELPER.addReFramedModel("double_step_side_special" , HELPER.autoDouble(ReFramed.id("block/step_side"), ReFramed.id("block/step_side_complement_slab")));
//item model assignments (in lieu of models/item/___.json)
HELPER.assignItemModel(ReFramed.id("button_inventory_special") , ReFramed.BUTTON);
HELPER.assignItemModel(ReFramed.id("carpet_special") , ReFramed.CARPET);
HELPER.assignItemModel(ReFramed.id("cube_special") , ReFramed.CUBE);
HELPER.assignItemModel(ReFramed.id("fence_inventory_special") , ReFramed.FENCE);
HELPER.assignItemModel(ReFramed.id("fence_gate_special") , ReFramed.FENCE_GATE);
HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.IRON_TRAPDOOR);
HELPER.assignItemModel(ReFramed.id("fence_post_inventory_special") , ReFramed.POST);
HELPER.assignItemModel(ReFramed.id("pressure_plate_up_special") , ReFramed.PRESSURE_PLATE);
HELPER.assignItemModel(ReFramed.id("slab_special") , ReFramed.SLAB);
HELPER.assignItemModel(ReFramed.id("stairs_special") , ReFramed.STAIRS);
HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.TRAPDOOR);
HELPER.assignItemModel(ReFramed.id("wall_inventory_special") , ReFramed.WALL);
HELPER.assignItemModel("cube_special" , ReFramed.CUBE);
HELPER.assignItemModel("slab_special" , ReFramed.SLAB);
HELPER.assignItemModel("double_slab_special" , ReFramed.DOUBLE_SLAB);
HELPER.assignItemModel("stairs_special" , ReFramed.STAIRS);
HELPER.assignItemModel("double_stairs_special" , ReFramed.DOUBLE_STAIRS);
HELPER.assignItemModel("step_special" , ReFramed.STEP);
HELPER.assignItemModel("double_step_special" , ReFramed.DOUBLE_STEP);
}
private void privateInit() {

View File

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

View File

@@ -37,9 +37,9 @@ public class ReFramedModelProvider implements ModelResourceProvider, ModelVarian
//but json models are never allowed to have non-json models as a parent, and frame unbaked models are not json models. Ah well.
//So, instead, we use a ModelVariantProvider to redirect attempts to load the item:id#inventory model.
@Override
public @Nullable UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) {
Identifier customModelId = itemAssignments.get(modelId);
return customModelId == null ? null : loadModelResource(customModelId, context);
public @Nullable UnbakedModel loadModelVariant(ModelIdentifier model, ModelProviderContext context) {
Identifier custom_model = itemAssignments.get(model);
return custom_model == null ? null : loadModelResource(custom_model, context);
}
/// camo appearance manager cache

View File

@@ -0,0 +1,51 @@
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.BakedModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.random.Random;
import net.minecraft.world.BlockRenderView;
import java.util.List;
import java.util.function.Supplier;
@Environment(EnvType.CLIENT)
public class DoubleRetexturingBakedModel extends ForwardingBakedModel implements MultiRetexturableModel {
private final ForwardingBakedModel model_1, model_2;
public DoubleRetexturingBakedModel(ForwardingBakedModel model_1, ForwardingBakedModel model_2) {
this.wrapped = model_1.getWrappedModel();
this.model_1 = model_1;
this.model_2 = model_2;
}
@Override
public boolean isVanillaAdapter() {
return false;
}
@Override
public Sprite getParticleSprite() {
return model_1.getParticleSprite();
}
@Override
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {}
@Override // models are emitted here because no checks are done on items
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
model_1.emitItemQuads(stack, randomSupplier, context);
model_2.emitItemQuads(stack, randomSupplier, context);
}
@Override
public List<BakedModel> models() {
return List.of(model_1, model_2);
}
}

View File

@@ -6,5 +6,5 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
public interface DynamicBakedModel {
BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos);
BakedModel computeQuads(BlockRenderView level, BlockState state, 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,10 @@
package fr.adrien1106.reframed.client.model;
import net.minecraft.client.render.model.BakedModel;
import java.util.List;
public interface MultiRetexturableModel {
List<BakedModel> models();
}

View File

@@ -1,12 +1,15 @@
package fr.adrien1106.reframed.client.model;
import fr.adrien1106.reframed.block.ReFramedEntity;
import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.client.model.apperance.SpriteProperties;
import fr.adrien1106.reframed.mixin.MinecraftAccessor;
import fr.adrien1106.reframed.client.model.apperance.CamoAppearance;
import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
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.MutableQuadView;
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
@@ -29,30 +32,44 @@ import java.util.concurrent.ConcurrentMap;
import java.util.function.Supplier;
public abstract class RetexturingBakedModel extends ForwardingBakedModel {
public RetexturingBakedModel(BakedModel baseModel, CamoAppearanceManager tam, ModelBakeSettings settings, BlockState itemModelState, boolean ao) {
this.wrapped = baseModel; //field from the superclass; vanilla getQuads etc. will delegate through to this
public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state, boolean ao) {
this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this
this.tam = tam;
this.uvlock = settings.isUvLocked();
this.itemModelState = itemModelState;
this.theme_index = theme_index;
this.uv_lock = settings.isUvLocked();
this.item_state = item_state;
this.ao = ao;
}
protected final CamoAppearanceManager tam;
protected final boolean uvlock;
protected final BlockState itemModelState;
protected final int theme_index;
protected final boolean uv_lock;
protected final BlockState item_state;
protected final boolean ao;
/* ----------------------------------------------- CACHE ELEMENT ------------------------------------------------ */
// TODO make static ? for connected textures ?
protected record MeshCacheKey(BlockState state, TransformCacheKey transform) {}
protected final ConcurrentMap<MeshCacheKey, Mesh> retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache
protected record TransformCacheKey(CamoAppearance appearance, int model_id) {}
protected final ConcurrentMap<TransformCacheKey, RetexturingTransformer> retextured_transforms = new ConcurrentHashMap<>();
protected static final Direction[] DIRECTIONS = Direction.values();
protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1];
static { System.arraycopy(DIRECTIONS, 0, DIRECTIONS_AND_NULL, 0, DIRECTIONS.length); }
protected abstract Mesh getBaseMesh(BlockState state);
protected final ConcurrentMap<MeshCacheKey, Mesh> retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache
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 final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
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);
}
protected abstract Mesh convertModel(BlockState state);
@Override
public boolean isVanillaAdapter() {
@@ -61,20 +78,20 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
@Override
public Sprite getParticleSprite() {
return tam.getDefaultAppearance().getSprites(Direction.UP, 0).get(0).sprite();
return tam.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite();
}
@Override
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();
if(theme == null || theme.isAir()) {
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(), 0)), 0).outputTo(quad_emitter);
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(theme_index), 0)), 0).outputTo(quad_emitter);
return;
}
if(theme.getBlock() == Blocks.BARRIER) return;
CamoAppearance camo = tam.getCamoAppearance(world, theme, pos);
CamoAppearance camo = tam.getCamoAppearance(world, theme, pos, theme_index);
long seed = theme.getRenderingSeed(pos);
int model_id = 0;
if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
@@ -105,16 +122,16 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
//none of this is accessible unless you're in creative mode doing ctrl-pick btw
CamoAppearance nbtAppearance;
int tint;
BlockState theme = ReFramedEntity.readStateFromItem(stack);
BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index);
if(!theme.isAir()) {
nbtAppearance = tam.getCamoAppearance(null, theme, null);
nbtAppearance = tam.getCamoAppearance(null, theme, null, theme_index);
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
} else {
nbtAppearance = tam.getDefaultAppearance();
nbtAppearance = tam.getDefaultAppearance(theme_index);
tint = 0xFFFFFFFF;
}
Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(itemModelState, new TransformCacheKey(nbtAppearance, 0)), 0);
Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(item_state, new TransformCacheKey(nbtAppearance, 0)), 0);
QuadEmitter quad_emitter = context.getEmitter();
if(tint == 0xFFFFFFFF) {
@@ -132,7 +149,22 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
protected Mesh createUntintedRetexturedMesh(MeshCacheKey key, long seed) {
RetexturingTransformer transformer = retextured_transforms.computeIfAbsent(key.transform, (k) -> new RetexturingTransformer(k.appearance, seed));
return MeshTransformUtil.pretransformMesh(getBaseMesh(key.state), transformer);
return pretransformMesh(getBaseMesh(key.state), transformer);
}
private static Mesh pretransformMesh(Mesh mesh, 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 class RetexturingTransformer {
@@ -161,7 +193,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
properties.sprite(),
MutableQuadView.BAKE_NORMALIZED
| properties.flags()
| (uvlock ? MutableQuadView.BAKE_LOCK_UV : 0)
| (uv_lock ? MutableQuadView.BAKE_LOCK_UV : 0)
);
quad.tag(i+1);
quad.emit();
@@ -175,7 +207,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
// 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
quad.spriteBake( // seems to work without the flags and break with it
properties.sprite(),
MutableQuadView.BAKE_NORMALIZED
| MutableQuadView.BAKE_LOCK_UV

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.Baker;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
@@ -20,50 +19,27 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
public class UnbakedAutoRetexturedModel implements UnbakedModel {
public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel {
public UnbakedAutoRetexturedModel(Identifier parent) {
this.parent = parent;
}
protected final Identifier parent;
protected BlockState itemModelState = Blocks.AIR.getDefaultState();
protected boolean ao = true;
@Override
public Collection<Identifier> getModelDependencies() {
return Collections.singletonList(parent);
}
@Override
public void setParents(Function<Identifier, UnbakedModel> function) {
function.apply(parent).setParents(function);
super(parent);
item_state = Blocks.AIR.getDefaultState();
}
@Nullable
@Override
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings bake_settings, Identifier identifier) {
return new RetexturingBakedModel(
baker.bake(parent, modelBakeSettings),
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
modelBakeSettings,
itemModelState,
baker.bake(parent, bake_settings),
ReFramedClient.HELPER.getCamoApperanceManager(texture_getter),
theme_index,
bake_settings,
item_state,
ao
) {
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
@Override
protected Mesh getBaseMesh(BlockState state) {
//Convert models to retexturable Meshes lazily, the first time we encounter each blockstate
return jsonToMesh.computeIfAbsent(state, this::convertModel);
}
private Mesh convertModel(BlockState state) {
protected Mesh convertModel(BlockState state) {
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
MeshBuilder builder = r.meshBuilder();
QuadEmitter emitter = builder.getEmitter();

View File

@@ -0,0 +1,48 @@
package fr.adrien1106.reframed.client.model;
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.render.model.UnbakedModel;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.util.Identifier;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.function.Function;
public class UnbakedDoubleRetexturedModel implements UnbakedModel {
protected final UnbakedModel model_1;
protected final UnbakedModel model_2;
public UnbakedDoubleRetexturedModel(UnbakedRetexturedModel model_1, UnbakedRetexturedModel model_2) {
this.model_1 = model_1;
this.model_2 = model_2;
model_2.setThemeIndex(2);
}
@Override
public Collection<Identifier> getModelDependencies() {
return List.of(((List<Identifier>) model_1.getModelDependencies()).get(0), ((List<Identifier>) model_2.getModelDependencies()).get(0));
}
@Override
public void setParents(Function<Identifier, UnbakedModel> function) {
model_1.setParents(function);
model_2.setParents(function);
}
@Nullable
@Override
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings model_bake_settings, Identifier identifier) {
return new DoubleRetexturingBakedModel(
(ForwardingBakedModel) model_1.bake(baker, texture_getter, model_bake_settings, identifier),
(ForwardingBakedModel) model_2.bake(baker, texture_getter, model_bake_settings, identifier)
);
}
}

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.QuadEmitter;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.*;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.BakedQuad;
import net.minecraft.client.render.model.Baker;
import net.minecraft.client.render.model.ModelBakeSettings;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.SpriteIdentifier;
import net.minecraft.screen.PlayerScreenHandler;
@@ -17,61 +20,34 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.random.Random;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;
public class UnbakedJsonRetexturedModel implements UnbakedModel {
public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
public UnbakedJsonRetexturedModel(Identifier parent) {
this.parent = parent;
}
protected final Identifier parent;
protected BlockState itemModelState;
protected boolean ao = true;
@Override
public Collection<Identifier> getModelDependencies() {
return Collections.singletonList(parent);
}
@Override
public void setParents(Function<Identifier, UnbakedModel> function) {
function.apply(parent).setParents(function);
super(parent);
}
@Nullable
@Override
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
Direction[] DIRECTIONS = RetexturingBakedModel.DIRECTIONS;
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings bake_settings, Identifier identifier) {
Direction[] directions = Direction.values();
Sprite[] specialSprites = new Sprite[DIRECTIONS.length];
for(int i = 0; i < DIRECTIONS.length; i++) {
SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + DIRECTIONS[i].getName()));
specialSprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
Sprite[] sprites = new Sprite[directions.length];
for(int i = 0; i < directions.length; i++) {
SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + directions[i].getName()));
sprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
}
BakedModel model = baker.bake(parent, modelBakeSettings);
return new RetexturingBakedModel(
model,
baker.bake(parent, bake_settings),
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
modelBakeSettings,
itemModelState,
theme_index,
bake_settings,
item_state,
ao
) {
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
@Override
protected Mesh getBaseMesh(BlockState state) {
//Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate
return jsonToMesh.computeIfAbsent(state, this::convertModel);
}
private Mesh convertModel(BlockState state) {
protected Mesh convertModel(BlockState state) {
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
MeshBuilder builder = r.meshBuilder();
QuadEmitter emitter = builder.getEmitter();
@@ -84,9 +60,9 @@ public class UnbakedJsonRetexturedModel implements UnbakedModel {
emitter.fromVanilla(quad, mat, cullFace);
QuadUvBounds bounds = QuadUvBounds.read(emitter);
for(int i = 0; i < specialSprites.length; i++) {
if(bounds.displaysSprite(specialSprites[i])) {
bounds.normalizeUv(emitter, specialSprites[i]);
for(int i = 0; i < sprites.length; i++) {
if(bounds.displaysSprite(sprites[i])) {
bounds.normalizeUv(emitter, sprites[i]);
emitter.tag(i + 1);
break;
}

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

View File

@@ -44,37 +44,42 @@ public class CamoAppearanceManager {
materialsWithAo.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO
}
Sprite defaultSprite = spriteLookup.apply(DEFAULT_SPRITE_ID);
if(defaultSprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_ID + " !");
this.defaultAppearance = new SingleSpriteAppearance(defaultSprite, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement());
Sprite barrier = spriteLookup.apply(BARRIER_SPRITE_ID);
if(barrier == null) barrier = defaultSprite; //eh
this.barrierItemAppearance = new SingleSpriteAppearance(barrier, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement());
Sprite sprite = spriteLookup.apply(DEFAULT_SPRITE_MAIN);
if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
this.default_appearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
sprite = spriteLookup.apply(DEFAULT_SPRITE_SECONDARY);
if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
this.accent_appearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
sprite = spriteLookup.apply(BARRIER_SPRITE_ID);
this.barrierItemAppearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
}
protected static final SpriteIdentifier DEFAULT_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block"));
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 final CamoAppearance defaultAppearance;
private final CamoAppearance default_appearance;
private final CamoAppearance accent_appearance;
private final CamoAppearance barrierItemAppearance;
private final ConcurrentHashMap<BlockState, CamoAppearance> appearanceCache = new ConcurrentHashMap<>(); //Mutable, append-only cache
private final AtomicInteger serialNumber = new AtomicInteger(0); //Mutable
private final AtomicInteger serial_number = new AtomicInteger(0); //Mutable
private final EnumMap<BlendMode, RenderMaterial> materialsWithAo = new EnumMap<>(BlendMode.class);
private final EnumMap<BlendMode, RenderMaterial> materialsWithoutAo = new EnumMap<>(BlendMode.class); //Immutable contents
public CamoAppearance getDefaultAppearance() {
return defaultAppearance;
public CamoAppearance getDefaultAppearance(int appearance) {
return appearance == 2 ? accent_appearance: default_appearance;
}
public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos) {
public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index) {
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
if (model instanceof DynamicBakedModel dynamic_model) {
return computeAppearance(dynamic_model.computeQuads(world, state, pos), state);
return computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state);
}
return appearanceCache.computeIfAbsent(state, block_state -> computeAppearance(model, block_state));
}
@@ -96,7 +101,7 @@ public class CamoAppearanceManager {
getAppearance(model),
getCachedMaterial(state, true),
getCachedMaterial(state, false),
serialNumber.getAndIncrement()
serial_number.getAndIncrement()
);
}
List<Weighted.Present<Appearance>> appearances = weighted_model.getModels().stream()
@@ -107,7 +112,7 @@ public class CamoAppearanceManager {
appearances,
getCachedMaterial(state, true),
getCachedMaterial(state, false),
serialNumber.getAndIncrement()
serial_number.getAndIncrement()
);
}
@@ -124,7 +129,7 @@ public class CamoAppearanceManager {
Arrays.stream(Direction.values()).forEach(direction -> {
List<BakedQuad> quads = model.getQuads(null, direction, random);
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;
}

View File

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

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;
import fr.adrien1106.reframed.ReFramed;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricBlockLootTableProvider;
import net.minecraft.registry.Registries;
@@ -12,6 +13,6 @@ public class GBlockLoot extends FabricBlockLootTableProvider {
@Override
public void generate() {
Generator.BLOCKS.forEach(block -> addDrop(block, Registries.ITEM.get(Registries.BLOCK.getId(block))));
ReFramed.BLOCKS.forEach(block -> addDrop(block, Registries.ITEM.get(Registries.BLOCK.getId(block))));
}
}

View File

@@ -0,0 +1,22 @@
package fr.adrien1106.reframed.generator;
import fr.adrien1106.reframed.ReFramed;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider.BlockTagProvider;
import net.minecraft.registry.RegistryWrapper.WrapperLookup;
import net.minecraft.registry.tag.BlockTags;
import java.util.concurrent.CompletableFuture;
public class GBlockTag extends BlockTagProvider {
public GBlockTag(FabricDataOutput output, CompletableFuture<WrapperLookup> registries) {
super(output, registries);
}
@Override
protected void configure(WrapperLookup arg) {
FabricTagBuilder builder = getOrCreateTagBuilder(BlockTags.AXE_MINEABLE);
ReFramed.BLOCKS.forEach(builder::add);
}
}

View File

@@ -1,5 +1,6 @@
package fr.adrien1106.reframed.generator;
import fr.adrien1106.reframed.ReFramed;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider;
import net.minecraft.data.client.*;
@@ -8,6 +9,8 @@ import net.minecraft.util.Identifier;
import java.util.Objects;
import static net.minecraft.data.client.VariantSettings.Rotation.R0;
public class GBlockstate extends FabricModelProvider {
public GBlockstate(FabricDataOutput output) {
@@ -16,10 +19,20 @@ public class GBlockstate extends FabricModelProvider {
@Override
public void generateBlockStateModels(BlockStateModelGenerator model_generator) {
Generator.BLOCKS
ReFramed.BLOCKS
.forEach(model_generator::excludeFromSimpleItemModelGeneration);
Generator.BLOCKS.stream()
.map(block -> block instanceof MultipartBlockStateProvider multipart_block ? multipart_block.getMultipart(): null)
ReFramed.BLOCKS.stream()
.map(block -> {
if (block instanceof BlockStateProvider multipart_block) return multipart_block.getMultipart();
return VariantsBlockStateSupplier.create(
block,
GBlockstate.variant(
ReFramed.id("cube_special"),
true,
R0, R0
)
);
})
.filter(Objects::nonNull)
.forEach(model_generator.blockStateCollector);
}
@@ -30,8 +43,8 @@ public class GBlockstate extends FabricModelProvider {
public static BlockStateVariant variant(Identifier model, boolean uv_lock, VariantSettings.Rotation x, VariantSettings.Rotation y) {
BlockStateVariant variant = BlockStateVariant.create().put(VariantSettings.MODEL, model);
if (uv_lock) variant.put(VariantSettings.UVLOCK, uv_lock);
if (!x.equals(VariantSettings.Rotation.R0)) variant.put(VariantSettings.X, x);
if (!y.equals(VariantSettings.Rotation.R0)) variant.put(VariantSettings.Y, y);
if (!x.equals(R0)) variant.put(VariantSettings.X, x);
if (!y.equals(R0)) variant.put(VariantSettings.Y, y);
return variant;
}

View File

@@ -0,0 +1,30 @@
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");
});
}
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,19 @@
package fr.adrien1106.reframed.generator;
import fr.adrien1106.reframed.ReFramed;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricRecipeProvider;
import net.minecraft.data.server.recipe.RecipeExporter;
public class GRecipe extends FabricRecipeProvider {
public GRecipe(FabricDataOutput output) {
super(output);
}
@Override
public void generate(RecipeExporter exporter) {
ReFramed.BLOCKS.forEach(block -> {
if (block instanceof RecipeSetter provider) provider.setRecipe(exporter);
});
}
}

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.FabricDataGenerator;
import net.minecraft.block.Block;
import java.util.List;
import static fr.adrien1106.reframed.ReFramed.*;
public class Generator implements DataGeneratorEntrypoint {
/**
* missing DOOR, IRON_DOOR, CANDLE
*/
public static List<Block> BLOCKS = List.of(CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, TRAPDOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE);
@Override
public void onInitializeDataGenerator(FabricDataGenerator data_generator) {
FabricDataGenerator.Pack myPack = data_generator.createPack();
myPack.addProvider(GBlockLoot::new);
myPack.addProvider(GBlockstate::new);
FabricDataGenerator.Pack my_pack = data_generator.createPack();
my_pack.addProvider(GBlockLoot::new);
my_pack.addProvider(GBlockstate::new);
my_pack.addProvider(GLanguage::new);
my_pack.addProvider(GBlockTag::new);
my_pack.addProvider(GRecipe::new);
my_pack.addProvider(GAdvancement::new);
}
}

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,8 @@
package fr.adrien1106.reframed.generator;
import net.minecraft.data.server.recipe.RecipeExporter;
public interface RecipeSetter {
void setRecipe(RecipeExporter exporter);
}

View File

@@ -0,0 +1,48 @@
package fr.adrien1106.reframed.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import com.llamalad7.mixinextras.sugar.ref.LocalRef;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import static fr.adrien1106.reframed.block.ReFramedEntity.BLOCKSTATE_KEY;
@Mixin(BlockItem.class)
public class BlockItemMixin {
@Shadow @Final @Deprecated private Block block;
@Inject(
method = "writeNbtToBlockEntity",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lnet/minecraft/item/BlockItem;getBlockEntityNbt(Lnet/minecraft/item/ItemStack;)Lnet/minecraft/nbt/NbtCompound;",
shift = At.Shift.AFTER
)
)
private static void placeBlockWithOffHandCamo(World world, PlayerEntity player, BlockPos pos, ItemStack stack, CallbackInfoReturnable<Boolean> cir, @Local LocalRef<NbtCompound> compound) {
if (compound.get() != null
|| player.getOffHandStack().isEmpty()
|| !(player.getOffHandStack().getItem() instanceof BlockItem block)
|| block.getBlock() instanceof BlockEntityProvider
|| !Block.isShapeFullCube(block.getBlock().getDefaultState().getCollisionShape(world, pos))
) return;
NbtCompound new_comp = new NbtCompound();
new_comp.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(block.getBlock().getDefaultState()));
compound.set(new_comp);
}
}

View File

@@ -1,46 +0,0 @@
package fr.adrien1106.reframed.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedEntity;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.function.BooleanBiFunction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.*;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(Block.class)
public class BlockMixin {
@Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isOpaque()Z"))
private static boolean isNeighborCamoOpaque(BlockState state, @Local(argsOnly = true) BlockView world, @Local(ordinal = 1, argsOnly = true) BlockPos pos) {
BlockEntity block_entity = world.getBlockEntity(pos);
if (!(block_entity instanceof ReFramedEntity frame_entity)) return state.isOpaque();
return frame_entity.getThemeState().isOpaque();
}
@Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isSideInvisible(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/Direction;)Z"))
private static boolean isCamoInvisible(BlockState state, BlockState other_state, Direction direction, @Local(argsOnly = true) BlockView world, @Local(ordinal = 0, argsOnly = true) BlockPos pos, @Local(ordinal = 1, argsOnly = true) BlockPos other_pos) {
if (world.getBlockEntity(other_pos) instanceof ReFramedEntity entity) other_state = entity.getThemeState();
if (world.getBlockEntity(pos) instanceof ReFramedEntity entity) state = entity.getThemeState();
return state.isSideInvisible(other_state, direction);
}
@Inject(method = "shouldDrawSide", at = @At(value = "RETURN", ordinal = 0), cancellable = true)
private static void shouldDrawGlassCamoSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, CallbackInfoReturnable<Boolean> cir, @Local(ordinal = 1) BlockState neighbor) {
if (!(world.getBlockEntity(pos) instanceof ReFramedEntity) && !(world.getBlockEntity(other_pos) instanceof ReFramedEntity)) return;
VoxelShape voxel_block = state.getCullingFace(world, pos, side);
if (voxel_block.isEmpty()) {
cir.setReturnValue(true);
return;
}
VoxelShape voxel_neighbor = neighbor.getCullingFace(world, pos, side.getOpposite());
cir.setReturnValue(VoxelShapes.matchesAnywhere(voxel_block, voxel_neighbor, BooleanBiFunction.ONLY_FIRST));
}
}

View File

@@ -1,49 +0,0 @@
package fr.adrien1106.reframed.mixin;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedEntity;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockRenderView;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(BlockRenderInfo.class)
public abstract class BlockRenderInfoMixin {
@Shadow public BlockPos blockPos;
@Shadow public BlockState blockState;
@Shadow public BlockRenderView blockView;
@Shadow @Final private BlockPos.Mutable searchPos;
@ModifyArg(method = "prepareForBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
"getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;"))
public BlockState prepareCamoLayer(BlockState state, @Local(argsOnly = true) BlockPos pos) {
BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(pos);
if (!(block_entity instanceof ReFramedEntity frame_entity)) return state;
return frame_entity.getThemeState();
}
@Inject(method = "shouldDrawFace",
at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/util/math/Direction;getId()I"),
cancellable = true)
private void shouldDrawCamoFace(Direction face, CallbackInfoReturnable<Boolean> cir) {
BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(blockPos);
if (!(block_entity instanceof ReFramedEntity)) return;
cir.setReturnValue(Block.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face)));
}
}

View File

@@ -2,6 +2,8 @@ package fr.adrien1106.reframed.mixin;
import net.fabricmc.loader.api.FabricLoader;
import org.objectweb.asm.tree.ClassNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin;
import org.spongepowered.asm.mixin.extensibility.IMixinInfo;
@@ -12,9 +14,17 @@ import java.util.function.Supplier;
public class CompatMixinPlugin implements IMixinConfigPlugin {
private static final FabricLoader LOADER = FabricLoader.getInstance();
private static final Logger LOGGER = LoggerFactory.getLogger("ReFramed MIXIN");
private static final List<String> COMPAT_MOD = List.of("athena", "indium", "sodium");
private static final Map<String, Supplier<Boolean>> CONDITIONS = Map.of(
"fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> FabricLoader.getInstance().isModLoaded("athena"),
"fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> FabricLoader.getInstance().isModLoaded("athena")
"fr.adrien1106.reframed.mixin.compat.AthenaBakedModelMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
"fr.adrien1106.reframed.mixin.compat.AthenaWrappedGetterMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(0)),
"fr.adrien1106.reframed.mixin.render.TerrainRenderContextMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainRenderContextMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.render.BlockRenderInfoMixin", () -> !LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.compat.IndiumTerrainBlockRenderInfoMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(1)),
"fr.adrien1106.reframed.mixin.compat.SodiumBlockOcclusionCacheMixin", () -> LOADER.isModLoaded(COMPAT_MOD.get(2))
);
@@ -44,12 +54,16 @@ public class CompatMixinPlugin implements IMixinConfigPlugin {
}
@Override
public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
public void preApply(String target_class_name, ClassNode target_class, String mixin_class_name, IMixinInfo mixin_info) {
}
@Override
public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) {
public void postApply(String target_class, ClassNode target, String mixin_class, IMixinInfo mixin_info) {
String mixin_class_name = mixin_class.substring(mixin_class.lastIndexOf('.') + 1);
COMPAT_MOD.forEach(mod -> {
if (mixin_class_name.toLowerCase().startsWith(mod))
LOGGER.info("Loaded compatibility mixin class for mod \"" + mod + "\" (class: " + target_class + ")");
});
}
}

View File

@@ -6,6 +6,7 @@ import earth.terrarium.athena.api.client.models.AthenaBlockModel;
import fr.adrien1106.reframed.client.ReFramedClient;
import fr.adrien1106.reframed.client.model.DynamicBakedModel;
import fr.adrien1106.reframed.compat.RebakedAthenaModel;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import net.fabricmc.fabric.api.renderer.v1.Renderer;
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
@@ -26,9 +27,9 @@ import java.util.*;
@Mixin(AthenaBakedModel.class)
public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedModel {
@Shadow @Final private AthenaBlockModel model;
@Shadow(remap = false) @Final private AthenaBlockModel model;
@Shadow @Final private Int2ObjectMap<Sprite> textures;
@Shadow(remap = false) @Final private Int2ObjectMap<Sprite> textures;
/**
* Reuses the emitQuad method to compute the quads to be used by the frame
@@ -39,35 +40,41 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM
* @return - the rebakedmodel containing the computed quads
*/
@Override
public BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos) {
if (level == null || pos == null) return this;
public BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos, int theme_index) {
Map<Direction, List<BakedQuad>> face_quads = new HashMap<>();
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
QuadEmitter emitter = r.meshBuilder().getEmitter();
WrappedGetter getter = new WrappedGetter(level);
Arrays.stream(Direction.values()).forEach(direction -> {
face_quads.put(direction, new ArrayList<>());
model.getQuads(getter, state, pos, direction).forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> {
Sprite texture = textures.get(sprite.sprite());
if (texture == null) return quads;
emitter.square(direction, sprite.left(), sprite.bottom(), sprite.right(), sprite.top(), sprite.depth());
int flag = MutableQuadView.BAKE_LOCK_UV;
(level == null || pos == null
? model.getDefaultQuads(direction).get(direction)
: model.getQuads(
getter,
level.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity
? framed_entity.getTheme(theme_index)
: state, pos, direction)
)
.forEach(sprite -> face_quads.computeIfPresent(direction, (d, quads) -> {
Sprite texture = textures.get(sprite.sprite());
if (texture == null) return quads;
emitter.square(direction, sprite.left(), sprite.bottom(), sprite.right(), sprite.top(), sprite.depth());
switch (sprite.rotation()) {
case CLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_90;
case CLOCKWISE_180 -> flag |= MutableQuadView.BAKE_ROTATE_180;
case COUNTERCLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_270;
}
int flag = MutableQuadView.BAKE_LOCK_UV;
emitter.spriteBake(texture, flag);
emitter.color(-1, -1, -1, -1);
quads.add(emitter.toBakedQuad(texture));
return quads;
}));
switch (sprite.rotation()) {
case CLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_90;
case CLOCKWISE_180 -> flag |= MutableQuadView.BAKE_ROTATE_180;
case COUNTERCLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_270;
}
emitter.spriteBake(texture, flag);
emitter.color(-1, -1, -1, -1);
quads.add(emitter.toBakedQuad(texture));
return quads;
}));
});
return new RebakedAthenaModel(face_quads);

View File

@@ -3,7 +3,7 @@ package fr.adrien1106.reframed.mixin.compat;
import earth.terrarium.athena.api.client.utils.AppearanceAndTintGetter;
import earth.terrarium.athena.api.client.utils.CtmUtils;
import earth.terrarium.athena.impl.client.models.ConnectedBlockModel;
import fr.adrien1106.reframed.block.ReFramedEntity;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
@@ -22,8 +22,8 @@ public class AthenaConnectedBlockModelMixin {
"Lnet/minecraft/util/math/Direction;)Z"))
private boolean checkForCull(AppearanceAndTintGetter level, BlockState state, BlockPos pos, Direction direction) {
// Always get all the textures unless its another block then use default behaviour
if (level.getBlockEntity(pos) instanceof ReFramedEntity
|| level.getBlockEntity(pos.offset(direction)) instanceof ReFramedEntity)
if (level.getBlockEntity(pos) instanceof ThemeableBlockEntity
|| level.getBlockEntity(pos.offset(direction)) instanceof ThemeableBlockEntity)
return false;
return CtmUtils.checkRelative(level, state, pos, direction);
}

View File

@@ -1,45 +1,30 @@
package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local;
import earth.terrarium.athena.api.client.fabric.WrappedGetter;
import fr.adrien1106.reframed.block.ReFramedEntity;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.BlockRenderView;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(WrappedGetter.class)
public class AthenaWrappedGetterMixin {
@Shadow @Final private BlockRenderView getter;
@Inject(method = "getBlockState", at = @At(value = "HEAD"), cancellable = true)
private void getCamoState(BlockPos pos, CallbackInfoReturnable<BlockState> cir) {
if (!(getter.getBlockEntity(pos) instanceof ReFramedEntity framed_entity)) return;
cir.setReturnValue(framed_entity.getThemeState());
}
@Redirect(method = "getAppearance(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)" +
"Lnet/minecraft/block/BlockState;",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/BlockRenderView;" +
"getBlockState(Lnet/minecraft/util/math/BlockPos;)" +
"Lnet/minecraft/block/BlockState;"))
private BlockState appearanceCamoState(BlockRenderView world, BlockPos pos) {
if (world.getBlockEntity(pos) instanceof ReFramedEntity framed_entity) return framed_entity.getThemeState();
return world.getBlockState(pos);
}
@Redirect(method = "query",
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/BlockRenderView;" +
"getBlockState(Lnet/minecraft/util/math/BlockPos;)" +
"Lnet/minecraft/block/BlockState;"))
private BlockState queryCamoState(BlockRenderView world, BlockPos pos) {
if (world.getBlockEntity(pos) instanceof ReFramedEntity framed_entity) return framed_entity.getThemeState();
private BlockState queryCamoState(BlockRenderView world, BlockPos pos, @Local(argsOnly = true) BlockState reference_state) {
// get Any that will connect or return any other (/!\ isOf is an uncertain check)
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)
return framed_entity.getThemes()
.stream()
.filter(state -> reference_state.isOf(state.getBlock()))
.findAny()
.orElse(framed_entity.getTheme(1));
return world.getBlockState(pos);
}
}

View File

@@ -0,0 +1,43 @@
package fr.adrien1106.reframed.mixin.compat;
import fr.adrien1106.reframed.util.BlockHelper;
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import link.infra.indium.renderer.render.BlockRenderInfo;
import link.infra.indium.renderer.render.TerrainBlockRenderInfo;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TerrainBlockRenderInfo.class)
public abstract class IndiumTerrainBlockRenderInfoMixin extends BlockRenderInfo implements IBlockRenderInfoMixin {
@Unique private int theme_index = 1;
@Redirect(
method = "shouldDrawFaceInner",
at = @At(
value = "INVOKE",
target = "Lme/jellysquid/mods/sodium/client/render/chunk/compile/pipeline/BlockOcclusionCache;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)Z"
)
)
private boolean shouldDrawCamoSide(BlockOcclusionCache instance, BlockState state, BlockView view, BlockPos pos, Direction face) {
BlockPos other_pos = pos.offset(face);
if (!(view.getBlockEntity(pos) instanceof ThemeableBlockEntity
|| view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity))
return instance.shouldDrawSide(state, view, pos, face);
return BlockHelper.shouldDrawSide(state, view, pos, face, other_pos, theme_index);
}
@Override
public void prepareForBlock(BlockState blockState, BlockPos blockPos, long seed, boolean modelAo, int theme_index) {
this.theme_index = theme_index;
prepareForBlock(blockState, blockPos, seed, modelAo);
}
}

View File

@@ -0,0 +1,42 @@
package fr.adrien1106.reframed.mixin.compat;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.IMultipartBakedModelMixin;
import link.infra.indium.renderer.render.AbstractBlockRenderContext;
import link.infra.indium.renderer.render.TerrainRenderContext;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockRenderContext;
import net.minecraft.client.render.model.BakedModel;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(TerrainRenderContext.class)
public abstract class IndiumTerrainRenderContextMixin extends AbstractBlockRenderContext {
@Inject(
method = "tessellateBlock",
at = @At(
value = "INVOKE",
target = "Llink/infra/indium/renderer/aocalc/AoCalculator;clear()V"
), remap = false,
cancellable = true)
private void renderMultipleModels(BlockRenderContext ctx, CallbackInfo ci) {
if (!(ctx.model() instanceof IMultipartBakedModelMixin wrapped)
|| !(wrapped.getModel(ctx.state()) instanceof MultiRetexturableModel retexturing_model)) return;
List<BakedModel> models = retexturing_model.models();
int i = 0;
for (BakedModel bakedModel : models) {
i++;
aoCalc.clear();
((IBlockRenderInfoMixin) blockInfo).prepareForBlock(ctx.state(), ctx.pos(), ctx.seed(), bakedModel.useAmbientOcclusion(), i);
bakedModel.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
}
ci.cancel();
}
}

View File

@@ -0,0 +1,30 @@
package fr.adrien1106.reframed.mixin.compat;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.util.BlockHelper;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import me.jellysquid.mods.sodium.client.render.chunk.compile.pipeline.BlockOcclusionCache;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(BlockOcclusionCache.class)
public class SodiumBlockOcclusionCacheMixin {
@Inject(
method = "shouldDrawSide",
at = @At(
value = "INVOKE_ASSIGN",
target = "Lnet/minecraft/world/BlockView;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;",
shift = At.Shift.AFTER
), cancellable = true)
private void shouldDrawFrameNeighborSide(BlockState self_state, BlockView view, BlockPos self_pos, Direction face, CallbackInfoReturnable<Boolean> cir, @Local BlockPos.Mutable other_pos) {
if (!(view.getBlockEntity(other_pos) instanceof ThemeableBlockEntity)) return;
cir.setReturnValue(BlockHelper.shouldDrawSide(self_state, view, self_pos, face, other_pos, 0));
}
}

View File

@@ -1,5 +1,6 @@
package fr.adrien1106.reframed.mixin.particles;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.client.MinecraftClient;
@@ -20,8 +21,11 @@ public class MixinBlockDustParticle {
)
void modifyParticleSprite(ClientWorld clientWorld, double d, double e, double f, double g, double h, double i, BlockState state, BlockPos pos, CallbackInfo ci) {
AccessorParticle a = (AccessorParticle) this;
if(a.getRandom().nextBoolean() && clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable) {
BlockState theme = themeable.getThemeState();
if(a.getRandom().nextBoolean()
&& clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable
&& state.getBlock() instanceof ReFramedBlock block
) {
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
if(theme == null || theme.isAir()) return;
Sprite replacement = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(theme);

View File

@@ -1,31 +1,31 @@
package fr.adrien1106.reframed.mixin.particles;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
@Mixin(Entity.class)
public abstract class MixinEntity {
@Shadow @Deprecated public abstract BlockPos getLandingPos(); //TODO, somewhat expensive method
@ModifyArg(
method = "spawnSprintingParticles",
at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;<init>(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V")
)
private BlockState modifyParticleState(BlockState origState) {
private BlockState modifyParticleState(BlockState state, @Local(ordinal = 0) BlockPos landing_pos) {
World world = ((Entity) (Object) this).getWorld();
if(world.getBlockEntity(getLandingPos()) instanceof ThemeableBlockEntity themeable) {
BlockState theme = themeable.getThemeState();
if(!theme.isAir()) return theme;
if(world.getBlockEntity(landing_pos) instanceof ThemeableBlockEntity themeable
&& state.getBlock() instanceof ReFramedBlock block) {
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
if(!theme.isAir()) return theme;
}
return origState;
return state;
}
}

View File

@@ -1,5 +1,7 @@
package fr.adrien1106.reframed.mixin.particles;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
@@ -7,33 +9,25 @@ import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LivingEntity.class)
public class MixinLivingEntity {
@Unique private BlockPos lastFallCheckPos;
@Inject(method = "fall", at = @At("HEAD"))
private void onFall(double d, boolean bl, BlockState blockState, BlockPos blockPos, CallbackInfo ci) {
lastFallCheckPos = blockPos;
}
@ModifyArg(
method = "fall",
at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;<init>(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V")
)
private BlockState modifyParticleState(BlockState origState) {
private BlockState modifyParticleState(BlockState state, @Local(ordinal = 0, argsOnly = true) BlockPos land_pos) {
World world = ((Entity) (Object) this).getWorld();
if(lastFallCheckPos != null && world.getBlockEntity(lastFallCheckPos) instanceof ThemeableBlockEntity themeable) {
BlockState theme = themeable.getThemeState();
if(!theme.isAir()) return theme;
if(world.getBlockEntity(land_pos) instanceof ThemeableBlockEntity themeable
&& state.getBlock() instanceof ReFramedBlock block) {
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
if(!theme.isAir()) return theme;
}
return origState;
return state;
}
}

View File

@@ -0,0 +1,50 @@
package fr.adrien1106.reframed.mixin.render;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.util.BlockHelper;
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
import net.minecraft.block.BlockState;
import net.minecraft.block.entity.BlockEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.BlockRenderView;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyArg;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(BlockRenderInfo.class)
public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
@Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo);
@Shadow public BlockRenderView blockView;
@Unique
private int theme_index = 1;
@ModifyArg(method = "prepareForBlock",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
"getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;"))
public BlockState prepareCamoLayer(BlockState state, @Local(argsOnly = true) BlockPos pos) {
BlockEntity block_entity = blockView.getBlockEntity(pos);
if (!(block_entity instanceof ThemeableBlockEntity frame_entity)) return state;
return frame_entity.getTheme(theme_index);
}
@Redirect(method = "shouldDrawFace", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/Block;shouldDrawSide(Lnet/minecraft/block/BlockState;Lnet/minecraft/world/BlockView;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;Lnet/minecraft/util/math/BlockPos;)Z"))
private boolean shouldDrawAdjacentCamoSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos) {
return BlockHelper.shouldDrawSide(state, world, pos, side, other_pos, theme_index);
}
@Override
@Unique
public void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index) {
this.theme_index = theme_index;
prepareForBlock(state, pos, ao);
}
}

View File

@@ -0,0 +1,25 @@
package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.util.IMultipartBakedModelMixin;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.render.model.MultipartBakedModel;
import org.apache.commons.lang3.tuple.Pair;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
@Mixin(MultipartBakedModel.class)
public class MultipartBakedModelMixin implements IMultipartBakedModelMixin {
@Shadow @Final private List<Pair<Predicate<BlockState>, BakedModel>> components;
@Override
public BakedModel getModel(BlockState state) {
return components.stream().map(pair -> pair.getLeft().test(state) ? pair.getRight(): null).filter(Objects::nonNull).findAny().orElse(null);
}
}

View File

@@ -0,0 +1,41 @@
package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.client.model.MultiRetexturableModel;
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin;
import fr.adrien1106.reframed.util.IMultipartBakedModelMixin;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.AbstractBlockRenderContext;
import net.fabricmc.fabric.impl.client.indigo.renderer.render.TerrainRenderContext;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.List;
@Mixin(TerrainRenderContext.class)
public abstract class TerrainRenderContextMixin extends AbstractBlockRenderContext {
@Inject(method = "tessellateBlock", at = @At(
value = "INVOKE",
target = "Lnet/fabricmc/fabric/impl/client/indigo/renderer/aocalc/AoCalculator;clear()V"
), remap = false,
cancellable = true)
private void renderMultipleModels(BlockState state, BlockPos pos, BakedModel wrapper, MatrixStack matrixStack, CallbackInfo ci) {
if (!(wrapper instanceof IMultipartBakedModelMixin wrapped)
|| !(wrapped.getModel(state) instanceof MultiRetexturableModel retexturing_model)) return;
List<BakedModel> models = retexturing_model.models();
int i = 0;
for (BakedModel bakedModel : models) {
i++;
aoCalc.clear();
((IBlockRenderInfoMixin) blockInfo).prepareForBlock(state, pos, bakedModel.useAmbientOcclusion(), i);
bakedModel.emitBlockQuads(blockInfo.blockView, blockInfo.blockState, blockInfo.blockPos, blockInfo.randomSupplier, this);
}
ci.cancel();
}
}

View File

@@ -0,0 +1,35 @@
package fr.adrien1106.reframed.mixin.render;
import fr.adrien1106.reframed.block.ReFramedDoubleBlock;
import net.minecraft.block.BlockState;
import net.minecraft.block.ShapeContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.BlockView;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(WorldRenderer.class)
public class WorldRendererMixin {
@Shadow @Final private MinecraftClient client;
@Redirect(method = "drawBlockOutline",
at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;" +
"getOutlineShape(" +
"Lnet/minecraft/world/BlockView;" +
"Lnet/minecraft/util/math/BlockPos;" +
"Lnet/minecraft/block/ShapeContext;" +
")Lnet/minecraft/util/shape/VoxelShape;"))
private VoxelShape getRenderOutline(BlockState state, BlockView world, BlockPos pos, ShapeContext shape_context) {
if (state.getBlock() instanceof ReFramedDoubleBlock double_frame_block) // cast is already checked in render
return double_frame_block.getRenderOutline(state, (BlockHitResult) client.crosshairTarget);
return state.getOutlineShape(world, pos, shape_context);
}
}

View File

@@ -0,0 +1,48 @@
package fr.adrien1106.reframed.mixin.sound;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.item.BlockItem;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.sound.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(BlockItem.class)
public class BlockItemMixin {
@Redirect(
method = "place(Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/util/ActionResult;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/BlockState;getSoundGroup()Lnet/minecraft/sound/BlockSoundGroup;"
)
)
public BlockSoundGroup getCamoPlaceSound(BlockState state, @Local World world, @Local BlockPos pos) {
if (state.getBlock() instanceof ReFramedBlock frame_block
&& world.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity
) {
BlockState camo_state = frame_entity.getTheme(frame_block.getTopThemeIndex(state));
state = camo_state.getBlock() != Blocks.AIR ? camo_state : state;
}
return state.getSoundGroup();
}
@Redirect(
method = "place(Lnet/minecraft/item/ItemPlacementContext;)Lnet/minecraft/util/ActionResult;",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/item/BlockItem;getPlaceSound(Lnet/minecraft/block/BlockState;)Lnet/minecraft/sound/SoundEvent;"
)
)
private SoundEvent getCamoSoundEvent(BlockItem item, BlockState state, @Local BlockSoundGroup group) {
// I don't know why it wasn't doing that by default
return group.getPlaceSound();
}
}

View File

@@ -0,0 +1,38 @@
package fr.adrien1106.reframed.mixin.sound;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(Entity.class)
public abstract class EntityMixin {
@Shadow public abstract World getWorld();
@Redirect(
method = "playStepSound",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/BlockState;getSoundGroup()Lnet/minecraft/sound/BlockSoundGroup;"
)
)
private BlockSoundGroup playStepCamoSound(BlockState state, @Local(argsOnly = true) BlockPos pos) {
if (state.getBlock() instanceof ReFramedBlock frame_block
&& getWorld().getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity
) {
BlockState camo_state = frame_entity.getTheme(frame_block.getTopThemeIndex(state));
state = camo_state.getBlock() != Blocks.AIR ? camo_state : state;
}
return state.getSoundGroup();
}
}

View File

@@ -0,0 +1,34 @@
package fr.adrien1106.reframed.mixin.sound;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.LivingEntity;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(LivingEntity.class)
public class LivingEntityMixin {
@Redirect(
method = "playBlockFallSound",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/world/World;getBlockState(Lnet/minecraft/util/math/BlockPos;)Lnet/minecraft/block/BlockState;"
)
)
private BlockState playCamoFallSound(World world, BlockPos pos) {
BlockState state = world.getBlockState(pos);
if (state.getBlock() instanceof ReFramedBlock frame_block
&& world.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity
) {
BlockState camo_state = frame_entity.getTheme(frame_block.getTopThemeIndex(state));
state = camo_state.getBlock() != Blocks.AIR ? camo_state : state;
}
return state;
}
}

View File

@@ -0,0 +1,39 @@
package fr.adrien1106.reframed.mixin.sound;
import com.llamalad7.mixinextras.sugar.Local;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(WorldRenderer.class)
public class WorldRendererMixin {
@Shadow @Nullable private ClientWorld world;
@Redirect(
method = "processWorldEvent",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/BlockState;getSoundGroup()Lnet/minecraft/sound/BlockSoundGroup;"
)
)
private BlockSoundGroup getCamoBreakSound(BlockState state, @Local(argsOnly = true) BlockPos pos) {
if (state.getBlock() instanceof ReFramedBlock frame_block
&& world.getBlockEntity(pos) instanceof ThemeableBlockEntity frame_entity
) {
BlockState camo_state = frame_entity.getTheme(frame_block.getTopThemeIndex(state));
state = camo_state.getBlock() != Blocks.AIR ? camo_state : state;
}
return state.getSoundGroup();
}
}

View File

@@ -0,0 +1,305 @@
package fr.adrien1106.reframed.util;
import fr.adrien1106.reframed.block.ReFramedBlock;
import fr.adrien1106.reframed.block.ReFramedEntity;
import fr.adrien1106.reframed.util.property.Corner;
import fr.adrien1106.reframed.util.property.StairShape;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.*;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.function.BooleanBiFunction;
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.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.World;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static fr.adrien1106.reframed.util.BlockProperties.CORNER;
import static fr.adrien1106.reframed.util.BlockProperties.LIGHT;
import static fr.adrien1106.reframed.util.property.StairShape.*;
import static net.minecraft.util.shape.VoxelShapes.combine;
public class BlockHelper {
public static Corner getPlacementCorner(ItemPlacementContext ctx) {
Direction side = ctx.getSide().getOpposite();
Vec3d pos = getHitPos(ctx.getHitPos(), ctx.getBlockPos());
Direction.Axis axis = getHitAxis(pos, side);
Direction part_direction = Direction.from(
axis,
axis.choose(pos.x, pos.y, pos.z) > 0
? Direction.AxisDirection.POSITIVE
: Direction.AxisDirection.NEGATIVE
);
return Corner.getByDirections(side, part_direction);
}
public static Direction.Axis getHitAxis(Vec3d pos, Direction side) {
Stream<Direction.Axis> axes = Stream.of(Direction.Axis.values()).filter(axis -> !axis.equals(side.getAxis()));
return 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();
}
public static Vec3d getRelativePos(Vec3d pos, BlockPos block_pos) {
return new Vec3d(
pos.getX() - block_pos.getX(),
pos.getY() - block_pos.getY(),
pos.getZ() - block_pos.getZ()
);
}
public static Vec3d getHitPos(Vec3d pos, BlockPos block_pos) {
pos = getRelativePos(pos, block_pos);
return new Vec3d(
pos.getX() - .5d,
pos.getY() - .5d,
pos.getZ() - .5d
);
}
public static StairShape getStairsShape(Block block, Corner face, BlockView world, BlockPos pos) {
StairShape shape = STRAIGHT;
String sol = getNeighborPos(face, face.getFirstDirection(), true, face.getSecondDirection(), world, pos, block);
switch (sol) {
case "right": return INNER_RIGHT;
case "left": return INNER_LEFT;
}
sol = getNeighborPos(face, face.getSecondDirection(), true, face.getFirstDirection(), world, pos, block);
switch (sol) {
case "right": return INNER_RIGHT;
case "left": return INNER_LEFT;
}
sol = getNeighborPos(face, face.getFirstDirection(), false, face.getSecondDirection(), world, pos, block);
switch (sol) {
case "right" -> shape = FIRST_OUTER_RIGHT;
case "left" -> shape = FIRST_OUTER_LEFT;
}
sol = getNeighborPos(face, face.getSecondDirection(), false, face.getFirstDirection(), world, pos, block);
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;
}
public static String getNeighborPos(Corner face, Direction direction, Boolean reverse, Direction reference, BlockView world, BlockPos pos, Block block) {
BlockState block_state = world.getBlockState(
pos.offset(reverse ? direction.getOpposite() : direction)
);
if (block_state.isOf(block) && block_state.get(CORNER).hasDirection(reference)) {
if (block_state.get(CORNER).hasDirection(face.getLeftDirection())) return "left";
else if (block_state.get(CORNER).hasDirection(face.getRightDirection())) return "right";
}
return "";
}
public static ActionResult useCamo(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit, int theme_index) {
if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) return ActionResult.PASS;
// Changing the theme
ItemStack held = player.getStackInHand(hand);
if(held.getItem() instanceof BlockItem block_item && block_entity.getTheme(theme_index).getBlock() == Blocks.AIR) {
Block block = block_item.getBlock();
ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit));
BlockState placement_state = block.getPlacementState(ctx);
if(placement_state != null && Block.isShapeFullCube(placement_state.getCollisionShape(world, pos)) && !(block instanceof BlockEntityProvider)) {
List<BlockState> themes = block_entity.getThemes();
if(!world.isClient) block_entity.setTheme(placement_state, theme_index);
// check for default light emission
if (placement_state.getLuminance() > 0
&& themes.stream().noneMatch(theme -> theme.getLuminance() > 0))
if (block_entity.emitsLight()) Block.dropStack(world, pos, new ItemStack(Items.GLOWSTONE_DUST));
else block_entity.toggleLight();
world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight()));
// check for default redstone emission
if (placement_state.getWeakRedstonePower(world, pos, Direction.NORTH) > 0
&& themes.stream().noneMatch(theme -> theme.getWeakRedstonePower(world, pos, Direction.NORTH) > 0))
if (block_entity.emitsRedstone()) Block.dropStack(world, pos, new ItemStack(Items.GLOWSTONE_DUST));
else block_entity.toggleRedstone();
if(!player.isCreative()) held.decrement(1);
world.playSound(player, pos, placement_state.getSoundGroup().getPlaceSound(), SoundCategory.BLOCKS, 1f, 1.1f);
return ActionResult.SUCCESS;
}
}
return ActionResult.PASS;
}
public static ActionResult useUpgrade(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand) {
if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) return ActionResult.PASS;
ItemStack held = player.getStackInHand(hand);
ReframedInteractible ext = state.getBlock() instanceof ReframedInteractible e ? e : ReframedInteractible.Default.INSTANCE;
// frame will emit light if applied with glowstone
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST) {
block_entity.toggleLight();
world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight()));
if(!player.isCreative())
if (block_entity.emitsLight()) held.decrement(1);
else held.increment(1);
world.playSound(player, pos, SoundEvents.BLOCK_GLASS_HIT, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS;
}
// frame will emit redstone if applied with redstone torch can deactivate redstone block camo emission
if(held.getItem() == Items.REDSTONE_TORCH && ext.canAddRedstoneEmission(state, world, pos)) {
block_entity.toggleRedstone();
if(!player.isCreative())
if (block_entity.emitsRedstone()) held.decrement(1);
else held.increment(1);
world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS;
}
// Frame will lose its collision if applied with popped chorus fruit
if(held.getItem() == Items.POPPED_CHORUS_FRUIT && ext.canRemoveCollision(state, world, pos)) {
block_entity.toggleSolidity();
if(!player.isCreative())
if (!block_entity.isSolid()) held.decrement(1);
else held.increment(1);
world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS;
}
return ActionResult.PASS;
}
// Doing this method from scratch as it is simpler to do than injecting everywhere
public static boolean shouldDrawSide(BlockState self_state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, int theme_index) {
ThemeableBlockEntity self = world.getBlockEntity(pos) instanceof ThemeableBlockEntity e ? e : null;
ThemeableBlockEntity other = world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity e ? e : null;
BlockState other_state = world.getBlockState(other_pos);
// normal behaviour
if (self == null && other == null) return Block.shouldDrawSide(self_state, world, pos, side, other_pos);
// self is a normal Block
if (self == null && other_state.getBlock() instanceof ReFramedBlock other_block) {
VoxelShape self_shape = self_state.getCullingShape(world, pos);
if (self_shape.isEmpty()) return true;
int i = 0;
VoxelShape other_shape = VoxelShapes.empty();
for (BlockState s: other.getThemes()) {
i++;
if (self_state.isSideInvisible(s, side) || s.isOpaque())
other_shape = combine(
other_shape,
other_block
.getShape(other_state, i)
.getFace(side.getOpposite()),
BooleanBiFunction.OR
);
}
// determine if side needs to be rendered
return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST);
}
BlockState self_theme = self.getTheme(theme_index);
// other is normal Block
if (other == null && self_state.getBlock() instanceof ReFramedBlock self_block) {
// Transparent is simple if self and the neighbor are invisible don't render side (like default)
if (self_theme.isSideInvisible(other_state, side)) return false;
// Opaque is also simple as each model are rendered one by one
if (other_state.isOpaque()) {
// no cache section :( because it differs between each instance of the frame
VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side);
if (self_shape.isEmpty()) return true;
VoxelShape other_shape = other_state.getCullingFace(world, other_pos, side.getOpposite());
// determine if side needs to be rendered
return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST);
}
return true;
}
// Both are frames
// here both are computed in the same zone as there will necessarily a shape comparison
if (self_state.getBlock() instanceof ReFramedBlock self_block && other_state.getBlock() instanceof ReFramedBlock other_block) {
VoxelShape self_shape = self_block.getShape(self_state, theme_index).getFace(side);
if (self_shape.isEmpty()) return true;
int i = 0;
VoxelShape other_shape = VoxelShapes.empty();
for (BlockState s: other.getThemes()) {
i++;
if (self_theme.isSideInvisible(s, side) || s.isOpaque())
other_shape = combine(
other_shape,
other_block
.getShape(other_state, i)
.getFace(side.getOpposite()),
BooleanBiFunction.OR
);
}
// determine if side needs to be rendered
return VoxelShapes.matchesAnywhere(self_shape, other_shape, BooleanBiFunction.ONLY_FIRST);
}
return true;
}
public static boolean cursorMatchesFace(VoxelShape shape, Vec3d pos) {
Map<Direction.Axis, Double> axes = Arrays.stream(Direction.Axis.values())
.collect(Collectors.toMap(
x -> x,
x -> x.choose(pos.getX(), pos.getY(), pos.getZ())
));
return shape.getBoundingBoxes().stream()
.anyMatch(box ->
axes.keySet().stream()
.map(x -> box.getMin(x) <= axes.get(x) && box.getMax(x) >= axes.get(x))
.reduce((prev, current) -> prev && current).orElse(false)
);
}
public static int luminance(BlockState state) {
return state.contains(LIGHT) && state.get(LIGHT) ? 15 : 0;
}
}

View File

@@ -0,0 +1,12 @@
package fr.adrien1106.reframed.util;
import fr.adrien1106.reframed.util.property.Corner;
import fr.adrien1106.reframed.util.property.StairShape;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.EnumProperty;
public class BlockProperties {
public static final BooleanProperty LIGHT = BooleanProperty.of("emits_light");
public static final EnumProperty<Corner> CORNER = EnumProperty.of("corner", Corner.class);
public static final EnumProperty<StairShape> STAIR_SHAPE = EnumProperty.of("shape", StairShape.class);
}

View File

@@ -0,0 +1,11 @@
package fr.adrien1106.reframed.util;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
public interface IBlockRenderInfoMixin {
void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index);
void prepareForBlock(BlockState state, BlockPos pos, long seed, boolean ao, int theme_index);
}

View File

@@ -0,0 +1,9 @@
package fr.adrien1106.reframed.util;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.model.BakedModel;
public interface IMultipartBakedModelMixin {
BakedModel getModel(BlockState state);
}

View File

@@ -1,182 +0,0 @@
package fr.adrien1106.reframed.util;
import fr.adrien1106.reframed.block.ReFramedEntity;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.Block;
import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.ShapeContext;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.BlockItem;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.item.ItemUsageContext;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.sound.BlockSoundGroup;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.state.StateManager;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.ItemScatterer;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.util.shape.VoxelShapes;
import net.minecraft.world.BlockView;
import net.minecraft.world.GameRules;
import net.minecraft.world.World;
import org.jetbrains.annotations.Nullable;
//For an example of how to use this class, have a look at TemplateBlock.
//Basically there are several methods that would like to modify the return value of something.
public class ReFramedInteractionUtil {
public static final BooleanProperty LIGHT = BooleanProperty.of("frame_light");
public static StateManager.Builder<Block, BlockState> appendProperties(StateManager.Builder<Block, BlockState> builder) {
return builder.add(LIGHT);
}
//Use this to obtain a Block.Settings that'll make your Template act like the ones in the mod.
//(To complete the look, don't forget to tag your blocks with mineable/axe.)
private static final AbstractBlock.ContextPredicate NOPE = (blah, blahdey, blahh) -> false;
public static AbstractBlock.Settings configureSettings(AbstractBlock.Settings s) {
return s.luminance(ReFramedInteractionUtil::luminance).nonOpaque().sounds(BlockSoundGroup.WOOD).hardness(0.2f).suffocates(NOPE).blockVision(NOPE);
}
//And if you don't have a Block.Settings to copy off of.
public static AbstractBlock.Settings makeSettings() {
return configureSettings(AbstractBlock.Settings.create());
}
public static BlockState setDefaultStates(BlockState in) {
if(in.contains(LIGHT)) in = in.with(LIGHT, false);
return in;
}
public static @Nullable BlockState modifyPlacementState(@Nullable BlockState in, ItemPlacementContext ctx) {
return ReFramedEntity.weirdNbtLightLevelStuff(in, ctx.getStack());
}
public static ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
if(!(world.getBlockEntity(pos) instanceof ReFramedEntity be)) return ActionResult.PASS;
if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS;
ItemStack held = player.getStackInHand(hand);
ReframedInteractible ext = state.getBlock() instanceof ReframedInteractible e ? e : ReframedInteractible.Default.INSTANCE;
//Glowstone
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST && !state.get(LIGHT) && !be.hasSpentGlowstoneDust()) {
world.setBlockState(pos, state.with(LIGHT, true));
be.spentGlowstoneDust();
if(!player.isCreative()) held.decrement(1);
world.playSound(player, pos, SoundEvents.BLOCK_GLASS_HIT, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS;
}
//Redstone
if(held.getItem() == Blocks.REDSTONE_TORCH.asItem() &&
!be.emitsRedstone() &&
!be.hasSpentRedstoneTorch() &&
ext.canAddRedstoneEmission(state, world, pos)
) {
be.setEmitsRedstone(true);
be.spentRedstoneTorch();
if(!player.isCreative()) held.decrement(1);
world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS;
}
//Popped chorus fruit
if(held.getItem() == Items.POPPED_CHORUS_FRUIT &&
be.isSolid() &&
!be.hasSpentPoppedChorus() &&
ext.canRemoveCollision(state, world, pos)
) {
be.setSolidity(false);
be.spentPoppedChorus();
if(!player.isCreative()) held.decrement(1);
world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f);
return ActionResult.SUCCESS;
}
//Changing the theme
if(held.getItem() instanceof BlockItem bi && be.getThemeState().getBlock() == Blocks.AIR) {
Block block = bi.getBlock();
ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit));
BlockState placementState = block.getPlacementState(ctx);
if(placementState != null && Block.isShapeFullCube(placementState.getCollisionShape(world, pos)) && !(block instanceof BlockEntityProvider)) {
if(!world.isClient) be.setRenderedState(placementState);
world.setBlockState(pos, state.with(LIGHT, be.hasSpentGlowstoneDust() || (placementState.getLuminance() != 0)));
be.setEmitsRedstone(be.hasSpentRedstoneTorch() || placementState.getWeakRedstonePower(world, pos, Direction.NORTH) != 0);
if(!player.isCreative()) held.decrement(1);
world.playSound(player, pos, placementState.getSoundGroup().getPlaceSound(), SoundCategory.BLOCKS, 1f, 1.1f);
return ActionResult.SUCCESS;
}
}
return ActionResult.PASS;
}
//Maybe an odd spot to put this logic but it's consistent w/ vanilla chests, barrels, etc
public static void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
if(!state.isOf(newState.getBlock()) &&
world.getBlockEntity(pos) instanceof ReFramedEntity frame &&
world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
) {
DefaultedList<ItemStack> drops = DefaultedList.of();
//TODO: remember the specific ItemStack
Block theme = frame.getThemeState().getBlock();
if(theme != Blocks.AIR) drops.add(new ItemStack(theme));
if(frame.hasSpentRedstoneTorch()) drops.add(new ItemStack(Items.REDSTONE_TORCH));
if(frame.hasSpentGlowstoneDust()) drops.add(new ItemStack(Items.GLOWSTONE_DUST));
if(frame.hasSpentPoppedChorus()) drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
ItemScatterer.spawn(world, pos, drops);
}
}
public static void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
//Load the BlockEntityTag clientside, which fixes the template briefly showing its default state when placing it.
//I'm surprised this doesn't happen by default; the BlockEntityTag stuff is only done serverside.
if(world.isClient && world.getBlockEntity(pos) instanceof ReFramedEntity be) {
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
if(tag != null) be.readNbt(tag);
}
}
//Returns "null" to signal "no opinion". Imagine it like an InteractionResult.PASS.
public static @Nullable VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
return view.getBlockEntity(pos) instanceof ReFramedEntity be && !be.isSolid() ? VoxelShapes.empty() : null;
}
public static boolean emitsRedstonePower(BlockState state) {
//return state.contains(REDSTONE) ? state.get(REDSTONE) : false;
return false; //TODO, not available after punting this to BlockEntity. Yarn makes this method sound more important than it is, it's just for dust redirection.
}
public static int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
}
public static int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
}
public static int luminance(BlockState state) {
return state.contains(LIGHT) && state.get(LIGHT) ? 15 : 0;
}
}

View File

@@ -1,10 +1,13 @@
package fr.adrien1106.reframed.util;
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity;
import net.minecraft.block.BlockState;
public interface ThemeableBlockEntity extends RenderAttachmentBlockEntity {
default BlockState getThemeState() {
return (BlockState) getRenderAttachmentData();
}
import java.util.List;
public interface ThemeableBlockEntity {
BlockState getTheme(int i);
void setTheme(BlockState state, int i);
List<BlockState> getThemes();
}

View File

@@ -5,7 +5,7 @@ import net.minecraft.util.math.Direction;
import java.util.Arrays;
public enum StairDirection implements StringIdentifiable {
public enum Corner implements StringIdentifiable {
NORTH_DOWN("north_down", Direction.NORTH, Direction.DOWN, Direction.EAST, 0),
DOWN_SOUTH("down_south", Direction.DOWN, Direction.SOUTH, Direction.EAST, 1),
SOUTH_UP("south_up", Direction.SOUTH, Direction.UP, Direction.EAST, 2),
@@ -26,7 +26,7 @@ public enum StairDirection implements StringIdentifiable {
private final Direction left_direction;
private final int ID;
StairDirection(String name, Direction first_direction, Direction second_direction, Direction right_direction, int id) {
Corner(String name, Direction first_direction, Direction second_direction, Direction right_direction, int id) {
this.name = name;
this.first_direction = first_direction;
this.second_direction = second_direction;
@@ -66,21 +66,21 @@ public enum StairDirection implements StringIdentifiable {
return this.ID;
}
public static StairDirection getByDirections(Direction direction_1, Direction direction_2) {
return Arrays.stream(StairDirection.values())
public static Corner getByDirections(Direction direction_1, Direction direction_2) {
return Arrays.stream(Corner.values())
.filter(value -> value.hasDirection(direction_1) && value.hasDirection(direction_2))
.findFirst().orElse(StairDirection.NORTH_DOWN);
.findFirst().orElse(Corner.NORTH_DOWN);
}
public static StairDirection fromId(int id) {
return Arrays.stream(StairDirection.values())
public static Corner fromId(int id) {
return Arrays.stream(Corner.values())
.filter(value -> value.getID() == id)
.findFirst().orElse(StairDirection.NORTH_DOWN);
.findFirst().orElse(Corner.NORTH_DOWN);
}
public static StairDirection fromName(String name) {
return Arrays.stream(StairDirection.values())
public static Corner fromName(String name) {
return Arrays.stream(Corner.values())
.filter(value -> value.name().equals(name))
.findFirst().orElse(StairDirection.NORTH_DOWN);
.findFirst().orElse(Corner.NORTH_DOWN);
}
}

View File

@@ -1,118 +0,0 @@
{
"variants": {
"face=ceiling,facing=east,powered=false": {
"model": "reframed:button_special",
"x": 180,
"y": 270
},
"face=ceiling,facing=east,powered=true": {
"model": "reframed:button_pressed_special",
"x": 180,
"y": 270
},
"face=ceiling,facing=north,powered=false": {
"model": "reframed:button_special",
"x": 180,
"y": 180
},
"face=ceiling,facing=north,powered=true": {
"model": "reframed:button_pressed_special",
"x": 180,
"y": 180
},
"face=ceiling,facing=south,powered=false": {
"model": "reframed:button_special",
"x": 180
},
"face=ceiling,facing=south,powered=true": {
"model": "reframed:button_pressed_special",
"x": 180
},
"face=ceiling,facing=west,powered=false": {
"model": "reframed:button_special",
"x": 180,
"y": 90
},
"face=ceiling,facing=west,powered=true": {
"model": "reframed:button_pressed_special",
"x": 180,
"y": 90
},
"face=floor,facing=east,powered=false": {
"model": "reframed:button_special",
"y": 90
},
"face=floor,facing=east,powered=true": {
"model": "reframed:button_pressed_special",
"y": 90
},
"face=floor,facing=north,powered=false": {
"model": "reframed:button_special"
},
"face=floor,facing=north,powered=true": {
"model": "reframed:button_pressed_special"
},
"face=floor,facing=south,powered=false": {
"model": "reframed:button_special",
"y": 180
},
"face=floor,facing=south,powered=true": {
"model": "reframed:button_pressed_special",
"y": 180
},
"face=floor,facing=west,powered=false": {
"model": "reframed:button_special",
"y": 270
},
"face=floor,facing=west,powered=true": {
"model": "reframed:button_pressed_special",
"y": 270
},
"face=wall,facing=east,powered=false": {
"model": "reframed:button_special",
"uvlock": true,
"x": 90,
"y": 90
},
"face=wall,facing=east,powered=true": {
"model": "reframed:button_pressed_special",
"uvlock": true,
"x": 90,
"y": 90
},
"face=wall,facing=north,powered=false": {
"model": "reframed:button_special",
"uvlock": true,
"x": 90
},
"face=wall,facing=north,powered=true": {
"model": "reframed:button_pressed_special",
"uvlock": true,
"x": 90
},
"face=wall,facing=south,powered=false": {
"model": "reframed:button_special",
"uvlock": true,
"x": 90,
"y": 180
},
"face=wall,facing=south,powered=true": {
"model": "reframed:button_pressed_special",
"uvlock": true,
"x": 90,
"y": 180
},
"face=wall,facing=west,powered=false": {
"model": "reframed:button_special",
"uvlock": true,
"x": 90,
"y": 270
},
"face=wall,facing=west,powered=true": {
"model": "reframed:button_pressed_special",
"uvlock": true,
"x": 90,
"y": 270
}
}
}

View File

@@ -1,16 +0,0 @@
{
"variants": {
"candles=1": {
"model": "reframed:one_candle_special"
},
"candles=2": {
"model": "reframed:two_candles_special"
},
"candles=3": {
"model": "reframed:three_candles_special"
},
"candles=4": {
"model": "reframed:four_candles_special"
}
}
}

View File

@@ -1,7 +0,0 @@
{
"variants": {
"": {
"model": "reframed:carpet_special"
}
}
}

View File

@@ -1,7 +0,0 @@
{
"variants": {
"": {
"model": "reframed:cube_special"
}
}
}

View File

@@ -1,124 +0,0 @@
{
"variants": {
"facing=east,half=lower,hinge=left,open=false": {
"model": "reframed:door_bottom_left_special"
},
"facing=east,half=lower,hinge=left,open=true": {
"model": "reframed:door_bottom_left_open_special",
"y": 90
},
"facing=east,half=lower,hinge=right,open=false": {
"model": "reframed:door_bottom_right_special"
},
"facing=east,half=lower,hinge=right,open=true": {
"model": "reframed:door_bottom_right_open_special",
"y": 270
},
"facing=east,half=upper,hinge=left,open=false": {
"model": "reframed:door_top_left_special"
},
"facing=east,half=upper,hinge=left,open=true": {
"model": "reframed:door_top_left_open_special",
"y": 90
},
"facing=east,half=upper,hinge=right,open=false": {
"model": "reframed:door_top_right_special"
},
"facing=east,half=upper,hinge=right,open=true": {
"model": "reframed:door_top_right_open_special",
"y": 270
},
"facing=north,half=lower,hinge=left,open=false": {
"model": "reframed:door_bottom_left_special",
"y": 270
},
"facing=north,half=lower,hinge=left,open=true": {
"model": "reframed:door_bottom_left_open_special"
},
"facing=north,half=lower,hinge=right,open=false": {
"model": "reframed:door_bottom_right_special",
"y": 270
},
"facing=north,half=lower,hinge=right,open=true": {
"model": "reframed:door_bottom_right_open_special",
"y": 180
},
"facing=north,half=upper,hinge=left,open=false": {
"model": "reframed:door_top_left_special",
"y": 270
},
"facing=north,half=upper,hinge=left,open=true": {
"model": "reframed:door_top_left_open_special"
},
"facing=north,half=upper,hinge=right,open=false": {
"model": "reframed:door_top_right_special",
"y": 270
},
"facing=north,half=upper,hinge=right,open=true": {
"model": "reframed:door_top_right_open_special",
"y": 180
},
"facing=south,half=lower,hinge=left,open=false": {
"model": "reframed:door_bottom_left_special",
"y": 90
},
"facing=south,half=lower,hinge=left,open=true": {
"model": "reframed:door_bottom_left_open_special",
"y": 180
},
"facing=south,half=lower,hinge=right,open=false": {
"model": "reframed:door_bottom_right_special",
"y": 90
},
"facing=south,half=lower,hinge=right,open=true": {
"model": "reframed:door_bottom_right_open_special"
},
"facing=south,half=upper,hinge=left,open=false": {
"model": "reframed:door_top_left_special",
"y": 90
},
"facing=south,half=upper,hinge=left,open=true": {
"model": "reframed:door_top_left_open_special",
"y": 180
},
"facing=south,half=upper,hinge=right,open=false": {
"model": "reframed:door_top_right_special",
"y": 90
},
"facing=south,half=upper,hinge=right,open=true": {
"model": "reframed:door_top_right_open_special"
},
"facing=west,half=lower,hinge=left,open=false": {
"model": "reframed:door_bottom_left_special",
"y": 180
},
"facing=west,half=lower,hinge=left,open=true": {
"model": "reframed:door_bottom_left_open_special",
"y": 270
},
"facing=west,half=lower,hinge=right,open=false": {
"model": "reframed:door_bottom_right_special",
"y": 180
},
"facing=west,half=lower,hinge=right,open=true": {
"model": "reframed:door_bottom_right_open_special",
"y": 90
},
"facing=west,half=upper,hinge=left,open=false": {
"model": "reframed:door_top_left_special",
"y": 180
},
"facing=west,half=upper,hinge=left,open=true": {
"model": "reframed:door_top_left_open_special",
"y": 270
},
"facing=west,half=upper,hinge=right,open=false": {
"model": "reframed:door_top_right_special",
"y": 180
},
"facing=west,half=upper,hinge=right,open=true": {
"model": "reframed:door_top_right_open_special",
"y": 90
}
}
}

View File

@@ -1,48 +0,0 @@
{
"multipart": [
{
"apply": {
"model": "reframed:fence_post_special"
}
},
{
"apply": {
"model": "reframed:fence_side_special",
"uvlock": true
},
"when": {
"north": "true"
}
},
{
"apply": {
"model": "reframed:fence_side_special",
"uvlock": true,
"y": 90
},
"when": {
"east": "true"
}
},
{
"apply": {
"model": "reframed:fence_side_special",
"uvlock": true,
"y": 180
},
"when": {
"south": "true"
}
},
{
"apply": {
"model": "reframed:fence_side_special",
"uvlock": true,
"y": 270
},
"when": {
"west": "true"
}
}
]
}

View File

@@ -1,80 +0,0 @@
{
"variants": {
"facing=east,in_wall=false,open=false": {
"model": "reframed:fence_gate_special",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=false,open=true": {
"model": "reframed:fence_gate_open_special",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=true,open=false": {
"model": "reframed:fence_gate_wall_special",
"uvlock": true,
"y": 270
},
"facing=east,in_wall=true,open=true": {
"model": "reframed:fence_gate_wall_open_special",
"uvlock": true,
"y": 270
},
"facing=north,in_wall=false,open=false": {
"model": "reframed:fence_gate_special",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=false,open=true": {
"model": "reframed:fence_gate_open_special",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=true,open=false": {
"model": "reframed:fence_gate_wall_special",
"uvlock": true,
"y": 180
},
"facing=north,in_wall=true,open=true": {
"model": "reframed:fence_gate_wall_open_special",
"uvlock": true,
"y": 180
},
"facing=south,in_wall=false,open=false": {
"model": "reframed:fence_gate_special",
"uvlock": true
},
"facing=south,in_wall=false,open=true": {
"model": "reframed:fence_gate_open_special",
"uvlock": true
},
"facing=south,in_wall=true,open=false": {
"model": "reframed:fence_gate_wall_special",
"uvlock": true
},
"facing=south,in_wall=true,open=true": {
"model": "reframed:fence_gate_wall_open_special",
"uvlock": true
},
"facing=west,in_wall=false,open=false": {
"model": "reframed:fence_gate_special",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=false,open=true": {
"model": "reframed:fence_gate_open_special",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=true,open=false": {
"model": "reframed:fence_gate_wall_special",
"uvlock": true,
"y": 90
},
"facing=west,in_wall=true,open=true": {
"model": "reframed:fence_gate_wall_open_special",
"uvlock": true,
"y": 90
}
}
}

View File

@@ -1,124 +0,0 @@
{
"variants": {
"facing=east,half=lower,hinge=left,open=false": {
"model": "reframed:door_bottom_left_special"
},
"facing=east,half=lower,hinge=left,open=true": {
"model": "reframed:door_bottom_left_open_special",
"y": 90
},
"facing=east,half=lower,hinge=right,open=false": {
"model": "reframed:door_bottom_right_special"
},
"facing=east,half=lower,hinge=right,open=true": {
"model": "reframed:door_bottom_right_open_special",
"y": 270
},
"facing=east,half=upper,hinge=left,open=false": {
"model": "reframed:door_top_left_special"
},
"facing=east,half=upper,hinge=left,open=true": {
"model": "reframed:door_top_left_open_special",
"y": 90
},
"facing=east,half=upper,hinge=right,open=false": {
"model": "reframed:door_top_right_special"
},
"facing=east,half=upper,hinge=right,open=true": {
"model": "reframed:door_top_right_open_special",
"y": 270
},
"facing=north,half=lower,hinge=left,open=false": {
"model": "reframed:door_bottom_left_special",
"y": 270
},
"facing=north,half=lower,hinge=left,open=true": {
"model": "reframed:door_bottom_left_open_special"
},
"facing=north,half=lower,hinge=right,open=false": {
"model": "reframed:door_bottom_right_special",
"y": 270
},
"facing=north,half=lower,hinge=right,open=true": {
"model": "reframed:door_bottom_right_open_special",
"y": 180
},
"facing=north,half=upper,hinge=left,open=false": {
"model": "reframed:door_top_left_special",
"y": 270
},
"facing=north,half=upper,hinge=left,open=true": {
"model": "reframed:door_top_left_open_special"
},
"facing=north,half=upper,hinge=right,open=false": {
"model": "reframed:door_top_right_special",
"y": 270
},
"facing=north,half=upper,hinge=right,open=true": {
"model": "reframed:door_top_right_open_special",
"y": 180
},
"facing=south,half=lower,hinge=left,open=false": {
"model": "reframed:door_bottom_left_special",
"y": 90
},
"facing=south,half=lower,hinge=left,open=true": {
"model": "reframed:door_bottom_left_open_special",
"y": 180
},
"facing=south,half=lower,hinge=right,open=false": {
"model": "reframed:door_bottom_right_special",
"y": 90
},
"facing=south,half=lower,hinge=right,open=true": {
"model": "reframed:door_bottom_right_open_special"
},
"facing=south,half=upper,hinge=left,open=false": {
"model": "reframed:door_top_left_special",
"y": 90
},
"facing=south,half=upper,hinge=left,open=true": {
"model": "reframed:door_top_left_open_special",
"y": 180
},
"facing=south,half=upper,hinge=right,open=false": {
"model": "reframed:door_top_right_special",
"y": 90
},
"facing=south,half=upper,hinge=right,open=true": {
"model": "reframed:door_top_right_open_special"
},
"facing=west,half=lower,hinge=left,open=false": {
"model": "reframed:door_bottom_left_special",
"y": 180
},
"facing=west,half=lower,hinge=left,open=true": {
"model": "reframed:door_bottom_left_open_special",
"y": 270
},
"facing=west,half=lower,hinge=right,open=false": {
"model": "reframed:door_bottom_right_special",
"y": 180
},
"facing=west,half=lower,hinge=right,open=true": {
"model": "reframed:door_bottom_right_open_special",
"y": 90
},
"facing=west,half=upper,hinge=left,open=false": {
"model": "reframed:door_top_left_special",
"y": 180
},
"facing=west,half=upper,hinge=left,open=true": {
"model": "reframed:door_top_left_open_special",
"y": 270
},
"facing=west,half=upper,hinge=right,open=false": {
"model": "reframed:door_top_right_special",
"y": 180
},
"facing=west,half=upper,hinge=right,open=true": {
"model": "reframed:door_top_right_open_special",
"y": 90
}
}
}

View File

@@ -1,69 +0,0 @@
{
"variants": {
"facing=east,half=bottom,open=false": {
"model": "reframed:trapdoor_bottom_special",
"y": 90
},
"facing=east,half=bottom,open=true": {
"model": "reframed:trapdoor_open_special",
"y": 90
},
"facing=east,half=top,open=false": {
"model": "reframed:trapdoor_top_special",
"y": 90
},
"facing=east,half=top,open=true": {
"model": "reframed:trapdoor_open_special",
"x": 180,
"y": 270
},
"facing=north,half=bottom,open=false": {
"model": "reframed:trapdoor_bottom_special"
},
"facing=north,half=bottom,open=true": {
"model": "reframed:trapdoor_open_special"
},
"facing=north,half=top,open=false": {
"model": "reframed:trapdoor_top_special"
},
"facing=north,half=top,open=true": {
"model": "reframed:trapdoor_open_special",
"x": 180,
"y": 180
},
"facing=south,half=bottom,open=false": {
"model": "reframed:trapdoor_bottom_special",
"y": 180
},
"facing=south,half=bottom,open=true": {
"model": "reframed:trapdoor_open_special",
"y": 180
},
"facing=south,half=top,open=false": {
"model": "reframed:trapdoor_top_special",
"y": 180
},
"facing=south,half=top,open=true": {
"model": "reframed:trapdoor_open_special",
"x": 180,
"y": 0
},
"facing=west,half=bottom,open=false": {
"model": "reframed:trapdoor_bottom_special",
"y": 270
},
"facing=west,half=bottom,open=true": {
"model": "reframed:trapdoor_open_special",
"y": 270
},
"facing=west,half=top,open=false": {
"model": "reframed:trapdoor_top_special",
"y": 270
},
"facing=west,half=top,open=true": {
"model": "reframed:trapdoor_open_special",
"x": 180,
"y": 90
}
}
}

View File

@@ -1,110 +0,0 @@
{
"variants": {
"face=ceiling,facing=east,powered=false": {
"model": "reframed:lever_on_special",
"x": 180,
"y": 270
},
"face=ceiling,facing=east,powered=true": {
"model": "reframed:lever_special",
"x": 180,
"y": 270
},
"face=ceiling,facing=north,powered=false": {
"model": "reframed:lever_on_special",
"x": 180,
"y": 180
},
"face=ceiling,facing=north,powered=true": {
"model": "reframed:lever_special",
"x": 180,
"y": 180
},
"face=ceiling,facing=south,powered=false": {
"model": "reframed:lever_on_special",
"x": 180
},
"face=ceiling,facing=south,powered=true": {
"model": "reframed:lever_special",
"x": 180
},
"face=ceiling,facing=west,powered=false": {
"model": "reframed:lever_on_special",
"x": 180,
"y": 90
},
"face=ceiling,facing=west,powered=true": {
"model": "reframed:lever_special",
"x": 180,
"y": 90
},
"face=floor,facing=east,powered=false": {
"model": "reframed:lever_on_special",
"y": 90
},
"face=floor,facing=east,powered=true": {
"model": "reframed:lever_special",
"y": 90
},
"face=floor,facing=north,powered=false": {
"model": "reframed:lever_on_special"
},
"face=floor,facing=north,powered=true": {
"model": "reframed:lever_special"
},
"face=floor,facing=south,powered=false": {
"model": "reframed:lever_on_special",
"y": 180
},
"face=floor,facing=south,powered=true": {
"model": "reframed:lever_special",
"y": 180
},
"face=floor,facing=west,powered=false": {
"model": "reframed:lever_on_special",
"y": 270
},
"face=floor,facing=west,powered=true": {
"model": "reframed:lever_special",
"y": 270
},
"face=wall,facing=east,powered=false": {
"model": "reframed:lever_on_special",
"x": 90,
"y": 90
},
"face=wall,facing=east,powered=true": {
"model": "reframed:lever_special",
"x": 90,
"y": 90
},
"face=wall,facing=north,powered=false": {
"model": "reframed:lever_on_special",
"x": 90
},
"face=wall,facing=north,powered=true": {
"model": "reframed:lever_special",
"x": 90
},
"face=wall,facing=south,powered=false": {
"model": "reframed:lever_on_special",
"x": 90,
"y": 180
},
"face=wall,facing=south,powered=true": {
"model": "reframed:lever_special",
"x": 90,
"y": 180
},
"face=wall,facing=west,powered=false": {
"model": "reframed:lever_on_special",
"x": 90,
"y": 270
},
"face=wall,facing=west,powered=true": {
"model": "reframed:lever_special",
"x": 90,
"y": 270
}
}
}

View File

@@ -1,77 +0,0 @@
{
"multipart": [
{
"apply": {
"model": "reframed:glass_pane_post_special"
}
},
{
"apply": {
"model": "reframed:glass_pane_side_special"
},
"when": {
"north": "true"
}
},
{
"apply": {
"model": "reframed:glass_pane_side_special",
"y": 90
},
"when": {
"east": "true"
}
},
{
"apply": {
"model": "reframed:glass_pane_side_alt_special"
},
"when": {
"south": "true"
}
},
{
"apply": {
"model": "reframed:glass_pane_side_alt_special",
"y": 90
},
"when": {
"west": "true"
}
},
{
"apply": {
"model": "reframed:glass_pane_noside_special"
},
"when": {
"north": "false"
}
},
{
"apply": {
"model": "reframed:glass_pane_noside_alt_special"
},
"when": {
"east": "false"
}
},
{
"apply": {
"model": "reframed:glass_pane_noside_alt_special",
"y": 90
},
"when": {
"south": "false"
}
},
{
"apply": {
"model": "reframed:glass_pane_noside_special",
"y": 270
},
"when": {
"west": "false"
}
}
]
}

View File

@@ -1,19 +0,0 @@
{
"variants": {
"axis=x": {
"model": "reframed:fence_post_special",
"x": 90,
"y": 90,
"uvlock": true
},
"axis=y": {
"model": "reframed:fence_post_special",
"uvlock": true
},
"axis=z": {
"model": "reframed:fence_post_special",
"uvlock": true,
"x": 90
}
}
}

View File

@@ -1,10 +0,0 @@
{
"variants": {
"powered=false": {
"model": "reframed:pressure_plate_up_special"
},
"powered=true": {
"model": "reframed:pressure_plate_down_special"
}
}
}

View File

@@ -1,65 +0,0 @@
{
"multipart": [
{
"apply": {
"model": "reframed:slab_special",
"uvlock": true
},
"when": {
"facing": "down"
}
},
{
"apply": {
"model": "reframed:slab_special",
"uvlock": true,
"x": 180
},
"when": {
"facing": "up"
}
},
{
"apply": {
"model": "reframed:slab_special",
"uvlock": true,
"x": 270
},
"when": {
"facing": "north"
}
},
{
"apply": {
"model": "reframed:slab_special",
"uvlock": true,
"x": 90
},
"when": {
"facing": "south"
}
},
{
"apply": {
"model": "reframed:slab_special",
"uvlock": true,
"x": 90,
"y": 90
},
"when": {
"facing": "west"
}
},
{
"apply": {
"model": "reframed:slab_special",
"uvlock": true,
"x": 90,
"y": 270
},
"when": {
"facing": "east"
}
}
]
}

View File

@@ -1,69 +0,0 @@
{
"variants": {
"facing=east,half=bottom,open=false": {
"model": "reframed:trapdoor_bottom_special",
"y": 90
},
"facing=east,half=bottom,open=true": {
"model": "reframed:trapdoor_open_special",
"y": 90
},
"facing=east,half=top,open=false": {
"model": "reframed:trapdoor_top_special",
"y": 90
},
"facing=east,half=top,open=true": {
"model": "reframed:trapdoor_open_special",
"x": 180,
"y": 270
},
"facing=north,half=bottom,open=false": {
"model": "reframed:trapdoor_bottom_special"
},
"facing=north,half=bottom,open=true": {
"model": "reframed:trapdoor_open_special"
},
"facing=north,half=top,open=false": {
"model": "reframed:trapdoor_top_special"
},
"facing=north,half=top,open=true": {
"model": "reframed:trapdoor_open_special",
"x": 180,
"y": 180
},
"facing=south,half=bottom,open=false": {
"model": "reframed:trapdoor_bottom_special",
"y": 180
},
"facing=south,half=bottom,open=true": {
"model": "reframed:trapdoor_open_special",
"y": 180
},
"facing=south,half=top,open=false": {
"model": "reframed:trapdoor_top_special",
"y": 180
},
"facing=south,half=top,open=true": {
"model": "reframed:trapdoor_open_special",
"x": 180,
"y": 0
},
"facing=west,half=bottom,open=false": {
"model": "reframed:trapdoor_bottom_special",
"y": 270
},
"facing=west,half=bottom,open=true": {
"model": "reframed:trapdoor_open_special",
"y": 270
},
"facing=west,half=top,open=false": {
"model": "reframed:trapdoor_top_special",
"y": 270
},
"facing=west,half=top,open=true": {
"model": "reframed:trapdoor_open_special",
"x": 180,
"y": 90
}
}
}

View File

@@ -1,90 +0,0 @@
{
"multipart": [
{
"apply": {
"model": "reframed:wall_post_special"
},
"when": {
"up": "true"
}
},
{
"apply": {
"model": "reframed:wall_side_special",
"uvlock": true
},
"when": {
"north": "low"
}
},
{
"apply": {
"model": "reframed:wall_side_special",
"uvlock": true,
"y": 90
},
"when": {
"east": "low"
}
},
{
"apply": {
"model": "reframed:wall_side_special",
"uvlock": true,
"y": 180
},
"when": {
"south": "low"
}
},
{
"apply": {
"model": "reframed:wall_side_special",
"uvlock": true,
"y": 270
},
"when": {
"west": "low"
}
},
{
"apply": {
"model": "reframed:wall_side_tall_special",
"uvlock": true
},
"when": {
"north": "tall"
}
},
{
"apply": {
"model": "reframed:wall_side_tall_special",
"uvlock": true,
"y": 90
},
"when": {
"east": "tall"
}
},
{
"apply": {
"model": "reframed:wall_side_tall_special",
"uvlock": true,
"y": 180
},
"when": {
"south": "tall"
}
},
{
"apply": {
"model": "reframed:wall_side_tall_special",
"uvlock": true,
"y": 270
},
"when": {
"west": "tall"
}
}
]
}

View File

@@ -1,22 +0,0 @@
{
"itemGroup.reframed.tab": "Frames",
"block.reframed.button": "Button Frame",
"block.reframed.candle": "Candle Frame",
"block.reframed.carpet": "Carpet Frame",
"block.reframed.cube": "Cube Frame",
"block.reframed.door": "Door Frame",
"block.reframed.fence": "Fence Frame",
"block.reframed.fence_gate": "Fence Gate Frame",
"block.reframed.iron_door": "Iron Door Frame",
"block.reframed.iron_trapdoor": "Iron Trapdoor Frame",
"block.reframed.lever": "Lever Frame",
"block.reframed.pane": "Pane Frame",
"block.reframed.post": "Post Frame",
"block.reframed.pressure_plate": "Pressure Plate Frame",
"block.reframed.slab": "Slab Frame",
"block.reframed.stairs": "Stairs Frame",
"block.reframed.trapdoor": "Trapdoor Frame",
"block.reframed.wall": "Wall Frame"
}

View File

@@ -0,0 +1,51 @@
{
"credit": "Made with Blockbench",
"parent": "minecraft:block/block",
"textures": {
"particle": "#side"
},
"elements": [
{
"from": [8, 8, 0],
"to": [16, 16, 8],
"faces": {
"north": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "north"},
"east": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "east"},
"south": {"uv": [8, 0, 16, 8], "texture": "#side"},
"up": {"uv": [8, 0, 16, 8], "texture": "#top", "cullface": "up"},
"down": {"uv": [8, 8, 16, 16], "texture": "#bottom"}
}
},
{
"from": [0, 0, 0],
"to": [8, 8, 8],
"faces": {
"north": {"uv": [8, 8, 16, 16], "texture": "#side", "cullface": "north"},
"east": {"uv": [8, 8, 16, 16], "texture": "#side"},
"south": {"uv": [0, 8, 8, 16], "texture": "#side"},
"west": {"uv": [0, 8, 8, 16], "texture": "#side", "cullface": "west"},
"down": {"uv": [8, 0, 16, 8], "texture": "#bottom", "cullface": "down"}
}
},
{
"from": [0, 8, 0],
"to": [8, 16, 8],
"faces": {
"north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"},
"west": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "west"},
"up": {"uv": [0, 0, 8, 8], "texture": "#top", "cullface": "up"}
}
},
{
"from": [0, 8, 8],
"to": [8, 16, 16],
"faces": {
"east": {"uv": [0, 0, 8, 8], "texture": "#side"},
"south": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "south"},
"west": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "west"},
"up": {"uv": [0, 8, 8, 16], "texture": "#top", "cullface": "up"},
"down": {"uv": [0, 0, 8, 8], "texture": "#bottom"}
}
}
]
}

View File

@@ -1,41 +0,0 @@
{
"parent": "minecraft:block/block",
"textures": {
"particle": "#texture"
},
"elements": [
{
"from": [6, 0, 6],
"to": [10, 16, 10],
"faces": {
"down": {
"uv": [6, 6, 10, 10],
"texture": "#texture",
"cullface": "down"
},
"up": {
"uv": [6, 6, 10, 10],
"texture": "#texture",
"cullface": "up"
},
"north": {
"uv": [6, 0, 10, 16],
"texture": "#texture"
},
"south": {
"uv": [6, 0, 10, 16],
"texture": "#texture"
},
"west": {
"uv": [6, 0, 10, 16],
"texture": "#texture"
},
"east": {
"uv": [6, 0, 10, 16],
"texture": "#texture"
}
},
"__comment": "Center post"
}
]
}

View File

@@ -1,60 +0,0 @@
{
"elements": [
{
"from": [7, 12, 0],
"to": [9, 15, 9],
"faces": {
"down": {
"uv": [7, 0, 9, 9],
"texture": "#down"
},
"up": {
"uv": [7, 0, 9, 9],
"texture": "#up"
},
"north": {
"uv": [7, 1, 9, 4],
"texture": "#north",
"cullface": "north"
},
"west": {
"uv": [0, 1, 9, 4],
"texture": "#west"
},
"east": {
"uv": [0, 1, 9, 4],
"texture": "#east"
}
},
"__comment": "top bar"
},
{
"from": [7, 6, 0],
"to": [9, 9, 9],
"faces": {
"down": {
"uv": [7, 0, 9, 9],
"texture": "#down"
},
"up": {
"uv": [7, 0, 9, 9],
"texture": "#up"
},
"north": {
"uv": [7, 7, 9, 10],
"texture": "#north",
"cullface": "north"
},
"west": {
"uv": [0, 7, 9, 10],
"texture": "#west"
},
"east": {
"uv": [0, 7, 9, 10],
"texture": "#east"
}
},
"__comment": "lower bar"
}
]
}

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