Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 115 additions & 10 deletions Ultima/AnimationEdit.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,17 @@ public sealed class AnimationEdit
private static FileIndex _fileIndex3 = new FileIndex("Anim3.idx", "Anim3.mul", -1);
private static FileIndex _fileIndex4 = new FileIndex("Anim4.idx", "Anim4.mul", -1);
private static FileIndex _fileIndex5 = new FileIndex("Anim5.idx", "Anim5.mul", -1);

// Custom universal 175-slot block anim
private static FileIndex _fileIndex6 = new FileIndex("anim_custom.idx", "anim_custom.mul", -1);

private static AnimIdx[] _animCache;
private static AnimIdx[] _animCache2;
private static AnimIdx[] _animCache3;
private static AnimIdx[] _animCache4;
private static AnimIdx[] _animCache5;
// Ne cache for case 6: custom universal 175-slot block anim
private static AnimIdx[] _animCache6;

static AnimationEdit()
{
Expand Down Expand Up @@ -50,6 +55,12 @@ private static void InitializeCache()
{
_animCache5 = new AnimIdx[_fileIndex5.IdxLength / 12];
}

// Init cache for custom anim
if (_fileIndex6 != null && _fileIndex6.IdxLength > 0)
{
_animCache6 = new AnimIdx[_fileIndex6.IdxLength / 12];
}
}

/// <summary>
Expand All @@ -62,6 +73,8 @@ public static void Reload()
_fileIndex3 = new FileIndex("Anim3.idx", "Anim3.mul", -1);
_fileIndex4 = new FileIndex("Anim4.idx", "Anim4.mul", -1);
_fileIndex5 = new FileIndex("Anim5.idx", "Anim5.mul", -1);

_fileIndex6 = new FileIndex("anim_custom.idx", "anim_custom.mul", -1);

InitializeCache();
}
Expand Down Expand Up @@ -147,6 +160,12 @@ private static void GetFileIndex(
index = 35000 + ((body - 400) * 175);
}

break;
// NEW universal custom: anim_custom
case 6:
fileIndex = _fileIndex6;
index = body * 175; // universal block

break;
}

Expand Down Expand Up @@ -176,6 +195,8 @@ private static AnimIdx[] GetCache(int fileType)
return _animCache4;
case 5:
return _animCache5;
case 6:
return _animCache6;
default:
return _animCache;
}
Expand All @@ -184,7 +205,7 @@ private static AnimIdx[] GetCache(int fileType)
public static AnimIdx GetAnimation(int fileType, int body, int action, int dir)
{
AnimIdx[] cache = GetCache(fileType);

GetFileIndex(body, fileType, action, dir, out FileIndex fileIndex, out int index);

if (cache?[index] != null)
Expand All @@ -193,7 +214,7 @@ public static AnimIdx GetAnimation(int fileType, int body, int action, int dir)
}

return cache[index] = new AnimIdx(index, fileIndex);
}
}

public static bool IsActionDefined(int fileType, int body, int action)
{
Expand All @@ -215,13 +236,48 @@ public static bool IsActionDefined(int fileType, int body, int action)
bool valid = fileIndex.Valid(index, out int length, out int _, out bool _);

return valid && length >= 1;
}
}

public static void LoadFromVD(int fileType, int body, BinaryReader bin)
public static void SetBodyType(int fileType, int body, int bodyType)
{
AnimIdx[] cache = GetCache(fileType);
GetFileIndex(body, fileType, 0, 0, out FileIndex fileIndex, out int startIndex);

int totalSlots = 35 * 5; // universal block
for (int i = 0; i < totalSlots; i++)
{
int idx = startIndex + i;

// Materialize a minimal AnimIdx if missing so Save() will persist Extra
if (cache[idx] == null)
{
cache[idx] = AnimIdx.CreateEmpty();
}

cache[idx].BodyType = bodyType;
}
}

