Support for Dune II SHP files used for cursors in Red Alert.
This commit is contained in:
129
OpenRa.FileFormats/Dune2ShpReader.cs
Normal file
129
OpenRa.FileFormats/Dune2ShpReader.cs
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Collections;
|
||||||
|
using System.IO;
|
||||||
|
using System.Drawing;
|
||||||
|
|
||||||
|
namespace OpenRa.FileFormats
|
||||||
|
{
|
||||||
|
public enum Dune2ImageFlags : int
|
||||||
|
{
|
||||||
|
F80_F2 = 0,
|
||||||
|
F2 = 2,
|
||||||
|
L16_F80_F2_1 = 1,
|
||||||
|
L16_F80_F2_2 = 3,
|
||||||
|
Ln_F80_F2 = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Dune2ImageHeader
|
||||||
|
{
|
||||||
|
public readonly Dune2ImageFlags Flags;
|
||||||
|
public readonly int Width;
|
||||||
|
public readonly int Height;
|
||||||
|
public readonly int Slices;
|
||||||
|
public readonly int FileSize;
|
||||||
|
public readonly int DataSize;
|
||||||
|
|
||||||
|
public readonly byte[] LookupTable;
|
||||||
|
public byte[] Image;
|
||||||
|
|
||||||
|
public Dune2ImageHeader(BinaryReader reader)
|
||||||
|
{
|
||||||
|
Flags = (Dune2ImageFlags)reader.ReadUInt16();
|
||||||
|
Slices = reader.ReadByte();
|
||||||
|
Width = reader.ReadUInt16();
|
||||||
|
Height = reader.ReadByte();
|
||||||
|
FileSize = reader.ReadUInt16();
|
||||||
|
DataSize = reader.ReadUInt16();
|
||||||
|
|
||||||
|
if (Flags == Dune2ImageFlags.L16_F80_F2_1 ||
|
||||||
|
Flags == Dune2ImageFlags.L16_F80_F2_2 ||
|
||||||
|
Flags == Dune2ImageFlags.Ln_F80_F2)
|
||||||
|
{
|
||||||
|
int n = Flags == Dune2ImageFlags.Ln_F80_F2 ? reader.ReadByte() : 16;
|
||||||
|
LookupTable = new byte[n];
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
LookupTable[i] = reader.ReadByte();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Size Size
|
||||||
|
{
|
||||||
|
get { return new Size(Width, Height); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Dune2ShpReader : IEnumerable<Dune2ImageHeader>
|
||||||
|
{
|
||||||
|
public readonly int ImageCount;
|
||||||
|
|
||||||
|
List<Dune2ImageHeader> headers = new List<Dune2ImageHeader>();
|
||||||
|
|
||||||
|
public Dune2ShpReader(Stream stream)
|
||||||
|
{
|
||||||
|
BinaryReader reader = new BinaryReader(stream);
|
||||||
|
|
||||||
|
ImageCount = reader.ReadUInt16();
|
||||||
|
|
||||||
|
//Last offset is pointer to end of file.
|
||||||
|
uint[] offsets = new uint[ImageCount + 1];
|
||||||
|
|
||||||
|
uint temp = reader.ReadUInt32();
|
||||||
|
|
||||||
|
//If fourth byte in file is non-zero, the offsets are two bytes each.
|
||||||
|
bool twoByteOffsets = (temp & 0xFF0000) > 0;
|
||||||
|
if (twoByteOffsets)
|
||||||
|
{
|
||||||
|
offsets[0] = ((temp & 0xFFFF0000) >> 16) + 2; //Offset does not account for image count bytes
|
||||||
|
offsets[1] = (temp & 0xFFFF) + 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
offsets[0] = temp + 2;
|
||||||
|
|
||||||
|
for (int i = twoByteOffsets ? 2 : 1; i < ImageCount + 1; i++)
|
||||||
|
offsets[i] = (twoByteOffsets ? reader.ReadUInt16() : reader.ReadUInt32()) + 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < ImageCount; i++)
|
||||||
|
{
|
||||||
|
reader.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
|
||||||
|
Dune2ImageHeader header = new Dune2ImageHeader(reader);
|
||||||
|
byte[] imgData = reader.ReadBytes(header.FileSize);
|
||||||
|
header.Image = new byte[header.Height * header.Width];
|
||||||
|
|
||||||
|
//Decode image data
|
||||||
|
if (header.Flags != Dune2ImageFlags.F2)
|
||||||
|
{
|
||||||
|
byte[] tempData = new byte[header.DataSize];
|
||||||
|
Format80.DecodeInto(imgData, tempData);
|
||||||
|
Format2.DecodeInto(tempData, header.Image);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Format2.DecodeInto(imgData, header.Image);
|
||||||
|
|
||||||
|
//Lookup values in lookup table
|
||||||
|
if (header.LookupTable != null)
|
||||||
|
for (int j = 0; j < header.Image.Length; j++)
|
||||||
|
header.Image[j] = header.LookupTable[header.Image[j]];
|
||||||
|
|
||||||
|
headers.Add(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Dune2ImageHeader this[int index]
|
||||||
|
{
|
||||||
|
get { return headers[index]; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerator<Dune2ImageHeader> GetEnumerator()
|
||||||
|
{
|
||||||
|
return headers.GetEnumerator();
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator IEnumerable.GetEnumerator()
|
||||||
|
{
|
||||||
|
return GetEnumerator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
31
OpenRa.FileFormats/Format2.cs
Normal file
31
OpenRa.FileFormats/Format2.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace OpenRa.FileFormats
|
||||||
|
{
|
||||||
|
public static class Format2
|
||||||
|
{
|
||||||
|
public static int DecodeInto(byte[] src, byte[] dest)
|
||||||
|
{
|
||||||
|
FastByteReader r = new FastByteReader(src);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
while (!r.Done())
|
||||||
|
{
|
||||||
|
byte cmd = r.ReadByte();
|
||||||
|
if (cmd == 0)
|
||||||
|
{
|
||||||
|
byte count = r.ReadByte();
|
||||||
|
while (count-- > 0)
|
||||||
|
dest[i++] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
dest[i++] = cmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,8 +46,10 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Blowfish.cs" />
|
<Compile Include="Blowfish.cs" />
|
||||||
<Compile Include="BlowfishKeyProvider.cs" />
|
<Compile Include="BlowfishKeyProvider.cs" />
|
||||||
|
<Compile Include="Dune2ShpReader.cs" />
|
||||||
<Compile Include="FileSystem.cs" />
|
<Compile Include="FileSystem.cs" />
|
||||||
<Compile Include="Folder.cs" />
|
<Compile Include="Folder.cs" />
|
||||||
|
<Compile Include="Format2.cs" />
|
||||||
<Compile Include="Format40.cs" />
|
<Compile Include="Format40.cs" />
|
||||||
<Compile Include="Format80.cs" />
|
<Compile Include="Format80.cs" />
|
||||||
<Compile Include="IniFile.cs" />
|
<Compile Include="IniFile.cs" />
|
||||||
|
|||||||
34
OpenRa.Game/Graphics/CursorSheetBuilder.cs
Normal file
34
OpenRa.Game/Graphics/CursorSheetBuilder.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using OpenRa.FileFormats;
|
||||||
|
|
||||||
|
namespace OpenRa.Game.Graphics
|
||||||
|
{
|
||||||
|
static class CursorSheetBuilder
|
||||||
|
{
|
||||||
|
static Dictionary<string, Sprite[]> cursors =
|
||||||
|
new Dictionary<string, Sprite[]>();
|
||||||
|
|
||||||
|
public static Sprite LoadSprite(string filename, params string[] exts)
|
||||||
|
{
|
||||||
|
return LoadAllSprites(filename, exts)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Sprite[] LoadAllSprites(string filename, params string[] exts)
|
||||||
|
{
|
||||||
|
Sprite[] value;
|
||||||
|
if (!cursors.TryGetValue(filename, out value))
|
||||||
|
{
|
||||||
|
Dune2ShpReader shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts));
|
||||||
|
value = new Sprite[shp.ImageCount];
|
||||||
|
for (int i = 0; i < shp.ImageCount; i++)
|
||||||
|
value[i] = SheetBuilder.Add(shp[i].Image, shp[i].Size);
|
||||||
|
cursors.Add(filename, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -77,6 +77,7 @@
|
|||||||
<Compile Include="GameRules\UnitInfo.cs" />
|
<Compile Include="GameRules\UnitInfo.cs" />
|
||||||
<Compile Include="Graphics\Animation.cs" />
|
<Compile Include="Graphics\Animation.cs" />
|
||||||
<Compile Include="Game.cs" />
|
<Compile Include="Game.cs" />
|
||||||
|
<Compile Include="Graphics\CursorSheetBuilder.cs" />
|
||||||
<Compile Include="Graphics\LineRenderer.cs" />
|
<Compile Include="Graphics\LineRenderer.cs" />
|
||||||
<Compile Include="Graphics\OverlayRenderer.cs" />
|
<Compile Include="Graphics\OverlayRenderer.cs" />
|
||||||
<Compile Include="Graphics\WorldRenderer.cs" />
|
<Compile Include="Graphics\WorldRenderer.cs" />
|
||||||
|
|||||||
@@ -1,222 +1,222 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Drawing;
|
using System.Drawing;
|
||||||
using System.Windows.Forms;
|
using System.Windows.Forms;
|
||||||
using OpenRa.FileFormats;
|
using OpenRa.FileFormats;
|
||||||
using OpenRa.Game.Graphics;
|
using OpenRa.Game.Graphics;
|
||||||
using OpenRa.TechTree;
|
using OpenRa.TechTree;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace OpenRa.Game
|
namespace OpenRa.Game
|
||||||
{
|
{
|
||||||
using GRegion = OpenRa.Game.Graphics.Region;
|
using GRegion = OpenRa.Game.Graphics.Region;
|
||||||
|
|
||||||
class Sidebar
|
class Sidebar
|
||||||
{
|
{
|
||||||
TechTree.TechTree techTree;
|
TechTree.TechTree techTree;
|
||||||
|
|
||||||
SpriteRenderer spriteRenderer, clockRenderer;
|
SpriteRenderer spriteRenderer, clockRenderer;
|
||||||
Sprite blank;
|
Sprite blank;
|
||||||
Game game;
|
Game game;
|
||||||
readonly GRegion region;
|
readonly GRegion region;
|
||||||
|
|
||||||
public GRegion Region { get { return region; } }
|
public GRegion Region { get { return region; } }
|
||||||
public float Width { get { return spriteWidth * 2; } }
|
public float Width { get { return spriteWidth * 2; } }
|
||||||
|
|
||||||
Dictionary<string, Sprite> sprites = new Dictionary<string,Sprite>();
|
Dictionary<string, Sprite> sprites = new Dictionary<string,Sprite>();
|
||||||
const int spriteWidth = 64, spriteHeight = 48;
|
const int spriteWidth = 64, spriteHeight = 48;
|
||||||
|
|
||||||
static string[] groups = new string[] { "building", "vehicle", "boat", "infantry", "plane" };
|
static string[] groups = new string[] { "building", "vehicle", "boat", "infantry", "plane" };
|
||||||
|
|
||||||
Dictionary<string, string> itemGroups = new Dictionary<string,string>(); //item->group
|
Dictionary<string, string> itemGroups = new Dictionary<string,string>(); //item->group
|
||||||
Dictionary<string, Animation> clockAnimations = new Dictionary<string,Animation>(); //group->clockAnimation
|
Dictionary<string, Animation> clockAnimations = new Dictionary<string,Animation>(); //group->clockAnimation
|
||||||
Dictionary<string, SidebarItem> selectedItems = new Dictionary<string,SidebarItem>(); //group->selectedItem
|
Dictionary<string, SidebarItem> selectedItems = new Dictionary<string,SidebarItem>(); //group->selectedItem
|
||||||
|
|
||||||
List<SidebarItem> items = new List<SidebarItem>();
|
List<SidebarItem> items = new List<SidebarItem>();
|
||||||
|
|
||||||
public Sidebar( Renderer renderer, Game game )
|
public Sidebar( Renderer renderer, Game game )
|
||||||
{
|
{
|
||||||
this.techTree = game.LocalPlayer.TechTree;
|
this.techTree = game.LocalPlayer.TechTree;
|
||||||
this.techTree.BuildableItemsChanged += PopulateItemList;
|
this.techTree.BuildableItemsChanged += PopulateItemList;
|
||||||
this.game = game;
|
this.game = game;
|
||||||
region = GRegion.Create(game.viewport, DockStyle.Right, 128, Paint, MouseHandler);
|
region = GRegion.Create(game.viewport, DockStyle.Right, 128, Paint, MouseHandler);
|
||||||
game.viewport.AddRegion( region );
|
game.viewport.AddRegion( region );
|
||||||
spriteRenderer = new SpriteRenderer(renderer, false);
|
spriteRenderer = new SpriteRenderer(renderer, false);
|
||||||
clockRenderer = new SpriteRenderer(renderer, true);
|
clockRenderer = new SpriteRenderer(renderer, true);
|
||||||
|
|
||||||
LoadSprites("buildings.txt");
|
LoadSprites("buildings.txt");
|
||||||
LoadSprites("vehicles.txt");
|
LoadSprites("vehicles.txt");
|
||||||
LoadSprites("infantry.txt");
|
LoadSprites("infantry.txt");
|
||||||
|
|
||||||
foreach (string s in groups)
|
foreach (string s in groups)
|
||||||
{
|
{
|
||||||
clockAnimations.Add(s, new Animation("clock"));
|
clockAnimations.Add(s, new Animation("clock"));
|
||||||
clockAnimations[s].PlayRepeating("idle");
|
clockAnimations[s].PlayRepeating("idle");
|
||||||
selectedItems.Add(s, null);
|
selectedItems.Add(s, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
blank = SheetBuilder.Add(new Size((int)spriteWidth, (int)spriteHeight), 16);
|
blank = SheetBuilder.Add(new Size((int)spriteWidth, (int)spriteHeight), 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Build(SidebarItem item)
|
public void Build(SidebarItem item)
|
||||||
{
|
{
|
||||||
if (item != null)
|
if (item != null)
|
||||||
game.controller.orderGenerator = new PlaceBuilding(game.LocalPlayer, item.techTreeItem.tag.ToLowerInvariant());
|
game.controller.orderGenerator = new PlaceBuilding(game.LocalPlayer, item.techTreeItem.tag.ToLowerInvariant());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoadSprites(string filename)
|
void LoadSprites(string filename)
|
||||||
{
|
{
|
||||||
foreach (string line in Util.ReadAllLines(FileSystem.Open(filename)))
|
foreach (string line in Util.ReadAllLines(FileSystem.Open(filename)))
|
||||||
{
|
{
|
||||||
string key = line.Substring(0, line.IndexOf(','));
|
string key = line.Substring(0, line.IndexOf(','));
|
||||||
int secondComma = line.IndexOf(',', line.IndexOf(',') + 1);
|
int secondComma = line.IndexOf(',', line.IndexOf(',') + 1);
|
||||||
string group = line.Substring(secondComma + 1, line.Length - secondComma - 1);
|
string group = line.Substring(secondComma + 1, line.Length - secondComma - 1);
|
||||||
sprites.Add( key, SpriteSheetBuilder.LoadSprite( key + "icon", ".shp" ) );
|
sprites.Add( key, SpriteSheetBuilder.LoadSprite( key + "icon", ".shp" ) );
|
||||||
itemGroups.Add(key, group);
|
itemGroups.Add(key, group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawSprite(Sprite s, ref float2 p)
|
void DrawSprite(Sprite s, ref float2 p)
|
||||||
{
|
{
|
||||||
spriteRenderer.DrawSprite(s, p, 0);
|
spriteRenderer.DrawSprite(s, p, 0);
|
||||||
p.Y += spriteHeight;
|
p.Y += spriteHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Fill(float height, float2 p)
|
void Fill(float height, float2 p)
|
||||||
{
|
{
|
||||||
while (p.Y < height)
|
while (p.Y < height)
|
||||||
DrawSprite(blank, ref p);
|
DrawSprite(blank, ref p);
|
||||||
}
|
}
|
||||||
|
|
||||||
int buildPos = 0;
|
int buildPos = 0;
|
||||||
int unitPos = 0;
|
int unitPos = 0;
|
||||||
|
|
||||||
void PopulateItemList()
|
void PopulateItemList()
|
||||||
{
|
{
|
||||||
buildPos = 0; unitPos = 0;
|
buildPos = 0; unitPos = 0;
|
||||||
|
|
||||||
items.Clear();
|
items.Clear();
|
||||||
|
|
||||||
foreach (Item i in techTree.BuildableItems)
|
foreach (Item i in techTree.BuildableItems)
|
||||||
{
|
{
|
||||||
Sprite sprite;
|
Sprite sprite;
|
||||||
if (!sprites.TryGetValue(i.tag, out sprite)) continue;
|
if (!sprites.TryGetValue(i.tag, out sprite)) continue;
|
||||||
|
|
||||||
items.Add(new SidebarItem(sprite, i, i.IsStructure ? buildPos : unitPos));
|
items.Add(new SidebarItem(sprite, i, i.IsStructure ? buildPos : unitPos));
|
||||||
|
|
||||||
if (i.IsStructure)
|
if (i.IsStructure)
|
||||||
buildPos += spriteHeight;
|
buildPos += spriteHeight;
|
||||||
else
|
else
|
||||||
unitPos += spriteHeight;
|
unitPos += spriteHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (string g in groups) selectedItems[g] = null;
|
foreach (string g in groups) selectedItems[g] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Paint()
|
void Paint()
|
||||||
{
|
{
|
||||||
foreach (SidebarItem i in items)
|
foreach (SidebarItem i in items)
|
||||||
i.Paint(spriteRenderer, region.Location);
|
i.Paint(spriteRenderer, region.Location);
|
||||||
|
|
||||||
Fill(region.Size.Y + region.Location.Y, new float2(region.Location.X, buildPos + region.Location.Y));
|
Fill(region.Size.Y + region.Location.Y, new float2(region.Location.X, buildPos + region.Location.Y));
|
||||||
Fill(region.Size.Y + region.Location.Y, new float2(region.Location.X + spriteWidth, unitPos + region.Location.Y));
|
Fill(region.Size.Y + region.Location.Y, new float2(region.Location.X + spriteWidth, unitPos + region.Location.Y));
|
||||||
|
|
||||||
spriteRenderer.Flush();
|
spriteRenderer.Flush();
|
||||||
|
|
||||||
foreach (var kvp in selectedItems)
|
foreach (var kvp in selectedItems)
|
||||||
{
|
{
|
||||||
if (kvp.Value != null)
|
if (kvp.Value != null)
|
||||||
{
|
{
|
||||||
clockRenderer.DrawSprite(clockAnimations[kvp.Key].Image, region.Location.ToFloat2() + kvp.Value.location, 0);
|
clockRenderer.DrawSprite(clockAnimations[kvp.Key].Image, region.Location.ToFloat2() + kvp.Value.location, 0);
|
||||||
clockAnimations[kvp.Key].Tick(1);
|
clockAnimations[kvp.Key].Tick(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clockRenderer.Flush();
|
clockRenderer.Flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public SidebarItem GetItem(float2 point)
|
public SidebarItem GetItem(float2 point)
|
||||||
{
|
{
|
||||||
foreach (SidebarItem i in items)
|
foreach (SidebarItem i in items)
|
||||||
if (i.Clicked(point))
|
if (i.Clicked(point))
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MouseHandler(MouseInput mi)
|
void MouseHandler(MouseInput mi)
|
||||||
{
|
{
|
||||||
if (mi.Button == MouseButtons.Left && mi.Event == MouseInputEvent.Down)
|
if (mi.Button == MouseButtons.Left && mi.Event == MouseInputEvent.Down)
|
||||||
{
|
{
|
||||||
var point = new float2(mi.Location.X, mi.Location.Y);
|
var point = new float2(mi.Location.X, mi.Location.Y);
|
||||||
var item = GetItem(point);
|
var item = GetItem(point);
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
string group = itemGroups[item.techTreeItem.tag];
|
string group = itemGroups[item.techTreeItem.tag];
|
||||||
if (selectedItems[group] == null)
|
if (selectedItems[group] == null)
|
||||||
{
|
{
|
||||||
selectedItems[group] = item;
|
selectedItems[group] = item;
|
||||||
Build(item);
|
Build(item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if( mi.Button == MouseButtons.Right && mi.Event == MouseInputEvent.Down )
|
else if( mi.Button == MouseButtons.Right && mi.Event == MouseInputEvent.Down )
|
||||||
{
|
{
|
||||||
var point = new float2(mi.Location.X, mi.Location.Y);
|
var point = new float2(mi.Location.X, mi.Location.Y);
|
||||||
var item = GetItem(point);
|
var item = GetItem(point);
|
||||||
if( item != null )
|
if( item != null )
|
||||||
{
|
{
|
||||||
string group = itemGroups[ item.techTreeItem.tag ];
|
string group = itemGroups[ item.techTreeItem.tag ];
|
||||||
selectedItems[ group ] = null;
|
selectedItems[ group ] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlaceBuilding : IOrderGenerator
|
class PlaceBuilding : IOrderGenerator
|
||||||
{
|
{
|
||||||
public readonly Player Owner;
|
public readonly Player Owner;
|
||||||
public readonly string Name;
|
public readonly string Name;
|
||||||
|
|
||||||
public PlaceBuilding( Player owner, string name )
|
public PlaceBuilding( Player owner, string name )
|
||||||
{
|
{
|
||||||
Owner = owner;
|
Owner = owner;
|
||||||
Name = name;
|
Name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<Order> Order( Game game, int2 xy )
|
public IEnumerable<Order> Order( Game game, int2 xy )
|
||||||
{
|
{
|
||||||
// todo: check that space is free
|
// todo: check that space is free
|
||||||
yield return new PlaceBuildingOrder( this, xy );
|
yield return new PlaceBuildingOrder( this, xy );
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PrepareOverlay(Game game, int2 xy)
|
public void PrepareOverlay(Game game, int2 xy)
|
||||||
{
|
{
|
||||||
game.worldRenderer.uiOverlay.SetCurrentOverlay(xy, Name);
|
game.worldRenderer.uiOverlay.SetCurrentOverlay(xy, Name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PlaceBuildingOrder : Order
|
class PlaceBuildingOrder : Order
|
||||||
{
|
{
|
||||||
PlaceBuilding building;
|
PlaceBuilding building;
|
||||||
int2 xy;
|
int2 xy;
|
||||||
|
|
||||||
public PlaceBuildingOrder(PlaceBuilding building, int2 xy)
|
public PlaceBuildingOrder(PlaceBuilding building, int2 xy)
|
||||||
{
|
{
|
||||||
this.building = building;
|
this.building = building;
|
||||||
this.xy = xy;
|
this.xy = xy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Apply(Game game)
|
public override void Apply(Game game)
|
||||||
{
|
{
|
||||||
game.world.AddFrameEndTask(_ =>
|
game.world.AddFrameEndTask(_ =>
|
||||||
{
|
{
|
||||||
Log.Write( "Player \"{0}\" builds {1}", building.Owner.PlayerName, building.Name );
|
Log.Write( "Player \"{0}\" builds {1}", building.Owner.PlayerName, building.Name );
|
||||||
game.world.Add( new Actor( building.Name, xy, building.Owner ) );
|
game.world.Add( new Actor( building.Name, xy, building.Owner ) );
|
||||||
|
|
||||||
game.controller.orderGenerator = null;
|
game.controller.orderGenerator = null;
|
||||||
game.worldRenderer.uiOverlay.KillOverlay();
|
game.worldRenderer.uiOverlay.KillOverlay();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
57
doc/shp dune 2.txt
Normal file
57
doc/shp dune 2.txt
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
======================
|
||||||
|
Dune 2 SHP file format
|
||||||
|
======================
|
||||||
|
Sourced from Red Horizon Utilities by Emanuel Rabina
|
||||||
|
http://www.ultraq.net.nz/redhorizon/
|
||||||
|
|
||||||
|
The Dune 2 SHP file, is a multiple image filetype, where each image can have
|
||||||
|
it's own set of dimensions. The file is structured as follows:
|
||||||
|
|
||||||
|
File header:
|
||||||
|
NumImages (2 bytes) - the number of images in the file
|
||||||
|
Offsets[NumImages + 1] - offset to the image header for an image. The last
|
||||||
|
(2 or 4 bytes each) offset points to the end of the file. The offsets
|
||||||
|
don't take into account the NumImages bytes at the
|
||||||
|
beginning, so add 2 bytes to the offset value to
|
||||||
|
get the actual position of an image header in the
|
||||||
|
file
|
||||||
|
|
||||||
|
The size of the offsets can be either 2 or 4 bytes. There is no simple way
|
||||||
|
to determine which it will be, but checking the file's 4th byte to see if
|
||||||
|
it's 0, seems to be a commonly accepted practice amongst existing Dune 2 SHP
|
||||||
|
file readers:
|
||||||
|
|
||||||
|
eg: A 2-byte offset file: 01 00 06 00 EC 00 45 0A ...
|
||||||
|
A 4-byte offset file: 01 00 08 00 00 00 EC 00 ...
|
||||||
|
^^
|
||||||
|
The marked byte will be 0 in 4-byte offset files, non 0 in 2-byte offset
|
||||||
|
files.
|
||||||
|
Lastly, like C&C SHP files, there is an extra offset, pointing to the end of
|
||||||
|
the file (or what would have been the position of another image header/data
|
||||||
|
pair).
|
||||||
|
|
||||||
|
Following the file header, are a series of image header & image data pairs.
|
||||||
|
The image header is structured as follows:
|
||||||
|
|
||||||
|
Image header:
|
||||||
|
Flags (2 bytes) - flags to identify the type of data following the
|
||||||
|
Datasize field, and/or the compression schemes used
|
||||||
|
Slices (1 byte) - number of Format2 slices used to encode the image
|
||||||
|
data. Often this is the same as the height of the
|
||||||
|
image
|
||||||
|
Width (2 bytes) - width of the image
|
||||||
|
Height (1 byte) - height of the image
|
||||||
|
Filesize (2 bytes) - size of the image data in the file
|
||||||
|
Datasize (2 bytes) - size of the image data when Format2 encoded/compressed
|
||||||
|
|
||||||
|
Regarding the flags, there seem to be 4 values:
|
||||||
|
- 00000000 (0) = Decompress with Format80, then Format2
|
||||||
|
- 00000001 (1) = (see 3)
|
||||||
|
- 00000010 (2) = Decompress with Format2
|
||||||
|
- 00000011 (3) = A small 16-byte lookup table follows, and the image data
|
||||||
|
should be decompressed with Format80 then Format2.
|
||||||
|
- 00000101 (5) = The first byte gives the size of the lookup table that
|
||||||
|
follows, and the image data should be decompressed with
|
||||||
|
Format80 then Format2.
|
||||||
|
|
||||||
|
And after this image header is the image data.
|
||||||
Reference in New Issue
Block a user