feat: Adds item graphic size to Bounds.bin and makes item graphic offset available to gumps (#2115)

This commit is contained in:
Kamron Batman 2025-02-10 19:31:39 -08:00 committed by GitHub
parent 772511d1e8
commit 40479c946a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 96 additions and 50 deletions

Binary file not shown.

Binary file not shown.

View file

@ -1,6 +1,6 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Copyright 2019-2025 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: ArtData.cs *
* *
@ -50,18 +50,18 @@ public class ArtData : IDisposable
}
}
public Rectangle2D GetStaticBounds(int index)
public (ushort Width, ushort Height, Rectangle2D Bounds) GetStaticBounds(int index)
{
if (index is < 0 or > 0x10000)
{
return Rectangle2D.Empty;
return (0, 0, Rectangle2D.Empty);
}
index += 16384;
if (!_dataRanges.TryGetValue(index, out var entry))
{
return Rectangle2D.Empty;
return (0, 0, Rectangle2D.Empty);
}
Span<ushort> buffer = stackalloc ushort[entry.Size / 2];
@ -73,10 +73,10 @@ public class ArtData : IDisposable
if (width == 0 || height == 0)
{
return Rectangle2D.Empty;
return (0, 0, Rectangle2D.Empty);
}
return GetBoundsFromRGBA1555Bitmap(width, height, buffer[4..]);
return (width, height, GetBoundsFromRGBA1555Bitmap(width, height, buffer[4..]));
}
private static Dictionary<int, UOPEntry> LoadMulRanges(string idxPath)

View file

