Everything is now OpenRA, not OpenRa

This commit is contained in:
alzeih
2010-02-27 21:10:22 +13:00
parent 11b926a422
commit 7881deca30
299 changed files with 26919 additions and 21 deletions

View File

@@ -0,0 +1,150 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
namespace OpenRA.Graphics
{
public class Animation
{
string name;
public Sequence CurrentSequence { get; private set; }
int frame = 0;
bool backwards = false;
bool tickAlways;
Func<int> facingFunc;
public string Name { get { return name; } }
public Animation( string name )
: this( name, () => 0 )
{
}
public Animation( string name, Func<int> facingFunc )
{
this.name = name.ToLowerInvariant();
this.tickFunc = () => { };
this.facingFunc = facingFunc;
}
public Sprite Image
{
get
{
return backwards
? CurrentSequence.GetSprite(CurrentSequence.End - frame - 1, facingFunc())
: CurrentSequence.GetSprite(frame, facingFunc());
}
}
public void Play( string sequenceName )
{
PlayThen(sequenceName, () => { });
}
public void PlayRepeating( string sequenceName )
{
PlayThen( sequenceName, () => PlayRepeating( CurrentSequence.Name ) );
}
public void ReplaceAnim(string sequenceName)
{
CurrentSequence = SequenceProvider.GetSequence(name, sequenceName);
frame %= CurrentSequence.Length;
}
public void PlayThen( string sequenceName, Action after )
{
after = after ?? ( () => { } );
backwards = false;
tickAlways = false;
CurrentSequence = SequenceProvider.GetSequence( name, sequenceName );
frame = 0;
tickFunc = () =>
{
++frame;
if( frame >= CurrentSequence.Length )
{
frame = CurrentSequence.Length - 1;
tickFunc = () => { };
after();
}
};
}
public void PlayBackwardsThen(string sequenceName, Action after)
{
PlayThen(sequenceName, after);
backwards = true;
}
public void PlayFetchIndex( string sequenceName, Func<int> func )
{
backwards = false;
tickAlways = true;
CurrentSequence = SequenceProvider.GetSequence( name, sequenceName );
frame = func();
tickFunc = () => frame = func();
}
int timeUntilNextFrame;
Action tickFunc;
public void Tick()
{
Tick( 40 ); // tick one frame
}
public bool HasSequence(string seq) { return SequenceProvider.HasSequence( name, seq ); }
public void Tick( int t )
{
if( tickAlways )
tickFunc();
else
{
timeUntilNextFrame -= t;
while( timeUntilNextFrame <= 0 )
{
tickFunc();
timeUntilNextFrame += 40; // 25 fps == 40 ms
}
}
}
public void ChangeImage(string newImage)
{
newImage = newImage.ToLowerInvariant();
if (name != newImage)
{
name = newImage.ToLowerInvariant();
ReplaceAnim(CurrentSequence.Name);
}
}
public Sequence GetSequence( string sequenceName )
{
return SequenceProvider.GetSequence( name, sequenceName );
}
}
}

View File

@@ -0,0 +1,97 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
static class ChromeProvider
{
static Dictionary<string, Dictionary<string, MappedImage>> collections;
static Dictionary<string, Sheet> cachedSheets;
static Dictionary<string, Dictionary<string, Sprite>> cachedSprites;
public static void Initialize(params string[] chromeFiles)
{
collections = new Dictionary<string, Dictionary<string, MappedImage>>();
cachedSheets = new Dictionary<string, Sheet>();
cachedSprites = new Dictionary<string, Dictionary<string, Sprite>>();
foreach (var f in chromeFiles)
LoadChromeSource(f);
}
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<XmlElement>()
.Select(e => new MappedImage(defaultSrc, e))
.ToDictionary(s => s.Name);
collections.Add(elementName, images);
}
public static Sprite GetImage(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<string, Sprite>());
cachedSprites[collection].Add(image, mi.GetImage(renderer, sheet));
return cachedSprites[collection][image];
}
}
}

View File

@@ -0,0 +1,61 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Xml;
namespace OpenRA.Graphics
{
class CursorSequence
{
readonly int start, length;
public int Start { get { return start; } }
public int End { get { return start + length; } }
public int Length { get { return length; } }
public readonly int2 Hotspot;
Sprite[] sprites;
public CursorSequence(string cursorSrc, XmlElement e)
{
sprites = CursorSheetBuilder.LoadAllSprites(cursorSrc);
start = int.Parse(e.GetAttribute("start"));
if (e.GetAttribute("length") == "*" || e.GetAttribute("end") == "*")
length = sprites.Length - start;
else if (e.HasAttribute("length"))
length = int.Parse(e.GetAttribute("length"));
else if (e.HasAttribute("end"))
length = int.Parse(e.GetAttribute("end")) - start;
else
length = 1;
int.TryParse( e.GetAttribute("x"), out Hotspot.X );
int.TryParse( e.GetAttribute("y"), out Hotspot.Y );
}
public Sprite GetSprite(int frame)
{
return sprites[(frame % length) + start];
}
}
}

View File

@@ -0,0 +1,48 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
static class CursorSheetBuilder
{
static Cache<string, Sprite[]> cursors = new Cache<string, Sprite[]>(LoadCursors);
static readonly string[] exts = { ".shp" };
static Sprite[] LoadCursors(string filename)
{
try
{
var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts));
return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, a.Size)).ToArray();
}
catch (IndexOutOfRangeException) // This will occur when loading a custom (RA-format) .shp
{
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, shp.Size)).ToArray();
}
}
public static Sprite[] LoadAllSprites(string filename) { return cursors[filename]; }
}
}

View File

@@ -0,0 +1,86 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
class HardwarePalette : Sheet
{
public const int MaxPalettes = 64;
int allocated = 0;
// We need to store the Palettes themselves for the remap palettes to work
// We should probably try to fix this somehow
static Dictionary<string, Palette> palettes;
static Dictionary<string, int> indices;
public HardwarePalette(Renderer renderer, Map map)
: base(renderer,new Size(256, MaxPalettes))
{
palettes = new Dictionary<string, Palette>();
indices = new Dictionary<string, int>();
}
public Palette GetPalette(string name)
{
try { return palettes[name]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Palette `{0}` does not exist".F(name));
}
}
public int GetPaletteIndex(string name)
{
try { return indices[name]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Palette `{0}` does not exist".F(name));
}
}
public int AddPalette(string name, Palette p)
{
palettes.Add(name, p);
indices.Add(name, allocated);
for (int i = 0; i < 256; i++)
{
this[new Point(i, allocated)] = p.GetColor(i);
}
return allocated++;
}
public void Update(IEnumerable<IPaletteModifier> paletteMods)
{
var b = new Bitmap(Bitmap);
foreach (var mod in paletteMods)
mod.AdjustPalette(b);
Texture.SetData(b);
Game.renderer.PaletteTexture = Texture;
}
}
}

View File

