Double frames #6
@ -93,8 +93,8 @@ dependencies {
|
|||||||
modRuntimeOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
|
modRuntimeOnly "earth.terrarium.athena:athena-fabric-${project.minecraft_version}:${project.athena_version}"
|
||||||
|
|
||||||
// Chipped to test athena implementation
|
// 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"
|
||||||
|
|
||||||
// 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}"
|
||||||
|
@ -0,0 +1,53 @@
|
|||||||
|
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.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 getSecondTheme() {
|
||||||
|
return second_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSecondTheme(BlockState newState) {
|
||||||
|
if(!Objects.equals(second_state, newState)) {
|
||||||
|
second_state = newState;
|
||||||
|
markDirtyAndDispatch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readNbt(NbtCompound nbt) {
|
||||||
|
super.readNbt(nbt);
|
||||||
|
|
||||||
|
BlockState rendered_state = second_state;// keep previous state to check if rerender is needed
|
||||||
|
first_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));
|
||||||
|
}
|
||||||
|
}
|
@ -27,19 +27,13 @@ import java.util.Objects;
|
|||||||
//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 renderedState = Blocks.AIR.getDefaultState();
|
protected BlockState first_state = Blocks.AIR.getDefaultState();
|
||||||
protected byte bitfield = DEFAULT_BITFIELD;
|
protected byte bitfield = SOLIDITY_MASK;
|
||||||
|
|
||||||
protected static final int SPENT_GLOWSTONE_DUST_MASK = 0b00000001;
|
protected static final byte LIGHT_MASK = 0b001;
|
||||||
protected static final int SPENT_REDSTONE_TORCH_MASK = 0b00000010;
|
protected static final byte REDSTONE_MASK = 0b010;
|
||||||
protected static final int SPENT_POPPED_CHORUS_MASK = 0b00000100;
|
protected static final byte SOLIDITY_MASK = 0b100;
|
||||||
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";
|
||||||
|
|
||||||
@ -48,50 +42,38 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readNbt(NbtCompound tag) {
|
public void readNbt(NbtCompound nbt) {
|
||||||
super.readNbt(tag);
|
super.readNbt(nbt);
|
||||||
|
|
||||||
BlockState lastRenderedState = renderedState;
|
BlockState rendered_state = first_state; // keep previous state to check if rerender is needed
|
||||||
|
first_state = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), nbt.getCompound(BLOCKSTATE_KEY + 1));
|
||||||
|
if (nbt.contains(BITFIELD_KEY)) bitfield = nbt.getByte(BITFIELD_KEY);
|
||||||
|
|
||||||
if(tag.contains("BlockState")) { //2.0.4 and earlier
|
// Force a chunk remesh on the client if the displayed blockstate has changed
|
||||||
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound("BlockState"));
|
if(world != null && world.isClient && !Objects.equals(rendered_state, first_state)) {
|
||||||
|
|
||||||
if(tag.getBoolean("spentglow")) spentGlowstoneDust();
|
|
||||||
if(tag.getBoolean("spentredst")) spentRedstoneTorch();
|
|
||||||
if(tag.getBoolean("spentchor")) spentPoppedChorus();
|
|
||||||
setEmitsRedstone(tag.getBoolean("emitsredst"));
|
|
||||||
setSolidity(!tag.contains("solid") || tag.getBoolean("solid")); //default to "true" if it's nonexistent
|
|
||||||
} else {
|
|
||||||
renderedState = NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), tag.getCompound(BLOCKSTATE_KEY));
|
|
||||||
bitfield = tag.contains(BITFIELD_KEY) ? tag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Force a chunk remesh on the client if the displayed blockstate has changed
|
|
||||||
if(world != null && world.isClient && !Objects.equals(lastRenderedState, renderedState)) {
|
|
||||||
ReFramed.chunkRerenderProxy.accept(world, pos);
|
ReFramed.chunkRerenderProxy.accept(world, pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeNbt(NbtCompound tag) {
|
public void writeNbt(NbtCompound nbt) {
|
||||||
super.writeNbt(tag);
|
super.writeNbt(nbt);
|
||||||
|
|
||||||
if(renderedState != Blocks.AIR.getDefaultState()) tag.put(BLOCKSTATE_KEY, NbtHelper.fromBlockState(renderedState));
|
if(first_state != Blocks.AIR.getDefaultState()) nbt.put(BLOCKSTATE_KEY + 1, NbtHelper.fromBlockState(first_state));
|
||||||
if(bitfield != DEFAULT_BITFIELD) tag.putByte(BITFIELD_KEY, bitfield);
|
if(bitfield != SOLIDITY_MASK) nbt.putByte(BITFIELD_KEY, bitfield);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO revisit
|
||||||
public static @NotNull BlockState readStateFromItem(ItemStack stack) {
|
public static @NotNull BlockState readStateFromItem(ItemStack stack) {
|
||||||
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
|
NbtCompound blockEntityTag = BlockItem.getBlockEntityNbt(stack);
|
||||||
if(blockEntityTag == 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 subElement;
|
NbtElement subElement;
|
||||||
if(blockEntityTag.contains(BLOCKSTATE_KEY)) subElement = blockEntityTag.get(BLOCKSTATE_KEY); //2.0.5
|
if(blockEntityTag.contains(BLOCKSTATE_KEY)) subElement = blockEntityTag.get(BLOCKSTATE_KEY + 1);
|
||||||
else if(blockEntityTag.contains("BlockState")) subElement = blockEntityTag.get("BlockState"); //old 2.0.4 items
|
|
||||||
else return Blocks.AIR.getDefaultState();
|
else return Blocks.AIR.getDefaultState();
|
||||||
|
|
||||||
if(!(subElement instanceof NbtCompound subCompound)) return Blocks.AIR.getDefaultState();
|
if(!(subElement instanceof NbtCompound subCompound)) return Blocks.AIR.getDefaultState();
|
||||||
|
|
||||||
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), subCompound);
|
else return NbtHelper.toBlockState(Registries.BLOCK.getReadOnlyWrapper(), subCompound);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,91 +84,65 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
public static @Nullable BlockState weirdNbtLightLevelStuff(@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 blockEntityTag = BlockItem.getBlockEntityNbt(stack);
|
NbtCompound nbt = BlockItem.getBlockEntityNbt(stack);
|
||||||
if(blockEntityTag == null) return state;
|
if(nbt == null) return state;
|
||||||
|
|
||||||
if(state.contains(ReFramedInteractionUtil.LIGHT)) {
|
if(state.contains(ReFramedInteractionUtil.LIGHT)) {
|
||||||
state = state.with(ReFramedInteractionUtil.LIGHT,
|
state = state.with(ReFramedInteractionUtil.LIGHT,
|
||||||
blockEntityTag.getBoolean("spentglow") || //2.0.4
|
((nbt.contains(BITFIELD_KEY)
|
||||||
((blockEntityTag.contains(BITFIELD_KEY) ? blockEntityTag.getByte(BITFIELD_KEY) : DEFAULT_BITFIELD) & SPENT_GLOWSTONE_DUST_MASK) != 0 || //2.0.5
|
? nbt.getByte(BITFIELD_KEY)
|
||||||
readStateFromItem(stack).getLuminance() != 0 //glowstone dust wasn't manually added, the block just emits light
|
: SOLIDITY_MASK)
|
||||||
|
& LIGHT_MASK) != 0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
//RenderAttachmentBlockEntity impl. Note that ThemeableBlockEntity depends on this returning a BlockState object.
|
|
||||||
@Override
|
@Override
|
||||||
public BlockState getRenderAttachmentData() {
|
public BlockState getFirstTheme() {
|
||||||
return renderedState;
|
return first_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRenderedState(BlockState newState) {
|
public void setFirstTheme(BlockState newState) {
|
||||||
if(!Objects.equals(renderedState, newState)) {
|
if(!Objects.equals(first_state, newState)) {
|
||||||
renderedState = newState;
|
first_state = newState;
|
||||||
markDirtyAndDispatch();
|
markDirtyAndDispatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSpentGlowstoneDust() {
|
/* --------------------------------------------------- ADDONS --------------------------------------------------- */
|
||||||
return (bitfield & SPENT_GLOWSTONE_DUST_MASK) != 0;
|
public boolean emitsLight() {
|
||||||
|
return (bitfield & LIGHT_MASK) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spentGlowstoneDust() {
|
public void toggleLight() {
|
||||||
bitfield |= SPENT_GLOWSTONE_DUST_MASK;
|
bitfield |= emitsLight() ? ~LIGHT_MASK: LIGHT_MASK;
|
||||||
markDirtyAndDispatch();
|
markDirtyAndDispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSpentRedstoneTorch() {
|
public void toggleRedstone() {
|
||||||
return (bitfield & SPENT_REDSTONE_TORCH_MASK) != 0;
|
bitfield |= emitsRedstone() ? ~REDSTONE_MASK: REDSTONE_MASK;
|
||||||
}
|
|
||||||
|
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
|
||||||
public void spentRedstoneTorch() {
|
|
||||||
bitfield |= SPENT_REDSTONE_TORCH_MASK;
|
|
||||||
markDirtyAndDispatch();
|
markDirtyAndDispatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasSpentPoppedChorus() {
|
|
||||||
return (bitfield & SPENT_POPPED_CHORUS_MASK) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void spentPoppedChorus() {
|
|
||||||
bitfield |= SPENT_POPPED_CHORUS_MASK;
|
|
||||||
markDirtyAndDispatch();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean emitsRedstone() {
|
public boolean emitsRedstone() {
|
||||||
return (bitfield & EMITS_REDSTONE_MASK) != 0;
|
return (bitfield & REDSTONE_MASK) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEmitsRedstone(boolean nextEmitsRedstone) {
|
public void toggleSolidity() {
|
||||||
boolean currentlyEmitsRedstone = emitsRedstone();
|
bitfield |= isSolid() ? ~SOLIDITY_MASK: SOLIDITY_MASK;
|
||||||
|
|
||||||
if(currentlyEmitsRedstone != nextEmitsRedstone) {
|
if(world != null) world.setBlockState(pos, getCachedState());
|
||||||
if(currentlyEmitsRedstone) bitfield &= ~EMITS_REDSTONE_MASK;
|
markDirtyAndDispatch();
|
||||||
else bitfield |= EMITS_REDSTONE_MASK;
|
|
||||||
markDirtyAndDispatch();
|
|
||||||
if(world != null) world.updateNeighbors(pos, getCachedState().getBlock());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSolid() {
|
public boolean isSolid() {
|
||||||
return (bitfield & IS_SOLID_MASK) != 0;
|
return (bitfield & SOLIDITY_MASK) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSolidity(boolean nextSolid) {
|
|
||||||
boolean currentlySolid = isSolid();
|
|
||||||
|
|
||||||
if(currentlySolid != nextSolid) {
|
|
||||||
if(currentlySolid) bitfield &= ~IS_SOLID_MASK;
|
|
||||||
else bitfield |= IS_SOLID_MASK;
|
|
||||||
markDirtyAndDispatch();
|
|
||||||
if(world != null) world.setBlockState(pos, getCachedState()); //do i need to invalidate any shape caches or something
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//<standard blockentity boilerplate>
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
public Packet<ClientPlayPacketListener> toUpdatePacket() {
|
||||||
@ -195,8 +151,6 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NbtCompound toInitialChunkDataNbt() {
|
public NbtCompound toInitialChunkDataNbt() {
|
||||||
//TERRIBLE yarn name, this is "getUpdateTag", it's the nbt that will be sent to clients
|
|
||||||
//and it just calls "writeNbt"
|
|
||||||
return createNbt();
|
return createNbt();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -208,5 +162,4 @@ public class ReFramedEntity extends BlockEntity implements ThemeableBlockEntity
|
|||||||
markDirty();
|
markDirty();
|
||||||
dispatch();
|
dispatch();
|
||||||
}
|
}
|
||||||
//</standard blockentity boilerplate>
|
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,11 @@ package fr.adrien1106.reframed.client;
|
|||||||
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.UnbakedAutoRetexturedModel;
|
||||||
import fr.adrien1106.reframed.client.model.UnbakedJsonRetexturedModel;
|
import fr.adrien1106.reframed.client.model.UnbakedJsonRetexturedModel;
|
||||||
|
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||||
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;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.client.render.model.UnbakedModel;
|
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;
|
||||||
@ -24,11 +26,19 @@ public class ReFramedClientHelper {
|
|||||||
private final ReFramedModelProvider prov;
|
private final ReFramedModelProvider prov;
|
||||||
|
|
||||||
public UnbakedModel auto(Identifier parent) {
|
public UnbakedModel auto(Identifier parent) {
|
||||||
return new UnbakedAutoRetexturedModel(parent);
|
return auto(parent, ThemeableBlockEntity::getFirstTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnbakedModel auto(Identifier parent, Function<ThemeableBlockEntity, BlockState> state_getter) {
|
||||||
|
return new UnbakedAutoRetexturedModel(parent, state_getter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public UnbakedModel json(Identifier parent) {
|
public UnbakedModel json(Identifier parent) {
|
||||||
return new UnbakedJsonRetexturedModel(parent);
|
return json(parent, ThemeableBlockEntity::getFirstTheme);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnbakedModel json(Identifier parent, Function<ThemeableBlockEntity, BlockState> state_getter) {
|
||||||
|
return new UnbakedJsonRetexturedModel(parent, state_getter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addReFramedModel(Identifier id, UnbakedModel unbaked) {
|
public void addReFramedModel(Identifier id, UnbakedModel unbaked) {
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package fr.adrien1106.reframed.client.model;
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.model.ForwardingBakedModel;
|
||||||
|
import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
|
||||||
|
import net.minecraft.block.BlockState;
|
||||||
|
import net.minecraft.client.texture.Sprite;
|
||||||
|
import net.minecraft.item.ItemStack;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.random.Random;
|
||||||
|
import net.minecraft.world.BlockRenderView;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
public class DoubleRetexturingBakedModel extends ForwardingBakedModel {
|
||||||
|
|
||||||
|
private final ForwardingBakedModel model_1, model_2;
|
||||||
|
public DoubleRetexturingBakedModel(ForwardingBakedModel model_1, ForwardingBakedModel model_2) {
|
||||||
|
this.model_1 = model_1;
|
||||||
|
this.model_2 = model_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVanillaAdapter() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Sprite getParticleSprite() {
|
||||||
|
return model_1.getParticleSprite(); // TODO determine which face is on top
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
|
model_1.emitBlockQuads(world, state, pos, randomSupplier, context);
|
||||||
|
model_2.emitBlockQuads(world, state, pos, randomSupplier, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emitItemQuads(ItemStack stack, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
|
model_1.emitItemQuads(stack, randomSupplier, context);
|
||||||
|
model_2.emitItemQuads(stack, randomSupplier, context);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ 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.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;
|
||||||
@ -26,19 +27,22 @@ import net.minecraft.world.BlockRenderView;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
||||||
public RetexturingBakedModel(BakedModel baseModel, CamoAppearanceManager tam, ModelBakeSettings settings, BlockState itemModelState, boolean ao) {
|
public RetexturingBakedModel(BakedModel baseModel, CamoAppearanceManager tam, Function<ThemeableBlockEntity, BlockState> state_getter, ModelBakeSettings settings, BlockState itemModelState, boolean ao) {
|
||||||
this.wrapped = baseModel; //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.state_getter = state_getter;
|
||||||
this.uvlock = settings.isUvLocked();
|
this.uvlock = settings.isUvLocked();
|
||||||
this.itemModelState = itemModelState;
|
this.itemModelState = itemModelState;
|
||||||
this.ao = ao;
|
this.ao = ao;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final CamoAppearanceManager tam;
|
protected final CamoAppearanceManager tam;
|
||||||
|
protected final Function<ThemeableBlockEntity, BlockState> state_getter;
|
||||||
protected final boolean uvlock;
|
protected final boolean uvlock;
|
||||||
protected final BlockState itemModelState;
|
protected final BlockState itemModelState;
|
||||||
protected final boolean ao;
|
protected final boolean ao;
|
||||||
@ -51,8 +55,17 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
protected static final Direction[] DIRECTIONS = Direction.values();
|
protected static final Direction[] DIRECTIONS = Direction.values();
|
||||||
protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1];
|
protected static final Direction[] DIRECTIONS_AND_NULL = new Direction[DIRECTIONS.length + 1];
|
||||||
static { System.arraycopy(DIRECTIONS, 0, DIRECTIONS_AND_NULL, 0, DIRECTIONS.length); }
|
static { System.arraycopy(DIRECTIONS, 0, DIRECTIONS_AND_NULL, 0, DIRECTIONS.length); }
|
||||||
|
|
||||||
protected abstract Mesh getBaseMesh(BlockState state);
|
|
||||||
|
protected final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
protected Mesh getBaseMesh(BlockState state) {
|
||||||
|
//Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate
|
||||||
|
return jsonToMesh.computeIfAbsent(state, this::convertModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract Mesh convertModel(BlockState state);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isVanillaAdapter() {
|
public boolean isVanillaAdapter() {
|
||||||
@ -66,7 +79,7 @@ public abstract class RetexturingBakedModel extends ForwardingBakedModel {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
public void emitBlockQuads(BlockRenderView world, BlockState state, BlockPos pos, Supplier<Random> randomSupplier, RenderContext context) {
|
||||||
BlockState theme = (world.getBlockEntityRenderData(pos) instanceof BlockState s) ? s : null;
|
BlockState theme = (world.getBlockEntity(pos) instanceof ThemeableBlockEntity s) ? state_getter.apply(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(), 0)), 0).outputTo(quad_emitter);
|
getUntintedRetexturedMesh(new MeshCacheKey(state, new TransformCacheKey(tam.getDefaultAppearance(), 0)), 0).outputTo(quad_emitter);
|
||||||
@ -105,7 +118,7 @@ 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);
|
BlockState theme = ReFramedEntity.readStateFromItem(stack); // TODO Different states for both models
|
||||||
if(!theme.isAir()) {
|
if(!theme.isAir()) {
|
||||||
nbtAppearance = tam.getCamoAppearance(null, theme, null);
|
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);
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fr.adrien1106.reframed.client.model;
|
package fr.adrien1106.reframed.client.model;
|
||||||
|
|
||||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||||
|
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.Mesh;
|
||||||
@ -12,7 +13,6 @@ import net.minecraft.client.render.model.BakedModel;
|
|||||||
import net.minecraft.client.render.model.BakedQuad;
|
import net.minecraft.client.render.model.BakedQuad;
|
||||||
import net.minecraft.client.render.model.Baker;
|
import net.minecraft.client.render.model.Baker;
|
||||||
import net.minecraft.client.render.model.ModelBakeSettings;
|
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||||
import net.minecraft.client.render.model.UnbakedModel;
|
|
||||||
import net.minecraft.client.texture.Sprite;
|
import net.minecraft.client.texture.Sprite;
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
import net.minecraft.client.util.SpriteIdentifier;
|
||||||
import net.minecraft.util.Identifier;
|
import net.minecraft.util.Identifier;
|
||||||
@ -20,50 +20,27 @@ import net.minecraft.util.math.Direction;
|
|||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class UnbakedAutoRetexturedModel implements UnbakedModel {
|
public class UnbakedAutoRetexturedModel extends UnbakedRetexturedModel {
|
||||||
public UnbakedAutoRetexturedModel(Identifier parent) {
|
|
||||||
this.parent = parent;
|
public UnbakedAutoRetexturedModel(Identifier parent, Function<ThemeableBlockEntity, BlockState> state_getter) {
|
||||||
}
|
super(parent, state_getter);
|
||||||
|
item_state = Blocks.AIR.getDefaultState();
|
||||||
protected final Identifier parent;
|
|
||||||
protected BlockState itemModelState = Blocks.AIR.getDefaultState();
|
|
||||||
protected boolean ao = true;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Identifier> getModelDependencies() {
|
|
||||||
return Collections.singletonList(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setParents(Function<Identifier, UnbakedModel> function) {
|
|
||||||
function.apply(parent).setParents(function);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> texture_getter, ModelBakeSettings bake_settings, Identifier identifier) {
|
||||||
return new RetexturingBakedModel(
|
return new RetexturingBakedModel(
|
||||||
baker.bake(parent, modelBakeSettings),
|
baker.bake(parent, bake_settings),
|
||||||
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
|
ReFramedClient.HELPER.getCamoApperanceManager(texture_getter),
|
||||||
modelBakeSettings,
|
state_getter,
|
||||||
itemModelState,
|
bake_settings,
|
||||||
|
item_state,
|
||||||
ao
|
ao
|
||||||
) {
|
) {
|
||||||
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
|
protected Mesh convertModel(BlockState state) {
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Mesh getBaseMesh(BlockState state) {
|
|
||||||
//Convert models to retexturable Meshes lazily, the first time we encounter each blockstate
|
|
||||||
return jsonToMesh.computeIfAbsent(state, this::convertModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mesh convertModel(BlockState state) {
|
|
||||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||||
MeshBuilder builder = r.meshBuilder();
|
MeshBuilder builder = r.meshBuilder();
|
||||||
QuadEmitter emitter = builder.getEmitter();
|
QuadEmitter emitter = builder.getEmitter();
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
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(UnbakedModel model_1, UnbakedModel model_2) {
|
||||||
|
this.model_1 = model_1;
|
||||||
|
this.model_2 = model_2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Identifier> getModelDependencies() {
|
||||||
|
return List.of(model_1.getModelDependencies().iterator().next(), model_2.getModelDependencies().iterator().next());
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,13 +2,17 @@ package fr.adrien1106.reframed.client.model;
|
|||||||
|
|
||||||
import fr.adrien1106.reframed.ReFramed;
|
import fr.adrien1106.reframed.ReFramed;
|
||||||
import fr.adrien1106.reframed.client.ReFramedClient;
|
import fr.adrien1106.reframed.client.ReFramedClient;
|
||||||
|
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
import net.fabricmc.fabric.api.renderer.v1.Renderer;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
import net.fabricmc.fabric.api.renderer.v1.material.RenderMaterial;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.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.MeshBuilder;
|
||||||
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.client.render.model.*;
|
import net.minecraft.client.render.model.BakedModel;
|
||||||
|
import net.minecraft.client.render.model.BakedQuad;
|
||||||
|
import net.minecraft.client.render.model.Baker;
|
||||||
|
import net.minecraft.client.render.model.ModelBakeSettings;
|
||||||
import net.minecraft.client.texture.Sprite;
|
import net.minecraft.client.texture.Sprite;
|
||||||
import net.minecraft.client.util.SpriteIdentifier;
|
import net.minecraft.client.util.SpriteIdentifier;
|
||||||
import net.minecraft.screen.PlayerScreenHandler;
|
import net.minecraft.screen.PlayerScreenHandler;
|
||||||
@ -17,35 +21,17 @@ import net.minecraft.util.math.Direction;
|
|||||||
import net.minecraft.util.math.random.Random;
|
import net.minecraft.util.math.random.Random;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
public class UnbakedJsonRetexturedModel implements UnbakedModel {
|
public class UnbakedJsonRetexturedModel extends UnbakedRetexturedModel {
|
||||||
public UnbakedJsonRetexturedModel(Identifier parent) {
|
public UnbakedJsonRetexturedModel(Identifier parent, Function<ThemeableBlockEntity, BlockState> state_getter) {
|
||||||
this.parent = parent;
|
super(parent, state_getter);
|
||||||
}
|
|
||||||
|
|
||||||
protected final Identifier parent;
|
|
||||||
protected BlockState itemModelState;
|
|
||||||
protected boolean ao = true;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Collection<Identifier> getModelDependencies() {
|
|
||||||
return Collections.singletonList(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setParents(Function<Identifier, UnbakedModel> function) {
|
|
||||||
function.apply(parent).setParents(function);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings modelBakeSettings, Identifier identifier) {
|
public BakedModel bake(Baker baker, Function<SpriteIdentifier, Sprite> spriteLookup, ModelBakeSettings bake_settings, Identifier identifier) {
|
||||||
Direction[] DIRECTIONS = RetexturingBakedModel.DIRECTIONS;
|
Direction[] DIRECTIONS = RetexturingBakedModel.DIRECTIONS;
|
||||||
|
|
||||||
Sprite[] specialSprites = new Sprite[DIRECTIONS.length];
|
Sprite[] specialSprites = new Sprite[DIRECTIONS.length];
|
||||||
@ -54,24 +40,15 @@ public class UnbakedJsonRetexturedModel implements UnbakedModel {
|
|||||||
specialSprites[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(
|
||||||
model,
|
baker.bake(parent, bake_settings),
|
||||||
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
|
ReFramedClient.HELPER.getCamoApperanceManager(spriteLookup),
|
||||||
modelBakeSettings,
|
state_getter,
|
||||||
itemModelState,
|
bake_settings,
|
||||||
|
item_state,
|
||||||
ao
|
ao
|
||||||
) {
|
) {
|
||||||
final ConcurrentMap<BlockState, Mesh> jsonToMesh = new ConcurrentHashMap<>();
|
protected Mesh convertModel(BlockState state) {
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Mesh getBaseMesh(BlockState state) {
|
|
||||||
//Convert models to re-texturable Meshes lazily, the first time we encounter each blockstate
|
|
||||||
return jsonToMesh.computeIfAbsent(state, this::convertModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Mesh convertModel(BlockState state) {
|
|
||||||
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
Renderer r = ReFramedClient.HELPER.getFabricRenderer();
|
||||||
MeshBuilder builder = r.meshBuilder();
|
MeshBuilder builder = r.meshBuilder();
|
||||||
QuadEmitter emitter = builder.getEmitter();
|
QuadEmitter emitter = builder.getEmitter();
|
||||||
|
@ -0,0 +1,34 @@
|
|||||||
|
package fr.adrien1106.reframed.client.model;
|
||||||
|
|
||||||
|
import fr.adrien1106.reframed.util.ThemeableBlockEntity;
|
||||||
|
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 final Function<ThemeableBlockEntity, BlockState> state_getter;
|
||||||
|
|
||||||
|
protected BlockState item_state;
|
||||||
|
protected boolean ao = true;
|
||||||
|
|
||||||
|
public UnbakedRetexturedModel(Identifier parent, Function<ThemeableBlockEntity, BlockState> state_getter) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.state_getter = state_getter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Collection<Identifier> getModelDependencies() {
|
||||||
|
return List.of(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setParents(Function<Identifier, UnbakedModel> function) {
|
||||||
|
function.apply(parent).setParents(function);
|
||||||
|
}
|
||||||
|
}
|
@ -22,13 +22,13 @@ public class BlockMixin {
|
|||||||
private static boolean isNeighborCamoOpaque(BlockState state, @Local(argsOnly = true) BlockView world, @Local(ordinal = 1, argsOnly = true) BlockPos pos) {
|
private static boolean isNeighborCamoOpaque(BlockState state, @Local(argsOnly = true) BlockView world, @Local(ordinal = 1, argsOnly = true) BlockPos pos) {
|
||||||
BlockEntity block_entity = world.getBlockEntity(pos);
|
BlockEntity block_entity = world.getBlockEntity(pos);
|
||||||
if (!(block_entity instanceof ThemeableBlockEntity frame_entity)) return state.isOpaque();
|
if (!(block_entity instanceof ThemeableBlockEntity frame_entity)) return state.isOpaque();
|
||||||
return frame_entity.getThemeState().isOpaque();
|
return frame_entity.getFirstTheme().isOpaque();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "shouldDrawSide", at = @At(value = "INVOKE", target = "Lnet/minecraft/block/BlockState;isSideInvisible(Lnet/minecraft/block/BlockState;Lnet/minecraft/util/math/Direction;)Z"))
|
@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) {
|
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 ThemeableBlockEntity entity) other_state = entity.getThemeState();
|
if (world.getBlockEntity(other_pos) instanceof ThemeableBlockEntity entity) other_state = entity.getFirstTheme();
|
||||||
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity entity) state = entity.getThemeState();
|
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity entity) state = entity.getFirstTheme();
|
||||||
return state.isSideInvisible(other_state, direction);
|
return state.isSideInvisible(other_state, direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ public abstract class BlockRenderInfoMixin {
|
|||||||
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 ThemeableBlockEntity frame_entity)) return state;
|
||||||
return frame_entity.getThemeState();
|
return frame_entity.getFirstTheme();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject(method = "shouldDrawFace",
|
@Inject(method = "shouldDrawFace",
|
||||||
|
@ -21,7 +21,7 @@ public class AthenaWrappedGetterMixin {
|
|||||||
@Inject(method = "getBlockState", at = @At(value = "HEAD"), cancellable = true)
|
@Inject(method = "getBlockState", at = @At(value = "HEAD"), cancellable = true)
|
||||||
private void getCamoState(BlockPos pos, CallbackInfoReturnable<BlockState> cir) {
|
private void getCamoState(BlockPos pos, CallbackInfoReturnable<BlockState> cir) {
|
||||||
if (!(getter.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)) return;
|
if (!(getter.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity)) return;
|
||||||
cir.setReturnValue(framed_entity.getThemeState());
|
cir.setReturnValue(framed_entity.getFirstTheme());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Redirect(method = "getAppearance(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)" +
|
@Redirect(method = "getAppearance(Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/Direction;)" +
|
||||||
@ -30,7 +30,7 @@ public class AthenaWrappedGetterMixin {
|
|||||||
"getBlockState(Lnet/minecraft/util/math/BlockPos;)" +
|
"getBlockState(Lnet/minecraft/util/math/BlockPos;)" +
|
||||||
"Lnet/minecraft/block/BlockState;"))
|
"Lnet/minecraft/block/BlockState;"))
|
||||||
private BlockState appearanceCamoState(BlockRenderView world, BlockPos pos) {
|
private BlockState appearanceCamoState(BlockRenderView world, BlockPos pos) {
|
||||||
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) return framed_entity.getThemeState();
|
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) return framed_entity.getFirstTheme();
|
||||||
return world.getBlockState(pos);
|
return world.getBlockState(pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ public class AthenaWrappedGetterMixin {
|
|||||||
"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) {
|
private BlockState queryCamoState(BlockRenderView world, BlockPos pos) {
|
||||||
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) return framed_entity.getThemeState();
|
if (world.getBlockEntity(pos) instanceof ThemeableBlockEntity framed_entity) return framed_entity.getFirstTheme();
|
||||||
return world.getBlockState(pos);
|
return world.getBlockState(pos);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ 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() && clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable) {
|
if(a.getRandom().nextBoolean() && clientWorld.getBlockEntity(pos) instanceof ThemeableBlockEntity themeable) {
|
||||||
BlockState theme = themeable.getThemeState();
|
BlockState theme = themeable.getFirstTheme();
|
||||||
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);
|
||||||
|
@ -22,7 +22,7 @@ public abstract class MixinEntity {
|
|||||||
World world = ((Entity) (Object) this).getWorld();
|
World world = ((Entity) (Object) this).getWorld();
|
||||||
|
|
||||||
if(world.getBlockEntity(getLandingPos()) instanceof ThemeableBlockEntity themeable) {
|
if(world.getBlockEntity(getLandingPos()) instanceof ThemeableBlockEntity themeable) {
|
||||||
BlockState theme = themeable.getThemeState();
|
BlockState theme = themeable.getFirstTheme();
|
||||||
if(!theme.isAir()) return theme;
|
if(!theme.isAir()) return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ public class MixinLivingEntity {
|
|||||||
World world = ((Entity) (Object) this).getWorld();
|
World world = ((Entity) (Object) this).getWorld();
|
||||||
|
|
||||||
if(lastFallCheckPos != null && world.getBlockEntity(lastFallCheckPos) instanceof ThemeableBlockEntity themeable) {
|
if(lastFallCheckPos != null && world.getBlockEntity(lastFallCheckPos) instanceof ThemeableBlockEntity themeable) {
|
||||||
BlockState theme = themeable.getThemeState();
|
BlockState theme = themeable.getFirstTheme();
|
||||||
if(!theme.isAir()) return theme;
|
if(!theme.isAir()) return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,19 +1,10 @@
|
|||||||
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.AbstractBlock;
|
import net.minecraft.block.*;
|
||||||
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.BlockItem;
|
import net.minecraft.item.*;
|
||||||
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;
|
||||||
@ -65,60 +56,66 @@ public class ReFramedInteractionUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 be)) return ActionResult.PASS;
|
if(!(world.getBlockEntity(pos) instanceof ReFramedEntity block_entity)) 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;
|
||||||
|
|
||||||
//Glowstone
|
// frame will emit light if applied with glowstone
|
||||||
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST && !state.get(LIGHT) && !be.hasSpentGlowstoneDust()) {
|
if(state.contains(LIGHT) && held.getItem() == Items.GLOWSTONE_DUST) {
|
||||||
world.setBlockState(pos, state.with(LIGHT, true));
|
block_entity.toggleLight();
|
||||||
be.spentGlowstoneDust();
|
world.setBlockState(pos, state.with(LIGHT, block_entity.emitsLight()));
|
||||||
|
|
||||||
if(!player.isCreative()) held.decrement(1);
|
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);
|
world.playSound(player, pos, SoundEvents.BLOCK_GLASS_HIT, SoundCategory.BLOCKS, 1f, 1f);
|
||||||
return ActionResult.SUCCESS;
|
return ActionResult.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Redstone
|
// frame will emit redstone if applied with redstone torch can deactivate redstone block camo emission
|
||||||
if(held.getItem() == Blocks.REDSTONE_TORCH.asItem() &&
|
if(held.getItem() == Items.REDSTONE_TORCH && ext.canAddRedstoneEmission(state, world, pos)) {
|
||||||
!be.emitsRedstone() &&
|
block_entity.toggleRedstone();
|
||||||
!be.hasSpentRedstoneTorch() &&
|
|
||||||
ext.canAddRedstoneEmission(state, world, pos)
|
|
||||||
) {
|
|
||||||
be.setEmitsRedstone(true);
|
|
||||||
be.spentRedstoneTorch();
|
|
||||||
|
|
||||||
if(!player.isCreative()) held.decrement(1);
|
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);
|
world.playSound(player, pos, SoundEvents.BLOCK_LEVER_CLICK, SoundCategory.BLOCKS, 1f, 1f);
|
||||||
return ActionResult.SUCCESS;
|
return ActionResult.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Popped chorus fruit
|
// Frame will lose its collision if applied with popped chorus fruit
|
||||||
if(held.getItem() == Items.POPPED_CHORUS_FRUIT &&
|
if(held.getItem() == Items.POPPED_CHORUS_FRUIT && ext.canRemoveCollision(state, world, pos)) {
|
||||||
be.isSolid() &&
|
block_entity.toggleSolidity();
|
||||||
!be.hasSpentPoppedChorus() &&
|
|
||||||
ext.canRemoveCollision(state, world, pos)
|
|
||||||
) {
|
|
||||||
be.setSolidity(false);
|
|
||||||
be.spentPoppedChorus();
|
|
||||||
|
|
||||||
if(!player.isCreative()) held.decrement(1);
|
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);
|
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 TODO Move outside
|
||||||
if(held.getItem() instanceof BlockItem bi && be.getThemeState().getBlock() == Blocks.AIR) {
|
if(held.getItem() instanceof BlockItem block_item && block_entity.getFirstTheme().getBlock() == Blocks.AIR) {
|
||||||
Block block = bi.getBlock();
|
Block block = block_item.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)) {
|
||||||
if(!world.isClient) be.setRenderedState(placementState);
|
// TODO FOR SECOND
|
||||||
|
if(!world.isClient) block_entity.setFirstTheme(placementState);
|
||||||
world.setBlockState(pos, state.with(LIGHT, be.hasSpentGlowstoneDust() || (placementState.getLuminance() != 0)));
|
|
||||||
be.setEmitsRedstone(be.hasSpentRedstoneTorch() || placementState.getWeakRedstonePower(world, pos, Direction.NORTH) != 0);
|
// check for default light emission
|
||||||
|
if (placementState.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 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);
|
||||||
@ -128,22 +125,23 @@ public class ReFramedInteractionUtil {
|
|||||||
|
|
||||||
return ActionResult.PASS;
|
return ActionResult.PASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Maybe an odd spot to put this logic but it's consistent w/ vanilla chests, barrels, etc
|
|
||||||
public static void onStateReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean moved) {
|
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 &&
|
world.getBlockEntity(pos) instanceof ReFramedEntity frame_entity &&
|
||||||
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.getFirstTheme();
|
||||||
|
if(theme.getBlock() != Blocks.AIR) drops.add(new ItemStack(theme.getBlock()));
|
||||||
|
|
||||||
//TODO: remember the specific ItemStack
|
if(frame_entity.emitsRedstone() && theme.getWeakRedstonePower(world, pos, Direction.NORTH) == 0)
|
||||||
Block theme = frame.getThemeState().getBlock();
|
drops.add(new ItemStack(Items.REDSTONE_TORCH));
|
||||||
if(theme != Blocks.AIR) drops.add(new ItemStack(theme));
|
if(frame_entity.emitsLight() && theme.getLuminance() == 0)
|
||||||
|
drops.add(new ItemStack(Items.GLOWSTONE_DUST));
|
||||||
if(frame.hasSpentRedstoneTorch()) drops.add(new ItemStack(Items.REDSTONE_TORCH));
|
if(!frame_entity.isSolid() && theme.isSolid())
|
||||||
if(frame.hasSpentGlowstoneDust()) drops.add(new ItemStack(Items.GLOWSTONE_DUST));
|
drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
|
||||||
if(frame.hasSpentPoppedChorus()) drops.add(new ItemStack(Items.POPPED_CHORUS_FRUIT));
|
|
||||||
|
|
||||||
ItemScatterer.spawn(world, pos, drops);
|
ItemScatterer.spawn(world, pos, drops);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,17 @@
|
|||||||
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;
|
||||||
|
|
||||||
public interface ThemeableBlockEntity extends RenderAttachmentBlockEntity {
|
public interface ThemeableBlockEntity {
|
||||||
default BlockState getThemeState() {
|
BlockState getFirstTheme();
|
||||||
return (BlockState) getRenderAttachmentData();
|
|
||||||
|
default BlockState getSecondTheme() {
|
||||||
|
return getFirstTheme();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFirstTheme(BlockState state);
|
||||||
|
|
||||||
|
default void setSecondTheme(BlockState state) {
|
||||||
|
setFirstTheme(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user