diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
index 324674b94b..fa44f8d02b 100644
--- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
+++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj
@@ -245,6 +245,7 @@
+
@@ -388,6 +389,7 @@
+
diff --git a/OpenRA.Mods.Common/Scripting/Properties/PlayerExperienceProperties.cs b/OpenRA.Mods.Common/Scripting/Properties/PlayerExperienceProperties.cs
new file mode 100644
index 0000000000..87aae502f1
--- /dev/null
+++ b/OpenRA.Mods.Common/Scripting/Properties/PlayerExperienceProperties.cs
@@ -0,0 +1,43 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 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 Eluant;
+using OpenRA.Mods.Common.Traits;
+using OpenRA.Scripting;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Scripting
+{
+ [ScriptPropertyGroup("Player")]
+ public class PlayerExperienceProperties : ScriptPlayerProperties, Requires
+ {
+ readonly PlayerExperience exp;
+
+ public PlayerExperienceProperties(ScriptContext context, Player player)
+ : base(context, player)
+ {
+ exp = player.PlayerActor.Trait();
+ }
+
+ public int Experience
+ {
+ get
+ {
+ return exp.Experience;
+ }
+
+ set
+ {
+ exp.GiveExperience(value - exp.Experience);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenRA.Mods.Common/Traits/GivesExperience.cs b/OpenRA.Mods.Common/Traits/GivesExperience.cs
index c08d066387..14573f1d28 100644
--- a/OpenRA.Mods.Common/Traits/GivesExperience.cs
+++ b/OpenRA.Mods.Common/Traits/GivesExperience.cs
@@ -22,6 +22,12 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Stance the attacking player needs to receive the experience.")]
public readonly Stance ValidStances = Stance.Neutral | Stance.Enemy;
+ [Desc("Percentage of the `Experience` value that is being granted to the killing actor.")]
+ public readonly int ActorExperienceModifier = 10000;
+
+ [Desc("Percentage of the `Experience` value that is being granted to the player owning the killing actor.")]
+ public readonly int PlayerExperienceModifier = 0;
+
public object Create(ActorInitializer init) { return new GivesExperience(init.Self, this); }
}
@@ -44,14 +50,17 @@ namespace OpenRA.Mods.Common.Traits
var valued = self.Info.TraitInfoOrDefault();
- // Default experience is 100 times our value
var exp = info.Experience >= 0
? info.Experience
- : valued != null ? valued.Cost * 100 : 0;
+ : valued != null ? valued.Cost : 0;
var killer = e.Attacker.TraitOrDefault();
if (killer != null)
- killer.GiveExperience(exp);
+ killer.GiveExperience(Util.ApplyPercentageModifiers(exp, new[] { info.ActorExperienceModifier }));
+
+ var attackerExp = e.Attacker.Owner.PlayerActor.TraitOrDefault();
+ if (attackerExp != null)
+ attackerExp.GiveExperience(Util.ApplyPercentageModifiers(exp, new[] { info.PlayerExperienceModifier }));
}
}
}
diff --git a/OpenRA.Mods.Common/Traits/Player/PlayerExperience.cs b/OpenRA.Mods.Common/Traits/Player/PlayerExperience.cs
new file mode 100644
index 0000000000..a632787f26
--- /dev/null
+++ b/OpenRA.Mods.Common/Traits/Player/PlayerExperience.cs
@@ -0,0 +1,35 @@
+#region Copyright & License Information
+/*
+ * Copyright 2007-2016 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;
+using System.Linq;
+using OpenRA.Traits;
+
+namespace OpenRA.Mods.Common.Traits
+{
+ [Desc("This trait can be used to track player experience based on units killed with the `GivesExperience` trait.",
+ "It can also be used as a point score system in scripted maps, for example.",
+ "Attach this to the player actor.")]
+ public class PlayerExperienceInfo : ITraitInfo
+ {
+ public object Create(ActorInitializer init) { return new PlayerExperience(); }
+ }
+
+ public class PlayerExperience : ISync
+ {
+ [Sync] public int Experience { get; private set; }
+
+ public void GiveExperience(int num)
+ {
+ Experience += num;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs b/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs
index c8e34a01d8..ab6aa46eca 100644
--- a/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs
+++ b/OpenRA.Mods.Common/Traits/Player/PlayerStatistics.cs
@@ -20,10 +20,10 @@ namespace OpenRA.Mods.Common.Traits
public object Create(ActorInitializer init) { return new PlayerStatistics(init.Self); }
}
- public class PlayerStatistics : ITick, IResolveOrder
+ public class PlayerStatistics : ITick, IResolveOrder, INotifyCreated
{
- World world;
- Player player;
+ PlayerResources resources;
+ PlayerExperience experience;
public int OrderCount;
@@ -31,7 +31,15 @@ namespace OpenRA.Mods.Common.Traits
{
get
{
- return player.PlayerActor.Trait().Earned - earnedAtBeginningOfMinute;
+ return resources != null ? resources.Earned - earnedAtBeginningOfMinute : 0;
+ }
+ }
+
+ public int Experience
+ {
+ get
+ {
+ return experience != null ? experience.Experience : 0;
}
}
@@ -47,23 +55,25 @@ namespace OpenRA.Mods.Common.Traits
public int BuildingsKilled;
public int BuildingsDead;
- public PlayerStatistics(Actor self)
+ public PlayerStatistics(Actor self) { }
+
+ void INotifyCreated.Created(Actor self)
{
- world = self.World;
- player = self.Owner;
+ resources = self.TraitOrDefault();
+ experience = self.TraitOrDefault();
}
void UpdateEarnedThisMinute()
{
EarnedSamples.Enqueue(EarnedThisMinute);
- earnedAtBeginningOfMinute = player.PlayerActor.Trait().Earned;
+ earnedAtBeginningOfMinute = resources != null ? resources.Earned : 0;
if (EarnedSamples.Count > 100)
EarnedSamples.Dequeue();
}
public void Tick(Actor self)
{
- if (world.WorldTick % 1500 == 1)
+ if (self.World.WorldTick % 1500 == 1)
UpdateEarnedThisMinute();
}