@@ -0,0 +1,89 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Drawing;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
class LineRenderer
{
Renderer renderer;
IVertexBuffer<Vertex> vertexBuffer;
IIndexBuffer indexBuffer; /* kindof a waste of space, but the GPU likes indexing, oh well */
const int linesPerBatch = 1024;
Vertex[] vertices = new Vertex[ 2 * linesPerBatch ];
ushort[] indices = new ushort[ 2 * linesPerBatch ];
int lines = 0;
int nv = 0, ni = 0;
public LineRenderer( Renderer renderer )
{
this.renderer = renderer;
vertexBuffer = renderer.Device.CreateVertexBuffer(vertices.Length );
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
}
public void Flush()
{
if( lines > 0 )
{
renderer.LineShader.Render( () =>
{
vertexBuffer.SetData( vertices );
indexBuffer.SetData( indices );
renderer.DrawBatch( vertexBuffer, indexBuffer,
nv, ni / 2, null, PrimitiveType.LineList );
} );
nv = 0; ni = 0;
lines = 0;
}
}
public void DrawLine( float2 start, float2 end, Color startColor, Color endColor )
{
indices[ ni++ ] = (ushort)nv;
vertices[ nv++ ] = new Vertex( start,
new float2( startColor.R / 255.0f, startColor.G / 255.0f ),
new float2( startColor.B / 255.0f, startColor.A / 255.0f ) );
indices[ ni++ ] = (ushort)nv;
vertices[ nv++ ] = new Vertex( end,
new float2( endColor.R / 255.0f, endColor.G / 255.0f ),
new float2( endColor.B / 255.0f, endColor.A / 255.0f ) );
if( ++lines >= linesPerBatch )
Flush();
}
public void FillRect( RectangleF r, Color color )
{
for (float y = r.Top; y < r.Bottom; y++)
{
DrawLine(new float2(r.Left, y), new float2(r.Right, y), color, color);
}
}
}
}

View File

@@ -0,0 +1,51 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Drawing;
using System.IO;
using System.Xml;
namespace OpenRA.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);
}
}
}

View File

@@ -0,0 +1,213 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
class Minimap
{
readonly World world;
Sheet sheet, mapOnlySheet;
SpriteRenderer rgbaRenderer;
LineRenderer lineRenderer;
Sprite sprite, mapOnlySprite;
Bitmap terrain, oreLayer;
Rectangle bounds;
Sprite ownedSpawnPoint;
Sprite unownedSpawnPoint;
const int alpha = 230;
public void Tick() { }
public Minimap(World world, Renderer r)
{
this.world = world;
sheet = new Sheet(r, new Size(128, 128));
mapOnlySheet = new Sheet(r, new Size(128, 128));
lineRenderer = new LineRenderer(r);
rgbaRenderer = new SpriteRenderer(r, true, r.RgbaSpriteShader);
var size = Math.Max(world.Map.Width, world.Map.Height);
var dw = (size - world.Map.Width) / 2;
var dh = (size - world.Map.Height) / 2;
bounds = new Rectangle(world.Map.Offset.X - dw, world.Map.Offset.Y - dh, size, size);
sprite = new Sprite(sheet, bounds, TextureChannel.Alpha);
mapOnlySprite = new Sprite(mapOnlySheet, bounds, TextureChannel.Alpha);
shroudColor = Color.FromArgb(alpha, Color.Black);
ownedSpawnPoint = ChromeProvider.GetImage(r, "spawnpoints", "owned");
unownedSpawnPoint = ChromeProvider.GetImage(r, "spawnpoints", "unowned");
}
public static Rectangle MakeMinimapBounds(Map m)
{
var size = Math.Max(m.Width, m.Height);
var dw = (size - m.Width) / 2;
var dh = (size - m.Height) / 2;
return new Rectangle(m.Offset.X - dw, m.Offset.Y - dh, size, size);
}
static Cache<string, Color[]> terrainTypeColors = new Cache<string, Color[]>(
theater =>
{
var pal = Game.world.WorldRenderer.GetPalette("terrain");
return new[] {
theater == "snow" ? 0xe3 :0x1a,
0x63, 0x2f, 0x1f, 0x14, 0x64, 0x1f, 0x68, 0x6b, 0x6d, 0x88 }
.Select(a => Color.FromArgb(alpha, pal.GetColor(a))).ToArray();
});
static Color shroudColor;
public void InvalidateOre() { oreLayer = null; }
public static Bitmap RenderTerrainBitmap(Map map, TileSet tileset)
{
var colors = terrainTypeColors[map.Theater.ToLowerInvariant()];
var terrain = new Bitmap(128, 128);
for (var y = 0; y < 128; y++)
for (var x = 0; x < 128; x++)
terrain.SetPixel(x, y, map.IsInMap(x, y)
? colors[tileset.GetWalkability(map.MapTiles[x, y])]
: shroudColor);
return terrain;
}
public static Bitmap RenderTerrainBitmapWithSpawnPoints(Map map, TileSet tileset)
{
/* todo: do this a bit nicer */
var terrain = RenderTerrainBitmap(map, tileset);
foreach (var sp in map.SpawnPoints)
terrain.SetPixel(sp.X, sp.Y, Color.White);
return terrain;
}
public void Update()
{
if (terrain == null)
terrain = RenderTerrainBitmap(world.Map, world.TileSet);
if (oreLayer == null)
{
var colors = terrainTypeColors[world.Map.Theater.ToLowerInvariant()];
oreLayer = new Bitmap(terrain);
for (var y = world.Map.YOffset; y < world.Map.YOffset + world.Map.Height; y++)
for (var x = world.Map.XOffset; x < world.Map.XOffset + world.Map.Width; x++)
if (world.Map.ContainsResource(new int2(x, y)))
oreLayer.SetPixel(x, y, colors[(int)TerrainMovementType.Ore]);
}
mapOnlySheet.Texture.SetData(oreLayer);
if (!world.Queries.OwnedBy[world.LocalPlayer].WithTrait<ProvidesRadar>().Any())
return;
var bitmap = new Bitmap(oreLayer);
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height),
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
unsafe
{
var colors = terrainTypeColors[world.Map.Theater.ToLowerInvariant()];
int* c = (int*)bitmapData.Scan0;
foreach (var a in world.Queries.WithTrait<Unit>().Where( a => a.Actor.Owner != null ))
*(c + (a.Actor.Location.Y * bitmapData.Stride >> 2) + a.Actor.Location.X) =
Color.FromArgb(alpha, a.Actor.Owner.Color).ToArgb();
for (var y = world.Map.YOffset; y < world.Map.YOffset + world.Map.Height; y++)
for (var x = world.Map.XOffset; x < world.Map.XOffset + world.Map.Width; x++)
{
if (!world.LocalPlayer.Shroud.DisplayOnRadar(x, y))
{
*(c + (y * bitmapData.Stride >> 2) + x) = shroudColor.ToArgb();
continue;
}
var b = world.WorldActor.traits.Get<BuildingInfluence>().GetBuildingAt(new int2(x, y));
if (b != null)
*(c + (y * bitmapData.Stride >> 2) + x) =
(b.Owner != null ? Color.FromArgb(alpha, b.Owner.Color) : colors[4]).ToArgb();
}
}
bitmap.UnlockBits(bitmapData);
sheet.Texture.SetData(bitmap);
}
public void Draw(RectangleF rect, bool mapOnly)
{
rgbaRenderer.DrawSprite(mapOnly ? mapOnlySprite : sprite,
new float2(rect.X, rect.Y), "chrome", new float2(rect.Width, rect.Height));
rgbaRenderer.Flush();
}
int2 TransformCellToMinimapPixel(RectangleF viewRect, int2 p)
{
var fx = (float)(p.X - bounds.X) / bounds.Width;
var fy = (float)(p.Y - bounds.Y) / bounds.Height;
return new int2(
(int)(viewRect.Width * fx + viewRect.Left) - 8,
(int)(viewRect.Height * fy + viewRect.Top) - 8);
}
public void DrawSpawnPoints(RectangleF rect)
{
var points = world.Map.SpawnPoints
.Select( (sp,i) => Pair.New(sp,world.players.Values.FirstOrDefault(
p => p.SpawnPointIndex == i + 1 ) ))
.ToList();
foreach (var p in points)
{
var pos = TransformCellToMinimapPixel(rect, p.First);
if (p.Second == null)
rgbaRenderer.DrawSprite(unownedSpawnPoint, pos, "chrome");
else
{
lineRenderer.FillRect(new RectangleF(
Game.viewport.Location.X + pos.X + 2,
Game.viewport.Location.Y + pos.Y + 2,
12, 12), p.Second.Color);
rgbaRenderer.DrawSprite(ownedSpawnPoint, pos, "chrome");
}
}
lineRenderer.Flush();
rgbaRenderer.Flush();
}
}
}

