Compare commits
No commits in common. "28ae110e14ac4ace293bd341eb328f169efd0aab" and "6dc0cf7fdf9746b765f1ec51e2e4673945063264" have entirely different histories.
28ae110e14
...
6dc0cf7fdf
55
CHANGELOG.md
Normal file
55
CHANGELOG.md
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
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
|
@ -94,7 +94,7 @@ dependencies {
|
|||||||
|
|
||||||
// Chipped to test athena implementation
|
// Chipped to test athena implementation
|
||||||
modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7"
|
modRuntimeOnly "com.teamresourceful.resourcefullib:resourcefullib-fabric-${project.minecraft_version}:2.4.7"
|
||||||
modRuntimeOnly "earth.terrarium.chipped:Chipped-fabric-${project.minecraft_version}:3.1.2"
|
// modRuntimeOnly "earth.terrarium.chipped:chipped-fabric-${project.minecraft_version}:3.1.2" TODO not working for some reasons
|
||||||
|
|
||||||
// Fabric API. This is technically optional, but you probably want it anyway.
|
// Fabric API. This is technically optional, but you probably want it anyway.
|
||||||
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||||
|
@ -8,7 +8,7 @@ yarn_mappings=1.20.4+build.3
|
|||||||
loader_version=0.15.6
|
loader_version=0.15.6
|
||||||
|
|
||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version = 1.3
|
mod_version = 1.2
|
||||||
maven_group = fr.adrien1106
|
maven_group = fr.adrien1106
|
||||||
archives_base_name = ReFramed
|
archives_base_name = ReFramed
|
||||||
mod_id = reframed
|
mod_id = reframed
|
||||||
|
@ -25,16 +25,15 @@ import java.util.function.BiConsumer;
|
|||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO self culling, fix other models, better connected textures
|
* TODO multiple camos
|
||||||
*/
|
*/
|
||||||
public class ReFramed implements ModInitializer {
|
public class ReFramed implements ModInitializer {
|
||||||
public static final String MODID = "reframed";
|
public static final String MODID = "reframed";
|
||||||
|
|
||||||
public static final ArrayList<Block> BLOCKS = new ArrayList<>();
|
public static final ArrayList<Block> BLOCKS = new ArrayList<>();
|
||||||
public static Block CUBE, STAIRS, DOUBLE_STAIRS, SLAB, DOUBLE_SLAB, POST, FENCE, FENCE_GATE, DOOR, TRAPDOOR, IRON_DOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE, CANDLE;
|
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 BlockEntityType<ReFramedEntity> REFRAMED_BLOCK_ENTITY;
|
public static BlockEntityType<ReFramedEntity> REFRAMED_BLOCK_ENTITY;
|
||||||
public static BlockEntityType<ReFramedDoubleEntity> REFRAMED_DOUBLE_BLOCK_ENTITY;
|
|
||||||
|
|
||||||
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
|
public static BiConsumer<World, BlockPos> chunkRerenderProxy = (world, pos) -> {};
|
||||||
|
|
||||||
@ -47,9 +46,7 @@ public class ReFramed implements ModInitializer {
|
|||||||
//and button, because they're redstoney... hopefully this ordering makes sense lol
|
//and button, because they're redstoney... hopefully this ordering makes sense lol
|
||||||
CUBE = registerReFramed("cube" , new ReFramedBlock(ReFramedInteractionUtil.makeSettings()));
|
CUBE = registerReFramed("cube" , new ReFramedBlock(ReFramedInteractionUtil.makeSettings()));
|
||||||
STAIRS = registerReFramed("stairs" , new ReFramedStairsBlock(cp(Blocks.OAK_STAIRS)));
|
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)));
|
SLAB = registerReFramed("slab" , new ReFramedSlabBlock(cp(Blocks.OAK_SLAB)));
|
||||||
DOUBLE_SLAB = registerReFramed("double_slab" , new ReFramedDoubleSlabBlock(cp(Blocks.OAK_SLAB)));
|
|
||||||
POST = registerReFramed("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE)));
|
POST = registerReFramed("post" , new ReFramedPostBlock(cp(Blocks.OAK_FENCE)));
|
||||||
FENCE = registerReFramed("fence" , new ReFramedFenceBlock(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)));
|
FENCE_GATE = registerReFramed("fence_gate" , new ReFramedFenceGateBlock(cp(Blocks.OAK_FENCE_GATE)));
|
||||||
@ -66,19 +63,7 @@ public class ReFramed implements ModInitializer {
|
|||||||
CANDLE = registerReFramed("candle" , new ReFramedCandleBlock(ReFramedCandleBlock.configureSettings(cp(Blocks.CANDLE))));
|
CANDLE = registerReFramed("candle" , new ReFramedCandleBlock(ReFramedCandleBlock.configureSettings(cp(Blocks.CANDLE))));
|
||||||
|
|
||||||
REFRAMED_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("camo"),
|
REFRAMED_BLOCK_ENTITY = Registry.register(Registries.BLOCK_ENTITY_TYPE, id("camo"),
|
||||||
FabricBlockEntityTypeBuilder.create(
|
FabricBlockEntityTypeBuilder.create((pos, state) -> new ReFramedEntity(REFRAMED_BLOCK_ENTITY, pos, state), BLOCKS.toArray(new Block[0])).build(null)
|
||||||
(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()
|
Registry.register(Registries.ITEM_GROUP, id("tab"), FabricItemGroup.builder()
|
||||||
|
@ -1,44 +1,29 @@
|
|||||||
package fr.adrien1106.reframed.block;
|
package fr.adrien1106.reframed.block;
|
||||||
|
|
||||||
|
import com.google.common.base.MoreObjects;
|
||||||
import fr.adrien1106.reframed.ReFramed;
|
import fr.adrien1106.reframed.ReFramed;
|
||||||
import fr.adrien1106.reframed.util.ReframedInteractible;
|
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
|
||||||
import net.minecraft.block.*;
|
import net.minecraft.block.*;
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.*;
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
import net.minecraft.nbt.NbtCompound;
|
import net.minecraft.item.ItemStack;
|
||||||
import net.minecraft.sound.SoundCategory;
|
|
||||||
import net.minecraft.sound.SoundEvents;
|
|
||||||
import net.minecraft.state.StateManager;
|
import net.minecraft.state.StateManager;
|
||||||
import net.minecraft.state.property.BooleanProperty;
|
|
||||||
import net.minecraft.util.ActionResult;
|
import net.minecraft.util.ActionResult;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.ItemScatterer;
|
|
||||||
import net.minecraft.util.collection.DefaultedList;
|
|
||||||
import net.minecraft.util.function.BooleanBiFunction;
|
|
||||||
import net.minecraft.util.hit.BlockHitResult;
|
import net.minecraft.util.hit.BlockHitResult;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.shape.VoxelShape;
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
import net.minecraft.util.shape.VoxelShapes;
|
|
||||||
import net.minecraft.world.BlockView;
|
import net.minecraft.world.BlockView;
|
||||||
import net.minecraft.world.GameRules;
|
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import static net.minecraft.util.shape.VoxelShapes.*;
|
|
||||||
|
|
||||||
public class ReFramedBlock extends Block implements BlockEntityProvider {
|
public class ReFramedBlock extends Block implements BlockEntityProvider {
|
||||||
|
|
||||||
public static final BooleanProperty LIGHT = BooleanProperty.of("frame_light");
|
|
||||||
|
|
||||||
public ReFramedBlock(Settings settings) {
|
public ReFramedBlock(Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
setDefaultState(getDefaultState().with(LIGHT, false));
|
setDefaultState(ReFramedInteractionUtil.setDefaultStates(getDefaultState()));
|
||||||
}
|
}
|
||||||
|
|
||||||
//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
|
//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
|
||||||
@ -50,262 +35,51 @@ public class ReFramedBlock extends Block implements BlockEntityProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
super.appendProperties(builder.add(LIGHT));
|
super.appendProperties(ReFramedInteractionUtil.appendProperties(builder));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||||
return ReFramedEntity.getNbtLightLevel(super.getPlacementState(ctx), ctx.getStack());
|
return ReFramedInteractionUtil.modifyPlacementState(super.getPlacementState(ctx), ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||||
if (!canUse(world, pos, player)) return superUse(state, world, pos, player, hand, hit);
|
ActionResult r = ReFramedInteractionUtil.onUse(state, world, pos, player, hand, hit);
|
||||||
ActionResult result = useUpgrade(state, world, pos, player, hand);
|
if(!r.isAccepted()) r = super.onUse(state, world, pos, player, hand, hit);
|
||||||
if (result.isAccepted()) return result;
|
return r;
|
||||||
return 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected 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 && 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||||
if(!state.isOf(newState.getBlock()) &&
|
ReFramedInteractionUtil.onStateReplaced(state, world, pos, newState, moved);
|
||||||
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);
|
super.onStateReplaced(state, world, pos, newState, moved);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
public void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
||||||
if(world.isClient && world.getBlockEntity(pos) instanceof ReFramedEntity be) {
|
ReFramedInteractionUtil.onPlaced(world, pos, state, placer, stack);
|
||||||
NbtCompound tag = BlockItem.getBlockEntityNbt(stack);
|
|
||||||
if(tag != null) be.readNbt(tag);
|
|
||||||
}
|
|
||||||
super.onPlaced(world, pos, state, placer, stack);
|
super.onPlaced(world, pos, state, placer, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
||||||
return isGhost(view, pos)
|
return MoreObjects.firstNonNull(ReFramedInteractionUtil.getCollisionShape(state, view, pos, ctx), super.getCollisionShape(state, view, pos, ctx));
|
||||||
? VoxelShapes.empty()
|
|
||||||
: super.getCollisionShape(state, view, pos, ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VoxelShape getCullingShape(BlockState state, BlockView view, BlockPos pos) {
|
public boolean emitsRedstonePower(BlockState state) {
|
||||||
return isGhost(view, pos)
|
return ReFramedInteractionUtil.emitsRedstonePower(state);
|
||||||
? 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
|
@Override
|
||||||
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
public int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||||
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
|
return ReFramedInteractionUtil.getWeakRedstonePower(state, view, pos, dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
public int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||||
return getWeakRedstonePower(state, view, pos, dir);
|
return ReFramedInteractionUtil.getStrongRedstonePower(state, view, pos, dir);
|
||||||
}
|
|
||||||
|
|
||||||
public int getTopThemeIndex(BlockState state) {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
package fr.adrien1106.reframed.block;
|
|
||||||
|
|
||||||
import fr.adrien1106.reframed.ReFramed;
|
|
||||||
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 java.util.Arrays;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
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 getHitPart(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 = hit.getPos();
|
|
||||||
BlockPos origin = hit.getBlockPos();
|
|
||||||
Map<Direction.Axis, Double> axes = Arrays.stream(Direction.Axis.values())
|
|
||||||
.filter(axis -> axis != side.getAxis())
|
|
||||||
.collect(Collectors.toMap(
|
|
||||||
axis -> axis,
|
|
||||||
axis -> axis.choose(pos.getX() - origin.getX(), pos.getY() - origin.getY(), pos.getZ() - origin.getZ()))
|
|
||||||
);
|
|
||||||
|
|
||||||
if (matchesFace(first_shape.getFace(side), axes)) return 1;
|
|
||||||
if (matchesFace(second_shape.getFace(side), axes)) return 2;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean matchesFace(VoxelShape shape, Map<Direction.Axis, Double> axes) {
|
|
||||||
return shape.getBoundingBoxes().stream()
|
|
||||||
.anyMatch(box ->
|
|
||||||
axes.keySet().stream()
|
|
||||||
.map(axis -> box.getMin(axis) <= axes.get(axis) && box.getMax(axis) >= axes.get(axis))
|
|
||||||
.reduce((prev, current) -> prev && current).get()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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, getHitPart(state, hit));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VoxelShape getCollisionShape(BlockState state, BlockView view, BlockPos pos, ShapeContext ctx) {
|
|
||||||
return isGhost(view, pos) ? empty() : fullCube();
|
|
||||||
}
|
|
||||||
|
|
||||||
@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 = useUpgrade(state, world, pos, player, hand);
|
|
||||||
if (result.isAccepted()) return result;
|
|
||||||
return useCamo(state, world, pos, player, hand, hit, getHitPart(state, hit));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package fr.adrien1106.reframed.block;
|
|
||||||
|
|
||||||
import fr.adrien1106.reframed.ReFramed;
|
|
||||||
import fr.adrien1106.reframed.generator.GBlockstate;
|
|
||||||
import fr.adrien1106.reframed.generator.MultipartBlockStateProvider;
|
|
||||||
import net.minecraft.block.Block;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
|
||||||
import net.minecraft.item.ItemPlacementContext;
|
|
||||||
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.*;
|
|
||||||
import static net.minecraft.state.property.Properties.AXIS;
|
|
||||||
|
|
||||||
public class ReFramedDoubleSlabBlock extends ReFramedDoubleBlock implements MultipartBlockStateProvider {
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,187 +0,0 @@
|
|||||||
package fr.adrien1106.reframed.block;
|
|
||||||
|
|
||||||
import fr.adrien1106.reframed.generator.MultipartBlockStateProvider;
|
|
||||||
import fr.adrien1106.reframed.util.property.StairDirection;
|
|
||||||
import fr.adrien1106.reframed.util.property.StairShape;
|
|
||||||
import net.minecraft.block.Block;
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
|
||||||
import net.minecraft.item.ItemPlacementContext;
|
|
||||||
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.property.StairShape.STRAIGHT;
|
|
||||||
|
|
||||||
public class ReFramedDoubleStairsBlock extends ReFramedDoubleBlock implements MultipartBlockStateProvider {
|
|
||||||
|
|
||||||
private static final List<VoxelShape> COMPLEMENT_LIST = new ArrayList<>(52);
|
|
||||||
|
|
||||||
public ReFramedDoubleStairsBlock(Settings settings) {
|
|
||||||
super(settings);
|
|
||||||
setDefaultState(getDefaultState().with(FACING, StairDirection.NORTH_DOWN).with(SHAPE, STRAIGHT));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
|
||||||
super.appendProperties(builder.add(FACING).add(SHAPE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) {
|
|
||||||
return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved)
|
|
||||||
.with(SHAPE, getPlacementShape(this, state.get(FACING), world, pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
|
||||||
return getPlacement(super.getPlacementState(ctx), ctx);
|
|
||||||
}
|
|
||||||
|
|
||||||
@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(SHAPE);
|
|
||||||
StairDirection direction = state.get(FACING);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
VOXEL_LIST.forEach(shape -> COMPLEMENT_LIST.add(VoxelShapes.combineAndSimplify(VoxelShapes.fullCube(), shape, BooleanBiFunction.ONLY_FIRST)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
package fr.adrien1106.reframed.block;
|
package fr.adrien1106.reframed.block;
|
||||||
|
|
||||||
import fr.adrien1106.reframed.ReFramed;
|
import fr.adrien1106.reframed.ReFramed;
|
||||||
|
import fr.adrien1106.reframed.util.ReFramedInteractionUtil;
|
||||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
@ -20,21 +21,25 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
//Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic,
|
//Keeping the weight of this block entity down, both in terms of memory consumption and NBT sync traffic,
|
||||||
//is pretty important since players might place a lot of them. There were tons and tons of these at Blanketcon.
|
//is pretty important since players might place a lot of them. There were tons and tons of these at Blanketcon.
|
||||||
//To that end, most of the state has been crammed into a bitfield.
|
//To that end, most of the state has been crammed into a bitfield.
|
||||||
public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity {
|
public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity {
|
||||||
protected BlockState first_state = Blocks.AIR.getDefaultState();
|
protected BlockState renderedState = Blocks.AIR.getDefaultState();
|
||||||
protected byte bit_field = SOLIDITY_MASK;
|
protected byte bitfield = DEFAULT_BITFIELD;
|
||||||
|
|
||||||
protected static final byte LIGHT_MASK = 0b001;
|
protected static final int SPENT_GLOWSTONE_DUST_MASK = 0b00000001;
|
||||||
protected static final byte REDSTONE_MASK = 0b010;
|
protected static final int SPENT_REDSTONE_TORCH_MASK = 0b00000010;
|
||||||
protected static final byte SOLIDITY_MASK = 0b100;
|
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 String BLOCKSTATE_KEY = "s";
|
||||||
protected static final String BITFIELD_KEY = "b";
|
protected static final String BITFIELD_KEY = "b";
|
||||||
|
|
||||||
@ -43,116 +48,145 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readNbt(NbtCompound nbt) {
|
public void readNbt(NbtCompound tag) {
|
||||||
super.readNbt(nbt);
|
super.readNbt(tag);
|
||||||
|
|
||||||
BlockState rendered_state = first_state; // keep previous state to check if rerender is needed
|
BlockState lastRenderedState = renderedState;
|
||||||
first_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 1));
|
|
||||||
if (nbt.contains(BITFIELD_KEY)) bit_field = nbt.getByte(BITFIELD_KEY);
|
|
||||||
|
|
||||||
// Force a chunk remesh on the client if the displayed blockstate has changed
|
if(tag.contains("BlockState")) { //2.0.4 and earlier
|
||||||
if(world != null && world.isClient && !Objects.equals(rendered_state, first_state)) {
|
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)) {
|
||||||
ReFramed.chunkRerenderProxy.accept(world, pos);
|
ReFramed.chunkRerenderProxy.accept(world, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeNbt(NbtCompound nbt) {
|
public void writeNbt(NbtCompound tag) {
|
||||||
super.writeNbt(nbt);
|
super.writeNbt(tag);
|
||||||
|
|
||||||
if(first_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(first_state));
|
if(renderedState != Blocks.AIR.getDefaultState()) tag.put(BLOCKSTATE_KEY, NbtHelper.fromBlockState(renderedState));
|
||||||
if(bit_field != SOLIDITY_MASK) nbt.putByte(BITFIELD_KEY, bit_field);
|
if(bitfield != DEFAULT_BITFIELD) tag.putByte(BITFIELD_KEY, bitfield);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NotNull BlockState readStateFromItem(ItemStack stack, int state) {
|
public static @NotNull BlockState readStateFromItem(ItemStack stack) {
|
||||||
NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
|
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
|
||||||
if(nbt == null) return Blocks.AIR.getDefaultState();
|
if(blockEntityTag == null) return Blocks.AIR.getDefaultState();
|
||||||
|
|
||||||
//slightly paranoid NBT handling cause you never know what mysteries are afoot with items
|
//slightly paranoid NBT handling cause you never know what mysteries are afoot with items
|
||||||
NbtElement element;
|
NbtElement subElement;
|
||||||
if(nbt.contains(BLOCKSTATE_KEY + state)) element = nbt.get(BLOCKSTATE_KEY + state);
|
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
|
||||||
else return Blocks.AIR.getDefaultState();
|
else return Blocks.AIR.getDefaultState();
|
||||||
|
|
||||||
if(!(element instanceof NbtCompound compound)) return Blocks.AIR.getDefaultState();
|
if(!(subElement instanceof NbtCompound subCompound)) return Blocks.AIR.getDefaultState();
|
||||||
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), compound);
|
|
||||||
|
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), subCompound);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Awkward: usually the BlockState is the source of truth for things like the "emits light" blockstate, but if you
|
//Awkward: usually the BlockState is the source of truth for things like the "emits light" blockstate, but if you
|
||||||
//ctrl-pick a glowing block and place it, it should still be glowing. This is some hacky shit that guesses the value of
|
//ctrl-pick a glowing block and place it, it should still be glowing. This is some hacky shit that guesses the value of
|
||||||
//the LIGHT blockstate based off information in the NBT tag, and also prevents bugginess like "the blockstate is not
|
//the LIGHT blockstate based off information in the NBT tag, and also prevents bugginess like "the blockstate is not
|
||||||
//glowing but the copied NBT thinks glowstone dust was already added, so it refuses to accept more dust"
|
//glowing but the copied NBT thinks glowstone dust was already added, so it refuses to accept more dust"
|
||||||
public static @Nullable BlockState getNbtLightLevel(@Nullable BlockState state, ItemStack stack) {
|
public static @Nullable BlockState weirdNbtLightLevelStuff(@Nullable BlockState state, ItemStack stack) {
|
||||||
if(state == null || stack == null) return state;
|
if(state == null || stack == null) return state;
|
||||||
|
|
||||||
NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
|
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
|
||||||
if(nbt == null) return state;
|
if(blockEntityTag == null) return state;
|
||||||
|
|
||||||
if(state.contains(ReFramedBlock.LIGHT)) {
|
if(state.contains(ReFramedInteractionUtil.LIGHT)) {
|
||||||
state = state.with(ReFramedBlock.LIGHT,
|
state = state.with(ReFramedInteractionUtil.LIGHT,
|
||||||
((nbt.contains(BITFIELD_KEY)
|
blockEntityTag.getBoolean("spentglow") || //2.0.4
|
||||||
? nbt.getByte(BITFIELD_KEY)
|
((blockEntityTag.contains(BITFIELD_KEY) ? blockEntityTag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD) & SPENT_GLOWSTONE_DUST_MASK) != 0 || //2.0.5
|
||||||
: SOLIDITY_MASK)
|
readStateFromItem(stack).getLuminance() != 0 //glowstone dust wasn't manually added, the block just emits light
|
||||||
& LIGHT_MASK) != 0
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//RenderAttachmentBlockEntity impl. Note that ThemeableBlockEntity depends on this returning a BlockState object.
|
||||||
@Override
|
@Override
|
||||||
public BlockState getTheme(int i) {
|
public BlockState getRenderAttachmentData() {
|
||||||
return first_state;
|
return renderedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void setRenderedState(BlockState newState) {
|
||||||
public List<BlockState> getThemes() {
|
if(!Objects.equals(renderedState, newState)) {
|
||||||
List<BlockState> themes = new ArrayList<>();
|
renderedState = newState;
|
||||||
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();
|
markDirtyAndDispatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --------------------------------------------------- ADDONS --------------------------------------------------- */
|
public boolean hasSpentGlowstoneDust() {
|
||||||
public boolean emitsLight() {
|
return (bitfield & SPENT_GLOWSTONE_DUST_MASK) != 0;
|
||||||
return (bit_field & LIGHT_MASK) != 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleLight() {
|
public void spentGlowstoneDust() {
|
||||||
if (emitsLight()) bit_field &= ~LIGHT_MASK;
|
bitfield |= SPENT_GLOWSTONE_DUST_MASK;
|
||||||
else bit_field |= LIGHT_MASK;
|
|
||||||
markDirtyAndDispatch();
|
markDirtyAndDispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleRedstone() {
|
public boolean hasSpentRedstoneTorch() {
|
||||||
if (emitsRedstone()) bit_field &= ~REDSTONE_MASK;
|
return (bitfield & SPENT_REDSTONE_TORCH_MASK) != 0;
|
||||||
else bit_field |= REDSTONE_MASK;
|
}
|
||||||
|
|
||||||
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
|
public void spentRedstoneTorch() {
|
||||||
|
bitfield |= SPENT_REDSTONE_TORCH_MASK;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasSpentPoppedChorus() {
|
||||||
|
return (bitfield & SPENT_POPPED_CHORUS_MASK) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void spentPoppedChorus() {
|
||||||
|
bitfield |= SPENT_POPPED_CHORUS_MASK;
|
||||||
markDirtyAndDispatch();
|
markDirtyAndDispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean emitsRedstone() {
|
public boolean emitsRedstone() {
|
||||||
return (bit_field & REDSTONE_MASK) != 0;
|
return (bitfield & EMITS_REDSTONE_MASK) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void toggleSolidity() {
|
public void setEmitsRedstone(boolean nextEmitsRedstone) {
|
||||||
if (isSolid()) bit_field &= ~SOLIDITY_MASK;
|
boolean currentlyEmitsRedstone = emitsRedstone();
|
||||||
else bit_field |= SOLIDITY_MASK;
|
|
||||||
|
|
||||||
if(world != null) world.setBlockState(pos, getCachedState());
|
if(currentlyEmitsRedstone != nextEmitsRedstone) {
|
||||||
markDirtyAndDispatch();
|
if(currentlyEmitsRedstone) bitfield &= ~EMITS_REDSTONE_MASK;
|
||||||
|
else bitfield |= EMITS_REDSTONE_MASK;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSolid() {
|
public boolean isSolid() {
|
||||||
return (bit_field & SOLIDITY_MASK) != 0;
|
return (bitfield & IS_SOLID_MASK) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSolidity(boolean nextSolid) {
|
||||||
|
boolean currentlySolid = isSolid();
|
||||||
|
|
||||||
|
if(currentlySolid != nextSolid) {
|
||||||
|
if(currentlySolid) bitfield &= ~IS_SOLID_MASK;
|
||||||
|
else bitfield |= IS_SOLID_MASK;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
if(world != null) world.setBlockState(pos, getCachedState()); //do i need to invalidate any shape caches or something
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//<standard blockentity boilerplate>
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
||||||
@ -161,6 +195,8 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NbtCompound toInitialChunkDataNbt() {
|
public NbtCompound toInitialChunkDataNbt() {
|
||||||
|
//TERRIBLE yarn name, this is "getUpdateTag", it's the nbt that will be sent to clients
|
||||||
|
//and it just calls "writeNbt"
|
||||||
return createNbt();
|
return createNbt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,4 +208,5 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
markDirty();
|
markDirty();
|
||||||
dispatch();
|
dispatch();
|
||||||
}
|
}
|
||||||
|
//</standard blockentity boilerplate>
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,11 @@
|
|||||||
package fr.adrien1106.reframed.block;
|
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.MultipartBlockStateProvider;
|
||||||
import net.minecraft.block.Block;
|
import net.minecraft.block.*;
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.block.ShapeContext;
|
|
||||||
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
import net.minecraft.data.client.MultipartBlockStateSupplier;
|
||||||
import net.minecraft.item.ItemPlacementContext;
|
import net.minecraft.item.ItemPlacementContext;
|
||||||
import net.minecraft.state.StateManager;
|
import net.minecraft.state.StateManager;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.state.property.Properties;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.util.shape.VoxelShape;
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
@ -17,37 +13,34 @@ import net.minecraft.util.shape.VoxelShapes;
|
|||||||
import net.minecraft.world.BlockView;
|
import net.minecraft.world.BlockView;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import static net.minecraft.data.client.VariantSettings.Rotation.*;
|
|
||||||
import static net.minecraft.state.property.Properties.FACING;
|
|
||||||
|
|
||||||
public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements MultipartBlockStateProvider {
|
public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements MultipartBlockStateProvider {
|
||||||
|
|
||||||
protected static final VoxelShape DOWN = VoxelShapes.cuboid(0f, 0f, 0f, 1f, 0.5f, 1f);
|
private 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);
|
private 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);
|
private 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);
|
private 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);
|
private 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);
|
private static final VoxelShape WEST = VoxelShapes.cuboid(0f, 0f, 0f, 0.5f, 1f, 1f);
|
||||||
|
|
||||||
public ReFramedSlabBlock(Settings settings) {
|
public ReFramedSlabBlock(Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
setDefaultState(getDefaultState().with(FACING, Direction.DOWN));
|
setDefaultState(getDefaultState().with(Properties.FACING, Direction.DOWN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
protected void appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
super.appendProperties(builder.add(FACING));
|
super.appendProperties(builder.add(Properties.FACING));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||||
return super.getPlacementState(ctx).with(FACING, ctx.getSide().getOpposite());
|
return super.getPlacementState(ctx).with(Properties.FACING, ctx.getSide().getOpposite());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||||
return switch (state.get(FACING)) {
|
return switch (state.get(Properties.FACING)) {
|
||||||
case DOWN -> DOWN;
|
case DOWN -> DOWN;
|
||||||
case UP -> UP;
|
case UP -> UP;
|
||||||
case NORTH -> NORTH;
|
case NORTH -> NORTH;
|
||||||
@ -59,19 +52,6 @@ public class ReFramedSlabBlock extends WaterloggableReFramedBlock implements Mul
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MultipartBlockStateSupplier getMultipart() {
|
public MultipartBlockStateSupplier getMultipart() {
|
||||||
Identifier model_id = ReFramed.id("slab_special");
|
return null;
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M
|
|||||||
|
|
||||||
public static final EnumProperty<StairDirection> FACING = EnumProperty.of("facing", StairDirection.class);
|
public static final EnumProperty<StairDirection> FACING = EnumProperty.of("facing", StairDirection.class);
|
||||||
public static final EnumProperty<StairShape> SHAPE = EnumProperty.of("shape", StairShape.class);
|
public static final EnumProperty<StairShape> SHAPE = EnumProperty.of("shape", StairShape.class);
|
||||||
public static final List<VoxelShape> VOXEL_LIST = new ArrayList<>(52);
|
private static final List<VoxelShape> VOXEL_LIST = new ArrayList<>(52);
|
||||||
|
|
||||||
public ReFramedStairsBlock(Settings settings) {
|
public ReFramedStairsBlock(Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
@ -53,16 +53,13 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M
|
|||||||
@Override
|
@Override
|
||||||
public BlockState getStateForNeighborUpdate(BlockState state, Direction direction, BlockState neighbor_state, WorldAccess world, BlockPos pos, BlockPos moved) {
|
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)
|
return super.getStateForNeighborUpdate(state, direction, neighbor_state, world, pos, moved)
|
||||||
.with(SHAPE, getPlacementShape(state.getBlock(), state.get(FACING), world, pos));
|
.with(SHAPE, getPlacementShape(state.get(FACING), world, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override // Pretty happy of how clean it is (also got it on first try :) )
|
@Override // Pretty happy of how clean it is (also got it on first try :) )
|
||||||
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
public BlockState getPlacementState(ItemPlacementContext ctx) {
|
||||||
return getPlacement(super.getPlacementState(ctx), ctx);
|
BlockState state = super.getPlacementState(ctx);
|
||||||
}
|
|
||||||
|
|
||||||
public static BlockState getPlacement(BlockState state, ItemPlacementContext ctx) {
|
|
||||||
Direction side = ctx.getSide().getOpposite();
|
Direction side = ctx.getSide().getOpposite();
|
||||||
BlockPos block_pos = ctx.getBlockPos();
|
BlockPos block_pos = ctx.getBlockPos();
|
||||||
Vec3d hit_pos = ctx.getHitPos();
|
Vec3d hit_pos = ctx.getHitPos();
|
||||||
@ -87,23 +84,20 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M
|
|||||||
);
|
);
|
||||||
StairDirection face = StairDirection.getByDirections(side, part_direction);
|
StairDirection face = StairDirection.getByDirections(side, part_direction);
|
||||||
|
|
||||||
return state.with(FACING, face).with(SHAPE, getPlacementShape(state.getBlock(), face, ctx.getWorld(), block_pos));
|
return state.with(FACING, face).with(SHAPE, getPlacementShape(face, ctx.getWorld(), block_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
public void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||||
super.onStateReplaced(state, world, pos, newState, 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);
|
if(!state.isOf(newState.getBlock())) world.removeBlockEntity(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------------------------- DON'T GO FURTHER IF YOU LIKE HAVING EYES ---------------------------------- */
|
/* ---------------------------------- DON'T GO FURTHER IF YOU LIKE HAVING EYES ---------------------------------- */
|
||||||
@Override
|
@Override
|
||||||
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
public VoxelShape getOutlineShape(BlockState state, BlockView world, BlockPos pos, ShapeContext context) {
|
||||||
return getOutline(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VoxelShape getOutline(BlockState state) {
|
|
||||||
StairShape shape = state.get(SHAPE);
|
StairShape shape = state.get(SHAPE);
|
||||||
StairDirection direction = state.get(FACING);
|
StairDirection direction = state.get(FACING);
|
||||||
return switch (shape) {
|
return switch (shape) {
|
||||||
@ -217,40 +211,40 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getNeighborPos(StairDirection face, Direction direction, Boolean reverse, Direction reference, BlockView world, BlockPos pos, Block block) {
|
private static String getNeighborPos(StairDirection face, Direction direction, Boolean reverse, Direction reference, BlockView world, BlockPos pos) {
|
||||||
BlockState block_state = world.getBlockState(
|
BlockState block_state = world.getBlockState(
|
||||||
pos.offset(reverse ? direction.getOpposite() : direction)
|
pos.offset(reverse ? direction.getOpposite() : direction)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (block_state.isOf(block) && block_state.get(FACING).hasDirection(reference)) {
|
if (block_state.getBlock() instanceof ReFramedStairsBlock && block_state.get(FACING).hasDirection(reference)) {
|
||||||
if (block_state.get(FACING).hasDirection(face.getLeftDirection())) return "left";
|
if (block_state.get(FACING).hasDirection(face.getLeftDirection())) return "left";
|
||||||
else if (block_state.get(FACING).hasDirection(face.getRightDirection())) return "right";
|
else if (block_state.get(FACING).hasDirection(face.getRightDirection())) return "right";
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static StairShape getPlacementShape(Block block, StairDirection face, BlockView world, BlockPos pos) {
|
private static StairShape getPlacementShape(StairDirection face, BlockView world, BlockPos pos) {
|
||||||
StairShape shape = STRAIGHT;
|
StairShape shape = STRAIGHT;
|
||||||
|
|
||||||
String sol = getNeighborPos(face, face.getFirstDirection(), true, face.getSecondDirection(), world, pos, block);
|
String sol = getNeighborPos(face, face.getFirstDirection(), true, face.getSecondDirection(), world, pos);
|
||||||
switch (sol) {
|
switch (sol) {
|
||||||
case "right": return INNER_RIGHT;
|
case "right": return INNER_RIGHT;
|
||||||
case "left": return INNER_LEFT;
|
case "left": return INNER_LEFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
sol = getNeighborPos(face, face.getSecondDirection(), true, face.getFirstDirection(), world, pos, block);
|
sol = getNeighborPos(face, face.getSecondDirection(), true, face.getFirstDirection(), world, pos);
|
||||||
switch (sol) {
|
switch (sol) {
|
||||||
case "right": return INNER_RIGHT;
|
case "right": return INNER_RIGHT;
|
||||||
case "left": return INNER_LEFT;
|
case "left": return INNER_LEFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
sol = getNeighborPos(face, face.getFirstDirection(), false, face.getSecondDirection(), world, pos, block);
|
sol = getNeighborPos(face, face.getFirstDirection(), false, face.getSecondDirection(), world, pos);
|
||||||
switch (sol) {
|
switch (sol) {
|
||||||
case "right" -> shape = FIRST_OUTER_RIGHT;
|
case "right" -> shape = FIRST_OUTER_RIGHT;
|
||||||
case "left" -> shape = FIRST_OUTER_LEFT;
|
case "left" -> shape = FIRST_OUTER_LEFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
sol = getNeighborPos(face, face.getSecondDirection(), false, face.getFirstDirection(), world, pos, block);
|
sol = getNeighborPos(face, face.getSecondDirection(), false, face.getFirstDirection(), world, pos);
|
||||||
switch (sol) {
|
switch (sol) {
|
||||||
case "right" -> {
|
case "right" -> {
|
||||||
if (shape.equals(STRAIGHT)) shape = SECOND_OUTER_RIGHT;
|
if (shape.equals(STRAIGHT)) shape = SECOND_OUTER_RIGHT;
|
||||||
@ -267,17 +261,12 @@ public class ReFramedStairsBlock extends WaterloggableReFramedBlock implements M
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MultipartBlockStateSupplier getMultipart() {
|
public MultipartBlockStateSupplier getMultipart() {
|
||||||
return getStairMultipart(this, false);
|
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");
|
||||||
public static MultipartBlockStateSupplier getStairMultipart(Block block, boolean is_double) {
|
Identifier outer_id = ReFramed.id("outer_stairs_special");
|
||||||
String prefix = is_double ? "double_" : "";
|
Identifier outer_side_id = ReFramed.id("outer_side_stairs_special");
|
||||||
Identifier straight_id = ReFramed.id(prefix + "stairs_special");
|
return MultipartBlockStateSupplier.create(this)
|
||||||
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 */
|
/* STRAIGHT X AXIS */
|
||||||
.with(GBlockstate.when(FACING, DOWN_EAST, SHAPE, STRAIGHT),
|
.with(GBlockstate.when(FACING, DOWN_EAST, SHAPE, STRAIGHT),
|
||||||
GBlockstate.variant(straight_id, true, R0, R0))
|
GBlockstate.variant(straight_id, true, R0, R0))
|
||||||
|
@ -16,6 +16,7 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterloggable {
|
public class WaterloggableReFramedBlock extends ReFramedBlock implements Waterloggable {
|
||||||
public WaterloggableReFramedBlock(Settings settings) {
|
public WaterloggableReFramedBlock(Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
|
|
||||||
setDefaultState(getDefaultState().with(Properties.WATERLOGGED, false));
|
setDefaultState(getDefaultState().with(Properties.WATERLOGGED, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,80 +26,72 @@ public class ReFramedClient implements ClientModInitializer {
|
|||||||
//all frames mustn't be on the SOLID layer because they are not opaque!
|
//all frames mustn't be on the SOLID layer because they are not opaque!
|
||||||
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0]));
|
BlockRenderLayerMap.INSTANCE.putBlocks(RenderLayer.getCutout(), ReFramed.BLOCKS.toArray(new Block[0]));
|
||||||
|
|
||||||
HELPER.addReFramedModel("button_special" , HELPER.auto(new Identifier("block/button")));
|
HELPER.addReFramedModel(ReFramed.id("button_special") , HELPER.auto(new Identifier("block/button")));
|
||||||
HELPER.addReFramedModel("button_pressed_special" , HELPER.auto(new Identifier("block/button_pressed")));
|
HELPER.addReFramedModel(ReFramed.id("button_pressed_special") , HELPER.auto(new Identifier("block/button_pressed")));
|
||||||
HELPER.addReFramedModel("one_candle_special" , HELPER.auto(new Identifier("block/template_candle")));
|
HELPER.addReFramedModel(ReFramed.id("one_candle_special") , HELPER.auto(new Identifier("block/template_candle")));
|
||||||
HELPER.addReFramedModel("two_candles_special" , HELPER.auto(new Identifier("block/template_two_candles")));
|
HELPER.addReFramedModel(ReFramed.id("two_candles_special") , HELPER.auto(new Identifier("block/template_two_candles")));
|
||||||
HELPER.addReFramedModel("three_candles_special" , HELPER.auto(new Identifier("block/template_three_candles")));
|
HELPER.addReFramedModel(ReFramed.id("three_candles_special") , HELPER.auto(new Identifier("block/template_three_candles")));
|
||||||
HELPER.addReFramedModel("four_candles_special" , HELPER.auto(new Identifier("block/template_four_candles")));
|
HELPER.addReFramedModel(ReFramed.id("four_candles_special") , HELPER.auto(new Identifier("block/template_four_candles")));
|
||||||
HELPER.addReFramedModel("carpet_special" , HELPER.auto(new Identifier("block/carpet")));
|
HELPER.addReFramedModel(ReFramed.id("carpet_special") , HELPER.auto(new Identifier("block/carpet")));
|
||||||
HELPER.addReFramedModel("cube_special" , HELPER.auto(new Identifier("block/cube")));
|
HELPER.addReFramedModel(ReFramed.id("cube_special") , HELPER.auto(new Identifier("block/cube")));
|
||||||
HELPER.addReFramedModel("door_bottom_left_special" , HELPER.auto(new Identifier("block/door_bottom_left")));
|
HELPER.addReFramedModel(ReFramed.id("door_bottom_left_special") , HELPER.auto(new Identifier("block/door_bottom_left")));
|
||||||
HELPER.addReFramedModel("door_bottom_right_special" , HELPER.auto(new Identifier("block/door_bottom_right")));
|
HELPER.addReFramedModel(ReFramed.id("door_bottom_right_special") , HELPER.auto(new Identifier("block/door_bottom_right")));
|
||||||
HELPER.addReFramedModel("door_top_left_special" , HELPER.auto(new Identifier("block/door_top_left")));
|
HELPER.addReFramedModel(ReFramed.id("door_top_left_special") , HELPER.auto(new Identifier("block/door_top_left")));
|
||||||
HELPER.addReFramedModel("door_top_right_special" , HELPER.auto(new Identifier("block/door_top_right")));
|
HELPER.addReFramedModel(ReFramed.id("door_top_right_special") , HELPER.auto(new Identifier("block/door_top_right")));
|
||||||
HELPER.addReFramedModel("door_bottom_left_open_special" , HELPER.auto(new Identifier("block/door_bottom_left_open")));
|
HELPER.addReFramedModel(ReFramed.id("door_bottom_left_open_special"), HELPER.auto(new Identifier("block/door_bottom_left_open")));
|
||||||
HELPER.addReFramedModel("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_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("door_top_left_open_special" , HELPER.auto(new Identifier("block/door_top_left_open")));
|
HELPER.addReFramedModel(ReFramed.id("door_top_left_open_special") , HELPER.auto(new Identifier("block/door_top_left_open")));
|
||||||
HELPER.addReFramedModel("door_top_right_open_special" , HELPER.auto(new Identifier("block/door_top_right_open")));
|
HELPER.addReFramedModel(ReFramed.id("door_top_right_open_special") , HELPER.auto(new Identifier("block/door_top_right_open")));
|
||||||
HELPER.addReFramedModel("fence_post_special" , HELPER.auto(new Identifier("block/fence_post")));
|
HELPER.addReFramedModel(ReFramed.id("fence_post_special") , HELPER.auto(new Identifier("block/fence_post")));
|
||||||
HELPER.addReFramedModel("fence_gate_special" , HELPER.auto(new Identifier("block/template_fence_gate")));
|
HELPER.addReFramedModel(ReFramed.id("fence_gate_special") , HELPER.auto(new Identifier("block/template_fence_gate")));
|
||||||
HELPER.addReFramedModel("fence_gate_open_special" , HELPER.auto(new Identifier("block/template_fence_gate_open")));
|
HELPER.addReFramedModel(ReFramed.id("fence_gate_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_open")));
|
||||||
HELPER.addReFramedModel("fence_gate_wall_special" , HELPER.auto(new Identifier("block/template_fence_gate_wall")));
|
HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall")));
|
||||||
HELPER.addReFramedModel("fence_gate_wall_open_special" , HELPER.auto(new Identifier("block/template_fence_gate_wall_open")));
|
HELPER.addReFramedModel(ReFramed.id("fence_gate_wall_open_special") , HELPER.auto(new Identifier("block/template_fence_gate_wall_open")));
|
||||||
HELPER.addReFramedModel("glass_pane_post_special" , HELPER.auto(new Identifier("block/glass_pane_post")));
|
HELPER.addReFramedModel(ReFramed.id("glass_pane_post_special") , HELPER.auto(new Identifier("block/glass_pane_post")));
|
||||||
HELPER.addReFramedModel("glass_pane_noside_special" , HELPER.auto(new Identifier("block/glass_pane_noside")));
|
HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_special") , HELPER.auto(new Identifier("block/glass_pane_noside")));
|
||||||
HELPER.addReFramedModel("glass_pane_noside_alt_special" , HELPER.auto(new Identifier("block/glass_pane_noside_alt")));
|
HELPER.addReFramedModel(ReFramed.id("glass_pane_noside_alt_special"), HELPER.auto(new Identifier("block/glass_pane_noside_alt")));
|
||||||
HELPER.addReFramedModel("pressure_plate_up_special" , HELPER.auto(new Identifier("block/pressure_plate_up")));
|
HELPER.addReFramedModel(ReFramed.id("pressure_plate_up_special") , HELPER.auto(new Identifier("block/pressure_plate_up")));
|
||||||
HELPER.addReFramedModel("pressure_plate_down_special" , HELPER.auto(new Identifier("block/pressure_plate_down")));
|
HELPER.addReFramedModel(ReFramed.id("pressure_plate_down_special") , HELPER.auto(new Identifier("block/pressure_plate_down")));
|
||||||
HELPER.addReFramedModel("slab_special" , HELPER.auto(new Identifier("block/slab")));
|
HELPER.addReFramedModel(ReFramed.id("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(ReFramed.id("stairs_special") , HELPER.auto(ReFramed.id("block/stairs")));
|
||||||
HELPER.addReFramedModel("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("outers_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("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("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("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("double_stairs_special" , HELPER.autoDouble(ReFramed.id("block/stairs"), ReFramed.id("block/stairs_complement")));
|
HELPER.addReFramedModel(ReFramed.id("trapdoor_top_special") , HELPER.auto(new Identifier("block/template_trapdoor_top")));
|
||||||
HELPER.addReFramedModel("double_outers_stairs_special" , HELPER.autoDouble(ReFramed.id("block/double_outer_stairs"), ReFramed.id("block/double_outer_stairs_complement")));
|
HELPER.addReFramedModel(ReFramed.id("wall_post_special") , HELPER.auto(new Identifier("block/template_wall_post")));
|
||||||
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("trapdoor_bottom_special" , HELPER.auto(new Identifier("block/template_trapdoor_bottom")));
|
|
||||||
HELPER.addReFramedModel("trapdoor_top_special" , HELPER.auto(new Identifier("block/template_trapdoor_top")));
|
|
||||||
HELPER.addReFramedModel("wall_post_special" , HELPER.auto(new Identifier("block/template_wall_post")));
|
|
||||||
|
|
||||||
//vanilla style models (using "special-sprite replacement" method)
|
//vanilla style models (using "special-sprite replacement" method)
|
||||||
HELPER.addReFramedModel("lever_special" , HELPER.json(ReFramed.id("block/lever")));
|
HELPER.addReFramedModel(ReFramed.id("lever_special") , HELPER.json(ReFramed.id("block/lever")));
|
||||||
HELPER.addReFramedModel("trapdoor_open_special" , HELPER.json(ReFramed.id("block/trapdoor_open")));
|
HELPER.addReFramedModel(ReFramed.id("trapdoor_open_special") , HELPER.json(ReFramed.id("block/trapdoor_open")));
|
||||||
HELPER.addReFramedModel("lever_on_special" , HELPER.json(ReFramed.id("block/lever_on")));
|
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
|
//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("fence_side_special" , HELPER.json(ReFramed.id("block/fence_side")));
|
HELPER.addReFramedModel(ReFramed.id("fence_side_special") , HELPER.json(ReFramed.id("block/fence_side")));
|
||||||
HELPER.addReFramedModel("glass_pane_side_special" , HELPER.json(ReFramed.id("block/glass_pane_side")));
|
HELPER.addReFramedModel(ReFramed.id("glass_pane_side_special") , HELPER.json(ReFramed.id("block/glass_pane_side")));
|
||||||
HELPER.addReFramedModel("glass_pane_side_alt_special" , HELPER.json(ReFramed.id("block/glass_pane_side_alt")));
|
HELPER.addReFramedModel(ReFramed.id("glass_pane_side_alt_special") , HELPER.json(ReFramed.id("block/glass_pane_side_alt")));
|
||||||
HELPER.addReFramedModel("wall_side_special" , HELPER.json(ReFramed.id("block/wall_side")));
|
HELPER.addReFramedModel(ReFramed.id("wall_side_special") , HELPER.json(ReFramed.id("block/wall_side")));
|
||||||
HELPER.addReFramedModel("wall_side_tall_special" , HELPER.json(ReFramed.id("block/wall_side_tall")));
|
HELPER.addReFramedModel(ReFramed.id("wall_side_tall_special") , HELPER.json(ReFramed.id("block/wall_side_tall")));
|
||||||
|
|
||||||
//item only models
|
//item only models
|
||||||
HELPER.addReFramedModel("button_inventory_special" , HELPER.auto(new Identifier("block/button_inventory")));
|
HELPER.addReFramedModel(ReFramed.id("button_inventory_special") , HELPER.auto(new Identifier("block/button_inventory")));
|
||||||
HELPER.addReFramedModel("fence_inventory_special" , HELPER.auto(new Identifier("block/fence_inventory")));
|
HELPER.addReFramedModel(ReFramed.id("fence_inventory_special") , HELPER.auto(new Identifier("block/fence_inventory")));
|
||||||
HELPER.addReFramedModel("fence_post_inventory_special" , HELPER.auto(ReFramed.id("block/fence_post_inventory")));
|
HELPER.addReFramedModel(ReFramed.id("fence_post_inventory_special") , HELPER.auto(ReFramed.id("block/fence_post_inventory")));
|
||||||
HELPER.addReFramedModel("wall_inventory_special" , HELPER.auto(new Identifier("block/wall_inventory")));
|
HELPER.addReFramedModel(ReFramed.id("wall_inventory_special") , HELPER.auto(new Identifier("block/wall_inventory")));
|
||||||
|
|
||||||
//item model assignments (in lieu of models/item/___.json)
|
//item model assignments (in lieu of models/item/___.json)
|
||||||
HELPER.assignItemModel("button_inventory_special" , ReFramed.BUTTON);
|
HELPER.assignItemModel(ReFramed.id("button_inventory_special") , ReFramed.BUTTON);
|
||||||
HELPER.assignItemModel("carpet_special" , ReFramed.CARPET);
|
HELPER.assignItemModel(ReFramed.id("carpet_special") , ReFramed.CARPET);
|
||||||
HELPER.assignItemModel("cube_special" , ReFramed.CUBE);
|
HELPER.assignItemModel(ReFramed.id("cube_special") , ReFramed.CUBE);
|
||||||
HELPER.assignItemModel("fence_inventory_special" , ReFramed.FENCE);
|
HELPER.assignItemModel(ReFramed.id("fence_inventory_special") , ReFramed.FENCE);
|
||||||
HELPER.assignItemModel("fence_gate_special" , ReFramed.FENCE_GATE);
|
HELPER.assignItemModel(ReFramed.id("fence_gate_special") , ReFramed.FENCE_GATE);
|
||||||
HELPER.assignItemModel("trapdoor_bottom_special" , ReFramed.IRON_TRAPDOOR);
|
HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.IRON_TRAPDOOR);
|
||||||
HELPER.assignItemModel("fence_post_inventory_special" , ReFramed.POST);
|
HELPER.assignItemModel(ReFramed.id("fence_post_inventory_special") , ReFramed.POST);
|
||||||
HELPER.assignItemModel("pressure_plate_up_special" , ReFramed.PRESSURE_PLATE);
|
HELPER.assignItemModel(ReFramed.id("pressure_plate_up_special") , ReFramed.PRESSURE_PLATE);
|
||||||
HELPER.assignItemModel("slab_special" , ReFramed.SLAB);
|
HELPER.assignItemModel(ReFramed.id("slab_special") , ReFramed.SLAB);
|
||||||
HELPER.assignItemModel("double_slab_special" , ReFramed.DOUBLE_SLAB);
|
HELPER.assignItemModel(ReFramed.id("stairs_special") , ReFramed.STAIRS);
|
||||||
HELPER.assignItemModel("stairs_special" , ReFramed.STAIRS);
|
HELPER.assignItemModel(ReFramed.id("trapdoor_bottom_special") , ReFramed.TRAPDOOR);
|
||||||
HELPER.assignItemModel("double_stairs_special" , ReFramed.DOUBLE_STAIRS);
|
HELPER.assignItemModel(ReFramed.id("wall_inventory_special") , ReFramed.WALL);
|
||||||
HELPER.assignItemModel("trapdoor_bottom_special" , ReFramed.TRAPDOOR);
|
|
||||||
HELPER.assignItemModel("wall_inventory_special" , ReFramed.WALL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void privateInit() {
|
private void privateInit() {
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package fr.adrien1106.reframed.client;
|
package fr.adrien1106.reframed.client;
|
||||||
|
|
||||||
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 fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
|
||||||
|
import fr.adrien1106.reframed.client.model.UnbakedAutoRetexturedModel;
|
||||||
|
import fr.adrien1106.reframed.client.model.UnbakedJsonRetexturedModel;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
import net.fabricmc.fabric.api.renderer.v1.RendererAccess;
|
||||||
import net.fabricmc.loader.api.FabricLoader;
|
import net.fabricmc.loader.api.FabricLoader;
|
||||||
@ -26,25 +23,20 @@ public class ReFramedClientHelper {
|
|||||||
|
|
||||||
private final ReFramedModelProvider prov;
|
private final ReFramedModelProvider prov;
|
||||||
|
|
||||||
|
public UnbakedModel auto(Identifier parent) {
|
||||||
public UnbakedRetexturedModel auto(Identifier parent) {
|
|
||||||
return new UnbakedAutoRetexturedModel(parent);
|
return new UnbakedAutoRetexturedModel(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnbakedRetexturedModel json(Identifier parent) {
|
public UnbakedModel json(Identifier parent) {
|
||||||
return new UnbakedJsonRetexturedModel(parent);
|
return new UnbakedJsonRetexturedModel(parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnbakedModel autoDouble(Identifier first, Identifier second) {
|
public void addReFramedModel(Identifier id, UnbakedModel unbaked) {
|
||||||
return new UnbakedDoubleRetexturedModel(auto(first), auto(second));
|
prov.addReFramedModel(id, unbaked);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addReFramedModel(String id, UnbakedModel unbaked) {
|
public void assignItemModel(Identifier model_id, ItemConvertible... item_convertibles) {
|
||||||
prov.addReFramedModel(ReFramed.id(id), unbaked);
|
prov.assignItemModel(model_id, item_convertibles);
|
||||||
}
|
|
||||||
|
|
||||||
public void assignItemModel(String id, ItemConvertible... item_convertibles) {
|
|
||||||
prov.assignItemModel(ReFramed.id(id), item_convertibles);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CamoAppearanceManager getCamoApperanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
|
public CamoAppearanceManager getCamoApperanceManager(Function<SpriteIdentifier, Sprite> spriteLookup) {
|
||||||
|
@ -37,9 +37,9 @@ public class ReFramedModelProvider implements ModelResourceProvider, ModelVarian
|
|||||||
//but json models are never allowed to have non-json models as a parent, and frame unbaked models are not json models. Ah well.
|
//but json models are never allowed to have non-json models as a parent, and frame unbaked models are not json models. Ah well.
|
||||||
//So, instead, we use a ModelVariantProvider to redirect attempts to load the item:id#inventory model.
|
//So, instead, we use a ModelVariantProvider to redirect attempts to load the item:id#inventory model.
|
||||||
@Override
|
@Override
|
||||||
public @Nullable UnbakedModel loadModelVariant(ModelIdentifier model, ModelProviderContext context) {
|
public @Nullable UnbakedModel loadModelVariant(ModelIdentifier modelId, ModelProviderContext context) {
|
||||||
Identifier custom_model = itemAssignments.get(model);
|
Identifier customModelId = itemAssignments.get(modelId);
|
||||||
return custom_model == null ? null : loadModelResource(custom_model, context);
|
return customModelId == null ? null : loadModelResource(customModelId, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// camo appearance manager cache
|
/// camo appearance manager cache
|
||||||
|
@ -1,51 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,5 +6,5 @@ import net.minecraft.util.math.BlockPos;
|
|||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
|
||||||
public interface DynamicBakedModel {
|
public interface DynamicBakedModel {
|
||||||
BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos, int theme_index);
|
BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +0,0 @@
|
|||||||
package fr.adrien1106.reframed.client.model;
|
|
||||||
|
|
||||||
import net.minecraft.client.render.model.BakedModel;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface MultiRetexturableModel {
|
|
||||||
|
|
||||||
List<BakedModel> models();
|
|
||||||
}
|
|
@ -1,15 +1,12 @@
|
|||||||
package fr.adrien1106.reframed.client.model;
|
package fr.adrien1106.reframed.client.model;
|
||||||
|
|
||||||
import fr.adrien1106.reframed.block.ReFramedEntity;
|
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
|
||||||
import fr.adrien1106.reframed.client.model.apperance.SpriteProperties;
|
import fr.adrien1106.reframed.client.model.apperance.SpriteProperties;
|
||||||
import fr.adrien1106.reframed.mixin.MinecraftAccessor;
|
import fr.adrien1106.reframed.mixin.MinecraftAccessor;
|
||||||
import fr.adrien1106.reframed.client.model.apperance.CamoAppearance;
|
import fr.adrien1106.reframed.client.model.apperance.CamoAppearance;
|
||||||
import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
|
import fr.adrien1106.reframed.client.model.apperance.CamoAppearanceManager;
|
||||||
import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance;
|
import fr.adrien1106.reframed.client.model.apperance.WeightedComputedAppearance;
|
||||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
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.MutableQuadView;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
@ -32,44 +29,30 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
||||||
public RetexturingBakedModel(BakedModel base_model, CamoAppearanceManager tam, int theme_index, ModelBakeSettings settings, BlockState item_state, boolean ao) {
|
public RetexturingBakedModel(BakedModel baseModel, CamoAppearanceManager tam, ModelBakeSettings settings, BlockState itemModelState, boolean ao) {
|
||||||
this.wrapped = base_model; //field from the superclass; vanilla getQuads etc. will delegate through to this
|
this.wrapped = baseModel; //field from the superclass; vanilla getQuads etc. will delegate through to this
|
||||||
|
|
||||||
this.tam = tam;
|
this.tam = tam;
|
||||||
this.theme_index = theme_index;
|
this.uvlock = settings.isUvLocked();
|
||||||
this.uv_lock = settings.isUvLocked();
|
this.itemModelState = itemModelState;
|
||||||
this.item_state = item_state;
|
|
||||||
this.ao = ao;
|
this.ao = ao;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final CamoAppearanceManager tam;
|
protected final CamoAppearanceManager tam;
|
||||||
protected final int theme_index;
|
protected final boolean uvlock;
|
||||||
protected final boolean uv_lock;
|
protected final BlockState itemModelState;
|
||||||
protected final BlockState item_state;
|
|
||||||
protected final boolean ao;
|
protected final boolean ao;
|
||||||
|
|
||||||
/* ----------------------------------------------- CACHE ELEMENT ------------------------------------------------ */
|
|
||||||
// TODO make static ? for connected textures ?
|
|
||||||
protected record MeshCacheKey(BlockState state, TransformCacheKey transform) {}
|
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 record TransformCacheKey(CamoAppearance appearance, int model_id) {}
|
||||||
protected final ConcurrentMap<TransformCacheKey, RetexturingTransformer> retextured_transforms = new ConcurrentHashMap<>();
|
protected final ConcurrentMap<TransformCacheKey, RetexturingTransformer> retextured_transforms = new ConcurrentHashMap<>();
|
||||||
protected final ConcurrentMap<MeshCacheKey, Mesh> retextured_meshes = new ConcurrentHashMap<>(); //mutable, append-only cache
|
|
||||||
|
|
||||||
protected static final Direction[] DIRECTIONS_AND_NULL;
|
protected static final Direction[] DIRECTIONS = Direction.values();
|
||||||
static {
|
protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1];
|
||||||
Direction[] values = Direction.values();
|
static { System.arraycopy(DIRECTIONS, 0, DIRECTIONS_AND_NULL, 0, DIRECTIONS.length); }
|
||||||
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 abstract Mesh getBaseMesh(BlockState state);
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public boolean isVanillaAdapter() {
|
public boolean isVanillaAdapter() {
|
||||||
@ -78,20 +61,20 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Sprite getParticleSprite() {
|
public Sprite getParticleSprite() {
|
||||||
return tam.getDefaultAppearance(theme_index).getSprites(Direction.UP, 0).get(0).sprite();
|
return tam.getDefaultAppearance().getSprites(Direction.UP, 0).get(0).sprite();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? s.getTheme(theme_index) : null;
|
BlockState theme = (world.getBlockEntityRenderData(pos) instanceof BlockState s) ? s : null;
|
||||||
QuadEmitter quad_emitter = context.getEmitter();
|
QuadEmitter quad_emitter = context.getEmitter();
|
||||||
if(theme == null || theme.isAir()) {
|
if(theme == null || theme.isAir()) {
|
||||||
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(theme_index), 0)), 0).outputTo(quad_emitter);
|
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(), 0)), 0).outputTo(quad_emitter);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(theme.getBlock() == Blocks.BARRIER) return;
|
if(theme.getBlock() == Blocks.BARRIER) return;
|
||||||
|
|
||||||
CamoAppearance camo = tam.getCamoAppearance(world, theme, pos, theme_index);
|
CamoAppearance camo = tam.getCamoAppearance(world, theme, pos);
|
||||||
long seed = theme.getRenderingSeed(pos);
|
long seed = theme.getRenderingSeed(pos);
|
||||||
int model_id = 0;
|
int model_id = 0;
|
||||||
if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
|
if (camo instanceof WeightedComputedAppearance wca) model_id = wca.getAppearanceIndex(seed);
|
||||||
@ -122,16 +105,16 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
//none of this is accessible unless you're in creative mode doing ctrl-pick btw
|
//none of this is accessible unless you're in creative mode doing ctrl-pick btw
|
||||||
CamoAppearance nbtAppearance;
|
CamoAppearance nbtAppearance;
|
||||||
int tint;
|
int tint;
|
||||||
BlockState theme = ReFramedEntity.readStateFromItem(stack, theme_index);
|
BlockState theme = ReFramedEntity.readStateFromItem(stack);
|
||||||
if(!theme.isAir()) {
|
if(!theme.isAir()) {
|
||||||
nbtAppearance = tam.getCamoAppearance(null, theme, null, theme_index);
|
nbtAppearance = tam.getCamoAppearance(null, theme, null);
|
||||||
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
|
tint = 0xFF000000 | ((MinecraftAccessor) MinecraftClient.getInstance()).getItemColors().getColor(new ItemStack(theme.getBlock()), 0);
|
||||||
} else {
|
} else {
|
||||||
nbtAppearance = tam.getDefaultAppearance(theme_index);
|
nbtAppearance = tam.getDefaultAppearance();
|
||||||
tint = 0xFFFFFFFF;
|
tint = 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(item_state, new TransformCacheKey(nbtAppearance, 0)), 0);
|
Mesh untintedMesh = getUntintedRetexturedMesh(new MeshCacheKey(itemModelState, new TransformCacheKey(nbtAppearance, 0)), 0);
|
||||||
|
|
||||||
QuadEmitter quad_emitter = context.getEmitter();
|
QuadEmitter quad_emitter = context.getEmitter();
|
||||||
if(tint == 0xFFFFFFFF) {
|
if(tint == 0xFFFFFFFF) {
|
||||||
@ -149,22 +132,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
|
|
||||||
protected Mesh createUntintedRetexturedMesh(MeshCacheKey key, long seed) {
|
protected Mesh createUntintedRetexturedMesh(MeshCacheKey key, long seed) {
|
||||||
RetexturingTransformer transformer = retextured_transforms.computeIfAbsent(key.transform, (k) -> new RetexturingTransformer(k.appearance, seed));
|
RetexturingTransformer transformer = retextured_transforms.computeIfAbsent(key.transform, (k) -> new RetexturingTransformer(k.appearance, seed));
|
||||||
return pretransformMesh(getBaseMesh(key.state), transformer);
|
return MeshTransformUtil.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 {
|
public class RetexturingTransformer {
|
||||||
@ -193,7 +161,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
properties.sprite(),
|
properties.sprite(),
|
||||||
MutableQuadView.BAKE_NORMALIZED
|
MutableQuadView.BAKE_NORMALIZED
|
||||||
| properties.flags()
|
| properties.flags()
|
||||||
| (uv_lock ? MutableQuadView.BAKE_LOCK_UV : 0)
|
| (uvlock ? MutableQuadView.BAKE_LOCK_UV : 0)
|
||||||
);
|
);
|
||||||
quad.tag(i+1);
|
quad.tag(i+1);
|
||||||
quad.emit();
|
quad.emit();
|
||||||
@ -207,7 +175,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
// apply new quad shape
|
// apply new quad shape
|
||||||
quad.material(ta.getRenderMaterial(ao));
|
quad.material(ta.getRenderMaterial(ao));
|
||||||
bounds.intersection(origin_bounds, direction.getAxis()).apply(quad, origin_bounds);
|
bounds.intersection(origin_bounds, direction.getAxis()).apply(quad, origin_bounds);
|
||||||
quad.spriteBake( // seems to work without the flags and break with it
|
quad.spriteBake( // TODO check if the flags are usefull because it seems to be braking the functioning of it
|
||||||
properties.sprite(),
|
properties.sprite(),
|
||||||
MutableQuadView.BAKE_NORMALIZED
|
MutableQuadView.BAKE_NORMALIZED
|
||||||
| MutableQuadView.BAKE_LOCK_UV
|
| MutableQuadView.BAKE_LOCK_UV
|
||||||
|
@ -12,6 +12,7 @@ import net.minecraft.client.render.model.BakedModel;
|
|||||||
import net.minecraft.client.render.model.BakedQuad;
|
import net.minecraft.client.render.model.BakedQuad;
|
||||||
import net.minecraft.client.render.model.Baker;
|
import net.minecraft.client.render.model.Baker;
|
||||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||||
|
import net.minecraft.client.render.model.UnbakedModel;
|
||||||
import net.minecraft.client.texture.Sprite;
|
import net.minecraft.client.texture.Sprite;
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
import net.minecraft.client.util.SpriteIdentifier;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
@ -19,27 +20,50 @@ import net.minecraft.util.math.Direction;
|
|||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel {
|
public class UnbakedAutoRetexturedModel implements UnbakedModel {
|
||||||
|
|
||||||
public UnbakedAutoRetexturedModel(Identifier parent) {
|
public UnbakedAutoRetexturedModel(Identifier parent) {
|
||||||
super(parent);
|
this.parent = parent;
|
||||||
item_state = Blocks.AIR.getDefaultState();
|
}
|
||||||
|
|
||||||
|
protected final Identifier parent;
|
||||||
|
protected BlockState itemModelState = Blocks.AIR.getDefaultState();
|
||||||
|
protected boolean ao = true;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Identifier> getModelDependencies() {
|
||||||
|
return Collections.singletonList(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParents(Function<Identifier, UnbakedModel> function) {
|
||||||
|
function.apply(parent).setParents(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings bake_settings, Identifier identifier) {
|
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
||||||
return new RetexturingBakedModel(
|
return new RetexturingBakedModel(
|
||||||
baker.bake(parent, bake_settings),
|
baker.bake(parent, modelBakeSettings),
|
||||||
ReFramedClient.HELPER.getCamoApperanceManager(texture_getter),
|
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
|
||||||
theme_index,
|
modelBakeSettings,
|
||||||
bake_settings,
|
itemModelState,
|
||||||
item_state,
|
|
||||||
ao
|
ao
|
||||||
) {
|
) {
|
||||||
protected Mesh convertModel(BlockState state) {
|
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) {
|
||||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||||
MeshBuilder builder = r.meshBuilder();
|
MeshBuilder builder = r.meshBuilder();
|
||||||
QuadEmitter emitter = builder.getEmitter();
|
QuadEmitter emitter = builder.getEmitter();
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
package fr.adrien1106.reframed.client.model;
|
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
|
||||||
import net.minecraft.client.render.model.BakedModel;
|
|
||||||
import net.minecraft.client.render.model.Baker;
|
|
||||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
|
||||||
import net.minecraft.client.render.model.UnbakedModel;
|
|
||||||
import net.minecraft.client.texture.Sprite;
|
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
|
||||||
import net.minecraft.util.Identifier;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class UnbakedDoubleRetexturedModel implements UnbakedModel {
|
|
||||||
|
|
||||||
protected final UnbakedModel model_1;
|
|
||||||
protected final UnbakedModel model_2;
|
|
||||||
|
|
||||||
public UnbakedDoubleRetexturedModel(UnbakedRetexturedModel model_1, UnbakedRetexturedModel model_2) {
|
|
||||||
this.model_1 = model_1;
|
|
||||||
this.model_2 = model_2;
|
|
||||||
model_2.setThemeIndex(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Identifier> getModelDependencies() {
|
|
||||||
return List.of(((List<Identifier>) model_1.getModelDependencies()).get(0), ((List<Identifier>) model_2.getModelDependencies()).get(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setParents(Function<Identifier, UnbakedModel> function) {
|
|
||||||
model_1.setParents(function);
|
|
||||||
model_2.setParents(function);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings model_bake_settings, Identifier identifier) {
|
|
||||||
return new DoubleRetexturingBakedModel(
|
|
||||||
(ForwardingBakedModel) model_1.bake(baker, texture_getter, model_bake_settings, identifier),
|
|
||||||
(ForwardingBakedModel) model_2.bake(baker, texture_getter, model_bake_settings, identifier)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,10 +8,7 @@ import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
|||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.MeshBuilder;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.client.render.model.BakedModel;
|
import net.minecraft.client.render.model.*;
|
||||||
import net.minecraft.client.render.model.BakedQuad;
|
|
||||||
import net.minecraft.client.render.model.Baker;
|
|
||||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
|
||||||
import net.minecraft.client.texture.Sprite;
|
import net.minecraft.client.texture.Sprite;
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
import net.minecraft.client.util.SpriteIdentifier;
|
||||||
import net.minecraft.screen.PlayerScreenHandler;
|
import net.minecraft.screen.PlayerScreenHandler;
|
||||||
@ -20,34 +17,61 @@ import net.minecraft.util.math.Direction;
|
|||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
|
public class UnbakedJsonRetexturedModel implements UnbakedModel {
|
||||||
public UnbakedJsonRetexturedModel(Identifier parent) {
|
public UnbakedJsonRetexturedModel(Identifier parent) {
|
||||||
super(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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings bake_settings, Identifier identifier) {
|
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
||||||
Direction[] directions = Direction.values();
|
Direction[] DIRECTIONS = RetexturingBakedModel.DIRECTIONS;
|
||||||
|
|
||||||
Sprite[] sprites = new Sprite[directions.length];
|
Sprite[] specialSprites = new Sprite[DIRECTIONS.length];
|
||||||
for(int i = 0; i < directions.length; i++) {
|
for(int i = 0; i < DIRECTIONS.length; i++) {
|
||||||
SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + directions[i].getName()));
|
SpriteIdentifier id = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, ReFramed.id("reframed_special/" + DIRECTIONS[i].getName()));
|
||||||
sprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
|
specialSprites[i] = Objects.requireNonNull(spriteLookup.apply(id), () -> "Couldn't find sprite " + id + " !");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BakedModel model = baker.bake(parent, modelBakeSettings);
|
||||||
|
|
||||||
return new RetexturingBakedModel(
|
return new RetexturingBakedModel(
|
||||||
baker.bake(parent, bake_settings),
|
model,
|
||||||
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
|
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
|
||||||
theme_index,
|
modelBakeSettings,
|
||||||
bake_settings,
|
itemModelState,
|
||||||
item_state,
|
|
||||||
ao
|
ao
|
||||||
) {
|
) {
|
||||||
protected Mesh convertModel(BlockState state) {
|
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) {
|
||||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||||
MeshBuilder builder = r.meshBuilder();
|
MeshBuilder builder = r.meshBuilder();
|
||||||
QuadEmitter emitter = builder.getEmitter();
|
QuadEmitter emitter = builder.getEmitter();
|
||||||
@ -60,9 +84,9 @@ public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
|
|||||||
emitter.fromVanilla(quad, mat, cullFace);
|
emitter.fromVanilla(quad, mat, cullFace);
|
||||||
|
|
||||||
QuadUvBounds bounds = QuadUvBounds.read(emitter);
|
QuadUvBounds bounds = QuadUvBounds.read(emitter);
|
||||||
for(int i = 0; i < sprites.length; i++) {
|
for(int i = 0; i < specialSprites.length; i++) {
|
||||||
if(bounds.displaysSprite(sprites[i])) {
|
if(bounds.displaysSprite(specialSprites[i])) {
|
||||||
bounds.normalizeUv(emitter, sprites[i]);
|
bounds.normalizeUv(emitter, specialSprites[i]);
|
||||||
emitter.tag(i + 1);
|
emitter.tag(i + 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -44,42 +44,37 @@ public class CamoAppearanceManager {
|
|||||||
materialsWithAo.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO
|
materialsWithAo.put(blend, finder.ambientOcclusion(TriState.DEFAULT).find()); //not "true" since that *forces* AO, i just want to *allow* AO
|
||||||
}
|
}
|
||||||
|
|
||||||
Sprite sprite = spriteLookup.apply(DEFAULT_SPRITE_MAIN);
|
Sprite defaultSprite = spriteLookup.apply(DEFAULT_SPRITE_ID);
|
||||||
if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
|
if(defaultSprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_ID + " !");
|
||||||
this.default_appearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
|
this.defaultAppearance = new SingleSpriteAppearance(defaultSprite, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.getAndIncrement());
|
||||||
|
|
||||||
sprite = spriteLookup.apply(DEFAULT_SPRITE_SECONDARY);
|
Sprite barrier = spriteLookup.apply(BARRIER_SPRITE_ID);
|
||||||
if(sprite == null) throw new IllegalStateException("Couldn't locate " + DEFAULT_SPRITE_MAIN + " !");
|
if(barrier == null) barrier = defaultSprite; //eh
|
||||||
this.accent_appearance = new SingleSpriteAppearance(sprite, materialsWithoutAo.get(BlendMode.CUTOUT), serial_number.getAndIncrement());
|
this.barrierItemAppearance = new SingleSpriteAppearance(barrier, materialsWithoutAo.get(BlendMode.CUTOUT), serialNumber.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_MAIN = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_block"));
|
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_SECONDARY = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier(ReFramed.MODID, "block/framed_accent_block"));
|
|
||||||
private static final SpriteIdentifier BARRIER_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:item/barrier"));
|
private static final SpriteIdentifier BARRIER_SPRITE_ID = new SpriteIdentifier(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE, new Identifier("minecraft:item/barrier"));
|
||||||
|
|
||||||
private final CamoAppearance default_appearance;
|
private final CamoAppearance defaultAppearance;
|
||||||
private final CamoAppearance accent_appearance;
|
|
||||||
private final CamoAppearance barrierItemAppearance;
|
private final CamoAppearance barrierItemAppearance;
|
||||||
|
|
||||||
private final ConcurrentHashMap<BlockState, CamoAppearance> appearanceCache = new ConcurrentHashMap<>(); //Mutable, append-only cache
|
private final ConcurrentHashMap<BlockState, CamoAppearance> appearanceCache = new ConcurrentHashMap<>(); //Mutable, append-only cache
|
||||||
private final AtomicInteger serial_number = new AtomicInteger(0); //Mutable
|
private final AtomicInteger serialNumber = new AtomicInteger(0); //Mutable
|
||||||
|
|
||||||
private final EnumMap<BlendMode, RenderMaterial> materialsWithAo = new EnumMap<>(BlendMode.class);
|
private final EnumMap<BlendMode, RenderMaterial> materialsWithAo = new EnumMap<>(BlendMode.class);
|
||||||
private final EnumMap<BlendMode, RenderMaterial> materialsWithoutAo = new EnumMap<>(BlendMode.class); //Immutable contents
|
private final EnumMap<BlendMode, RenderMaterial> materialsWithoutAo = new EnumMap<>(BlendMode.class); //Immutable contents
|
||||||
|
|
||||||
public CamoAppearance getDefaultAppearance(int appearance) {
|
public CamoAppearance getDefaultAppearance() {
|
||||||
return appearance == 2 ? accent_appearance: default_appearance;
|
return defaultAppearance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos, int theme_index) {
|
public CamoAppearance getCamoAppearance(BlockRenderView world, BlockState state, BlockPos pos) {
|
||||||
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
|
BakedModel model = MinecraftClient.getInstance().getBlockRenderManager().getModel(state);
|
||||||
|
|
||||||
// add support for connected textures and more generally any compatible models injected so that they return baked quads
|
// add support for connected textures and more generally any compatible models injected so that they return baked quads
|
||||||
if (model instanceof DynamicBakedModel dynamic_model) {
|
if (model instanceof DynamicBakedModel dynamic_model) {
|
||||||
return computeAppearance(dynamic_model.computeQuads(world, state, pos, theme_index), state);
|
return computeAppearance(dynamic_model.computeQuads(world, state, pos), state);
|
||||||
}
|
}
|
||||||
return appearanceCache.computeIfAbsent(state, block_state -> computeAppearance(model, block_state));
|
return appearanceCache.computeIfAbsent(state, block_state -> computeAppearance(model, block_state));
|
||||||
}
|
}
|
||||||
@ -101,7 +96,7 @@ public class CamoAppearanceManager {
|
|||||||
getAppearance(model),
|
getAppearance(model),
|
||||||
getCachedMaterial(state, true),
|
getCachedMaterial(state, true),
|
||||||
getCachedMaterial(state, false),
|
getCachedMaterial(state, false),
|
||||||
serial_number.getAndIncrement()
|
serialNumber.getAndIncrement()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
List<Weighted.Present<Appearance>> appearances = weighted_model.getModels().stream()
|
List<Weighted.Present<Appearance>> appearances = weighted_model.getModels().stream()
|
||||||
@ -112,7 +107,7 @@ public class CamoAppearanceManager {
|
|||||||
appearances,
|
appearances,
|
||||||
getCachedMaterial(state, true),
|
getCachedMaterial(state, true),
|
||||||
getCachedMaterial(state, false),
|
getCachedMaterial(state, false),
|
||||||
serial_number.getAndIncrement()
|
serialNumber.getAndIncrement()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +124,7 @@ public class CamoAppearanceManager {
|
|||||||
Arrays.stream(Direction.values()).forEach(direction -> {
|
Arrays.stream(Direction.values()).forEach(direction -> {
|
||||||
List<BakedQuad> quads = model.getQuads(null, direction, random);
|
List<BakedQuad> quads = model.getQuads(null, direction, random);
|
||||||
if(quads.isEmpty()) { // add default appearance if none present
|
if(quads.isEmpty()) { // add default appearance if none present
|
||||||
sprites.put(direction, default_appearance.getSprites(direction, 0));
|
sprites.put(direction, defaultAppearance.getSprites(direction, 0));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ public class Generator implements DataGeneratorEntrypoint {
|
|||||||
/**
|
/**
|
||||||
* missing DOOR, IRON_DOOR, CANDLE
|
* missing DOOR, IRON_DOOR, CANDLE
|
||||||
*/
|
*/
|
||||||
public static List<Block> BLOCKS = List.of(CUBE, STAIRS, DOUBLE_STAIRS, SLAB, DOUBLE_SLAB, POST, FENCE, FENCE_GATE, TRAPDOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE);
|
public static List<Block> BLOCKS = List.of(CUBE, STAIRS, SLAB, POST, FENCE, FENCE_GATE, TRAPDOOR, IRON_TRAPDOOR, PRESSURE_PLATE, BUTTON, LEVER, WALL, CARPET, PANE);
|
||||||
@Override
|
@Override
|
||||||
public void onInitializeDataGenerator(FabricDataGenerator data_generator) {
|
public void onInitializeDataGenerator(FabricDataGenerator data_generator) {
|
||||||
FabricDataGenerator.Pack myPack = data_generator.createPack();
|
FabricDataGenerator.Pack myPack = data_generator.createPack();
|
||||||
|
46
src/main/java/fr/adrien1106/reframed/mixin/BlockMixin.java
Normal file
46
src/main/java/fr/adrien1106/reframed/mixin/BlockMixin.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package fr.adrien1106.reframed.mixin;
|
||||||
|
|
||||||
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
|
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.util.function.BooleanBiFunction;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.Direction;
|
||||||
|
import net.minecraft.util.shape.VoxelShape;
|
||||||
|
import net.minecraft.util.shape.VoxelShapes;
|
||||||
|
import net.minecraft.world.BlockView;
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.*;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
|
@Mixin(Block.class)
|
||||||
|
public class BlockMixin {
|
||||||
|
|
||||||
|
@Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isOpaque()Z"))
|
||||||
|
private static boolean isNeighborCamoOpaque(BlockState state, @Local(argsOnly = true) BlockView world, @Local(ordinal = 1, argsOnly = true) BlockPos pos) {
|
||||||
|
BlockEntity block_entity = world.getBlockEntity(pos);
|
||||||
|
if (!(block_entity instanceof ReFramedEntity frame_entity)) return state.isOpaque();
|
||||||
|
return frame_entity.getThemeState().isOpaque();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isSideInvisible(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/Direction;)Z"))
|
||||||
|
private static boolean isCamoInvisible(BlockState state, BlockState other_state, Direction direction, @Local(argsOnly = true) BlockView world, @Local(ordinal = 0, argsOnly = true) BlockPos pos, @Local(ordinal = 1, argsOnly = true) BlockPos other_pos) {
|
||||||
|
if (world.getBlockEntity(other_pos) instanceof ReFramedEntity entity) other_state = entity.getThemeState();
|
||||||
|
if (world.getBlockEntity(pos) instanceof ReFramedEntity entity) state = entity.getThemeState();
|
||||||
|
return state.isSideInvisible(other_state, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Inject(method = "shouldDrawSide", at = @At(value = "RETURN", ordinal = 0), cancellable = true)
|
||||||
|
private static void shouldDrawGlassCamoSide(BlockState state, BlockView world, BlockPos pos, Direction side, BlockPos other_pos, CallbackInfoReturnable<Boolean> cir, @Local(ordinal = 1) BlockState neighbor) {
|
||||||
|
if (!(world.getBlockEntity(pos) instanceof ReFramedEntity) && !(world.getBlockEntity(other_pos) instanceof ReFramedEntity)) return;
|
||||||
|
VoxelShape voxel_block = state.getCullingFace(world, pos, side);
|
||||||
|
if (voxel_block.isEmpty()) {
|
||||||
|
cir.setReturnValue(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
VoxelShape voxel_neighbor = neighbor.getCullingFace(world, pos, side.getOpposite());
|
||||||
|
cir.setReturnValue(VoxelShapes.matchesAnywhere(voxel_block, voxel_neighbor, BooleanBiFunction.ONLY_FIRST));
|
||||||
|
}
|
||||||
|
}
|
@ -1,29 +1,25 @@
|
|||||||
package fr.adrien1106.reframed.mixin.render;
|
package fr.adrien1106.reframed.mixin;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
import com.llamalad7.mixinextras.sugar.Local;
|
||||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||||
import fr.adrien1106.reframed.util.IBlockRenderInfoMixin;
|
|
||||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
|
||||||
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
|
import net.fabricmc.fabric.impl.client.indigo.renderer.render.BlockRenderInfo;
|
||||||
|
import net.minecraft.block.Block;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.entity.BlockEntity;
|
import net.minecraft.block.entity.BlockEntity;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
import net.minecraft.world.BlockView;
|
|
||||||
import org.spongepowered.asm.mixin.Final;
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
import org.spongepowered.asm.mixin.Shadow;
|
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.At;
|
||||||
import org.spongepowered.asm.mixin.injection.Inject;
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
|
||||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
@Mixin(BlockRenderInfo.class)
|
@Mixin(BlockRenderInfo.class)
|
||||||
public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
|
public abstract class BlockRenderInfoMixin {
|
||||||
|
|
||||||
@Shadow public BlockPos blockPos;
|
@Shadow public BlockPos blockPos;
|
||||||
|
|
||||||
@ -33,38 +29,21 @@ public abstract class BlockRenderInfoMixin implements IBlockRenderInfoMixin {
|
|||||||
|
|
||||||
@Shadow @Final private BlockPos.Mutable searchPos;
|
@Shadow @Final private BlockPos.Mutable searchPos;
|
||||||
|
|
||||||
@Shadow public abstract void prepareForBlock(BlockState blockState, BlockPos blockPos, boolean modelAo);
|
|
||||||
|
|
||||||
@Unique
|
|
||||||
private int theme_index = 1;
|
|
||||||
|
|
||||||
@ModifyArg(method = "prepareForBlock",
|
@ModifyArg(method = "prepareForBlock",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/RenderLayers;" +
|
||||||
"getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;"))
|
"getBlockLayer(Lnet/minecraft/block/BlockState;)Lnet/minecraft/client/render/RenderLayer;"))
|
||||||
public BlockState prepareCamoLayer(BlockState state, @Local(argsOnly = true) BlockPos pos) {
|
public BlockState prepareCamoLayer(BlockState state, @Local(argsOnly = true) BlockPos pos) {
|
||||||
BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(pos);
|
BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(pos);
|
||||||
if (!(block_entity instanceof ThemeableBlockEntity frame_entity)) return state;
|
if (!(block_entity instanceof ReFramedEntity frame_entity)) return state;
|
||||||
return frame_entity.getTheme(theme_index);
|
return frame_entity.getThemeState();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "shouldDrawFace",
|
@Inject(method = "shouldDrawFace",
|
||||||
at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/util/math/Direction;getId()I"),
|
at = @At(value = "INVOKE_ASSIGN", target = "Lnet/minecraft/util/math/Direction;getId()I"),
|
||||||
cancellable = true)
|
cancellable = true)
|
||||||
private void shouldDrawCamoFace(Direction face, CallbackInfoReturnable<Boolean> cir) {
|
private void shouldDrawCamoFace(Direction face, CallbackInfoReturnable<Boolean> cir) {
|
||||||
// early injection for camos themselves
|
|
||||||
BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(blockPos);
|
BlockEntity block_entity = MinecraftClient.getInstance().world.getBlockEntity(blockPos);
|
||||||
if (!(block_entity instanceof ThemeableBlockEntity)) return;
|
if (!(block_entity instanceof ReFramedEntity)) return;
|
||||||
cir.setReturnValue(ReFramedBlock.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face), theme_index));
|
cir.setReturnValue(Block.shouldDrawSide(blockState, blockView, blockPos, face, searchPos.set(blockPos, face)));
|
||||||
}
|
|
||||||
|
|
||||||
@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 ReFramedBlock.shouldDrawSide(state, world, pos, side, other_pos, theme_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void prepareForBlock(BlockState state, BlockPos pos, boolean ao, int theme_index) {
|
|
||||||
this.theme_index = theme_index;
|
|
||||||
prepareForBlock(state, pos, ao);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -6,7 +6,6 @@ import earth.terrarium.athena.api.client.models.AthenaBlockModel;
|
|||||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||||
import fr.adrien1106.reframed.client.model.DynamicBakedModel;
|
import fr.adrien1106.reframed.client.model.DynamicBakedModel;
|
||||||
import fr.adrien1106.reframed.compat.RebakedAthenaModel;
|
import fr.adrien1106.reframed.compat.RebakedAthenaModel;
|
||||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.MutableQuadView;
|
||||||
@ -40,41 +39,35 @@ public abstract class AthenaBakedModelMixin implements DynamicBakedModel, BakedM
|
|||||||
* @return - the rebakedmodel containing the computed quads
|
* @return - the rebakedmodel containing the computed quads
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos, int theme_index) {
|
public BakedModel computeQuads(BlockRenderView level, BlockState state, BlockPos pos) {
|
||||||
|
if (level == null || pos == null) return this;
|
||||||
|
|
||||||
Map<Direction, List<BakedQuad>> face_quads = new HashMap<>();
|
Map<Direction, List<BakedQuad>> face_quads = new HashMap<>();
|
||||||
|
|
||||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||||
QuadEmitter emitter = r.meshBuilder().getEmitter();
|
QuadEmitter emitter = r.meshBuilder().getEmitter();
|
||||||
|
|
||||||
WrappedGetter getter = new WrappedGetter(level);
|
WrappedGetter getter = new WrappedGetter(level);
|
||||||
Arrays.stream(Direction.values()).forEach(direction -> {
|
Arrays.stream(Direction.values()).forEach(direction -> {
|
||||||
face_quads.put(direction, new ArrayList<>());
|
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());
|
||||||
|
|
||||||
(level == null || pos == null
|
int flag = MutableQuadView.BAKE_LOCK_UV;
|
||||||
? 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());
|
|
||||||
|
|
||||||
int flag = MutableQuadView.BAKE_LOCK_UV;
|
switch (sprite.rotation()) {
|
||||||
|
case CLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_90;
|
||||||
|
case CLOCKWISE_180 -> flag |= MutableQuadView.BAKE_ROTATE_180;
|
||||||
|
case COUNTERCLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_270;
|
||||||
|
}
|
||||||
|
|
||||||
switch (sprite.rotation()) {
|
emitter.spriteBake(texture, flag);
|
||||||
case CLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_90;
|
emitter.color(-1, -1, -1, -1);
|
||||||
case CLOCKWISE_180 -> flag |= MutableQuadView.BAKE_ROTATE_180;
|
quads.add(emitter.toBakedQuad(texture));
|
||||||
case COUNTERCLOCKWISE_90 -> flag |= MutableQuadView.BAKE_ROTATE_270;
|
return quads;
|
||||||
}
|
}));
|
||||||
|
|
||||||
emitter.spriteBake(texture, flag);
|
|
||||||
emitter.color(-1, -1, -1, -1);
|
|
||||||
quads.add(emitter.toBakedQuad(texture));
|
|
||||||
return quads;
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return new RebakedAthenaModel(face_quads);
|
return new RebakedAthenaModel(face_quads);
|
||||||
|
@ -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.AppearanceAndTintGetter;
|
||||||
import earth.terrarium.athena.api.client.utils.CtmUtils;
|
import earth.terrarium.athena.api.client.utils.CtmUtils;
|
||||||
import earth.terrarium.athena.impl.client.models.ConnectedBlockModel;
|
import earth.terrarium.athena.impl.client.models.ConnectedBlockModel;
|
||||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.util.math.Direction;
|
import net.minecraft.util.math.Direction;
|
||||||
@ -22,8 +22,8 @@ public class AthenaConnectedBlockModelMixin {
|
|||||||
"Lnet/minecraft/util/math/Direction;)Z"))
|
"Lnet/minecraft/util/math/Direction;)Z"))
|
||||||
private boolean checkForCull(AppearanceAndTintGetter level, BlockState state, BlockPos pos, Direction direction) {
|
private boolean checkForCull(AppearanceAndTintGetter level, BlockState state, BlockPos pos, Direction direction) {
|
||||||
// Always get all the textures unless its another block then use default behaviour
|
// Always get all the textures unless its another block then use default behaviour
|
||||||
if (level.getBlockEntity(pos) instanceof ThemeableBlockEntity
|
if (level.getBlockEntity(pos) instanceof ReFramedEntity
|
||||||
|| level.getBlockEntity(pos.offset(direction)) instanceof ThemeableBlockEntity)
|
|| level.getBlockEntity(pos.offset(direction)) instanceof ReFramedEntity)
|
||||||
return false;
|
return false;
|
||||||
return CtmUtils.checkRelative(level, state, pos, direction);
|
return CtmUtils.checkRelative(level, state, pos, direction);
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,45 @@
|
|||||||
package fr.adrien1106.reframed.mixin.compat;
|
package fr.adrien1106.reframed.mixin.compat;
|
||||||
|
|
||||||
import com.llamalad7.mixinextras.sugar.Local;
|
|
||||||
import earth.terrarium.athena.api.client.fabric.WrappedGetter;
|
import earth.terrarium.athena.api.client.fabric.WrappedGetter;
|
||||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.BlockRenderView;
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
import org.spongepowered.asm.mixin.Final;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
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.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.Redirect;
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||||
|
|
||||||
@Mixin(WrappedGetter.class)
|
@Mixin(WrappedGetter.class)
|
||||||
public class AthenaWrappedGetterMixin {
|
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",
|
@Redirect(method = "query",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/BlockRenderView;" +
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/world/BlockRenderView;" +
|
||||||
"getBlockState(Lnet/minecraft/util/math/BlockPos;)" +
|
"getBlockState(Lnet/minecraft/util/math/BlockPos;)" +
|
||||||
"Lnet/minecraft/block/BlockState;"))
|
"Lnet/minecraft/block/BlockState;"))
|
||||||
private BlockState queryCamoState(BlockRenderView world, BlockPos pos, @Local(argsOnly = true) BlockState reference_state) {
|
private BlockState queryCamoState(BlockRenderView world, BlockPos pos) {
|
||||||
// get Any that will connect or return any other (/!\ isOf is an uncertain check)
|
if (world.getBlockEntity(pos) instanceof ReFramedEntity framed_entity) return framed_entity.getThemeState();
|
||||||
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);
|
return world.getBlockState(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package fr.adrien1106.reframed.mixin.particles;
|
package fr.adrien1106.reframed.mixin.particles;
|
||||||
|
|
||||||
import fr.adrien1106.reframed.block.ReFramedBlock;
|
|
||||||
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.client.MinecraftClient;
|
import net.minecraft.client.MinecraftClient;
|
||||||
@ -21,11 +20,8 @@ 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) {
|
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;
|
AccessorParticle a = (AccessorParticle) this;
|
||||||
if(a.getRandom().nextBoolean()
|
if(a.getRandom().nextBoolean() && clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable) {
|
||||||
&& clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable
|
BlockState theme = themeable.getThemeState();
|
||||||
&& state.getBlock() instanceof ReFramedBlock block
|
|
||||||
) {
|
|
||||||
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
|
|
||||||
if(theme == null || theme.isAir()) return;
|
if(theme == null || theme.isAir()) return;
|
||||||
|
|
||||||
Sprite replacement = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(theme);
|
Sprite replacement = MinecraftClient.getInstance().getBlockRenderManager().getModels().getModelParticleSprite(theme);
|
||||||
|
@ -1,31 +1,31 @@
|
|||||||
package fr.adrien1106.reframed.mixin.particles;
|
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 fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
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.At;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
|
||||||
@Mixin(Entity.class)
|
@Mixin(Entity.class)
|
||||||
public abstract class MixinEntity {
|
public abstract class MixinEntity {
|
||||||
|
@Shadow @Deprecated public abstract BlockPos getLandingPos(); //TODO, somewhat expensive method
|
||||||
|
|
||||||
@ModifyArg(
|
@ModifyArg(
|
||||||
method = "spawnSprintingParticles",
|
method = "spawnSprintingParticles",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;<init>(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V")
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;<init>(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V")
|
||||||
)
|
)
|
||||||
private BlockState modifyParticleState(BlockState state, @Local(ordinal = 0) BlockPos landing_pos) {
|
private BlockState modifyParticleState(BlockState origState) {
|
||||||
World world = ((Entity) (Object) this).getWorld();
|
World world = ((Entity) (Object) this).getWorld();
|
||||||
|
|
||||||
if(world.getBlockEntity(landing_pos) instanceof ThemeableBlockEntity themeable
|
if(world.getBlockEntity(getLandingPos()) instanceof ThemeableBlockEntity themeable) {
|
||||||
&& state.getBlock() instanceof ReFramedBlock block) {
|
BlockState theme = themeable.getThemeState();
|
||||||
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
|
if(!theme.isAir()) return theme;
|
||||||
if(!theme.isAir()) return theme;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return origState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
package fr.adrien1106.reframed.mixin.particles;
|
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 fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.entity.Entity;
|
import net.minecraft.entity.Entity;
|
||||||
@ -9,25 +7,33 @@ import net.minecraft.entity.LivingEntity;
|
|||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
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.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject;
|
||||||
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
import org.spongepowered.asm.mixin.injection.ModifyArg;
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||||
|
|
||||||
@Mixin(LivingEntity.class)
|
@Mixin(LivingEntity.class)
|
||||||
public class MixinLivingEntity {
|
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(
|
@ModifyArg(
|
||||||
method = "fall",
|
method = "fall",
|
||||||
at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;<init>(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V")
|
at = @At(value = "INVOKE", target = "Lnet/minecraft/particle/BlockStateParticleEffect;<init>(Lnet/minecraft/particle/ParticleType;Lnet/minecraft/block/BlockState;)V")
|
||||||
)
|
)
|
||||||
private BlockState modifyParticleState(BlockState state, @Local(ordinal = 0, argsOnly = true) BlockPos land_pos) {
|
private BlockState modifyParticleState(BlockState origState) {
|
||||||
World world = ((Entity) (Object) this).getWorld();
|
World world = ((Entity) (Object) this).getWorld();
|
||||||
|
|
||||||
if(world.getBlockEntity(land_pos) instanceof ThemeableBlockEntity themeable
|
if(lastFallCheckPos != null && world.getBlockEntity(lastFallCheckPos) instanceof ThemeableBlockEntity themeable) {
|
||||||
&& state.getBlock() instanceof ReFramedBlock block) {
|
BlockState theme = themeable.getThemeState();
|
||||||
BlockState theme = themeable.getTheme(block.getTopThemeIndex(state));
|
if(!theme.isAir()) return theme;
|
||||||
if(!theme.isAir()) return theme;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return origState;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
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",
|
|
||||||
shift = At.Shift.AFTER
|
|
||||||
), 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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
package fr.adrien1106.reframed.util;
|
|
||||||
|
|
||||||
import net.minecraft.block.BlockState;
|
|
||||||
import net.minecraft.client.render.model.BakedModel;
|
|
||||||
|
|
||||||
public interface IMultipartBakedModelMixin {
|
|
||||||
|
|
||||||
BakedModel getModel(BlockState state);
|
|
||||||
}
|
|
@ -1,10 +1,19 @@
|
|||||||
package fr.adrien1106.reframed.util;
|
package fr.adrien1106.reframed.util;
|
||||||
|
|
||||||
import fr.adrien1106.reframed.block.ReFramedEntity;
|
import fr.adrien1106.reframed.block.ReFramedEntity;
|
||||||
import net.minecraft.block.*;
|
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.LivingEntity;
|
||||||
import net.minecraft.entity.player.PlayerEntity;
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
import net.minecraft.item.*;
|
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.nbt.NbtCompound;
|
||||||
import net.minecraft.sound.BlockSoundGroup;
|
import net.minecraft.sound.BlockSoundGroup;
|
||||||
import net.minecraft.sound.SoundCategory;
|
import net.minecraft.sound.SoundCategory;
|
||||||
@ -28,10 +37,8 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
//For an example of how to use this class, have a look at TemplateBlock.
|
//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.
|
//Basically there are several methods that would like to modify the return value of something.
|
||||||
public class ReFramedInteractionUtil {
|
public class ReFramedInteractionUtil {
|
||||||
@Deprecated // TODO remove
|
|
||||||
public static final BooleanProperty LIGHT = BooleanProperty.of("frame_light");
|
public static final BooleanProperty LIGHT = BooleanProperty.of("frame_light");
|
||||||
|
|
||||||
@Deprecated // TODO remove
|
|
||||||
public static StateManager.Builder<Block, BlockState> appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
public static StateManager.Builder<Block, BlockState> appendProperties(StateManager.Builder<Block, BlockState> builder) {
|
||||||
return builder.add(LIGHT);
|
return builder.add(LIGHT);
|
||||||
}
|
}
|
||||||
@ -39,92 +46,79 @@ public class ReFramedInteractionUtil {
|
|||||||
//Use this to obtain a Block.Settings that'll make your Template act like the ones in the mod.
|
//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.)
|
//(To complete the look, don't forget to tag your blocks with mineable/axe.)
|
||||||
private static final AbstractBlock.ContextPredicate NOPE = (blah, blahdey, blahh) -> false;
|
private static final AbstractBlock.ContextPredicate NOPE = (blah, blahdey, blahh) -> false;
|
||||||
|
|
||||||
// TODO Find better place
|
|
||||||
public static AbstractBlock.Settings configureSettings(AbstractBlock.Settings s) {
|
public static AbstractBlock.Settings configureSettings(AbstractBlock.Settings s) {
|
||||||
return s.luminance(ReFramedInteractionUtil::luminance).nonOpaque().sounds(BlockSoundGroup.WOOD).hardness(0.2f).suffocates(NOPE).blockVision(NOPE);
|
return s.luminance(ReFramedInteractionUtil::luminance).nonOpaque().sounds(BlockSoundGroup.WOOD).hardness(0.2f).suffocates(NOPE).blockVision(NOPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO Find better place
|
|
||||||
//And if you don't have a Block.Settings to copy off of.
|
//And if you don't have a Block.Settings to copy off of.
|
||||||
public static AbstractBlock.Settings makeSettings() {
|
public static AbstractBlock.Settings makeSettings() {
|
||||||
return configureSettings(AbstractBlock.Settings.create());
|
return configureSettings(AbstractBlock.Settings.create());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // TODO remove
|
|
||||||
public static BlockState setDefaultStates(BlockState in) {
|
public static BlockState setDefaultStates(BlockState in) {
|
||||||
if(in.contains(LIGHT)) in = in.with(LIGHT, false);
|
if(in.contains(LIGHT)) in = in.with(LIGHT, false);
|
||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // TODO remove
|
|
||||||
public static @Nullable BlockState modifyPlacementState(@Nullable BlockState in, ItemPlacementContext ctx) {
|
public static @Nullable BlockState modifyPlacementState(@Nullable BlockState in, ItemPlacementContext ctx) {
|
||||||
return ReFramedEntity.getNbtLightLevel(in, ctx.getStack());
|
return ReFramedEntity.weirdNbtLightLevelStuff(in, ctx.getStack());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // TODO remove
|
|
||||||
public static ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
public static ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||||
if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) return ActionResult.PASS;
|
if(!(world.getBlockEntity(pos) instanceof ReFramedEntity be)) return ActionResult.PASS;
|
||||||
if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS;
|
if(!player.canModifyBlocks() || !world.canPlayerModifyAt(player, pos)) return ActionResult.PASS;
|
||||||
|
|
||||||
ItemStack held = player.getStackInHand(hand);
|
ItemStack held = player.getStackInHand(hand);
|
||||||
ReframedInteractible ext = state.getBlock() instanceof ReframedInteractible e ? e : ReframedInteractible.Default.INSTANCE;
|
ReframedInteractible ext = state.getBlock() instanceof ReframedInteractible e ? e : ReframedInteractible.Default.INSTANCE;
|
||||||
|
|
||||||
// frame will emit light if applied with glowstone
|
//Glowstone
|
||||||
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST) {
|
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST && !state.get(LIGHT) && !be.hasSpentGlowstoneDust()) {
|
||||||
block_entity.toggleLight();
|
world.setBlockState(pos, state.with(LIGHT, true));
|
||||||
world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight()));
|
be.spentGlowstoneDust();
|
||||||
|
|
||||||
if(!player.isCreative())
|
if(!player.isCreative()) held.decrement(1);
|
||||||
if (block_entity.emitsLight()) held.decrement(1);
|
|
||||||
else held.increment(1);
|
|
||||||
world.playSound(player, pos, SoundEvents.BLOCK_GLASS_HIT, SoundCategory.BLOCKS, 1f, 1f);
|
world.playSound(player, pos, SoundEvents.BLOCK_GLASS_HIT, SoundCategory.BLOCKS, 1f, 1f);
|
||||||
return ActionResult.SUCCESS;
|
return ActionResult.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// frame will emit redstone if applied with redstone torch can deactivate redstone block camo emission
|
//Redstone
|
||||||
if(held.getItem() == Items.REDSTONE_TORCH && ext.canAddRedstoneEmission(state, world, pos)) {
|
if(held.getItem() == Blocks.REDSTONE_TORCH.asItem() &&
|
||||||
block_entity.toggleRedstone();
|
!be.emitsRedstone() &&
|
||||||
|
!be.hasSpentRedstoneTorch() &&
|
||||||
|
ext.canAddRedstoneEmission(state, world, pos)
|
||||||
|
) {
|
||||||
|
be.setEmitsRedstone(true);
|
||||||
|
be.spentRedstoneTorch();
|
||||||
|
|
||||||
if(!player.isCreative())
|
if(!player.isCreative()) held.decrement(1);
|
||||||
if (block_entity.emitsRedstone()) held.decrement(1);
|
|
||||||
else held.increment(1);
|
|
||||||
world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f);
|
world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f);
|
||||||
return ActionResult.SUCCESS;
|
return ActionResult.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frame will lose its collision if applied with popped chorus fruit
|
//Popped chorus fruit
|
||||||
if(held.getItem() == Items.POPPED_CHORUS_FRUIT && ext.canRemoveCollision(state, world, pos)) {
|
if(held.getItem() == Items.POPPED_CHORUS_FRUIT &&
|
||||||
block_entity.toggleSolidity();
|
be.isSolid() &&
|
||||||
|
!be.hasSpentPoppedChorus() &&
|
||||||
|
ext.canRemoveCollision(state, world, pos)
|
||||||
|
) {
|
||||||
|
be.setSolidity(false);
|
||||||
|
be.spentPoppedChorus();
|
||||||
|
|
||||||
if(!player.isCreative())
|
if(!player.isCreative()) held.decrement(1);
|
||||||
if (!block_entity.isSolid()) held.decrement(1);
|
|
||||||
else held.increment(1);
|
|
||||||
world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f);
|
world.playSound(player, pos, SoundEvents.ITEM_CHORUS_FRUIT_TELEPORT, SoundCategory.BLOCKS, 1f, 1f);
|
||||||
return ActionResult.SUCCESS;
|
return ActionResult.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Changing the theme
|
//Changing the theme
|
||||||
if(held.getItem() instanceof BlockItem block_item && block_entity.getTheme(1).getBlock() == Blocks.AIR) {
|
if(held.getItem() instanceof BlockItem bi && be.getThemeState().getBlock() == Blocks.AIR) {
|
||||||
Block block = block_item.getBlock();
|
Block block = bi.getBlock();
|
||||||
ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit));
|
ItemPlacementContext ctx = new ItemPlacementContext(new ItemUsageContext(player, hand, hit));
|
||||||
BlockState placementState = block.getPlacementState(ctx);
|
BlockState placementState = block.getPlacementState(ctx);
|
||||||
if(placementState != null && Block.isShapeFullCube(placementState.getCollisionShape(world, pos)) && !(block instanceof BlockEntityProvider)) {
|
if(placementState != null && Block.isShapeFullCube(placementState.getCollisionShape(world, pos)) && !(block instanceof BlockEntityProvider)) {
|
||||||
// TODO FOR SECOND
|
if(!world.isClient) be.setRenderedState(placementState);
|
||||||
if(!world.isClient) block_entity.setTheme(placementState, 1);
|
|
||||||
|
|
||||||
// check for default light emission
|
world.setBlockState(pos, state.with(LIGHT, be.hasSpentGlowstoneDust() || (placementState.getLuminance() != 0)));
|
||||||
if (placementState.getLuminance() > 0)
|
be.setEmitsRedstone(be.hasSpentRedstoneTorch() || placementState.getWeakRedstonePower(world, pos, Direction.NORTH) != 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 redstone emission
|
|
||||||
if (placementState.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);
|
if(!player.isCreative()) held.decrement(1);
|
||||||
world.playSound(player, pos, placementState.getSoundGroup().getPlaceSound(), SoundCategory.BLOCKS, 1f, 1.1f);
|
world.playSound(player, pos, placementState.getSoundGroup().getPlaceSound(), SoundCategory.BLOCKS, 1f, 1.1f);
|
||||||
@ -135,29 +129,26 @@ public class ReFramedInteractionUtil {
|
|||||||
return ActionResult.PASS;
|
return ActionResult.PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
//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) {
|
public static void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
||||||
if(!state.isOf(newState.getBlock()) &&
|
if(!state.isOf(newState.getBlock()) &&
|
||||||
world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity &&
|
world.getBlockEntity(pos) instanceof ReFramedEntity frame &&
|
||||||
world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
|
world.getGameRules().getBoolean(GameRules.DO_TILE_DROPS)
|
||||||
) {
|
) {
|
||||||
DefaultedList<ItemStack> drops = DefaultedList.of();
|
DefaultedList<ItemStack> drops = DefaultedList.of();
|
||||||
|
|
||||||
BlockState theme = frame_entity.getTheme(1);
|
//TODO: remember the specific ItemStack
|
||||||
if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock()));
|
Block theme = frame.getThemeState().getBlock();
|
||||||
|
if(theme != Blocks.AIR) drops.add(new ItemStack(theme));
|
||||||
|
|
||||||
if(frame_entity.emitsRedstone() && theme.getWeakRedstonePower(world, pos, Direction.NORTH) == 0)
|
if(frame.hasSpentRedstoneTorch()) drops.add(new ItemStack(Items.REDSTONE_TORCH));
|
||||||
drops.add(new ItemStack(Items.REDSTONE_TORCH));
|
if(frame.hasSpentGlowstoneDust()) drops.add(new ItemStack(Items.GLOWSTONE_DUST));
|
||||||
if(frame_entity.emitsLight() && theme.getLuminance() == 0)
|
if(frame.hasSpentPoppedChorus()) drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
|
||||||
drops.add(new ItemStack(Items.GLOWSTONE_DUST));
|
|
||||||
if(!frame_entity.isSolid() && theme.isSolid())
|
|
||||||
drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
|
|
||||||
|
|
||||||
ItemScatterer.spawn(world, pos, drops);
|
ItemScatterer.spawn(world, pos, drops);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public static void onPlaced(World world, BlockPos pos, BlockState state, @Nullable LivingEntity placer, ItemStack stack) {
|
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.
|
//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.
|
//I'm surprised this doesn't happen by default; the BlockEntityTag stuff is only done serverside.
|
||||||
@ -167,24 +158,20 @@ public class ReFramedInteractionUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
//Returns "null" to signal "no opinion". Imagine it like an InteractionResult.PASS.
|
//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) {
|
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;
|
return view.getBlockEntity(pos) instanceof ReFramedEntity be && !be.isSolid() ? VoxelShapes.empty() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // TODO remove
|
|
||||||
public static boolean emitsRedstonePower(BlockState state) {
|
public static boolean emitsRedstonePower(BlockState state) {
|
||||||
//return state.contains(REDSTONE) ? state.get(REDSTONE) : false;
|
//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.
|
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.
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // TODO remove
|
|
||||||
public static int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
public static int getWeakRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||||
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
|
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // TODO remove
|
|
||||||
public static int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
public static int getStrongRedstonePower(BlockState state, BlockView view, BlockPos pos, Direction dir) {
|
||||||
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
|
return view.getBlockEntity(pos) instanceof ReFramedEntity be && be.emitsRedstone() ? 15 : 0;
|
||||||
}
|
}
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
package fr.adrien1106.reframed.util;
|
package fr.adrien1106.reframed.util;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachmentBlockEntity;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
|
|
||||||
import java.util.List;
|
public interface ThemeableBlockEntity extends RenderAttachmentBlockEntity {
|
||||||
|
default BlockState getThemeState() {
|
||||||
public interface ThemeableBlockEntity {
|
return (BlockState) getRenderAttachmentData();
|
||||||
BlockState getTheme(int i);
|
}
|
||||||
|
|
||||||
void setTheme(BlockState state, int i);
|
|
||||||
|
|
||||||
List<BlockState> getThemes();
|
|
||||||
}
|
}
|
||||||
|
65
src/main/resources/assets/reframed/blockstates/slab.json
Normal file
65
src/main/resources/assets/reframed/blockstates/slab.json
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,51 +0,0 @@
|
|||||||
{
|
|
||||||
"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"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"credit": "Made with Blockbench",
|
|
||||||
"textures": {
|
|
||||||
"particle": "#side"
|
|
||||||
},
|
|
||||||
"elements": [
|
|
||||||
{
|
|
||||||
"from": [0, 8, 0],
|
|
||||||
"to": [8, 16, 8],
|
|
||||||
"faces": {
|
|
||||||
"north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"},
|
|
||||||
"east": {"uv": [8, 0, 16, 8], "texture": "#side"},
|
|
||||||
"south": {"uv": [0, 0, 8, 8], "texture": "#side"},
|
|
||||||
"west": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "west"},
|
|
||||||
"up": {"uv": [0, 0, 8, 8], "texture": "#top", "cullface": "up"},
|
|
||||||
"down": {"uv": [0, 8, 8, 16], "texture": "#bottom"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
{
|
|
||||||
"credit": "Made with Blockbench",
|
|
||||||
"textures": {
|
|
||||||
"particle": "#side"
|
|
||||||
},
|
|
||||||
"elements": [
|
|
||||||
{
|
|
||||||
"from": [0, 8, 0],
|
|
||||||
"to": [8, 16, 8],
|
|
||||||
"faces": {
|
|
||||||
"north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"},
|
|
||||||
"east": {"uv": [8, 0, 16, 8], "texture": "#side"},
|
|
||||||
"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"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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": [0, 8, 8, 16], "texture": "#bottom", "cullface": "down"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"groups": [
|
|
||||||
{
|
|
||||||
"name": "outer_stairs",
|
|
||||||
"origin": [8, 8, 8],
|
|
||||||
"color": 0,
|
|
||||||
"children": [0, 1, 2]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
"credit": "Made with Blockbench",
|
|
||||||
"textures": {
|
|
||||||
"particle": "#side"
|
|
||||||
},
|
|
||||||
"elements": [
|
|
||||||
{
|
|
||||||
"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"}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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": [9, 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, 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"},
|
|
||||||
"down": {"uv": [0, 8, 8, 16], "texture": "#bottom"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
{
|
|
||||||
"credit": "Made with Blockbench",
|
|
||||||
"parent": "block/block",
|
|
||||||
"textures": {
|
|
||||||
"particle": "#side"
|
|
||||||
},
|
|
||||||
"elements": [
|
|
||||||
{
|
|
||||||
"from": [0, 8, 0],
|
|
||||||
"to": [8, 16, 16],
|
|
||||||
"faces": {
|
|
||||||
"north": {"uv": [8, 0, 16, 8], "texture": "#side", "cullface": "north"},
|
|
||||||
"east": {"uv": [0, 0, 16, 8], "texture": "#side"},
|
|
||||||
"south": {"uv": [0, 0, 8, 8], "texture": "#side", "cullface": "south"},
|
|
||||||
"west": {"uv": [0, 8, 16, 16], "texture": "#side", "cullface": "west"},
|
|
||||||
"up": {"uv": [0, 0, 8, 16], "texture": "#top", "cullface": "up"},
|
|
||||||
"down": {"uv": [0, 0, 8, 16], "texture": "#bottom"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"display": {
|
|
||||||
"thirdperson_lefthand": {
|
|
||||||
"rotation": [75, -135, 0],
|
|
||||||
"translation": [0, 2.5, 0],
|
|
||||||
"scale": [0.375, 0.375, 0.375]
|
|
||||||
},
|
|
||||||
"gui": {
|
|
||||||
"rotation": [30, 135, 0],
|
|
||||||
"scale": [0.625, 0.625, 0.625]
|
|
||||||
},
|
|
||||||
"head": {
|
|
||||||
"rotation": [0, -90, 0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,18 +9,16 @@
|
|||||||
"particles.MixinLivingEntity"
|
"particles.MixinLivingEntity"
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
|
"BlockMixin",
|
||||||
|
"BlockRenderInfoMixin",
|
||||||
"MinecraftAccessor",
|
"MinecraftAccessor",
|
||||||
"compat.AthenaBakedModelMixin",
|
"compat.AthenaBakedModelMixin",
|
||||||
"compat.AthenaConnectedBlockModelMixin",
|
|
||||||
"compat.AthenaWrappedGetterMixin",
|
"compat.AthenaWrappedGetterMixin",
|
||||||
|
"compat.AthenaConnectedBlockModelMixin",
|
||||||
"model.WeightedBakedModelAccessor",
|
"model.WeightedBakedModelAccessor",
|
||||||
"particles.AccessorParticle",
|
"particles.AccessorParticle",
|
||||||
"particles.AccessorSpriteBillboardParticle",
|
"particles.AccessorSpriteBillboardParticle",
|
||||||
"particles.MixinBlockDustParticle",
|
"particles.MixinBlockDustParticle"
|
||||||
"render.BlockRenderInfoMixin",
|
|
||||||
"render.MultipartBakedModelMixin",
|
|
||||||
"render.TerrainRenderContextMixin",
|
|
||||||
"render.WorldRendererMixin"
|
|
||||||
],
|
],
|
||||||
"injectors": {
|
"injectors": {
|
||||||
"defaultRequire": 1
|
"defaultRequire": 1
|
||||||
|
Loading…
Reference in New Issue
Block a user