Veteran unit production & unlocking through spies.

This commit is contained in:
unknown
2015-05-21 20:59:03 +01:00
committed by Matija Hustić
parent ff2a5ef68b
commit 206ed8ebb8
13 changed files with 257 additions and 10 deletions

View File

@@ -293,6 +293,7 @@
<Compile Include="Traits\Buildings\TargetableBuilding.cs" /> <Compile Include="Traits\Buildings\TargetableBuilding.cs" />
<Compile Include="Traits\Burns.cs" /> <Compile Include="Traits\Burns.cs" />
<Compile Include="Traits\C4Demolition.cs" /> <Compile Include="Traits\C4Demolition.cs" />
<Compile Include="Traits\VeteranProductionIconOverlay.cs" />
<Compile Include="Traits\Capturable.cs" /> <Compile Include="Traits\Capturable.cs" />
<Compile Include="Traits\CaptureNotification.cs" /> <Compile Include="Traits\CaptureNotification.cs" />
<Compile Include="Traits\Captures.cs" /> <Compile Include="Traits\Captures.cs" />
@@ -472,6 +473,7 @@
<Compile Include="Traits\TransformOnPassenger.cs" /> <Compile Include="Traits\TransformOnPassenger.cs" />
<Compile Include="Traits\Transforms.cs" /> <Compile Include="Traits\Transforms.cs" />
<Compile Include="Traits\Turreted.cs" /> <Compile Include="Traits\Turreted.cs" />
<Compile Include="Traits\ProduceableWithLevel.cs" />
<Compile Include="Traits\Upgrades\DeployToUpgrade.cs" /> <Compile Include="Traits\Upgrades\DeployToUpgrade.cs" />
<Compile Include="Traits\Upgrades\DisableUpgrade.cs" /> <Compile Include="Traits\Upgrades\DisableUpgrade.cs" />
<Compile Include="Traits\Upgrades\GainsStatUpgrades.cs" /> <Compile Include="Traits\Upgrades\GainsStatUpgrades.cs" />

View File

