Merge pull request #4682 from ScottNZ/beacon

closes #2320
closes #3767
closes #3810
This commit is contained in:
Matthias Mailänder
2014-02-23 09:55:23 +01:00
32 changed files with 432 additions and 42 deletions

View File

@@ -0,0 +1,64 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Effects;
using OpenRA.Graphics;
namespace OpenRA.Mods.RA.Effects
{
public class Beacon : IEffect
{
readonly Player owner;
readonly WPos position;
readonly string palettePrefix;
readonly Animation arrow = new Animation("beacon");
readonly Animation circles = new Animation("beacon");
static readonly int maxArrowHeight = 512;
int arrowHeight = maxArrowHeight;
int arrowSpeed = 50;
public Beacon(Player owner, WPos position, int duration, string palettePrefix)
{
this.owner = owner;
this.position = position;
this.palettePrefix = palettePrefix;
arrow.Play("arrow");
circles.Play("circles");
owner.World.Add(new DelayedAction(duration, () => owner.World.Remove(this)));
}
public void Tick(World world)
{
arrowHeight += arrowSpeed;
var clamped = arrowHeight.Clamp(0, maxArrowHeight);
if (arrowHeight != clamped)
{
arrowHeight = clamped;
arrowSpeed *= -1;
}
arrow.Tick();
circles.Tick();
}
public IEnumerable<IRenderable> Render(WorldRenderer r)
{
if (!owner.IsAlliedWith(owner.World.RenderPlayer))
return SpriteRenderable.None;
var palette = r.Palette(palettePrefix + owner.InternalName);
return circles.Render(position, palette).Concat(arrow.Render(position + new WVec(0, 0, arrowHeight), palette));
}
}
}

View File

@@ -123,6 +123,8 @@
<Compile Include="Air\Aircraft.cs" />
<Compile Include="Air\AttackHeli.cs" />
<Compile Include="Air\AttackPlane.cs" />
<Compile Include="Effects\Beacon.cs" />
<Compile Include="Player\PlaceBeacon.cs" />
<Compile Include="MenuPaletteEffect.cs" />
<Compile Include="Crates\UnitUpgradeCrateAction.cs" />
<Compile Include="EjectOnDeath.cs" />
@@ -306,6 +308,7 @@
<Compile Include="Player\ClassicProductionQueue.cs" />
<Compile Include="Player\PlaceBuilding.cs" />
<Compile Include="Player\ProductionQueue.cs" />
<Compile Include="World\RadarPings.cs" />
<Compile Include="Player\TechTree.cs" />
<Compile Include="PlayerExts.cs" />
<Compile Include="PrimaryBuilding.cs" />

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System.Drawing;
using OpenRA.Mods.RA.Buildings;
using OpenRA.Traits;
@@ -16,18 +17,25 @@ namespace OpenRA.Mods.RA
public class BaseAttackNotifierInfo : ITraitInfo
{
public readonly int NotifyInterval = 30; // seconds
public readonly Color RadarPingColor = Color.Red;
public readonly int RadarPingDuration = 10 * 25;
public object Create(ActorInitializer init) { return new BaseAttackNotifier(this); }
public object Create(ActorInitializer init) { return new BaseAttackNotifier(init.self, this); }
}
public class BaseAttackNotifier : INotifyDamage
{
BaseAttackNotifierInfo info;
readonly RadarPings radarPings;
readonly BaseAttackNotifierInfo info;
public int lastAttackTime = -1;
public CPos lastAttackLocation;
int lastAttackTime;
public BaseAttackNotifier(BaseAttackNotifierInfo info) { this.info = info; }
public BaseAttackNotifier(Actor self, BaseAttackNotifierInfo info)
{
radarPings = self.World.WorldActor.TraitOrDefault<RadarPings>();
this.info = info;
lastAttackTime = -info.NotifyInterval * 25;
}
public void Damaged(Actor self, AttackInfo e)
{
@@ -45,9 +53,13 @@ namespace OpenRA.Mods.RA
return;
if (self.World.FrameNumber - lastAttackTime > info.NotifyInterval * 25)
{
Sound.PlayNotification(self.Owner, "Speech", "BaseAttack", self.Owner.Country.Race);
lastAttackLocation = self.CenterPosition.ToCPos();
if (radarPings != null)
radarPings.Add(() => self.Owner == self.World.LocalPlayer, self.CenterPosition, info.RadarPingColor, info.RadarPingDuration);
}
lastAttackTime = self.World.FrameNumber;
}
}