View File

@@ -0,0 +1,114 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
class OverlayRenderer
{
static string[] overlaySpriteNames =
{
"sbag", "cycl", "brik", "fenc", "wood",
"gold01", "gold02", "gold03", "gold04",
"gem01", "gem02", "gem03", "gem04",
"v12", "v13", "v14", "v15", "v16", "v17", "v18",
"fpls", "wcrate", "scrate", "barb", "sbag",
};
static string[] smudgeSpriteNames =
{
"bib3", "bib2", "bib1", "sc1", "sc2", "sc3", "sc4", "sc5", "sc6",
"cr1", "cr2", "cr3", "cr4", "cr5", "cr6",
};
readonly Sprite[][] overlaySprites;
readonly Sprite[] smudgeSprites;
SpriteRenderer spriteRenderer;
Map map;
public OverlayRenderer( Renderer renderer, Map map )
{
this.spriteRenderer = new SpriteRenderer( renderer, true );
this.map = map;
overlaySprites = overlaySpriteNames.Select(f => SpriteSheetBuilder.LoadAllSprites(f)).ToArray();
smudgeSprites = smudgeSpriteNames.SelectMany(f => SpriteSheetBuilder.LoadAllSprites(f)).ToArray();
}
public void Draw()
{
var shroud = Game.world.LocalPlayer.Shroud;
for (int y = map.YOffset; y < map.YOffset + map.Height; y++)
for (int x = map.XOffset; x < map.XOffset + map.Width; x++)
{
if (!shroud.IsExplored(new int2(x,y))) continue;
var tr = map.MapTiles[x,y];
if (tr.smudge != 0 && tr.smudge <= smudgeSprites.Length)
{
var location = new int2(x, y);
spriteRenderer.DrawSprite(smudgeSprites[tr.smudge - 1],
Game.CellSize * (float2)location, "terrain");
}
var o = tr.overlay;
if (o < overlaySprites.Length)
{
var location = new int2(x, y);
var sprites = overlaySprites[o];
var spriteIndex = 0;
if (Ore.overlayIsFence[o]) spriteIndex = NearbyFences(x, y);
else if (Ore.overlayIsOre[o]) spriteIndex = map.MapTiles[x,y].density - 1;
else if (Ore.overlayIsGems[o]) spriteIndex = map.MapTiles[x,y].density - 1;
spriteRenderer.DrawSprite(sprites[spriteIndex],
Game.CellSize * (float2)location, "terrain");
}
}
spriteRenderer.Flush();
}
bool IsFence( int x, int y )
{
var o = map.MapTiles[ x, y ].overlay;
if (o < Ore.overlayIsFence.Length)
return Ore.overlayIsFence[o];
return false;
}
int NearbyFences( int x, int y )
{
int ret = 0;
if( IsFence( x, y - 1 ) )
ret |= 1;
if( IsFence( x + 1, y ) )
ret |= 2;
if( IsFence( x, y + 1 ) )
ret |= 4;
if( IsFence( x - 1, y ) )
ret |= 8;
return ret;
}
}
}

View File

@@ -0,0 +1,134 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Drawing;
using System.Drawing.Text;
using System.Reflection;
using System.Windows.Forms;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
using OpenRA.Support;
using System.IO;
namespace OpenRA.Graphics
{
internal class Renderer
{
internal static int SheetSize;
readonly IGraphicsDevice device;
public IShader SpriteShader { get; private set; } /* note: shared shader params */
public IShader LineShader { get; private set; }
public IShader RgbaSpriteShader { get; private set; }
public IShader WorldSpriteShader { get; private set; }
public ITexture PaletteTexture;
public readonly SpriteFont RegularFont, BoldFont;
public Size Resolution { get { return device.WindowSize; } }
public Renderer(Size resolution, bool windowed)
{
device = CreateDevice( Assembly.LoadFile( Path.GetFullPath( "OpenRA.Gl.dll" ) ), resolution.Width, resolution.Height, windowed, false );
SpriteShader = device.CreateShader(FileSystem.Open("shaders/world-shp.fx"));
LineShader = device.CreateShader(FileSystem.Open("shaders/line.fx"));
RgbaSpriteShader = device.CreateShader(FileSystem.Open("shaders/chrome-rgba.fx"));
WorldSpriteShader = device.CreateShader(FileSystem.Open("shaders/chrome-shp.fx"));
// RegularFont = device.CreateFont( "FreeSans.ttf" );
// BoldFont = device.CreateFont( "FreeSansBold.ttf" );
RegularFont = new SpriteFont(this, "FreeSans.ttf", 14);
BoldFont = new SpriteFont(this, "FreeSansBold.ttf", 14);
}
IGraphicsDevice CreateDevice( Assembly rendererDll, int width, int height, bool windowed, bool vsync )
{
foreach( RendererAttribute r in rendererDll.GetCustomAttributes( typeof( RendererAttribute ), false ) )
{
return (IGraphicsDevice)r.Type.GetConstructor( new Type[] { typeof( int ), typeof( int ), typeof( bool ), typeof( bool ) } )
.Invoke( new object[] { width, height, windowed, vsync } );
}
throw new NotImplementedException();
}
public IGraphicsDevice Device { get { return device; } }
public void BeginFrame(float2 r1, float2 r2, float2 scroll)
{
device.Begin();
device.Clear(Color.Black);
SetShaderParams( SpriteShader, r1, r2, scroll );
SetShaderParams( LineShader, r1, r2, scroll );
SetShaderParams( RgbaSpriteShader, r1, r2, scroll );
SetShaderParams( WorldSpriteShader, r1, r2, scroll );
}
private void SetShaderParams( IShader s, float2 r1, float2 r2, float2 scroll )
{
s.SetValue( "Palette", PaletteTexture );
s.SetValue( "Scroll", scroll.X, scroll.Y );
s.SetValue( "r1", r1.X, r1.Y );
s.SetValue( "r2", r2.X, r2.Y );
s.Commit();
}
public void EndFrame()
{
device.End();
device.Present();
}
public void DrawBatch<T>(IVertexBuffer<T> vertices, IIndexBuffer indices,
Range<int> vertexRange, Range<int> indexRange, ITexture texture, PrimitiveType type, IShader shader)
where T : struct
{
shader.SetValue("DiffuseTexture", texture);
shader.Commit();
vertices.Bind();
indices.Bind();
device.DrawIndexedPrimitives(type, vertexRange, indexRange);
PerfHistory.Increment("batches", 1);
}
public void DrawBatch<T>(IVertexBuffer<T> vertices, IIndexBuffer indices,
int vertexPool, int numPrimitives, ITexture texture, PrimitiveType type)
where T : struct
{
SpriteShader.SetValue("DiffuseTexture", texture);
SpriteShader.Commit();
vertices.Bind();
indices.Bind();
device.DrawIndexedPrimitives(type, vertexPool, numPrimitives);
PerfHistory.Increment("batches", 1);
}
}
}