@@ -18,7 +18,7 @@ using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
{ {
[Desc("This actor's experience increases when it has killed a GivesExperience actor.")] [Desc("This actor's experience increases when it has killed a GivesExperience actor.")]
public class GainsExperienceInfo : ITraitInfo, Requires<ValuedInfo> public class GainsExperienceInfo : ITraitInfo, Requires<ValuedInfo>, Requires<UpgradeManagerInfo>
{ {
[FieldLoader.LoadUsing("LoadUpgrades")] [FieldLoader.LoadUsing("LoadUpgrades")]
[Desc("Upgrades to grant at each level", [Desc("Upgrades to grant at each level",
@@ -29,6 +29,9 @@ namespace OpenRA.Mods.Common.Traits
[Desc("Palette for the level up sprite.")] [Desc("Palette for the level up sprite.")]
public readonly string LevelUpPalette = "effect"; public readonly string LevelUpPalette = "effect";
[Desc("Should the level-up animation be suppressed when actor is created?")]
public readonly bool SuppressLevelupAnimation = true;
public object Create(ActorInitializer init) { return new GainsExperience(init, this); } public object Create(ActorInitializer init) { return new GainsExperience(init, this); }
static object LoadUpgrades(MiniYaml y) static object LoadUpgrades(MiniYaml y)
@@ -56,6 +59,7 @@ namespace OpenRA.Mods.Common.Traits
{ {
readonly Actor self; readonly Actor self;
readonly GainsExperienceInfo info; readonly GainsExperienceInfo info;
readonly UpgradeManager um;
readonly List<Pair<int, string[]>> nextLevel = new List<Pair<int, string[]>>(); readonly List<Pair<int, string[]>> nextLevel = new List<Pair<int, string[]>>();
@@ -77,18 +81,20 @@ namespace OpenRA.Mods.Common.Traits
nextLevel.Add(Pair.New(kv.Key * cost, kv.Value)); nextLevel.Add(Pair.New(kv.Key * cost, kv.Value));
if (init.Contains<ExperienceInit>()) if (init.Contains<ExperienceInit>())
GiveExperience(init.Get<ExperienceInit, int>()); GiveExperience(init.Get<ExperienceInit, int>(), info.SuppressLevelupAnimation);
um = self.Trait<UpgradeManager>();
} }
public bool CanGainLevel { get { return Level < MaxLevel; } } public bool CanGainLevel { get { return Level < MaxLevel; } }
public void GiveLevels(int numLevels) public void GiveLevels(int numLevels, bool silent = false)
{ {
var newLevel = Math.Min(Level + numLevels, MaxLevel); var newLevel = Math.Min(Level + numLevels, MaxLevel);
GiveExperience(nextLevel[newLevel - 1].First - experience); GiveExperience(nextLevel[newLevel - 1].First - experience, silent);
} }
public void GiveExperience(int amount) public void GiveExperience(int amount, bool silent = false)
{ {
experience += amount; experience += amount;
@@ -98,11 +104,12 @@ namespace OpenRA.Mods.Common.Traits
Level++; Level++;
var um = self.TraitOrDefault<UpgradeManager>(); foreach (var u in upgrades)
if (um != null) um.GrantUpgrade(self, u, this);
foreach (var u in upgrades) }
um.GrantUpgrade(self, u, this);
if (!silent)
{
Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Sounds", "LevelUp", self.Owner.Country.Race); 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))); self.World.AddFrameEndTask(w => w.Add(new CrateEffect(self, "levelup", info.LevelUpPalette)));
} }

View File

@@ -0,0 +1,52 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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 OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Actors possessing this trait should define the GainsExperience trait. When the prerequisites are fulfilled, ",
"this trait grants a level-up to newly spawned actors. If additionally the actor's owning player defines the ProductionIconOverlay ",
"trait, the production queue icon renders with an overlay defined in that trait.")]
public class ProduceableWithLevelInfo : ITraitInfo, Requires<GainsExperienceInfo>
{
public readonly string[] Prerequisites = { };
[Desc("Number of levels to give to the actor on creation.")]
public readonly int InitialLevels = 1;
[Desc("Should the level-up animation be suppressed when actor is created?")]
public readonly bool SuppressLevelupAnimation = true;
public object Create(ActorInitializer init) { return new ProduceableWithLevel(init, this); }
}
public class ProduceableWithLevel : INotifyCreated
{
readonly ProduceableWithLevelInfo info;
public ProduceableWithLevel(ActorInitializer init, ProduceableWithLevelInfo info)
{
this.info = info;
}
public void Created(Actor self)
{
if (!self.Owner.PlayerActor.Trait<TechTree>().HasPrerequisites(info.Prerequisites))
return;
var ge = self.Trait<GainsExperience>();
if (!ge.CanGainLevel)
return;
ge.GiveLevels(info.InitialLevels, info.SuppressLevelupAnimation);
}
}
}

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.Traits
/// Abstract base for enabling and disabling trait using upgrades. /// Abstract base for enabling and disabling trait using upgrades.
/// Requires basing *Info on UpgradableTraitInfo and using base(info) constructor. /// Requires basing *Info on UpgradableTraitInfo and using base(info) constructor.
/// Note that EnabledByUpgrade is not called at creation even if this starts as enabled. /// Note that EnabledByUpgrade is not called at creation even if this starts as enabled.
/// </summary>, /// </summary>
public abstract class UpgradableTrait<InfoType> : IUpgradable, IDisabledTrait, ISync where InfoType : UpgradableTraitInfo public abstract class UpgradableTrait<InfoType> : IUpgradable, IDisabledTrait, ISync where InfoType : UpgradableTraitInfo
{ {
public readonly InfoType Info; public readonly InfoType Info;

View File

@@ -0,0 +1,151 @@
#region Copyright & License Information
/*
* Copyright 2007-2015 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.Collections.Generic;
using OpenRA.Graphics;
using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits
{
[Desc("Attach this to the player actor. When attached, enables all actors possessing the LevelupWhenCreated ",
"trait to have their production queue icons render with an overlay defined in this trait. ",
"The icon change occurs when LevelupWhenCreated.Prerequisites are met.")]
public class VeteranProductionIconOverlayInfo : ITraitInfo, Requires<TechTreeInfo>
{
[Desc("Image used for the overlay.")]
public readonly string Image = null;
[Desc("Sequence used for the overlay (cannot be animated).")]
[SequenceReference("Image")] public readonly string Sequence = null;
[Desc("Palette to render the sprite in. Reference the world actor's PaletteFrom* traits.")]
public readonly string Palette = "chrome";
[Desc("Point on the production icon's used as reference for offsetting the overlay. ",
"Possible values are any combination of Top, VCenter, Bottom and Left, HCenter, Right separated by a comma.")]
public readonly ReferencePoints ReferencePoint = ReferencePoints.Top | ReferencePoints.Left;
[Desc("Pixel offset relative to the icon's reference point.")]
public readonly int2 Offset = int2.Zero;
[Desc("Visual scale of the overlay.")]
public readonly float Scale = 1f;
public object Create(ActorInitializer init) { return new VeteranProductionIconOverlay(init, this); }
}
public class VeteranProductionIconOverlay : ITechTreeElement, IProductionIconOverlay
{
// HACK: TechTree doesn't associate Watcher.Key with the registering ITechTreeElement.
// So in a situation where multiple ITechTreeElements register Watchers with the same Key,
// and one removes its Watcher, all other ITechTreeElements' Watchers get removed too.
// This makes sure that the keys are unique with respect to the registering ITechTreeElement.
const string Prefix = "ProductionIconOverlay.";
readonly Actor self;
readonly Sprite sprite;
readonly VeteranProductionIconOverlayInfo info;
Dictionary<ActorInfo, bool> overlayActive = new Dictionary<ActorInfo, bool>();
public VeteranProductionIconOverlay(ActorInitializer init, VeteranProductionIconOverlayInfo info)
{
self = init.Self;
var anim = new Animation(self.World, info.Image);
anim.Play(info.Sequence);
sprite = anim.Image;
this.info = info;
var ttc = self.Trait<TechTree>();
foreach (var a in self.World.Map.Rules.Actors.Values)
{
var uwc = a.Traits.GetOrDefault<ProduceableWithLevelInfo>();
if (uwc != null)
ttc.Add(MakeKey(a.Name), uwc.Prerequisites, 0, this);
}
}
public Sprite Sprite()
{
return sprite;
}
public string Palette()
{
return info.Palette;
}
public float Scale()
{
return info.Scale;
}
public float2 Offset(float2 iconSize)
{
float offsetX = 0, offsetY = 0;
switch (info.ReferencePoint & (ReferencePoints)3)
{
case ReferencePoints.Top:
offsetY = (-iconSize.Y + sprite.Size.Y) / 2;
break;
case ReferencePoints.VCenter:
break;
case ReferencePoints.Bottom:
offsetY = (iconSize.Y - sprite.Size.Y) / 2;
break;
}
switch (info.ReferencePoint & (ReferencePoints)(3 << 2))
{
case ReferencePoints.Left:
offsetX = (-iconSize.X + sprite.Size.X) / 2;
break;
case ReferencePoints.HCenter:
break;
case ReferencePoints.Right:
offsetX = (iconSize.X - sprite.Size.X) / 2;
break;
}
return new float2(offsetX, offsetY) + info.Offset;
}
public bool IsOverlayActive(ActorInfo ai)
{
bool isActive;
overlayActive.TryGetValue(ai, out isActive);
return isActive;
}
static string MakeKey(string name)
{
return Prefix + name;
}
static string GetName(string key)
{
return key.Substring(Prefix.Length);
}
public void PrerequisitesAvailable(string key)
{
var ai = self.World.Map.Rules.Actors[GetName(key)];
overlayActive[ai] = true;
}
public void PrerequisitesUnavailable(string key) { }
public void PrerequisitesItemHidden(string key) { }
public void PrerequisitesItemVisible(string key) { }
}
}

View File

@@ -73,6 +73,15 @@ namespace OpenRA.Mods.Common.Traits
void PrerequisitesItemVisible(string key); void PrerequisitesItemVisible(string key);
} }
public interface IProductionIconOverlay
{
Sprite Sprite();
string Palette();
float Scale();
float2 Offset(float2 iconSize);
bool IsOverlayActive(ActorInfo ai);
}
public interface INotifyTransform { void BeforeTransform(Actor self); void OnTransform(Actor self); void AfterTransform(Actor toActor); } public interface INotifyTransform { void BeforeTransform(Actor self); void OnTransform(Actor self); void AfterTransform(Actor toActor); }
public interface IAcceptResources public interface IAcceptResources

View File

@@ -79,6 +79,11 @@ namespace OpenRA.Mods.Common.Widgets
var location = new float2(RenderBounds.Location) + new float2(queue.i * (IconWidth + IconSpacing), 0); var location = new float2(RenderBounds.Location) + new float2(queue.i * (IconWidth + IconSpacing), 0);
WidgetUtils.DrawSHPCentered(icon.Image, location + 0.5f * iconSize, worldRenderer.Palette(bi.IconPalette), 0.5f); WidgetUtils.DrawSHPCentered(icon.Image, location + 0.5f * iconSize, worldRenderer.Palette(bi.IconPalette), 0.5f);
var pio = queue.Trait.Actor.Owner.PlayerActor.TraitsImplementing<IProductionIconOverlay>().FirstOrDefault();
if (pio != null && pio.IsOverlayActive(actor))
WidgetUtils.DrawSHPCentered(pio.Sprite(), location + 0.5f * iconSize + pio.Offset(0.5f * iconSize),
worldRenderer.Palette(pio.Palette()), 0.5f * pio.Scale());
var clock = clocks[queue.Trait]; var clock = clocks[queue.Trait];
clock.PlayFetchIndex("idle", clock.PlayFetchIndex("idle",
() => current.TotalTime == 0 ? 0 : ((current.TotalTime - current.RemainingTime) () => current.TotalTime == 0 ? 0 : ((current.TotalTime - current.RemainingTime)

View File

@@ -351,11 +351,18 @@ namespace OpenRA.Mods.Common.Widgets
var buildableItems = CurrentQueue.BuildableItems(); var buildableItems = CurrentQueue.BuildableItems();
var pio = currentQueue.Actor.Owner.PlayerActor.TraitsImplementing<IProductionIconOverlay>().FirstOrDefault();
var pioOffset = pio != null ? pio.Offset(IconSize) : new float2(0, 0);
// Icons // Icons
foreach (var icon in icons.Values) foreach (var icon in icons.Values)
{ {
WidgetUtils.DrawSHPCentered(icon.Sprite, icon.Pos + iconOffset, icon.Palette); WidgetUtils.DrawSHPCentered(icon.Sprite, icon.Pos + iconOffset, icon.Palette);
// Draw the ProductionIconOverlay's sprite
if (pio != null && pio.IsOverlayActive(icon.Actor))
WidgetUtils.DrawSHPCentered(pio.Sprite(), icon.Pos + iconOffset + pioOffset, worldRenderer.Palette(pio.Palette()), pio.Scale());
// Build progress // Build progress
if (icon.Queued.Count > 0) if (icon.Queued.Count > 0)
{ {

Binary file not shown.

Binary file not shown.

View File

@@ -10,6 +10,10 @@
Filename: temperat.pal Filename: temperat.pal
ShadowIndex: 3 ShadowIndex: 3
AllowModifiers: false AllowModifiers: false
PaletteFromFile@cameo-chevron:
Name: cameo-chevron
Filename: cameo-chevron.pal
AllowModifiers: false
PaletteFromFile@effect: PaletteFromFile@effect:
Name: effect Name: effect
Filename: temperat.pal Filename: temperat.pal

View File

@@ -65,4 +65,9 @@ Player:
Prerequisites: techlevel.infonly, techlevel.low, techlevel.medium, techlevel.unrestricted Prerequisites: techlevel.infonly, techlevel.low, techlevel.medium, techlevel.unrestricted
GlobalUpgradeManager: GlobalUpgradeManager:
EnemyWatcher: EnemyWatcher:
VeteranProductionIconOverlay:
Offset: 2, 2
Image: cameo-chevron
Sequence: idle
Palette: cameo-chevron

View File

@@ -382,6 +382,11 @@ rank:
rank: rank:
Length: * Length: *
cameo-chevron:
idle:
Length: *
BlendMode: Additive
atomic: atomic:
up: atomicup up: atomicup
Length: * Length: *