diff --git a/OpenRA.Mods.Common/Activities/DonateExperience.cs b/OpenRA.Mods.Common/Activities/DonateExperience.cs new file mode 100644 index 0000000000..24466b4dba --- /dev/null +++ b/OpenRA.Mods.Common/Activities/DonateExperience.cs @@ -0,0 +1,53 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using OpenRA.Activities; +using OpenRA.Mods.Common.Traits; + +namespace OpenRA.Mods.Common.Activities +{ + class DonateExperience : Enter + { + readonly Actor target; + readonly GainsExperience targetGainsExperience; + readonly int level; + readonly int playerExperience; + + public DonateExperience(Actor self, Actor target, int level, int playerExperience, GainsExperience targetGainsExperience) + : base(self, target, EnterBehaviour.Dispose) + { + this.target = target; + this.level = level; + this.playerExperience = playerExperience; + this.targetGainsExperience = targetGainsExperience; + } + + protected override void OnInside(Actor self) + { + if (target.IsDead) + return; + + targetGainsExperience.GiveLevels(level); + + var exp = self.Owner.PlayerActor.TraitOrDefault(); + if (exp != null && target.Owner != self.Owner) + exp.GiveExperience(playerExperience); + } + + public override Activity Tick(Actor self) + { + if (target.IsDead || targetGainsExperience.Level == targetGainsExperience.MaxLevel) + Cancel(self); + + return base.Tick(self); + } + } +} diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 51876aa248..40eb4f8805 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -91,6 +91,7 @@ + @@ -253,6 +254,7 @@ + @@ -324,6 +326,8 @@ + + @@ -485,7 +489,6 @@ - diff --git a/OpenRA.Mods.Common/Traits/AcceptsDeliveredExperience.cs b/OpenRA.Mods.Common/Traits/AcceptsDeliveredExperience.cs new file mode 100644 index 0000000000..345ae97162 --- /dev/null +++ b/OpenRA.Mods.Common/Traits/AcceptsDeliveredExperience.cs @@ -0,0 +1,33 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("Tag trait for actors with `DeliversExperience`.")] + public class AcceptsDeliveredExperienceInfo : ITraitInfo, Requires + { + [Desc("Accepted `DeliversExperience` types. Leave empty to accept all types.")] + public readonly HashSet ValidTypes = new HashSet(); + + [Desc("Stance the delivering actor needs to enter.")] + public readonly Stance ValidStances = Stance.Ally; + + public object Create(ActorInitializer init) { return new AcceptsDeliveredExperience(init.Self, this); } + } + + public class AcceptsDeliveredExperience + { + public AcceptsDeliveredExperience(Actor self, AcceptsDeliveredExperienceInfo info) { } + } +} diff --git a/OpenRA.Mods.Common/Traits/DeliversExperience.cs b/OpenRA.Mods.Common/Traits/DeliversExperience.cs new file mode 100644 index 0000000000..15192ba90f --- /dev/null +++ b/OpenRA.Mods.Common/Traits/DeliversExperience.cs @@ -0,0 +1,140 @@ +#region Copyright & License Information +/* + * Copyright 2007-2017 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Drawing; +using OpenRA.Mods.Common.Activities; +using OpenRA.Mods.Common.Orders; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Traits +{ + [Desc("This actor can grant experience levels equal to it's own current level via entering to other actors with the `AcceptsDeliveredExperience` trait.")] + class DeliversExperienceInfo : ITraitInfo, Requires + { + [Desc("The amount of experience the donating player receives.")] + public readonly int PlayerExperience = 0; + + [Desc("Identifier checked against AcceptsDeliveredExperience.ValidTypes. Only needed if the latter is not empty.")] + public readonly string Type = null; + + [VoiceReference] public readonly string Voice = "Action"; + + public object Create(ActorInitializer init) { return new DeliversExperience(init, this); } + } + + class DeliversExperience : IIssueOrder, IResolveOrder, IOrderVoice + { + readonly DeliversExperienceInfo info; + readonly Actor self; + readonly GainsExperience gainsExperience; + + public DeliversExperience(ActorInitializer init, DeliversExperienceInfo info) + { + this.info = info; + self = init.Self; + gainsExperience = self.Trait(); + } + + public IEnumerable Orders + { + get + { + if (gainsExperience.Level != 0) + yield return new DeliversExperienceOrderTargeter(); + } + } + + public Order IssueOrder(Actor self, IOrderTargeter order, Target target, bool queued) + { + if (order.OrderID != "DeliverExperience") + return null; + + if (target.Type == TargetType.FrozenActor) + return new Order(order.OrderID, self, queued) { ExtraData = target.FrozenActor.ID }; + + return new Order(order.OrderID, self, queued) { TargetActor = target.Actor }; + } + + public string VoicePhraseForOrder(Actor self, Order order) + { + return info.Voice; + } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString != "DeliverExperience") + return; + + var target = self.ResolveFrozenActorOrder(order, Color.Yellow); + if (target.Type != TargetType.Actor) + return; + + var targetGainsExperience = target.Actor.Trait(); + if (targetGainsExperience.Level == targetGainsExperience.MaxLevel) + return; + + if (!order.Queued) + self.CancelActivity(); + + var level = gainsExperience.Level; + + self.SetTargetLine(target, Color.Yellow); + self.QueueActivity(new DonateExperience(self, target.Actor, level, info.PlayerExperience, targetGainsExperience)); + } + + public class DeliversExperienceOrderTargeter : UnitOrderTargeter + { + public DeliversExperienceOrderTargeter() + : base("DeliverExperience", 5, "enter", true, true) { } + + public override bool CanTargetActor(Actor self, Actor target, TargetModifiers modifiers, ref string cursor) + { + if (target == self) + return false; + + var type = self.Info.TraitInfo().Type; + var targetInfo = target.Info.TraitInfoOrDefault(); + var targetGainsExperience = target.TraitOrDefault(); + + if (targetGainsExperience == null || targetInfo == null) + return false; + + if (targetGainsExperience.Level == targetGainsExperience.MaxLevel) + return false; + + return targetInfo.ValidStances.HasStance(target.Owner.Stances[self.Owner]) + && (targetInfo.ValidTypes.Count == 0 + || (!string.IsNullOrEmpty(type) && targetInfo.ValidTypes.Contains(type))); + } + + public override bool CanTargetFrozenActor(Actor self, FrozenActor target, TargetModifiers modifiers, ref string cursor) + { + if (target.Actor == null || target.Actor == self) + return false; + + var type = self.Info.TraitInfo().Type; + var targetInfo = target.Info.TraitInfoOrDefault(); + var targetGainsExperience = target.Actor.TraitOrDefault(); + + if (targetGainsExperience == null || targetInfo == null) + return false; + + if (targetGainsExperience.Level == targetGainsExperience.MaxLevel) + return false; + + return targetInfo.ValidStances.HasStance(target.Owner.Stances[self.Owner]) + && (targetInfo.ValidTypes.Count == 0 + || (!string.IsNullOrEmpty(type) && targetInfo.ValidTypes.Contains(type))); + } + } + } +}