View File

@@ -0,0 +1,70 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Xml;
namespace OpenRA.Graphics
{
public class Sequence
{
readonly Sprite[] sprites;
readonly int start, length, facings;
public readonly string Name;
public int Start { get { return start; } }
public int End { get { return start + length; } }
public int Length { get { return length; } }
public int Facings { get { return facings; } }
public Sequence(string unit, XmlElement e)
{
string srcOverride = e.GetAttribute("src");
Name = e.GetAttribute("name");
sprites = SpriteSheetBuilder.LoadAllSprites(string.IsNullOrEmpty(srcOverride) ? unit : srcOverride );
start = int.Parse(e.GetAttribute("start"));
if (e.GetAttribute("length") == "*" || e.GetAttribute("end") == "*")
length = sprites.Length - Start;
else if (e.HasAttribute("length"))
length = int.Parse(e.GetAttribute("length"));
else if (e.HasAttribute("end"))
length = int.Parse(e.GetAttribute("end")) - int.Parse(e.GetAttribute("start"));
else
length = 1;
if( e.HasAttribute( "facings" ) )
facings = int.Parse( e.GetAttribute( "facings" ) );
else
facings = 1;
}
public Sprite GetSprite( int frame )
{
return GetSprite( frame, 0 );
}
public Sprite GetSprite(int frame, int facing)
{
var f = Traits.Util.QuantizeFacing( facing, facings );
return sprites[ (f * length) + ( frame % length ) + start ];
}
}
}

View File

@@ -0,0 +1,101 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
static class SequenceProvider
{
static Dictionary<string, Dictionary<string, Sequence>> units;
static Dictionary<string, CursorSequence> cursors;
public static void Initialize(params string[] sequenceFiles)
{
units = new Dictionary<string, Dictionary<string, Sequence>>();
cursors = new Dictionary<string, CursorSequence>();
foreach (var f in sequenceFiles)
LoadSequenceSource(f);
}
static void LoadSequenceSource(string filename)
{
XmlDocument document = new XmlDocument();
document.Load(FileSystem.Open(filename));
foreach (XmlElement eUnit in document.SelectNodes("/sequences/unit"))
LoadSequencesForUnit(eUnit);
foreach (XmlElement eCursor in document.SelectNodes("/sequences/cursor"))
LoadSequencesForCursor(eCursor);
}
static void LoadSequencesForCursor(XmlElement eCursor)
{
string cursorSrc = eCursor.GetAttribute("src");
foreach (XmlElement eSequence in eCursor.SelectNodes("./sequence"))
cursors.Add(eSequence.GetAttribute("name"), new CursorSequence(cursorSrc, eSequence));
Log.Write("* LoadSequencesForCursor() done");
}
static void LoadSequencesForUnit(XmlElement eUnit)
{
string unitName = eUnit.GetAttribute("name");
Log.Write("Loading sequence {0}", unitName);
var sequences = eUnit.SelectNodes("./sequence").OfType<XmlElement>()
.Select(e => new Sequence(unitName, e))
.ToDictionary(s => s.Name);
units.Add(unitName, sequences);
}
public static Sequence GetSequence(string unitName, string sequenceName)
{
try { return units[unitName][sequenceName]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Unit `{0}` does not have a sequence `{1}`".F(unitName, sequenceName));
}
}
public static bool HasSequence(string unit, string seq)
{
return units[unit].ContainsKey(seq);
}
public static CursorSequence GetCursorSequence(string cursor)
{
try { return cursors[cursor]; }
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Cursor does not have a sequence `{0}`".F(cursor));
}
}
}
}

View File

@@ -0,0 +1,72 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
public class Sheet
{
readonly Renderer renderer;
protected readonly Bitmap bitmap;
ITexture texture;
internal Sheet(Renderer renderer, Size size)
{
this.renderer = renderer;
this.bitmap = new Bitmap(size.Width, size.Height);
}
internal Sheet(Renderer renderer, string filename)
{
this.renderer = renderer;
this.bitmap = (Bitmap)Image.FromStream(FileSystem.Open(filename));
}
void Resolve()
{
texture = renderer.Device.CreateTexture(bitmap);
}
public ITexture Texture
{
get
{
if (texture == null)
Resolve();
return texture;
}
}
public Size Size { get { return bitmap.Size; } }
protected Color this[Point p]
{
get { return bitmap.GetPixel(p.X, p.Y); }
set { bitmap.SetPixel(p.X, p.Y, value); }
}
public Bitmap Bitmap { get { return bitmap; } } // for perf
}
}

View File