@ -2346,7 +2346,7 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
};
}
var bounds = ItemBounds.Table[itemID & 0x3FFF];
var bounds = ItemBounds.Bounds[itemID & 0x3FFF];
if (doubled)
{

View file

@ -1,6 +1,6 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Copyright 2019-2025 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: ItemBounds.cs *
* *
@ -17,13 +17,15 @@ using System;
using System.IO;
using System.Threading;
using Server.Logging;
using Size = System.ValueTuple<ushort, ushort>;
namespace Server;
public static class ItemBounds
{
private static readonly ILogger logger = LogFactory.GetLogger(typeof(ItemBounds));
private static readonly string _pathToBounds = Path.Combine(Core.BaseDirectory, "Data", "Binary", "Bounds.bin");
private const string _boundsFileName = "ItemBounds.bin";
private static readonly string _boundsFolder = Path.Combine(Core.BaseDirectory, "Data", "Items");
private static bool _isGenerating;
public static void Configure()
@ -32,7 +34,7 @@ public static class ItemBounds
}
[Usage("GenBounds")]
[Description("Asynchronously generates the bounds.bin file from art.mul/artidx.mul or artLegacyMUL.uop to determine container boundaries.")]
[Description("Asynchronously generates the ItemBounds.bin file from art.mul/artidx.mul or artLegacyMUL.uop to determine graphic sizes and boundaries.")]
private static void GenBounds_OnCommand(CommandEventArgs e)
{
GenerateBoundsFileAsync(e.Mobile);
@ -40,19 +42,20 @@ public static class ItemBounds
static ItemBounds()
{
Table = new Rectangle2D[TileData.ItemTable.Length];
if (!File.Exists(_pathToBounds))
if (!File.Exists(Path.Combine(_boundsFolder, _boundsFileName)))
{
logger.Information("Generating {BoundsFilePath}...", "Bounds.bin");
logger.Information("Generating {BoundsFilePath}...", _boundsFileName);
try
{
GenerateBoundsFile();
logger.Information("Generated {BoundsFilePath} successfully.", "Bounds.bin");
GenerateBoundsFile(out var sizes, out var bounds);
Sizes = sizes;
Bounds = bounds;
logger.Information("Generated {BoundsFilePath} successfully.", _boundsFileName);
}
catch (Exception ex)
{
logger.Error(ex, "Failed to generate {BoundsFilePath}", "Bounds.bin");
logger.Error(ex, "Failed to generate {BoundsFilePath}", _boundsFileName);
}
return;
@ -61,7 +64,9 @@ public static class ItemBounds
GenerateTable();
}
public static Rectangle2D[] Table { get; private set; }
public static Size[] Sizes { get; private set; }
public static Rectangle2D[] Bounds { get; private set; }
private static void GenerateBoundsFileAsync(Mobile m)
{
@ -76,16 +81,21 @@ public static class ItemBounds
state =>
{
var from = state as Mobile;
logger.Information("Generating {BoundsFilePath}...", "Bounds.bin");
logger.Information("Generating {BoundsFilePath}...", _boundsFileName);
if (from != null)
{
Core.LoopContext.Post(() => from?.SendMessage("Generating bounds file..."));
Core.LoopContext.Post(() => from.SendMessage("Generating bounds file..."));
}
try
{
var table = GenerateBoundsFile();
Core.LoopContext.Post(() => Table = table);
GenerateBoundsFile(out var sizes, out var bounds);
Core.LoopContext.Post(() =>
{
Sizes = sizes;
Bounds = bounds;
}
);
}
catch (Exception ex)
{
@ -93,21 +103,21 @@ public static class ItemBounds
{
Core.LoopContext.Post(() =>
{
from?.SendMessage("Failed to generate bounds file:");
from?.SendMessage(ex.Message);
from.SendMessage("Failed to generate bounds file:");
from.SendMessage(ex.Message);
}
);
}
logger.Error(ex, "Failed to generate {BoundsFilePath}", "Bounds.bin");
logger.Error(ex, "Failed to generate {BoundsFilePath}", _boundsFileName);
return;
}
if (from != null)
{
Core.LoopContext.Post(
() => from?.SendMessage(
$"Bounds file saved to {Path.GetRelativePath(Core.BaseDirectory, _pathToBounds)}."
() => from.SendMessage(
$"Bounds file saved to {Path.GetRelativePath(Core.BaseDirectory, Path.Combine(_boundsFolder, _boundsFileName))}."
)
);
}
@ -119,48 +129,57 @@ public static class ItemBounds
);
}
private static Rectangle2D[] GenerateBoundsFile()
private static void GenerateBoundsFile(out Size[] sizes, out Rectangle2D[] bounds)
{
var table = new Rectangle2D[TileData.ItemTable.Length];
bounds = new Rectangle2D[TileData.ItemTable.Length];
sizes = new Size[TileData.ItemTable.Length];
using var artData = new ArtData();
if (!artData.IsInitialized)
{
throw new FileNotFoundException("Unable to load art.mul/artidx.mul or artLegacyMUL.uop");
}
using var fs = new FileStream(_pathToBounds, FileMode.Create, FileAccess.Write);
PathUtility.EnsureDirectory(_boundsFolder);
using var fs = new FileStream(Path.Combine(_boundsFolder, _boundsFileName), FileMode.Create, FileAccess.Write);
using var bw = new BinaryWriter(fs);
for (var i = 0; i < table.Length; i++)
for (var i = 0; i < bounds.Length; i++)
{
var bounds = artData.GetStaticBounds(i);
var (w, h, b) = artData.GetStaticBounds(i);
bw.Write((short)bounds.X);
bw.Write((short)bounds.Y);
bw.Write((short)(bounds.X + bounds.Width + 1));
bw.Write((short)(bounds.Y + bounds.Height + 1));
bw.Write(w);
bw.Write(h);
bw.Write((short)b.X);
bw.Write((short)b.Y);
bw.Write((short)(b.X + b.Width + 1));
bw.Write((short)(b.Y + b.Height + 1));
table[i] = bounds;
bounds[i].Set(b.X, b.Y, b.Width, b.Height);
sizes[i] = (w, h);
}
return table;
}
private static void GenerateTable()
{
using var fs = new FileStream(_pathToBounds, FileMode.Open, FileAccess.Read, FileShare.Read);
Bounds = new Rectangle2D[TileData.ItemTable.Length];
Sizes = new Size[TileData.ItemTable.Length];
using var fs = new FileStream(Path.Combine(_boundsFolder, _boundsFileName), FileMode.Open, FileAccess.Read, FileShare.Read);
using var bin = new BinaryReader(fs);
var count = Math.Min(Table.Length, (int)(fs.Length / 8));
var count = Math.Min(Bounds.Length, (int)(fs.Length / 8));
for (var i = 0; i < count; ++i)
{
Sizes[i] = (bin.ReadUInt16(), bin.ReadUInt16());
int xMin = bin.ReadInt16();
int yMin = bin.ReadInt16();
int xMax = bin.ReadInt16();
int yMax = bin.ReadInt16();
Table[i].Set(xMin, yMin, xMax - xMin, yMax - yMin);
Bounds[i].Set(xMin, yMin, xMax - xMin, yMax - yMin);
}
}
}

View file

@ -187,7 +187,7 @@ namespace Server.Gumps
Console.WriteLine("Type {0} does not have a valid item id or shrink table entry.", obj.Type);
}
var bounds = ItemBounds.Table[itemID];
var bounds = ItemBounds.Bounds[itemID];
if (itemID != 1 && bounds.Height < EntryHeight * 2)
{

View file

@ -118,7 +118,7 @@ public class SheriffGump : FactionGump
private void CenterItem(int itemID, int x, int y, int w, int h)
{
var rc = ItemBounds.Table[itemID];
var rc = ItemBounds.Bounds[itemID];
AddItem(x + (w - rc.Width) / 2 - rc.X, y + (h - rc.Height) / 2 - rc.Y, itemID);
}

View file

@ -1,6 +1,6 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2024 - ModernUO Development Team *
* Copyright 2019-2025 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: BaseGump.cs *
* *
@ -86,4 +86,31 @@ public abstract class BaseGump
return hash == 461 ? hash * primeMulti : hash;
}
}
public static Point2D GetItemGraphicOffset(int itemId)
{
var (width, height) = ItemBounds.Sizes[itemId];
var x = 0;
var y = 0;
if (width > 44)
{
x -= (width - 44) / 2;
}
else if (width < 44)
{
x += (44 - width) / 2;
}
if (height > 44)
{
y -= height - 44;
}
else if (height < 44)
{
y += 44 - height;
}
return new Point2D(x, y);
}
}

