Skip to content

Commit d722ffc

Browse files
committed
add steam, road and roadstation viewmodels
1 parent 1effec1 commit d722ffc

24 files changed

+471
-202
lines changed

Dat/Objects/DockObject.cs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ public record DockObject(
2525
[property: LocoStructOffset(0x08), Browsable(false)] image_id Image,
2626
[property: LocoStructOffset(0x0C), Browsable(false)] image_id UnkImage,
2727
[property: LocoStructOffset(0x10)] DockObjectFlags Flags,
28-
[property: LocoStructOffset(0x12), LocoPropertyMaybeUnused] uint8_t NumAux01,
29-
[property: LocoStructOffset(0x13), LocoPropertyMaybeUnused] uint8_t NumAux02Ent, // must be 1 or 0
28+
[property: LocoStructOffset(0x12), LocoPropertyMaybeUnused] uint8_t NumBuildingPartAnimations,
29+
[property: LocoStructOffset(0x13), LocoPropertyMaybeUnused] uint8_t NumBuildingVariationParts, // must be 1 or 0
3030
[property: LocoStructOffset(0x14), LocoStructVariableLoad] List<uint8_t> var_14,
3131
[property: LocoStructOffset(0x18), LocoStructVariableLoad] List<uint16_t> BuildingPartAnimations,
3232
[property: LocoStructOffset(0x1C), LocoStructVariableLoad] List<uint8_t> BuildingVariationParts,
@@ -42,20 +42,20 @@ public ReadOnlySpan<byte> Load(ReadOnlySpan<byte> remainingData)
4242
BuildingVariationParts.Clear();
4343

4444
// var_14 - a list of uint8_t
45-
var_14.AddRange(remainingData[..(NumAux01 * 1)]);
46-
remainingData = remainingData[(NumAux01 * 1)..]; // sizeof(uint8_t)
45+
var_14.AddRange(remainingData[..(NumBuildingPartAnimations * 1)]);
46+
remainingData = remainingData[(NumBuildingPartAnimations * 1)..]; // sizeof(uint8_t)
4747

4848
// var_18 - a list of uint16_t
49-
var bytearr = remainingData[..(NumAux01 * 2)].ToArray();
50-
for (var i = 0; i < NumAux01; ++i)
49+
var bytearr = remainingData[..(NumBuildingPartAnimations * 2)].ToArray();
50+
for (var i = 0; i < NumBuildingPartAnimations; ++i)
5151
{
5252
BuildingPartAnimations.Add(BitConverter.ToUInt16(bytearr, i * 2)); // sizeof(uint16_t)
5353
}
5454

55-
remainingData = remainingData[(NumAux01 * 2)..]; // sizeof(uint16_t)
55+
remainingData = remainingData[(NumBuildingPartAnimations * 2)..]; // sizeof(uint16_t)
5656

5757
// parts
58-
for (var i = 0; i < NumAux02Ent; ++i)
58+
for (var i = 0; i < NumBuildingVariationParts; ++i)
5959
{
6060
var ptr_1C = 0;
6161
while (remainingData[ptr_1C] != 0xFF)

Dat/Objects/RoadStationObject.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ public record RoadStationObject(
2323
[property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name,
2424
[property: LocoStructOffset(0x02)] uint8_t PaintStyle,
2525
[property: LocoStructOffset(0x03)] uint8_t Height,
26-
[property: LocoStructOffset(0x04)] uint16_t RoadPieces,
26+
[property: LocoStructOffset(0x04)] RoadTraitFlags RoadPieces,
2727
[property: LocoStructOffset(0x06)] int16_t BuildCostFactor,
2828
[property: LocoStructOffset(0x08)] int16_t SellCostFactor,
2929
[property: LocoStructOffset(0x0A)] uint8_t CostIndex,
3030
[property: LocoStructOffset(0x0B)] RoadStationObjectFlags Flags,
3131
[property: LocoStructOffset(0x0C), LocoStructVariableLoad, Browsable(false)] image_id Image,
3232
[property: LocoStructOffset(0x10), LocoStructVariableLoad, LocoArrayLength(RoadStationObject.MaxImageOffsets)] uint32_t[] ImageOffsets,
3333
[property: LocoStructOffset(0x20)] uint8_t NumCompatible,
34-
[property: LocoStructOffset(0x21), LocoStructVariableLoad, LocoArrayLength(RoadStationObject.MaxNumMods)] uint8_t[] _Mods,
34+
[property: LocoStructOffset(0x21), LocoStructVariableLoad, LocoArrayLength(RoadStationObject.MaxNumCompatible)] object_id[] _Compatible,
3535
[property: LocoStructOffset(0x28)] uint16_t DesignedYear,
3636
[property: LocoStructOffset(0x2A)] uint16_t ObsoleteYear,
3737
[property: LocoStructOffset(0x2C), LocoStructVariableLoad, Browsable(false)] object_id _CargoTypeId,
@@ -40,7 +40,7 @@ public record RoadStationObject(
4040
) : ILocoStruct, ILocoStructVariableData, IImageTableNameProvider
4141
{
4242
public const int MaxImageOffsets = 4;
43-
public const int MaxNumMods = 7;
43+
public const int MaxNumCompatible = 7;
4444
public const int CargoOffsetBytesSize = 16;
4545
public List<S5Header> Compatible { get; set; } = [];
4646

Dat/Objects/SteamObject.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ public ReadOnlySpan<byte> Save()
9797
.Concat(new byte[] { 0xFF })
9898
.Concat(SoundEffects.SelectMany(sfx => sfx.Write().ToArray()))
9999
.ToArray();
100-
public bool Validate() => true;
100+
101+
public bool Validate()
102+
=> true;
101103
}
102104
}

Dat/Objects/TrainStationObject.cs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public enum TrainStationObjectFlags : uint8_t
2020
[LocoStringTable("Name")]
2121
public record TrainStationObject(
2222
[property: LocoStructOffset(0x00), LocoString, Browsable(false)] string_id Name,
23-
[property: LocoStructOffset(0x02)] uint8_t DrawStyle,
23+
[property: LocoStructOffset(0x02)] uint8_t PaintStyle,
2424
[property: LocoStructOffset(0x03)] uint8_t Height,
2525
[property: LocoStructOffset(0x04)] TrackTraitFlags TrackPieces,
2626
[property: LocoStructOffset(0x06)] int16_t BuildCostFactor,
@@ -29,10 +29,10 @@ public record TrainStationObject(
2929
[property: LocoStructOffset(0x0B)] uint8_t var_0B,
3030
[property: LocoStructOffset(0x0C)] TrainStationObjectFlags Flags,
3131
[property: LocoStructOffset(0x0D)] uint8_t var_0D,
32-
[property: LocoStructOffset(0x0E)] image_id Image,
33-
[property: LocoStructOffset(0x12), LocoArrayLength(4)] uint32_t[] ImageOffsets,
32+
[property: LocoStructOffset(0x0E), LocoStructVariableLoad, Browsable(false)] image_id Image,
33+
[property: LocoStructOffset(0x12), LocoArrayLength(TrainStationObject.MaxImageOffsets)] uint32_t[] ImageOffsets,
3434
[property: LocoStructOffset(0x22)] uint8_t NumCompatible,
35-
[property: LocoStructOffset(0x23), LocoArrayLength(7)] uint8_t[] Mods,
35+
[property: LocoStructOffset(0x23), LocoArrayLength(TrainStationObject.MaxNumCompatible)] object_id[] _Compatible,
3636
[property: LocoStructOffset(0x2A)] uint16_t DesignedYear,
3737
[property: LocoStructOffset(0x2C)] uint16_t ObsoleteYear,
3838
[property: LocoStructOffset(0x2E), LocoStructVariableLoad, LocoArrayLength(TrainStationObject.CargoOffsetBytesSize), Browsable(false)] uint8_t[] _CargoOffsetBytes,
@@ -41,6 +41,8 @@ public record TrainStationObject(
4141
{
4242
public List<S5Header> Compatible { get; set; } = [];
4343

44+
public const int MaxImageOffsets = 4;
45+
public const int MaxNumCompatible = 7;
4446
public const int ManualPowerLength = 16;
4547
public const int CargoOffsetBytesSize = 16;
4648
public uint8_t[][][] CargoOffsetBytes { get; set; }
@@ -144,7 +146,7 @@ public bool Validate()
144146
return false;
145147
}
146148

147-
if (DrawStyle >= 1)
149+
if (PaintStyle >= 1)
148150
{
149151
return false;
150152
}

Gui/Typedefs.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
global using int16_t = System.Int16;
22
global using int8_t = System.SByte;
3+
global using MicroZ = System.Byte;
34
global using Speed16 = System.Int16;
45
global using uint16_t = System.UInt16;
56
global using uint32_t = System.UInt32;

Gui/ViewModels/DatTypes/DatObjectEditorViewModel.cs

Lines changed: 24 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
using OpenLoco.Common.Logging;
22
using OpenLoco.Dat;
33
using OpenLoco.Dat.FileParsing;
4-
using OpenLoco.Dat.Objects;
54
using OpenLoco.Dat.Objects.Sound;
65
using OpenLoco.Dat.Types;
76
using OpenLoco.Gui.Models;
@@ -12,6 +11,7 @@
1211
using System.Collections.ObjectModel;
1312
using System.IO;
1413
using System.Linq;
14+
using System.Reflection;
1515
using System.Threading.Tasks;
1616

1717
namespace OpenLoco.Gui.ViewModels
@@ -64,6 +64,23 @@ public void UpdateHexDumpView()
6464
? GetDumpLines(currentByteList, positionValues.Start, positionValues.End).ToArray()
6565
: GetDumpLines(currentByteList, null, null).ToArray();
6666

67+
public static IObjectViewModel<ILocoStruct> GetViewModelFromStruct(ILocoStruct locoStruct)
68+
{
69+
var asm = Assembly
70+
.GetExecutingAssembly()
71+
.GetTypes()
72+
.SingleOrDefault(type
73+
=> type.IsClass
74+
&& !type.IsAbstract
75+
&& type.BaseType?.IsGenericType == true
76+
&& type.BaseType.GetGenericTypeDefinition() == typeof(LocoObjectViewModel<>)
77+
&& type.BaseType.GenericTypeArguments.Single() == locoStruct.GetType());
78+
79+
return asm == null
80+
? new GenericObjectViewModel() { Object = locoStruct }
81+
: (Activator.CreateInstance(asm, locoStruct) as IObjectViewModel<ILocoStruct>)!;
82+
}
83+
6784
public override void Load()
6885
{
6986
// this stops any currently-playing sounds
@@ -80,57 +97,13 @@ public override void Load()
8097

8198
if (CurrentObject?.LocoObject != null)
8299
{
83-
if (CurrentObject.LocoObject.Object is VehicleObject veh)
84-
{
85-
CurrentObjectViewModel = new VehicleViewModel(veh);
86-
}
87-
else if (CurrentObject.LocoObject.Object is TownNamesObject tn)
88-
{
89-
CurrentObjectViewModel = new TownNamesViewModel(tn);
90-
}
91-
else if (CurrentObject.LocoObject.Object is AirportObject ao)
92-
{
93-
CurrentObjectViewModel = new AirportViewModel(ao);
94-
}
95-
else if (CurrentObject.LocoObject.Object is IndustryObject io)
96-
{
97-
CurrentObjectViewModel = new IndustryViewModel(io);
98-
}
99-
else if (CurrentObject.LocoObject.Object is BuildingObject bo)
100-
{
101-
CurrentObjectViewModel = new BuildingViewModel(bo);
102-
}
103-
else if (CurrentObject.LocoObject.Object is TrainStationObject ts)
104-
{
105-
CurrentObjectViewModel = new TrainStationViewModel(ts);
106-
}
107-
else if (CurrentObject.LocoObject.Object is TrackObject to)
108-
{
109-
CurrentObjectViewModel = new TrackViewModel(to);
110-
}
111-
else if (CurrentObject.LocoObject.Object is RegionObject ro)
112-
{
113-
CurrentObjectViewModel = new RegionViewModel(ro);
114-
}
115-
else if (CurrentObject.LocoObject.Object is TrainSignalObject tso)
116-
{
117-
CurrentObjectViewModel = new TrainSignalViewModel(tso);
118-
}
119-
else if (CurrentObject.LocoObject.Object is StreetLightObject sl)
120-
{
121-
CurrentObjectViewModel = new StreetLightViewModel(sl);
122-
}
123-
else if (CurrentObject.LocoObject.Object is ClimateObject cl)
124-
{
125-
CurrentObjectViewModel = new ClimateViewModel(cl);
126-
}
127-
else
128-
{
129-
CurrentObjectViewModel = new GenericObjectViewModel() { Object = CurrentObject.LocoObject.Object };
130-
}
131-
132-
var imageNameProvider = (CurrentObject.LocoObject.Object is IImageTableNameProvider itnp) ? itnp : new DefaultImageTableNameProvider();
100+
CurrentObjectViewModel = GetViewModelFromStruct(CurrentObject.LocoObject.Object);
133101
StringTableViewModel = new(CurrentObject.LocoObject.StringTable);
102+
103+
var imageNameProvider = (CurrentObject.LocoObject.Object is IImageTableNameProvider itnp)
104+
? itnp
105+
: new DefaultImageTableNameProvider();
106+
134107
ExtraContentViewModel = CurrentObject.LocoObject.Object is SoundObject soundObject
135108
? new SoundViewModel(CurrentObject.DatFileInfo.S5Header.Name, soundObject.SoundObjectData.PcmHeader, soundObject.PcmData)
136109
: new ImageTableViewModel(CurrentObject.LocoObject, imageNameProvider, Model.PaletteMap, CurrentObject.Images, Model.Logger);

Gui/ViewModels/DatTypes/IObjectViewModel.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
namespace OpenLoco.Gui.ViewModels
22
{
3-
public interface IObjectViewModel<T>
3+
// this is purely to bind to the UI elements since Avalonia XAML doesn't support binding to generic types
4+
public interface IObjectViewModel
5+
{ }
6+
7+
public interface IObjectViewModel<T> : IObjectViewModel
48
{
59
T GetAsUnderlyingType(T underlyingType);
610
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using OpenLoco.Dat.Objects;
2+
using ReactiveUI.Fody.Helpers;
3+
using System.ComponentModel;
4+
using System.Linq;
5+
6+
namespace OpenLoco.Gui.ViewModels
7+
{
8+
public class BridgeViewModel : LocoObjectViewModel<BridgeObject>
9+
{
10+
[Reactive] public uint8_t NoRoof { get; set; }
11+
[Reactive] public uint8_t SpanLength { get; set; }
12+
[Reactive] public uint8_t PillarSpacing { get; set; }
13+
[Reactive] public Speed16 MaxSpeed { get; set; }
14+
[Reactive] public MicroZ MaxHeight { get; set; }
15+
[Reactive] public BridgeDisabledTrackFlags DisabledTrackFlags { get; set; }
16+
[Reactive] public uint16_t DesignedYear { get; set; }
17+
[Reactive, Category("Cost")] public uint8_t CostIndex { get; set; }
18+
[Reactive, Category("Cost")] public int16_t BaseCostFactor { get; set; }
19+
[Reactive, Category("Cost")] public int16_t HeightCostFactor { get; set; }
20+
[Reactive, Category("Cost")] public int16_t SellCostFactor { get; set; }
21+
[Reactive] public BindingList<S5HeaderViewModel> TrackCompatibleMods { get; set; }
22+
[Reactive] public BindingList<S5HeaderViewModel> RoadCompatibleMods { get; set; }
23+
[Reactive, Category("<unknown>")] public uint16_t var_06 { get; set; }
24+
[Reactive, Category("<unknown>")] public BindingList<uint8_t> var_03 { get; set; }
25+
26+
public BridgeViewModel(BridgeObject bo)
27+
{
28+
NoRoof = bo.NoRoof;
29+
SpanLength = bo.SpanLength;
30+
PillarSpacing = bo.PillarSpacing;
31+
MaxSpeed = bo.MaxSpeed;
32+
MaxHeight = bo.MaxHeight;
33+
CostIndex = bo.CostIndex;
34+
BaseCostFactor = bo.BaseCostFactor;
35+
HeightCostFactor = bo.HeightCostFactor;
36+
SellCostFactor = bo.SellCostFactor;
37+
DisabledTrackFlags = bo.DisabledTrackFlags;
38+
DesignedYear = bo.DesignedYear;
39+
TrackCompatibleMods = new(bo.TrackCompatibleMods.ConvertAll(x => new S5HeaderViewModel(x)));
40+
RoadCompatibleMods = new(bo.RoadCompatibleMods.ConvertAll(x => new S5HeaderViewModel(x)));
41+
var_03 = new(bo.var_03);
42+
var_06 = bo.var_06;
43+
}
44+
45+
public override BridgeObject GetAsStruct(BridgeObject bro)
46+
=> bro with
47+
{
48+
NoRoof = NoRoof,
49+
SpanLength = SpanLength,
50+
PillarSpacing = PillarSpacing,
51+
MaxSpeed = MaxSpeed,
52+
MaxHeight = MaxHeight,
53+
CostIndex = CostIndex,
54+
BaseCostFactor = BaseCostFactor,
55+
HeightCostFactor = HeightCostFactor,
56+
SellCostFactor = SellCostFactor,
57+
DisabledTrackFlags = DisabledTrackFlags,
58+
DesignedYear = DesignedYear,
59+
TrackCompatibleMods = TrackCompatibleMods.ToList().ConvertAll(x => x.GetAsUnderlyingType()),
60+
RoadCompatibleMods = RoadCompatibleMods.ToList().ConvertAll(x => x.GetAsUnderlyingType()),
61+
NumCompatibleTrackMods = (uint8_t)TrackCompatibleMods.Count,
62+
NumCompatibleRoadMods = (uint8_t)RoadCompatibleMods.Count,
63+
var_03 = [.. var_03],
64+
var_06 = var_06,
65+
};
66+
}
67+
}

Gui/ViewModels/DatTypes/Objects/ClimateViewModel.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using OpenLoco.Dat.Objects;
1+
using OpenLoco.Dat.Objects;
22
using PropertyModels.Extensions;
33
using ReactiveUI.Fody.Helpers;
44
using System.ComponentModel;
@@ -22,8 +22,8 @@ public ClimateViewModel(ClimateObject ro)
2222
SeasonLengths = ro.SeasonLengths.ToBindingList();
2323
}
2424

25-
public override ClimateObject GetAsStruct(ClimateObject input)
26-
=> input with
25+
public override ClimateObject GetAsStruct(ClimateObject co)
26+
=> co with
2727
{
2828
FirstSeason = FirstSeason,
2929
WinterSnowLine = WinterSnowLine,
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using OpenLoco.Dat.Objects;
2+
using OpenLoco.Dat.Types;
3+
using ReactiveUI.Fody.Helpers;
4+
using System.ComponentModel;
5+
using System.Linq;
6+
7+
namespace OpenLoco.Gui.ViewModels
8+
{
9+
public class DockViewModel : LocoObjectViewModel<DockObject>
10+
{
11+
[Reactive] public DockObjectFlags Flags { get; set; }
12+
[Reactive] public uint16_t DesignedYear { get; set; }
13+
[Reactive] public uint16_t ObsoleteYear { get; set; }
14+
[Reactive] public Pos2 BoatPosition { get; set; }
15+
[Reactive, Category("Cost")] public uint8_t CostIndex { get; set; }
16+
[Reactive, Category("Cost")] public int16_t BuildCostFactor { get; set; }
17+
[Reactive, Category("Cost")] public int16_t SellCostFactor { get; set; }
18+
[Reactive, Category("Building")] public BindingList<uint16_t> BuildingPartAnimations { get; set; }
19+
[Reactive, Category("Building")] public BindingList<uint8_t> BuildingVariationParts { get; set; }
20+
[Reactive, Category("<unknown>")] public uint8_t var_07 { get; set; }
21+
[Reactive, Category("<unknown>")] public BindingList<uint8_t> var_14 { get; set; }
22+
23+
public DockViewModel(DockObject @do)
24+
{
25+
Flags = @do.Flags;
26+
DesignedYear = @do.DesignedYear;
27+
ObsoleteYear = @do.ObsoleteYear;
28+
CostIndex = @do.CostIndex;
29+
BuildCostFactor = @do.BuildCostFactor;
30+
SellCostFactor = @do.SellCostFactor;
31+
BoatPosition = @do.BoatPosition;
32+
BuildingPartAnimations = new(@do.BuildingPartAnimations);
33+
BuildingVariationParts = new(@do.BuildingVariationParts);
34+
var_07 = @do.var_07;
35+
var_14 = new(@do.var_14);
36+
}
37+
38+
public override DockObject GetAsStruct(DockObject @do)
39+
=> @do with
40+
{
41+
Flags = Flags,
42+
DesignedYear = DesignedYear,
43+
ObsoleteYear = ObsoleteYear,
44+
CostIndex = CostIndex,
45+
BuildCostFactor = BuildCostFactor,
46+
SellCostFactor = SellCostFactor,
47+
BoatPosition = BoatPosition,
48+
var_07 = var_07,
49+
BuildingPartAnimations = BuildingPartAnimations.ToList(),
50+
BuildingVariationParts = BuildingVariationParts.ToList(),
51+
NumBuildingPartAnimations = (uint8_t)BuildingPartAnimations.Count,
52+
NumBuildingVariationParts = (uint8_t)BuildingVariationParts.Count,
53+
};
54+
}
55+
}

0 commit comments

Comments
 (0)