@@ -0,0 +1,119 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Drawing;
namespace OpenRA.Graphics
{
class SheetBuilder
{
public static SheetBuilder SharedInstance;
public static void Initialize(Renderer r)
{
SharedInstance = new SheetBuilder(r, TextureChannel.Red);
}
public SheetBuilder(Renderer r, TextureChannel ch)
{
renderer = r;
current = null;
rowHeight = 0;
channel = null;
initialChannel = ch;
}
public Sprite Add(byte[] src, Size size)
{
Sprite rect = Allocate(size);
Util.FastCopyIntoChannel(rect, src);
return rect;
}
public Sprite Add(Size size, byte paletteIndex)
{
byte[] data = new byte[size.Width * size.Height];
for (int i = 0; i < data.Length; i++)
data[i] = paletteIndex;
return Add(data, size);
}
Sheet NewSheet() { return new Sheet( renderer, new Size( Renderer.SheetSize, Renderer.SheetSize ) ); }
Renderer renderer;
Sheet current = null;
int rowHeight = 0;
Point p;
TextureChannel? channel = null;
TextureChannel initialChannel;
TextureChannel? NextChannel(TextureChannel? t)
{
if (t == null)
return initialChannel;
switch (t.Value)
{
case TextureChannel.Red: return TextureChannel.Green;
case TextureChannel.Green: return TextureChannel.Blue;
case TextureChannel.Blue: return TextureChannel.Alpha;
case TextureChannel.Alpha: return null;
default: return null;
}
}
public Sprite Allocate(Size imageSize)
{
if (current == null)
{
current = NewSheet();
channel = NextChannel(null);
}
if (imageSize.Width + p.X > current.Size.Width)
{
p = new Point(0, p.Y + rowHeight);
rowHeight = imageSize.Height;
}
if (imageSize.Height > rowHeight)
rowHeight = imageSize.Height;
if (p.Y + imageSize.Height > current.Size.Height)
{
if (null == (channel = NextChannel(channel)))
{
current = NewSheet();
channel = NextChannel(channel);
}
rowHeight = imageSize.Height;
p = new Point(0,0);
}
Sprite rect = new Sprite(current, new Rectangle(p, imageSize), channel.Value);
p.X += imageSize.Width;
return rect;
}
}
}

View File

@@ -0,0 +1,71 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Drawing;
namespace OpenRA.Graphics
{
public class Sprite
{
public readonly Rectangle bounds;
public readonly Sheet sheet;
public readonly TextureChannel channel;
public readonly RectangleF uv;
public readonly float2 size;
readonly float2[] uvhax;
internal Sprite(Sheet sheet, Rectangle bounds, TextureChannel channel)
{
this.bounds = bounds;
this.sheet = sheet;
this.channel = channel;
uv = new RectangleF(
(float)(bounds.Left) / sheet.Size.Width,
(float)(bounds.Top) / sheet.Size.Height,
(float)(bounds.Width) / sheet.Size.Width,
(float)(bounds.Height) / sheet.Size.Height);
uvhax = new float2[]
{
new float2( uv.Left, uv.Top ),
new float2( uv.Right, uv.Top ),
new float2( uv.Left, uv.Bottom ),
new float2( uv.Right, uv.Bottom ),
};
this.size = new float2(bounds.Size);
}
public float2 FastMapTextureCoords( int k )
{
return uvhax[ k ];
}
}
public enum TextureChannel
{
Red = 0,
Green = 1,
Blue = 2,
Alpha = 3,
}
}

View File

@@ -0,0 +1,116 @@
using System;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using Tao.FreeType;
using System.Runtime.InteropServices;
namespace OpenRA.Graphics
{
class SpriteFont
{
int size;
public SpriteFont(Renderer r, string name, int size)
{
this.size = size;
if (0 != FT.FT_New_Face(library, name, 0, out face))
throw new InvalidOperationException("FT_New_Face failed");
FT.FT_Set_Pixel_Sizes(face, 0, (uint)size);
glyphs = new Cache<char, GlyphInfo>(CreateGlyph);
// setup a 1-channel SheetBuilder for our private use
if (builder == null) builder = new SheetBuilder(r, TextureChannel.Alpha);
// precache glyphs for U+0020 - U+007f
for (var n = (char)0x20; n < (char)0x7f; n++)
if (glyphs[n] == null)
throw new InvalidOperationException();
}
public void DrawText(SpriteRenderer r, string text, float2 location, Color c)
{
location.Y += size; // baseline vs top
var p = location;
foreach (var s in text)
{
if (s == '\n')
{
location.Y += size;
p = location;
continue;
}
var g = glyphs[s];
r.DrawSprite(g.Sprite,
new float2(
(int)Math.Round(p.X + g.Offset.X, 0),
p.Y + g.Offset.Y),
"chrome");
p.X += g.Advance;
}
r.Flush();
}
public int2 Measure(string text)
{
return new int2((int)text.Split( '\n' ).Max( s => s.Sum(a => glyphs[a].Advance)), size);
}
Cache<char, GlyphInfo> glyphs;
IntPtr face;
GlyphInfo CreateGlyph(char c)
{
var index = FT.FT_Get_Char_Index(face, (uint)c);
FT.FT_Load_Glyph(face, index, FT.FT_LOAD_RENDER);
var _face = (FT_FaceRec)Marshal.PtrToStructure(face, typeof(FT_FaceRec));
var _glyph = (FT_GlyphSlotRec)Marshal.PtrToStructure(_face.glyph, typeof(FT_GlyphSlotRec));
var s = builder.Allocate(new Size(_glyph.metrics.width >> 6, _glyph.metrics.height >> 6));
var g = new GlyphInfo
{
Sprite = s,
Advance = _glyph.metrics.horiAdvance / 64f,
Offset = { X = _glyph.bitmap_left, Y = -_glyph.bitmap_top }
};
unsafe
{
var p = (byte*)_glyph.bitmap.buffer;
for (var j = 0; j < s.size.Y; j++)
{
for (var i = 0; i < s.size.X; i++)
if (p[i] != 0)
s.sheet.Bitmap.SetPixel(i + s.bounds.Left, j + s.bounds.Top,
Color.FromArgb(p[i], 0xff, 0xff, 0xff));
p += _glyph.bitmap.pitch;
}
}
return g;
}
static SpriteFont()
{
FT.FT_Init_FreeType(out library);
}
static IntPtr library;
static SheetBuilder builder;
}
class GlyphInfo
{
public float Advance;
public int2 Offset;
public Sprite Sprite;
}
}

View File

@@ -0,0 +1,91 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
class SpriteRenderer
{
IVertexBuffer<Vertex> vertexBuffer;
IIndexBuffer indexBuffer;
Renderer renderer;
IShader shader;
const int spritesPerBatch = 1024;
Vertex[] vertices = new Vertex[4 * spritesPerBatch];
ushort[] indices = new ushort[6 * spritesPerBatch];
Sheet currentSheet = null;
int sprites = 0;
int nv = 0, ni = 0;
public SpriteRenderer(Renderer renderer, bool allowAlpha, IShader shader)
{
this.renderer = renderer;
this.shader = shader;
vertexBuffer = renderer.Device.CreateVertexBuffer( vertices.Length );
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
}
public SpriteRenderer(Renderer renderer, bool allowAlpha)
: this(renderer, allowAlpha, renderer.SpriteShader) { }
public void Flush()
{
if (sprites > 0)
{
shader.SetValue( "DiffuseTexture", currentSheet.Texture );
shader.Render(() =>
{
vertexBuffer.SetData(vertices);
indexBuffer.SetData(indices);
renderer.DrawBatch(vertexBuffer, indexBuffer,
new Range<int>(0, nv),
new Range<int>(0, ni),
currentSheet.Texture, PrimitiveType.TriangleList,
shader);
});
nv = 0; ni = 0;
currentSheet = null;
sprites = 0;
}
}
public void DrawSprite(Sprite s, float2 location, string palette)
{
DrawSprite(s, location, palette, s.size);
}
public void DrawSprite(Sprite s, float2 location, string palette, float2 size)
{
if (s.sheet != currentSheet)
Flush();
currentSheet = s.sheet;
Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, Game.world.WorldRenderer.GetPaletteIndex(palette), nv, ni, size);
nv += 4; ni += 6;
if (++sprites >= spritesPerBatch)
Flush();
}
}
}

