diff --git a/OpenRa.Game/Chrome.cs b/OpenRa.Game/Chrome.cs index 961bd2d743..318120d691 100644 --- a/OpenRa.Game/Chrome.cs +++ b/OpenRa.Game/Chrome.cs @@ -15,20 +15,19 @@ namespace OpenRa.Game { readonly Renderer renderer; readonly LineRenderer lineRenderer; - readonly Sheet chromeTexture; readonly SpriteRenderer rgbaRenderer; - readonly Sprite[] specialBinSprites; - readonly Sprite moneyBinSprite; - readonly Sprite tooltipSprite; - readonly Sprite powerIndicatorSprite; - readonly Sprite powerLevelTopSprite; - readonly Sprite powerLevelBottomSprite; + readonly SpriteRenderer shpRenderer; - readonly Animation repairButton; - readonly Animation sellButton; - readonly Animation pwrdownButton; - readonly Animation optionsButton; + string chromeCollection; + string radarCollection; + string paletteCollection; + string digitCollection; + // Special power bin + readonly Dictionary spsprites; + + // Options menu (to be refactored) + bool optionsPressed = false; readonly Sprite optionsTop; readonly Sprite optionsBottom; readonly Sprite optionsLeft; @@ -38,65 +37,57 @@ namespace OpenRa.Game readonly Sprite optionsBottomLeft; readonly Sprite optionsBottomRight; readonly Sprite optionsBackground; - - readonly SpriteRenderer shpRenderer; + + // Buttons + readonly Animation repairButton; + readonly Animation sellButton; + readonly Animation pwrdownButton; + readonly Animation optionsButton; + + // Build Palette tabs + string currentTab = "Building"; + bool paletteOpen = false; + static string[] groups = new string[] { "Building", "Defense", "Infantry", "Vehicle", "Plane", "Ship" }; + readonly Dictionary tabImageNames; + readonly Dictionary tabSprites; + + // Build Palette + const int paletteColumns = 3; + const int paletteRows = 5; + static float2 paletteOpenOrigin = new float2(Game.viewport.Width - 215, 280); + static float2 paletteClosedOrigin = new float2(Game.viewport.Width - 16, 280); + static float2 paletteOrigin = paletteClosedOrigin; + const int paletteAnimationLength = 7; + int paletteAnimationFrame = 0; + bool paletteAnimating = false; + readonly List>> buttons = new List>>(); readonly Animation cantBuild; readonly Animation ready; readonly Animation clock; - - readonly List>> buttons = new List>>(); - readonly List digitSprites; - readonly Dictionary tabSprites; - readonly Dictionary spsprites; - readonly Sprite[] shimSprites; - readonly Sprite blank; - - readonly int paletteColumns; - readonly int2 paletteOrigin; - bool hadRadar = false; - bool optionsPressed = false; - const int MinRows = 4; - - string currentTab = "Building"; - static string[] groups = new string[] { "Building", "Defense", "Infantry", "Vehicle", "Plane", "Ship" }; - readonly Dictionary sprites; - const int NumClockFrames = 54; + + // Radar + static float2 radarOpenOrigin = new float2(Game.viewport.Width - 215, 29); + static float2 radarClosedOrigin = new float2(Game.viewport.Width - 215, -166); + static float2 radarOrigin = radarClosedOrigin; + float radarMinimapHeight; + const int radarSlideAnimationLength = 15; + const int radarActivateAnimationLength = 5; + int radarAnimationFrame = 0; + bool radarAnimating = false; + bool hasRadar = false; + + // Power bar + static float2 powerOrigin = new float2(42, 205); // Relative to radarOrigin + static Size powerSize = new Size(138,5); public Chrome(Renderer r) - { - // Positioning of chrome elements - // Build palette - paletteColumns = 4; - paletteOrigin = new int2(Game.viewport.Width - paletteColumns * 64 - 9 - 20, 282); - + { this.renderer = r; - chromeTexture = new Sheet(renderer, "specialbin.png"); rgbaRenderer = new SpriteRenderer(renderer, true, renderer.RgbaSpriteShader); lineRenderer = new LineRenderer(renderer); shpRenderer = new SpriteRenderer(renderer, true); - specialBinSprites = new [] - { - new Sprite(chromeTexture, new Rectangle(0, 0, 32, 51), TextureChannel.Alpha), - new Sprite(chromeTexture, new Rectangle(0, 51, 32, 51 /*144*/), TextureChannel.Alpha), - new Sprite(chromeTexture, new Rectangle(0, 192-39, 32, 39 ), TextureChannel.Alpha), - }; - moneyBinSprite = new Sprite(chromeTexture, new Rectangle(512 - 320, 0, 320, 32), TextureChannel.Alpha); - tooltipSprite = new Sprite(chromeTexture, new Rectangle(0, 288, 272, 136), TextureChannel.Alpha); - - var powerIndicator = new Animation("power"); - powerIndicator.PlayRepeating("power-level-indicator"); - powerIndicatorSprite = powerIndicator.Image; - - var powerTop = new Animation("powerbar"); - powerTop.PlayRepeating("powerbar-top"); - powerLevelTopSprite = powerTop.Image; - - var powerBottom = new Animation("powerbar"); - powerBottom.PlayRepeating("powerbar-bottom"); - powerLevelBottomSprite = powerBottom.Image; - repairButton = new Animation("repair"); repairButton.PlayRepeating("normal"); @@ -119,9 +110,7 @@ namespace OpenRa.Game optionsBottomRight = SpriteSheetBuilder.LoadAllSprites("dd-crnr")[3]; optionsBackground = SpriteSheetBuilder.LoadAllSprites("dd-bkgnd")[Game.CosmeticRandom.Next(4)]; - blank = SheetBuilder.Add(new Size(64, 48), 16); - - sprites = groups + tabSprites = groups .SelectMany(g => Rules.Categories[g]) .Where(u => Rules.UnitInfo[u].TechLevel != -1) .ToDictionary( @@ -132,59 +121,52 @@ namespace OpenRa.Game .ToDictionary( u => u.Key, u => SpriteSheetBuilder.LoadAllSprites(u.Value.Image)[0]); - - tabSprites = groups.Select( + + tabImageNames = groups.Select( (g, i) => Pair.New(g, OpenRa.Game.Graphics.Util.MakeArray(3, - n => new Sprite(chromeTexture, - new Rectangle(512 - (n + 1) * 27, 64 + i * 40, 27, 40), - TextureChannel.Alpha)))) + n => i.ToString()))) .ToDictionary(a => a.First, a => a.Second); cantBuild = new Animation("clock"); cantBuild.PlayFetchIndex("idle", () => 0); - digitSprites = Graphics.Util.MakeArray(10, a => a) - .Select(n => new Sprite(chromeTexture, new Rectangle(32 + 13 * n, 0, 13, 17), TextureChannel.Alpha)).ToList(); - - shimSprites = new[] - { - new Sprite( chromeTexture, new Rectangle( 0, 192, 9, 10 ), TextureChannel.Alpha ), - new Sprite( chromeTexture, new Rectangle( 0, 202, 9, 10 ), TextureChannel.Alpha ), - new Sprite( chromeTexture, new Rectangle( 0, 216, 9, 48 ), TextureChannel.Alpha ), - new Sprite( chromeTexture, new Rectangle( 11, 192, 64, 10 ), TextureChannel.Alpha ), - new Sprite( chromeTexture, new Rectangle( 11, 202, 64, 10 ), TextureChannel.Alpha ), - }; - ready = new Animation("pips"); ready.PlayRepeating("ready"); clock = new Animation("clock"); } + public void Tick() + { + TickPaletteAnimation(); + TickRadarAnimation(); + } + public void Draw() { + chromeCollection = (Game.LocalPlayer.Race == Race.Allies) ? "chrome-allies" : "chrome-soviet"; + radarCollection = (Game.LocalPlayer.Race == Race.Allies) ? "radar-allies" : "radar-soviet"; + paletteCollection = (Game.LocalPlayer.Race == Race.Allies) ? "palette-allies" : "palette-soviet"; + digitCollection = (Game.LocalPlayer.Race == Race.Allies) ? "digits-allies" : "digits-soviet"; + buttons.Clear(); renderer.Device.DisableScissor(); - renderer.DrawText("RenderFrame {0} ({2:F1} ms)\nTick {1} ({3:F1} ms)\nPower {4}/{5}\nReady: {6} (F8 to toggle)".F( + renderer.DrawText("RenderFrame {0} ({2:F1} ms)\nTick {1} ({3:F1} ms)\nReady: {4} (F8 to toggle)".F( Game.RenderFrame, Game.orderManager.FrameNumber, PerfHistory.items["render"].LastValue, PerfHistory.items["tick_time"].LastValue, - Game.LocalPlayer.PowerDrained, - Game.LocalPlayer.PowerProvided, Game.LocalPlayer.IsReady ? "Yes" : "No" ), new int2(140, 15), Color.White); if (Game.Settings.PerfGraph) PerfHistory.Render(renderer, Game.worldRenderer.lineRenderer); - DrawMinimap(); - - rgbaRenderer.DrawSprite(moneyBinSprite, new float2(Game.viewport.Width - 320, 0), PaletteType.Chrome); - - DrawMoney(); + DrawRadar(); DrawPower(); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, chromeCollection, "moneybin"), new float2(Game.viewport.Width - 320, 0), PaletteType.Chrome); + DrawMoney(); rgbaRenderer.Flush(); DrawButtons(); @@ -195,22 +177,73 @@ namespace OpenRa.Game DrawOptionsMenu(); } - void DrawMinimap() + public void TickRadarAnimation() { - var hasRadar = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer + if (!radarAnimating) + return; + + // Increment frame + if (hasRadar) + radarAnimationFrame++; + else + radarAnimationFrame--; + + // Calculate radar bin position + if (radarAnimationFrame <= radarSlideAnimationLength) + radarOrigin = float2.Lerp(radarClosedOrigin, radarOpenOrigin, radarAnimationFrame * 1.0f / radarSlideAnimationLength); + + // Play radar-on sound at the start of the activate anim (open) + if (radarAnimationFrame == radarSlideAnimationLength && hasRadar) + Sound.Play("radaron2.aud"); + + // Play radar-on sound at the start of the activate anim (close) + if (radarAnimationFrame == radarSlideAnimationLength + radarActivateAnimationLength - 1 && !hasRadar) + Sound.Play("radardn1.aud"); + + // Minimap height + if (radarAnimationFrame >= radarSlideAnimationLength) + radarMinimapHeight = float2.Lerp(0, 192, (radarAnimationFrame - radarSlideAnimationLength) * 1.0f / radarActivateAnimationLength); + + // Animation is complete + if ((radarAnimationFrame == 0 && !hasRadar) + || (radarAnimationFrame == radarSlideAnimationLength + radarActivateAnimationLength && hasRadar)) + { + radarAnimating = false; + } + } + + void DrawRadar() + { + var hasNewRadar = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains() && a.traits.Get().IsActive()); - if (hasRadar != hadRadar) - Sound.Play((hasRadar) ? "radaron2.aud" : "radardn1.aud"); - hadRadar = hasRadar; + if (hasNewRadar != hasRadar) + { + radarAnimating = true; + } + + hasRadar = hasNewRadar; var isJammed = false; // todo: MRJ can do this + + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, radarCollection, "left"), radarOrigin, PaletteType.Chrome); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, radarCollection, "right"), radarOrigin + new float2(201, 0), PaletteType.Chrome); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, radarCollection, "bottom"), radarOrigin + new float2(0, 192), PaletteType.Chrome); + + if (radarAnimating) + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, radarCollection, "bg"), radarOrigin + new float2(9, 0), PaletteType.Chrome); - Game.minimap.Draw(new float2(Game.viewport.Width - 247, 10), hasRadar, isJammed); + rgbaRenderer.Flush(); + + if (radarAnimationFrame >= radarSlideAnimationLength) + { + RectangleF mapRect = new RectangleF(radarOrigin.X + 9, radarOrigin.Y+(192-radarMinimapHeight)/2, 192, radarMinimapHeight); + Game.minimap.Draw(mapRect, hasRadar, isJammed); + } } - void AddButton(Rectangle r, Action b) { buttons.Add(Pair.New(r, b)); } + void AddButton(RectangleF r, Action b) { buttons.Add(Pair.New(r, b)); } void DrawBuildTabs(int paletteHeight) { @@ -224,7 +257,7 @@ namespace OpenRa.Game var queue = Game.LocalPlayer.PlayerActor.traits.Get(); - foreach (var q in tabSprites) + foreach (var q in tabImageNames) { var groupName = q.Key; if (!Rules.TechTree.BuildableItems(Game.LocalPlayer, groupName).Any()) @@ -232,29 +265,30 @@ namespace OpenRa.Game CheckDeadTab(groupName); continue; } - + string[] tabKeys = { "normal", "ready", "selected" }; var producing = queue.CurrentItem(groupName); var index = q.Key == currentTab ? 2 : (producing != null && producing.Done) ? 1 : 0; - - - // Don't let tabs overlap the bevel - if (y > paletteOrigin.Y + paletteHeight - tabHeight - 9 && y < paletteOrigin.Y + paletteHeight) - y += tabHeight; - - // Stick tabs to the edge of the screen - if (y > paletteOrigin.Y + paletteHeight) - x = Game.viewport.Width - tabWidth; + var race = (Game.LocalPlayer.Race == Race.Allies) ? "allies" : "soviet"; + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer,"tabs-"+tabKeys[index], race+"-"+q.Key), new float2(x, y), PaletteType.Chrome); - rgbaRenderer.DrawSprite(q.Value[index], new float2(x, y), PaletteType.Chrome); - - buttons.Add(Pair.New(new Rectangle(x, y, tabWidth, tabHeight), - (Action)(isLmb => currentTab = groupName))); + buttons.Add(Pair.New(new RectangleF(x, y, tabWidth, tabHeight), + (Action)(isLmb => HandleTabClick(groupName)))); y += tabHeight; } rgbaRenderer.Flush(); } + void HandleTabClick(string button) + { + Sound.Play("ramenu1.aud"); + var wasOpen = paletteOpen; + paletteOpen = (currentTab == button && wasOpen) ? false : true; + currentTab = button; + if (wasOpen != paletteOpen) + paletteAnimating = true; + } + void CheckDeadTab( string groupName ) { var queue = Game.LocalPlayer.PlayerActor.traits.Get(); @@ -264,17 +298,17 @@ namespace OpenRa.Game void ChooseAvailableTab() { - currentTab = tabSprites.Select(q => q.Key).FirstOrDefault( + currentTab = tabImageNames.Select(q => q.Key).FirstOrDefault( t => Rules.TechTree.BuildableItems(Game.LocalPlayer, t).Any()); } void DrawMoney() { var moneyDigits = Game.LocalPlayer.DisplayCash.ToString(); - var x = Game.viewport.Width - 155; + var x = Game.viewport.Width - 65; foreach (var d in moneyDigits.Reverse()) { - rgbaRenderer.DrawSprite(digitSprites[d - '0'], new float2(x, 6), PaletteType.Chrome); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, digitCollection, (d - '0').ToString()), new float2(x, 6), PaletteType.Chrome); x -= 14; } } @@ -284,51 +318,60 @@ namespace OpenRa.Game void DrawPower() { - //draw background - float2 powerOrigin = Game.viewport.Location + new float2(Game.viewport.Width - 20, paletteOrigin.Y); - - shpRenderer.DrawSprite(powerLevelTopSprite, powerOrigin, PaletteType.Chrome); - shpRenderer.DrawSprite(powerLevelBottomSprite, powerOrigin + new float2(0, powerLevelTopSprite.size.Y), PaletteType.Chrome); - shpRenderer.Flush(); - float2 top = powerOrigin + new float2(0, 15); - float2 bottom = powerOrigin + new float2(0, powerLevelTopSprite.size.Y + powerLevelBottomSprite.size.Y) - new float2(0, 50); + // Nothing to draw + if (Game.LocalPlayer.PowerProvided == 0 && Game.LocalPlayer.PowerDrained == 0) + return; - var scale = 100; - while(Math.Max(Game.LocalPlayer.PowerProvided, Game.LocalPlayer.PowerDrained) >= scale) scale *= 2; - //draw bar + // Draw bar horizontally + var barStart = powerOrigin + Game.viewport.Location + radarOrigin; + var barEnd = barStart + new float2(powerSize.Width, 0); - var powerTopY = bottom.Y + (top.Y - bottom.Y) * (Game.LocalPlayer.PowerProvided / (float)scale) - Game.viewport.Location.Y; - lastPowerProvidedPos = float2.Lerp(lastPowerProvidedPos.GetValueOrDefault(powerTopY), powerTopY, .3f); - float2 powerTop = new float2(bottom.X, lastPowerProvidedPos.Value + Game.viewport.Location.Y); + float powerScaleBy = 100; + var maxPower = Math.Max(Game.LocalPlayer.PowerProvided, Game.LocalPlayer.PowerDrained); + while (maxPower >= powerScaleBy) powerScaleBy *= 2; + // Current power supply + var powerLevelTemp = barStart.X + (barEnd.X - barStart.X) * (Game.LocalPlayer.PowerProvided / powerScaleBy) - Game.viewport.Location.X; + lastPowerProvidedPos = float2.Lerp(lastPowerProvidedPos.GetValueOrDefault(powerLevelTemp), powerLevelTemp, .3f); + float2 powerLevel = new float2(lastPowerProvidedPos.Value + Game.viewport.Location.X, barStart.Y); + var color = Color.LimeGreen; if (Game.LocalPlayer.GetPowerState() == PowerState.Low) color = Color.Orange; if (Game.LocalPlayer.GetPowerState() == PowerState.Critical) color = Color.Red; - - var color2 = Graphics.Util.Lerp(0.25f, color, Color.Black); - - for(int i = 11; i < 13; i++) - lineRenderer.DrawLine(bottom + new float2(i, 0), powerTop + new float2(i, 0), color, color); - for (int i = 13; i < 15; i++) - lineRenderer.DrawLine(bottom + new float2(i, 0), powerTop + new float2(i, 0), color2, color2); - + + var colorDark = Graphics.Util.Lerp(0.25f, color, Color.Black); + for (int i = 0; i < powerSize.Height; i++) + { + color = (i-1 < powerSize.Height/2) ? color : colorDark; + float2 leftOffset = new float2(0,i); + float2 rightOffset = new float2(0,i); + // Indent corners + if ((i == 0 || i == powerSize.Height - 1) && powerLevel.X - barStart.X > 1) + { + leftOffset.X += 1; + rightOffset.X -= 1; + } + lineRenderer.DrawLine(barStart + leftOffset, powerLevel + rightOffset, color, color); + } lineRenderer.Flush(); - var drainedPositionY = bottom.Y + (top.Y - bottom.Y)*(Game.LocalPlayer.PowerDrained/(float) scale) - powerIndicatorSprite.size.Y /2 - Game.viewport.Location.Y; - lastPowerDrainedPos = float2.Lerp(lastPowerDrainedPos.GetValueOrDefault(drainedPositionY), drainedPositionY, .3f); - //draw indicator - float2 drainedPosition = new float2(bottom.X + 2, lastPowerDrainedPos.Value + Game.viewport.Location.Y); - - shpRenderer.DrawSprite(powerIndicatorSprite, drainedPosition, PaletteType.Chrome); - shpRenderer.Flush(); + // Power usage indicator + var indicator = SequenceProvider.GetImageFromCollection(renderer, radarCollection, "power-indicator"); + var powerDrainedTemp = barStart.X + (barEnd.X - barStart.X) * (Game.LocalPlayer.PowerDrained / powerScaleBy) - Game.viewport.Location.X; + lastPowerDrainedPos = float2.Lerp(lastPowerDrainedPos.GetValueOrDefault(powerDrainedTemp), powerDrainedTemp, .3f); + float2 powerDrainLevel = new float2(lastPowerDrainedPos.Value-indicator.size.X/2, barStart.Y - Game.viewport.Location.Y-1); + + rgbaRenderer.DrawSprite(indicator, powerDrainLevel, PaletteType.Chrome); + rgbaRenderer.Flush(); } void DrawButtons() { + int2 buttonOrigin = new int2(Game.viewport.Width - 320, 2); // Repair - Rectangle repairRect = new Rectangle(Game.viewport.Width - 120, 5, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height); + Rectangle repairRect = new Rectangle(buttonOrigin.X, buttonOrigin.Y, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height); var repairDrawPos = Game.viewport.Location + new float2(repairRect.Location); var hasFact = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains()); @@ -343,7 +386,7 @@ namespace OpenRa.Game shpRenderer.DrawSprite(repairButton.Image, repairDrawPos, PaletteType.Chrome); // Sell - Rectangle sellRect = new Rectangle(Game.viewport.Width - 80, 5, + Rectangle sellRect = new Rectangle(buttonOrigin.X+40, buttonOrigin.Y, sellButton.Image.bounds.Width, sellButton.Image.bounds.Height); var sellDrawPos = Game.viewport.Location + new float2(sellRect.Location); @@ -357,7 +400,7 @@ namespace OpenRa.Game if (Game.Settings.PowerDownBuildings) { // Power Down - Rectangle pwrdownRect = new Rectangle(Game.viewport.Width - 40, 5, + Rectangle pwrdownRect = new Rectangle(buttonOrigin.X+80, buttonOrigin.Y, pwrdownButton.Image.bounds.Width, pwrdownButton.Image.bounds.Height); var pwrdownDrawPos = Game.viewport.Location + new float2(pwrdownRect.Location); @@ -437,12 +480,48 @@ namespace OpenRa.Game renderer.DrawText(line.c, p + new int2(size.X + 10, 0), Color.White); } + void TickPaletteAnimation() + { + Log.Write("{0} {1} {2} {3}", paletteAnimationFrame, paletteOrigin.X, paletteAnimating, paletteOpen); + + if (!paletteAnimating) + return; + + // Increment frame + if (paletteOpen) + paletteAnimationFrame++; + else + paletteAnimationFrame--; + + Log.Write("{0}",paletteAnimationFrame); + + // Calculate palette position + if (paletteAnimationFrame <= paletteAnimationLength) + paletteOrigin = float2.Lerp(paletteClosedOrigin, paletteOpenOrigin, paletteAnimationFrame * 1.0f / paletteAnimationLength); + + // Play radar-on sound at the start of the activate anim (open) + if (paletteAnimationFrame == 1 && paletteOpen) + Sound.Play("bleep13.aud"); + + // Play radar-on sound at the start of the activate anim (close) + if (paletteAnimationFrame == paletteAnimationLength + -1 && !paletteOpen) + Sound.Play("bleep13.aud"); + + // Animation is complete + if ((paletteAnimationFrame == 0 && !paletteOpen) + || (paletteAnimationFrame == paletteAnimationLength && paletteOpen)) + { + paletteAnimating = false; + } + } + + // Return an int telling us the y coordinate at the bottom of the palette int DrawBuildPalette(string queueName) { // Hack int columns = paletteColumns; - int2 origin = new int2(paletteOrigin.X + 9, paletteOrigin.Y + 9); + float2 origin = new float2(paletteOrigin.X + 9, paletteOrigin.Y + 9); if (queueName == null) return 0; @@ -461,13 +540,26 @@ namespace OpenRa.Game string tooltipItem = null; + // Draw the top border + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, paletteCollection, "top"), new float2(origin.X - 9, origin.Y - 9), PaletteType.Chrome); + + // Draw the icons + int lasty = -1; foreach (var item in allItems) { - var rect = new Rectangle(origin.X + x * 64, origin.Y + 48 * y, 64, 48); + // Draw the background for this row + if (y != lasty) + { + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, paletteCollection, "bg-" + (y % 4).ToString()), new float2(origin.X - 9, origin.Y + 48 * y), PaletteType.Chrome); + rgbaRenderer.Flush(); + lasty = y; + } + + var rect = new RectangleF(origin.X + x * 64, origin.Y + 48 * y, 64, 48); var drawPos = Game.viewport.Location + new float2(rect.Location); var isBuildingSomething = queue.CurrentItem(queueName) != null; - shpRenderer.DrawSprite(sprites[item], drawPos, PaletteType.Chrome); + shpRenderer.DrawSprite(tabSprites[item], drawPos, PaletteType.Chrome); var firstOfThis = queue.AllItems(queueName).FirstOrDefault(a => a.Item == item); @@ -516,38 +608,35 @@ namespace OpenRa.Game var closureItem = item; AddButton(rect, isLmb => HandleBuildPalette(closureItem, isLmb)); + + if (++x == columns) { x = 0; y++; } } - - while (x != 0 || y < MinRows) + if (x != 0) y++; + + while (y < paletteRows) { - var rect = new Rectangle(origin.X + x * 64, origin.Y + 48 * y, 64, 48); - var drawPos = Game.viewport.Location + new float2(rect.Location); - shpRenderer.DrawSprite(blank, drawPos, PaletteType.Chrome); - AddButton(rect, _ => { }); - if (++x == columns) { x = 0; y++; } + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, paletteCollection, "bg-" + (y % 4).ToString()), new float2(origin.X - 9, origin.Y + 48 * y), PaletteType.Chrome); + y++; } foreach (var ob in overlayBits) shpRenderer.DrawSprite(ob.First, ob.Second, PaletteType.Chrome); shpRenderer.Flush(); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, paletteCollection, "bottom"), new float2(origin.X - 9, origin.Y - 1 + 48 * y), PaletteType.Chrome); - for (var j = 0; j < y; j++) - rgbaRenderer.DrawSprite(shimSprites[2], new float2(origin.X - 9, origin.Y + 48 * j), PaletteType.Chrome); - - rgbaRenderer.DrawSprite(shimSprites[0], new float2(origin.X - 9, origin.Y - 9), PaletteType.Chrome); - rgbaRenderer.DrawSprite(shimSprites[1], new float2(origin.X - 9, origin.Y - 1 + 48 * y), PaletteType.Chrome); - - for (var i = 0; i < columns; i++) + // Draw dock + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, paletteCollection, "dock-top"), new float2(Game.viewport.Width - 14, origin.Y - 23), PaletteType.Chrome); + for (int i = 0; i < y; i++) { - rgbaRenderer.DrawSprite(shimSprites[3], new float2(origin.X + 64 * i, origin.Y - 9), PaletteType.Chrome); - rgbaRenderer.DrawSprite(shimSprites[4], new float2(origin.X + 64 * i, origin.Y - 1 + 48 * y), PaletteType.Chrome); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, paletteCollection, "dock-" + (y % 4).ToString()), new float2(Game.viewport.Width - 14, origin.Y + 48 * i), PaletteType.Chrome); } + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, paletteCollection, "dock-bottom"), new float2(Game.viewport.Width - 14, origin.Y - 1 + 48 * y), PaletteType.Chrome); rgbaRenderer.Flush(); if (tooltipItem != null) - DrawProductionTooltip(tooltipItem, new int2(Game.viewport.Width, origin.Y + y * 48 + 9)/*tooltipPos*/); + DrawProductionTooltip(tooltipItem, new float2(Game.viewport.Width, origin.Y + y * 48 + 9).ToInt2()/*tooltipPos*/); return y*48+9; } @@ -637,6 +726,7 @@ namespace OpenRa.Game void DrawProductionTooltip(string unit, int2 pos) { + var tooltipSprite = SequenceProvider.GetImageFromCollection(renderer, chromeCollection, "tooltip-bg"); var p = pos.ToFloat2() - new float2(tooltipSprite.size.X, 0); rgbaRenderer.DrawSprite(tooltipSprite, p, PaletteType.Chrome); rgbaRenderer.Flush(); @@ -681,10 +771,10 @@ namespace OpenRa.Game if (numPowers == 0) return; - rgbaRenderer.DrawSprite(specialBinSprites[0], new float2(0,14), PaletteType.Chrome); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, chromeCollection, "specialbin-top"), new float2(0, 14), PaletteType.Chrome); for (var i = 1; i < numPowers; i++) - rgbaRenderer.DrawSprite(specialBinSprites[1], new float2(0, 14 + i * 51), PaletteType.Chrome); - rgbaRenderer.DrawSprite(specialBinSprites[2], new float2(0, 14 + numPowers * 51), PaletteType.Chrome); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, chromeCollection, "specialbin-middle"), new float2(0, 14 + i * 51), PaletteType.Chrome); + rgbaRenderer.DrawSprite(SequenceProvider.GetImageFromCollection(renderer, chromeCollection, "specialbin-bottom"), new float2(0, 14 + numPowers * 51), PaletteType.Chrome); rgbaRenderer.Flush(); @@ -750,6 +840,7 @@ namespace OpenRa.Game void DrawSupportPowerTooltip(string sp, int2 pos) { + var tooltipSprite = SequenceProvider.GetImageFromCollection(renderer, chromeCollection, "tooltip-bg"); rgbaRenderer.DrawSprite(tooltipSprite, pos, PaletteType.Chrome); rgbaRenderer.Flush(); diff --git a/OpenRa.Game/Game.cs b/OpenRa.Game/Game.cs index 271d636519..4ce3d7d8c4 100644 --- a/OpenRa.Game/Game.cs +++ b/OpenRa.Game/Game.cs @@ -178,6 +178,7 @@ namespace OpenRa.Game UpdatePalette(world.Actors.SelectMany( a => a.traits.WithInterface())); minimap.Update(); + chrome.Tick(); orderManager.TickImmediate(); diff --git a/OpenRa.Game/Graphics/MappedImage.cs b/OpenRa.Game/Graphics/MappedImage.cs new file mode 100644 index 0000000000..1d9365eae8 --- /dev/null +++ b/OpenRa.Game/Graphics/MappedImage.cs @@ -0,0 +1,31 @@ +using System.Xml; +using System.Drawing; +using Ijw.DirectX; +using System.IO; +namespace OpenRa.Game.Graphics +{ + class MappedImage + { + readonly Rectangle rect; + public readonly string Src; + public readonly string Name; + + public MappedImage(string defaultSrc, XmlElement e) + { + Src = (e.HasAttribute("src")) ? e.GetAttribute("src") : defaultSrc; + Name = e.GetAttribute("name"); + if (Src == null) + throw new InvalidDataException("Image src missing"); + + rect = new Rectangle(int.Parse(e.GetAttribute("x")), + int.Parse(e.GetAttribute("y")), + int.Parse(e.GetAttribute("width")), + int.Parse(e.GetAttribute("height"))); + } + + public Sprite GetImage(Renderer r, Sheet s) + { + return new Sprite(s, rect, TextureChannel.Alpha); + } + } +} diff --git a/OpenRa.Game/Graphics/Minimap.cs b/OpenRa.Game/Graphics/Minimap.cs index 6cb55468b6..f80cb3a066 100644 --- a/OpenRa.Game/Graphics/Minimap.cs +++ b/OpenRa.Game/Graphics/Minimap.cs @@ -1,4 +1,5 @@ -using System.Drawing; +using System; +using System.Drawing; using System.Linq; using OpenRa.Game.Traits; using OpenRa.FileFormats; @@ -10,36 +11,32 @@ namespace OpenRa.Game.Graphics { Sheet sheet; SpriteRenderer rgbaRenderer; - SpriteRenderer shpRenderer; Sprite sprite; Bitmap terrain, oreLayer; - Animation radarAnim, alliesAnim, sovietAnim; + const int alpha = 230; public void Tick() { } public Minimap(Renderer r) { sheet = new Sheet(r, new Size(128, 128)); - shpRenderer = new SpriteRenderer(r, true); - rgbaRenderer = new SpriteRenderer(r, true, r.RgbaSpriteShader); - sprite = new Sprite(sheet, new Rectangle(0, 0, 128, 128), TextureChannel.Alpha); - sovietAnim = new Animation("ussrradr"); - sovietAnim.PlayRepeating("idle"); - alliesAnim = new Animation("natoradr"); - alliesAnim.PlayRepeating("idle"); - radarAnim = Game.LocalPlayer.Race == Race.Allies ? alliesAnim : sovietAnim; + rgbaRenderer = new SpriteRenderer(r, true, r.RgbaSpriteShader); + var size = Math.Max(Rules.Map.Width, Rules.Map.Height); + var dw = (size - Rules.Map.Width) / 2; + var dh = (size - Rules.Map.Height) / 2; + + sprite = new Sprite(sheet, new Rectangle(Rules.Map.Offset.X+dw, Rules.Map.Offset.Y+dh, size, size), TextureChannel.Alpha); } Color[] terrainTypeColors; + Color[] playerColors; + Color shroudColor; public void InvalidateOre() { oreLayer = null; } public void Update() { - radarAnim = Game.LocalPlayer.Race == Race.Allies ? alliesAnim : sovietAnim; - radarAnim.Tick(); - if (!Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains())) return; @@ -47,17 +44,20 @@ namespace OpenRa.Game.Graphics { var pal = new Palette(FileSystem.Open(Rules.Map.Theater + ".pal")); terrainTypeColors = new[] { - pal.GetColor(0x1a), - pal.GetColor(0x63), - pal.GetColor(0x2f), - pal.GetColor(0x1f), - pal.GetColor(0x14), - pal.GetColor(0x64), - pal.GetColor(0x1f), - pal.GetColor(0x68), - pal.GetColor(0x6b), - pal.GetColor(0x6d), + Color.FromArgb(alpha, pal.GetColor(0x1a)), + Color.FromArgb(alpha, pal.GetColor(0x63)), + Color.FromArgb(alpha, pal.GetColor(0x2f)), + Color.FromArgb(alpha, pal.GetColor(0x1f)), + Color.FromArgb(alpha, pal.GetColor(0x14)), + Color.FromArgb(alpha, pal.GetColor(0x64)), + Color.FromArgb(alpha, pal.GetColor(0x1f)), + Color.FromArgb(alpha, pal.GetColor(0x68)), + Color.FromArgb(alpha, pal.GetColor(0x6b)), + Color.FromArgb(alpha, pal.GetColor(0x6d)), }; + + playerColors = Util.MakeArray( 8, b => Color.FromArgb(alpha, Chat.paletteColors[b]) ); + shroudColor = Color.FromArgb(alpha, Color.Black); } if (terrain == null) @@ -67,7 +67,7 @@ namespace OpenRa.Game.Graphics for (var x = 0; x < 128; x++) terrain.SetPixel(x, y, Rules.Map.IsInMap(x, y) ? terrainTypeColors[Rules.TileSet.GetWalkability(Rules.Map.MapTiles[x, y])] - : Color.Black); + : shroudColor); } if (oreLayer == null) @@ -92,19 +92,19 @@ namespace OpenRa.Game.Graphics { var b = Game.BuildingInfluence.GetBuildingAt(new int2(x, y)); if (b != null) - *(c + (y * bitmapData.Stride >> 2) + x) = - (b.Owner != null ? Chat.paletteColors[(int)b.Owner.Palette] : terrainTypeColors[4]).ToArgb(); + *(c + (y * bitmapData.Stride >> 2) + x) = + (b.Owner != null ? playerColors[(int)b.Owner.Palette] : terrainTypeColors[4]).ToArgb(); } foreach (var a in Game.world.Actors.Where(a => a.traits.Contains())) *(c + (a.Location.Y * bitmapData.Stride >> 2) + a.Location.X) = Chat.paletteColors[(int)a.Owner.Palette].ToArgb(); - + unchecked { for (var y = 0; y < 128; y++) for (var x = 0; x < 128; x++) if (!Game.LocalPlayer.Shroud.IsExplored(new int2(x, y))) - *(c + (y * bitmapData.Stride >> 2) + x) = (int)0xff000000; + *(c + (y * bitmapData.Stride >> 2) + x) = shroudColor.ToArgb(); } } @@ -112,27 +112,10 @@ namespace OpenRa.Game.Graphics sheet.Texture.SetData(bitmap); } - public void Draw(float2 pos, bool hasRadar, bool isJammed) + public void Draw(RectangleF rect, bool hasRadar, bool isJammed) { - if (hasRadar && radarAnim.CurrentSequence.Name == "idle") - radarAnim.PlayThen("open", () => radarAnim.PlayRepeating("active")); - if (hasRadar && radarAnim.CurrentSequence.Name == "no-power") - radarAnim.PlayBackwardsThen("close", () => radarAnim.PlayRepeating("active")); - if (!hasRadar && radarAnim.CurrentSequence.Name == "active") - radarAnim.PlayThen("close", () => radarAnim.PlayRepeating("no-power")); - if (isJammed && radarAnim.CurrentSequence.Name == "active") - radarAnim.PlayRepeating("jammed"); - if (!isJammed && radarAnim.CurrentSequence.Name == "jammed") - radarAnim.PlayRepeating("active"); - - shpRenderer.DrawSprite(radarAnim.Image, pos + Game.viewport.Location - new float2( 290-256,0), PaletteType.Chrome, new float2(290, 272)); - shpRenderer.Flush(); - - if (radarAnim.CurrentSequence.Name == "active") - { - rgbaRenderer.DrawSprite(sprite, pos - new float2((290-256)/2, -5), PaletteType.Chrome, new float2(256, 256)); - rgbaRenderer.Flush(); - } + rgbaRenderer.DrawSprite(sprite, new float2(rect.X, rect.Y), PaletteType.Chrome, new float2(rect.Width, rect.Height)); + rgbaRenderer.Flush(); } } } diff --git a/OpenRa.Game/Graphics/SequenceProvider.cs b/OpenRa.Game/Graphics/SequenceProvider.cs index 756f523a8b..96512aa4e7 100644 --- a/OpenRa.Game/Graphics/SequenceProvider.cs +++ b/OpenRa.Game/Graphics/SequenceProvider.cs @@ -10,15 +10,25 @@ namespace OpenRa.Game.Graphics { static Dictionary> units; static Dictionary cursors; - + + static Dictionary> collections; + static Dictionary cachedSheets; + static Dictionary> cachedSprites; + public static void Initialize( bool useAftermath ) { units = new Dictionary>(); cursors = new Dictionary(); + collections = new Dictionary>(); + cachedSheets = new Dictionary(); + cachedSprites = new Dictionary>(); + LoadSequenceSource("sequences.xml"); if (useAftermath) LoadSequenceSource("sequences-aftermath.xml"); + + LoadChromeSource("chrome.xml"); } static void LoadSequenceSource(string filename) @@ -32,7 +42,27 @@ namespace OpenRa.Game.Graphics foreach (XmlElement eCursor in document.SelectNodes("/sequences/cursor")) LoadSequencesForCursor(eCursor); } + + static void LoadChromeSource(string filename) + { + XmlDocument document = new XmlDocument(); + document.Load(FileSystem.Open(filename)); + foreach (XmlElement eCollection in document.SelectNodes("/chrome/collection")) + LoadChromeForCollection(eCollection); + } + static void LoadChromeForCollection(XmlElement eCollection) + { + string elementName = eCollection.GetAttribute("name"); + string defaultSrc = (eCollection.HasAttribute("src") ? eCollection.GetAttribute("src") : null); + + var images = eCollection.SelectNodes("./image").OfType() + .Select(e => new MappedImage(defaultSrc, e)) + .ToDictionary(s => s.Name); + + collections.Add(elementName, images); + } + static void LoadSequencesForCursor(XmlElement eCursor) { string cursorSrc = eCursor.GetAttribute("src"); @@ -73,5 +103,40 @@ namespace OpenRa.Game.Graphics { return cursors[cursor]; } + + + public static Sprite GetImageFromCollection(Renderer renderer,string collection, string image) + { + + + // Cached sprite + if (cachedSprites.ContainsKey(collection) && cachedSprites[collection].ContainsKey(image)) + return cachedSprites[collection][image]; + + MappedImage mi; + try { mi = collections[collection][image];} + catch (KeyNotFoundException) + { + throw new InvalidOperationException( + "Collection `{0}` does not have an image `{1}`".F(collection, image)); + } + + // Cached sheet + Sheet sheet; + if (cachedSheets.ContainsKey(mi.Src)) + sheet = cachedSheets[mi.Src]; + else + { + sheet = new Sheet(renderer, mi.Src); + cachedSheets.Add(mi.Src, sheet); + } + + // Cache the sprite + if (!cachedSprites.ContainsKey(collection)) + cachedSprites.Add(collection, new Dictionary()); + cachedSprites[collection].Add(image, mi.GetImage(renderer, sheet)); + + return cachedSprites[collection][image]; + } } } diff --git a/OpenRa.Game/OpenRa.Game.csproj b/OpenRa.Game/OpenRa.Game.csproj index 088f626e4e..aac3bc8b21 100644 --- a/OpenRa.Game/OpenRa.Game.csproj +++ b/OpenRa.Game/OpenRa.Game.csproj @@ -100,6 +100,7 @@ + diff --git a/OpenRa.Game/Orders/Order.cs b/OpenRa.Game/Orders/Order.cs index 828d44b4d8..c1994cad54 100644 --- a/OpenRa.Game/Orders/Order.cs +++ b/OpenRa.Game/Orders/Order.cs @@ -154,5 +154,10 @@ namespace OpenRa.Game { return new Order("CancelProduction", subject.PlayerActor, null, int2.Zero, item); } + + public static Order PlayAnimation(Actor actor, string animationString) + { + return new Order("PlayAnimation", actor, null, int2.Zero, animationString); + } } } diff --git a/OpenRa.Game/SupportPowers/ChronospherePower.cs b/OpenRa.Game/SupportPowers/ChronospherePower.cs index e3355fb771..0e860b987f 100644 --- a/OpenRa.Game/SupportPowers/ChronospherePower.cs +++ b/OpenRa.Game/SupportPowers/ChronospherePower.cs @@ -19,14 +19,14 @@ namespace OpenRa.Game.SupportPowers Sound.Play("chrono2.aud"); - foreach (var a in Game.world.Actors.Where(a => a.traits.Contains())) - a.traits.Get().DoChronoshift(); - // Play chronosphere active anim var chronosphere = Game.world.Actors.Where(a => a.Owner == p.Owner && a.traits.Contains()).FirstOrDefault(); - if (chronosphere != null) - chronosphere.traits.Get().PlayCustomAnim(chronosphere, "active"); + Game.controller.AddOrder(Order.PlayAnimation(chronosphere, "active")); + + // Trigger screen desaturate effect + foreach (var a in Game.world.Actors.Where(a => a.traits.Contains())) + a.traits.Get().DoChronoshift(); } SupportPower p; public void Activate(SupportPower p) diff --git a/OpenRa.Game/SupportPowers/IronCurtainPower.cs b/OpenRa.Game/SupportPowers/IronCurtainPower.cs index 6b69cd0278..65c50320ea 100644 --- a/OpenRa.Game/SupportPowers/IronCurtainPower.cs +++ b/OpenRa.Game/SupportPowers/IronCurtainPower.cs @@ -29,7 +29,8 @@ namespace OpenRa.Game.SupportPowers .Where(a => a.Owner == p.Owner && a.traits.Contains()) .FirstOrDefault(); if (ironCurtain != null) - ironCurtain.traits.Get().PlayCustomAnim(ironCurtain, "active"); + Game.controller.AddOrder(Order.PlayAnimation(ironCurtain, "active")); + } SupportPower p; public void Activate(SupportPower p) diff --git a/OpenRa.Game/Traits/Chronosphere.cs b/OpenRa.Game/Traits/Chronosphere.cs index cad8a1cf13..771cc8f4b4 100644 --- a/OpenRa.Game/Traits/Chronosphere.cs +++ b/OpenRa.Game/Traits/Chronosphere.cs @@ -5,8 +5,18 @@ using System.Text; namespace OpenRa.Game.Traits { - class Chronosphere + class Chronosphere : IResolveOrder { public Chronosphere(Actor self) { } + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "PlayAnimation") + { + var rb = self.traits.Get(); + if (rb != null) + rb.PlayCustomAnim(self, order.TargetString); + } + } } } diff --git a/OpenRa.Game/Traits/IronCurtain.cs b/OpenRa.Game/Traits/IronCurtain.cs index 668b469bd1..995af17d38 100644 --- a/OpenRa.Game/Traits/IronCurtain.cs +++ b/OpenRa.Game/Traits/IronCurtain.cs @@ -5,8 +5,18 @@ using System.Text; namespace OpenRa.Game.Traits { - class IronCurtain + class IronCurtain : IResolveOrder { public IronCurtain(Actor self) {} + + public void ResolveOrder(Actor self, Order order) + { + if (order.OrderString == "PlayAnimation") + { + var rb = self.traits.Get(); + if (rb != null) + rb.PlayCustomAnim(self, order.TargetString); + } + } } } diff --git a/artsrc/buildpalette.xcf b/artsrc/buildpalette.xcf new file mode 100644 index 0000000000..fc3e44c2ce Binary files /dev/null and b/artsrc/buildpalette.xcf differ diff --git a/artsrc/chrome.xcf b/artsrc/chrome.xcf new file mode 100644 index 0000000000..ff5b798750 Binary files /dev/null and b/artsrc/chrome.xcf differ diff --git a/artsrc/tabs.xcf b/artsrc/tabs.xcf new file mode 100644 index 0000000000..b48da081d5 Binary files /dev/null and b/artsrc/tabs.xcf differ diff --git a/chrome-allies.png b/chrome-allies.png new file mode 100644 index 0000000000..6bf0bdf808 Binary files /dev/null and b/chrome-allies.png differ diff --git a/chrome-soviet.png b/chrome-soviet.png new file mode 100644 index 0000000000..e8ec2ac96d Binary files /dev/null and b/chrome-soviet.png differ diff --git a/chrome.xml b/chrome.xml new file mode 100644 index 0000000000..84242c5709 --- /dev/null +++ b/chrome.xml @@ -0,0 +1,125 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sequences.xml b/sequences.xml index 135736bbcc..2b8524610e 100644 --- a/sequences.xml +++ b/sequences.xml @@ -1010,13 +1010,6 @@ - - - - - - - @@ -1041,20 +1034,4 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/specialbin.png b/specialbin.png deleted file mode 100644 index 9985343d8c..0000000000 Binary files a/specialbin.png and /dev/null differ diff --git a/tabs.png b/tabs.png new file mode 100644 index 0000000000..e0edf88e58 Binary files /dev/null and b/tabs.png differ