public static void LoadFromVD(int fileType, int body, BinaryReader bin, int animType)
{
AnimIdx[] cache = GetCache(fileType);
GetFileIndex(body, fileType, 0, 0, out FileIndex _, out int index);
int animLength = Animations.GetAnimLength(body, fileType) * 5;

// Calculate the correct animLength based on the TYPE FROM THE *.VD FILE, not the destination slot.
int animLength;
switch (animType)
{
case 0: // High Detail Monster
animLength = 22 * 5;
break;
case 1: // Low Detail Animal
animLength = 13 * 5;
break;
default: // People/Equipment
animLength = 35 * 5;
break;
}

var entries = new Entry3D[animLength];

for (int i = 0; i < animLength; ++i)
Expand All @@ -237,6 +293,10 @@ public static void LoadFromVD(int fileType, int body, BinaryReader bin)
{
bin.BaseStream.Seek(entry.Lookup, SeekOrigin.Begin);
cache[index] = new AnimIdx(bin, entry.Extra);
if (fileType == 6)
{
cache[index].BodyType = animType; // Set the type!
}
}
++index;
}
Expand All @@ -246,26 +306,34 @@ public static void ExportToVD(int fileType, int body, string file)
{
AnimIdx[] cache = GetCache(fileType);
GetFileIndex(body, fileType, 0, 0, out FileIndex fileIndex, out int index);

using (var fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Write))
using (var bin = new BinaryWriter(fs))
{
bin.Write((short)6);

int animLength = Animations.GetAnimLength(body, fileType);
int currType = animLength == 22 ? 0 : animLength == 13 ? 1 : 2;

// Optional: prefer cached BodyType for the universal custom file (type 6)
// Ensures VD header reflects actual cached BodyType, even if GetAnimLength
// didn't infer it yet for a newly created empty slot that just got BodyType set.
AnimIdx animProbe = cache[index] ?? (cache[index] = new AnimIdx(index, fileIndex));
if (fileType == 6 && animProbe != null)
currType = animProbe.BodyType;

bin.Write((short)currType);

long indexPos = bin.BaseStream.Position;
long animPos = bin.BaseStream.Position + (12 * animLength * 5);

for (int i = index; i < index + (animLength * 5); i++)
{
AnimIdx anim;
if (cache != null)
{
anim = cache[i] != null ? cache[i] : cache[i] = new AnimIdx(i, fileIndex);
}
else
{
anim = cache[i] = new AnimIdx(i, fileIndex);
}

if (anim == null)
{
Expand Down Expand Up @@ -316,6 +384,11 @@ public static void Save(int fileType, string path)
cache = _animCache5;
fileIndex = _fileIndex5;
break;
case 6:
filename = "anim_custom";
cache = _animCache6;
fileIndex = _fileIndex6;
break;
}

string idx = Path.Combine(path, filename + ".idx");
Expand All @@ -326,6 +399,7 @@ public static void Save(int fileType, string path)
using (var binidx = new BinaryWriter(fsidx))
using (var binmul = new BinaryWriter(fsmul))
{
AnimIdx.fileType = fileType;
for (int idxc = 0; idxc < cache.Length; ++idxc)
{
AnimIdx anim;
Expand Down Expand Up @@ -355,6 +429,9 @@ public static void Save(int fileType, string path)

public sealed class AnimIdx
{
public int BodyType { get; set; }
public static int fileType { get; set; }

public readonly int PaletteCapacity = 0x100;

private readonly int _idxExtra;
Expand All @@ -373,6 +450,7 @@ public AnimIdx(int index, FileIndex fileIndex)
}

_idxExtra = extra;
this.BodyType = _idxExtra; // This is for anim_custom

using (var bin = new BinaryReader(stream))
{
Expand Down Expand Up @@ -432,6 +510,25 @@ public AnimIdx(BinaryReader bin, int extra)
}
}

public static AnimIdx CreateEmpty()
{
var ai = new AnimIdx(true);
ai.Palette = new ushort[ai.PaletteCapacity];
// Fill palette with transparent (0x8000)
for (int i = 0; i < ai.PaletteCapacity; i++)
ai.Palette[i] = 0x8000;

ai.Frames = new List<FrameEdit>(); // no frames
ai.BodyType = 2; // default P; caller will override
return ai;
}

// Private ctor used only by CreateEmpty()
private AnimIdx(bool _)
{
// intentionally empty; fields are assigned by the factory
}

public unsafe Bitmap[] GetFrames()
{
if ((Frames == null) || (Frames.Count == 0))
Expand Down Expand Up @@ -617,7 +714,15 @@ public void Save(BinaryWriter bin, BinaryWriter idx)

start = bin.BaseStream.Position - start;
idx.Write((int)start);
idx.Write(_idxExtra);
if (fileType == 6)
{
// Write our stored BodyType property into the 'extra' field on disk.
idx.Write(this.BodyType);
}
else
{
idx.Write(_idxExtra);
}
}

public void ExportToVD(BinaryWriter bin, ref long indexpos, ref long animpos)
Expand Down
47 changes: 47 additions & 0 deletions Ultima/Animations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ public static class Animations
private static FileIndex _fileIndex3 = new FileIndex("Anim3.idx", "Anim3.mul", 0x20000, -1);
private static FileIndex _fileIndex4 = new FileIndex("Anim4.idx", "Anim4.mul", 0x20000, -1);
private static FileIndex _fileIndex5 = new FileIndex("Anim5.idx", "Anim5.mul", 0x20000, -1);

// Custom universal 175-slot block anim
private static FileIndex _fileIndex6 = new FileIndex("anim_custom.idx", "anim_custom.mul", 0x20000, -1);

private static byte[] _streamBuffer;

Expand All @@ -27,6 +30,9 @@ public static void Reload()
_fileIndex3 = new FileIndex("Anim3.idx", "Anim3.mul", 0x20000, -1);
_fileIndex4 = new FileIndex("Anim4.idx", "Anim4.mul", 0x20000, -1);
_fileIndex5 = new FileIndex("Anim5.idx", "Anim5.mul", 0x20000, -1);

// Custom universal 175-slot block anim
_fileIndex6 = new FileIndex("anim_custom.idx", "anim_custom.mul", 0x20000, -1);

BodyConverter.Initialize();
BodyTable.Initialize();
Expand Down Expand Up @@ -313,6 +319,13 @@ public static int GetAnimCount(int fileType)
return 400 + ((int)(_fileIndex4.IdxLength - (35000 * 12)) / (12 * 175));
case 5:
return 400 + ((int)(_fileIndex5.IdxLength - (35000 * 12)) / (12 * 175));
case 6:
if (_fileIndex6 != null && _fileIndex6.IdxLength > 0)
{
// Calculate total bodies based on the universal block size.
return (int)(_fileIndex6.IdxLength / (175 * 12));
}
return 0;
}
}

Expand Down Expand Up @@ -384,6 +397,34 @@ public static int GetAnimLength(int body, int fileType)
length = 35;
}

break;
case 6:
GetFileIndex(body, 0, 0, fileType, out FileIndex fileIndex, out int index);

if (!fileIndex.Valid(index, out length, out int extra, out _))
{
length = 0;
}

if (length < 0)
{
length = 0;
}

// The 'extra' field now dictates the animation type.
// 0=H, 1=L, 2=P.
if (extra == 0)
{
length = 22; // High Detail (Monster)}
}
else if (extra == 1)
{
length = 13; // Low Detail (Animal)
}
else
{
length = 35; // People/Equipment
}
break;
}
return length;
Expand Down Expand Up @@ -478,6 +519,12 @@ private static void GetFileIndex(int body, int action, int direction, int fileTy
index = 35000 + ((body - 400) * 175);
}

break;
// NEW universal custom: anim_custom
case 6:
fileIndex = _fileIndex6;
index = body * 175; // universal block

break;
}

Expand Down
2 changes: 2 additions & 0 deletions Ultima/Files.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public static void FireFileSaveEvent()
"anim4.mul",
"anim5.idx",
"anim5.mul",
"anim_custom.idx",
"anim_custom.mul",
"animdata.mul",
"art.mul",
"artidx.mul",
Expand Down
Loading