View File

@@ -0,0 +1,50 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Linq;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
static class SpriteSheetBuilder
{
public static void Initialize( Map map )
{
exts = new[] {
"." + map.Theater.Substring( 0, 3 ).ToLowerInvariant(),
".shp",
".tem",
".sno",
".int" };
sprites = new Cache<string, Sprite[]>( LoadSprites );
}
static Cache<string, Sprite[]> sprites;
static string[] exts;
static Sprite[] LoadSprites(string filename)
{
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
return shp.Select(a => SheetBuilder.SharedInstance.Add(a.Image, shp.Size)).ToArray();
}
public static Sprite[] LoadAllSprites(string filename) { return sprites[filename]; }
}
}

View File

@@ -0,0 +1,110 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
class TerrainRenderer
{
IVertexBuffer<Vertex> vertexBuffer;
IIndexBuffer indexBuffer;
Sheet terrainSheet;
Renderer renderer;
Map map;
OverlayRenderer overlayRenderer;
public TerrainRenderer(World world, Renderer renderer, WorldRenderer wr)
{
this.renderer = renderer;
this.map = world.Map;
Size tileSize = new Size( Game.CellSize, Game.CellSize );
var tileMapping = new Cache<TileReference, Sprite>(
x => SheetBuilder.SharedInstance.Add(world.TileSet.GetBytes(x), tileSize));
Vertex[] vertices = new Vertex[4 * map.Height * map.Width];
ushort[] indices = new ushort[6 * map.Height * map.Width];
int nv = 0;
int ni = 0;
for( int j = map.YOffset ; j < map.YOffset + map.Height ; j++ )
for( int i = map.XOffset ; i < map.XOffset + map.Width; i++ )
{
Sprite tile = tileMapping[map.MapTiles[i, j]];
// TODO: The zero below should explicitly refer to the terrain palette, but this code is called
// before the palettes are created
Util.FastCreateQuad(vertices, indices, Game.CellSize * new float2(i, j), tile, 0, nv, ni, tile.size);
nv += 4;
ni += 6;
}
terrainSheet = tileMapping[map.MapTiles[map.XOffset, map.YOffset]].sheet;
vertexBuffer = renderer.Device.CreateVertexBuffer( vertices.Length );
vertexBuffer.SetData( vertices );
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
indexBuffer.SetData( indices );
overlayRenderer = new OverlayRenderer( renderer, map );
}
public void Draw( Viewport viewport )
{
int indicesPerRow = map.Width * 6;
int verticesPerRow = map.Width * 4;
int visibleRows = (int)(viewport.Height / 24.0f + 2);
int firstRow = (int)((viewport.Location.Y) / 24.0f - map.YOffset);
int lastRow = firstRow + visibleRows;
if (lastRow < 0 || firstRow > map.Height)
return;
if (firstRow < 0) firstRow = 0;
if (lastRow > map.Height) lastRow = map.Height;
if (!Game.world.LocalPlayer.Shroud.HasGPS && Game.world.LocalPlayer.Shroud.bounds.HasValue)
{
var r = Game.world.LocalPlayer.Shroud.bounds.Value;
if (firstRow < r.Top - map.YOffset)
firstRow = r.Top - map.YOffset;
if (firstRow > r.Bottom - map.YOffset)
firstRow = r.Bottom - map.YOffset;
}
renderer.SpriteShader.SetValue( "DiffuseTexture", terrainSheet.Texture );
renderer.SpriteShader.Render(() =>
renderer.DrawBatch(vertexBuffer, indexBuffer,
new Range<int>(verticesPerRow * firstRow, verticesPerRow * lastRow),
new Range<int>(indicesPerRow * firstRow, indicesPerRow * lastRow),
terrainSheet.Texture, PrimitiveType.TriangleList, renderer.SpriteShader));
overlayRenderer.Draw();
}
}
}

View File

@@ -0,0 +1,131 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using OpenRA.FileFormats.Graphics;
namespace OpenRA.Graphics
{
static class Util
{
public static string[] ReadAllLines(Stream s)
{
List<string> result = new List<string>();
using (StreamReader reader = new StreamReader(s))
while(!reader.EndOfStream)
{
var line = reader.ReadLine();
if( !string.IsNullOrEmpty( line ) && line[0] != '#' )
result.Add( line );
}
return result.ToArray();
}
public static T[] MakeArray<T>(int count, Converter<int, T> f)
{
T[] result = new T[count];
for (int i = 0; i < count; i++)
result[i] = f(i);
return result;
}
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
public static void FastCreateQuad(Vertex[] vertices, ushort[] indices, float2 o, Sprite r, int palette, int nv, int ni, float2 size)
{
var attrib = new float2(palette / (float)HardwarePalette.MaxPalettes, channelSelect[(int)r.channel]);
vertices[nv] = new Vertex(o,
r.FastMapTextureCoords(0), attrib);
vertices[nv + 1] = new Vertex(new float2(o.X + size.X, o.Y),
r.FastMapTextureCoords(1), attrib);
vertices[nv + 2] = new Vertex(new float2(o.X, o.Y + size.Y),
r.FastMapTextureCoords(2), attrib);
vertices[nv + 3] = new Vertex(new float2(o.X + size.X, o.Y + size.Y),
r.FastMapTextureCoords(3), attrib);
indices[ni] = (ushort)(nv);
indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1);
indices[ni + 2] = indices[ni + 5] = (ushort)(nv + 2);
indices[ni + 4] = (ushort)(nv + 3);
}
public static void FastCopyIntoChannel(Sprite dest, byte[] src)
{
var bitmap = dest.sheet.Bitmap;
BitmapData bits = null;
uint[] channelMasks = { 0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000 };
int[] shifts = { 16, 8, 0, 24 };
uint mask = channelMasks[(int)dest.channel];
int shift = shifts[(int)dest.channel];
try
{
bits = bitmap.LockBits(dest.bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
int width = dest.bounds.Width;
int height = dest.bounds.Height;
unsafe
{
fixed (byte* srcbase = &src[0])
{
byte* s = srcbase;
uint* t = (uint*)bits.Scan0.ToPointer();
int stride = bits.Stride >> 2;
for (int j = 0; j < height; j++)
{
uint* p = t;
for (int i = 0; i < width; i++, p++)
*p = (*p & ~mask) | ((mask & ((uint)*s++) << shift));
t += stride;
}
}
}
}
finally
{
bitmap.UnlockBits(bits);
}
}
public static Color Lerp(float t, Color a, Color b)
{
return Color.FromArgb(
LerpChannel(t, a.A, b.A),
LerpChannel(t, a.R, b.R),
LerpChannel(t, a.G, b.G),
LerpChannel(t, a.B, b.B));
}
public static int LerpChannel(float t, int a, int b)
{
return (int)((1 - t) * a + t * b);
}
}
}

