Veteran unit production & unlocking through spies.
This commit is contained in:
@@ -293,6 +293,7 @@
|
||||
<Compile Include="Traits\Buildings\TargetableBuilding.cs" />
|
||||
<Compile Include="Traits\Burns.cs" />
|
||||
<Compile Include="Traits\C4Demolition.cs" />
|
||||
<Compile Include="Traits\VeteranProductionIconOverlay.cs" />
|
||||
<Compile Include="Traits\Capturable.cs" />
|
||||
<Compile Include="Traits\CaptureNotification.cs" />
|
||||
<Compile Include="Traits\Captures.cs" />
|
||||
@@ -472,6 +473,7 @@
|
||||
<Compile Include="Traits\TransformOnPassenger.cs" />
|
||||
<Compile Include="Traits\Transforms.cs" />
|
||||
<Compile Include="Traits\Turreted.cs" />
|
||||
<Compile Include="Traits\ProduceableWithLevel.cs" />
|
||||
<Compile Include="Traits\Upgrades\DeployToUpgrade.cs" />
|
||||
<Compile Include="Traits\Upgrades\DisableUpgrade.cs" />
|
||||
<Compile Include="Traits\Upgrades\GainsStatUpgrades.cs" />
|
||||
|
||||
@@ -18,7 +18,7 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[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")]
|
||||
[Desc("Upgrades to grant at each level",
|
||||
@@ -29,6 +29,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
[Desc("Palette for the level up sprite.")]
|
||||
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); }
|
||||
|
||||
static object LoadUpgrades(MiniYaml y)
|
||||
@@ -56,6 +59,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
readonly Actor self;
|
||||
readonly GainsExperienceInfo info;
|
||||
readonly UpgradeManager um;
|
||||
|
||||
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));
|
||||
|
||||
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 void GiveLevels(int numLevels)
|
||||
public void GiveLevels(int numLevels, bool silent = false)
|
||||
{
|
||||
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;
|
||||
|
||||
@@ -98,11 +104,12 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
Level++;
|
||||
|
||||
var um = self.TraitOrDefault<UpgradeManager>();
|
||||
if (um != null)
|
||||
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);
|
||||
self.World.AddFrameEndTask(w => w.Add(new CrateEffect(self, "levelup", info.LevelUpPalette)));
|
||||
}
|
||||
|
||||
52
OpenRA.Mods.Common/Traits/ProduceableWithLevel.cs
Normal file
52
OpenRA.Mods.Common/Traits/ProduceableWithLevel.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
/// Abstract base for enabling and disabling trait using upgrades.
|
||||
/// Requires basing *Info on UpgradableTraitInfo and using base(info) constructor.
|
||||
/// 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 readonly InfoType Info;
|
||||
|
||||
151
OpenRA.Mods.Common/Traits/VeteranProductionIconOverlay.cs
Normal file
151
OpenRA.Mods.Common/Traits/VeteranProductionIconOverlay.cs
Normal 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) { }
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,15 @@ namespace OpenRA.Mods.Common.Traits
|
||||
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 IAcceptResources
|
||||
|
||||
@@ -79,6 +79,11 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
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);
|
||||
|
||||
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];
|
||||
clock.PlayFetchIndex("idle",
|
||||
() => current.TotalTime == 0 ? 0 : ((current.TotalTime - current.RemainingTime)
|
||||
|
||||
@@ -351,11 +351,18 @@ namespace OpenRA.Mods.Common.Widgets
|
||||
|
||||
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
|
||||
foreach (var icon in icons.Values)
|
||||
{
|
||||
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
|
||||
if (icon.Queued.Count > 0)
|
||||
{
|
||||
|
||||
BIN
mods/ra/bits/cameo-chevron.pal
Normal file
BIN
mods/ra/bits/cameo-chevron.pal
Normal file
Binary file not shown.
BIN
mods/ra/bits/cameo-chevron.shp
Normal file
BIN
mods/ra/bits/cameo-chevron.shp
Normal file
Binary file not shown.
@@ -10,6 +10,10 @@
|
||||
Filename: temperat.pal
|
||||
ShadowIndex: 3
|
||||
AllowModifiers: false
|
||||
PaletteFromFile@cameo-chevron:
|
||||
Name: cameo-chevron
|
||||
Filename: cameo-chevron.pal
|
||||
AllowModifiers: false
|
||||
PaletteFromFile@effect:
|
||||
Name: effect
|
||||
Filename: temperat.pal
|
||||
|
||||
@@ -65,4 +65,9 @@ Player:
|
||||
Prerequisites: techlevel.infonly, techlevel.low, techlevel.medium, techlevel.unrestricted
|
||||
GlobalUpgradeManager:
|
||||
EnemyWatcher:
|
||||
VeteranProductionIconOverlay:
|
||||
Offset: 2, 2
|
||||
Image: cameo-chevron
|
||||
Sequence: idle
|
||||
Palette: cameo-chevron
|
||||
|
||||
|
||||
@@ -382,6 +382,11 @@ rank:
|
||||
rank:
|
||||
Length: *
|
||||
|
||||
cameo-chevron:
|
||||
idle:
|
||||
Length: *
|
||||
BlendMode: Additive
|
||||
|
||||
atomic:
|
||||
up: atomicup
|
||||
Length: *
|
||||
|
||||
Reference in New Issue
Block a user