262 lines
8.1 KiB
C#
262 lines
8.1 KiB
C#
using System;
|
|
using System.Text;
|
|
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 float DisplayOriginX => GetDisplayFloat("displayOriginX", 0.31666666f);
|
|
|
|
private float DisplayOriginY => GetDisplayFloat("displayOriginY", 0.01333332f);
|
|
|
|
private float DisplayOriginZ => GetDisplayFloat("displayOriginZ", 0.47916666f);
|
|
|
|
private float DisplayStepX => GetDisplayFloat("displayStepX", -0.25f);
|
|
|
|
private float DisplayStepY => GetDisplayFloat("displayStepY", -0.25f);
|
|
|
|
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 override void GetBlockInfo(IPlayer forPlayer, StringBuilder dsc)
|
|
{
|
|
int highlightedSlot = -1;
|
|
BlockSelection? selection = forPlayer?.CurrentBlockSelection;
|
|
if (selection?.Position != null && selection.Position.Equals(Pos))
|
|
highlightedSlot = GetSlotIndex(selection.HitPosition);
|
|
|
|
AppendContentsBlockInfo(dsc, highlightedSlot);
|
|
}
|
|
|
|
public bool AppendContentsBlockInfo(StringBuilder builder, int highlightedSlot)
|
|
{
|
|
int initialLength = builder.Length;
|
|
|
|
for (int slotIndex = 0; slotIndex < ActiveSlotCount; slotIndex++)
|
|
{
|
|
ItemStack? stack = inventory[slotIndex].Itemstack;
|
|
if (stack == null)
|
|
continue;
|
|
|
|
if (builder.Length > initialLength)
|
|
builder.AppendLine();
|
|
else if (initialLength > 0)
|
|
builder.AppendLine();
|
|
|
|
string itemName = stack.GetName();
|
|
if (slotIndex == highlightedSlot)
|
|
{
|
|
builder.Append("<font color=\"#00ffff\">");
|
|
builder.Append(itemName);
|
|
builder.Append("</font>");
|
|
continue;
|
|
}
|
|
|
|
builder.Append(itemName);
|
|
}
|
|
|
|
return builder.Length > initialLength;
|
|
}
|
|
|
|
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);
|
|
if (IsNorthSouthVariant())
|
|
column = (Columns - 1) - column;
|
|
|
|
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
|
|
);
|
|
}
|
|
|
|
private float GetDisplayFloat(string key, float defaultValue)
|
|
{
|
|
return Block?.Attributes?[key].AsFloat(defaultValue) ?? defaultValue;
|
|
}
|
|
|
|
protected override float[][] genTransformationMatrices()
|
|
{
|
|
float[][] matrices = new float[DisplayedItems][];
|
|
float boardRotationY = GetRotationY();
|
|
float itemRotationY = boardRotationY - GameMath.PIHALF;
|
|
|
|
for (int slotIndex = 0; slotIndex < DisplayedItems; slotIndex++)
|
|
{
|
|
int row = slotIndex / Columns;
|
|
int column = slotIndex % Columns;
|
|
|
|
Vec3f off = new(
|
|
DisplayOriginX + (column * DisplayStepX),
|
|
DisplayOriginY + (row * DisplayStepY),
|
|
DisplayOriginZ
|
|
);
|
|
off = new Matrixf().RotateY(boardRotationY).TransformVector(off.ToVec4f(0)).XYZ;
|
|
|
|
matrices[slotIndex] = new Matrixf()
|
|
.Translate(off.X, off.Y, off.Z)
|
|
.Translate(0.5f, 0.5f, 0.5f)
|
|
.RotateY(itemRotationY)
|
|
.Scale(RenderScale, RenderScale, RenderScale)
|
|
.Translate(-0.5f, -0.5f, -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,
|
|
};
|
|
}
|
|
|
|
private bool IsNorthSouthVariant()
|
|
{
|
|
string side = Block?.Variant?["side"] ?? "south";
|
|
return side is "north" or "south";
|
|
}
|
|
|
|
public float GetRenderScale()
|
|
{
|
|
return RenderScale;
|
|
}
|
|
} |