initial implementation

This commit is contained in:
2026-03-18 23:15:17 +01:00
parent 897c5a80e1
commit 56b9a0d209
13 changed files with 814 additions and 20 deletions

View File

@@ -1,10 +1,25 @@
{
"total": 123,
"total": 11122,
"sessions": [
{
"begin": "2026-03-17T23:54:43+01:00",
"end": "2026-03-17T23:56:46+01:00",
"duration": 123
},
{
"begin": "2026-03-17T23:56:56+01:00",
"end": "2026-03-18T00:27:03+01:00",
"duration": 1807
},
{
"begin": "2026-03-18T19:11:43+01:00",
"end": "2026-03-18T21:24:21+01:00",
"duration": 7958
},
{
"begin": "2026-03-18T22:54:14+01:00",
"end": "2026-03-18T23:14:49+01:00",
"duration": 1234
}
]
}

View File

@@ -0,0 +1,202 @@
using System;
using Vintagestory.API.Client;
using Vintagestory.API.Common;
using Vintagestory.API.Datastructures;
using Vintagestory.API.MathTools;
using Vintagestory.GameContent;
namespace ButterflyPins.BlockEntities;
public class BlockEntityButterflyPinBoard : BlockEntityDisplay
{
private const int MaxBoardSlots = 9;
private const float BoardMargin = 1f / 16f;
private const float BoardSurface = 1f / 16f;
private const string ButterflyPinPrefix = "clothes-butterflypin-";
private readonly InventoryGeneric inventory;
public override InventoryBase Inventory => inventory;
public override string InventoryClassName => "butterflypinboard";
public override int DisplayedItems => ActiveSlotCount;
private int Rows => Math.Max(1, Block?.Attributes?["rows"].AsInt(2) ?? 2);
private int Columns => Math.Max(1, Block?.Attributes?["columns"].AsInt(2) ?? 2);
private float RenderScale => Block?.Attributes?["renderScale"].AsFloat(0.18f) ?? 0.18f;
private int ActiveSlotCount => Rows * Columns;
public BlockEntityButterflyPinBoard()
{
inventory = new InventoryGeneric(MaxBoardSlots, null, null);
}
public override void FromTreeAttributes(ITreeAttribute tree, IWorldAccessor worldForResolving)
{
base.FromTreeAttributes(tree, worldForResolving);
RedrawAfterReceivingTreeAttributes(worldForResolving);
}
public bool TryInsert(IPlayer byPlayer, int slotIndex, ItemSlot activeSlot)
{
if (!IsValidSlot(slotIndex) || activeSlot.Empty || !CanInsert(slotIndex, activeSlot.Itemstack))
return false;
ItemStack sourceStack = activeSlot.Itemstack;
ItemStack placedStack = sourceStack.Clone();
placedStack.StackSize = 1;
inventory[slotIndex].Itemstack = placedStack;
activeSlot.TakeOut(1);
activeSlot.MarkDirty();
inventory[slotIndex].MarkDirty();
MarkChanged();
return true;
}
public bool TryTake(IPlayer byPlayer, int slotIndex)
{
if (!CanTake(slotIndex))
return false;
ItemSlot slot = inventory[slotIndex];
ItemStack stack = slot.Itemstack!;
slot.Itemstack = null;
slot.MarkDirty();
if (!byPlayer.InventoryManager.TryGiveItemstack(stack, true))
Api.World.SpawnItemEntity(stack, Pos.ToVec3d().Add(0.5, 0.5, 0.5));
MarkChanged();
return true;
}
public bool CanInsert(int slotIndex, ItemStack stack)
{
return IsValidSlot(slotIndex) && inventory[slotIndex].Empty && IsButterflyPin(stack);
}
public bool CanTake(int slotIndex)
{
return IsValidSlot(slotIndex) && !inventory[slotIndex].Empty;
}
public void DropContents()
{
for (int slotIndex = 0; slotIndex < ActiveSlotCount; slotIndex++)
{
ItemSlot slot = inventory[slotIndex];
ItemStack? stack = slot.Itemstack;
if (stack == null)
continue;
Api.World.SpawnItemEntity(stack, Pos.ToVec3d().Add(0.5, 0.5, 0.5));
slot.Itemstack = null;
slot.MarkDirty();
}
}
public int GetSlotIndex(Vec3d hitPosition)
{
Vec3d local = ToBaseOrientation(hitPosition);
if (local.X < BoardMargin || local.X > 1 - BoardMargin || local.Y < BoardMargin || local.Y > 1 - BoardMargin)
return -1;
double width = (1 - (BoardMargin * 2)) / Columns;
double height = (1 - (BoardMargin * 2)) / Rows;
int column = GameMath.Clamp((int)((local.X - BoardMargin) / width), 0, Columns - 1);
int rowFromBottom = GameMath.Clamp((int)((local.Y - BoardMargin) / height), 0, Rows - 1);
int rowFromTop = Rows - 1 - rowFromBottom;
return (rowFromTop * Columns) + column;
}
private bool IsValidSlot(int slotIndex)
{
return slotIndex >= 0 && slotIndex < ActiveSlotCount;
}
private bool IsButterflyPin(ItemStack stack)
{
AssetLocation? code = stack.Collectible?.Code;
return code != null && code.Path.StartsWith(ButterflyPinPrefix, StringComparison.Ordinal);
}
private void MarkChanged()
{
RedrawAfterReceivingTreeAttributes(Api.World);
base.MarkDirty(true);
Api.World.BlockAccessor.MarkBlockDirty(Pos);
}
private Vec3d ToBaseOrientation(Vec3d hitPosition)
{
Vec3d centered = new(hitPosition.X - 0.5, hitPosition.Y, hitPosition.Z - 0.5);
float angle = -GetRotationY();
float sin = GameMath.Sin(angle);
float cos = GameMath.Cos(angle);
return new Vec3d(
(centered.X * cos) - (centered.Z * sin) + 0.5,
hitPosition.Y,
(centered.X * sin) + (centered.Z * cos) + 0.5
);
}
protected override float[][] genTransformationMatrices()
{
float[][] matrices = new float[DisplayedItems][];
double cellWidth = (1 - (BoardMargin * 2)) / Columns;
double cellHeight = (1 - (BoardMargin * 2)) / Rows;
float stepX = 0.75f / Columns;
float stepY = 0.75f / Rows;
float yOffset = (Rows == 2 && Columns == 2 ? 1.0f : 0.75f) + 0.07f;
float boardRotationY = GetRotationY();
float itemRotationY = boardRotationY - GameMath.PIHALF;
for (int slotIndex = 0; slotIndex < DisplayedItems; slotIndex++)
{
int row = slotIndex / Columns;
int column = slotIndex % Columns;
float x = (float)(1 - BoardMargin - (0.5 * cellWidth) - (column * stepX));
float y = (float)(BoardMargin + (0.5 * cellHeight) + ((Rows - 1 - row) * stepY));
float z = Columns == 2 ? -0/1f : -1/48f;
float xOffset = Columns == 2 ? 0.465f : 0.475f;
Vec3f off = new(x - xOffset, y - yOffset, z + 0.5f);
off = new Matrixf().RotateY(boardRotationY).TransformVector(off.ToVec4f(0)).XYZ;
matrices[slotIndex] = new Matrixf()
.Translate(off.X, off.Y, off.Z)
.Translate(0.5f, 0f, 0.5f)
.RotateY(itemRotationY)
.Scale(RenderScale, RenderScale, RenderScale)
.Translate(-0.5f, 0f, -0.5f)
.Values;
}
return matrices;
}
public float GetRotationY()
{
string side = Block?.Variant?["side"] ?? "south";
return side switch
{
"north" => GameMath.PI,
"east" => GameMath.PIHALF,
"west" => GameMath.PI + GameMath.PIHALF,
_ => 0f,
};
}
public float GetRenderScale()
{
return RenderScale;
}
}

