diff --git a/src/basegame/CSM.BaseGame.csproj b/src/basegame/CSM.BaseGame.csproj index 1d2e91e9..bc4f165a 100644 --- a/src/basegame/CSM.BaseGame.csproj +++ b/src/basegame/CSM.BaseGame.csproj @@ -76,6 +76,7 @@ + @@ -118,6 +119,7 @@ + @@ -156,6 +158,7 @@ + @@ -198,6 +201,7 @@ + diff --git a/src/basegame/Commands/Data/Buildings/BuildingSetVariationCommand.cs b/src/basegame/Commands/Data/Buildings/BuildingSetVariationCommand.cs new file mode 100644 index 00000000..28906084 --- /dev/null +++ b/src/basegame/Commands/Data/Buildings/BuildingSetVariationCommand.cs @@ -0,0 +1,26 @@ +using CSM.API.Commands; +using ProtoBuf; + +namespace CSM.BaseGame.Commands.Data.Buildings +{ + /// + /// Called when the building variation was changed. + /// + /// Sent by: + /// - BuildingHandler + [ProtoContract] + public class BuildingSetVariationCommand : CommandBase + { + /// + /// The id of the modified building. + /// + [ProtoMember(1)] + public ushort Building { get; set; } + + /// + /// The new variation flags. + /// + [ProtoMember(2)] + public Building.Flags2 Variation { get; set; } + } +} diff --git a/src/basegame/Commands/Data/Roads/RoadAdjustCommand.cs b/src/basegame/Commands/Data/Roads/RoadAdjustCommand.cs new file mode 100644 index 00000000..256a8e2b --- /dev/null +++ b/src/basegame/Commands/Data/Roads/RoadAdjustCommand.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using CSM.API.Commands; +using ProtoBuf; + +namespace CSM.BaseGame.Commands.Data.Roads +{ + /// + /// Sent when the player adjusts the extents of a road through the route menu. + /// + /// Sent by: RoadHandler + [ProtoContract] + public class RoadAdjustCommand : CommandBase + { + /// + /// The segments previously belonging to the edited road. + /// + [ProtoMember(1)] + public HashSet OriginalSegments { get; set; } + + /// + /// The segments now belonging to the edited road. + /// + [ProtoMember(2)] + public HashSet IncludedSegments { get; set; } + + /// + /// The selected road segment instance. + /// + [ProtoMember(3)] + public InstanceID LastInstance { get; set; } + } +} diff --git a/src/basegame/Commands/Handler/Buildings/BuildingSetVariationHandler.cs b/src/basegame/Commands/Handler/Buildings/BuildingSetVariationHandler.cs new file mode 100644 index 00000000..016a5a05 --- /dev/null +++ b/src/basegame/Commands/Handler/Buildings/BuildingSetVariationHandler.cs @@ -0,0 +1,21 @@ +using ColossalFramework; +using CSM.API.Commands; +using CSM.API.Helpers; +using CSM.BaseGame.Commands.Data.Buildings; + +namespace CSM.BaseGame.Commands.Handler.Buildings +{ + public class BuildingSetVariationHandler : CommandHandler + { + protected override void Handle(BuildingSetVariationCommand command) + { + IgnoreHelper.Instance.StartIgnore(); + + CommonBuildingAI building_ai = Singleton.instance.m_buildings.m_buffer[command.Building].Info.m_buildingAI as CommonBuildingAI; + if (building_ai != null) + building_ai.ReplaceVariation(command.Building, command.Variation); + + IgnoreHelper.Instance.EndIgnore(); + } + } +} diff --git a/src/basegame/Commands/Handler/Roads/RoadAdjustHandler.cs b/src/basegame/Commands/Handler/Roads/RoadAdjustHandler.cs new file mode 100644 index 00000000..466fc81a --- /dev/null +++ b/src/basegame/Commands/Handler/Roads/RoadAdjustHandler.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using CSM.API.Commands; +using CSM.API.Helpers; +using CSM.BaseGame.Commands.Data.Roads; + +namespace CSM.BaseGame.Commands.Handler.Roads +{ + public class RoadAdjustHandler : CommandHandler + { + private NetAdjust _adjustDummy; + + protected override void Handle(RoadAdjustCommand command) + { + IgnoreHelper.Instance.StartIgnore(); + + if (_adjustDummy == null) + { + SetupAdjustDummy(); + } + + ReflectionHelper.SetAttr(_adjustDummy, "m_originalSegments", command.OriginalSegments); + ReflectionHelper.SetAttr(_adjustDummy, "m_includedSegments", command.IncludedSegments); + ReflectionHelper.SetAttr(_adjustDummy, "m_lastInstance", command.LastInstance); + ReflectionHelper.SetAttr(_adjustDummy, "m_tempAdjustmentIndex", 0); + + _adjustDummy.ApplyModification(0); + + IgnoreHelper.Instance.EndIgnore(); + } + + private void SetupAdjustDummy() + { + _adjustDummy = new NetAdjust(); + ReflectionHelper.SetAttr(_adjustDummy, "m_pathVisible", true); + ReflectionHelper.SetAttr(_adjustDummy, "m_startPath", new FastList()); + ReflectionHelper.SetAttr(_adjustDummy, "m_endPath", new FastList()); + ReflectionHelper.SetAttr(_adjustDummy, "m_tempPath", new FastList()); + ReflectionHelper.SetAttr(_adjustDummy, "m_segmentQueue", new FastList()); + ReflectionHelper.SetAttr(_adjustDummy, "m_segmentData", new Dictionary()); + } + } +} diff --git a/src/basegame/Injections/BuildingHandler.cs b/src/basegame/Injections/BuildingHandler.cs index e1807b03..e8637275 100644 --- a/src/basegame/Injections/BuildingHandler.cs +++ b/src/basegame/Injections/BuildingHandler.cs @@ -423,4 +423,28 @@ public static void Postfix(ushort buildingId, VehicleInfo info) }); } } + + [HarmonyPatch(typeof(CommonBuildingAI))] + [HarmonyPatch("ReplaceVariation")] + public class ReplaceVariation + { + public static void Prefix(ushort buildingID, Building.Flags2 variation) + { + if (IgnoreHelper.Instance.IsIgnored()) + { + return; + } + + if ((variation & Building.Flags2.SubmeshVariation) == Building.Flags2.None) + { + return; + } + + Command.SendToAll(new BuildingSetVariationCommand + { + Building = buildingID, + Variation = variation, + }); + } + } } diff --git a/src/basegame/Injections/RoadHandler.cs b/src/basegame/Injections/RoadHandler.cs index 76329ba7..eb8077ab 100644 --- a/src/basegame/Injections/RoadHandler.cs +++ b/src/basegame/Injections/RoadHandler.cs @@ -86,4 +86,27 @@ public static MethodBase TargetMethod() return ReflectionHelper.GetIteratorTargetMethod(typeof(NetManager), "c__Iterator3", out Type _); } } + + [HarmonyPatch(typeof(NetAdjust))] + [HarmonyPatch("ApplyModification")] + public class ApplyModification + { + public static void Prefix(int index, bool ___m_pathVisible, int ___m_tempAdjustmentIndex, + HashSet ___m_originalSegments, HashSet ___m_includedSegments, + InstanceID ___m_lastInstance) + { + if (IgnoreHelper.Instance.IsIgnored()) + return; + + if (!___m_pathVisible || ___m_tempAdjustmentIndex != index) + return; + + Command.SendToAll(new RoadAdjustCommand() + { + OriginalSegments = ___m_originalSegments, + IncludedSegments = ___m_includedSegments, + LastInstance = ___m_lastInstance, + }); + } + } }