View File

@@ -8,6 +8,7 @@
*/
#endregion
using System.Drawing;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -15,18 +16,25 @@ namespace OpenRA.Mods.RA
public class HarvesterAttackNotifierInfo : ITraitInfo
{
public readonly int NotifyInterval = 30; // seconds
public readonly Color RadarPingColor = Color.Red;
public readonly int RadarPingDuration = 10 * 25;
public object Create(ActorInitializer init) { return new HarvesterAttackNotifier(this); }
public object Create(ActorInitializer init) { return new HarvesterAttackNotifier(init.self, this); }
}
public class HarvesterAttackNotifier : INotifyDamage
{
HarvesterAttackNotifierInfo info;
readonly RadarPings radarPings;
readonly HarvesterAttackNotifierInfo info;
public int lastAttackTime = -1;
public CPos lastAttackLocation;
int lastAttackTime;
public HarvesterAttackNotifier(HarvesterAttackNotifierInfo info) { this.info = info; }
public HarvesterAttackNotifier(Actor self, HarvesterAttackNotifierInfo info)
{
radarPings = self.World.WorldActor.TraitOrDefault<RadarPings>();
this.info = info;
lastAttackTime = -info.NotifyInterval * 25;
}
public void Damaged(Actor self, AttackInfo e)
{
@@ -39,9 +47,13 @@ namespace OpenRA.Mods.RA
return;
if (self.World.FrameNumber - lastAttackTime > info.NotifyInterval * 25)
{
Sound.PlayNotification(self.Owner, "Speech", "HarvesterAttack", self.Owner.Country.Race);
lastAttackLocation = self.CenterPosition.ToCPos();
if (radarPings != null)
radarPings.Add(() => self.Owner == self.World.LocalPlayer, self.CenterPosition, info.RadarPingColor, info.RadarPingDuration);
}
lastAttackTime = self.World.FrameNumber;
}
}

View File

@@ -0,0 +1,73 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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.Mods.RA.Effects;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class PlaceBeaconInfo : ITraitInfo
{
public readonly int Duration = 30 * 25;
public readonly string NotificationType = "Sounds";
public readonly string Notification = "Beacon";
public readonly string PalettePrefix = "player";
public object Create(ActorInitializer init) { return new PlaceBeacon(init.self, this); }
}
public class PlaceBeacon : IResolveOrder
{
readonly PlaceBeaconInfo info;
readonly RadarPings radarPings;
Beacon playerBeacon;
RadarPing playerRadarPing;
public PlaceBeacon(Actor self, PlaceBeaconInfo info)
{
radarPings = self.World.WorldActor.TraitOrDefault<RadarPings>();
this.info = info;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString != "PlaceBeacon")
return;
var pos = order.TargetLocation.CenterPosition;
self.World.AddFrameEndTask(w =>
{
if (playerBeacon != null)
self.World.Remove(playerBeacon);
playerBeacon = new Beacon(self.Owner, pos, info.Duration, info.PalettePrefix);
self.World.Add(playerBeacon);
if (self.Owner.IsAlliedWith(self.World.RenderPlayer))
Sound.PlayNotification(null, info.NotificationType, info.Notification,
self.World.RenderPlayer != null ? self.World.RenderPlayer.Country.Race : null);
if (radarPings != null)
{
if (playerRadarPing != null)
radarPings.Remove(playerRadarPing);
playerRadarPing = radarPings.Add(
() => self.Owner.IsAlliedWith(self.World.RenderPlayer),
pos,
self.Owner.Color.RGB,
info.Duration);
}
});
}
}
}