View File

@@ -0,0 +1,38 @@
using ButterflyPins.BlockEntities;
using Vintagestory.API.Common;
using Vintagestory.API.MathTools;
namespace ButterflyPins.Blocks;
public class BlockButterflyPinBoard : Block
{
public override bool OnBlockInteractStart(IWorldAccessor world, IPlayer byPlayer, BlockSelection blockSel)
{
if (blockSel == null)
return base.OnBlockInteractStart(world, byPlayer, blockSel);
if (world.BlockAccessor.GetBlockEntity(blockSel.Position) is not BlockEntityButterflyPinBoard board)
return base.OnBlockInteractStart(world, byPlayer, blockSel);
int slotIndex = board.GetSlotIndex(blockSel.HitPosition);
if (slotIndex < 0)
return base.OnBlockInteractStart(world, byPlayer, blockSel);
ItemSlot activeSlot = byPlayer.InventoryManager.ActiveHotbarSlot;
if (world.Side == EnumAppSide.Client)
return (!activeSlot.Empty && board.CanInsert(slotIndex, activeSlot.Itemstack)) || board.CanTake(slotIndex);
if (!activeSlot.Empty && board.TryInsert(byPlayer, slotIndex, activeSlot))
return true;
return board.TryTake(byPlayer, slotIndex) || base.OnBlockInteractStart(world, byPlayer, blockSel);
}
public override void OnBlockBroken(IWorldAccessor world, BlockPos pos, IPlayer byPlayer, float dropQuantityMultiplier = 1)
{
if (world.Side == EnumAppSide.Server && world.BlockAccessor.GetBlockEntity(pos) is BlockEntityButterflyPinBoard board)
board.DropContents();
base.OnBlockBroken(world, pos, byPlayer, dropQuantityMultiplier);
}
}