View file

@ -62,7 +62,7 @@ namespace Server.Gumps
{
var entry = Rewards[i];
var bounds = ItemBounds.Table[entry.ItemID];
var bounds = ItemBounds.Bounds[entry.ItemID];
var height = Math.Max(36, bounds.Height);
if (offset + height > 320)

View file

@ -125,7 +125,7 @@ public partial class PlagueBeastBackpack : BaseContainer
return false;
}
var ir = ItemBounds.Table[item.ItemID];
var ir = ItemBounds.Bounds[item.ItemID];
int x, y;
var cx = p.X + ir.X + ir.Width / 2;
var cy = p.Y + ir.Y + ir.Height / 2;
@ -134,7 +134,7 @@ public partial class PlagueBeastBackpack : BaseContainer
{
if (Items[i] is PlagueBeastComponent innard)
{
var r = ItemBounds.Table[innard.ItemID];
var r = ItemBounds.Bounds[innard.ItemID];
x = innard.X + r.X;
y = innard.Y + r.Y;

View file

@ -4733,7 +4733,7 @@ namespace Server.Mobiles
for (int i = _page * 4, y = 72; i < (_page + 1) * 4 && i < _items.Length; ++i, y += 75)
{
var item = _items[i];
var b = ItemBounds.Table[item.ItemID];
var b = ItemBounds.Bounds[item.ItemID];
builder.AddImageTiledButton(
40,

View file

@ -422,7 +422,7 @@ public class AnimalForm : NinjaSpell
int y = Math.DivRem(pos, 2, out var rem) * 64 + 44;
int x = rem == 0 ? 14 : 264;
Rectangle2D b = ItemBounds.Table[entry.ItemID];
Rectangle2D b = ItemBounds.Bounds[entry.ItemID];
builder.AddImageTiledButton(x, y, 0x918, 0x919, i + 1, GumpButtonType.Reply, 0, entry.ItemID,
entry.Hue, 40 - b.Width / 2 - b.X, 30 - b.Height / 2 - b.Y, entry.Tooltip);