mirror of
https://github.com/GTA-ASM/SanAndreasUnity
synced 2024-12-12 13:42:32 +00:00
273 lines
9.6 KiB
C#
273 lines
9.6 KiB
C#
|
using SanAndreasUnity.Utilities;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Globalization;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using System.Reflection;
|
|||
|
|
|||
|
namespace SanAndreasUnity.Importing.Vehicles
|
|||
|
{
|
|||
|
public enum DriveType
|
|||
|
{
|
|||
|
Forward,
|
|||
|
Rear,
|
|||
|
FourWheel
|
|||
|
}
|
|||
|
|
|||
|
public enum EngineType
|
|||
|
{
|
|||
|
Petrol,
|
|||
|
Diesel,
|
|||
|
Electric
|
|||
|
}
|
|||
|
|
|||
|
public static class Handling
|
|||
|
{
|
|||
|
#region Reflection
|
|||
|
|
|||
|
[Flags]
|
|||
|
private enum ColumnFlags
|
|||
|
{
|
|||
|
Default = 0,
|
|||
|
HexNumber = 1
|
|||
|
}
|
|||
|
|
|||
|
[AttributeUsage(AttributeTargets.Property)]
|
|||
|
private class ColumnAttribute : Attribute
|
|||
|
{
|
|||
|
public readonly int Value;
|
|||
|
public readonly ColumnFlags Flags;
|
|||
|
|
|||
|
public bool IsHexNumber
|
|||
|
{
|
|||
|
get { return (Flags & ColumnFlags.HexNumber) == ColumnFlags.HexNumber; }
|
|||
|
}
|
|||
|
|
|||
|
public ColumnAttribute(int value, ColumnFlags flags = ColumnFlags.Default)
|
|||
|
{
|
|||
|
Value = value;
|
|||
|
Flags = flags;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
[AttributeUsage(AttributeTargets.Class)]
|
|||
|
private class PrefixAttribute : Attribute
|
|||
|
{
|
|||
|
public readonly char Value;
|
|||
|
|
|||
|
public PrefixAttribute(char value)
|
|||
|
{
|
|||
|
Value = value;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public interface IEntry { }
|
|||
|
|
|||
|
public interface IVehicleEntry : IEntry
|
|||
|
{
|
|||
|
string Id { get; set; }
|
|||
|
}
|
|||
|
|
|||
|
public abstract class Entry<TEntry> : IEntry
|
|||
|
where TEntry : Entry<TEntry>
|
|||
|
{
|
|||
|
private static Dictionary<int, Action<string, object>> _sParsers;
|
|||
|
|
|||
|
private static void GenerateParsers()
|
|||
|
{
|
|||
|
_sParsers = new Dictionary<int, Action<string, object>>();
|
|||
|
|
|||
|
var type = typeof(TEntry);
|
|||
|
|
|||
|
foreach (var prop in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))
|
|||
|
{
|
|||
|
var attrib = (ColumnAttribute)prop.GetCustomAttributes(typeof(ColumnAttribute), true).FirstOrDefault();
|
|||
|
if (attrib == null) continue;
|
|||
|
|
|||
|
var set = prop.GetSetMethod(true);
|
|||
|
if (set == null)
|
|||
|
throw new Exception("Set method is inaccessible");
|
|||
|
|
|||
|
_sParsers.Add(attrib.Value, (s, instance) =>
|
|||
|
{
|
|||
|
set.Invoke(instance, new object[] { prop.PropertyType != typeof(string) ? Convert.ChangeType(attrib.IsHexNumber ? s.FromHex(prop.PropertyType, CultureInfo.InvariantCulture) : s, prop.PropertyType, CultureInfo.InvariantCulture) : s });
|
|||
|
});
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
protected Entry(string line)
|
|||
|
{
|
|||
|
if (_sParsers == null)
|
|||
|
GenerateParsers();
|
|||
|
|
|||
|
var split = line.Split(new[] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
|
|||
|
var self = (TEntry)this;
|
|||
|
|
|||
|
for (var i = 0; i < split.Length; ++i)
|
|||
|
if (_sParsers.ContainsKey(i))
|
|||
|
_sParsers[i](split[i], self);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private static Dictionary<char, Func<String, IEntry>> _sCtors;
|
|||
|
|
|||
|
private static IEnumerable<IEntry> _sEntries;
|
|||
|
|
|||
|
private static void GenerateCtors()
|
|||
|
{
|
|||
|
_sCtors = new Dictionary<char, Func<string, IEntry>>();
|
|||
|
|
|||
|
foreach (var type in typeof(Handling).GetNestedTypes())
|
|||
|
{
|
|||
|
if (type.IsAbstract) continue;
|
|||
|
if (type.IsInterface) continue;
|
|||
|
if (!typeof(IEntry).IsAssignableFrom(type)) continue;
|
|||
|
var attrib = (PrefixAttribute)type.GetCustomAttributes(typeof(PrefixAttribute), true).FirstOrDefault();
|
|||
|
var prefix = attrib != null ? attrib.Value : '\0';
|
|||
|
|
|||
|
_sCtors.Add(prefix, s => { return (IEntry)Activator.CreateInstance(type, new object[] { s }); });
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion Reflection
|
|||
|
|
|||
|
public class Car : Entry<Car>, IVehicleEntry
|
|||
|
{
|
|||
|
[Column(0)] public string Id { get; set; }
|
|||
|
[Column(1)] public float Mass { get; set; }
|
|||
|
[Column(2)] public float TurnMass { get; set; }
|
|||
|
[Column(3)] public float Drag { get; set; }
|
|||
|
[Column(4)] private float _CentreOfMassX { get; set; }
|
|||
|
[Column(5)] private float _CentreOfMassZ { get; set; }
|
|||
|
[Column(6)] private float _CentreOfMassY { get; set; }
|
|||
|
[Column(7)] public int BoyantPercent { get; set; }
|
|||
|
[Column(8)] public float TractionMult { get; set; }
|
|||
|
[Column(9)] public float TractionLoss { get; set; }
|
|||
|
[Column(10)] public float TractionBias { get; set; }
|
|||
|
|
|||
|
[Column(11)] public int TransmissionGears { get; set; }
|
|||
|
[Column(12)] public float TransmissionMaxVel { get; set; }
|
|||
|
[Column(13)] public float TransmissionEngineAccel { get; set; }
|
|||
|
[Column(14)] public float TransmissionEngineInertia { get; set; }
|
|||
|
[Column(15)] private string _TransmissionDriveType { get; set; }
|
|||
|
[Column(16)] private string _TransmissionEngineType { get; set; }
|
|||
|
|
|||
|
[Column(17)] public float BrakeDecel { get; set; }
|
|||
|
[Column(18)] public float BrakeBias { get; set; }
|
|||
|
[Column(19)] private int _AntiLockBrakes { get; set; }
|
|||
|
[Column(20)] public float SteeringLock { get; set; }
|
|||
|
|
|||
|
[Column(21)] public float SuspensionForceLevel { get; set; }
|
|||
|
[Column(22)] public float SuspensionDampingLevel { get; set; }
|
|||
|
[Column(23)] public float SuspensionHighSpeedDamping { get; set; }
|
|||
|
[Column(24)] public float SuspensionUpperLimit { get; set; }
|
|||
|
[Column(25)] public float SuspensionLowerLimit { get; set; }
|
|||
|
[Column(26)] public float SuspensionFrontRearBias { get; set; }
|
|||
|
[Column(27)] public float SuspensionAntiDriveMult { get; set; }
|
|||
|
|
|||
|
[Column(28)] public float SeatOffsetDistance { get; set; }
|
|||
|
[Column(29)] public float CollisionDamageMult { get; set; }
|
|||
|
[Column(30)] public int MonetaryValue { get; set; }
|
|||
|
|
|||
|
[Column(31, ColumnFlags.HexNumber)] private ulong _ModelFlags { get; set; }
|
|||
|
[Column(32, ColumnFlags.HexNumber)] private ulong _HandlingFlags { get; set; }
|
|||
|
|
|||
|
[Column(33)] private int _FrontLights { get; set; }
|
|||
|
[Column(34)] private int _RearLights { get; set; }
|
|||
|
|
|||
|
[Column(35)] public int AnimGroup { get; set; }
|
|||
|
|
|||
|
public UnityEngine.Vector3 CentreOfMass
|
|||
|
{
|
|||
|
get { return new UnityEngine.Vector3(_CentreOfMassX, _CentreOfMassY, _CentreOfMassZ); }
|
|||
|
}
|
|||
|
|
|||
|
public DriveType TransmissionDriveType
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
switch (_TransmissionDriveType)
|
|||
|
{
|
|||
|
case "F": return DriveType.Forward;
|
|||
|
case "R": return DriveType.Rear;
|
|||
|
case "4": return DriveType.FourWheel;
|
|||
|
default: throw new FormatException();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public EngineType TransmissionEngineType
|
|||
|
{
|
|||
|
get
|
|||
|
{
|
|||
|
switch (_TransmissionEngineType)
|
|||
|
{
|
|||
|
case "P": return EngineType.Petrol;
|
|||
|
case "D": return EngineType.Diesel;
|
|||
|
case "E": return EngineType.Electric;
|
|||
|
default: throw new FormatException();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public bool AntiLockBrakes { get { return _AntiLockBrakes != 0; } }
|
|||
|
|
|||
|
public Car(string line) : base(line)
|
|||
|
{
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static void Load(string path)
|
|||
|
{
|
|||
|
_sEntries = LoadInternal(path);
|
|||
|
}
|
|||
|
|
|||
|
private static IEnumerable<IEntry> LoadInternal(string path)
|
|||
|
{
|
|||
|
if (_sCtors == null)
|
|||
|
GenerateCtors();
|
|||
|
|
|||
|
//throw new Exception("Constructor count: " + _sCtors.Count);
|
|||
|
|
|||
|
using (var reader = File.OpenText(path))
|
|||
|
{
|
|||
|
string line;
|
|||
|
while ((line = reader.ReadLine()) != null)
|
|||
|
{
|
|||
|
line = line.Trim();
|
|||
|
|
|||
|
if (line.Length == 0) continue;
|
|||
|
if (line.StartsWith(";")) continue;
|
|||
|
|
|||
|
var prefix = line[0];
|
|||
|
|
|||
|
if (char.IsLetterOrDigit(prefix))
|
|||
|
{
|
|||
|
yield return _sCtors['\0'](line);
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
foreach (var pair in _sCtors)
|
|||
|
{
|
|||
|
if (pair.Key != prefix) continue;
|
|||
|
yield return pair.Value(line.Substring(1).TrimStart());
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
public static TEntry Get<TEntry>(String id)
|
|||
|
where TEntry : IVehicleEntry
|
|||
|
{
|
|||
|
//throw new Exception("Number of car loaded: " + _sEntries.Count);
|
|||
|
var entries = _sEntries.OfType<TEntry>();
|
|||
|
|
|||
|
if (entries == null || (entries != null && (entries.Count() == 0 || entries.All(x => x == null))))
|
|||
|
throw new Exception(string.Format("Cars didn't loaded!\nEntries null?: {0}\nEntries count: {1}\nIf this two vars are false & 0, that means that all car instances are null.", entries == null, entries.Count()));
|
|||
|
|
|||
|
return entries.FirstOrDefault(x => x.Id == id);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|