Curses II

- Added (temporary) rendering to the bells
- Create soul pulse effects on players holding the Cursed Bell item
This commit is contained in:
reidbhuntley 2021-06-27 11:46:56 -04:00
parent e61dd71df1
commit a8aa0beed9
30 changed files with 900 additions and 160 deletions

View file

@ -68,6 +68,7 @@ f0031f5e970b3d5695472ed384950b8631b015ed assets/create/blockstates/creative_moto
fe2f78b94c20944399101e7369e2d43324297fb6 assets/create/blockstates/crushing_wheel.json
a1dd6cb3daa97ea871290ef7b178d28b564ee2a2 assets/create/blockstates/crushing_wheel_controller.json
b1126c191877cff86b4e2de83e1fcbd151451cb7 assets/create/blockstates/cuckoo_clock.json
e8f1222b21e8e2e67d18252f7d3c9eefa650d3b9 assets/create/blockstates/cursed_bell.json
b496452f2f7dbbba385e1fc10b560ec266e4b5e7 assets/create/blockstates/cyan_sail.json
4de72f65bff4e5d9c8153fa3adeee6b61d6f912b assets/create/blockstates/cyan_seat.json
2c04d57e56849f243aec8a1e769574d24daac1e9 assets/create/blockstates/cyan_valve_handle.json
@ -1295,6 +1296,7 @@ b359064405d189e2802969715cd5f682ddbf0bb1 assets/create/models/item/crushed_urani
2bb791db62dce6bf2e2227f9b607c131828471fd assets/create/models/item/crushed_zinc_ore.json
823c91f63565db54ec3944a1e90e7aee18e41062 assets/create/models/item/crushing_wheel.json
dae5cffa4e1263d6a113469f79fba8695fa8232a assets/create/models/item/cuckoo_clock.json
9826c00050ba493f844e8812cc45153a156c5317 assets/create/models/item/cursed_bell.json
3e3edc9ccded444496d3336926b93bbf1234cd84 assets/create/models/item/cyan_seat.json
523cd531eadaadc45fb356ca58b99a8fe206c3a7 assets/create/models/item/cyan_valve_handle.json
f786a43e296d9f10d7c302fe3ae9cddf4ba9984e assets/create/models/item/dark_oak_window.json

View file

@ -0,0 +1,124 @@
{
"variants": {
"attachment=floor,facing=north,powered=false": {
"model": "create:block/cursed_bell/block_floor"
},
"attachment=ceiling,facing=north,powered=false": {
"model": "create:block/cursed_bell/block_ceiling"
},
"attachment=single_wall,facing=north,powered=false": {
"model": "create:block/cursed_bell/block_single_wall"
},
"attachment=double_wall,facing=north,powered=false": {
"model": "create:block/cursed_bell/block_double_wall"
},
"attachment=floor,facing=south,powered=false": {
"model": "create:block/cursed_bell/block_floor",
"y": 180
},
"attachment=ceiling,facing=south,powered=false": {
"model": "create:block/cursed_bell/block_ceiling",
"y": 180
},
"attachment=single_wall,facing=south,powered=false": {
"model": "create:block/cursed_bell/block_single_wall",
"y": 180
},
"attachment=double_wall,facing=south,powered=false": {
"model": "create:block/cursed_bell/block_double_wall",
"y": 180
},
"attachment=floor,facing=west,powered=false": {
"model": "create:block/cursed_bell/block_floor",
"y": 270
},
"attachment=ceiling,facing=west,powered=false": {
"model": "create:block/cursed_bell/block_ceiling",
"y": 270
},
"attachment=single_wall,facing=west,powered=false": {
"model": "create:block/cursed_bell/block_single_wall",
"y": 270
},
"attachment=double_wall,facing=west,powered=false": {
"model": "create:block/cursed_bell/block_double_wall",
"y": 270
},
"attachment=floor,facing=east,powered=false": {
"model": "create:block/cursed_bell/block_floor",
"y": 90
},
"attachment=ceiling,facing=east,powered=false": {
"model": "create:block/cursed_bell/block_ceiling",
"y": 90
},
"attachment=single_wall,facing=east,powered=false": {
"model": "create:block/cursed_bell/block_single_wall",
"y": 90
},
"attachment=double_wall,facing=east,powered=false": {
"model": "create:block/cursed_bell/block_double_wall",
"y": 90
},
"attachment=floor,facing=north,powered=true": {
"model": "create:block/cursed_bell/block_floor"
},
"attachment=ceiling,facing=north,powered=true": {
"model": "create:block/cursed_bell/block_ceiling"
},
"attachment=single_wall,facing=north,powered=true": {
"model": "create:block/cursed_bell/block_single_wall"
},
"attachment=double_wall,facing=north,powered=true": {
"model": "create:block/cursed_bell/block_double_wall"
},
"attachment=floor,facing=south,powered=true": {
"model": "create:block/cursed_bell/block_floor",
"y": 180
},
"attachment=ceiling,facing=south,powered=true": {
"model": "create:block/cursed_bell/block_ceiling",
"y": 180
},
"attachment=single_wall,facing=south,powered=true": {
"model": "create:block/cursed_bell/block_single_wall",
"y": 180
},
"attachment=double_wall,facing=south,powered=true": {
"model": "create:block/cursed_bell/block_double_wall",
"y": 180
},
"attachment=floor,facing=west,powered=true": {
"model": "create:block/cursed_bell/block_floor",
"y": 270
},
"attachment=ceiling,facing=west,powered=true": {
"model": "create:block/cursed_bell/block_ceiling",
"y": 270
},
"attachment=single_wall,facing=west,powered=true": {
"model": "create:block/cursed_bell/block_single_wall",
"y": 270
},
"attachment=double_wall,facing=west,powered=true": {
"model": "create:block/cursed_bell/block_double_wall",
"y": 270
},
"attachment=floor,facing=east,powered=true": {
"model": "create:block/cursed_bell/block_floor",
"y": 90
},
"attachment=ceiling,facing=east,powered=true": {
"model": "create:block/cursed_bell/block_ceiling",
"y": 90
},
"attachment=single_wall,facing=east,powered=true": {
"model": "create:block/cursed_bell/block_single_wall",
"y": 90
},
"attachment=double_wall,facing=east,powered=true": {
"model": "create:block/cursed_bell/block_double_wall",
"y": 90
}
}
}

