@@ -619,6 +619,8 @@
|
||||
<Compile Include="UtilityCommands\CheckSequenceSprites.cs" />
|
||||
<Compile Include="UtilityCommands\FixClassicTilesets.cs" />
|
||||
<Compile Include="Graphics\TilesetSpecificSpriteSequence.cs" />
|
||||
<Compile Include="Traits\Pluggable.cs" />
|
||||
<Compile Include="Traits\Plug.cs" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace OpenRA.Mods.Common.Orders
|
||||
readonly string building;
|
||||
readonly BuildingInfo buildingInfo;
|
||||
readonly PlaceBuildingInfo placeBuildingInfo;
|
||||
readonly BuildingInfluence buildingInfluence;
|
||||
readonly string race;
|
||||
readonly Sprite buildOk;
|
||||
readonly Sprite buildBlocked;
|
||||
@@ -52,6 +53,8 @@ namespace OpenRA.Mods.Common.Orders
|
||||
|
||||
buildOk = map.SequenceProvider.GetSequence("overlay", "build-valid-{0}".F(tileset)).GetSprite(0);
|
||||
buildBlocked = map.SequenceProvider.GetSequence("overlay", "build-invalid").GetSprite(0);
|
||||
|
||||
buildingInfluence = producer.World.WorldActor.Trait<BuildingInfluence>();
|
||||
}
|
||||
|
||||
public IEnumerable<Order> Order(World world, CPos xy, MouseInput mi)
|
||||
@@ -73,16 +76,33 @@ namespace OpenRA.Mods.Common.Orders
|
||||
|
||||
if (mi.Button == MouseButton.Left)
|
||||
{
|
||||
var orderType = "PlaceBuilding";
|
||||
var topLeft = xy - FootprintUtils.AdjustForBuildingSize(buildingInfo);
|
||||
if (!world.CanPlaceBuilding(building, buildingInfo, topLeft, null)
|
||||
|| !buildingInfo.IsCloseEnoughToBase(world, producer.Owner, building, topLeft))
|
||||
|
||||
var plugInfo = world.Map.Rules.Actors[building].Traits.GetOrDefault<PlugInfo>();
|
||||
if (plugInfo != null)
|
||||
{
|
||||
Sound.PlayNotification(world.Map.Rules, producer.Owner, "Speech", "BuildingCannotPlaceAudio", producer.Owner.Country.Race);
|
||||
yield break;
|
||||
orderType = "PlacePlug";
|
||||
if (!AcceptsPlug(topLeft, plugInfo))
|
||||
{
|
||||
Sound.PlayNotification(world.Map.Rules, producer.Owner, "Speech", "BuildingCannotPlaceAudio", producer.Owner.Country.Race);
|
||||
yield break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!world.CanPlaceBuilding(building, buildingInfo, topLeft, null)
|
||||
|| !buildingInfo.IsCloseEnoughToBase(world, producer.Owner, building, topLeft))
|
||||
{
|
||||
Sound.PlayNotification(world.Map.Rules, producer.Owner, "Speech", "BuildingCannotPlaceAudio", producer.Owner.Country.Race);
|
||||
yield break;
|
||||
}
|
||||
|
||||
if (world.Map.Rules.Actors[building].Traits.Contains<LineBuildInfo>())
|
||||
orderType = "LineBuild";
|
||||
}
|
||||
|
||||
var isLineBuild = world.Map.Rules.Actors[building].Traits.Contains<LineBuildInfo>();
|
||||
yield return new Order(isLineBuild ? "LineBuild" : "PlaceBuilding", producer.Owner.PlayerActor, false)
|
||||
yield return new Order(orderType, producer.Owner.PlayerActor, false)
|
||||
{
|
||||
TargetLocation = topLeft,
|
||||
TargetActor = producer,
|
||||
@@ -101,6 +121,16 @@ namespace OpenRA.Mods.Common.Orders
|
||||
p.Tick();
|
||||
}
|
||||
|
||||
bool AcceptsPlug(CPos cell, PlugInfo plug)
|
||||
{
|
||||
var host = buildingInfluence.GetBuildingAt(cell);
|
||||
if (host == null)
|
||||
return false;
|
||||
|
||||
var location = host.Location;
|
||||
return host.TraitsImplementing<Pluggable>().Any(p => location + p.Info.Offset == cell && p.AcceptsPlug(host, plug.Type));
|
||||
}
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World world) { yield break; }
|
||||
public IEnumerable<IRenderable> RenderAfterWorld(WorldRenderer wr, World world)
|
||||
{
|
||||
@@ -116,10 +146,17 @@ namespace OpenRA.Mods.Common.Orders
|
||||
|
||||
var cells = new Dictionary<CPos, bool>();
|
||||
|
||||
// Linebuild for walls.
|
||||
// Requires a 1x1 footprint
|
||||
if (rules.Actors[building].Traits.Contains<LineBuildInfo>())
|
||||
var plugInfo = rules.Actors[building].Traits.GetOrDefault<PlugInfo>();
|
||||
if (plugInfo != null)
|
||||
{
|
||||
if (buildingInfo.Dimensions.X != 1 || buildingInfo.Dimensions.Y != 1)
|
||||
throw new InvalidOperationException("Plug requires a 1x1 sized Building");
|
||||
|
||||
cells.Add(topLeft, AcceptsPlug(topLeft, plugInfo));
|
||||
}
|
||||
else if (rules.Actors[building].Traits.Contains<LineBuildInfo>())
|
||||
{
|
||||
// Linebuild for walls.
|
||||
if (buildingInfo.Dimensions.X != 1 || buildingInfo.Dimensions.Y != 1)
|
||||
throw new InvalidOperationException("LineBuild requires a 1x1 sized Building");
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public abstract class AttackBaseInfo : ITraitInfo
|
||||
public abstract class AttackBaseInfo : UpgradableTraitInfo, ITraitInfo
|
||||
{
|
||||
[Desc("Armament names")]
|
||||
public readonly string[] Armaments = { "primary", "secondary" };
|
||||
@@ -35,11 +35,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public abstract object Create(ActorInitializer init);
|
||||
}
|
||||
|
||||
public abstract class AttackBase : IIssueOrder, IResolveOrder, IOrderVoice, ISync
|
||||
public abstract class AttackBase : UpgradableTrait<AttackBaseInfo>, IIssueOrder, IResolveOrder, IOrderVoice, ISync
|
||||
{
|
||||
[Sync] public bool IsAttacking { get; internal set; }
|
||||
public IEnumerable<Armament> Armaments { get { return getArmaments(); } }
|
||||
public readonly AttackBaseInfo Info;
|
||||
|
||||
protected Lazy<IFacing> facing;
|
||||
protected Lazy<Building> building;
|
||||
@@ -49,9 +48,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
readonly Actor self;
|
||||
|
||||
public AttackBase(Actor self, AttackBaseInfo info)
|
||||
: base(info)
|
||||
{
|
||||
this.self = self;
|
||||
Info = info;
|
||||
|
||||
var armaments = Exts.Lazy(() => self.TraitsImplementing<Armament>()
|
||||
.Where(a => info.Armaments.Contains(a.Info.Name)));
|
||||
@@ -65,7 +64,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
protected virtual bool CanAttack(Actor self, Target target)
|
||||
{
|
||||
if (!self.IsInWorld)
|
||||
if (!self.IsInWorld || IsTraitDisabled)
|
||||
return false;
|
||||
|
||||
if (!HasAnyValidWeapons(target))
|
||||
@@ -101,7 +100,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
get
|
||||
{
|
||||
var armament = Armaments.FirstOrDefault(a => a.Weapon.Warheads.Any(w => (w is DamageWarhead)));
|
||||
if (armament == null)
|
||||
if (armament == null || IsTraitDisabled)
|
||||
yield break;
|
||||
|
||||
var negativeDamage = (armament.Weapon.Warheads.FirstOrDefault(w => (w is DamageWarhead)) as DamageWarhead).Damage < 0;
|
||||
@@ -149,6 +148,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public bool HasAnyValidWeapons(Target t)
|
||||
{
|
||||
if (IsTraitDisabled)
|
||||
return false;
|
||||
|
||||
if (Info.AttackRequiresEnteringCell && !positionable.Value.CanEnterCell(t.Actor.Location, null, false))
|
||||
return false;
|
||||
|
||||
@@ -157,14 +159,19 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public WRange GetMaximumRange()
|
||||
{
|
||||
return Armaments.Select(a => a.Weapon.Range).Append(WRange.Zero).Max();
|
||||
if (IsTraitDisabled)
|
||||
return WRange.Zero;
|
||||
|
||||
return Armaments.Where(a => !a.IsTraitDisabled)
|
||||
.Select(a => a.Weapon.Range)
|
||||
.Append(WRange.Zero).Max();
|
||||
}
|
||||
|
||||
public Armament ChooseArmamentForTarget(Target t) { return Armaments.FirstOrDefault(a => a.Weapon.IsValidAgainst(t, self.World, self)); }
|
||||
|
||||
public void AttackTarget(Target target, bool queued, bool allowMove)
|
||||
{
|
||||
if (self.IsDisabled())
|
||||
if (self.IsDisabled() || IsTraitDisabled)
|
||||
return;
|
||||
|
||||
if (!target.IsValidFor(self))
|
||||
|
||||
@@ -123,7 +123,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!Cloaked || self.Owner.IsAlliedWith(viewer))
|
||||
return true;
|
||||
|
||||
return self.World.ActorsWithTrait<DetectCloaked>().Any(a => a.Actor.Owner.IsAlliedWith(viewer)
|
||||
return self.World.ActorsWithTrait<DetectCloaked>().Any(a => !a.Trait.IsTraitDisabled && a.Actor.Owner.IsAlliedWith(viewer)
|
||||
&& Info.CloakTypes.Intersect(a.Trait.Info.CloakTypes).Any()
|
||||
&& (self.CenterPosition - a.Actor.CenterPosition).Length <= WRange.FromCells(a.Trait.Info.Range).Range);
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Actor can reveal Cloak actors in a specified range.")]
|
||||
public class DetectCloakedInfo : ITraitInfo
|
||||
public class DetectCloakedInfo : UpgradableTraitInfo, ITraitInfo
|
||||
{
|
||||
[Desc("Specific cloak classifications I can reveal.")]
|
||||
public readonly string[] CloakTypes = { "Cloak" };
|
||||
@@ -24,13 +24,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public object Create(ActorInitializer init) { return new DetectCloaked(this); }
|
||||
}
|
||||
|
||||
public class DetectCloaked
|
||||
public class DetectCloaked : UpgradableTrait<DetectCloakedInfo>
|
||||
{
|
||||
public readonly DetectCloakedInfo Info;
|
||||
|
||||
public DetectCloaked(DetectCloakedInfo info)
|
||||
{
|
||||
Info = info;
|
||||
}
|
||||
public DetectCloaked(DetectCloakedInfo info) : base(info) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public void ResolveOrder(Actor self, Order order)
|
||||
{
|
||||
if (order.OrderString == "PlaceBuilding" || order.OrderString == "LineBuild")
|
||||
if (order.OrderString == "PlaceBuilding" || order.OrderString == "LineBuild" || order.OrderString == "PlacePlug")
|
||||
{
|
||||
self.World.AddFrameEndTask(w =>
|
||||
{
|
||||
@@ -69,6 +69,27 @@ namespace OpenRA.Mods.Common.Traits
|
||||
playSounds = false;
|
||||
}
|
||||
}
|
||||
else if (order.OrderString == "PlacePlug")
|
||||
{
|
||||
var host = self.World.WorldActor.Trait<BuildingInfluence>().GetBuildingAt(order.TargetLocation);
|
||||
if (host == null)
|
||||
return;
|
||||
|
||||
var plugInfo = unit.Traits.GetOrDefault<PlugInfo>();
|
||||
if (plugInfo == null)
|
||||
return;
|
||||
|
||||
var location = host.Location;
|
||||
var pluggable = host.TraitsImplementing<Pluggable>()
|
||||
.FirstOrDefault(p => location + p.Info.Offset == order.TargetLocation && p.AcceptsPlug(host, plugInfo.Type));
|
||||
|
||||
if (pluggable == null)
|
||||
return;
|
||||
|
||||
pluggable.EnablePlug(host, plugInfo.Type);
|
||||
foreach (var s in buildingInfo.BuildSounds)
|
||||
Sound.PlayToPlayer(order.Player, s, host.CenterPosition);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!self.World.CanPlaceBuilding(order.TargetString, buildingInfo, order.TargetLocation, null)
|
||||
|
||||
24
OpenRA.Mods.Common/Traits/Plug.cs
Normal file
24
OpenRA.Mods.Common/Traits/Plug.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
#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 System.Linq;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public class PlugInfo : TraitInfo<Plug>
|
||||
{
|
||||
[Desc("Plug type (matched against Upgrades in Pluggable)")]
|
||||
public readonly string Type = null;
|
||||
}
|
||||
|
||||
public class Plug { }
|
||||
}
|
||||
79
OpenRA.Mods.Common/Traits/Pluggable.cs
Normal file
79
OpenRA.Mods.Common/Traits/Pluggable.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
#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 System.Linq;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public class PluggableInfo : ITraitInfo, Requires<UpgradeManagerInfo>
|
||||
{
|
||||
[Desc("Footprint cell offset where a plug can be placed.")]
|
||||
public readonly CVec Offset = CVec.Zero;
|
||||
|
||||
[FieldLoader.LoadUsing("LoadUpgrades")]
|
||||
[Desc("Upgrades to grant for each accepted plug type.")]
|
||||
public readonly Dictionary<string, string[]> Upgrades = null;
|
||||
|
||||
static object LoadUpgrades(MiniYaml y)
|
||||
{
|
||||
MiniYaml upgrades;
|
||||
|
||||
if (!y.ToDictionary().TryGetValue("Upgrades", out upgrades))
|
||||
return new Dictionary<string, string[]>();
|
||||
|
||||
return upgrades.Nodes.ToDictionary(
|
||||
kv => kv.Key,
|
||||
kv => FieldLoader.GetValue<string[]>("(value)", kv.Value.Value));
|
||||
}
|
||||
|
||||
public object Create(ActorInitializer init) { return new Pluggable(init.Self, this); }
|
||||
}
|
||||
|
||||
public class Pluggable
|
||||
{
|
||||
public readonly PluggableInfo Info;
|
||||
readonly UpgradeManager upgradeManager;
|
||||
string active;
|
||||
|
||||
public Pluggable(Actor self, PluggableInfo info)
|
||||
{
|
||||
Info = info;
|
||||
upgradeManager = self.Trait<UpgradeManager>();
|
||||
}
|
||||
|
||||
public bool AcceptsPlug(Actor self, string type)
|
||||
{
|
||||
return active == null && Info.Upgrades.ContainsKey(type);
|
||||
}
|
||||
|
||||
public void EnablePlug(Actor self, string type)
|
||||
{
|
||||
string[] upgrades;
|
||||
if (!Info.Upgrades.TryGetValue(type, out upgrades))
|
||||
return;
|
||||
|
||||
foreach (var u in upgrades)
|
||||
upgradeManager.GrantUpgrade(self, u, this);
|
||||
|
||||
active = type;
|
||||
}
|
||||
|
||||
public void DisablePlug(Actor self, string type)
|
||||
{
|
||||
if (type != active)
|
||||
return;
|
||||
|
||||
foreach (var u in Info.Upgrades[type])
|
||||
upgradeManager.RevokeUpgrade(self, u, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Graphics;
|
||||
using OpenRA.Mods.Common.Graphics;
|
||||
using OpenRA.Traits;
|
||||
@@ -32,9 +33,17 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (self.Owner != self.World.LocalPlayer)
|
||||
yield break;
|
||||
|
||||
var range = self.TraitsImplementing<DetectCloaked>()
|
||||
.Where(a => !a.IsTraitDisabled)
|
||||
.Select(a => WRange.FromCells(a.Info.Range))
|
||||
.Append(WRange.Zero).Max();
|
||||
|
||||
if (range == WRange.Zero)
|
||||
yield break;
|
||||
|
||||
yield return new RangeCircleRenderable(
|
||||
self.CenterPosition,
|
||||
WRange.FromCells(self.Info.Traits.Get<DetectCloakedInfo>().Range),
|
||||
range,
|
||||
0,
|
||||
Color.FromArgb(128, Color.LimeGreen),
|
||||
Color.FromArgb(96, Color.Black));
|
||||
|
||||
@@ -27,7 +27,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public IEnumerable<IRenderable> Render(WorldRenderer wr, World w, ActorInfo ai, WPos centerPosition)
|
||||
{
|
||||
var armaments = ai.Traits.WithInterface<ArmamentInfo>();
|
||||
var armaments = ai.Traits.WithInterface<ArmamentInfo>()
|
||||
.Where(a => a.UpgradeMinEnabledLevel == 0);
|
||||
var range = FallbackRange;
|
||||
|
||||
if (armaments.Any())
|
||||
@@ -69,9 +70,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (self.Owner != self.World.LocalPlayer)
|
||||
yield break;
|
||||
|
||||
var range = attack.GetMaximumRange();
|
||||
if (range == WRange.Zero)
|
||||
yield break;
|
||||
|
||||
yield return new RangeCircleRenderable(
|
||||
self.CenterPosition,
|
||||
attack.GetMaximumRange(),
|
||||
range,
|
||||
0,
|
||||
Color.FromArgb(128, Color.Yellow),
|
||||
Color.FromArgb(96, Color.Black));
|
||||
|
||||
@@ -17,7 +17,7 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Renders a decorative animation on units and buildings.")]
|
||||
public class WithIdleOverlayInfo : ITraitInfo, IRenderActorPreviewSpritesInfo, Requires<RenderSpritesInfo>, Requires<IBodyOrientationInfo>
|
||||
public class WithIdleOverlayInfo : UpgradableTraitInfo, ITraitInfo, IRenderActorPreviewSpritesInfo, Requires<RenderSpritesInfo>, Requires<IBodyOrientationInfo>
|
||||
{
|
||||
[Desc("Sequence name to use")]
|
||||
public readonly string Sequence = "idle-overlay";
|
||||
@@ -37,6 +37,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
|
||||
{
|
||||
if (UpgradeMinEnabledLevel > 0)
|
||||
yield break;
|
||||
|
||||
var body = init.Actor.Traits.Get<BodyOrientationInfo>();
|
||||
var facing = init.Contains<FacingInit>() ? init.Get<FacingInit, int>() : 0;
|
||||
var anim = new Animation(init.World, image, () => facing);
|
||||
@@ -48,12 +51,13 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public class WithIdleOverlay : INotifyDamageStateChanged, INotifyBuildComplete, INotifySold, INotifyTransform
|
||||
public class WithIdleOverlay : UpgradableTrait<WithIdleOverlayInfo>, INotifyDamageStateChanged, INotifyBuildComplete, INotifySold, INotifyTransform
|
||||
{
|
||||
Animation overlay;
|
||||
bool buildComplete;
|
||||
|
||||
public WithIdleOverlay(Actor self, WithIdleOverlayInfo info)
|
||||
: base(info)
|
||||
{
|
||||
var rs = self.Trait<RenderSprites>();
|
||||
var body = self.Trait<IBodyOrientation>();
|
||||
@@ -65,7 +69,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
rs.Add("idle_overlay_{0}".F(info.Sequence),
|
||||
new AnimationWithOffset(overlay,
|
||||
() => body.LocalToWorld(info.Offset.Rotate(body.QuantizeOrientation(self, self.Orientation))),
|
||||
() => !buildComplete,
|
||||
() => IsTraitDisabled || !buildComplete,
|
||||
() => info.PauseOnLowPower && disabled.Any(d => d.Disabled),
|
||||
p => WithTurret.ZOffsetFromCenter(self, p, 1)),
|
||||
info.Palette, info.IsPlayerPalette);
|
||||
|
||||
@@ -17,7 +17,7 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Renders the MuzzleSequence from the Armament trait.")]
|
||||
class WithMuzzleFlashInfo : ITraitInfo, Requires<RenderSpritesInfo>, Requires<AttackBaseInfo>, Requires<ArmamentInfo>
|
||||
class WithMuzzleFlashInfo : UpgradableTraitInfo, ITraitInfo, Requires<RenderSpritesInfo>, Requires<AttackBaseInfo>, Requires<ArmamentInfo>
|
||||
{
|
||||
[Desc("Ignore the weapon position, and always draw relative to the center of the actor")]
|
||||
public readonly bool IgnoreOffset = false;
|
||||
@@ -25,13 +25,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public object Create(ActorInitializer init) { return new WithMuzzleFlash(init.Self, this); }
|
||||
}
|
||||
|
||||
class WithMuzzleFlash : INotifyAttack, IRender, ITick
|
||||
class WithMuzzleFlash : UpgradableTrait<WithMuzzleFlashInfo>, INotifyAttack, IRender, ITick
|
||||
{
|
||||
Dictionary<Barrel, bool> visible = new Dictionary<Barrel, bool>();
|
||||
Dictionary<Barrel, AnimationWithOffset> anims = new Dictionary<Barrel, AnimationWithOffset>();
|
||||
Func<int> getFacing;
|
||||
|
||||
public WithMuzzleFlash(Actor self, WithMuzzleFlashInfo info)
|
||||
: base(info)
|
||||
{
|
||||
var render = self.Trait<RenderSprites>();
|
||||
var facing = self.TraitOrDefault<IFacing>();
|
||||
@@ -64,7 +65,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
anims.Add(barrel,
|
||||
new AnimationWithOffset(muzzleFlash,
|
||||
() => info.IgnoreOffset ? WVec.Zero : armClosure.MuzzleOffset(self, barrel),
|
||||
() => !visible[barrel],
|
||||
() => IsTraitDisabled || !visible[barrel],
|
||||
() => false,
|
||||
p => WithTurret.ZOffsetFromCenter(self, p, 2)));
|
||||
}
|
||||
|
||||
@@ -17,7 +17,8 @@ using OpenRA.Traits;
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
[Desc("Renders turrets for units with the Turreted trait.")]
|
||||
public class WithTurretInfo : ITraitInfo, IRenderActorPreviewSpritesInfo, Requires<RenderSpritesInfo>, Requires<TurretedInfo>, Requires<IBodyOrientationInfo>
|
||||
public class WithTurretInfo : UpgradableTraitInfo, ITraitInfo, IRenderActorPreviewSpritesInfo,
|
||||
Requires<RenderSpritesInfo>, Requires<TurretedInfo>, Requires<IBodyOrientationInfo>
|
||||
{
|
||||
[Desc("Sequence name to use")]
|
||||
public readonly string Sequence = "turret";
|
||||
@@ -35,6 +36,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public IEnumerable<IActorPreview> RenderPreviewSprites(ActorPreviewInitializer init, RenderSpritesInfo rs, string image, int facings, PaletteReference p)
|
||||
{
|
||||
if (UpgradeMinEnabledLevel > 0)
|
||||
yield break;
|
||||
|
||||
var body = init.Actor.Traits.Get<BodyOrientationInfo>();
|
||||
var t = init.Actor.Traits.WithInterface<TurretedInfo>()
|
||||
.First(tt => tt.Turret == Turret);
|
||||
@@ -52,9 +56,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
}
|
||||
|
||||
public class WithTurret : ITick
|
||||
public class WithTurret : UpgradableTrait<WithTurretInfo>, ITick
|
||||
{
|
||||
WithTurretInfo info;
|
||||
RenderSprites rs;
|
||||
IBodyOrientation body;
|
||||
AttackBase ab;
|
||||
@@ -63,8 +66,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
Animation anim;
|
||||
|
||||
public WithTurret(Actor self, WithTurretInfo info)
|
||||
: base(info)
|
||||
{
|
||||
this.info = info;
|
||||
rs = self.Trait<RenderSprites>();
|
||||
body = self.Trait<IBodyOrientation>();
|
||||
|
||||
@@ -76,8 +79,8 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
anim = new Animation(self.World, rs.GetImage(self), () => t.TurretFacing);
|
||||
anim.Play(info.Sequence);
|
||||
rs.Add("turret_{0}".F(info.Turret), new AnimationWithOffset(
|
||||
anim, () => TurretOffset(self), null, () => false, p => ZOffsetFromCenter(self, p, 1)));
|
||||
rs.Add("turret_{0}_{1}".F(info.Turret, info.Sequence), new AnimationWithOffset(
|
||||
anim, () => TurretOffset(self), () => IsTraitDisabled, () => false, p => ZOffsetFromCenter(self, p, 1)));
|
||||
|
||||
// Restrict turret facings to match the sprite
|
||||
t.QuantizedFacings = anim.CurrentSequence.Facings;
|
||||
@@ -85,7 +88,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
WVec TurretOffset(Actor self)
|
||||
{
|
||||
if (!info.Recoils)
|
||||
if (!Info.Recoils)
|
||||
return t.Position(self);
|
||||
|
||||
var recoil = arms.Aggregate(WRange.Zero, (a, b) => a + b.Recoil);
|
||||
@@ -97,10 +100,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
public void Tick(Actor self)
|
||||
{
|
||||
if (info.AimSequence == null)
|
||||
if (Info.AimSequence == null)
|
||||
return;
|
||||
|
||||
var sequence = ab.IsAttacking ? info.AimSequence : info.Sequence;
|
||||
var sequence = ab.IsAttacking ? Info.AimSequence : Info.Sequence;
|
||||
anim.ReplaceAnim(sequence);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
{
|
||||
public abstract class SupportPowerInfo : ITraitInfo
|
||||
public abstract class SupportPowerInfo : UpgradableTraitInfo, ITraitInfo
|
||||
{
|
||||
[Desc("Measured in seconds.")]
|
||||
public readonly int ChargeTime = 0;
|
||||
@@ -53,15 +53,14 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public SupportPowerInfo() { OrderName = GetType().Name + "Order"; }
|
||||
}
|
||||
|
||||
public class SupportPower
|
||||
public class SupportPower : UpgradableTrait<SupportPowerInfo>
|
||||
{
|
||||
public readonly Actor Self;
|
||||
public readonly SupportPowerInfo Info;
|
||||
protected RadarPing ping;
|
||||
|
||||
public SupportPower(Actor self, SupportPowerInfo info)
|
||||
: base(info)
|
||||
{
|
||||
Info = info;
|
||||
Self = self;
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!Powers.TryGetValue(key, out sp))
|
||||
return;
|
||||
|
||||
sp.Disabled = false;
|
||||
sp.PrerequisitesAvailable(true);
|
||||
}
|
||||
|
||||
public void PrerequisitesUnavailable(string key)
|
||||
@@ -141,7 +141,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
if (!Powers.TryGetValue(key, out sp))
|
||||
return;
|
||||
|
||||
sp.Disabled = true;
|
||||
sp.PrerequisitesAvailable(false);
|
||||
sp.RemainingTime = sp.TotalTime;
|
||||
}
|
||||
|
||||
@@ -158,17 +158,25 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public int RemainingTime;
|
||||
public int TotalTime;
|
||||
public bool Active { get; private set; }
|
||||
public bool Disabled { get; set; }
|
||||
public bool Disabled { get { return !prereqsAvailable || !upgradeAvailable; } }
|
||||
|
||||
public SupportPowerInfo Info { get { return Instances.Select(i => i.Info).FirstOrDefault(); } }
|
||||
public bool Ready { get { return Active && RemainingTime == 0; } }
|
||||
|
||||
bool upgradeAvailable;
|
||||
bool prereqsAvailable = true;
|
||||
|
||||
public SupportPowerInstance(string key, SupportPowerManager manager)
|
||||
{
|
||||
this.manager = manager;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public void PrerequisitesAvailable(bool available)
|
||||
{
|
||||
prereqsAvailable = available;
|
||||
}
|
||||
|
||||
static bool InstanceDisabled(SupportPower sp)
|
||||
{
|
||||
return sp.Self.TraitsImplementing<IDisable>().Any(d => d.Disabled);
|
||||
@@ -178,6 +186,10 @@ namespace OpenRA.Mods.Common.Traits
|
||||
bool notifiedReady;
|
||||
public void Tick()
|
||||
{
|
||||
upgradeAvailable = Instances.Any(i => !i.IsTraitDisabled);
|
||||
if (!upgradeAvailable)
|
||||
RemainingTime = TotalTime;
|
||||
|
||||
Active = !Disabled && Instances.Any(i => !i.Self.IsDisabled());
|
||||
if (!Active)
|
||||
return;
|
||||
@@ -226,7 +238,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
notifiedCharging = notifiedReady = false;
|
||||
|
||||
if (Info.OneShot)
|
||||
Disabled = true;
|
||||
PrerequisitesAvailable(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user