View File

@@ -75,16 +75,16 @@
<Warning Text="No VS_CONFIGLIB - will compile without supporting ConfigLib. Set VS_CONFIGLIB env var to a path that contains the configlib's dlls." />
</Target>
<ItemGroup>
<Content Include="modinfo.json">
<ItemGroup>
<Content Include="modinfo.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Content>
</ItemGroup>
<ItemGroup>
<Content Include="modicon.png" Condition="Exists('modicon.png')">
<ItemGroup>
<Content Include="modicon.png" Condition="Exists('modicon.png')">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
</Content>
</ItemGroup>
</Project>

View File

@@ -1,11 +0,0 @@
using Vintagestory.API.Common;
namespace ButterflyPins.Core;
public class ButterflyPinsModSystem : ModSystem
{
public override void Start(ICoreAPI api)
{
api.Logger.Notification("ButterflyPins loaded");
}
}

View File

@@ -0,0 +1,15 @@
using ButterflyPins.BlockEntities;
using ButterflyPins.Blocks;
using Vintagestory.API.Common;
namespace ButterflyPins.Systems;
public class ButterflyPinsModSystem : ModSystem
{
public override void Start(ICoreAPI api)
{
api.RegisterBlockClass("BlockButterflyPinBoard", typeof(BlockButterflyPinBoard));
api.RegisterBlockEntityClass("ButterflyPinBoard", typeof(BlockEntityButterflyPinBoard));
api.Logger.Notification("ButterflyPins loaded");
}
}

View File