View file

@ -0,0 +1,3 @@
{
"parent": "create:block/cursed_bell/item"
}

View file

@ -93,6 +93,8 @@ public class AllBlockPartials {
SPOUT_TOP = get("spout/top"), SPOUT_MIDDLE = get("spout/middle"), SPOUT_BOTTOM = get("spout/bottom"),
BELL = get("cursed_bell/bell"),
SPEED_CONTROLLER_BRACKET = get("rotation_speed_controller/bracket"),
GOGGLES = get("goggles"),

View file

@ -13,6 +13,7 @@ import com.simibubi.create.AllTags.AllBlockTags;
import com.simibubi.create.AllTags.AllItemTags;
import com.simibubi.create.content.AllSections;
import com.simibubi.create.content.contraptions.base.CasingBlock;
import com.simibubi.create.content.contraptions.components.actors.BellMovementBehaviour;
import com.simibubi.create.content.contraptions.components.actors.DrillBlock;
import com.simibubi.create.content.contraptions.components.actors.DrillMovementBehaviour;
import com.simibubi.create.content.contraptions.components.actors.HarvesterBlock;
@ -115,6 +116,7 @@ import com.simibubi.create.content.contraptions.relays.gauge.GaugeGenerator;
import com.simibubi.create.content.contraptions.relays.gearbox.GearboxBlock;
import com.simibubi.create.content.curiosities.armor.CopperBacktankBlock;
import com.simibubi.create.content.curiosities.bell.CursedBellBlock;
import com.simibubi.create.content.curiosities.bell.CursedBellMovementBehaviour;
import com.simibubi.create.content.curiosities.bell.PeculiarBellBlock;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelBlock;
import com.simibubi.create.content.logistics.block.belts.tunnel.BrassTunnelBlock;
@ -192,6 +194,7 @@ import net.minecraft.loot.conditions.ILootCondition.IBuilder;
import net.minecraft.loot.conditions.SurvivesExplosion;
import net.minecraft.loot.functions.CopyName;
import net.minecraft.loot.functions.CopyNbt;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.state.properties.PistonType;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
@ -1305,8 +1308,11 @@ public class AllBlocks {
public static final BlockEntry<PeculiarBellBlock> PECULIAR_BELL =
REGISTRATE.block("peculiar_bell", PeculiarBellBlock::new)
.initialProperties(SharedProperties::softMetal)
.blockstate((c, p) -> {})
.properties(Block.Properties::nonOpaque)
.addLayer(() -> RenderType::getCutoutMipped)
.tag(AllBlockTags.BRITTLE.tag)
.onRegister(addMovementBehaviour(new BellMovementBehaviour(PeculiarBellBlock::playSound)))
.blockstate((c, p) -> {})
.item()
.model((c, p) -> {})
.build()
@ -1315,11 +1321,12 @@ public class AllBlocks {
public static final BlockEntry<CursedBellBlock> CURSED_BELL =
REGISTRATE.block("cursed_bell", CursedBellBlock::new)
.initialProperties(() -> PECULIAR_BELL.get())
.addLayer(() -> RenderType::getCutoutMipped)
.tag(AllBlockTags.BRITTLE.tag)
.blockstate((c, p) -> {})
.onRegister(addMovementBehaviour(new CursedBellMovementBehaviour()))
.blockstate(BlockStateGen.bell())
.item()
.model((c, p) -> {})
.build()
.transform(customItemModel())
.register();
// Materials

View file

@ -119,10 +119,9 @@ import com.simibubi.create.content.contraptions.relays.gearbox.GearshiftTileEnti
import com.simibubi.create.content.curiosities.armor.CopperBacktankInstance;
import com.simibubi.create.content.curiosities.armor.CopperBacktankRenderer;
import com.simibubi.create.content.curiosities.armor.CopperBacktankTileEntity;
import com.simibubi.create.content.curiosities.bell.AbstractBellRenderer;
import com.simibubi.create.content.curiosities.bell.CursedBellTileEntity;
import com.simibubi.create.content.curiosities.bell.PeculiarBellTileEntity;
import com.simibubi.create.content.curiosities.projector.ChromaticProjectorInstance;
import com.simibubi.create.content.curiosities.projector.ChromaticProjectorTileEntity;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelInstance;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelRenderer;
import com.simibubi.create.content.logistics.block.belts.tunnel.BeltTunnelTileEntity;
@ -676,6 +675,7 @@ public class AllTileEntities {
public static final TileEntityEntry<CursedBellTileEntity> CURSED_BELL = Create.registrate()
.tileEntity("cursed_bell", CursedBellTileEntity::new)
.validBlocks(AllBlocks.CURSED_BELL)
.renderer(() -> AbstractBellRenderer::new)
.register();
public static void register() {}

View file

@ -10,6 +10,7 @@ import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher;
import com.simibubi.create.content.contraptions.relays.encased.CasingConnectivity;
import com.simibubi.create.content.curiosities.armor.CopperBacktankArmorLayer;
import com.simibubi.create.content.curiosities.bell.SoulPulseEffectHandler;
import com.simibubi.create.content.curiosities.weapons.PotatoCannonRenderHandler;
import com.simibubi.create.content.curiosities.zapper.ZapperRenderHandler;
import com.simibubi.create.content.schematics.ClientSchematicLoader;
@ -67,6 +68,7 @@ public class CreateClient {
public static final ZapperRenderHandler ZAPPER_RENDER_HANDLER = new ZapperRenderHandler();
public static final PotatoCannonRenderHandler POTATO_CANNON_RENDER_HANDLER = new PotatoCannonRenderHandler();
public static final SoulPulseEffectHandler SOUL_PULSE_EFFECT_HANDLER = new SoulPulseEffectHandler();
private static CustomBlockModels customBlockModels;
private static CustomItemModels customItemModels;
@ -83,7 +85,7 @@ public class CreateClient {
modEventBus.addListener(CreateContexts::flwInit);
modEventBus.addListener(AllMaterialSpecs::flwInit);
modEventBus.addListener(ContraptionRenderDispatcher::invalidateOnGatherContext);
ZAPPER_RENDER_HANDLER.register(forgeEventBus);
POTATO_CANNON_RENDER_HANDLER.register(forgeEventBus);
}

View file

@ -1,5 +1,7 @@
package com.simibubi.create.content.contraptions.components.actors;
import java.util.function.BiConsumer;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
@ -7,8 +9,24 @@ import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
public class BellMovementBehaviour extends MovementBehaviour {
private BiConsumer<World, BlockPos> soundPlayer;
public static final BiConsumer<World, BlockPos> VANILLA_SOUND = (world, pos) -> {
world.playSound(null, pos, SoundEvents.BLOCK_BELL_USE, SoundCategory.BLOCKS,
2.0F, 1.0F);
};
public BellMovementBehaviour(BiConsumer<World, BlockPos> soundPlayer) {
this.soundPlayer = soundPlayer;
}
public BellMovementBehaviour() {
this(VANILLA_SOUND);
}
@Override
public boolean renderAsNormalTileEntity() {
return true;
@ -19,14 +37,12 @@ public class BellMovementBehaviour extends MovementBehaviour {
double dotProduct = oldMotion.dotProduct(motion);
if (dotProduct <= 0 && (context.relativeMotion.length() != 0) || context.firstMovement)
context.world.playSound(null, new BlockPos(context.position), SoundEvents.BLOCK_BELL_USE,
SoundCategory.BLOCKS, 2.0F, 1.0F);
soundPlayer.accept(context.world, new BlockPos(context.position));
}
@Override
public void stopMoving(MovementContext context) {
if (context.position != null)
context.world.playSound(null, new BlockPos(context.position), SoundEvents.BLOCK_BELL_USE, SoundCategory.BLOCKS,
2.0F, 1.0F);
soundPlayer.accept(context.world, new BlockPos(context.position));
}
}

View file

@ -55,11 +55,11 @@ public abstract class AbstractBellBlock<TE extends AbstractBellTileEntity> exten
if (world.isRemote)
return false;
playSound(world, pos, direction);
playSound(world, pos);
return true;
}
public static void playSound(World world, BlockPos pos, Direction direction) {
public static void playSound(World world, BlockPos pos) {
world.playSound(null, pos, SoundEvents.BLOCK_BELL_USE, SoundCategory.BLOCKS, 2.0F, 1.0F);
}

View file

@ -0,0 +1,57 @@
package com.simibubi.create.content.curiosities.bell;
import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.vertex.IVertexBuilder;
import com.simibubi.create.AllBlockPartials;
import com.simibubi.create.content.logistics.block.redstone.AnalogLeverBlock;
import com.simibubi.create.foundation.render.PartialBufferer;
import com.simibubi.create.foundation.render.SuperByteBuffer;
import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer;
import com.simibubi.create.foundation.utility.AngleHelper;
import net.minecraft.block.BellBlock;
import net.minecraft.block.BlockState;
import net.minecraft.client.renderer.IRenderTypeBuffer;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.WorldRenderer;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.util.Direction;
import net.minecraft.util.math.MathHelper;
public class AbstractBellRenderer<TE extends AbstractBellTileEntity> extends SafeTileEntityRenderer<TE> {
public AbstractBellRenderer(TileEntityRendererDispatcher dispatcher) {
super(dispatcher);
}
@Override
protected void renderSafe(TE te, float partialTicks, MatrixStack ms, IRenderTypeBuffer buffer, int light, int overlay) {
BlockState state = te.getBlockState();
int lightCoords = WorldRenderer.getLightmapCoordinates(te.getWorld(), state, te.getPos());
IVertexBuilder vb = buffer.getBuffer(RenderType.getCutout());
SuperByteBuffer bell = PartialBufferer.get(AllBlockPartials.BELL, state);
float rY = AngleHelper.horizontalAngle(state.get(BellBlock.field_220133_a));
bell.rotateCentered(Direction.UP, (float) (rY / 180 * Math.PI));
float ringingTicks = (float)te.ringingTicks + partialTicks;
if (te.isRinging) {
float swing = MathHelper.sin(ringingTicks / (float)Math.PI) / (4.0F + ringingTicks / 3.0F);
// if (te.ringDirection == Direction.NORTH) {
// rX = -swing;
// } else if (te.ringDirection == Direction.SOUTH) {
// rX = swing;
// } else if (te.ringDirection == Direction.EAST) {
// rZ = -swing;
// } else if (te.ringDirection == Direction.WEST) {
// rZ = swing;
// }
bell.rotateCentered(te.ringDirection, swing);
}
bell.light(lightCoords).renderInto(ms, vb);
}
}

View file

@ -13,6 +13,12 @@ import java.util.List;
public abstract class AbstractBellTileEntity extends SmartTileEntity {
public static final int RING_DURATION = 50;
public boolean isRinging;
public int ringingTicks;
public Direction ringDirection;
public AbstractBellTileEntity(TileEntityType<?> type) {
super(type);
}
@ -20,6 +26,25 @@ public abstract class AbstractBellTileEntity extends SmartTileEntity {
@Override
public void addBehaviours(List<TileEntityBehaviour> behaviours) { }
public abstract boolean ring(World world, BlockPos pos, Direction direction);
public boolean ring(World world, BlockPos pos, Direction direction) {
isRinging = true;
ringingTicks = 0;
ringDirection = direction;
return true;
};
@Override
public void tick() {
super.tick();
if (isRinging) {
++ringingTicks;
}
if (ringingTicks >= RING_DURATION) {
isRinging = false;
ringingTicks = 0;
}
}
}

View file

@ -0,0 +1,42 @@
package com.simibubi.create.content.curiosities.bell;
import com.simibubi.create.AllBlocks;
import net.minecraft.block.Block;
import net.minecraft.item.BlockItem;
import net.minecraft.item.Item;
import net.minecraft.util.Hand;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.LogicalSide;
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
@EventBusSubscriber
public class CursedBellItemPulser {
public static final int DISTANCE = 2;
public static final int RECHARGE_TICKS = 8;
@SubscribeEvent
public static void bellItemCreatesPulses(TickEvent.PlayerTickEvent event) {
if (event.phase != TickEvent.Phase.END)
return;
if (event.side != LogicalSide.SERVER)
return;
if (event.player.world.getGameTime() % RECHARGE_TICKS != 0)
return;
for (Hand hand : Hand.values()) {
Item held = event.player.getHeldItem(hand).getItem();
if (!(held instanceof BlockItem))
continue;
Block block = ((BlockItem) held).getBlock();
if (!block.is(AllBlocks.CURSED_BELL.get()))
continue;
SoulPulseEffectHandler.sendPulsePacket(event.player.world, event.player.getBlockPos(), DISTANCE, false);
}
}
}

View file

@ -0,0 +1,44 @@
package com.simibubi.create.content.curiosities.bell;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext;
import net.minecraft.util.math.BlockPos;
public class CursedBellMovementBehaviour extends MovementBehaviour {
public static final int DISTANCE = 3;
@Override
public void tick(MovementContext context) {
int recharge = getRecharge(context);
if (recharge > 0)
setRecharge(context, recharge - 1);
}
@Override
public void visitNewPosition(MovementContext context, BlockPos pos) {
if (!context.world.isRemote && getRecharge(context) == 0) {
SoulPulseEffectHandler.sendPulsePacket(context.world, pos, DISTANCE, true);
setRecharge(context, CursedBellTileEntity.RECHARGE_TICKS);
CursedBellBlock.playSound(context.world, pos);
}
}
@Override
public void writeExtraData(MovementContext context) {
context.tileData.putInt("Recharge", getRecharge(context));
}
private int getRecharge(MovementContext context) {
if (!(context.temporaryData instanceof Integer) && context.world != null) {
context.temporaryData = context.tileData.getInt("Recharge");
}
return (Integer) context.temporaryData;
}
private void setRecharge(MovementContext context, int value) {
context.temporaryData = value;
}
}

View file

@ -1,36 +1,24 @@
package com.simibubi.create.content.curiosities.bell;
import java.util.ArrayList;
import java.util.List;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.sun.org.apache.regexp.internal.RE;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import net.minecraft.entity.EntityType;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.tileentity.TileEntityType;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.LightType;
import net.minecraft.world.World;
import net.minecraft.world.spawner.WorldEntitySpawner;
public class CursedBellTileEntity extends AbstractBellTileEntity {
public static final int MAX_DISTANCE = 6;
private static final List<List<BlockPos>> LAYERS = genLayers();
public enum Mode {
RUNNING, RECHARGING
}
public static final int RECHARGE_TICKS = 16;
public static final int TICKS_PER_LAYER = 3;
public int ticks;
public Mode mode = Mode.RECHARGING;
public static final int DISTANCE = 5;
public static final int RECHARGE_TICKS = 60;
public int rechargeTicks = 0;
public CursedBellTileEntity(TileEntityType<?> type) {
super(type);
@ -41,137 +29,32 @@ public class CursedBellTileEntity extends AbstractBellTileEntity {
@Override
protected void fromTag(BlockState state, CompoundNBT compound, boolean clientPacket) {
ticks = compound.getInt("Ticks");
mode = NBTHelper.readEnum(compound, "Mode", Mode.class);
rechargeTicks = compound.getInt("Recharge");
}
@Override
public void write(CompoundNBT compound, boolean clientPacket) {
compound.putInt("Ticks", ticks);
NBTHelper.writeEnum(compound, "Mode", mode);
compound.putInt("Recharge", rechargeTicks);
}
@Override
public void tick() {
super.tick();
switch (mode) {
case RECHARGING:
if (ticks > 0)
ticks--;
break;
case RUNNING:
if (ticks <= 0)
break;
ticks--;
if (ticks % TICKS_PER_LAYER == 0) {
while (!trySpawnSouls(world, pos, MAX_DISTANCE - ticks / TICKS_PER_LAYER - 1)
&& ticks > 0) {
ticks -= TICKS_PER_LAYER;
}
if (ticks == 0) {
ticks = RECHARGE_TICKS;
mode = Mode.RECHARGING;
}
}
break;
}
}
public boolean tryStart() {
if (mode != Mode.RECHARGING || ticks > 0)
return false;
ticks = TICKS_PER_LAYER*MAX_DISTANCE;
mode = Mode.RUNNING;
if (!world.isRemote)
sendData();
return true;
if (rechargeTicks > 0)
rechargeTicks--;
}
@Override
public boolean ring(World world, BlockPos pos, Direction direction) {
return tryStart();
}
public static boolean trySpawnSouls(World world, BlockPos at, int layerIdx) {
if (world == null)
if (rechargeTicks > 0)
return false;
boolean spawnedAny = false;
List<BlockPos> layer = LAYERS.get(layerIdx);
for (BlockPos candidate : layer) {
candidate = candidate.add(at);
if (!WorldEntitySpawner.canCreatureTypeSpawnAtLocation(
EntitySpawnPlacementRegistry.PlacementType.ON_GROUND,
world, candidate, EntityType.ZOMBIE))
continue;
if (world.getLightLevel(LightType.BLOCK, candidate) >= 8)
continue;
if (!world.isRemote)
return true;
spawnedAny = true;
spawnParticles(world, candidate);
if (!world.isRemote) {
SoulPulseEffectHandler.sendPulsePacket(world, pos, DISTANCE, true);
rechargeTicks = RECHARGE_TICKS;
sendData();
}
return spawnedAny;
}
public static void spawnParticles(World world, BlockPos at) {
if (world == null || !world.isRemote)
return;
Vector3d p = Vector3d.of(at);
world.addParticle(new SoulParticle.Data(), p.x + 0.5, p.y + 0.5, p.z + 0.5, 0, 0, 0);
world.addParticle(new SoulBaseParticle.Data(), p.x + 0.5, p.y + 0.01, p.z + 0.5, 0, 0, 0);
}
private static List<List<BlockPos>> genLayers() {
List<List<BlockPos>> layers = new ArrayList<>();
for (int i = 0; i < MAX_DISTANCE; i++)
layers.add(new ArrayList<>());
for (int x = 0; x < MAX_DISTANCE; x++) {
for (int y = 0; y < MAX_DISTANCE; y++) {
for (int z = 0; z < MAX_DISTANCE; z++) {
BlockPos candidate = new BlockPos(x,y,z);
int dist = 1 + (int)Math.sqrt(candidate.distanceSq(0,0,0,false));
if (dist == 0 || dist > MAX_DISTANCE)
continue;
List<BlockPos> layer = layers.get(dist - 1);
int start = layer.size(), end = start + 1;
layer.add(candidate);
if (candidate.getX() != 0) {
layer.add(new BlockPos(-candidate.getX(), candidate.getY(), candidate.getZ()));
end += 1;
}
if (candidate.getY() != 0) {
for (int i = start; i < end; i++) {
BlockPos prev = layer.get(i);
layer.add(new BlockPos(prev.getX(), -prev.getY(), prev.getZ()));
}
end += end - start;
}
if (candidate.getZ() != 0) {
for (int i = start; i < end; i++) {
BlockPos prev = layer.get(i);
layer.add(new BlockPos(prev.getX(), prev.getY(), -prev.getZ()));
}
}
}
}
}
return layers;
return super.ring(world, pos, direction);
}
}

View file

@ -11,9 +11,4 @@ public class PeculiarBellTileEntity extends AbstractBellTileEntity {
super(type);
}
@Override
public boolean ring(World world, BlockPos pos, Direction direction) {
return true;
}
}

View file

@ -39,10 +39,10 @@ public class SoulParticle extends CustomRotationParticle {
this.particleScale = 0.5f;
this.setSize(this.particleScale, this.particleScale);
this.loopLength = loopFrames + (int) (this.rand.nextFloat() * 4f - 2f);
this.startTicks = startFrames + (int) (this.rand.nextFloat() * 4f - 2f);
this.endTicks = endFrames + (int) (this.rand.nextFloat() * 4f - 2f);
this.numLoops = (int)(3.0F / (this.rand.nextFloat() * 0.36F + 0.64F));
this.loopLength = loopFrames + (int) (this.rand.nextFloat() * 5f - 4f);
this.startTicks = startFrames + (int) (this.rand.nextFloat() * 5f - 4f);
this.endTicks = endFrames + (int) (this.rand.nextFloat() * 5f - 4f);
this.numLoops = (int)(1f + this.rand.nextFloat() * 2f);
this.setFrame(0);
this.field_21507 = true; // disable movement

View file

@ -0,0 +1,148 @@
package com.simibubi.create.content.curiosities.bell;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.minecraft.entity.EntitySpawnPlacementRegistry;
import net.minecraft.entity.EntityType;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.LightType;
import net.minecraft.world.World;
import net.minecraft.world.spawner.WorldEntitySpawner;
public class SoulPulseEffect {
public static final int MAX_DISTANCE = 5;
private static final List<List<BlockPos>> LAYERS = genLayers();
private static final int WAITING_TICKS = 120;
public static final int TICKS_PER_LAYER = 4;
private int ticks;
public final BlockPos pos;
public final int distance;
private List<BlockPos> added;
public SoulPulseEffect(BlockPos pos, int distance, boolean overlaps) {
this.ticks = TICKS_PER_LAYER * distance;
this.pos = pos;
this.distance = distance;
this.added = overlaps ? null : new ArrayList<>();
}
public boolean finished() {
return ticks <= -WAITING_TICKS;
}
public boolean overlaps() { return added == null; }
public void removeAdded(Collection<BlockPos> positions) {
if (!overlaps())
positions.removeAll(added);
}
public List<BlockPos> tick(World world) {
if (finished())
return null;
ticks--;
if (ticks < 0 || ticks % TICKS_PER_LAYER != 0)
return null;
List<BlockPos> spawns = getSoulSpawns(world);
while (spawns.isEmpty() && ticks > 0) {
ticks -= TICKS_PER_LAYER;
spawns.addAll(getSoulSpawns(world));
}
if (!overlaps())
added.addAll(spawns);
return spawns;
}
public int currentLayerIdx() {
return distance - ticks / TICKS_PER_LAYER - 1;
}
public List<BlockPos> getSoulSpawns(World world) {
if (world == null)
return new ArrayList<>();
return getLayer(currentLayerIdx()).map(p -> p.add(pos))
.filter(p -> canSpawnSoulAt(world, p))
.collect(Collectors.toList());
}
public static boolean canSpawnSoulAt(World world, BlockPos at) {
return world != null
&& WorldEntitySpawner.canCreatureTypeSpawnAtLocation(
EntitySpawnPlacementRegistry.PlacementType.ON_GROUND,
world, at, EntityType.ZOMBIE)
&& world.getLightLevel(LightType.BLOCK, at) < 8;
}
public static void spawnParticles(World world, BlockPos at) {
if (world == null || !world.isRemote)
return;
Vector3d p = Vector3d.of(at);
world.addParticle(new SoulParticle.Data(), p.x + 0.5, p.y + 0.5, p.z + 0.5, 0, 0, 0);
world.addParticle(new SoulBaseParticle.Data(), p.x + 0.5, p.y + 0.01, p.z + 0.5, 0, 0, 0);
}
private static List<List<BlockPos>> genLayers() {
List<List<BlockPos>> layers = new ArrayList<>();
for (int i = 0; i < MAX_DISTANCE; i++)
layers.add(new ArrayList<>());
for (int x = 0; x < MAX_DISTANCE; x++) {
for (int y = 0; y < MAX_DISTANCE; y++) {
for (int z = 0; z < MAX_DISTANCE; z++) {
BlockPos candidate = new BlockPos(x,y,z);
int dist = (int) Math.round(Math.sqrt(
candidate.distanceSq(0,0,0,false)));
if (dist > MAX_DISTANCE)
continue;
if (dist <= 0)
dist = 1;
List<BlockPos> layer = layers.get(dist - 1);
int start = layer.size(), end = start + 1;
layer.add(candidate);
if (candidate.getX() != 0) {
layer.add(new BlockPos(-candidate.getX(), candidate.getY(), candidate.getZ()));
end += 1;
}
if (candidate.getY() != 0) {
for (int i = start; i < end; i++) {
BlockPos prev = layer.get(i);
layer.add(new BlockPos(prev.getX(), -prev.getY(), prev.getZ()));
}
end += end - start;
}
if (candidate.getZ() != 0) {
for (int i = start; i < end; i++) {
BlockPos prev = layer.get(i);
layer.add(new BlockPos(prev.getX(), prev.getY(), -prev.getZ()));
}
}
}
}
}
return layers;
}
public static Stream<BlockPos> getLayer(int idx) {
if (idx < 0 || idx >= MAX_DISTANCE)
return Stream.empty();
return LAYERS.get(idx).stream();
}
}

View file

@ -0,0 +1,110 @@
package com.simibubi.create.content.curiosities.bell;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.simibubi.create.CreateClient;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import com.sun.org.apache.xpath.internal.operations.Bool;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import net.minecraftforge.fml.network.NetworkEvent;
import net.minecraftforge.fml.network.PacketDistributor;
public class SoulPulseEffectHandler {
private List<SoulPulseEffect> pulses;
private Set<BlockPos> occupied;
public SoulPulseEffectHandler() {
pulses = new ArrayList<>();
occupied = new HashSet<>();
}
public void tick(World world) {
for (SoulPulseEffect pulse : pulses) {
List<BlockPos> added = pulse.tick(world);
if (added == null)
continue;
if (pulse.overlaps()) {
for (BlockPos pos : added) {
SoulPulseEffect.spawnParticles(world, pos);
}
} else {
for (BlockPos pos : added) {
if (occupied.contains(pos))
continue;
SoulPulseEffect.spawnParticles(world, pos);
occupied.add(pos);
}
}
}
Map<Boolean, List<SoulPulseEffect>> split = pulses.stream()
.collect(Collectors.partitioningBy(SoulPulseEffect::finished));
for (SoulPulseEffect finished : split.get(true))
finished.removeAdded(occupied);
pulses = split.get(false);
}
public void refresh() {
pulses.clear();
occupied.clear();
}
public static void sendPulsePacket(World world, BlockPos at, int distance, boolean overlaps) {
Chunk chunk = world.getChunkAt(at);
AllPackets.channel.send(PacketDistributor.TRACKING_CHUNK.with(() -> chunk), new Packet(at, distance, overlaps));
}
private void handlePulse(BlockPos pos, int distance, boolean overlaps) {
pulses.add(new SoulPulseEffect(pos, distance, overlaps));
}
public static class Packet extends SimplePacketBase {
public BlockPos pos;
public int distance;
public boolean overlaps;
public Packet(BlockPos pos, int distance, boolean overlaps) {
this.pos = pos;
this.distance = distance;
this.overlaps = overlaps;
}
public Packet(PacketBuffer buffer) {
pos = new BlockPos(buffer.readInt(), buffer.readInt(), buffer.readInt());
distance = buffer.readInt();
overlaps = buffer.readBoolean();
}
@Override
public void write(PacketBuffer buffer) {
buffer.writeInt(pos.getX());
buffer.writeInt(pos.getY());
buffer.writeInt(pos.getZ());
buffer.writeInt(distance);
buffer.writeBoolean(overlaps);
}
@Override
public void handle(Supplier<NetworkEvent.Context> context) {
context.get().enqueueWork(() -> CreateClient.SOUL_PULSE_EFFECT_HANDLER.handlePulse(pos, distance, overlaps));
context.get().setPacketHandled(true);
}
}
}

View file

@ -25,6 +25,7 @@ import com.simibubi.create.content.contraptions.goggles.GoggleOverlayRenderer;
import com.simibubi.create.content.contraptions.goggles.IHaveGoggleInformation;
import com.simibubi.create.content.contraptions.relays.belt.item.BeltConnectorHandler;
import com.simibubi.create.content.curiosities.armor.CopperBacktankArmorLayer;
import com.simibubi.create.content.curiosities.bell.SoulPulseEffectHandler;
import com.simibubi.create.content.curiosities.tools.BlueprintOverlayRenderer;
import com.simibubi.create.content.curiosities.tools.ExtendoGripRenderHandler;
import com.simibubi.create.content.curiosities.zapper.ZapperItem;
@ -114,6 +115,7 @@ public class ClientEvents {
CreateClient.SCHEMATIC_HANDLER.tick();
CreateClient.ZAPPER_RENDER_HANDLER.tick();
CreateClient.POTATO_CANNON_RENDER_HANDLER.tick();
CreateClient.SOUL_PULSE_EFFECT_HANDLER.tick(world);
ContraptionHandler.tick(world);
CapabilityMinecartController.tick(world);
@ -166,9 +168,9 @@ public class ClientEvents {
@SubscribeEvent
public static void onUnloadWorld(WorldEvent.Unload event) {
if (event.getWorld()
.isRemote()) {
if (event.getWorld().isRemote()) {
CreateClient.invalidateRenderers();
CreateClient.SOUL_PULSE_EFFECT_HANDLER.refresh();
AnimationTickHolder.reset();
}
}

View file

@ -9,6 +9,10 @@ import java.util.Vector;
import java.util.function.BiFunction;
import java.util.function.Function;
import com.simibubi.create.content.curiosities.bell.CursedBellBlock;
import net.minecraft.block.BellBlock;
import org.apache.commons.lang3.tuple.Pair;
import com.google.common.collect.ImmutableList;
@ -220,9 +224,9 @@ public class BlockStateGen {
Boolean powered = state.get(CartAssemblerBlock.POWERED);
Boolean backwards = state.get(CartAssemblerBlock.BACKWARDS);
RailShape shape = state.get(CartAssemblerBlock.RAIL_SHAPE);
int yRotation = shape == RailShape.EAST_WEST ? 270 : 0;
if (backwards)
if (backwards)
yRotation += 180;
return ConfiguredModel.builder()
@ -462,6 +466,12 @@ public class BlockStateGen {
};
}
public static <B extends BellBlock> NonNullBiConsumer<DataGenContext<Block, B>, RegistrateBlockstateProvider> bell() {
return (c, p) -> p.horizontalBlock(c.getEntry(), state ->
AssetLookup.partialBaseModel(c, p, state.get(BlockStateProperties.BELL_ATTACHMENT).getString())
);
}
private static void putPart(Map<Pair<String, Axis>, ModelFile> coreModels, MultiPartBlockStateBuilder builder,
Axis axis, String s, boolean up, boolean down, boolean left, boolean right) {
Direction positiveAxis = Direction.getFacingFromAxis(AxisDirection.POSITIVE, axis);

View file

@ -21,6 +21,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.tra
import com.simibubi.create.content.contraptions.components.structureMovement.train.capability.MinecartControllerUpdatePacket;
import com.simibubi.create.content.contraptions.fluids.actors.FluidSplashPacket;
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.ConfigureSequencedGearshiftPacket;
import com.simibubi.create.content.curiosities.bell.SoulPulseEffectHandler;
import com.simibubi.create.content.curiosities.projector.ConfigureProjectorPacket;
import com.simibubi.create.content.curiosities.symmetry.SymmetryEffectPacket;
import com.simibubi.create.content.curiosities.tools.BlueprintAssignCompleteRecipePacket;
@ -114,6 +115,7 @@ public enum AllPackets {
TUNNEL_FLAP(TunnelFlapPacket.class, TunnelFlapPacket::new, PLAY_TO_CLIENT),
FUNNEL_FLAP(FunnelFlapPacket.class, FunnelFlapPacket::new, PLAY_TO_CLIENT),
POTATO_CANNON(PotatoCannonPacket.class, PotatoCannonPacket::new, PLAY_TO_CLIENT),
SOUL_PULSE(SoulPulseEffectHandler.Packet.class, SoulPulseEffectHandler.Packet::new, PLAY_TO_CLIENT),
PERSISTENT_DATA(ISyncPersistentData.Packet.class, ISyncPersistentData.Packet::new, PLAY_TO_CLIENT),
;

View file

@ -0,0 +1,92 @@
{
"credit": "Made with Blockbench",
"textures": {
"0": "create:block/bell",
"particle": "create:block/bell"
},
"elements": [
{
"from": [14, 7, 7],
"to": [16, 9, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]},
"faces": {
"north": {"uv": [10.5, 6, 9.5, 7], "texture": "#0"},
"east": {"uv": [9.5, 7, 10.5, 8], "rotation": 90, "texture": "#0"},
"south": {"uv": [9.5, 6, 10.5, 7], "texture": "#0"},
"west": {"uv": [10, 7, 10.5, 7.5], "texture": "#0"},
"up": {"uv": [9.5, 5, 10.5, 6], "texture": "#0"},
"down": {"uv": [9.5, 7, 10.5, 8], "texture": "#0"}
}
},
{
"from": [0, 7, 7],
"to": [2, 9, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]},
"faces": {
"north": {"uv": [9.5, 6, 10.5, 7], "texture": "#0"},
"east": {"uv": [10, 7, 10.5, 7.5], "texture": "#0"},
"south": {"uv": [10.5, 6, 9.5, 7], "texture": "#0"},
"west": {"uv": [9.5, 7, 10.5, 8], "rotation": 90, "texture": "#0"},
"up": {"uv": [9.5, 5, 10.5, 6], "rotation": 180, "texture": "#0"},
"down": {"uv": [9.5, 7, 10.5, 8], "rotation": 180, "texture": "#0"}
}
},
{
"from": [3, 0, 3],
"to": [13, 2, 13],
"faces": {
"north": {"uv": [0, 15, 5, 16], "texture": "#0"},
"east": {"uv": [0, 15, 5, 16], "texture": "#0"},
"south": {"uv": [0, 15, 5, 16], "texture": "#0"},
"west": {"uv": [0, 15, 5, 16], "texture": "#0"},
"up": {"uv": [0, 10, 5, 15], "texture": "#0"},
"down": {"uv": [5, 10, 10, 15], "texture": "#0"}
}
},
{
"from": [4, 2, 4],
"to": [12, 11, 12],
"faces": {
"north": {"uv": [8, 0, 12, 4.5], "texture": "#0"},
"east": {"uv": [8, 0, 12, 4.5], "texture": "#0"},
"south": {"uv": [8, 0, 12, 4.5], "texture": "#0"},
"west": {"uv": [8, 0, 12, 4.5], "texture": "#0"},
"up": {"uv": [12, 0, 16, 4], "texture": "#0"}
}
},
{
"from": [7, 11, 7],
"to": [9, 15, 9],
"faces": {
"north": {"uv": [8, 6, 9, 8], "texture": "#0"},
"east": {"uv": [8, 6, 9, 8], "texture": "#0"},
"south": {"uv": [8, 6, 9, 8], "texture": "#0"},
"west": {"uv": [8, 6, 9, 8], "texture": "#0"},
"up": {"uv": [8, 5, 9, 6], "texture": "#0"}
}
},
{
"from": [0, 0, 8],
"to": [16, 16, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8.5]},
"faces": {
"north": {"uv": [0, 0, 8, 8], "texture": "#0"}
}
},
{
"from": [0, 0, 7],
"to": [16, 16, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8.5]},
"faces": {
"south": {"uv": [0, 0, 8, 8], "texture": "#0"}
}
}
],
"groups": [
{
"name": "Bell",
"origin": [8, 8, 8],
"children": [0, 1, 2, 3, 4, 5, 6]
}
]
}

View file

@ -0,0 +1,36 @@
{
"credit": "Made with Blockbench",
"textures": {
"1": "create:block/bell_frame_side",
"2": "create:block/bell_frame"
},
"elements": [
{
"from": [0.1, 0, 0],
"to": [15.9, 16, 16],
"rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]},
"faces": {
"east": {"uv": [0, 0, 16, 16], "rotation": 180, "texture": "#1"},
"west": {"uv": [0, 0, 16, 16], "rotation": 180, "texture": "#1"},
"up": {"uv": [0, 0, 15.8, 16], "texture": "#2"}
}
},
{
"from": [15.9, 0, 0],
"to": [0.1, 16, 16],
"rotation": {"angle": 0, "axis": "x", "origin": [8, 8, 8]},
"faces": {
"east": {"uv": [0, 0, 16, 16], "rotation": 180, "texture": "#1"},
"west": {"uv": [0, 0, 16, 16], "rotation": 180, "texture": "#1"},
"up": {"uv": [0, 0, 16, 16], "texture": "#2"}
}
}
],
"groups": [
{
"name": "Frame",
"origin": [8, 8, 8],
"children": [0, 1]
}
]
}

View file

@ -0,0 +1,6 @@
{
"credit": "Made with Blockbench",
"textures": {},
"elements": [],
"groups": []
}

View file

@ -0,0 +1,34 @@
{
"credit": "Made with Blockbench",
"textures": {
"1": "create:block/bell_frame_side",
"2": "create:block/bell_frame"
},
"elements": [
{
"from": [0.1, 0, 0],
"to": [15.9, 16, 16],
"faces": {
"east": {"uv": [0, 0, 16, 16], "texture": "#1"},
"west": {"uv": [0, 0, 16, 16], "texture": "#1"},
"down": {"uv": [0, 0, 15.8, 16], "texture": "#2"}
}
},
{
"from": [15.9, 0, 0],
"to": [0.1, 16, 16],
"faces": {
"east": {"uv": [0, 0, 16, 16], "texture": "#1"},
"west": {"uv": [0, 0, 16, 16], "texture": "#1"},
"down": {"uv": [0, 0, 16, 16], "texture": "#2"}
}
}
],
"groups": [
{
"name": "Frame",
"origin": [8, 8, 8],
"children": [0, 1]
}
]
}

View file

@ -0,0 +1,6 @@
{
"credit": "Made with Blockbench",
"textures": {},
"elements": [],
"groups": []
}

View file

@ -0,0 +1,92 @@
{
"credit": "Made with Blockbench",
"textures": {
"0": "create:block/bell",
"particle": "create:block/bell"
},
"elements": [
{
"from": [14, 7, 7],
"to": [16, 9, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]},
"faces": {
"north": {"uv": [10.5, 6, 9.5, 7], "texture": "#0"},
"east": {"uv": [9.5, 7, 10.5, 8], "rotation": 90, "texture": "#0"},
"south": {"uv": [9.5, 6, 10.5, 7], "texture": "#0"},
"west": {"uv": [10, 7, 10.5, 7.5], "texture": "#0"},
"up": {"uv": [9.5, 5, 10.5, 6], "texture": "#0"},
"down": {"uv": [9.5, 7, 10.5, 8], "texture": "#0"}
}
},
{
"from": [0, 7, 7],
"to": [2, 9, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8]},
"faces": {
"north": {"uv": [9.5, 6, 10.5, 7], "texture": "#0"},
"east": {"uv": [10, 7, 10.5, 7.5], "texture": "#0"},
"south": {"uv": [10.5, 6, 9.5, 7], "texture": "#0"},
"west": {"uv": [9.5, 7, 10.5, 8], "rotation": 90, "texture": "#0"},
"up": {"uv": [9.5, 5, 10.5, 6], "rotation": 180, "texture": "#0"},
"down": {"uv": [9.5, 7, 10.5, 8], "rotation": 180, "texture": "#0"}
}
},
{
"from": [3, 0, 3],
"to": [13, 2, 13],
"faces": {
"north": {"uv": [0, 15, 5, 16], "texture": "#0"},
"east": {"uv": [0, 15, 5, 16], "texture": "#0"},
"south": {"uv": [0, 15, 5, 16], "texture": "#0"},
"west": {"uv": [0, 15, 5, 16], "texture": "#0"},
"up": {"uv": [0, 10, 5, 15], "texture": "#0"},
"down": {"uv": [5, 10, 10, 15], "texture": "#0"}
}
},
{
"from": [4, 2, 4],
"to": [12, 11, 12],
"faces": {
"north": {"uv": [8, 0, 12, 4.5], "texture": "#0"},
"east": {"uv": [8, 0, 12, 4.5], "texture": "#0"},
"south": {"uv": [8, 0, 12, 4.5], "texture": "#0"},
"west": {"uv": [8, 0, 12, 4.5], "texture": "#0"},
"up": {"uv": [12, 0, 16, 4], "texture": "#0"}
}
},
{
"from": [7, 11, 7],
"to": [9, 15, 9],
"faces": {
"north": {"uv": [8, 6, 9, 8], "texture": "#0"},
"east": {"uv": [8, 6, 9, 8], "texture": "#0"},
"south": {"uv": [8, 6, 9, 8], "texture": "#0"},
"west": {"uv": [8, 6, 9, 8], "texture": "#0"},
"up": {"uv": [8, 5, 9, 6], "texture": "#0"}
}
},
{
"from": [0, 0, 8],
"to": [16, 16, 9],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8.5]},
"faces": {
"north": {"uv": [0, 0, 8, 8], "texture": "#0"}
}
},
{
"from": [0, 0, 7],
"to": [16, 16, 8],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 8, 8.5]},
"faces": {
"south": {"uv": [0, 0, 8, 8], "texture": "#0"}
}
}
],
"groups": [
{
"name": "Bell",
"origin": [8, 8, 8],
"children": [0, 1, 2, 3, 4, 5, 6]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 836 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 561 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 586 B