diff --git a/NHSE.Core/Editing/FieldItem/FieldItemColumn.cs b/NHSE.Core/Editing/FieldItem/FieldItemColumn.cs
new file mode 100644
index 0000000..ae5fe7d
--- /dev/null
+++ b/NHSE.Core/Editing/FieldItem/FieldItemColumn.cs
@@ -0,0 +1,24 @@
+namespace NHSE.Core
+{
+ public sealed class FieldItemColumn
+ {
+ /// X Coordinate within the Field Item Layer
+ public readonly int X;
+
+ /// Y Coordinate within the Field Item Layer
+ public readonly int Y;
+
+ /// Offset relative to the start of the Field Item Layer
+ public readonly int Offset;
+
+ public readonly byte[] Data;
+
+ public FieldItemColumn(int x, int y, int offset, byte[] data)
+ {
+ X = x;
+ Y = y;
+ Offset = offset;
+ Data = data;
+ }
+ }
+}
diff --git a/NHSE.Core/Editing/FieldItem/FieldItemDropper.cs b/NHSE.Core/Editing/FieldItem/FieldItemDropper.cs
new file mode 100644
index 0000000..eed06e0
--- /dev/null
+++ b/NHSE.Core/Editing/FieldItem/FieldItemDropper.cs
@@ -0,0 +1,103 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace NHSE.Core
+{
+ ///
+ /// Converts into columns of writable Item tiles.
+ ///
+ public static class FieldItemDropper
+ {
+ private const int MapHeight = FieldItemLayer.FieldItemHeight;
+ private const int MapWidth = FieldItemLayer.FieldItemWidth;
+
+ // Each dropped item is a 2x2 square, with the top left tile being the root node, and the other 3 being extensions pointing back to the root.
+
+ public static bool CanFitDropped(int x, int y, int count, int vStride)
+ {
+ if (2 * (Math.Max(16, y) + vStride) > MapHeight - 32)
+ return false;
+
+ var xStride = count / vStride;
+ if (2 * (Math.Max(16, x) + xStride) > MapWidth - 32)
+ return false;
+
+ return count < (MapHeight * MapWidth / 32);
+ }
+
+ public static IReadOnlyList InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList- item)
+ {
+ int yStride = (item.Count > 16) ? 16 : item.Count;
+ return InjectItemsAsDropped(mapX, mapY, item, yStride);
+ }
+
+ public static IReadOnlyList InjectItemsAsDropped(int mapX, int mapY, IReadOnlyList
- item, int yStride)
+ {
+ var xStride = item.Count / yStride;
+ List result = new(yStride * xStride);
+ for (int i = 0; i < xStride; i++)
+ {
+ var x = mapX + (i * 2);
+ var y = mapY;
+ var itemSlice = item.Skip(i * yStride).Take(yStride).ToArray();
+
+ // Root+ExtensionY
+ var offset = GetTileOffset(x, y);
+ var data = GetColumnRoot(itemSlice);
+ var column = new FieldItemColumn(x, y, offset, data);
+ result.Add(column);
+
+ // Ex X/XY
+ ++x;
+ offset = GetTileOffset(x, y);
+ data = GetColumnExtension(itemSlice);
+ column = new FieldItemColumn(x, y, offset, data);
+ result.Add(column);
+ }
+ return result;
+ }
+
+ private static byte[] GetColumnRoot(Item[] items)
+ {
+ var col = new Item[items.Length * 2];
+ for (int i = 0; i < items.Length; i++)
+ {
+ var item = items[i];
+ var idx = i * 2;
+ col[idx] = GetDroppedItem(item);
+ col[idx + 1] = GetExtension(item, 0, 1);
+ }
+ return Item.SetArray(col);
+ }
+
+ private static byte[] GetColumnExtension(Item[] items)
+ {
+ var col = new Item[items.Length * 2];
+ for (int i = 0; i < items.Length; i++)
+ {
+ var item = items[i];
+ var idx = i * 2;
+ col[idx] = GetExtension(item, 1, 0);
+ col[idx + 1] = GetExtension(item, 1, 1);
+ }
+ return Item.SetArray(col);
+ }
+
+ private static int GetTileOffset(int x, int y)
+ {
+ return Item.SIZE * (y + (x * MapHeight));
+ }
+
+ private static Item GetDroppedItem(Item item)
+ {
+ var copy = new Item();
+ copy.CopyFrom(item);
+ copy.ClearFlags();
+ copy.IsDropped = true;
+ return copy;
+ }
+
+ private static Item GetExtension(Item item, byte x, byte y) => new(Item.EXTENSION) { ExtensionItemId = item.ItemId, ExtensionX = x, ExtensionY = y };
+ }
+}
diff --git a/NHSE.Core/Structures/Item/Item.cs b/NHSE.Core/Structures/Item/Item.cs
index 5270a53..5604758 100644
--- a/NHSE.Core/Structures/Item/Item.cs
+++ b/NHSE.Core/Structures/Item/Item.cs
@@ -23,13 +23,14 @@ public class Item : ICopyableItem
-
[field: FieldOffset(3)] public byte AdditionalParam { get; set; }
[field: FieldOffset(4)] public int FreeParam { get; set; }
- public int Rotation => SystemParam & 3;
- public bool IsBuried => (SystemParam & 4) != 0;
- public bool Is_08 => (SystemParam & 0x08) != 0;
- public bool Is_10 => (SystemParam & 0x10) != 0;
- public bool IsDropped => (SystemParam & 0x20) != 0;
- public bool Is_40 => (SystemParam & 0x40) != 0;
- public bool Is_80 => (SystemParam & 0x80) != 0;
+ public void ClearFlags() => SystemParam = 0;
+ public int Rotation { get => SystemParam & 3; set => SystemParam = (byte)((SystemParam & ~3) | (value & 3)); }
+ public bool IsBuried { get => (SystemParam & 0x04) != 0; set => SystemParam = (byte) ((SystemParam & ~0x04) | (value ? 0x04 : 0)); }
+ public bool Is_08 { get => (SystemParam & 0x08) != 0; set => SystemParam = (byte) ((SystemParam & ~0x08) | (value ? 0x08 : 0)); }
+ public bool Is_10 { get => (SystemParam & 0x10) != 0; set => SystemParam = (byte) ((SystemParam & ~0x10) | (value ? 0x10 : 0)); }
+ public bool IsDropped { get => (SystemParam & 0x20) != 0; set => SystemParam = (byte) ((SystemParam & ~0x20) | (value ? 0x20 : 0)); }
+ public bool Is_40 { get => (SystemParam & 0x40) != 0; set => SystemParam = (byte) ((SystemParam & ~0x40) | (value ? 0x40 : 0)); }
+ public bool Is_80 { get => (SystemParam & 0x80) != 0; set => SystemParam = (byte) ((SystemParam & ~0x80) | (value ? 0x80 : 0)); }
#region Flag1 (Wrapping / Etc)
public bool IsWrapped