@@ -0,0 +1,92 @@
{
"code": "pinboard2x2",
"class": "BlockButterflyPinBoard",
"entityClass": "ButterflyPinBoard",
"behaviors": [
{ "name": "HorizontalAttachable" },
{ "name": "Lockable" }
],
"variantgroups": [
{ "code": "side", "loadFromProperties": "abstract/horizontalorientation" }
],
"attributes": {
"rows": 2,
"columns": 2,
"renderScale": 0.85,
"reinforcable": true
},
"creativeinventory": {
"general": ["pinboard2x2-south"],
"decorative": ["pinboard2x2-south"]
},
"shapebytype": {
"*-north": { "base": "butterflypins:block/pinboard-2x2", "rotateY": 180 },
"*-east": { "base": "butterflypins:block/pinboard-2x2", "rotateY": 90 },
"*-south": { "base": "butterflypins:block/pinboard-2x2", "rotateY": 0 },
"*-west": { "base": "butterflypins:block/pinboard-2x2", "rotateY": 270 }
},
"blockmaterial": "Wood",
"drawtype": "json",
"replaceable": 900,
"resistance": 1.5,
"lightAbsorption": 0,
"faceCullMode": "NeverCull",
"sidesolid": { "all": false },
"sideopaque": { "all": false },
"emitSideAo": { "all": false },
"collisionbox": {
"x1": 0.0625,
"y1": 0.0625,
"z1": 0.9375,
"x2": 0.9375,
"y2": 0.9375,
"z2": 1.0,
"rotateYByType": {
"*-north": 180,
"*-east": 90,
"*-south": 0,
"*-west": 270
}
},
"selectionbox": {
"x1": 0.0625,
"y1": 0.0625,
"z1": 0.9375,
"x2": 0.9375,
"y2": 0.9375,
"z2": 1.0,
"rotateYByType": {
"*-north": 180,
"*-east": 90,
"*-south": 0,
"*-west": 270
}
},
"combustibleProps": {
"burnTemperature": 600,
"burnDuration": 20
},
"sounds": {
"place": "block/planks",
"break": "block/planks",
"hit": "block/planks"
},
"materialDensity": 400,
"guiTransform": {
"rotation": { "x": -19, "y": 136, "z": 0 },
"origin": { "x": 0.5, "y": 0.5, "z": 0.5 },
"scale": 1.9
},
"groundTransform": {
"translation": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": -90, "y": 0, "z": 0 },
"origin": { "x": 0.5, "y": 0, "z": 0.5 },
"scale": 2.2
},
"tpHandTransform": {
"translation": { "x": -0.45, "y": -0.8, "z": 0.04 },
"rotation": { "x": 0, "y": 0, "z": -10 },
"origin": { "x": 0.5, "y": 0.5, "z": 0.5 },
"scale": 0.95
}
}

View File

@@ -0,0 +1,92 @@
{
"code": "pinboard3x3",
"class": "BlockButterflyPinBoard",
"entityClass": "ButterflyPinBoard",
"behaviors": [
{ "name": "HorizontalAttachable" },
{ "name": "Lockable" }
],
"variantgroups": [
{ "code": "side", "loadFromProperties": "abstract/horizontalorientation" }
],
"attributes": {
"rows": 3,
"columns": 3,
"renderScale": 0.675,
"reinforcable": true
},
"creativeinventory": {
"general": ["pinboard3x3-south"],
"decorative": ["pinboard3x3-south"]
},
"shapebytype": {
"*-north": { "base": "butterflypins:block/pinboard-3x3", "rotateY": 180 },
"*-east": { "base": "butterflypins:block/pinboard-3x3", "rotateY": 90 },
"*-south": { "base": "butterflypins:block/pinboard-3x3", "rotateY": 0 },
"*-west": { "base": "butterflypins:block/pinboard-3x3", "rotateY": 270 }
},
"blockmaterial": "Wood",
"drawtype": "json",
"replaceable": 900,
"resistance": 1.5,
"lightAbsorption": 0,
"faceCullMode": "NeverCull",
"sidesolid": { "all": false },
"sideopaque": { "all": false },
"emitSideAo": { "all": false },
"collisionbox": {
"x1": 0.0625,
"y1": 0.0625,
"z1": 0.9375,
"x2": 0.9375,
"y2": 0.9375,
"z2": 1.0,
"rotateYByType": {
"*-north": 180,
"*-east": 90,
"*-south": 0,
"*-west": 270
}
},
"selectionbox": {
"x1": 0.0625,
"y1": 0.0625,
"z1": 0.9375,
"x2": 0.9375,
"y2": 0.9375,
"z2": 1.0,
"rotateYByType": {
"*-north": 180,
"*-east": 90,
"*-south": 0,
"*-west": 270
}
},
"combustibleProps": {
"burnTemperature": 600,
"burnDuration": 25
},
"sounds": {
"place": "block/planks",
"break": "block/planks",
"hit": "block/planks"
},
"materialDensity": 400,
"guiTransform": {
"rotation": { "x": -19, "y": 136, "z": 0 },
"origin": { "x": 0.5, "y": 0.5, "z": 0.5 },
"scale": 1.9
},
"groundTransform": {
"translation": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": -90, "y": 0, "z": 0 },
"origin": { "x": 0.5, "y": 0, "z": 0.5 },
"scale": 2.2
},
"tpHandTransform": {
"translation": { "x": -0.45, "y": -0.8, "z": 0.04 },
"rotation": { "x": 0, "y": 0, "z": -10 },
"origin": { "x": 0.5, "y": 0.5, "z": 0.5 },
"scale": 0.95
}
}