View File

@@ -30,6 +30,11 @@ namespace OpenRA.Mods.RA
public readonly bool DisplayTimer = false;
public readonly bool DisplayBeacon = false;
public readonly int BeaconDuration = 10 * 25;
public readonly string BeaconPalettePrefix = "player";
public readonly bool DisplayRadarPing = false;
public readonly string OrderName;
public abstract object Create(ActorInitializer init);

View File

@@ -10,7 +10,9 @@
using System.Collections.Generic;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Mods.RA.Effects;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
@@ -23,13 +25,16 @@ namespace OpenRA.Mods.RA
public class SupportPowerManager : ITick, IResolveOrder
{
public readonly Actor self;
public Dictionary<string, SupportPowerInstance> Powers = new Dictionary<string, SupportPowerInstance>();
public readonly Dictionary<string, SupportPowerInstance> Powers = new Dictionary<string, SupportPowerInstance>();
public readonly DeveloperMode DevMode;
public readonly Lazy<RadarPings> RadarPings;
public readonly DeveloperMode devMode;
public SupportPowerManager(ActorInitializer init)
{
self = init.self;
devMode = init.self.Trait<DeveloperMode>();
DevMode = init.self.Trait<DeveloperMode>();
RadarPings = Lazy.New(() => init.world.WorldActor.TraitOrDefault<RadarPings>());
init.world.ActorAdded += ActorAdded;
init.world.ActorRemoved += ActorRemoved;
@@ -148,7 +153,7 @@ namespace OpenRA.Mods.RA
if (Active)
{
var power = Instances.First();
if (Manager.devMode.FastCharge && RemainingTime > 25)
if (Manager.DevMode.FastCharge && RemainingTime > 25)
RemainingTime = 25;
if (RemainingTime > 0) --RemainingTime;
@@ -187,6 +192,20 @@ namespace OpenRA.Mods.RA
RemainingTime = TotalTime;
notifiedCharging = notifiedReady = false;
if (power.Info.DisplayBeacon)
power.self.World.Add(new Beacon(
order.Player,
order.TargetLocation.CenterPosition,
power.Info.BeaconDuration,
power.Info.BeaconPalettePrefix));
if (power.Info.DisplayRadarPing && Manager.RadarPings != null)
Manager.RadarPings.Value.Add(
() => order.Player.IsAlliedWith(power.self.World.RenderPlayer),
order.TargetLocation.CenterPosition,
order.Player.Color.RGB,
power.Info.BeaconDuration);
if (Info.OneShot)
Disabled = true;
}

View File

@@ -255,6 +255,8 @@ namespace OpenRA.Mods.RA.Widgets.Logic
{ "SelectAllUnitsKey", "Select all units on screen" },
{ "SelectUnitsByTypeKey", "Select units by type" },
{ "PlaceBeaconKey", "Place beacon" },
{ "PauseKey", "Pause / Unpause" },
{ "SellKey", "Sell mode" },
{ "PowerDownKey", "Power-down mode" },

View File

@@ -10,6 +10,7 @@
using System;
using System.Drawing;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Widgets;
@@ -22,8 +23,8 @@ namespace OpenRA.Mods.RA.Widgets
public string RadarOnlineSound = null;
public string RadarOfflineSound = null;
public Func<bool> IsEnabled = () => true;
public Action AfterOpen = () => {};
public Action AfterClose = () => {};
public Action AfterOpen = () => { };
public Action AfterClose = () => { };
float radarMinimapHeight;
int frame;
@@ -43,11 +44,14 @@ namespace OpenRA.Mods.RA.Widgets
readonly World world;
readonly WorldRenderer worldRenderer;
readonly RadarPings radarPings;
[ObjectCreator.UseCtor]
public RadarWidget(World world, WorldRenderer worldRenderer)
{
this.world = world;
this.worldRenderer = worldRenderer;
radarPings = world.WorldActor.TraitOrDefault<RadarPings>();
}
public override void Initialize(WidgetArgs args)
@@ -60,8 +64,8 @@ namespace OpenRA.Mods.RA.Widgets
var rb = RenderBounds;
previewScale = Math.Min(rb.Width * 1f / width, rb.Height * 1f / height);
previewOrigin = new int2((int)(previewScale*(size - width)/2), (int)(previewScale*(size - height)/2));
mapRect = new Rectangle(previewOrigin.X, previewOrigin.Y, (int)(previewScale*width), (int)(previewScale*height));
previewOrigin = new int2((int)(previewScale * (size - width) / 2), (int)(previewScale * (size - height) / 2));
mapRect = new Rectangle(previewOrigin.X, previewOrigin.Y, (int)(previewScale * width), (int)(previewScale * height));
// Only needs to be done once
var terrainBitmap = Minimap.TerrainBitmap(world.Map);
@@ -95,7 +99,7 @@ namespace OpenRA.Mods.RA.Widgets
if (cursor == null)
return "default";
return CursorProvider.HasCursorSequence(cursor+"-minimap") ? cursor+"-minimap" : cursor;
return CursorProvider.HasCursorSequence(cursor + "-minimap") ? cursor + "-minimap" : cursor;
}
public override bool HandleMouseInput(MouseInput mi)
@@ -140,8 +144,8 @@ namespace OpenRA.Mods.RA.Widgets
if (world == null)
return;
var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight)/2);
var s = new float2(mapRect.Size.Width, mapRect.Size.Height*radarMinimapHeight);
var o = new float2(mapRect.Location.X, mapRect.Location.Y + world.Map.Bounds.Height * previewScale * (1 - radarMinimapHeight) / 2);
var s = new float2(mapRect.Size.Width, mapRect.Size.Height * radarMinimapHeight);
var rsr = Game.Renderer.RgbaSpriteRenderer;
rsr.DrawSprite(terrainSprite, o, s);
@@ -156,11 +160,34 @@ namespace OpenRA.Mods.RA.Widgets
var br = CellToMinimapPixel(worldRenderer.Position(worldRenderer.Viewport.BottomRight).ToCPos());
Game.Renderer.EnableScissor(mapRect);
DrawRadarPings();
Game.Renderer.LineRenderer.DrawRect(tl, br, Color.White);
Game.Renderer.DisableScissor();
}
}
void DrawRadarPings()
{
if (radarPings == null)
return;
var lr = Game.Renderer.LineRenderer;
var oldWidth = lr.LineWidth;
lr.LineWidth = 2;
foreach (var radarPing in radarPings.Pings.Where(e => e.IsVisible()))
{
var c = radarPing.Color;
var points = radarPing.Points(CellToMinimapPixel(radarPing.Position.ToCPos())).ToArray();
lr.DrawLine(points[0], points[1], c, c);
lr.DrawLine(points[1], points[2], c, c);
lr.DrawLine(points[2], points[0], c, c);
}
lr.LineWidth = oldWidth;
}
public override void Tick()
{
// Update the radar animation even when its closed

View File

@@ -22,12 +22,14 @@ namespace OpenRA.Mods.RA.Widgets
{
readonly World world;
readonly WorldRenderer worldRenderer;
readonly RadarPings radarPings;
[ObjectCreator.UseCtor]
public WorldCommandWidget(World world, WorldRenderer worldRenderer)
{
this.world = world;
this.worldRenderer = worldRenderer;
radarPings = world.WorldActor.TraitOrDefault<RadarPings>();
}
public override string GetCursor(int2 pos) { return null; }
@@ -63,6 +65,9 @@ namespace OpenRA.Mods.RA.Widgets
if (key == ks.ToggleStatusBarsKey)
return ToggleStatusBars();
if (key == ks.PlaceBeaconKey)
return PerformPlaceBeacon();
// Put all functions that aren't unit-specific before this line!
if (!world.Selection.Actors.Any())
return false;
@@ -184,6 +189,15 @@ namespace OpenRA.Mods.RA.Widgets
return true;
}
bool PerformPlaceBeacon()
{
if (world.LocalPlayer == null)
return true;
world.OrderGenerator = new GenericSelectTarget(world.LocalPlayer.PlayerActor, "PlaceBeacon", "ability");
return true;
}
bool CycleBases()
{
var bases = world.ActorsWithTrait<BaseBuilding>()
@@ -233,17 +247,10 @@ namespace OpenRA.Mods.RA.Widgets
bool ToLastEvent()
{
if (world.LocalPlayer == null)
if (radarPings == null || radarPings.LastPingPosition == null)
return true;
var eventNotifier = world.LocalPlayer.PlayerActor.TraitOrDefault<BaseAttackNotifier>();
if (eventNotifier == null)
return true;
if (eventNotifier.lastAttackTime < 0)
return true;
worldRenderer.Viewport.Center(eventNotifier.lastAttackLocation.CenterPosition);
worldRenderer.Viewport.Center(radarPings.LastPingPosition.Value);
return true;
}

View File

@@ -0,0 +1,113 @@
#region Copyright & License Information
/*
* Copyright 2007-2014 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;
using System.Collections.Generic;
using System.Drawing;
using OpenRA.Traits;
namespace OpenRA.Mods.RA
{
public class RadarPingsInfo : ITraitInfo
{
public readonly int FromRadius = 200;
public readonly int ToRadius = 15;
public readonly int ShrinkSpeed = 4;
public readonly float RotationSpeed = 0.12f;
public object Create(ActorInitializer init) { return new RadarPings(this); }
}
public class RadarPings : ITick
{
public readonly List<RadarPing> Pings = new List<RadarPing>();
readonly RadarPingsInfo info;
public WPos? LastPingPosition;
public RadarPings(RadarPingsInfo info)
{
this.info = info;
}
public void Tick(Actor self)
{
foreach (var ping in Pings.ToArray())
if (!ping.Tick())
Pings.Remove(ping);
}
public RadarPing Add(Func<bool> isVisible, WPos position, Color color, int duration)
{
var ping = new RadarPing(isVisible, position, color, duration,
info.FromRadius, info.ToRadius, info.ShrinkSpeed, info.RotationSpeed);
if (ping.IsVisible())
LastPingPosition = ping.Position;
Pings.Add(ping);
return ping;
}
public void Remove(RadarPing ping)
{
Pings.Remove(ping);
}
}
public class RadarPing
{
public Func<bool> IsVisible;
public WPos Position;
public Color Color;
public int Duration;
public int FromRadius;
public int ToRadius;
public int ShrinkSpeed;
public float RotationSpeed;
int radius;
float angle;
int tick;
public RadarPing(Func<bool> isVisible, WPos position, Color color, int duration,
int fromRadius, int toRadius, int shrinkSpeed, float rotationSpeed)
{
IsVisible = isVisible;
Position = position;
Color = color;
Duration = duration;
FromRadius = fromRadius;
ToRadius = toRadius;
ShrinkSpeed = shrinkSpeed;
RotationSpeed = rotationSpeed;
radius = fromRadius;
}
public bool Tick()
{
if (++tick == Duration)
return false;
radius = Math.Max(radius - ShrinkSpeed, ToRadius);
angle -= RotationSpeed;
return true;
}
public IEnumerable<float2> Points(float2 center)
{
yield return center + radius * float2.FromAngle(angle);
yield return center + radius * float2.FromAngle((float)(angle + 2 * Math.PI / 3));
yield return center + radius * float2.FromAngle((float)(angle + 4 * Math.PI / 3));
}
}
}