diff --git a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs index a0fae122f4..6114bd801e 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/BaseBuilderBotModule.cs @@ -125,7 +125,8 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new BaseBuilderBotModule(init.Self, this); } } - public class BaseBuilderBotModule : ConditionalTrait, IBotTick, IBotPositionsUpdated, IBotRespondToAttack, IBotRequestPauseUnitProduction + public class BaseBuilderBotModule : ConditionalTrait, IGameSaveTraitData, + IBotTick, IBotPositionsUpdated, IBotRespondToAttack, IBotRequestPauseUnitProduction { public CPos GetRandomBaseCenter() { @@ -268,5 +269,31 @@ namespace OpenRA.Mods.Common.Traits return AIUtils.CountBuildingByCommonName(Info.BarracksTypes, player) > 0 ? 2 : 1; } } + + List IGameSaveTraitData.IssueTraitData(Actor self) + { + if (IsTraitDisabled) + return null; + + return new List() + { + new MiniYamlNode("InitialBaseCenter", FieldSaver.FormatValue(initialBaseCenter)), + new MiniYamlNode("DefenseCenter", FieldSaver.FormatValue(defenseCenter)) + }; + } + + void IGameSaveTraitData.ResolveTraitData(Actor self, List data) + { + if (self.World.IsReplay) + return; + + var initialBaseCenterNode = data.FirstOrDefault(n => n.Key == "InitialBaseCenter"); + if (initialBaseCenterNode != null) + initialBaseCenter = FieldLoader.GetValue("InitialBaseCenter", initialBaseCenterNode.Value.Value); + + var defenseCenterNode = data.FirstOrDefault(n => n.Key == "DefenseCenter"); + if (defenseCenterNode != null) + defenseCenter = FieldLoader.GetValue("DefenseCenter", defenseCenterNode.Value.Value); + } } } diff --git a/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs index 4278395dc7..2be41d3949 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/McvManagerBotModule.cs @@ -49,7 +49,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new McvManagerBotModule(init.Self, this); } } - public class McvManagerBotModule : ConditionalTrait, IBotTick, IBotPositionsUpdated + public class McvManagerBotModule : ConditionalTrait, IBotTick, IBotPositionsUpdated, IGameSaveTraitData { public CPos GetRandomBaseCenter() { @@ -220,5 +220,35 @@ namespace OpenRA.Mods.Common.Traits return findPos(baseCenter, baseCenter, Info.MinBaseRadius, distanceToBaseIsImportant ? Info.MaxBaseRadius : world.Map.Grid.MaximumTileSearchRange); } + + List IGameSaveTraitData.IssueTraitData(Actor self) + { + if (IsTraitDisabled) + return null; + + return new List() + { + new MiniYamlNode("InitialBaseCenter", FieldSaver.FormatValue(initialBaseCenter)), + new MiniYamlNode("ActiveMCVs", FieldSaver.FormatValue(activeMCVs.Select(a => a.ActorID).ToArray())) + }; + } + + void IGameSaveTraitData.ResolveTraitData(Actor self, List data) + { + if (self.World.IsReplay) + return; + + var initialBaseCenterNode = data.FirstOrDefault(n => n.Key == "InitialBaseCenter"); + if (initialBaseCenterNode != null) + initialBaseCenter = FieldLoader.GetValue("InitialBaseCenter", initialBaseCenterNode.Value.Value); + + var activeMCVsNode = data.FirstOrDefault(n => n.Key == "ActiveMCVs"); + if (activeMCVsNode != null) + { + activeMCVs.Clear(); + activeMCVs.AddRange(FieldLoader.GetValue("ActiveMCVs", activeMCVsNode.Value.Value) + .Select(a => world.GetActorById(a))); + } + } } } diff --git a/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs index 07112c674e..f23990bc78 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/SquadManagerBotModule.cs @@ -77,7 +77,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new SquadManagerBotModule(init.Self, this); } } - public class SquadManagerBotModule : ConditionalTrait, IBotTick, IBotRespondToAttack, IBotPositionsUpdated + public class SquadManagerBotModule : ConditionalTrait, IBotEnabled, IBotTick, IBotRespondToAttack, IBotPositionsUpdated, IGameSaveTraitData { public CPos GetRandomBaseCenter() { @@ -95,6 +95,7 @@ namespace OpenRA.Mods.Common.Traits public List Squads = new List(); + IBot bot; IBotPositionsUpdated[] notifyPositionsUpdated; IBotNotifyIdleBaseUnits[] notifyIdleBaseUnits; @@ -140,6 +141,11 @@ namespace OpenRA.Mods.Common.Traits minAttackForceDelayTicks = World.LocalRandom.Next(0, Info.MinimumAttackForceDelay); } + void IBotEnabled.BotEnabled(IBot bot) + { + this.bot = bot; + } + void IBotTick.BotTick(IBot bot) { AssignRolesToIdleUnits(bot); @@ -341,5 +347,73 @@ namespace OpenRA.Mods.Common.Traits ProtectOwn(bot, e.Attacker); } } + + List IGameSaveTraitData.IssueTraitData(Actor self) + { + if (IsTraitDisabled) + return null; + + return new List() + { + new MiniYamlNode("Squads", "", Squads.Select(s => new MiniYamlNode("Squad", s.Serialize())).ToList()), + new MiniYamlNode("InitialBaseCenter", FieldSaver.FormatValue(initialBaseCenter)), + new MiniYamlNode("UnitsHangingAroundTheBase", FieldSaver.FormatValue(unitsHangingAroundTheBase.Select(a => a.ActorID).ToArray())), + new MiniYamlNode("ActiveUnits", FieldSaver.FormatValue(activeUnits.Select(a => a.ActorID).ToArray())), + new MiniYamlNode("RushTicks", FieldSaver.FormatValue(rushTicks)), + new MiniYamlNode("AssignRolesTicks", FieldSaver.FormatValue(assignRolesTicks)), + new MiniYamlNode("AttackForceTicks", FieldSaver.FormatValue(attackForceTicks)), + new MiniYamlNode("MinAttackForceDelayTicks", FieldSaver.FormatValue(minAttackForceDelayTicks)), + }; + } + + void IGameSaveTraitData.ResolveTraitData(Actor self, List data) + { + if (self.World.IsReplay) + return; + + var initialBaseCenterNode = data.FirstOrDefault(n => n.Key == "InitialBaseCenter"); + if (initialBaseCenterNode != null) + initialBaseCenter = FieldLoader.GetValue("InitialBaseCenter", initialBaseCenterNode.Value.Value); + + var unitsHangingAroundTheBaseNode = data.FirstOrDefault(n => n.Key == "UnitsHangingAroundTheBase"); + if (unitsHangingAroundTheBaseNode != null) + { + unitsHangingAroundTheBase.Clear(); + unitsHangingAroundTheBase.AddRange(FieldLoader.GetValue("UnitsHangingAroundTheBase", unitsHangingAroundTheBaseNode.Value.Value) + .Select(a => self.World.GetActorById(a))); + } + + var activeUnitsNode = data.FirstOrDefault(n => n.Key == "ActiveUnits"); + if (activeUnitsNode != null) + { + activeUnits.Clear(); + activeUnits.AddRange(FieldLoader.GetValue("ActiveUnits", activeUnitsNode.Value.Value) + .Select(a => self.World.GetActorById(a))); + } + + var rushTicksNode = data.FirstOrDefault(n => n.Key == "RushTicks"); + if (rushTicksNode != null) + rushTicks = FieldLoader.GetValue("RushTicks", rushTicksNode.Value.Value); + + var assignRolesTicksNode = data.FirstOrDefault(n => n.Key == "AssignRolesTicks"); + if (assignRolesTicksNode != null) + assignRolesTicks = FieldLoader.GetValue("AssignRolesTicks", assignRolesTicksNode.Value.Value); + + var attackForceTicksNode = data.FirstOrDefault(n => n.Key == "AttackForceTicks"); + if (attackForceTicksNode != null) + attackForceTicks = FieldLoader.GetValue("AttackForceTicks", attackForceTicksNode.Value.Value); + + var minAttackForceDelayTicksNode = data.FirstOrDefault(n => n.Key == "MinAttackForceDelayTicks"); + if (minAttackForceDelayTicksNode != null) + minAttackForceDelayTicks = FieldLoader.GetValue("MinAttackForceDelayTicks", minAttackForceDelayTicksNode.Value.Value); + + var squadsNode = data.FirstOrDefault(n => n.Key == "Squads"); + if (squadsNode != null) + { + Squads.Clear(); + foreach (var n in squadsNode.Value.Nodes) + Squads.Add(Squad.Deserialize(bot, this, n.Value)); + } + } } } diff --git a/OpenRA.Mods.Common/Traits/BotModules/Squads/Squad.cs b/OpenRA.Mods.Common/Traits/BotModules/Squads/Squad.cs index c52cc0f2cd..c48cb31ad5 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/Squads/Squad.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/Squads/Squad.cs @@ -86,5 +86,42 @@ namespace OpenRA.Mods.Common.Traits.BotModules.Squads } public WPos CenterPosition { get { return Units.Select(u => u.CenterPosition).Average(); } } + + public MiniYaml Serialize() + { + var nodes = new MiniYaml("", new List() + { + new MiniYamlNode("Type", FieldSaver.FormatValue(Type)), + new MiniYamlNode("Units", FieldSaver.FormatValue(Units.Select(a => a.ActorID).ToArray())), + }); + + if (Target.Type == TargetType.Actor) + nodes.Nodes.Add(new MiniYamlNode("Target", FieldSaver.FormatValue(Target.Actor.ActorID))); + + return nodes; + } + + public static Squad Deserialize(IBot bot, SquadManagerBotModule squadManager, MiniYaml yaml) + { + var type = SquadType.Rush; + Actor targetActor = null; + + var typeNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Type"); + if (typeNode != null) + type = FieldLoader.GetValue("Type", typeNode.Value.Value); + + var targetNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Target"); + if (targetNode != null) + targetActor = squadManager.World.GetActorById(FieldLoader.GetValue("ActiveUnits", targetNode.Value.Value)); + + var squad = new Squad(bot, squadManager, type, targetActor); + + var unitsNode = yaml.Nodes.FirstOrDefault(n => n.Key == "Units"); + if (unitsNode != null) + squad.Units.AddRange(FieldLoader.GetValue("Units", unitsNode.Value.Value) + .Select(a => squadManager.World.GetActorById(a))); + + return squad; + } } } diff --git a/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs index 895f00a579..0743eefda5 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/SupportPowerBotModule.cs @@ -36,7 +36,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new SupportPowerBotModule(init.Self, this); } } - public class SupportPowerBotModule : ConditionalTrait, IBotTick + public class SupportPowerBotModule : ConditionalTrait, IBotTick, IGameSaveTraitData { readonly World world; readonly Player player; @@ -185,5 +185,31 @@ namespace OpenRA.Mods.Common.Traits return bestLocation; } + + List IGameSaveTraitData.IssueTraitData(Actor self) + { + if (IsTraitDisabled) + return null; + + var waitingPowersNodes = waitingPowers + .Select(kv => new MiniYamlNode(kv.Key.Key, FieldSaver.FormatValue(kv.Value))) + .ToList(); + + return new List() + { + new MiniYamlNode("WaitingPowers", "", waitingPowersNodes) + }; + } + + void IGameSaveTraitData.ResolveTraitData(Actor self, List data) + { + if (self.World.IsReplay) + return; + + var waitingPowersNode = data.FirstOrDefault(n => n.Key == "WaitingPowers"); + if (waitingPowersNode != null) + foreach (var n in waitingPowersNode.Value.Nodes) + waitingPowers[supportPowerManager.Powers[n.Key]] = FieldLoader.GetValue("WaitingPowers", n.Value.Value); + } } } diff --git a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs index b9a715d45a..386448939b 100644 --- a/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs +++ b/OpenRA.Mods.Common/Traits/BotModules/UnitBuilderBotModule.cs @@ -39,7 +39,7 @@ namespace OpenRA.Mods.Common.Traits public override object Create(ActorInitializer init) { return new UnitBuilderBotModule(init.Self, this); } } - public class UnitBuilderBotModule : ConditionalTrait, IBotTick, IBotNotifyIdleBaseUnits, IBotRequestUnitProduction + public class UnitBuilderBotModule : ConditionalTrait, IBotTick, IBotNotifyIdleBaseUnits, IBotRequestUnitProduction, IGameSaveTraitData { public const int FeedbackTime = 30; // ticks; = a bit over 1s. must be >= netlag. @@ -209,5 +209,38 @@ namespace OpenRA.Mods.Common.Traits return true; } + + List IGameSaveTraitData.IssueTraitData(Actor self) + { + if (IsTraitDisabled) + return null; + + return new List() + { + new MiniYamlNode("QueuedBuildRequests", FieldSaver.FormatValue(queuedBuildRequests.ToArray())), + new MiniYamlNode("IdleUnits", FieldSaver.FormatValue(idleUnits.Select(a => a.ActorID).ToArray())) + }; + } + + void IGameSaveTraitData.ResolveTraitData(Actor self, List data) + { + if (self.World.IsReplay) + return; + + var queuedBuildRequestsNode = data.FirstOrDefault(n => n.Key == "QueuedBuildRequests"); + if (queuedBuildRequestsNode != null) + { + queuedBuildRequests.Clear(); + queuedBuildRequests.AddRange(FieldLoader.GetValue("QueuedBuildRequests", queuedBuildRequestsNode.Value.Value)); + } + + var idleUnitsNode = data.FirstOrDefault(n => n.Key == "IdleUnits"); + if (idleUnitsNode != null) + { + idleUnits.Clear(); + idleUnits.AddRange(FieldLoader.GetValue("IdleUnits", idleUnitsNode.Value.Value) + .Select(a => world.GetActorById(a))); + } + } } }