diff --git a/OpenRA.Mods.Common/Graphics/ContrailRenderable.cs b/OpenRA.Mods.Common/Graphics/ContrailRenderable.cs index b5e1e0d3e7..a397ef9e8e 100644 --- a/OpenRA.Mods.Common/Graphics/ContrailRenderable.cs +++ b/OpenRA.Mods.Common/Graphics/ContrailRenderable.cs @@ -27,19 +27,21 @@ namespace OpenRA.Mods.Common.Graphics // Store trail positions in a circular buffer readonly WPos[] trail; - readonly WDist width; + readonly WDist startWidth; + readonly WDist endWidth; int next; int length; readonly int skip; - public ContrailRenderable(World world, Color startcolor, Color endcolor, WDist width, int length, int skip, int zOffset) - : this(world, new WPos[length], width, 0, 0, skip, startcolor, endcolor, zOffset) { } + public ContrailRenderable(World world, Color startcolor, Color endcolor, WDist startWidth, WDist endWidth, int length, int skip, int zOffset) + : this(world, new WPos[length], startWidth, endWidth, 0, 0, skip, startcolor, endcolor, zOffset) { } - ContrailRenderable(World world, WPos[] trail, WDist width, int next, int length, int skip, Color startcolor, Color endcolor, int zOffset) + ContrailRenderable(World world, WPos[] trail, WDist startWidth, WDist endWidth, int next, int length, int skip, Color startcolor, Color endcolor, int zOffset) { this.world = world; this.trail = trail; - this.width = width; + this.startWidth = startWidth; + this.endWidth = endWidth; this.next = next; this.length = length; this.skip = skip; @@ -52,12 +54,12 @@ namespace OpenRA.Mods.Common.Graphics public int ZOffset { get; } public bool IsDecoration => true; - public IRenderable WithZOffset(int newOffset) { return new ContrailRenderable(world, (WPos[])trail.Clone(), width, next, length, skip, startcolor, endcolor, newOffset); } + public IRenderable WithZOffset(int newOffset) { return new ContrailRenderable(world, (WPos[])trail.Clone(), startWidth, endWidth, next, length, skip, startcolor, endcolor, newOffset); } public IRenderable OffsetBy(in WVec vec) { // Lambdas can't use 'in' variables, so capture a copy for later var offset = vec; - return new ContrailRenderable(world, trail.Select(pos => pos + offset).ToArray(), width, next, length, skip, startcolor, endcolor, ZOffset); + return new ContrailRenderable(world, trail.Select(pos => pos + offset).ToArray(), startWidth, endWidth, next, length, skip, startcolor, endcolor, ZOffset); } public IRenderable AsDecoration() { return this; } @@ -71,7 +73,7 @@ namespace OpenRA.Mods.Common.Graphics if (renderLength <= 1) return; - var screenWidth = wr.ScreenVector(new WVec(width, WDist.Zero, WDist.Zero))[0]; + var screenWidth = wr.ScreenVector(new WVec(1, 0, 0))[0]; var wcr = Game.Renderer.WorldRgbaColorRenderer; // Start of the first line segment is the tail of the list - don't smooth it. @@ -97,8 +99,20 @@ namespace OpenRA.Mods.Common.Graphics var nextPos = new WPos((int)(nextX / k), (int)(nextY / k), (int)(nextZ / k)); - if (!world.FogObscures(curPos) && !world.FogObscures(nextPos)) - wcr.DrawLine(wr.Screen3DPosition(curPos), wr.Screen3DPosition(nextPos), screenWidth, curColor, nextColor); + // When renderLength = 2 we are rendering only one segment, so it needs to be handled differently to avoid + // division by 0. For width we choose startWidth instead of the average as this makes the transition between + // rendering 1 and multiple segments smoother. Above checks make sure that renderLength can never be lower than 2. + float width; + if (renderLength == 2) + width = startWidth.Length; + else + { + var lerp = (i - 1f) / (renderLength - 2); + width = startWidth.Length * (1 - lerp) + endWidth.Length * lerp; + } + + if (width > 0 && !world.FogObscures(curPos) && !world.FogObscures(nextPos)) + wcr.DrawLine(wr.Screen3DPosition(curPos), wr.Screen3DPosition(nextPos), screenWidth * width, curColor, nextColor); curPos = nextPos; curColor = nextColor; diff --git a/OpenRA.Mods.Common/Projectiles/Bullet.cs b/OpenRA.Mods.Common/Projectiles/Bullet.cs index cdfb0f425b..b774819dd0 100644 --- a/OpenRA.Mods.Common/Projectiles/Bullet.cs +++ b/OpenRA.Mods.Common/Projectiles/Bullet.cs @@ -110,8 +110,11 @@ namespace OpenRA.Mods.Common.Projectiles [Desc("Equivalent to sequence ZOffset. Controls Z sorting.")] public readonly int ContrailZOffset = 2047; - [Desc("Thickness of the emitted line.")] - public readonly WDist ContrailWidth = new(64); + [Desc("Thickness of the emitted line at the start of the contrail.")] + public readonly WDist ContrailStartWidth = new(64); + + [Desc("Thickness of the emitted line at the end of the contrail. Will default to " + nameof(ContrailStartWidth) + " if left undefined")] + public readonly WDist? ContrailEndWidth = null; [Desc("RGB color at the contrail start.")] public readonly Color ContrailStartColor = Color.White; @@ -198,7 +201,7 @@ namespace OpenRA.Mods.Common.Projectiles { var startcolor = info.ContrailStartColorUsePlayerColor ? Color.FromArgb(info.ContrailStartColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailStartColorAlpha, info.ContrailStartColor); var endcolor = info.ContrailEndColorUsePlayerColor ? Color.FromArgb(info.ContrailEndColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailEndColorAlpha, info.ContrailEndColor ?? startcolor); - contrail = new ContrailRenderable(world, startcolor, endcolor, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset); + contrail = new ContrailRenderable(world, startcolor, endcolor, info.ContrailStartWidth, info.ContrailEndWidth ?? info.ContrailStartWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset); } trailPalette = info.TrailPalette; diff --git a/OpenRA.Mods.Common/Projectiles/Missile.cs b/OpenRA.Mods.Common/Projectiles/Missile.cs index a1d9f90904..9ca6050b40 100644 --- a/OpenRA.Mods.Common/Projectiles/Missile.cs +++ b/OpenRA.Mods.Common/Projectiles/Missile.cs @@ -139,8 +139,11 @@ namespace OpenRA.Mods.Common.Projectiles [Desc("Equivalent to sequence ZOffset. Controls Z sorting.")] public readonly int ContrailZOffset = 2047; - [Desc("Thickness of the emitted line.")] - public readonly WDist ContrailWidth = new(64); + [Desc("Thickness of the emitted line at the start of the contrail.")] + public readonly WDist ContrailStartWidth = new(64); + + [Desc("Thickness of the emitted line at the end of the contrail. Will default to " + nameof(ContrailStartWidth) + " if left undefined")] + public readonly WDist? ContrailEndWidth = null; [Desc("RGB color at the contrail start.")] public readonly Color ContrailStartColor = Color.White; @@ -284,7 +287,7 @@ namespace OpenRA.Mods.Common.Projectiles { var startcolor = info.ContrailStartColorUsePlayerColor ? Color.FromArgb(info.ContrailStartColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailStartColorAlpha, info.ContrailStartColor); var endcolor = info.ContrailEndColorUsePlayerColor ? Color.FromArgb(info.ContrailEndColorAlpha, args.SourceActor.Owner.Color) : Color.FromArgb(info.ContrailEndColorAlpha, info.ContrailEndColor ?? startcolor); - contrail = new ContrailRenderable(world, startcolor, endcolor, info.ContrailWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset); + contrail = new ContrailRenderable(world, startcolor, endcolor, info.ContrailStartWidth, info.ContrailEndWidth ?? info.ContrailStartWidth, info.ContrailLength, info.ContrailDelay, info.ContrailZOffset); } trailPalette = info.TrailPalette; diff --git a/OpenRA.Mods.Common/Traits/Render/Contrail.cs b/OpenRA.Mods.Common/Traits/Render/Contrail.cs index 226b743e97..73ecd8921c 100644 --- a/OpenRA.Mods.Common/Traits/Render/Contrail.cs +++ b/OpenRA.Mods.Common/Traits/Render/Contrail.cs @@ -33,8 +33,11 @@ namespace OpenRA.Mods.Common.Traits [Desc("Time (in ticks) after which the line should appear. Controls the distance to the actor.")] public readonly int TrailDelay = 0; - [Desc("Thickness of the emitted line.")] - public readonly WDist TrailWidth = new(64); + [Desc("Thickness of the emitted line at the start of the contrail.")] + public readonly WDist StartWidth = new(64); + + [Desc("Thickness of the emitted line at the end of the contrail. Will default to " + nameof(StartWidth) + " if left undefined")] + public readonly WDist? EndWidth = null; [Desc("RGB color at the contrail start.")] public readonly Color StartColor = Color.White; @@ -74,7 +77,7 @@ namespace OpenRA.Mods.Common.Traits startcolor = info.StartColorUsePlayerColor ? Color.FromArgb(info.StartColorAlpha, self.Owner.Color) : Color.FromArgb(info.StartColorAlpha, info.StartColor); endcolor = info.EndColorUsePlayerColor ? Color.FromArgb(info.EndColorAlpha, self.Owner.Color) : Color.FromArgb(info.EndColorAlpha, info.EndColor ?? startcolor); - trail = new ContrailRenderable(self.World, startcolor, endcolor, info.TrailWidth, info.TrailLength, info.TrailDelay, info.ZOffset); + trail = new ContrailRenderable(self.World, startcolor, endcolor, info.StartWidth, info.EndWidth ?? info.StartWidth, info.TrailLength, info.TrailDelay, info.ZOffset); body = self.Trait(); } @@ -103,7 +106,7 @@ namespace OpenRA.Mods.Common.Traits void INotifyAddedToWorld.AddedToWorld(Actor self) { - trail = new ContrailRenderable(self.World, startcolor, endcolor, info.TrailWidth, info.TrailLength, info.TrailDelay, info.ZOffset); + trail = new ContrailRenderable(self.World, startcolor, endcolor, info.StartWidth, info.EndWidth ?? info.StartWidth, info.TrailLength, info.TrailDelay, info.ZOffset); } } } diff --git a/OpenRA.Mods.Common/UpdateRules/Rules/20221203/RenameContrailWidth.cs b/OpenRA.Mods.Common/UpdateRules/Rules/20221203/RenameContrailWidth.cs new file mode 100644 index 0000000000..5774d43cb3 --- /dev/null +++ b/OpenRA.Mods.Common/UpdateRules/Rules/20221203/RenameContrailWidth.cs @@ -0,0 +1,42 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * 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, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace OpenRA.Mods.Common.UpdateRules.Rules +{ + public class RenameContrailWidth : UpdateRule + { + public override string Name => "Rename contrail width"; + + public override string Description => "Rename contrail `TrailWidth` to `StartWidth` in traits and weapons to acount for added `EndWidth` functionality"; + + public override IEnumerable UpdateActorNode(ModData modData, MiniYamlNode actorNode) + { + foreach (var traitNode in actorNode.ChildrenMatching("Contrail")) + traitNode.RenameChildrenMatching("TrailWidth", "StartWidth"); + + yield break; + } + + public override IEnumerable UpdateWeaponNode(ModData modData, MiniYamlNode weaponNode) + { + foreach (var traitNode in weaponNode.ChildrenMatching("Projectile").Where(n => n.Value.Value == "Missile")) + traitNode.RenameChildrenMatching("ContrailWidth", "ContrailStartWidth"); + + foreach (var traitNode in weaponNode.ChildrenMatching("Projectile").Where(n => n.Value.Value == "Bullet")) + traitNode.RenameChildrenMatching("ContrailWidth", "ContrailStartWidth"); + + yield break; + } + } +} diff --git a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs index 928e988716..3f4cd99aa4 100644 --- a/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs +++ b/OpenRA.Mods.Common/UpdateRules/UpdatePath.cs @@ -96,6 +96,7 @@ namespace OpenRA.Mods.Common.UpdateRules new RemoveTSRefinery(), new RenameMcvCrateAction(), new RemoveSequenceHasEmbeddedPalette(), + new RenameContrailWidth(), }) };