Files
OpenRA/OpenRA.Mods.RA/GainsExperience.cs
atlimit8 bbd54cb32f Added IDisabledTrait & rewrote upgrade code using a level-based approach.
Upgradeable traits are notified whenever an upgrade of their declared types are granted or revoked.  The traits maintain their own internal level counter, which is then used to enable or disable the trait functionality.  A trait can register for multiple upgrade types which then all affect the internal level counter.

	IDisabledTrait for identifying (and filtering) disabled traits
	UpgradableTrait provides an abstract base for traits to support upgrade levels
	Added IDisabledTrait support to GlobalButtonOrderGenerator

	Includes rework by pchote with alterations.
2014-11-26 05:45:26 -06:00

134 lines
3.9 KiB
C#

#region Copyright & License Information
/*
* Copyright 2007-2014 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. For more information,
* see COPYING.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Mods.Common;
using OpenRA.Mods.Common.Effects;
using OpenRA.Mods.RA.Effects;
using OpenRA.Primitives;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
[Desc("This actor's experience increases when it has killed a GivesExperience actor.")]
public class GainsExperienceInfo : ITraitInfo, Requires<ValuedInfo>
{
[FieldLoader.LoadUsing("LoadUpgrades")]
[Desc("Upgrades to grant at each level",
"Key is the XP requirements for each level as a percentage of our own value.",
"Value is a list of the upgrade types to grant")]
public readonly Dictionary<int, string[]> Upgrades = null;
[Desc("Palette for the chevron glyph rendered in the selection box.")]
public readonly string ChevronPalette = "effect";
[Desc("Palette for the level up sprite.")]
public readonly string LevelUpPalette = "effect";
public object Create(ActorInitializer init) { return new GainsExperience(init, this); }
static object LoadUpgrades(MiniYaml y)
{
MiniYaml upgrades;
if (!y.ToDictionary().TryGetValue("Upgrades", out upgrades))
{
return new Dictionary<int, string[]>()
{
{ 200, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } },
{ 400, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } },
{ 800, new[] { "firepower", "damage", "speed", "reload", "inaccuracy" } },
{ 1600, new[] { "firepower", "damage", "speed", "reload", "inaccuracy", "eliteweapon", "selfheal" } }
};
}
return upgrades.Nodes.ToDictionary(
kv => FieldLoader.GetValue<int>("(key)", kv.Key),
kv => FieldLoader.GetValue<string[]>("(value)", kv.Value.Value));
}
}
public class GainsExperience : ISync
{
readonly Actor self;
readonly GainsExperienceInfo info;
readonly List<Pair<int, string[]>> nextLevel = new List<Pair<int, string[]>>();
// Stored as a percentage of our value
[Sync] int experience = 0;
[Sync] public int Level { get; private set; }
public readonly int MaxLevel;
public GainsExperience(ActorInitializer init, GainsExperienceInfo info)
{
self = init.self;
this.info = info;
MaxLevel = info.Upgrades.Count;
var cost = self.Info.Traits.Get<ValuedInfo>().Cost;
foreach (var kv in info.Upgrades)
nextLevel.Add(Pair.New(kv.Key * cost, kv.Value));
if (init.Contains<ExperienceInit>())
GiveExperience(init.Get<ExperienceInit, int>());
}
public bool CanGainLevel { get { return Level < MaxLevel; } }
public void GiveLevels(int numLevels)
{
var newLevel = Math.Min(Level + numLevels, MaxLevel);
GiveExperience(nextLevel[newLevel - 1].First - experience);
}
public void GiveExperience(int amount)
{
experience += amount;
while (Level < MaxLevel && experience >= nextLevel[Level].First)
{
var upgrades = nextLevel[Level].Second;
Level++;
var um = self.TraitOrDefault<UpgradeManager>();
if (um != null)
foreach (var u in upgrades)
um.GrantUpgrade(self, u, this);
Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", "LevelUp", self.Owner.Country.Race);
self.World.AddFrameEndTask(w => w.Add(new CrateEffect(self, "levelup", info.LevelUpPalette)));
if (Level == 1)
{
self.World.AddFrameEndTask(w =>
{
if (!self.IsDead)
w.Add(new Rank(self, info.ChevronPalette));
});
}
}
}
}
class ExperienceInit : IActorInit<int>
{
[FieldFromYamlKey] public readonly int value = 0;
public ExperienceInit() { }
public ExperienceInit(int init) { value = init; }
public int Value(World world) { return value; }
}
}