diff --git a/OpenRA.Mods.Common/Effects/Bullet.cs b/OpenRA.Mods.Common/Effects/Bullet.cs index 30236ec260..bef851c0da 100644 --- a/OpenRA.Mods.Common/Effects/Bullet.cs +++ b/OpenRA.Mods.Common/Effects/Bullet.cs @@ -31,8 +31,8 @@ namespace OpenRA.Mods.Common.Effects [Desc("Image to display.")] public readonly string Image = null; - [Desc("Loop this sequence of Image while this projectile is moving."), SequenceReference("Image")] - public readonly string Sequence = "idle"; + [Desc("Loop this sequence of Image while this projectile is moving.")] + [SequenceReference("Image")] public readonly string Sequence = "idle"; [Desc("The palette used to draw this projectile.")] public readonly string Palette = "effect"; diff --git a/OpenRA.Mods.Common/Effects/GravityBomb.cs b/OpenRA.Mods.Common/Effects/GravityBomb.cs index b41438b0d3..0a3bb40a08 100644 --- a/OpenRA.Mods.Common/Effects/GravityBomb.cs +++ b/OpenRA.Mods.Common/Effects/GravityBomb.cs @@ -19,13 +19,19 @@ namespace OpenRA.Mods.Common.Effects public class GravityBombInfo : IProjectileInfo { public readonly string Image = null; + [Desc("Sequence to loop while falling.")] - public readonly string Sequence = "idle"; + [SequenceReference("Image")] public readonly string Sequence = "idle"; + [Desc("Sequence to play when launched. Skipped if null or empty.")] - public readonly string OpenSequence = null; + [SequenceReference("Image")] public readonly string OpenSequence = null; + public readonly string Palette = "effect"; + public readonly bool Shadow = false; + public readonly WDist Velocity = WDist.Zero; + [Desc("Value added to velocity every tick.")] public readonly WDist Acceleration = new WDist(15); diff --git a/OpenRA.Mods.Common/Effects/LaserZap.cs b/OpenRA.Mods.Common/Effects/LaserZap.cs index c7cab338fb..6018af5273 100644 --- a/OpenRA.Mods.Common/Effects/LaserZap.cs +++ b/OpenRA.Mods.Common/Effects/LaserZap.cs @@ -22,14 +22,20 @@ namespace OpenRA.Mods.Common.Effects class LaserZapInfo : IProjectileInfo { public readonly int BeamWidth = 2; + public readonly int BeamDuration = 10; + public readonly bool UsePlayerColor = false; + [Desc("Laser color in (A,)R,G,B.")] public readonly Color Color = Color.Red; + [Desc("Impact animation.")] public readonly string HitAnim = null; + [Desc("Sequence of impact animation to use.")] - public readonly string HitAnimSequence = "idle"; + [SequenceReference("HitAnim")] public readonly string HitAnimSequence = "idle"; + public readonly string HitAnimPalette = "effect"; public IEffect Create(ProjectileArgs args) diff --git a/OpenRA.Mods.Common/Effects/Missile.cs b/OpenRA.Mods.Common/Effects/Missile.cs index 903e1a1b88..bfd59ea81c 100644 --- a/OpenRA.Mods.Common/Effects/Missile.cs +++ b/OpenRA.Mods.Common/Effects/Missile.cs @@ -24,38 +24,54 @@ namespace OpenRA.Mods.Common.Effects { public readonly string Image = null; [SequenceReference("Image")] public readonly string Sequence = "idle"; + public readonly string Palette = "effect"; + public readonly bool Shadow = false; + [Desc("Projectile speed in WDist / tick")] public readonly WDist Speed = new WDist(8); + [Desc("Maximum vertical pitch when changing altitude.")] public readonly WAngle MaximumPitch = WAngle.FromDegrees(30); + [Desc("How many ticks before this missile is armed and can explode.")] public readonly int Arm = 0; + [Desc("Is the missile blocked by actors with BlocksProjectiles: trait.")] public readonly bool Blockable = true; + [Desc("Maximum offset at the maximum range")] public readonly WDist Inaccuracy = WDist.Zero; + [Desc("Probability of locking onto and following target.")] public readonly int LockOnProbability = 100; + [Desc("In n/256 per tick.")] public readonly int RateOfTurn = 5; + [Desc("Explode when following the target longer than this many ticks.")] public readonly int RangeLimit = 0; + [Desc("Trail animation.")] public readonly string Trail = null; + [Desc("Interval in ticks between each spawned Trail animation.")] public readonly int TrailInterval = 2; + public readonly string TrailPalette = "effect"; public readonly bool TrailUsePlayerPalette = false; public readonly int ContrailLength = 0; public readonly Color ContrailColor = Color.White; public readonly bool ContrailUsePlayerColor = false; public readonly int ContrailDelay = 1; + [Desc("Should missile targeting be thrown off by nearby actors with JamsMissiles.")] public readonly bool Jammable = true; + [Desc("Explodes when leaving the following terrain type, e.g., Water for torpedoes.")] public readonly string BoundToTerrainType = ""; + [Desc("Explodes when inside this proximity radius to target.", "Note: If this value is lower than the missile speed, this check might", "not trigger fast enough, causing the missile to fly past the target.")] diff --git a/OpenRA.Mods.Common/Lint/CheckSequences.cs b/OpenRA.Mods.Common/Lint/CheckSequences.cs index 05c3108092..284959f624 100644 --- a/OpenRA.Mods.Common/Lint/CheckSequences.cs +++ b/OpenRA.Mods.Common/Lint/CheckSequences.cs @@ -12,6 +12,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; +using OpenRA.GameRules; using OpenRA.Mods.Common.Traits; using OpenRA.Traits; @@ -89,6 +90,48 @@ namespace OpenRA.Mods.Common.Lint } } } + + foreach (var weaponInfo in map.Rules.Weapons) + { + var projectileInfo = weaponInfo.Value.Projectile; + if (projectileInfo == null) + continue; + + var fields = projectileInfo.GetType().GetFields(); + foreach (var field in fields) + { + if (field.HasAttribute()) + { + var sequences = LintExts.GetFieldValues(projectileInfo, field, emitError); + foreach (var sequence in sequences) + { + if (string.IsNullOrEmpty(sequence)) + continue; + + var sequenceReference = field.GetCustomAttributes(true).FirstOrDefault(); + if (sequenceReference != null && !string.IsNullOrEmpty(sequenceReference.ImageReference)) + { + var imageField = fields.FirstOrDefault(f => f.Name == sequenceReference.ImageReference); + if (imageField != null) + { + foreach (var imageOverride in LintExts.GetFieldValues(projectileInfo, imageField, emitError)) + { + if (!string.IsNullOrEmpty(imageOverride)) + { + var definitions = sequenceDefinitions.FirstOrDefault(n => n.Key == imageOverride.ToLowerInvariant()); + if (definitions == null) + emitWarning("Can't find sequence definition for projectile image {0} at weapon {1}.".F(imageOverride, weaponInfo.Key)); + else if (!definitions.Value.Nodes.Any(n => n.Key == sequence)) + emitWarning("Projectile sprite image {0} from weapon {1} does not define sequence {2} from field {3} of {4}" + .F(imageOverride, weaponInfo.Key, sequence, field.Name, projectileInfo)); + } + } + } + } + } + } + } + } } } diff --git a/OpenRA.Mods.Common/UtilityCommands/CheckYaml.cs b/OpenRA.Mods.Common/UtilityCommands/CheckYaml.cs index 73da3275fa..30922a8537 100644 --- a/OpenRA.Mods.Common/UtilityCommands/CheckYaml.cs +++ b/OpenRA.Mods.Common/UtilityCommands/CheckYaml.cs @@ -77,7 +77,7 @@ namespace OpenRA.Mods.Common.UtilityCommands } catch (Exception e) { - EmitError("{0} failed with exception: {0}".F(customPassType, e)); + EmitError("{0} failed with exception: {1}".F(customPassType, e)); } } } diff --git a/OpenRA.Mods.RA/Effects/TeslaZap.cs b/OpenRA.Mods.RA/Effects/TeslaZap.cs index c0f10bc6e6..e65dfd6b9d 100644 --- a/OpenRA.Mods.RA/Effects/TeslaZap.cs +++ b/OpenRA.Mods.RA/Effects/TeslaZap.cs @@ -20,9 +20,15 @@ namespace OpenRA.Mods.RA.Effects class TeslaZapInfo : IProjectileInfo { public readonly string Image = "litning"; + + [SequenceReference("Image")] public readonly string BrightSequence = "bright"; + [SequenceReference("Image")] public readonly string DimSequence = "dim"; + public readonly string Palette = "effect"; + public readonly int BrightZaps = 1; public readonly int DimZaps = 2; + public IEffect Create(ProjectileArgs args) { return new TeslaZap(this, args); } } @@ -59,7 +65,8 @@ namespace OpenRA.Mods.RA.Effects if (!initialized) { var pos = args.GuidedTarget.IsValidFor(args.SourceActor) ? args.GuidedTarget.CenterPosition : args.PassiveTarget; - zap = new TeslaZapRenderable(args.Source, 0, pos - args.Source, info.Image, info.BrightZaps, info.DimZaps, info.Palette); + zap = new TeslaZapRenderable(args.Source, 0, pos - args.Source, + info.Image, info.BrightSequence, info.BrightZaps, info.DimSequence, info.DimZaps, info.Palette); } yield return zap; diff --git a/OpenRA.Mods.RA/Graphics/TeslaZapRenderable.cs b/OpenRA.Mods.RA/Graphics/TeslaZapRenderable.cs index b226736fe8..6e583765bf 100644 --- a/OpenRA.Mods.RA/Graphics/TeslaZapRenderable.cs +++ b/OpenRA.Mods.RA/Graphics/TeslaZapRenderable.cs @@ -35,13 +35,15 @@ namespace OpenRA.Mods.RA.Graphics readonly WVec length; readonly string image; readonly string palette; + readonly string dimSequence; + readonly string brightSequence; readonly int brightZaps, dimZaps; WPos cachedPos; WVec cachedLength; IEnumerable cache; - public TeslaZapRenderable(WPos pos, int zOffset, WVec length, string image, int brightZaps, int dimZaps, string palette) + public TeslaZapRenderable(WPos pos, int zOffset, WVec length, string image, string brightSequence, int brightZaps, string dimSequence, int dimZaps, string palette) { this.pos = pos; this.zOffset = zOffset; @@ -50,6 +52,8 @@ namespace OpenRA.Mods.RA.Graphics this.palette = palette; this.brightZaps = brightZaps; this.dimZaps = dimZaps; + this.dimSequence = dimSequence; + this.brightSequence = brightSequence; cachedPos = WPos.Zero; cachedLength = WVec.Zero; @@ -61,17 +65,20 @@ namespace OpenRA.Mods.RA.Graphics public int ZOffset { get { return zOffset; } } public bool IsDecoration { get { return true; } } - public IRenderable WithPalette(PaletteReference newPalette) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps, palette); } - public IRenderable WithZOffset(int newOffset) { return new TeslaZapRenderable(pos, zOffset, length, image, brightZaps, dimZaps, palette); } - public IRenderable OffsetBy(WVec vec) { return new TeslaZapRenderable(pos + vec, zOffset, length, image, brightZaps, dimZaps, palette); } + public IRenderable WithPalette(PaletteReference newPalette) + { + return new TeslaZapRenderable(pos, zOffset, length, image, brightSequence, brightZaps, dimSequence, dimZaps, palette); + } + + public IRenderable WithZOffset(int newOffset) { return new TeslaZapRenderable(pos, zOffset, length, image, brightSequence, brightZaps, dimSequence, dimZaps, palette); } + public IRenderable OffsetBy(WVec vec) { return new TeslaZapRenderable(pos + vec, zOffset, length, image, brightSequence, brightZaps, dimSequence, dimZaps, palette); } public IRenderable AsDecoration() { return this; } public IFinalizedRenderable PrepareRender(WorldRenderer wr) { return this; } public void RenderDebugGeometry(WorldRenderer wr) { } public void Render(WorldRenderer wr) { - if (wr.World.FogObscures(pos) && - wr.World.FogObscures(pos + length)) + if (wr.World.FogObscures(pos) && wr.World.FogObscures(pos + length)) return; if (!cache.Any() || length != cachedLength || pos != cachedPos) @@ -84,8 +91,8 @@ namespace OpenRA.Mods.RA.Graphics public IEnumerable GenerateRenderables(WorldRenderer wr) { - var bright = wr.World.Map.SequenceProvider.GetSequence(image, "bright"); - var dim = wr.World.Map.SequenceProvider.GetSequence(image, "dim"); + var bright = wr.World.Map.SequenceProvider.GetSequence(image, brightSequence); + var dim = wr.World.Map.SequenceProvider.GetSequence(image, dimSequence); var source = wr.ScreenPosition(pos); var target = wr.ScreenPosition(pos + length);