From 0084566478bc70bbf826905e43625dfc6db666a0 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 9 Mar 2011 00:07:57 +1300 Subject: [PATCH] Standalone radar widget for new sidebar --- OpenRA.Mods.RA/OpenRA.Mods.RA.csproj | 1 + OpenRA.Mods.RA/Widgets/RadarWidget.cs | 214 ++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100755 OpenRA.Mods.RA/Widgets/RadarWidget.cs diff --git a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj index 82a67c82db..8eab689ffd 100644 --- a/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj +++ b/OpenRA.Mods.RA/OpenRA.Mods.RA.csproj @@ -329,6 +329,7 @@ + diff --git a/OpenRA.Mods.RA/Widgets/RadarWidget.cs b/OpenRA.Mods.RA/Widgets/RadarWidget.cs new file mode 100755 index 0000000000..d708997b75 --- /dev/null +++ b/OpenRA.Mods.RA/Widgets/RadarWidget.cs @@ -0,0 +1,214 @@ +#region Copyright & License Information +/* + * Copyright 2007-2011 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.Drawing; +using System.Linq; +using OpenRA.Graphics; +using OpenRA.Traits; +using OpenRA.Widgets; + +namespace OpenRA.Mods.RA.Widgets +{ + public class RadarWidget : Widget + { + public string WorldInteractionController = null; + public int AnimationLength = 5; + public string RadarOnlineSound = null; + public string RadarOfflineSound = null; + float radarMinimapHeight; + int AnimationFrame = 0; + bool hasRadar = false; + + float previewScale = 0; + RectangleF mapRect = Rectangle.Empty; + int2 previewOrigin; + + Sprite terrainSprite; + Sprite customTerrainSprite; + Sprite actorSprite; + Sprite shroudSprite; + + readonly World world; + [ObjectCreator.UseCtor] + public RadarWidget( [ObjectCreator.Param] World world ) + { + this.world = world; + } + + public override void Initialize() + { + base.Initialize(); + + var size = Math.Max(world.Map.Bounds.Width, world.Map.Bounds.Height); + previewScale = Math.Min(RenderBounds.Width * 1f / world.Map.Bounds.Width, RenderBounds.Height * 1f / world.Map.Bounds.Height); + previewOrigin = new int2(RenderOrigin.X, RenderOrigin.Y + (int)(previewScale * (size - world.Map.Bounds.Height)/2)); + mapRect = new RectangleF(previewOrigin.X, previewOrigin.Y, (int)(world.Map.Bounds.Width * previewScale), (int)(world.Map.Bounds.Height * previewScale)); + + // Only needs to be done once + var terrainBitmap = Minimap.TerrainBitmap(world.Map); + var r = new Rectangle( 0, 0, world.Map.Bounds.Width, world.Map.Bounds.Height ); + var s = new Size( terrainBitmap.Width, terrainBitmap.Height ); + terrainSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha); + terrainSprite.sheet.Texture.SetData(terrainBitmap); + + // Data is set in Tick() + customTerrainSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha); + actorSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha); + shroudSprite = new Sprite(new Sheet(s), r, TextureChannel.Alpha); + } + + public override string GetCursor(int2 pos) + { + if (world == null || !hasRadar) + return null; + + var loc = MinimapPixelToCell(pos); + + var mi = new MouseInput + { + Location = loc, + Button = MouseButton.Right, + Modifiers = Game.GetModifierKeys() + }; + + var cursor = world.OrderGenerator.GetCursor( world, loc, mi ); + if (cursor == null) + return "default"; + + return CursorProvider.HasCursorSequence(cursor+"-minimap") ? cursor+"-minimap" : cursor; + } + + // TODO: RadarWidget doesn't support delegate methods for mouse input + public override bool HandleMouseInput(MouseInput mi) + { + if (!hasRadar || Animating) return false; + + if (!mapRect.Contains(mi.Location)) + return false; + + var loc = MinimapPixelToCell(mi.Location); + if ((mi.Event == MouseInputEvent.Down || mi.Event == MouseInputEvent.Move) && mi.Button == MouseButton.Left) + Game.viewport.Center(loc); + + if (mi.Event == MouseInputEvent.Down && mi.Button == MouseButton.Right) + { + // fake a mousedown/mouseup here + var fakemi = new MouseInput + { + Event = MouseInputEvent.Down, + Button = MouseButton.Right, + Modifiers = mi.Modifiers, + Location = (loc * Game.CellSize - Game.viewport.Location).ToInt2() + }; + + if (WorldInteractionController != null) + { + var controller = Widget.RootWidget.GetWidget(WorldInteractionController); + controller.HandleMouseInput(fakemi); + fakemi.Event = MouseInputEvent.Up; + controller.HandleMouseInput(fakemi); + } + } + + return true; + } + + public override Rectangle EventBounds + { + get { return new Rectangle((int)mapRect.X, (int)mapRect.Y, (int)mapRect.Width, (int)mapRect.Height);} + } + + public override void DrawInner() + { + if( world == null || world.LocalPlayer == 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); + Game.Renderer.RgbaSpriteRenderer.DrawSprite(terrainSprite, o, s); + Game.Renderer.RgbaSpriteRenderer.DrawSprite(customTerrainSprite, o, s); + Game.Renderer.RgbaSpriteRenderer.DrawSprite(actorSprite, o, s); + Game.Renderer.RgbaSpriteRenderer.DrawSprite(shroudSprite, o, s); + + // Draw viewport rect + if (hasRadar && !Animating) + { + var tl = CellToMinimapPixel(new int2((int)(Game.viewport.Location.X/Game.CellSize), (int)(Game.viewport.Location.Y/Game.CellSize))); + var br = CellToMinimapPixel(new int2((int)((Game.viewport.Location.X + Game.viewport.Width)/Game.CellSize), (int)((Game.viewport.Location.Y + Game.viewport.Height)/Game.CellSize))); + var tr = new int2(br.X, tl.Y); + var bl = new int2(tl.X, br.Y); + Game.Renderer.EnableScissor((int)mapRect.Left, (int)mapRect.Top, (int)mapRect.Width, (int)mapRect.Height); + Game.Renderer.LineRenderer.DrawLine(Game.viewport.Location + tl, Game.viewport.Location + tr, Color.White, Color.White); + Game.Renderer.LineRenderer.DrawLine(Game.viewport.Location + tr, Game.viewport.Location + br, Color.White, Color.White); + Game.Renderer.LineRenderer.DrawLine(Game.viewport.Location + br, Game.viewport.Location + bl, Color.White, Color.White); + Game.Renderer.LineRenderer.DrawLine(Game.viewport.Location + bl, Game.viewport.Location + tl, Color.White, Color.White); + Game.Renderer.DisableScissor(); + } + } + + bool Animating = false; + int updateTicks = 0; + public override void Tick() + { + var hasRadarNew = world.Queries.OwnedBy[world.LocalPlayer] + .WithTrait() + .Any(a => a.Trait.IsActive); + + if (hasRadarNew != hasRadar) + { + Animating = true; + Sound.Play(hasRadarNew ? RadarOnlineSound : RadarOfflineSound); + } + hasRadar = hasRadarNew; + + // Build the radar image + if (hasRadar) + { + --updateTicks; + if (updateTicks <= 0) + { + updateTicks = 12; + customTerrainSprite.sheet.Texture.SetData(Minimap.CustomTerrainBitmap(world)); + } + + if (updateTicks == 8) + actorSprite.sheet.Texture.SetData(Minimap.ActorsBitmap(world)); + + if (updateTicks == 4) + shroudSprite.sheet.Texture.SetData(Minimap.ShroudBitmap(world)); + } + + if (!Animating) + return; + + // Increment frame + if (hasRadar) + AnimationFrame++; + else + AnimationFrame--; + + // Minimap height + radarMinimapHeight = float2.Lerp(0, 1, AnimationFrame*1.0f / AnimationLength); + + // Animation is complete + if (AnimationFrame == (hasRadar ? AnimationLength : 0)) + Animating = false; + } + + int2 CellToMinimapPixel(int2 p) + { + return new int2((int)(mapRect.X + previewScale*(p.X - world.Map.Bounds.Left)), (int)(mapRect.Y + previewScale*(p.Y - world.Map.Bounds.Top))); + } + + int2 MinimapPixelToCell(int2 p) + { + return new int2(world.Map.Bounds.Left + (int)((p.X - mapRect.X)/previewScale), world.Map.Bounds.Top + (int)((p.Y - mapRect.Y)/previewScale)); + } + } +}