Veteran unit production & unlocking through spies.
This commit is contained in:
@@ -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" />
|
||||||
|
|||||||
@@ -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)));
|
||||||
}
|
}
|
||||||
|
|||||||
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.
|
/// 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;
|
||||||
|
|||||||
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);
|
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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
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
|
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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -382,6 +382,11 @@ rank:
|
|||||||
rank:
|
rank:
|
||||||
Length: *
|
Length: *
|
||||||
|
|
||||||
|
cameo-chevron:
|
||||||
|
idle:
|
||||||
|
Length: *
|
||||||
|
BlendMode: Additive
|
||||||
|
|
||||||
atomic:
|
atomic:
|
||||||
up: atomicup
|
up: atomicup
|
||||||
Length: *
|
Length: *
|
||||||
|
|||||||
Reference in New Issue
Block a user