View File

@@ -0,0 +1,151 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Network;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
interface IHandleInput
{
bool HandleInput(World world, MouseInput mi);
}
class Viewport
{
readonly float2 screenSize;
float2 scrollPosition;
readonly Renderer renderer;
public float2 Location { get { return scrollPosition; } }
public int Width { get { return (int)screenSize.X; } }
public int Height { get { return (int)screenSize.Y; } }
SpriteRenderer cursorRenderer;
int2 mousePos;
float cursorFrame = 0f;
public void Scroll(float2 delta)
{
scrollPosition = scrollPosition + delta;
}
public IEnumerable<IHandleInput> regions { get { return new IHandleInput[] { Game.chrome, Game.controller }; } }
public Viewport(float2 screenSize, int2 mapStart, int2 mapEnd, Renderer renderer)
{
this.screenSize = screenSize;
this.renderer = renderer;
cursorRenderer = new SpriteRenderer(renderer, true);
this.scrollPosition = Game.CellSize* mapStart;
}
public void DrawRegions( World world )
{
world.WorldRenderer.palette.Update(world.Queries.WithTraitMultiple<IPaletteModifier>().Select(t=>t.Trait));
float2 r1 = new float2(2, -2) / screenSize;
float2 r2 = new float2(-1, 1);
renderer.BeginFrame(r1, r2, scrollPosition);
if( Game.orderManager.GameStarted )
{
world.WorldRenderer.Draw();
Game.chrome.Draw( world );
if( Game.orderManager.Connection.ConnectionState == ConnectionState.NotConnected )
Game.chrome.DrawDialog("Connection lost.");
}
else
{
// what a hack. as soon as we have some real chrome stuff...
switch( Game.orderManager.Connection.ConnectionState )
{
case ConnectionState.Connecting:
Game.chrome.DrawDialog("Connecting to {0}:{1}...".F( Game.Settings.NetworkHost, Game.Settings.NetworkPort ));
break;
case ConnectionState.NotConnected:
Game.chrome.DrawDialog("Connection failed.");
break;
case ConnectionState.Connected:
Game.chrome.DrawLobby( world );
break;
}
}
var cursorName = Game.chrome.HitTest(mousePos) ? "default" : Game.controller.ChooseCursor( world );
var c = new Cursor(cursorName);
cursorRenderer.DrawSprite(c.GetSprite((int)cursorFrame), mousePos + Location - c.GetHotspot(), "cursor");
cursorRenderer.Flush();
renderer.EndFrame();
}
public void Tick()
{
cursorFrame += 0.5f;
}
IHandleInput dragRegion = null;
public void DispatchMouseInput(World world, MouseInput mi)
{
if (mi.Event == MouseInputEvent.Move)
mousePos = mi.Location;
if (dragRegion != null) {
dragRegion.HandleInput( world, mi );
if (mi.Event == MouseInputEvent.Up) dragRegion = null;
return;
}
dragRegion = regions.FirstOrDefault(r => r.HandleInput(world, mi));
if (mi.Event != MouseInputEvent.Down)
dragRegion = null;
}
public float2 ViewToWorld(MouseInput mi)
{
return (1 / 24.0f) * (new float2(mi.Location.X, mi.Location.Y) + Location);
}
public void Center(IEnumerable<Actor> actors)
{
if (!actors.Any()) return;
var avgPos = (1f / actors.Count()) * actors
.Select(a => a.CenterLocation)
.Aggregate((a, b) => a + b);
scrollPosition = (avgPos - .5f * new float2(Width, Height)).ToInt2();
}
public void GoToStartLocation( Player player )
{
Center( player.World.Queries.OwnedBy[ player ].WithTrait<Selectable>().Select( a => a.Actor ) );
}
}
}

View File