View File

@@ -0,0 +1,4 @@
{
"block-pinboard2x2-*": "Butterfly Pin Board (2x2)",
"block-pinboard3x3-*": "Butterfly Pin Board (3x3)"
}

View File

@@ -0,0 +1,10 @@
{
"ingredientPattern": "PPP\tS_S\tPPP",
"ingredients": {
"P": { "type": "item", "code": "plank-*" },
"S": { "type": "item", "code": "stick" }
},
"width": 3,
"height": 3,
"output": { "type": "block", "code": "pinboard2x2-south" }
}

View File

@@ -0,0 +1,10 @@
{
"ingredientPattern": "PPP\tPSP\tPPP",
"ingredients": {
"P": { "type": "item", "code": "plank-*" },
"S": { "type": "item", "code": "stick" }
},
"width": 3,
"height": 3,
"output": { "type": "block", "code": "pinboard3x3-south" }
}

View File

@@ -0,0 +1,131 @@
{
"editor": {
"allAngles": false,
"singleTexture": false
},
"textureWidth": 16,
"textureHeight": 16,
"textures": {
"wood": "game:block/wood/debarked/aged",
"metal": "game:block/metal/tarnished/rusty-iron"
},
"elements": [
{
"name": "board",
"from": [1.0, 1.0, 15.0],
"to": [15.0, 15.0, 16.0],
"faces": {
"north": { "texture": "#wood", "uv": [1.0, 1.0, 15.0, 15.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 14.0] },
"south": { "texture": "#wood", "uv": [1.0, 1.0, 15.0, 15.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 14.0] },
"up": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"down": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] }
}
},
{
"name": "lipTop",
"from": [1.0, 14.0, 14.5],
"to": [15.0, 15.0, 15.0],
"faces": {
"north": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 1.0] },
"south": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 1.0] },
"up": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 0.5] },
"down": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 0.5] }
}
},
{
"name": "lipBottom",
"from": [1.0, 1.0, 14.5],
"to": [15.0, 2.0, 15.0],
"faces": {
"north": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 1.0] },
"south": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 1.0] },
"up": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 0.5] },
"down": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 0.5] }
}
},
{
"name": "lipLeft",
"from": [1.0, 2.0, 14.5],
"to": [2.0, 14.0, 15.0],
"faces": {
"north": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 12.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 12.0] },
"south": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 12.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 12.0] },
"up": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 0.5] },
"down": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 0.5] }
}
},
{
"name": "lipRight",
"from": [14.0, 2.0, 14.5],
"to": [15.0, 14.0, 15.0],
"faces": {
"north": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 12.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 12.0] },
"south": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 12.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 12.0] },
"up": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 0.5] },
"down": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 0.5] }
}
},
{
"name": "pinTopLeft",
"from": [4.75, 10.75, 14.2],
"to": [5.25, 11.25, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinTopRight",
"from": [10.75, 10.75, 14.2],
"to": [11.25, 11.25, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinBottomLeft",
"from": [4.75, 4.75, 14.2],
"to": [5.25, 5.25, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinBottomRight",
"from": [10.75, 4.75, 14.2],
"to": [11.25, 5.25, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
}
]
}

View File

@@ -0,0 +1,196 @@
{
"editor": {
"allAngles": false,
"singleTexture": false
},
"textureWidth": 16,
"textureHeight": 16,
"textures": {
"wood": "game:block/wood/debarked/aged",
"metal": "game:block/metal/tarnished/rusty-iron"
},
"elements": [
{
"name": "board",
"from": [1.0, 1.0, 15.0],
"to": [15.0, 15.0, 16.0],
"faces": {
"north": { "texture": "#wood", "uv": [1.0, 1.0, 15.0, 15.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 14.0] },
"south": { "texture": "#wood", "uv": [1.0, 1.0, 15.0, 15.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 14.0] },
"up": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"down": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] }
}
},
{
"name": "lipTop",
"from": [1.0, 14.0, 14.5],
"to": [15.0, 15.0, 15.0],
"faces": {
"north": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 1.0] },
"south": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 1.0] },
"up": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 0.5] },
"down": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 0.5] }
}
},
{
"name": "lipBottom",
"from": [1.0, 1.0, 14.5],
"to": [15.0, 2.0, 15.0],
"faces": {
"north": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 1.0] },
"south": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 1.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 1.0] },
"up": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 0.5] },
"down": { "texture": "#wood", "uv": [1.0, 0.0, 15.0, 0.5] }
}
},
{
"name": "lipLeft",
"from": [1.0, 2.0, 14.5],
"to": [2.0, 14.0, 15.0],
"faces": {
"north": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 12.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 12.0] },
"south": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 12.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 12.0] },
"up": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 0.5] },
"down": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 0.5] }
}
},
{
"name": "lipRight",
"from": [14.0, 2.0, 14.5],
"to": [15.0, 14.0, 15.0],
"faces": {
"north": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 12.0] },
"east": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 12.0] },
"south": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 12.0] },
"west": { "texture": "#wood", "uv": [0.0, 0.0, 0.5, 12.0] },
"up": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 0.5] },
"down": { "texture": "#wood", "uv": [0.0, 0.0, 1.0, 0.5] }
}
},
{
"name": "pinA",
"from": [4.0, 12.0, 14.2],
"to": [4.5, 12.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinB",
"from": [8.0, 12.0, 14.2],
"to": [8.5, 12.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinC",
"from": [12.0, 12.0, 14.2],
"to": [12.5, 12.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinD",
"from": [4.0, 8.0, 14.2],
"to": [4.5, 8.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinE",
"from": [8.0, 8.0, 14.2],
"to": [8.5, 8.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinF",
"from": [12.0, 8.0, 14.2],
"to": [12.5, 8.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinG",
"from": [4.0, 4.0, 14.2],
"to": [4.5, 4.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinH",
"from": [8.0, 4.0, 14.2],
"to": [8.5, 4.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
},
{
"name": "pinI",
"from": [12.0, 4.0, 14.2],
"to": [12.5, 4.5, 14.8],
"faces": {
"north": { "texture": "#metal", "uv": [12.0, 11.5, 12.5, 12.0] },
"east": { "texture": "#metal", "uv": [10.5, 12.0, 11.0, 12.5] },
"south": { "texture": "#metal", "uv": [11.0, 12.5, 11.5, 13.0] },
"west": { "texture": "#metal", "uv": [12.0, 12.0, 12.5, 12.5] },
"up": { "texture": "#metal", "uv": [10.5, 12.5, 11.0, 13.0] },
"down": { "texture": "#metal", "uv": [10.5, 11.5, 11.0, 12.0] }
}
}
]
}