mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-10-16 23:49:19 +02:00
Match scenery vehicles by folder too, as the old starter did
The old starter looked a vehicle up by folder, skin and model. The folder was left out here, and 39 skin names recur across folders - "red" is a Clio, an Audi and a crane - so a vehicle could get another folder's livery.
This commit is contained in:
@@ -134,17 +134,31 @@ public class VehicleCatalogTests
|
||||
{
|
||||
var catalog = SharedSkinPair();
|
||||
|
||||
Assert.Equal("ET42-026-A", catalog.TextureFor("112E-026", "112E-A")!.TextureMini);
|
||||
Assert.Equal("ET42-026-B", catalog.TextureFor("112E-026", "112E-B")!.TextureMini);
|
||||
Assert.Equal("ET42-026-A", catalog.TextureFor(@"PKP\ET42_V2", "112E-026", "112E-A")!.TextureMini);
|
||||
Assert.Equal("ET42-026-B", catalog.TextureFor(@"PKP\ET42_V2", "112E-026", "112E-B")!.TextureMini);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void The_second_half_on_a_shared_skin_follows_the_first_in_its_set()
|
||||
{
|
||||
var catalog = SharedSkinPair();
|
||||
var b = catalog.TextureFor("112E-026", "112E-B")!;
|
||||
var b = catalog.TextureFor(@"PKP\ET42_V2", "112E-026", "112E-B")!;
|
||||
|
||||
Assert.True(catalog.IsSetFollower(b));
|
||||
Assert.Equal(new[] { "112E-A", "112E-B" }, catalog.ResolveSet(b)!.Select(t => t.Model));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_skin_name_used_in_two_folders_is_found_by_the_folder()
|
||||
{
|
||||
var files = new InMemoryFileSystem()
|
||||
.WithFile(TexturesFile("road", "clio"), "red.mat=clio,CLIO")
|
||||
.WithFile(TexturesFile("industrial", "wls50_v1"), "red.mat=wls50,WLS50");
|
||||
var installation = new TestInstallation(files);
|
||||
installation.Vehicles.Load(installation.Library.Vehicles);
|
||||
var catalog = installation.Library.Vehicles;
|
||||
|
||||
Assert.Equal("clio", catalog.TextureFor(@"road\clio", "red", "clio")!.Model);
|
||||
Assert.Equal("wls50", catalog.TextureFor("industrial/wls50_v1", "red", "wls50.mmd")!.Model);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ public sealed class Consist : IReadOnlyList<ConsistItem>
|
||||
var cars = trainset.Vehicles.Select(v =>
|
||||
{
|
||||
var c = v.Clone();
|
||||
c.MiniName = _db.MiniFor(c.SkinFile, c.MmdFile) ?? c.MiniName;
|
||||
c.MiniName = _db.MiniFor(c.DataFolder, c.SkinFile, c.MmdFile) ?? c.MiniName;
|
||||
return c;
|
||||
}).ToList();
|
||||
|
||||
@@ -86,7 +86,7 @@ public sealed class Consist : IReadOnlyList<ConsistItem>
|
||||
}
|
||||
|
||||
VehicleSet? set = null;
|
||||
if (_db.TextureFor(cars[i].SkinFile, cars[i].MmdFile)?.Uuid is { } uuid)
|
||||
if (_db.TextureFor(cars[i].DataFolder, cars[i].SkinFile, cars[i].MmdFile)?.Uuid is { } uuid)
|
||||
_db.SetByTextureUuid.TryGetValue(uuid, out set);
|
||||
|
||||
if (set?.TextureRefs is { Count: > 1 })
|
||||
@@ -97,7 +97,7 @@ public sealed class Consist : IReadOnlyList<ConsistItem>
|
||||
int j = i;
|
||||
while (j < cars.Count && group.Count < set.TextureRefs.Count)
|
||||
{
|
||||
if (_db.TextureFor(cars[j].SkinFile, cars[j].MmdFile)?.Uuid is { } u
|
||||
if (_db.TextureFor(cars[j].DataFolder, cars[j].SkinFile, cars[j].MmdFile)?.Uuid is { } u
|
||||
&& members.Contains(u))
|
||||
group.Add(cars[j++]);
|
||||
else
|
||||
@@ -285,7 +285,7 @@ public sealed class Consist : IReadOnlyList<ConsistItem>
|
||||
}
|
||||
|
||||
private bool IsPowered(ConsistItem item) =>
|
||||
item.Cars.Count > 0 && _db.TextureFor(item.Cars[0].SkinFile, item.Cars[0].MmdFile) is { } texture &&
|
||||
item.Cars.Count > 0 && _db.TextureFor(item.Cars[0].DataFolder, item.Cars[0].SkinFile, item.Cars[0].MmdFile) is { } texture &&
|
||||
VehicleInfo.IsPoweredCategory(VehicleInfo.CategoryOf(texture));
|
||||
|
||||
public void MoveLeft(ConsistItem item)
|
||||
@@ -611,8 +611,8 @@ public sealed class Consist : IReadOnlyList<ConsistItem>
|
||||
if (IsUnitCar(a) && IsUnitCar(b) && UnitKey(a) == UnitKey(b))
|
||||
return true;
|
||||
|
||||
string? ua = _db.TextureFor(a.SkinFile, a.MmdFile)?.Uuid;
|
||||
string? ub = _db.TextureFor(b.SkinFile, b.MmdFile)?.Uuid;
|
||||
string? ua = _db.TextureFor(a.DataFolder, a.SkinFile, a.MmdFile)?.Uuid;
|
||||
string? ub = _db.TextureFor(b.DataFolder, b.SkinFile, b.MmdFile)?.Uuid;
|
||||
if (string.IsNullOrEmpty(ua) || string.IsNullOrEmpty(ub))
|
||||
return false;
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ public sealed class TexRandomizer
|
||||
var item = consist[i];
|
||||
if (item.Cars.Count == 0) continue;
|
||||
|
||||
var current = _db.TextureFor(item.Cars[0].SkinFile, item.Cars[0].MmdFile);
|
||||
var current = _db.TextureFor(item.Cars[0].DataFolder, item.Cars[0].SkinFile, item.Cars[0].MmdFile);
|
||||
if (current != null && _db.IsSetFollower(current))
|
||||
continue;
|
||||
|
||||
@@ -128,13 +128,13 @@ public sealed class TexRandomizer
|
||||
|
||||
if (index > 0)
|
||||
{
|
||||
var prev = _db.TextureFor(consist[index - 1].Cars[0].SkinFile, consist[index - 1].Cars[0].MmdFile);
|
||||
var prev = _db.TextureFor(consist[index - 1].Cars[0].DataFolder, consist[index - 1].Cars[0].SkinFile, consist[index - 1].Cars[0].MmdFile);
|
||||
if (prev != null && !string.IsNullOrEmpty(prev.ResolvedClass))
|
||||
return prev.ResolvedClass;
|
||||
}
|
||||
if (index + 1 < consist.Count)
|
||||
{
|
||||
var next = _db.TextureFor(consist[index + 1].Cars[0].SkinFile, consist[index + 1].Cars[0].MmdFile);
|
||||
var next = _db.TextureFor(consist[index + 1].Cars[0].DataFolder, consist[index + 1].Cars[0].SkinFile, consist[index + 1].Cars[0].MmdFile);
|
||||
if (next != null && !string.IsNullOrEmpty(next.ResolvedClass))
|
||||
return next.ResolvedClass;
|
||||
}
|
||||
|
||||
@@ -67,14 +67,14 @@ public static class TrainsetDisplay
|
||||
|
||||
public static string VehicleLabel(Dynamic v, bool head = false)
|
||||
{
|
||||
var tex = AppServices.Current.Library.Vehicles.TextureFor(v.SkinFile, v.MmdFile);
|
||||
var tex = AppServices.Current.Library.Vehicles.TextureFor(v.DataFolder, v.SkinFile, v.MmdFile);
|
||||
string? cls = string.IsNullOrEmpty(tex?.ResolvedClass) ? null : tex!.ResolvedClass;
|
||||
string? name = string.IsNullOrWhiteSpace(v.Name) ? null : v.Name;
|
||||
return (head ? name ?? cls : cls ?? name) ?? BaseSkin(v);
|
||||
}
|
||||
|
||||
public static bool IsPowered(Dynamic v) =>
|
||||
AppServices.Current.Library.Vehicles.TextureFor(v.SkinFile, v.MmdFile)?.ResolvedCategory
|
||||
AppServices.Current.Library.Vehicles.TextureFor(v.DataFolder, v.SkinFile, v.MmdFile)?.ResolvedCategory
|
||||
is "e" or "s" or "p" or "z" or "a";
|
||||
|
||||
private static string BaseSkin(Dynamic v) =>
|
||||
@@ -206,7 +206,7 @@ public static class TrainsetDisplay
|
||||
|
||||
bool mu = trainset.Vehicles.Any(v =>
|
||||
{
|
||||
string? cat = db.TextureFor(v.SkinFile, v.MmdFile)?.ResolvedCategory;
|
||||
string? cat = db.TextureFor(v.DataFolder, v.SkinFile, v.MmdFile)?.ResolvedCategory;
|
||||
return cat is "z" or "a";
|
||||
});
|
||||
|
||||
@@ -232,7 +232,7 @@ public static class TrainsetDisplay
|
||||
|
||||
public static string PreferredBaseName(Dynamic v, VehicleCatalog db)
|
||||
{
|
||||
var tex = db.TextureFor(v.SkinFile, v.MmdFile);
|
||||
var tex = db.TextureFor(v.DataFolder, v.SkinFile, v.MmdFile);
|
||||
if (tex != null)
|
||||
{
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ public sealed class VehicleInfo
|
||||
public static bool IsPoweredCategory(string? c) =>
|
||||
c is "e" or "s" or "p" or "z" or "a";
|
||||
|
||||
public VehicleTexture? TextureFor(Dynamic car) => _db.TextureFor(car.SkinFile, car.MmdFile);
|
||||
public VehicleTexture? TextureFor(Dynamic car) => _db.TextureFor(car.DataFolder, car.SkinFile, car.MmdFile);
|
||||
|
||||
public string? CategoryOf(Dynamic car) =>
|
||||
TextureFor(car) is { } t ? CategoryOf(t) : null;
|
||||
|
||||
@@ -27,8 +27,16 @@ public sealed class VehicleCatalog
|
||||
|
||||
public Dictionary<string, VehicleTexture> TextureBySkin { get; } = new();
|
||||
|
||||
/// <summary>For a skin shared by several models, which one dresses which.</summary>
|
||||
private readonly Dictionary<string, VehicleTexture> _textureBySkinAndModel = new();
|
||||
/// <summary>
|
||||
/// The way the old starter matched a scenery vehicle: folder, skin and model.
|
||||
/// A skin name recurs across folders ("red" for a Clio and a crane), and one
|
||||
/// skin may dress several models (both halves of the ET42-026).
|
||||
/// </summary>
|
||||
private readonly Dictionary<string, VehicleTexture> _textureByFolderSkinModel = new();
|
||||
|
||||
private readonly Dictionary<string, VehicleTexture> _textureByFolderSkin = new();
|
||||
|
||||
private readonly Dictionary<string, VehicleTexture> _textureBySkinModel = new();
|
||||
|
||||
public void BeginLoad()
|
||||
{
|
||||
@@ -38,7 +46,9 @@ public sealed class VehicleCatalog
|
||||
SetByTextureUuid.Clear();
|
||||
TextureByUuid.Clear();
|
||||
TextureBySkin.Clear();
|
||||
_textureBySkinAndModel.Clear();
|
||||
_textureByFolderSkinModel.Clear();
|
||||
_textureByFolderSkin.Clear();
|
||||
_textureBySkinModel.Clear();
|
||||
}
|
||||
|
||||
public void Ingest(VehicleEntry entry)
|
||||
@@ -55,7 +65,9 @@ public sealed class VehicleCatalog
|
||||
if (!string.IsNullOrEmpty(texture.Skinfile))
|
||||
{
|
||||
TextureBySkin.TryAdd(Key(texture.Skinfile), texture);
|
||||
_textureBySkinAndModel.TryAdd(Key(texture.Skinfile, texture.Model), texture);
|
||||
_textureByFolderSkinModel.TryAdd(FolderSkinModelKey(texture.Directory, texture.Skinfile, texture.Model), texture);
|
||||
_textureByFolderSkin.TryAdd(FolderSkinKey(texture.Directory, texture.Skinfile), texture);
|
||||
_textureBySkinModel.TryAdd(SkinModelKey(texture.Skinfile, texture.Model), texture);
|
||||
}
|
||||
|
||||
if (texture.Wreck)
|
||||
@@ -112,21 +124,22 @@ public sealed class VehicleCatalog
|
||||
return texture.TextureMini;
|
||||
}
|
||||
|
||||
public string? MiniFor(string? skinFile, string? model) =>
|
||||
TextureFor(skinFile, model) is { } texture ? ResolveMiniName(texture) : null;
|
||||
public string? MiniFor(string? dataFolder, string? skinFile, string? model) =>
|
||||
TextureFor(dataFolder, skinFile, model) is { } texture ? ResolveMiniName(texture) : null;
|
||||
|
||||
/// <summary>
|
||||
/// The texture a vehicle wears: by the skin, and where one skin dresses several
|
||||
/// models, by the model too - the scenery names both.
|
||||
/// The texture a scenery vehicle wears, matched like the old starter did: by
|
||||
/// folder, skin and model, then by less of it for a vehicle that moved.
|
||||
/// </summary>
|
||||
public VehicleTexture? TextureFor(string? skinFile, string? model)
|
||||
public VehicleTexture? TextureFor(string? dataFolder, string? skinFile, string? model)
|
||||
{
|
||||
if (string.IsNullOrEmpty(skinFile))
|
||||
return null;
|
||||
|
||||
if (!string.IsNullOrEmpty(model) &&
|
||||
_textureBySkinAndModel.TryGetValue(Key(skinFile, model), out var exact))
|
||||
return exact;
|
||||
if (_textureByFolderSkinModel.TryGetValue(FolderSkinModelKey(dataFolder, skinFile, model), out var texture) ||
|
||||
_textureByFolderSkin.TryGetValue(FolderSkinKey(dataFolder, skinFile), out texture) ||
|
||||
_textureBySkinModel.TryGetValue(SkinModelKey(skinFile, model), out texture))
|
||||
return texture;
|
||||
|
||||
return TextureForSkin(skinFile);
|
||||
}
|
||||
@@ -141,9 +154,18 @@ public sealed class VehicleCatalog
|
||||
|
||||
private static string Key(string skinFile) => Path.GetFileNameWithoutExtension(skinFile).ToLowerInvariant();
|
||||
|
||||
private static string Key(string skinFile, string? model) =>
|
||||
private static string SkinModelKey(string skinFile, string? model) =>
|
||||
Key(skinFile) + "|" + (string.IsNullOrEmpty(model) ? "" : Path.GetFileNameWithoutExtension(model).ToLowerInvariant());
|
||||
|
||||
private static string FolderSkinKey(string? folder, string skinFile) => Folder(folder) + "|" + Key(skinFile);
|
||||
|
||||
private static string FolderSkinModelKey(string? folder, string skinFile, string? model) =>
|
||||
Folder(folder) + "|" + SkinModelKey(skinFile, model);
|
||||
|
||||
/// <summary>"PKP\ET42_V2" in a scenery, "pkp/et42_v2/" in the catalogue.</summary>
|
||||
private static string Folder(string? folder) =>
|
||||
(folder ?? "").Replace('\\', '/').Trim('/').ToLowerInvariant();
|
||||
|
||||
public string GroupHeader(string? groupId)
|
||||
{
|
||||
if (string.IsNullOrEmpty(groupId) || !GroupsById.TryGetValue(groupId, out var group))
|
||||
|
||||
@@ -254,7 +254,7 @@ public partial class Depot : UserControl
|
||||
StarterNG.Infrastructure.StatsBar.Fill(trainStats, TrainsetDisplay.StatsFields(
|
||||
_consist.EditingTrainset,
|
||||
_info.PhysicsFor,
|
||||
car => _db.TextureFor(car.SkinFile, car.MmdFile) is { } t ? VehicleInfo.CategoryOf(t) : null,
|
||||
car => _db.TextureFor(car.DataFolder, car.SkinFile, car.MmdFile) is { } t ? VehicleInfo.CategoryOf(t) : null,
|
||||
AppServices.Current.LoadWeights.Table.WeightOf));
|
||||
}
|
||||
|
||||
@@ -607,7 +607,7 @@ public partial class Depot : UserControl
|
||||
private void AddCopiesAfter(ConsistItem item, int count)
|
||||
{
|
||||
int index = _consist.IndexOf(item);
|
||||
if (index < 0 || item.Cars.Count == 0 || _db.TextureFor(item.Cars[0].SkinFile, item.Cars[0].MmdFile) is not { } texture)
|
||||
if (index < 0 || item.Cars.Count == 0 || _db.TextureFor(item.Cars[0].DataFolder, item.Cars[0].SkinFile, item.Cars[0].MmdFile) is not { } texture)
|
||||
return;
|
||||
|
||||
InsertTexturesAt(texture, index + 1, count);
|
||||
|
||||
@@ -590,7 +590,7 @@ public sealed class VehicleBrowser
|
||||
|
||||
public void SelectInBrowser(Dynamic car)
|
||||
{
|
||||
var texture = _db.TextureFor(car.SkinFile, car.MmdFile);
|
||||
var texture = _db.TextureFor(car.DataFolder, car.SkinFile, car.MmdFile);
|
||||
if (texture is null)
|
||||
return;
|
||||
|
||||
|
||||
@@ -97,7 +97,7 @@ public sealed class VehicleDetails
|
||||
|
||||
BuildGeneralInfo(_consist.Selected);
|
||||
|
||||
if (_db.TextureFor(_consist.ActiveCar(_consist.Selected).SkinFile, _consist.ActiveCar(_consist.Selected).MmdFile) is { } tex)
|
||||
if (_db.TextureFor(_consist.ActiveCar(_consist.Selected).DataFolder, _consist.ActiveCar(_consist.Selected).SkinFile, _consist.ActiveCar(_consist.Selected).MmdFile) is { } tex)
|
||||
_showTextureInfo(tex, consistTexturePanel);
|
||||
else
|
||||
consistTexturePanel.Children.Add(PanelNote(App.Loc["NoTextureInfo"]));
|
||||
|
||||
@@ -363,14 +363,14 @@ public partial class Scenarios : UserControl
|
||||
foreach (var v in ts.Vehicles)
|
||||
{
|
||||
if (string.IsNullOrEmpty(v.SkinFile)) continue;
|
||||
if (db.TextureFor(v.SkinFile, v.MmdFile) is null)
|
||||
if (db.TextureFor(v.DataFolder, v.SkinFile, v.MmdFile) is null)
|
||||
lines.Add($"# unknown texture: {v.Name} ({v.SkinFile})");
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var v in scenery.LooseVehicles)
|
||||
{
|
||||
if (db.TextureFor(v.SkinFile, v.MmdFile) is null)
|
||||
if (db.TextureFor(v.DataFolder, v.SkinFile, v.MmdFile) is null)
|
||||
lines.Add($"# unknown loose texture: {v.Name} ({v.SkinFile})");
|
||||
}
|
||||
|
||||
@@ -860,7 +860,7 @@ public partial class Scenarios : UserControl
|
||||
foreach (var train in trainset.Vehicles)
|
||||
{
|
||||
|
||||
string miniName = db.MiniFor(train.SkinFile, train.MmdFile) ?? train.SkinFile;
|
||||
string miniName = db.MiniFor(train.DataFolder, train.SkinFile, train.MmdFile) ?? train.SkinFile;
|
||||
if (!AppServices.Current.MiniTextures.Has(miniName) && AppServices.Current.MiniTextures.Has(train.SkinFile))
|
||||
miniName = train.SkinFile;
|
||||
int thumbH = AppServices.Current.Settings.LargeThumbnails ? 64 : 32;
|
||||
@@ -1161,10 +1161,10 @@ public partial class Scenarios : UserControl
|
||||
var db = AppServices.Current.Library.Vehicles;
|
||||
StarterNG.Infrastructure.StatsBar.Fill(trainStats, TrainsetDisplay.StatsFields(
|
||||
trainset,
|
||||
car => AppServices.Current.Physics.For(car.DataFolder, db.TextureFor(car.SkinFile, car.MmdFile)?.Model)
|
||||
car => AppServices.Current.Physics.For(car.DataFolder, db.TextureFor(car.DataFolder, car.SkinFile, car.MmdFile)?.Model)
|
||||
?? AppServices.Current.Physics.For(car.DataFolder, car.MmdFile)
|
||||
?? AppServices.Current.Physics.For(car.DataFolder, car.SkinFile),
|
||||
car => db.TextureFor(car.SkinFile, car.MmdFile)?.ResolvedCategory));
|
||||
car => db.TextureFor(car.DataFolder, car.SkinFile, car.MmdFile)?.ResolvedCategory));
|
||||
}
|
||||
|
||||
private void RefreshSelectedConsist()
|
||||
|
||||
Reference in New Issue
Block a user