@@ -0,0 +1,315 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it 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.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
public class WorldRenderer
{
readonly World world;
internal readonly TerrainRenderer terrainRenderer;
internal readonly SpriteRenderer spriteRenderer;
internal readonly LineRenderer lineRenderer;
internal readonly UiOverlay uiOverlay;
internal readonly Renderer renderer;
internal readonly HardwarePalette palette;
public static bool ShowUnitPaths = false;
internal WorldRenderer(World world, Renderer renderer)
{
this.world = world;
this.renderer = renderer;
terrainRenderer = new TerrainRenderer(world, renderer, this);
spriteRenderer = new SpriteRenderer(renderer, true);
lineRenderer = new LineRenderer(renderer);
uiOverlay = new UiOverlay(spriteRenderer);
palette = new HardwarePalette(renderer, world.Map);
Log.Write("Created worldrenderer");
}
public int GetPaletteIndex(string name)
{
return palette.GetPaletteIndex(name);
}
public Palette GetPalette(string name)
{
return palette.GetPalette(name);
}
public void AddPalette(string name, Palette pal)
{
palette.AddPalette(name, pal);
}
void DrawSpriteList(RectangleF rect,
IEnumerable<Renderable> images)
{
foreach (var image in images)
{
var loc = image.Pos;
if (loc.X > rect.Right || loc.X < rect.Left - image.Sprite.bounds.Width)
continue;
if (loc.Y > rect.Bottom || loc.Y < rect.Top - image.Sprite.bounds.Height)
continue;
spriteRenderer.DrawSprite(image.Sprite, loc, image.Palette);
}
}
class SpriteComparer : IComparer<Renderable>
{
public int Compare(Renderable x, Renderable y)
{
var result = x.ZOffset.CompareTo(y.ZOffset);
if (result == 0)
result = x.Pos.Y.CompareTo(y.Pos.Y);
return result;
}
}
Rectangle GetBoundsRect()
{
if (!world.LocalPlayer.Shroud.HasGPS && world.LocalPlayer.Shroud.bounds.HasValue)
{
var r = world.LocalPlayer.Shroud.bounds.Value;
var left = (int)(Game.CellSize * r.Left - Game.viewport.Location.X);
var top = (int)(Game.CellSize * r.Top - Game.viewport.Location.Y);
var right = left + (int)(Game.CellSize * r.Width);
var bottom = top + (int)(Game.CellSize * r.Height);
if (left < 0) left = 0;
if (top < 0) top = 0;
if (right > Game.viewport.Width) right = Game.viewport.Width;
if (bottom > Game.viewport.Height) bottom = Game.viewport.Height;
return new Rectangle(left, top, right - left, bottom - top);
}
else
return new Rectangle(0, 0, Game.viewport.Width, Game.viewport.Height);
}
public void Draw()
{
var bounds = GetBoundsRect();
renderer.Device.EnableScissor(bounds.Left, bounds.Top, bounds.Width, bounds.Height);
terrainRenderer.Draw(Game.viewport);
var comparer = new SpriteComparer();
bounds.Offset((int)Game.viewport.Location.X, (int)Game.viewport.Location.Y);
var renderables = world.Actors.SelectMany(a => a.Render())
.OrderBy(r => r, comparer);
DrawSpriteList(bounds, renderables);
foreach (var e in world.Effects)
DrawSpriteList(bounds, e.Render());
uiOverlay.Draw( world );
spriteRenderer.Flush();
DrawBandBox();
if (Game.controller.orderGenerator != null)
Game.controller.orderGenerator.Render( world );
world.LocalPlayer.Shroud.Draw(spriteRenderer);
lineRenderer.Flush();
spriteRenderer.Flush();
}
void DrawBandBox()
{
var selbox = Game.controller.SelectionBox;
if (selbox == null) return;
var a = selbox.Value.First;
var b = new float2(selbox.Value.Second.X - a.X, 0);
var c = new float2(0, selbox.Value.Second.Y - a.Y);
lineRenderer.DrawLine(a, a + b, Color.White, Color.White);
lineRenderer.DrawLine(a + b, a + b + c, Color.White, Color.White);
lineRenderer.DrawLine(a + b + c, a + c, Color.White, Color.White);
lineRenderer.DrawLine(a, a + c, Color.White, Color.White);
foreach (var u in world.SelectActorsInBox(selbox.Value.First, selbox.Value.Second))
DrawSelectionBox(u, Color.Yellow, false);
}
public void DrawSelectionBox(Actor selectedUnit, Color c, bool drawHealthBar)
{
var bounds = selectedUnit.GetBounds(true);
var xy = new float2(bounds.Left, bounds.Top);
var Xy = new float2(bounds.Right, bounds.Top);
var xY = new float2(bounds.Left, bounds.Bottom);
var XY = new float2(bounds.Right, bounds.Bottom);
lineRenderer.DrawLine(xy, xy + new float2(4, 0), c, c);
lineRenderer.DrawLine(xy, xy + new float2(0, 4), c, c);
lineRenderer.DrawLine(Xy, Xy + new float2(-4, 0), c, c);
lineRenderer.DrawLine(Xy, Xy + new float2(0, 4), c, c);
lineRenderer.DrawLine(xY, xY + new float2(4, 0), c, c);
lineRenderer.DrawLine(xY, xY + new float2(0, -4), c, c);
lineRenderer.DrawLine(XY, XY + new float2(-4, 0), c, c);
lineRenderer.DrawLine(XY, XY + new float2(0, -4), c, c);
if (drawHealthBar)
{
DrawHealthBar(selectedUnit, xy, Xy);
DrawControlGroup(selectedUnit, xy);
// Only display pips and tags to the owner
if (selectedUnit.Owner == world.LocalPlayer)
{
DrawPips(selectedUnit, xY);
DrawTags(selectedUnit, new float2(.5f * (bounds.Left + bounds.Right ), xy.Y));
}
}
if (ShowUnitPaths)
{
var mobile = selectedUnit.traits.GetOrDefault<Mobile>();
if (mobile != null)
{
var path = mobile.GetCurrentPath();
var start = selectedUnit.Location;
foreach (var step in path)
{
lineRenderer.DrawLine(
Game.CellSize * start + new float2(12, 12),
Game.CellSize * step + new float2(12, 12),
Color.Red, Color.Red);
start = step;
}
}
}
}
void DrawHealthBar(Actor selectedUnit, float2 xy, float2 Xy)
{
var c = Color.Gray;
lineRenderer.DrawLine(xy + new float2(0, -2), xy + new float2(0, -4), c, c);
lineRenderer.DrawLine(Xy + new float2(0, -2), Xy + new float2(0, -4), c, c);
var healthAmount = (float)selectedUnit.Health / selectedUnit.Info.Traits.Get<OwnedActorInfo>().HP;
var healthColor = (healthAmount < Rules.General.ConditionRed) ? Color.Red
: (healthAmount < Rules.General.ConditionYellow) ? Color.Yellow
: Color.LimeGreen;
var healthColor2 = Color.FromArgb(
255,
healthColor.R / 2,
healthColor.G / 2,
healthColor.B / 2);
var z = float2.Lerp(xy, Xy, healthAmount);
lineRenderer.DrawLine(z + new float2(0, -4), Xy + new float2(0, -4), c, c);
lineRenderer.DrawLine(z + new float2(0, -2), Xy + new float2(0, -2), c, c);
lineRenderer.DrawLine(xy + new float2(0, -3), z + new float2(0, -3), healthColor, healthColor);
lineRenderer.DrawLine(xy + new float2(0, -2), z + new float2(0, -2), healthColor2, healthColor2);
lineRenderer.DrawLine(xy + new float2(0, -4), z + new float2(0, -4), healthColor2, healthColor2);
}
// depends on the order of pips in TraitsInterfaces.cs!
static readonly string[] pipStrings = { "pip-empty", "pip-green", "pip-yellow", "pip-red", "pip-gray" };
static readonly string[] tagStrings = { "", "tag-fake", "tag-primary" };
void DrawControlGroup(Actor selectedUnit, float2 basePosition)
{
var group = Game.controller.selection.GetControlGroupForActor(selectedUnit);
if (group == null) return;
var pipImages = new Animation("pips");
pipImages.PlayFetchIndex("groups", () => (int)group);
pipImages.Tick();
spriteRenderer.DrawSprite(pipImages.Image, basePosition + new float2(-8, 1), "chrome");
}
void DrawPips(Actor selectedUnit, float2 basePosition)
{
// If a mod wants to implement a unit with multiple pip sources, then they are placed on multiple rows
var pipxyBase = basePosition + new float2(-12, -7); // Correct for the offset in the shp file
var pipxyOffset = new float2(0, 0); // Correct for offset due to multiple columns/rows
foreach (var pips in selectedUnit.traits.WithInterface<IPips>())
{
foreach (var pip in pips.GetPips(selectedUnit))
{
var pipImages = new Animation("pips");
pipImages.PlayRepeating(pipStrings[(int)pip]);
spriteRenderer.DrawSprite(pipImages.Image, pipxyBase + pipxyOffset, "chrome");
pipxyOffset += new float2(4, 0);
if (pipxyOffset.X+5 > selectedUnit.GetBounds(false).Width)
{
pipxyOffset.X = 0;
pipxyOffset.Y -= 4;
}
}
// Increment row
pipxyOffset.X = 0;
pipxyOffset.Y -= 5;
}
}
void DrawTags(Actor selectedUnit, float2 basePosition)
{
// If a mod wants to implement a unit with multiple tags, then they are placed on multiple rows
var tagxyBase = basePosition + new float2(-16, 2); // Correct for the offset in the shp file
var tagxyOffset = new float2(0, 0); // Correct for offset due to multiple rows
foreach (var tags in selectedUnit.traits.WithInterface<ITags>())
{
foreach (var tag in tags.GetTags())
{
if (tag == TagType.None)
continue;
var tagImages = new Animation("pips");
tagImages.PlayRepeating(tagStrings[(int)tag]);
spriteRenderer.DrawSprite(tagImages.Image, tagxyBase + tagxyOffset, "chrome");
// Increment row
tagxyOffset.Y += 8;
}
}
}
}
}