mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-11-04 05:11:50 +01:00
read rolling stock from dynamic/*/*/textures.txt, drop vehicles.json
This commit is contained in:
@@ -2,6 +2,7 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace StarterNG.Classes;
|
namespace StarterNG.Classes;
|
||||||
@@ -25,43 +26,25 @@ public sealed class GameData
|
|||||||
public bool Loaded { get; private set; }
|
public bool Loaded { get; private set; }
|
||||||
|
|
||||||
public void Load(IProgress<LoadStatus>? progress = null,
|
public void Load(IProgress<LoadStatus>? progress = null,
|
||||||
string vehiclesDir = "databases/vehicles/",
|
|
||||||
string sceneryDir = "scenery/",
|
string sceneryDir = "scenery/",
|
||||||
string miniDir = "textures/mini/")
|
string miniDir = "textures/mini/",
|
||||||
|
string dynamicRoot = "dynamic")
|
||||||
{
|
{
|
||||||
if (Loaded)
|
if (Loaded)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var vehicleFiles = VehicleDatabase.EnumerateFiles(vehiclesDir);
|
|
||||||
var scnFiles = EnumerateScenery(sceneryDir);
|
var scnFiles = EnumerateScenery(sceneryDir);
|
||||||
int total = Math.Max(1, vehicleFiles.Count + scnFiles.Count);
|
int total = Math.Max(1, scnFiles.Count);
|
||||||
int done = 0;
|
int done = 0;
|
||||||
|
|
||||||
Vehicles.BeginLoad();
|
progress?.Report(new LoadStatus(0, LoadPhase.Vehicles, "textures.txt"));
|
||||||
foreach (string file in vehicleFiles)
|
int vehicles = Vehicles.LoadFromTexturesTxt(dynamicRoot);
|
||||||
{
|
if (vehicles == 0)
|
||||||
progress?.Report(new LoadStatus((double)done / total, LoadPhase.Vehicles,
|
Infrastructure.Diagnostics.Log($"{dynamicRoot}: brak plikow textures.txt");
|
||||||
Path.GetFileName(file)));
|
|
||||||
try { Vehicles.LoadFile(file); }
|
|
||||||
catch { }
|
|
||||||
done++;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int legacy = Vehicles.ImportLegacy();
|
|
||||||
if (legacy > 0)
|
|
||||||
Infrastructure.Diagnostics.Log($"textures.txt: wczytano {legacy} tekstur");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Infrastructure.Diagnostics.Log("textures.txt import", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vehicles.EndLoad();
|
Parallel.Invoke(
|
||||||
|
() => VehicleDatabase.PreloadMiniIndex(miniDir),
|
||||||
VehicleDatabase.PreloadMiniIndex(miniDir);
|
() => Physics.PreloadIndex());
|
||||||
|
|
||||||
Physics.PreloadIndex();
|
|
||||||
|
|
||||||
var sceneryLock = new object();
|
var sceneryLock = new object();
|
||||||
var doneCount = done;
|
var doneCount = done;
|
||||||
@@ -79,7 +62,10 @@ public sealed class GameData
|
|||||||
Path.GetFileName(file)));
|
Path.GetFileName(file)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch { }
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StarterNG.Infrastructure.Diagnostics.Log($"scenery/{Path.GetFileName(file)}", ex);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
done = doneCount;
|
done = doneCount;
|
||||||
|
|
||||||
@@ -101,7 +87,10 @@ public sealed class GameData
|
|||||||
lock (sceneryLock)
|
lock (sceneryLock)
|
||||||
Sceneries.Add(scenery);
|
Sceneries.Add(scenery);
|
||||||
}
|
}
|
||||||
catch { }
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StarterNG.Infrastructure.Diagnostics.Log($"scenery/{Path.GetFileName(file)}", ex);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ using System.Text;
|
|||||||
|
|
||||||
namespace StarterNG.Classes;
|
namespace StarterNG.Classes;
|
||||||
|
|
||||||
public static class LegacyTextureDatabase
|
public static class TexturesTxt
|
||||||
{
|
{
|
||||||
public static List<string> EnumerateFiles(string dynamicRoot = "dynamic")
|
public static List<string> EnumerateFiles(string dynamicRoot = "dynamic")
|
||||||
{
|
{
|
||||||
@@ -139,8 +139,7 @@ public static class LegacyTextureDatabase
|
|||||||
Id = groupId,
|
Id = groupId,
|
||||||
Category = categorySign,
|
Category = categorySign,
|
||||||
Mini = first.Mini,
|
Mini = first.Mini,
|
||||||
Archived = archived,
|
Archived = archived
|
||||||
Implicit = true
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var texture = new VehicleTexture
|
var texture = new VehicleTexture
|
||||||
@@ -148,6 +147,8 @@ public static class LegacyTextureDatabase
|
|||||||
Uuid = $"legacy:{dir}{skin}",
|
Uuid = $"legacy:{dir}{skin}",
|
||||||
Directory = dir,
|
Directory = dir,
|
||||||
Skinfile = skin,
|
Skinfile = skin,
|
||||||
|
Wreck = skin.Contains("wreck", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
skin.Contains("wrak", StringComparison.OrdinalIgnoreCase),
|
||||||
Model = first.Model,
|
Model = first.Model,
|
||||||
Group = groupId,
|
Group = groupId,
|
||||||
MiniRef = first.Mini,
|
MiniRef = first.Mini,
|
||||||
@@ -2,78 +2,68 @@ using System;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Threading.Tasks;
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using System.Text.Json.Serialization.Metadata;
|
|
||||||
|
|
||||||
namespace StarterNG.Classes;
|
namespace StarterNG.Classes;
|
||||||
|
|
||||||
public class VehicleEntry
|
public class VehicleEntry
|
||||||
{
|
{
|
||||||
[JsonPropertyName("uuid")] public string? Uuid { get; set; }
|
public string? Uuid { get; set; }
|
||||||
[JsonPropertyName("schema_version")] public int SchemaVersion { get; set; }
|
public List<VehicleGroup> Groups { get; set; } = new();
|
||||||
[JsonPropertyName("groups")] public List<VehicleGroup> Groups { get; set; } = new();
|
public List<VehicleTexture> Textures { get; set; } = new();
|
||||||
[JsonPropertyName("textures")] public List<VehicleTexture> Textures { get; set; } = new();
|
public List<VehicleSet> Sets { get; set; } = new();
|
||||||
[JsonPropertyName("sets")] public List<VehicleSet> Sets { get; set; } = new();
|
|
||||||
[JsonPropertyName("unknown")] public List<string> Unknown { get; set; } = new();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class VehicleEntryCollection
|
|
||||||
{
|
|
||||||
[JsonPropertyName("vehicles")] public List<VehicleEntry> Vehicles { get; set; } = new();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VehicleGroup
|
public class VehicleGroup
|
||||||
{
|
{
|
||||||
[JsonPropertyName("id")] public string Id { get; set; } = "";
|
public string Id { get; set; } = "";
|
||||||
[JsonPropertyName("category")] public string? Category { get; set; }
|
public string? Category { get; set; }
|
||||||
[JsonPropertyName("mini")] public string? Mini { get; set; }
|
public string? Mini { get; set; }
|
||||||
[JsonPropertyName("archived")] public bool Archived { get; set; }
|
public bool Archived { get; set; }
|
||||||
[JsonPropertyName("implicit")] public bool Implicit { get; set; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VehicleTexture
|
public class VehicleTexture
|
||||||
{
|
{
|
||||||
[JsonPropertyName("uuid")] public string? Uuid { get; set; }
|
public string? Uuid { get; set; }
|
||||||
[JsonPropertyName("directory")] public string Directory { get; set; } = "";
|
public string Directory { get; set; } = "";
|
||||||
[JsonPropertyName("skinfile")] public string Skinfile { get; set; } = "";
|
public string Skinfile { get; set; } = "";
|
||||||
[JsonPropertyName("model")] public string? Model { get; set; }
|
public string? Model { get; set; }
|
||||||
[JsonPropertyName("group")] public string? Group { get; set; }
|
public string? Group { get; set; }
|
||||||
[JsonPropertyName("mini_ref")] public string? MiniRef { get; set; }
|
public string? MiniRef { get; set; }
|
||||||
[JsonPropertyName("texture_mini")] public string? TextureMini { get; set; }
|
public string? TextureMini { get; set; }
|
||||||
[JsonPropertyName("wreck")] public bool Wreck { get; set; }
|
public bool Wreck { get; set; }
|
||||||
[JsonPropertyName("aliases")] public List<VehicleAlias> Aliases { get; set; } = new();
|
public List<VehicleAlias> Aliases { get; set; } = new();
|
||||||
[JsonPropertyName("meta")] public VehicleMeta? Meta { get; set; }
|
public VehicleMeta? Meta { get; set; }
|
||||||
|
|
||||||
[JsonIgnore] public string FullPath => Directory + Skinfile;
|
public string FullPath => Directory + Skinfile;
|
||||||
|
|
||||||
[JsonIgnore] public string ResolvedClass { get; internal set; } = "";
|
public string ResolvedClass { get; internal set; } = "";
|
||||||
|
|
||||||
[JsonIgnore] public string? ResolvedCategory { get; internal set; }
|
public string? ResolvedCategory { get; internal set; }
|
||||||
|
|
||||||
[JsonIgnore] public bool ResolvedArchived { get; internal set; }
|
public bool ResolvedArchived { get; internal set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VehicleAlias
|
public class VehicleAlias
|
||||||
{
|
{
|
||||||
[JsonPropertyName("model")] public string? Model { get; set; }
|
public string? Model { get; set; }
|
||||||
[JsonPropertyName("group")] public string? Group { get; set; }
|
public string? Group { get; set; }
|
||||||
[JsonPropertyName("mini_ref")] public string? MiniRef { get; set; }
|
public string? MiniRef { get; set; }
|
||||||
[JsonPropertyName("texture_mini")] public string? TextureMini { get; set; }
|
public string? TextureMini { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VehicleMeta
|
public class VehicleMeta
|
||||||
{
|
{
|
||||||
[JsonPropertyName("raw")] public string? Raw { get; set; }
|
public string? Raw { get; set; }
|
||||||
[JsonPropertyName("version")] public string? Version { get; set; }
|
public string? Version { get; set; }
|
||||||
[JsonPropertyName("vehicle")] public string? Vehicle { get; set; }
|
public string? Vehicle { get; set; }
|
||||||
[JsonPropertyName("operator")] public string? Operator { get; set; }
|
public string? Operator { get; set; }
|
||||||
[JsonPropertyName("depot")] public string? Depot { get; set; }
|
public string? Depot { get; set; }
|
||||||
[JsonPropertyName("revision_date")] public string? RevisionDate { get; set; }
|
public string? RevisionDate { get; set; }
|
||||||
[JsonPropertyName("revision_place")] public string? RevisionPlace { get; set; }
|
public string? RevisionPlace { get; set; }
|
||||||
[JsonPropertyName("texture_author")] public string? TextureAuthor { get; set; }
|
public string? TextureAuthor { get; set; }
|
||||||
[JsonPropertyName("photo_author")] public string? PhotoAuthor { get; set; }
|
public string? PhotoAuthor { get; set; }
|
||||||
[JsonPropertyName("extra")] public List<string> Extra { get; set; } = new();
|
public List<string> Extra { get; set; } = new();
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VehicleDatabase
|
public class VehicleDatabase
|
||||||
@@ -88,20 +78,22 @@ public class VehicleDatabase
|
|||||||
|
|
||||||
public Dictionary<string, VehicleTexture> TextureBySkin { get; } = new();
|
public Dictionary<string, VehicleTexture> TextureBySkin { get; } = new();
|
||||||
|
|
||||||
private static readonly JsonSerializerOptions JsonOpts = new()
|
public int LoadFromTexturesTxt(string dynamicRoot = "dynamic")
|
||||||
{
|
|
||||||
PropertyNameCaseInsensitive = true,
|
|
||||||
ReadCommentHandling = JsonCommentHandling.Skip,
|
|
||||||
AllowTrailingCommas = true,
|
|
||||||
TypeInfoResolver = VehicleJsonContext.Default,
|
|
||||||
};
|
|
||||||
|
|
||||||
public void Load(string directory = "databases/vehicles/")
|
|
||||||
{
|
{
|
||||||
BeginLoad();
|
BeginLoad();
|
||||||
foreach (string file in EnumerateFiles(directory))
|
|
||||||
LoadFile(file);
|
int count = 0;
|
||||||
|
foreach (string file in TexturesTxt.EnumerateFiles(dynamicRoot))
|
||||||
|
{
|
||||||
|
var entry = TexturesTxt.Parse(file, dynamicRoot);
|
||||||
|
if (entry is null) continue;
|
||||||
|
|
||||||
|
count += entry.Textures.Count;
|
||||||
|
Ingest(entry);
|
||||||
|
}
|
||||||
|
|
||||||
EndLoad();
|
EndLoad();
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BeginLoad()
|
public void BeginLoad()
|
||||||
@@ -120,79 +112,6 @@ public class VehicleDatabase
|
|||||||
BuildTextureIndex();
|
BuildTextureIndex();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<string> EnumerateFiles(string directory = "databases/vehicles/")
|
|
||||||
{
|
|
||||||
if (!Directory.Exists(directory))
|
|
||||||
return new List<string>();
|
|
||||||
|
|
||||||
string merged = Path.Combine(directory, "vehicles.json");
|
|
||||||
if (File.Exists(merged))
|
|
||||||
return new List<string> { merged };
|
|
||||||
|
|
||||||
return Directory.GetFiles(directory, "*.json").ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void LoadFile(string file)
|
|
||||||
{
|
|
||||||
if (string.Equals(Path.GetFileName(file), "vehicles.json", StringComparison.OrdinalIgnoreCase))
|
|
||||||
LoadMerged(file);
|
|
||||||
else
|
|
||||||
LoadEntryFile(file);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadMerged(string file)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var collection = JsonSerializer.Deserialize(
|
|
||||||
File.ReadAllText(file),
|
|
||||||
(JsonTypeInfo<VehicleEntryCollection>)JsonOpts.GetTypeInfo(typeof(VehicleEntryCollection)));
|
|
||||||
if (collection?.Vehicles is null) return;
|
|
||||||
foreach (var entry in collection.Vehicles)
|
|
||||||
Ingest(entry);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LoadEntryFile(string file)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var entry = JsonSerializer.Deserialize(
|
|
||||||
File.ReadAllText(file),
|
|
||||||
(JsonTypeInfo<VehicleEntry>)JsonOpts.GetTypeInfo(typeof(VehicleEntry)));
|
|
||||||
if (entry is not null)
|
|
||||||
Ingest(entry);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int ImportLegacy(string dynamicRoot = "dynamic")
|
|
||||||
{
|
|
||||||
int added = 0;
|
|
||||||
foreach (string file in LegacyTextureDatabase.EnumerateFiles(dynamicRoot))
|
|
||||||
{
|
|
||||||
var entry = LegacyTextureDatabase.Parse(file, dynamicRoot);
|
|
||||||
if (entry is null) continue;
|
|
||||||
|
|
||||||
entry.Textures.RemoveAll(t =>
|
|
||||||
TextureBySkin.ContainsKey(
|
|
||||||
Path.GetFileNameWithoutExtension(t.Skinfile).ToLowerInvariant()));
|
|
||||||
|
|
||||||
if (entry.Textures.Count == 0) continue;
|
|
||||||
|
|
||||||
added += entry.Textures.Count;
|
|
||||||
Ingest(entry);
|
|
||||||
}
|
|
||||||
return added;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Ingest(VehicleEntry entry)
|
private void Ingest(VehicleEntry entry)
|
||||||
{
|
{
|
||||||
foreach (var group in entry.Groups)
|
foreach (var group in entry.Groups)
|
||||||
@@ -386,14 +305,8 @@ public class VehicleDatabase
|
|||||||
|
|
||||||
public class VehicleSet
|
public class VehicleSet
|
||||||
{
|
{
|
||||||
[JsonPropertyName("uuid")] public string? Uuid { get; set; }
|
public string? Uuid { get; set; }
|
||||||
[JsonPropertyName("mode")] public string? Mode { get; set; }
|
public string? Mode { get; set; }
|
||||||
[JsonPropertyName("count")] public int Count { get; set; }
|
public int Count { get; set; }
|
||||||
[JsonPropertyName("texture_refs")] public List<string> TextureRefs { get; set; } = new();
|
public List<string> TextureRefs { get; set; } = new();
|
||||||
}
|
|
||||||
|
|
||||||
[JsonSerializable(typeof(VehicleEntry))]
|
|
||||||
[JsonSerializable(typeof(VehicleEntryCollection))]
|
|
||||||
internal partial class VehicleJsonContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
<String key="Vehicle">Vozidlo</String>
|
<String key="Vehicle">Vozidlo</String>
|
||||||
<String key="Crew">s osádkou</String>
|
<String key="Crew">s osádkou</String>
|
||||||
<String key="Reversed">otočeno</String>
|
<String key="Reversed">otočeno</String>
|
||||||
<String key="NoVehicles">Ve složce databases/vehicles/ nebyla nalezena žádná vozidla.</String>
|
<String key="NoVehicles">Ve složce dynamic/**/textures.txt nebyla nalezena žádná vozidla.</String>
|
||||||
<String key="ConsistEmpty">Přidejte vozidla z databáze vlevo nebo načtěte existující soupravu ze souboru scenérie.</String>
|
<String key="ConsistEmpty">Přidejte vozidla z databáze vlevo nebo načtěte existující soupravu ze souboru scenérie.</String>
|
||||||
<String key="TipMoveLeft">Posunout doleva</String>
|
<String key="TipMoveLeft">Posunout doleva</String>
|
||||||
<String key="TipMoveRight">Posunout doprava</String>
|
<String key="TipMoveRight">Posunout doprava</String>
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
<String key="Vehicle">Vehicle</String>
|
<String key="Vehicle">Vehicle</String>
|
||||||
<String key="Crew">with crew</String>
|
<String key="Crew">with crew</String>
|
||||||
<String key="Reversed">reversed</String>
|
<String key="Reversed">reversed</String>
|
||||||
<String key="NoVehicles">No vehicles found in databases/vehicles/.</String>
|
<String key="NoVehicles">No vehicles found in dynamic/**/textures.txt.</String>
|
||||||
<String key="ConsistEmpty">Add vehicles from the database on the left, or load an existing consist from a scenery file.</String>
|
<String key="ConsistEmpty">Add vehicles from the database on the left, or load an existing consist from a scenery file.</String>
|
||||||
<String key="TipMoveLeft">Move left</String>
|
<String key="TipMoveLeft">Move left</String>
|
||||||
<String key="TipMoveRight">Move right</String>
|
<String key="TipMoveRight">Move right</String>
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
<String key="Vehicle">Jármű</String>
|
<String key="Vehicle">Jármű</String>
|
||||||
<String key="Crew">személyzettel</String>
|
<String key="Crew">személyzettel</String>
|
||||||
<String key="Reversed">megfordítva</String>
|
<String key="Reversed">megfordítva</String>
|
||||||
<String key="NoVehicles">A databases/vehicles/ mappában nem található jármű.</String>
|
<String key="NoVehicles">A dynamic/**/textures.txt fájlokban nem található jármű.</String>
|
||||||
<String key="ConsistEmpty">Adjon hozzá járműveket a bal oldali adatbázisból, vagy töltsön be meglévő szerelvényt egy pályafájlból.</String>
|
<String key="ConsistEmpty">Adjon hozzá járműveket a bal oldali adatbázisból, vagy töltsön be meglévő szerelvényt egy pályafájlból.</String>
|
||||||
<String key="TipMoveLeft">Mozgatás balra</String>
|
<String key="TipMoveLeft">Mozgatás balra</String>
|
||||||
<String key="TipMoveRight">Mozgatás jobbra</String>
|
<String key="TipMoveRight">Mozgatás jobbra</String>
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
<String key="Vehicle">Pojazd</String>
|
<String key="Vehicle">Pojazd</String>
|
||||||
<String key="Crew">z obsadą</String>
|
<String key="Crew">z obsadą</String>
|
||||||
<String key="Reversed">obrócony</String>
|
<String key="Reversed">obrócony</String>
|
||||||
<String key="NoVehicles">Nie znaleziono pojazdów w databases/vehicles/.</String>
|
<String key="NoVehicles">Nie znaleziono pojazdów w dynamic/**/textures.txt.</String>
|
||||||
<String key="ConsistEmpty">Dodaj pojazdy z bazy po lewej stronie lub wczytaj istniejący skład z pliku scenerii.</String>
|
<String key="ConsistEmpty">Dodaj pojazdy z bazy po lewej stronie lub wczytaj istniejący skład z pliku scenerii.</String>
|
||||||
<String key="TipMoveLeft">Przesuń w lewo</String>
|
<String key="TipMoveLeft">Przesuń w lewo</String>
|
||||||
<String key="TipMoveRight">Przesuń w prawo</String>
|
<String key="TipMoveRight">Przesuń w prawo</String>
|
||||||
|
|||||||
@@ -90,7 +90,7 @@
|
|||||||
<String key="Vehicle">Единица</String>
|
<String key="Vehicle">Единица</String>
|
||||||
<String key="Crew">с бригадой</String>
|
<String key="Crew">с бригадой</String>
|
||||||
<String key="Reversed">развёрнут</String>
|
<String key="Reversed">развёрнут</String>
|
||||||
<String key="NoVehicles">В databases/vehicles/ подвижной состав не найден.</String>
|
<String key="NoVehicles">В dynamic/**/textures.txt подвижной состав не найден.</String>
|
||||||
<String key="ConsistEmpty">Добавьте единицы из базы слева или загрузите готовый состав из файла сценерии.</String>
|
<String key="ConsistEmpty">Добавьте единицы из базы слева или загрузите готовый состав из файла сценерии.</String>
|
||||||
<String key="TipMoveLeft">Сдвинуть влево</String>
|
<String key="TipMoveLeft">Сдвинуть влево</String>
|
||||||
<String key="TipMoveRight">Сдвинуть вправо</String>
|
<String key="TipMoveRight">Сдвинуть вправо</String>
|
||||||
|
|||||||
Reference in New Issue
Block a user