Merge branch 'master' of github.com:chrisforbes/OpenRA into nsis

This commit is contained in:
Matthew Bowra-Dean
2010-03-04 22:44:48 +13:00
56 changed files with 4776 additions and 4696 deletions

View File

@@ -18,23 +18,18 @@
*/
#endregion
using System.Drawing;
namespace OpenRA.FileFormats
{
public struct TreeReference
public struct ActorReference
{
public readonly int X;
public readonly int Y;
public readonly string Image;
public TreeReference(int xy, string image)
public readonly int2 Location;
public readonly string Name;
public readonly string Owner;
public ActorReference( string name, int2 location, string owner )
{
X = xy % 128;
Y = xy / 128;
Image = image;
Name = name;
Location = location;
Owner = owner;
}
public Point Location { get { return new Point(X, Y); } }
}
}

View File

@@ -30,7 +30,9 @@ namespace OpenRA.FileFormats
{
public readonly string Title;
public readonly string Theater;
public readonly int INIFormat;
public readonly int MapSize;
public readonly int XOffset;
public readonly int YOffset;
public int2 Offset { get { return new int2( XOffset, YOffset ); } }
@@ -39,8 +41,8 @@ namespace OpenRA.FileFormats
public readonly int Height;
public int2 Size { get { return new int2(Width, Height); } }
public readonly TileReference[ , ] MapTiles = new TileReference[ 128, 128 ];
public readonly List<TreeReference> Trees = new List<TreeReference>();
public readonly TileReference[ , ] MapTiles;
public readonly List<ActorReference> Actors = new List<ActorReference>();
public readonly IEnumerable<int2> SpawnPoints;
@@ -49,35 +51,53 @@ namespace OpenRA.FileFormats
return s.Length <= maxLength ? s : s.Substring(0,maxLength );
}
public string TileSuffix { get { return "." + Truncate(Theater, 3); } }
public Map(IniFile file)
{
for (int j = 0; j < 128; j++)
for (int i = 0; i < 128; i++)
MapTiles[i, j] = new TileReference();
public Map(string filename)
{
IniFile file = new IniFile(FileSystem.Open(filename));
IniSection basic = file.GetSection("Basic");
Title = basic.GetValue("Name", "(null)");
INIFormat = int.Parse(basic.GetValue("NewINIFormat", "0"));
IniSection map = file.GetSection("Map");
Theater = Truncate(map.GetValue("Theater", "TEMPERATE"), 8);
Theater = Truncate(map.GetValue("Theater", "TEMPERAT"), 8);
XOffset = int.Parse(map.GetValue("X", "0"));
YOffset = int.Parse(map.GetValue("Y", "0"));
Width = int.Parse(map.GetValue("Width", "0"));
Height = int.Parse(map.GetValue("Height", "0"));
MapSize = (INIFormat == 3) ? 128 : 64;
MapTiles = new TileReference[ MapSize, MapSize ];
for (int j = 0; j < MapSize; j++)
for (int i = 0; i < MapSize; i++)
MapTiles[i, j] = new TileReference();
UnpackTileData(ReadPackedSection(file.GetSection("MapPack")));
UnpackOverlayData(ReadPackedSection(file.GetSection("OverlayPack")));
ReadTrees(file);
if (INIFormat == 3) // RA map
{
UnpackRATileData(ReadPackedSection(file.GetSection("MapPack")));
UnpackRAOverlayData(ReadPackedSection(file.GetSection("OverlayPack")));
ReadRATrees(file);
}
else // CNC
{
UnpackCncTileData(FileSystem.Open(filename.Substring(0,filename.Length-4)+".bin"));
ReadCncOverlay(file);
ReadCncTrees(file);
}
LoadActors(file, "STRUCTURES");
LoadActors(file, "UNITS");
LoadActors(file, "INFANTRY");
SpawnPoints = file.GetSection("Waypoints")
.Select(kv => Pair.New(int.Parse(kv.Key), new int2(int.Parse(kv.Value) % 128, int.Parse(kv.Value) / 128)))
.Where(a => a.First < 8)
.Select(a => a.Second)
.ToArray();
.Where(kv => int.Parse(kv.Value) > 0)
.Select(kv => Pair.New(int.Parse(kv.Key), new int2(int.Parse(kv.Value) % MapSize, int.Parse(kv.Value) / MapSize)))
.Where(a => a.First < 8)
.Select(a => a.Second)
.ToArray();
}
static MemoryStream ReadPackedSection(IniSection mapPackSection)
@@ -136,38 +156,112 @@ namespace OpenRA.FileFormats
return ret;
}
void UnpackTileData( MemoryStream ms )
void UnpackRATileData( MemoryStream ms )
{
for( int i = 0 ; i < 128 ; i++ )
for( int j = 0 ; j < 128 ; j++ )
for( int i = 0 ; i < MapSize ; i++ )
for( int j = 0 ; j < MapSize ; j++ )
MapTiles[j, i].tile = ReadWord(ms);
for( int i = 0 ; i < 128 ; i++ )
for( int j = 0 ; j < 128 ; j++ )
for( int i = 0 ; i < MapSize ; i++ )
for( int j = 0 ; j < MapSize ; j++ )
{
MapTiles[j, i].image = (byte)ms.ReadByte();
if( MapTiles[ j, i ].tile == 0xff || MapTiles[ j, i ].tile == 0xffff )
MapTiles[ j, i ].image = (byte)( i % 4 + ( j % 4 ) * 4 );
}
}
void UnpackOverlayData( MemoryStream ms )
static string[] raOverlayNames =
{
for( int i = 0 ; i < 128 ; i++ )
for( int j = 0 ; j < 128 ; j++ )
MapTiles[ j, i ].overlay = ReadByte( ms );
"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",
};
void UnpackRAOverlayData( MemoryStream ms )
{
for( int i = 0 ; i < MapSize ; i++ )
for( int j = 0 ; j < MapSize ; j++ )
{
byte o = ReadByte( ms );
MapTiles[ j, i ].overlay = (o == 255) ? null : raOverlayNames[o];
}
}
void ReadTrees( IniFile file )
void ReadRATrees( IniFile file )
{
IniSection terrain = file.GetSection( "TERRAIN", true );
if( terrain == null )
return;
foreach( KeyValuePair<string, string> kv in terrain )
{
var loc = int.Parse( kv.Key );
Actors.Add( new ActorReference(kv.Value, new int2(loc % MapSize, loc / MapSize), null ) );
}
}
void UnpackCncTileData( Stream ms )
{
for( int i = 0 ; i < MapSize ; i++ )
for( int j = 0 ; j < MapSize ; j++ )
{
MapTiles[j, i].tile = (byte)ms.ReadByte();
MapTiles[j, i].image = (byte)ms.ReadByte();
if( MapTiles[ j, i ].tile == 0xff )
MapTiles[ j, i ].image = (byte)( i % 4 + ( j % 4 ) * 4 );
}
}
void ReadCncOverlay( IniFile file )
{
IniSection overlay = file.GetSection( "OVERLAY", true );
if( overlay == null )
return;
foreach( KeyValuePair<string, string> kv in overlay )
{
var loc = int.Parse( kv.Key );
int2 cell = new int2(loc % MapSize, loc / MapSize);
Log.Write("Overlay {0} at ({1},{2})",kv.Value,cell.X,cell.Y);
MapTiles[ cell.X, cell.Y ].overlay = kv.Value.ToLower();
}
}
void ReadCncTrees( IniFile file )
{
IniSection terrain = file.GetSection( "TERRAIN", true );
if( terrain == null )
return;
foreach( KeyValuePair<string, string> kv in terrain )
Trees.Add( new TreeReference( int.Parse( kv.Key ), kv.Value ) );
{
var loc = int.Parse( kv.Key );
Actors.Add( new ActorReference( kv.Value.Split(',')[0], new int2(loc % MapSize, loc / MapSize),null));
}
}
void LoadActors(IniFile file, string section)
{
foreach (var s in file.GetSection(section, true))
{
//num=owner,type,health,location,facing,...
var parts = s.Value.Split( ',' );
var loc = int.Parse(parts[3]);
Actors.Add( new ActorReference( parts[1].ToLowerInvariant(), new int2(loc % MapSize, loc / MapSize), parts[0]));
}
}
public bool IsInMap(int2 xy)
{
return IsInMap(xy.X,xy.Y);
}
public bool IsInMap(int x, int y)
{
return (x >= XOffset && y >= YOffset && x < XOffset + Width && y < YOffset + Height);

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -90,10 +90,10 @@
<Compile Include="Terrain.cs" />
<Compile Include="TileReference.cs" />
<Compile Include="TileSet.cs" />
<Compile Include="TreeReference.cs" />
<Compile Include="Tuple.cs" />
<Compile Include="TypeDictionary.cs" />
<Compile Include="Walkability.cs" />
<Compile Include="ActorReference.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#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.
@@ -47,7 +47,7 @@ namespace OpenRA.FileFormats
public class Global
{
public string Map = "scm12ea.ini";
public string Map = "scm02ea.ini";
public string[] Packages = {}; // filename:sha1 pairs.
public string[] Mods = { "ra" }; // mod names
public int OrderLatency = 3;

View File

@@ -28,28 +28,49 @@ namespace OpenRA.FileFormats
public readonly List<byte[]> TileBitmapBytes = new List<byte[]>();
public Terrain( Stream stream )
{
int Width, Height;
{
// Try loading as a cnc .tem
BinaryReader reader = new BinaryReader( stream );
Width = reader.ReadUInt16();
Height = reader.ReadUInt16();
int Width = reader.ReadUInt16();
int Height = reader.ReadUInt16();
if( Width != 24 || Height != 24 )
throw new InvalidDataException( string.Format( "{0}x{1}", Width, Height ) );
/*NumTiles = */reader.ReadUInt16();
reader.ReadUInt16();
/*XDim = */reader.ReadUInt16();
/*YDim = */reader.ReadUInt16();
/*uint FileSize = */reader.ReadUInt32();
/*Zero1 = */reader.ReadUInt16();
/*uint Size = */reader.ReadUInt32();
uint ImgStart = reader.ReadUInt32();
reader.ReadUInt32();
reader.ReadUInt32();
int IndexEnd = reader.ReadInt32();
reader.ReadUInt32();
int IndexStart = reader.ReadInt32();
/*Zero2 = */reader.ReadUInt32();
int IndexEnd, IndexStart;
if (reader.ReadUInt16() == 65535) // ID1 = FFFFh for cnc
{
/*ID2 = */reader.ReadUInt16();
IndexEnd = reader.ReadInt32();
IndexStart = reader.ReadInt32();
}
else // Load as a ra .tem
{
stream.Position = 0;
reader = new BinaryReader( stream );
Width = reader.ReadUInt16();
Height = reader.ReadUInt16();
if( Width != 24 || Height != 24 )
throw new InvalidDataException( string.Format( "{0}x{1}", Width, Height ) );
/*NumTiles = */reader.ReadUInt16();
reader.ReadUInt16();
/*XDim = */reader.ReadUInt16();
/*YDim = */reader.ReadUInt16();
/*uint FileSize = */reader.ReadUInt32();
ImgStart = reader.ReadUInt32();
reader.ReadUInt32();
reader.ReadUInt32();
IndexEnd = reader.ReadInt32();
reader.ReadUInt32();
IndexStart = reader.ReadInt32();
}
stream.Position = IndexStart;
foreach( byte b in new BinaryReader(stream).ReadBytes(IndexEnd - IndexStart) )

View File

@@ -24,9 +24,8 @@ namespace OpenRA.FileFormats
{
public ushort tile;
public byte image;
public byte overlay;
public string overlay;
public byte smudge;
public byte density; /* used for ore/gems */
public override int GetHashCode() { return tile.GetHashCode() ^ image.GetHashCode(); }

View File

@@ -28,7 +28,7 @@ namespace OpenRA.FileFormats
{
public readonly Dictionary<ushort, Terrain> tiles = new Dictionary<ushort, Terrain>();
public readonly Walkability Walkability = new Walkability();
public readonly Walkability Walkability;
public readonly Dictionary<ushort, TileTemplate> walk
= new Dictionary<ushort, TileTemplate>();
@@ -46,12 +46,12 @@ namespace OpenRA.FileFormats
return ret;
}
public TileSet( string suffix )
public TileSet( string tilesetFile, string templatesFile, string suffix )
{
Walkability = new Walkability();
Walkability = new Walkability(templatesFile);
char tileSetChar = char.ToUpperInvariant( suffix[ 1 ] );
StreamReader tileIdFile = new StreamReader( FileSystem.Open( "tileSet.til" ) );
char tileSetChar = char.ToUpperInvariant( suffix[ 0 ] );
StreamReader tileIdFile = new StreamReader( FileSystem.Open(tilesetFile) );
while( true )
{
@@ -74,7 +74,7 @@ namespace OpenRA.FileFormats
if (!walk.ContainsKey((ushort)(start + i)))
walk.Add((ushort)(start + i), Walkability.GetWalkability(tilename));
using( Stream s = FileSystem.Open( tilename + suffix ) )
using( Stream s = FileSystem.Open( tilename + "." + suffix ) )
{
if( !tiles.ContainsKey( (ushort)( start + i ) ) )
tiles.Add( (ushort)( start + i ), new Terrain( s ) );
@@ -88,9 +88,10 @@ namespace OpenRA.FileFormats
public byte[] GetBytes(TileReference r)
{
Terrain tile;
if( tiles.TryGetValue( r.tile, out tile ) )
return tile.TileBitmapBytes[ r.image ];
byte[] missingTile = new byte[ 24 * 24 ];
for( int i = 0 ; i < missingTile.Length ; i++ )
missingTile[ i ] = 0x36;
@@ -102,8 +103,15 @@ namespace OpenRA.FileFormats
{
if (r.tile == 0xff || r.tile == 0xffff)
r.image = 0;
return walk[r.tile].TerrainType[r.image];
try {
return walk[r.tile].TerrainType[r.image];
}
catch (KeyNotFoundException)
{
return 0; // Default zero (walkable)
}
}
}
}

View File

@@ -38,9 +38,9 @@ namespace OpenRA.FileFormats
Dictionary<string, TileTemplate> walkability
= new Dictionary<string,TileTemplate>();
public Walkability()
public Walkability(string templatesFile)
{
var file = new IniFile( FileSystem.Open( "templates.ini" ) );
var file = new IniFile( FileSystem.Open( templatesFile ) );
foreach (var section in file.Sections)
{

View File

@@ -139,8 +139,6 @@ namespace OpenRA
ready = new Animation("pips");
ready.PlayRepeating("ready");
clock = new Animation("clock");
mapChooserSheet = new Sheet(r, new Size(128, 128));
}
List<string> visibleTabs = new List<string>();
@@ -251,7 +249,7 @@ namespace OpenRA
public MapInfo(string filename)
{
Filename = filename.ToLowerInvariant();
Map = new Map(new IniFile(FileSystem.Open(Filename)));
Map = new Map(Filename);
}
};
@@ -301,6 +299,9 @@ namespace OpenRA
if (mapPreviewDirty)
{
if (mapChooserSheet == null || mapChooserSheet.Size.Width != currentMap.Map.MapSize)
mapChooserSheet = new Sheet(renderer, new Size(currentMap.Map.MapSize, currentMap.Map.MapSize));
var b = Minimap.RenderTerrainBitmapWithSpawnPoints(currentMap.Map, Game.world.TileSet); // tileset -> hack
mapChooserSheet.Texture.SetData(b);
mapChooserSprite = new Sprite(mapChooserSheet,
@@ -318,9 +319,10 @@ namespace OpenRA
var y = r.Top + 50;
int numListItems = ((r.Bottom - 60 - y ) / 20);
int maxListItems = ((r.Bottom - 60 - y ) / 20);
for(int i = mapOffset; i < numListItems + mapOffset; i++, y += 20){
for(int i = mapOffset; i < Math.Min(maxListItems + mapOffset, mapList.Value.Count()); i++, y += 20){
var map = mapList.Value.ElementAt(i);
var itemRect = new Rectangle(r.Left + 50, y - 2, r.Width - 340, 20);
if (map == currentMap)
@@ -338,7 +340,9 @@ namespace OpenRA
DrawCentered("Size: {0}x{1}".F(currentMap.Map.Width, currentMap.Map.Height),
new int2(mapRect.Left + mapRect.Width / 2, y), Color.White);
y += 20;
DrawCentered("Theater: {0}".F(currentMap.Map.Theater, currentMap.Map.Height),
var theaterInfo = Game.world.WorldActor.Info.Traits.WithInterface<TheaterInfo>().FirstOrDefault(t => t.Theater == currentMap.Map.Theater);
DrawCentered("Theater: {0}".F(theaterInfo.Name, currentMap.Map.Height),
new int2(mapRect.Left + mapRect.Width / 2, y), Color.White);
y += 20;
DrawCentered("Spawnpoints: {0}".F(currentMap.Map.SpawnPoints.Count()),
@@ -355,7 +359,7 @@ namespace OpenRA
AddUiButton(new int2(mapRect.Left + mapRect.Width / 2, y), "\\/",
_ =>
{
mapOffset = (mapOffset + 1 > mapList.Value.Count() - numListItems) ? mapOffset : mapOffset + 1;
mapOffset = (mapOffset + 1 > mapList.Value.Count() - maxListItems) ? mapOffset : mapOffset + 1;
});
AddButton(r, _ => { });
@@ -424,8 +428,8 @@ namespace OpenRA
DrawDialogBackground(r, "dialog");
DrawCentered("OpenRA Multiplayer Lobby", new int2(r.Left + w / 2, r.Top + 20), Color.White);
DrawDialogBackground(new Rectangle(r.Right - 324, r.Top + 43, 304, 244),"dialog2");
var minimapRect = new Rectangle(r.Right - 322, r.Top + 45, 300, 240);
DrawDialogBackground(new Rectangle(r.Right - 264, r.Top + 43, 244, 244),"dialog2");
var minimapRect = new Rectangle(r.Right - 262, r.Top + 45, 240, 240);
world.Minimap.Update();
world.Minimap.Draw(minimapRect, true);

View File

@@ -48,7 +48,8 @@ namespace OpenRA
if (impactSound != null) Sound.Play(impactSound + ".aud");
if (!isWater) world.Map.AddSmudge(targetTile, warhead);
if (warhead.Ore) world.Map.DestroyOre(targetTile.X, targetTile.Y);
if (warhead.Ore)
world.WorldActor.traits.Get<ResourceLayer>().Destroy(targetTile);
var firepowerModifier = firedBy.traits
.WithInterface<IFirepowerModifier>()

View File

@@ -101,11 +101,8 @@ namespace OpenRA
Timer.Time( "ChromeProv, SeqProv, viewport: {0}" );
skipMakeAnims = true;
foreach (var treeReference in Game.world.Map.Trees)
world.CreateActor(treeReference.Image, new int2(treeReference.Location), null);
Timer.Time( "trees: {0}" );
world.LoadMapActors(Rules.AllRules);
foreach (var actorReference in world.Map.Actors)
world.CreateActor(actorReference.Name, actorReference.Location, world.players.Values.FirstOrDefault(p => p.InternalName == actorReference.Owner) ?? world.players[0]);
skipMakeAnims = false;
Timer.Time( "map actors: {0}" );

View File

@@ -64,8 +64,6 @@ namespace OpenRA.GameRules
/* Income & Production */
public readonly float BuildSpeed = 0;
public readonly float BuildupTime = 0;
public readonly int GemValue = 0;
public readonly int GoldValue = 0;
public readonly float OreTruckRate = 0;
public readonly bool SeparateAircraft = true;
public readonly float SurvivorRate = 0;

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#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.
@@ -39,7 +39,7 @@ namespace OpenRA.GameRules
// External game settings
public readonly string NetworkHost = "";
public readonly int NetworkPort = 0;
public readonly string Map = "scm12ea.ini";
public readonly string Map = "scm02ea.ini";
public readonly int Player = 1;
public readonly string Replay = "";
public readonly string PlayerName = "";

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#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.
@@ -47,8 +47,8 @@ namespace OpenRA.Graphics
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));
sheet = new Sheet(r, new Size(world.Map.MapSize, world.Map.MapSize));
mapOnlySheet = new Sheet(r, new Size(world.Map.MapSize, world.Map.MapSize));
lineRenderer = new LineRenderer(r);
rgbaRenderer = new SpriteRenderer(r, true, r.RgbaSpriteShader);
@@ -93,9 +93,9 @@ namespace OpenRA.Graphics
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++)
var terrain = new Bitmap(map.MapSize, map.MapSize);
for (var y = 0; y < map.MapSize; y++)
for (var x = 0; x < map.MapSize; x++)
terrain.SetPixel(x, y, map.IsInMap(x, y)
? colors[tileset.GetWalkability(map.MapTiles[x, y])]
: shroudColor);
@@ -105,14 +105,13 @@ namespace OpenRA.Graphics
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)
@@ -120,11 +119,13 @@ namespace OpenRA.Graphics
if (oreLayer == null)
{
var res = world.WorldActor.traits.Get<ResourceLayer>();
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)))
if (res.GetResource(new int2(x,y)) != null)
oreLayer.SetPixel(x, y, colors[(int)TerrainMovementType.Ore]);
}

View File

@@ -22,6 +22,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.IO;
using OpenRA.FileFormats;
namespace OpenRA.Graphics
@@ -65,12 +66,14 @@ namespace OpenRA.Graphics
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);
// Log.Write("Loading sequence {0}", unitName);
try {
var sequences = eUnit.SelectNodes("./sequence").OfType<XmlElement>()
.Select(e => new Sequence(unitName, e))
.ToDictionary(s => s.Name);
units.Add(unitName, sequences);
} catch (FileNotFoundException) {} // Do nothing; we can crash later if we actually wanted art
}
public static Sequence GetSequence(string unitName, string sequenceName)

View File

@@ -23,35 +23,24 @@ using OpenRA.FileFormats;
namespace OpenRA.Graphics
{
class OverlayRenderer
class SmudgeRenderer
{
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 )
public SmudgeRenderer( 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();
}
@@ -71,44 +60,9 @@ namespace OpenRA.Graphics
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

@@ -21,6 +21,7 @@
using System.Drawing;
using OpenRA.FileFormats;
using OpenRA.FileFormats.Graphics;
using OpenRA.Traits;
namespace OpenRA.Graphics
{
@@ -32,7 +33,7 @@ namespace OpenRA.Graphics
Renderer renderer;
Map map;
OverlayRenderer overlayRenderer;
SmudgeRenderer overlayRenderer;
public TerrainRenderer(World world, Renderer renderer, WorldRenderer wr)
{
@@ -68,7 +69,7 @@ namespace OpenRA.Graphics
indexBuffer = renderer.Device.CreateIndexBuffer( indices.Length );
indexBuffer.SetData( indices );
overlayRenderer = new OverlayRenderer( renderer, map );
overlayRenderer = new SmudgeRenderer( renderer, map );
}
public void Draw( Viewport viewport )
@@ -104,6 +105,9 @@ namespace OpenRA.Graphics
new Range<int>(indicesPerRow * firstRow, indicesPerRow * lastRow),
terrainSheet.Texture, PrimitiveType.TriangleList, renderer.SpriteShader));
foreach (var r in Game.world.WorldActor.traits.WithInterface<IRenderOverlay>())
r.Render();
overlayRenderer.Draw();
}
}

View File

@@ -98,7 +98,6 @@
<Compile Include="Network\OrderManager.cs" />
<Compile Include="Orders\RepairOrderGenerator.cs" />
<Compile Include="Orders\SellOrderGenerator.cs" />
<Compile Include="Ore.cs" />
<Compile Include="PackageDownloader.cs" />
<Compile Include="PathSearch.cs" />
<Compile Include="Selection.cs" />
@@ -115,7 +114,6 @@
<Compile Include="Traits\World\ChoosePaletteOnSelect.cs" />
<Compile Include="Traits\World\Country.cs" />
<Compile Include="Traits\World\CrateSpawner.cs" />
<Compile Include="Traits\World\OreGrowth.cs" />
<Compile Include="Traits\OreRefinery.cs" />
<Compile Include="Traits\Activities\Attack.cs" />
<Compile Include="Traits\Activities\CallFunc.cs" />
@@ -145,7 +143,7 @@
<Compile Include="Graphics\CursorSequence.cs" />
<Compile Include="Graphics\CursorSheetBuilder.cs" />
<Compile Include="Graphics\LineRenderer.cs" />
<Compile Include="Graphics\OverlayRenderer.cs" />
<Compile Include="Graphics\SmudgeRenderer.cs" />
<Compile Include="Graphics\WorldRenderer.cs" />
<Compile Include="Traits\Activities\Idle.cs" />
<Compile Include="Traits\Activities\Land.cs" />
@@ -216,6 +214,8 @@
<Compile Include="Traits\Passenger.cs" />
<Compile Include="Traits\Player\PlaceBuilding.cs" />
<Compile Include="Traits\World\PlayerColorPalette.cs" />
<Compile Include="Traits\World\ResourceLayer.cs" />
<Compile Include="Traits\World\ResourceType.cs" />
<Compile Include="Traits\World\ShroudPalette.cs" />
<Compile Include="Traits\SupportPowers\SupportPower.cs" />
<Compile Include="Traits\ProvidesRadar.cs" />
@@ -245,7 +245,7 @@
<Compile Include="Traits\Render\RenderUnitRotor.cs" />
<Compile Include="Traits\Render\RenderUnitSpinner.cs" />
<Compile Include="Traits\Render\RenderUnitTurreted.cs" />
<Compile Include="Traits\SeedsOre.cs" />
<Compile Include="Traits\SeedsResource.cs" />
<Compile Include="Traits\StoresOre.cs" />
<Compile Include="Traits\Submarine.cs" />
<Compile Include="Traits\AI\TakeCover.cs" />
@@ -253,6 +253,7 @@
<Compile Include="Traits\Turreted.cs" />
<Compile Include="Traits\Unit.cs" />
<Compile Include="Traits\World\UnitInfluence.cs" />
<Compile Include="Traits\World\WallLoadHook.cs" />
<Compile Include="Traits\World\WaterPaletteRotation.cs" />
<Compile Include="Traits\Modifiers\WithShadow.cs" />
<Compile Include="Network\UnitOrders.cs" />
@@ -268,6 +269,7 @@
<Compile Include="Traits\Player\EvaAlerts.cs" />
<Compile Include="Traits\World\ScreenShaker.cs" />
<Compile Include="Traits\LineBuild.cs" />
<Compile Include="Traits\World\Theater.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -1,207 +0,0 @@
#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 OpenRA.FileFormats;
using OpenRA.Traits;
namespace OpenRA
{
public static class Ore
{
public static void AddOre(this Map map, int i, int j)
{
if (map.ContainsOre(i, j) && map.MapTiles[i, j].density < 12)
map.MapTiles[i, j].density++;
else if (map.MapTiles[i, j].overlay == 0xff)
{
map.MapTiles[i, j].overlay = ChooseOre();
map.MapTiles[i, j].density = 1;
}
}
public static void DestroyOre(this Map map, int i, int j)
{
if (map.ContainsResource(new int2(i, j)))
{
map.MapTiles[i, j].density = 0;
map.MapTiles[i, j].overlay = 0xff;
}
}
public static bool OreCanSpreadInto(this World world, int i, int j)
{
if (world.WorldActor.traits.Get<BuildingInfluence>().GetBuildingAt(new int2(i, j)) != null)
return false;
return TerrainCosts.Cost(UnitMovementType.Wheel,
world.TileSet.GetWalkability(world.Map.MapTiles[i, j]))
< double.PositiveInfinity;
}
public static void SpreadOre(this World world, Random r, float chance)
{
var map = world.Map;
var mini = map.XOffset; var maxi = map.XOffset + map.Width;
var minj = map.YOffset; var maxj = map.YOffset + map.Height;
/* phase 1: grow into neighboring regions */
var newOverlay = new byte[128, 128];
for (int j = minj; j < maxj; j++)
for (int i = mini; i < maxi; i++)
{
newOverlay[i, j] = 0xff;
if (!map.HasOverlay(i, j)
&& r.NextDouble() < chance
&& map.GetOreDensity(i, j) > 0
&& world.OreCanSpreadInto(i, j))
newOverlay[i, j] = ChooseOre();
}
for (int j = minj; j < maxj; j++)
for (int i = mini; i < maxi; i++)
if (newOverlay[i, j] != 0xff)
map.MapTiles[i, j].overlay = newOverlay[i, j];
}
public static void GrowOre(this World world, Random r)
{
var map = world.Map;
var mini = map.XOffset; var maxi = map.XOffset + map.Width;
var minj = map.YOffset; var maxj = map.YOffset + map.Height;
/* phase 2: increase density of existing areas */
var newDensity = new byte[128, 128];
for (int j = minj; j < maxj; j++)
for (int i = mini; i < maxi; i++)
if (map.ContainsOre(i, j)) newDensity[i, j] = map.GetOreDensity(i, j);
for (int j = minj; j < maxj; j++)
for (int i = mini; i < maxi; i++)
if (map.MapTiles[i, j].density < newDensity[i, j])
++map.MapTiles[i, j].density;
}
public static void InitOreDensity( this Map map )
{
for (int j = 0; j < 128; j++)
for (int i = 0; i < 128; i++)
{
if (map.ContainsOre(i, j)) map.MapTiles[i, j].density = map.GetOreDensity(i, j);
if (map.ContainsGem(i, j)) map.MapTiles[i, j].density = map.GetGemDensity(i, j);
}
}
static byte GetOreDensity(this Map map, int i, int j)
{
int sum = 0;
for (var u = -1; u < 2; u++)
for (var v = -1; v < 2; v++)
if (map.ContainsOre(i + u, j + v))
++sum;
sum = (sum * 4 + 2) / 3;
return (byte)sum;
}
static byte GetGemDensity(this Map map, int i, int j)
{
int sum = 0;
for (var u = -1; u < 2; u++)
for (var v = -1; v < 2; v++)
if (map.ContainsGem(i + u, j + v))
++sum;
sum = (sum+2) / 3; /* 3 gem units/tile is full. */
return (byte)sum;
}
static bool HasOverlay(this Map map, int i, int j)
{
return map.MapTiles[i, j].overlay < overlayIsOre.Length;
}
static bool ContainsOre(this Map map, int i, int j)
{
return map.HasOverlay(i, j) && overlayIsOre[map.MapTiles[i, j].overlay];
}
static bool ContainsGem(this Map map, int i, int j)
{
return map.HasOverlay(i, j) && overlayIsGems[map.MapTiles[i, j].overlay];
}
public static bool ContainsResource(this Map map, int2 p)
{
return map.ContainsGem(p.X, p.Y) || map.ContainsOre(p.X, p.Y);
}
public static bool Harvest(this Map map, int2 p, out bool isGems) /* harvests one unit if possible */
{
isGems = map.ContainsGem(p.X, p.Y);
if (map.MapTiles[p.X, p.Y].density == 0) return false;
if (--map.MapTiles[p.X, p.Y].density == 0)
map.MapTiles[p.X, p.Y].overlay = 0xff;
return true;
}
static byte ore = 5;
static byte ChooseOre()
{
if (++ore > 8) ore = 5;
return ore;
}
public static bool IsOverlaySolid(this Map map, int2 p)
{
var o = map.MapTiles[p.X, p.Y].overlay;
return o < overlayIsFence.Length && overlayIsFence[o];
}
public static bool[] overlayIsFence =
{
true, true, true, true, true,
false, false, false, false,
false, false, false, false,
false, false, false, false, false, false, false,
false, false, false, true, true,
};
public static bool[] overlayIsOre =
{
false, false, false, false, false,
true, true, true, true,
false, false, false, false,
false, false, false, false, false, false, false,
false, false, false, false, false,
};
public static bool[] overlayIsGems =
{
false, false, false, false, false,
false, false, false, false,
true, true, true, true,
false, false, false, false, false, false, false,
false, false, false, false, false,
};
}
}

View File

@@ -35,10 +35,11 @@ namespace OpenRA
public PathFinder( World world )
{
this.world = world;
var map = world.Map;
for (var umt = UnitMovementType.Foot; umt <= UnitMovementType.Float; umt++)
passableCost[(int)umt] = new float[128, 128];
for( int x = 0 ; x < 128 ; x++ )
for( int y = 0 ; y < 128 ; y++ )
passableCost[(int)umt] = new float[map.MapSize, map.MapSize];
for( int x = 0 ; x < map.MapSize ; x++ )
for( int y = 0 ; y < map.MapSize ; y++ )
for (var umt = UnitMovementType.Foot; umt <= UnitMovementType.Float; umt++ )
passableCost[(int)umt][ x, y ] = ( world.Map.IsInMap( x, y ) )
? (float)TerrainCosts.Cost( umt, world.TileSet.GetWalkability( world.Map.MapTiles[ x, y ] ) )

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#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.
@@ -84,9 +84,7 @@ namespace OpenRA
if (!world.WorldActor.traits.Get<BuildingInfluence>().CanMoveHere(newHere) &&
world.WorldActor.traits.Get<BuildingInfluence>().GetBuildingAt(newHere) != ignoreBuilding)
continue;
if (world.Map.IsOverlaySolid(newHere))
continue;
// Replicate real-ra behavior of not being able to enter a cell if there is a mixture of crushable and uncrushable units
if (checkForBlocked && (world.WorldActor.traits.Get<UnitInfluence>().GetUnitsAt(newHere).Any(a => !world.IsActorPathableToCrush(a, umt))))
continue;
@@ -162,9 +160,9 @@ namespace OpenRA
static CellInfo[ , ] InitCellInfo()
{
var cellInfo = new CellInfo[ 128, 128 ];
for( int x = 0 ; x < 128 ; x++ )
for( int y = 0 ; y < 128 ; y++ )
var cellInfo = new CellInfo[ Game.world.Map.MapSize, Game.world.Map.MapSize ];
for( int x = 0 ; x < Game.world.Map.MapSize ; x++ )
for( int y = 0 ; y < Game.world.Map.MapSize ; y++ )
cellInfo[ x, y ] = new CellInfo( float.PositiveInfinity, new int2( x, y ), false );
return cellInfo;
}

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#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.
@@ -30,22 +30,31 @@ namespace OpenRA
{
public class Shroud
{
bool[,] explored = new bool[128, 128];
bool[,] explored;
Sprite[] shadowBits = SpriteSheetBuilder.LoadAllSprites("shadow");
Sprite[,] sprites = new Sprite[128, 128];
Sprite[,] sprites;
int gapOpaqueTicks = (int)(Rules.General.GapRegenInterval * 25 * 60);
int gapTicks;
int[,] gapField;
bool[,] gapActive;
bool dirty = true;
bool hasGPS = false;
Player owner;
Map map;
public Rectangle? bounds;
public Shroud(Player owner, Map map) { this.owner = owner; this.map = map; }
int gapOpaqueTicks = (int)(Rules.General.GapRegenInterval * 25 * 60);
int gapTicks;
int[,] gapField = new int[128, 128];
bool[,] gapActive = new bool[128, 128];
public Shroud(Player owner, Map map)
{
this.owner = owner;
this.map = map;
explored = new bool[map.MapSize, map.MapSize];
sprites = new Sprite[map.MapSize, map.MapSize];
gapField = new int[map.MapSize, map.MapSize];
gapActive = new bool[map.MapSize, map.MapSize];
}
public bool HasGPS
{
get { return hasGPS; }
@@ -57,7 +66,7 @@ namespace OpenRA
if (gapTicks > 0) { --gapTicks; return; }
// Clear active flags
gapActive = new bool[128, 128];
gapActive = new bool[map.MapSize, map.MapSize];
foreach (var a in world.Queries.WithTrait<GeneratesGap>().Where(a => owner != a.Actor.Owner))
foreach (var t in a.Trait.GetShroudedTiles())
{

View File

@@ -18,6 +18,8 @@
*/
#endregion
using System.Linq;
namespace OpenRA.Traits.Activities
{
public class Harvest : IActivity
@@ -39,7 +41,7 @@ namespace OpenRA.Traits.Activities
return this;
else
{
FindMoreOre(self);
FindMoreResource(self);
return NextActivity;
}
}
@@ -49,28 +51,31 @@ namespace OpenRA.Traits.Activities
var harv = self.traits.Get<Harvester>();
var renderUnit = self.traits.Get<RenderUnit>(); /* better have one of these! */
var isGem = false;
if (!self.World.Map.ContainsResource(self.Location) ||
!self.World.Map.Harvest(self.Location, out isGem))
var resource = self.World.WorldActor.traits.Get<ResourceLayer>().Harvest(self.Location);
if (resource == null)
return false;
if (renderUnit.anim.CurrentSequence.Name != "harvest")
{
isHarvesting = true;
renderUnit.PlayCustomAnimation(self, "harvest", () => isHarvesting = false);
}
harv.AcceptResource(isGem);
harv.AcceptResource(resource);
return true;
}
void FindMoreOre(Actor self)
void FindMoreResource(Actor self)
{
var res = self.World.WorldActor.traits.Get<ResourceLayer>();
var harv = self.Info.Traits.Get<HarvesterInfo>();
self.QueueActivity(new Move(
() =>
{
var search = new PathSearch
{
heuristic = loc => (self.World.Map.ContainsResource(loc) ? 0 : 1),
heuristic = loc => (res.GetResource(loc) != null
&& harv.Resources.Contains( res.GetResource(loc).Name )) ? 0 : 1,
umt = UnitMovementType.Wheel,
checkForBlocked = true
};

View File

@@ -19,24 +19,23 @@
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits.Activities;
namespace OpenRA.Traits
{
class HarvesterInfo : ITraitInfo
{
public readonly int BailCount = 28;
public readonly int Capacity = 28;
public readonly int PipCount = 7;
public readonly string[] Resources = { };
public object Create(Actor self) { return new Harvester(self); }
}
public class Harvester : IIssueOrder, IResolveOrder, IPips
{
[Sync]
public int oreCarried = 0; /* sum of these must not exceed capacity */
[Sync]
public int gemsCarried = 0;
Dictionary<ResourceTypeInfo, int> contents = new Dictionary<ResourceTypeInfo, int>();
Actor self;
public Harvester(Actor self)
@@ -44,21 +43,19 @@ namespace OpenRA.Traits
this.self = self;
}
public bool IsFull { get { return oreCarried + gemsCarried == self.Info.Traits.Get<HarvesterInfo>().BailCount; } }
public bool IsEmpty { get { return oreCarried == 0 && gemsCarried == 0; } }
public bool IsFull { get { return contents.Values.Sum() == self.Info.Traits.Get<HarvesterInfo>().Capacity; } }
public bool IsEmpty { get { return contents.Values.Sum() == 0; } }
public void AcceptResource(bool isGem)
public void AcceptResource(ResourceTypeInfo type)
{
if (isGem) gemsCarried++;
else oreCarried++;
if (!contents.ContainsKey(type)) contents[type] = 1;
else contents[type]++;
}
public void Deliver(Actor self, Actor proc)
{
proc.Owner.GiveOre(oreCarried * Rules.General.GoldValue);
proc.Owner.GiveOre(gemsCarried * Rules.General.GemValue);
oreCarried = 0;
gemsCarried = 0;
proc.Owner.GiveOre(contents.Sum(kv => kv.Key.ValuePerUnit * kv.Value));
contents.Clear();
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
@@ -70,7 +67,10 @@ namespace OpenRA.Traits
&& underCursor.traits.Contains<IAcceptOre>() && !IsEmpty)
return new Order("Deliver", self, underCursor);
if (underCursor == null && self.World.Map.ContainsResource(xy))
var res = self.World.WorldActor.traits.Get<ResourceLayer>().GetResource(xy);
var info = self.Info.Traits.Get<HarvesterInfo>();
if (underCursor == null && res != null && info.Resources.Contains(res.Name))
return new Order("Harvest", self, xy);
return null;
@@ -94,16 +94,12 @@ namespace OpenRA.Traits
public IEnumerable<PipType> GetPips(Actor self)
{
int numPips = self.Info.Traits.Get<HarvesterInfo>().PipCount;
int n = contents.Values.Sum();
for (int i = 0; i < numPips; i++)
{
if (gemsCarried * 1.0f / self.Info.Traits.Get<HarvesterInfo>().BailCount > i * 1.0f / numPips)
{
yield return PipType.Red;
continue;
}
if ((gemsCarried + oreCarried) * 1.0f / self.Info.Traits.Get<HarvesterInfo>().BailCount > i * 1.0f / numPips)
// todo: pip colors based on ResourceTypeInfo
if (n * 1.0f / self.Info.Traits.Get<HarvesterInfo>().Capacity > i * 1.0f / numPips)
{
yield return PipType.Yellow;
continue;

View File

@@ -18,17 +18,21 @@
*/
#endregion
using System;
using System.Linq;
namespace OpenRA.Traits
{
class SeedsOreInfo : ITraitInfo
class SeedsResourceInfo : ITraitInfo
{
public readonly float Chance = .05f;
public readonly int Interval = 5;
public readonly string ResourceType = "Ore";
public object Create(Actor self) { return new SeedsOre(); }
public object Create(Actor self) { return new SeedsResource(); }
}
class SeedsOre : ITick
class SeedsResource : ITick
{
int ticks;
@@ -36,13 +40,21 @@ namespace OpenRA.Traits
{
if (--ticks <= 0)
{
var info = self.Info.Traits.Get<SeedsOreInfo>();
var info = self.Info.Traits.Get<SeedsResourceInfo>();
var resourceType = self.World.WorldActor.Info.Traits
.WithInterface<ResourceTypeInfo>()
.FirstOrDefault(t => t.Name == info.ResourceType);
if (resourceType == null)
throw new InvalidOperationException("No such resource type `{0}`".F(info.ResourceType));
var resLayer = self.World.WorldActor.traits.Get<ResourceLayer>();
for (var j = -1; j < 2; j++)
for (var i = -1; i < 2; i++)
if (self.World.SharedRandom.NextDouble() < info.Chance)
if (self.World.OreCanSpreadInto(self.Location.X + i, self.Location.Y + j))
self.World.Map.AddOre(self.Location.X + i, self.Location.Y + j);
if (self.World.IsCellBuildable(self.Location + new int2(i, j), UnitMovementType.Wheel))
resLayer.AddResource(resourceType, self.Location.X + i, self.Location.Y + j, 1);
ticks = info.Interval;
}

View File

@@ -140,4 +140,6 @@ namespace OpenRA.Traits
bool Pressed { get; }
void OnClick();
}
public interface IRenderOverlay { void Render(); }
}

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#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.
@@ -19,6 +19,7 @@
#endregion
using OpenRA.GameRules;
using OpenRA.FileFormats;
namespace OpenRA.Traits
{
@@ -29,11 +30,17 @@ namespace OpenRA.Traits
public class BuildingInfluence
{
bool[,] blocked = new bool[128, 128];
Actor[,] influence = new Actor[128, 128];
bool[,] blocked;
Actor[,] influence;
Map map;
public BuildingInfluence( Actor self )
{
map = self.World.Map;
blocked = new bool[map.MapSize, map.MapSize];
influence = new Actor[map.MapSize, map.MapSize];
self.World.ActorAdded +=
a => { if (a.traits.Contains<Building>())
ChangeInfluence(a, a.traits.Get<Building>(), true); };
@@ -45,27 +52,22 @@ namespace OpenRA.Traits
void ChangeInfluence( Actor a, Building building, bool isAdd )
{
foreach( var u in Footprint.UnpathableTiles( a.Info.Name, a.Info.Traits.Get<BuildingInfo>(), a.Location ) )
if( IsValid( u ) )
if( map.IsInMap( u ) )
blocked[ u.X, u.Y ] = isAdd;
foreach( var u in Footprint.Tiles( a.Info.Name, a.Info.Traits.Get<BuildingInfo>(), a.Location ) )
if( IsValid( u ) )
if( map.IsInMap( u ) )
influence[ u.X, u.Y ] = isAdd ? a : null;
}
bool IsValid(int2 t)
{
return !(t.X < 0 || t.Y < 0 || t.X >= 128 || t.Y >= 128);
}
public Actor GetBuildingAt(int2 cell)
{
if (!IsValid(cell)) return null;
if (!map.IsInMap(cell)) return null;
return influence[cell.X, cell.Y];
}
public bool CanMoveHere(int2 cell)
{
return IsValid(cell) && !blocked[cell.X, cell.Y];
return map.IsInMap(cell) && !blocked[cell.X, cell.Y];
}
}}

View File

@@ -1,61 +0,0 @@
#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
namespace OpenRA.Traits
{
class OreGrowthInfo : ITraitInfo
{
public readonly float Interval = 1f;
public readonly float Chance = .02f;
public readonly bool Spreads = true;
public readonly bool Grows = true;
public object Create(Actor self) { return new OreGrowth(); }
}
class OreGrowth : ITick, ILoadWorldHook
{
int remainingTicks;
public void Tick(Actor self)
{
if (--remainingTicks <= 0)
{
var info = self.Info.Traits.Get<OreGrowthInfo>();
if (info.Spreads)
Ore.SpreadOre(self.World,
self.World.SharedRandom,
info.Chance);
if (info.Grows)
Ore.GrowOre(self.World, self.World.SharedRandom);
self.World.Minimap.InvalidateOre();
remainingTicks = (int)(info.Interval * 60 * 25);
}
}
public void WorldLoaded(World w)
{
Ore.InitOreDensity(w.Map);
}
}
}

View File

@@ -0,0 +1,201 @@
#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.Graphics;
namespace OpenRA.Traits
{
class ResourceLayerInfo : ITraitInfo
{
public object Create(Actor self) { return new ResourceLayer(self); }
}
class ResourceLayer : IRenderOverlay, ILoadWorldHook
{
SpriteRenderer sr;
World w;
public ResourceTypeInfo[] resourceTypes;
CellContents[,] content;
public ResourceLayer(Actor self)
{
sr = new SpriteRenderer( Game.renderer, true );
}
public void Render()
{
var shroud = Game.world.LocalPlayer.Shroud;
var map = Game.world.Map;
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 c = content[x, y];
if (c.image != null)
sr.DrawSprite(c.image[c.density],
Game.CellSize * new int2(x, y),
c.type.Palette);
}
sr.Flush();
}
public void WorldLoaded(World w)
{
this.w = w;
content = new CellContents[w.Map.MapSize, w.Map.MapSize];
resourceTypes = w.WorldActor.Info.Traits.WithInterface<ResourceTypeInfo>().ToArray();
foreach (var rt in resourceTypes)
rt.Sprites = rt.SpriteNames.Select(a => SpriteSheetBuilder.LoadAllSprites(a)).ToArray();
var map = w.Map;
for (int y = map.YOffset; y < map.YOffset + map.Height; y++)
for (int x = map.XOffset; x < map.XOffset + map.Width; x++)
{
content[x,y].type = resourceTypes.FirstOrDefault(
r => r.Overlays.Contains(w.Map.MapTiles[x, y].overlay));
if (content[x, y].type != null)
content[x, y].image = ChooseContent(content[x, y].type);
}
for (int y = map.YOffset; y < map.YOffset + map.Height; y++)
for (int x = map.XOffset; x < map.XOffset + map.Width; x++)
if (content[x, y].type != null)
content[x, y].density = GetIdealDensity(x, y);
}
public Sprite[] ChooseContent(ResourceTypeInfo info)
{
return info.Sprites[w.SharedRandom.Next(info.Sprites.Length)];
}
public int GetAdjacentCellsWith(ResourceTypeInfo info, int i, int j)
{
int sum = 0;
for (var u = -1; u < 2; u++)
for (var v = -1; v < 2; v++)
if (content[i+u, j+v].type == info)
++sum;
return sum;
}
public int GetIdealDensity(int x, int y)
{
return (GetAdjacentCellsWith(content[x, y].type, x, y) *
(content[x, y].image.Length - 1)) / 9;
}
public void AddResource(ResourceTypeInfo info, int i, int j, int n)
{
if (content[i, j].type == null)
{
content[i, j].type = info;
content[i, j].image = ChooseContent(info);
content[i, j].density = -1;
}
if (content[i, j].type != info)
return;
content[i, j].density = Math.Min(
content[i, j].image.Length - 1,
content[i, j].density + n);
}
public ResourceTypeInfo Harvest(int2 p)
{
var type = content[p.X,p.Y].type;
if (type == null) return null;
if (--content[p.X, p.Y].density < 0)
{
content[p.X, p.Y].type = null;
content[p.X, p.Y].image = null;
}
return type;
}
public void Destroy(int2 p)
{
content[p.X, p.Y].type = null;
content[p.X, p.Y].image = null;
content[p.X, p.Y].density = 0;
}
public void Grow(ResourceTypeInfo info)
{
var map = w.Map;
var mini = map.XOffset; var maxi = map.XOffset + map.Width;
var minj = map.YOffset; var maxj = map.YOffset + map.Height;
var newDensity = new byte[map.MapSize, map.MapSize];
for (int j = minj; j < maxj; j++)
for (int i = mini; i < maxi; i++)
if (content[i, j].type == info)
newDensity[i, j] = (byte)GetIdealDensity(i, j);
for (int j = minj; j < maxj; j++)
for (int i = mini; i < maxi; i++)
if (content[i, j].type == info && content[i, j].density < newDensity[i, j])
++content[i, j].density;
}
public void Spread(ResourceTypeInfo info)
{
var map = w.Map;
var mini = map.XOffset; var maxi = map.XOffset + map.Width;
var minj = map.YOffset; var maxj = map.YOffset + map.Height;
var growMask = new bool[map.MapSize, map.MapSize];
for (int j = minj; j < maxj; j++)
for (int i = mini; i < maxi; i++)
if (content[i,j].type == null
&& GetAdjacentCellsWith(info, i,j ) > 0
&& w.IsCellBuildable(new int2(i, j), UnitMovementType.Wheel))
growMask[i, j] = true;
for (int j = minj; j < maxj; j++)
for (int i = mini; i < maxi; i++)
if (growMask[i, j])
{
content[i, j].type = info;
content[i, j].image = ChooseContent(info);
content[i, j].density = 0;
}
}
public ResourceTypeInfo GetResource(int2 p) { return content[p.X, p.Y].type; }
public struct CellContents
{
public ResourceTypeInfo type;
public Sprite[] image;
public int density;
}
}
}

View File

@@ -0,0 +1,67 @@
#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.Graphics;
namespace OpenRA.Traits
{
public class ResourceTypeInfo : ITraitInfo
{
public readonly string[] Overlays = { };
public readonly string[] SpriteNames = { };
public readonly string Palette = "terrain";
public readonly int ValuePerUnit = 0;
public readonly string Name = null;
public readonly float GrowthInterval = 0;
public readonly float SpreadInterval = 0;
public Sprite[][] Sprites;
public object Create(Actor self) { return new ResourceType(this); }
}
class ResourceType : ITick
{
int growthTicks;
int spreadTicks;
ResourceTypeInfo info;
public ResourceType(ResourceTypeInfo info) { this.info = info; }
public void Tick(Actor self)
{
if (info.GrowthInterval != 0 && --growthTicks <= 0)
{
growthTicks = (int)(info.GrowthInterval * 25 * 60);
self.World.WorldActor.traits.Get<ResourceLayer>().Grow(info);
self.World.Minimap.InvalidateOre();
}
if (info.SpreadInterval != 0 && --spreadTicks <= 0)
{
spreadTicks = (int)(info.SpreadInterval * 25 * 60);
self.World.WorldActor.traits.Get<ResourceLayer>().Spread(info);
self.World.Minimap.InvalidateOre();
}
}
}
}

View File

@@ -0,0 +1,32 @@
#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
namespace OpenRA.Traits
{
class TheaterInfo : StatelessTraitInfo<Theater>
{
public readonly string Name = null;
public readonly string Theater = null;
public readonly string Suffix = null;
public readonly string Tileset = null;
public readonly string Templates = null;
}
class Theater {}
}

View File

@@ -1,4 +1,4 @@
#region Copyright & License Information
#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.
@@ -32,12 +32,14 @@ namespace OpenRA.Traits
public class UnitInfluence : ITick
{
List<Actor>[,] influence = new List<Actor>[128, 128];
List<Actor>[,] influence;
public UnitInfluence( Actor self )
{
for (int i = 0; i < 128; i++)
for (int j = 0; j < 128; j++)
int size = self.World.Map.MapSize;
influence = new List<Actor>[size, size];
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
influence[ i, j ] = new List<Actor>();
self.World.ActorRemoved += a => Remove( a, a.traits.GetOrDefault<IOccupySpace>() );
@@ -73,8 +75,8 @@ namespace OpenRA.Traits
[Conditional( "SANITY_CHECKS" )]
void SanityCheck( Actor self )
{
for( int y = 0 ; y < 128 ; y++ )
for( int x = 0 ; x < 128 ; x++ )
for( int y = 0 ; y < self.World.Map.MapSize ; y++ )
for( int x = 0 ; x < self.World.Map.MapSize ; x++ )
if( influence[ x, y ] != null )
foreach (var a in influence[ x, y ])
if (!a.traits.Get<IOccupySpace>().OccupiedCells().Contains( new int2( x, y ) ) )

View File

@@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRA.Traits
{
class WallLoadHookInfo : ITraitInfo
{
public readonly string[] OverlayTypes = { };
public readonly string ActorType = "brik";
public object Create(Actor self) { return new WallLoadHook( self, this ); }
}
class WallLoadHook : ILoadWorldHook
{
WallLoadHookInfo info;
public WallLoadHook(Actor self, WallLoadHookInfo info)
{
this.info = info;
}
public void WorldLoaded(World w)
{
var map = w.Map;
for (int y = map.YOffset; y < map.YOffset + map.Height; y++)
for (int x = map.XOffset; x < map.XOffset + map.Width; x++)
if (info.OverlayTypes.Contains(w.Map.MapTiles[x, y].overlay))
w.CreateActor(info.ActorType, new int2(x, y), w.players[0]); // todo: neutral player or null?
}
}
}

View File

@@ -56,8 +56,8 @@ namespace OpenRA
public void Draw( World world )
{
if (ShowUnitDebug)
for (var j = 0; j < 128; j++)
for (var i = 0; i < 128; i++)
for (var j = 0; j < world.Map.MapSize; j++)
for (var i = 0; i < world.Map.MapSize; i++)
if (world.WorldActor.traits.Get<UnitInfluence>().GetUnitsAt(new int2(i, j)).Any())
spriteRenderer.DrawSprite(unitDebug, Game.CellSize * new float2(i, j), "terrain");
}
@@ -67,10 +67,11 @@ namespace OpenRA
var position = Game.controller.MousePosition.ToInt2();
var topLeft = position - Footprint.AdjustForBuildingSize( bi );
var isCloseEnough = world.IsCloseEnoughToBase(world.LocalPlayer, name, bi, topLeft);
var res = world.WorldActor.traits.Get<ResourceLayer>();
foreach( var t in Footprint.Tiles( name, bi, topLeft ) )
spriteRenderer.DrawSprite( ( isCloseEnough && world.IsCellBuildable( t, bi.WaterBound
? UnitMovementType.Float : UnitMovementType.Wheel ) && !world.Map.ContainsResource( t ) )
? UnitMovementType.Float : UnitMovementType.Wheel ) && res.GetResource(t) == null )
? buildOk : buildBlocked, Game.CellSize * t, "terrain" );
// Linebuild for walls.

View File

@@ -59,15 +59,14 @@ namespace OpenRA
Game.IssueOrder(Order.Chat("/name " + Game.Settings.PlayerName));
}
public readonly Actor WorldActor;
public readonly Actor WorldActor;
public readonly PathFinder PathFinder;
public readonly Map Map;
public readonly TileSet TileSet;
// for tricky things like bridges.
public readonly ICustomTerrain[,] customTerrain = new ICustomTerrain[128, 128];
public readonly ICustomTerrain[,] customTerrain;
public readonly WorldRenderer WorldRenderer;
internal readonly Minimap Minimap;
@@ -76,9 +75,13 @@ namespace OpenRA
{
Timer.Time( "----World.ctor" );
Map = new Map( Rules.AllRules );
Map = new Map( Game.LobbyInfo.GlobalSettings.Map );
customTerrain = new ICustomTerrain[Map.MapSize, Map.MapSize];
Timer.Time( "new Map: {0}" );
TileSet = new TileSet( Map.TileSuffix );
var theaterInfo = Rules.Info["world"].Traits.WithInterface<TheaterInfo>().FirstOrDefault(t => t.Theater == Map.Theater);
TileSet = new TileSet(theaterInfo.Tileset, theaterInfo.Templates, theaterInfo.Suffix);
SpriteSheetBuilder.Initialize( Map );
Timer.Time( "Tileset: {0}" );

View File

@@ -149,8 +149,9 @@ namespace OpenRA
public static bool CanPlaceBuilding(this World world, string name, BuildingInfo building, int2 topLeft, Actor toIgnore)
{
var res = world.WorldActor.traits.Get<ResourceLayer>();
return !Footprint.Tiles(name, building, topLeft).Any(
t => !world.Map.IsInMap(t.X, t.Y) || world.Map.ContainsResource(t) || !world.IsCellBuildable(t,
t => !world.Map.IsInMap(t.X, t.Y) || res.GetResource(t) != null || !world.IsCellBuildable(t,
building.WaterBound ? UnitMovementType.Float : UnitMovementType.Wheel,
toIgnore));
}
@@ -199,22 +200,6 @@ namespace OpenRA
return xy;
}
public static void LoadMapActors(this World world, IniFile mapfile)
{
var toLoad =
mapfile.GetSection("STRUCTURES", true)
.Concat(mapfile.GetSection("UNITS", true));
foreach (var s in toLoad)
{
//num=owner,type,health,location,facing,...
var parts = s.Value.Split( ',' );
var loc = int.Parse(parts[3]);
world.CreateActor(parts[1].ToLowerInvariant(), new int2(loc % 128, loc / 128),
world.players.Values.FirstOrDefault(p => p.InternalName == parts[0]) ?? world.players[0]);
}
}
public static int2 ChooseRandomEdgeCell(this World w)
{
var isX = w.SharedRandom.Next(2) == 0;

625
doc/cncff.txt Normal file
View File

@@ -0,0 +1,625 @@
COMMAND & CONQUER FILE FORMATS
Revision 4
by Vladan Bato (bat22@geocities.com)
This document explains the file formats used by Command & Conquer.
Command & Conquer is a tradmark of Westwood Studios, Inc.
Command & Conquer is Copyright (C)1995 Westwood Studios, Inc.
The information provided here is meant for programmers that want to make
editor and utilites for Command & Conquer. My explanation might not be
the best one, but it should be enough.
I can't guarantee that the information in here is correct. If you find any
errors, please report them to me.
In this document I'll use Pascal notation, and any code samples will be in
Pascal....
I wanted to rewrite them in C, but I don't have the time to test the code.
So, to avoid any risks, I'll use the code from Mix Manager.
In case you don't know, the information contained here has been used to
make the program Mix Manager, which contains a lot of conversion utilities
for the various formats. For more info, check my homepage (see the end of
the document).
===================
1. THE .MIX FILES
===================
You probably already know the format of these files, but I will add a
description here for completeness.
The MIX file consists of two parts :
-A header including the index of all the files contained within
-A body containing all the files
It's format is :
Header : record
NumFiles : word; {Number of files in MIX}
DataSize : longint; {Size of body}
Index : array [1..NumFiles] of
record
ID : longint; {File ID}
Start : longint; {Offset of file from the start of the
body}
Size : longint; {file size}
end;
end;
The ID field is computed from the original filename, which is not stored in
the MIX.
The records are always sorted by the ID field (the numbers are signed
longints).
Note that the offsets are relative to the start of the body so to find the
actual offset in the MIX you have to add the size of the header which is
NumFiles*12+6
===================
2. THE .PAL FILES
===================
The most easiest files....
These files contain the palette in the same format used by VGA cards.
Palette : array [0..255] of record
red,green,blue:byte;
end;
Note that only the first 6 bits of each number are used, giving a total of
262144 possible colors (as opposed to the 8 bits used by .PCX files for
example).
=================================
3. THE TEMPLATE AND .BIN FILES
=================================
The Template files contain the map graphics, and can be found in the
theater specific MIX files (TEMPERAT.MIX, WINTER.MIX, DESERT.MIX).
The .BIN files contain the maps for the missions and are used in conjunction
with the .INI files.
I won't explain them here. They are explained with great detail in the
document titled "Command & Conquer maps" I wrote some time ago.
The said document can be found on my homepage.
===================
5. THE .SHP FILES
===================
The .SHP files contain almost all the graphics : units, structures,
trees,...
The header has the following structure :
Header : record
NumImages : word; {Number of images}
A,B : word; {Unknown}
Width,
Height : word; {Width and Height of the images}
C : longint; {Unknown}
end;
If you know something about those unknown fields, please e-mail me.
Following that there's an array of records, one for each image :
Offsets : array [0..NumImages+1] of
record
Offset : longint; {Offset and format of image in file}
RefOffs : longint; {Offset and format of image on
which it is based}
end;
The most significant byte (last) of the Offset and RefOffs fields
contains the format, while the lower three are used for the offset.
The format byte can have one of the three values : 80h, 40h, 20h.
I will call the three image formats Format80, Format40 and Format20.
The Format80 images are compressed with a compression method I'll explain
later.
The Format40 images must be xor-ed with a Format80 image. That's what the
RefOffs field is used for. It tells which Format80 image they are
based upon. The Format40 will be explained in detail later.
The Format20 images use the same format as the Format40, the difference is
that they are xor-ed with the image that precedes them in the file. That can
be either in Format20 or in Format40.
The offset part of the RefOffs field contains the number of the first
Format40 image in the chain, and the format field is always 48h.
Here's an example :
0) Off0(three bytes) 80h 000000h 00h
1) Off1(three bytes) 80h 000000h 00h
2) Off2(three bytes) 40h Off1 80h
3) Off3(three bytes) 80h 000000h 00h
4) Off4(three bytes) 40h Off1 80h
5) Off5(three bytes) 20h 000400h 48h
6) Off6(three bytes) 20h 000400h 48h
7) Off7(three bytes) 40h Off3 80h
For example to draw image 7, you have to draw the image 3 first (whose
offset
and format are given) and then xor image 7 over it.
To draw image 6, you have to xor it over the previous image, i.e. 5, which
is format20 again, that means that it has to be xor-ed over image 4, which
is in format40, i.e. it must be xor-ed over the image in format80 it has a
reference to. In this case it's image 1. Thus the chain is 1,4,5,6.
This is one way to see it, the other could be :
Image 6 is in Format20, the RefOffs field contains the number of the first
Format40 image in the chain, in this case image 4. To draw Image 4, the
Image 1 has to be drawn first, next is image 4, and then all the images
from the 4th to the 6th have to be xor-ed over the previous.
I made some experiments and found out that you don't have to use the
Format40 and Format20 images. I tried converting all of them into Format80
and it worked.
Also, when changing graphics, note that all the unit and structure graphics
should be drawn using the GDI colors, which will be automatically converted
for the other sides.
The palette you should use is one of those found in DESERT.MIX, WINTER.MIX
and TEMPERAT.MIX. The GDI colors are colors 0B0h-0BFh. The other colors
won't be converted and will remain the same for all the sides (be sure to
use only the colors that are the same all three palettes).
The above applies only to the graphics that appear in all three theaters
(the .SHP file found in CONQUER.MIX). The graphics for the structures and
overlays that appear in a single theater (found inside the theater specific
MIX) can use the palette entries that are unique for that theater (and will
be shown with garbled colors in the others).
Also a special color is used for shadows. It's color 04h. In the palettes
it's bright green, but C&C puts a shadow instead of it. I don't know how
the shadows are calculated however.
You should've noticed that the array has NumImages+2 elements when only
NumImages elements are needed. The last one contains zeros, and the one
before
that points to the end of the file. These two can be used to identify the
file as a .SHP.
Here's the description of the compression formats : Format80 and Format40.
----------
Format80
----------
There are several different commands, with different sizes : form 1 to 5
bytes.
The positions mentioned below always refer to the destination buffer (i.e.
the uncompressed image). The relative positions are relative to the current
position in the destination buffer, which is one byte beyond the last
written
byte.
I will give some sample code at the end.
(1) 1 byte
+---+---+---+---+---+---+---+---+
| 1 | 0 | | | | | | |
+---+---+---+---+---+---+---+---+
\_______________________/
|
Count
This one means : copy next Count bytes as is from Source to Dest.
(2) 2 bytes
+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
| 0 | | | | | | | | | | | | | | | | |
+---+---+---+---+---+---+---+---+ +---+---+---+---+---+---+---+---+
\___________/\__________________________________________________/
| |
Count-3 Relative Pos.
This means copy Count bytes from Dest at Current Pos.-Rel. Pos. to
Current position.
Note that you have to add 3 to the number you find in the bits 4-6 of the
first byte to obtain the Count.
Note that if the Rel. Pos. is 1, that means repeat Count times the
previous
byte.
(3) 3 bytes
+---+---+---+---+---+---+---+---+ +---------------+---------------+
| 1 | 1 | | | | | | | | | |
+---+---+---+---+---+---+---+---+ +---------------+---------------+
\_______________________/ Pos
|
Count-3
Copy Count bytes from Pos, where Pos is absolute from the start of the
destination buffer. (Pos is a word, that means that the images can't be
larger than 64K)
(4) 4 bytes
+---+---+---+---+---+---+---+---+ +-------+-------+ +-------+
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | | | | | |
+---+---+---+---+---+---+---+---+ +-------+-------+ +-------+
Count Color
Write Color Count times.
(Count is a word, color is a byte)
(5) 5 bytes
+---+---+---+---+---+---+---+---+ +-------+-------+ +-------+-------+
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | | | | | | |
+---+---+---+---+---+---+---+---+ +-------+-------+ +-------+-------+
Count Pos
Copy Count bytes from Dest. starting at Pos. Pos is absolute from the
start of the Destination buffer.
Both Count and Pos are words.
These are all the commands I found out. Maybe there are other ones, but I
haven't seen them yet.
All the images end with a 80h command.
To make things more clearer here's a piece of code that will uncompress the
image.
DP = destination pointer
SP = source pointer
Source and Dest are the two buffers
SP:=0;
DP:=0;
repeat
Com:=Source[SP];
inc(SP);
b7:=Com shr 7; {b7 is bit 7 of Com}
case b7 of
0 : begin {copy command (2)}
{Count is bits 4-6 + 3}
Count:=(Com and $7F) shr 4 + 3;
{Position is bits 0-3, with bits 0-7 of next byte}
Posit:=(Com and $0F) shl 8+Source[SP];
Inc(SP);
{Starting pos=Cur pos. - calculated value}
Posit:=DP-Posit;
for i:=Posit to Posit+Count-1 do
begin
Dest[DP]:=Dest[i];
Inc(DP);
end;
end;
1 : begin
{Check bit 6 of Com}
b6:=(Com and $40) shr 6;
case b6 of
0 : begin {Copy as is command (1)}
Count:=Com and $3F; {mask 2 topmost bits}
if Count=0 then break; {EOF marker}
for i:=1 to Count do
begin
Dest[DP]:=Source[SP];
Inc(DP);
Inc(SP);
end;
end;
1 : begin {large copy, very large copy and fill commands}
{Count = (bits 0-5 of Com) +3}
{if Com=FEh then fill, if Com=FFh then very large copy}
Count:=Com and $3F;
if Count<$3E then {large copy (3)}
begin
Inc(Count,3);
{Next word = pos. from start of image}
Posit:=Word(Source[SP]);
Inc(SP,2);
for i:=Posit to Posit+Count-1 do
begin
Dest[DP]:=Dest[i];
Inc(DP);
end;
end
else if Count=$3F then {very large copy (5)}
begin
{next 2 words are Count and Pos}
Count:=Word(Source[SP]);
Posit:=Word(Source[SP+2]);
Inc(SP,4);
for i:=Posit to Posit+Count-1 do
begin
Dest[DP]:=Dest[i];
Inc(DP);
end;
end else
begin {Count=$3E, fill (4)}
{Next word is count, the byte after is color}
Count:=Word(Source[SP]);
Inc(SP,2);
b:=Source[SP];
Inc(SP);
for i:=0 to Count-1 do
begin
Dest[DP]:=b;
inc(DP);
end;
end;
end;
end;
end;
end;
until false;
Note that you won't be able to compile this code, because the typecasting
won't work. (But I'm sure you'll be able to fix it).
----------
Format40
----------
As I said before the images in Format40 must be xor-ed over a previous
image, or against a black screen (as in the .WSA format).
It is used when there are only minor changes between an image and a
following one.
Here I'll assume that the old image is in Dest, and that the Dest pointer is
set to the beginning of that buffer.
As for the Format80, there are many commands :
(1) 1 byte
byte
+---+---+---+---+---+---+---+---+
| 1 | | | | | | | |
+---+---+---+---+---+---+---+---+
\___________________________/
|
Count
Skip count bytes in Dest (move the pointer forward).
(2) 3 bytes
byte word
+---+---+---+---+---+---+---+---+ +---+-----+-------+
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 0 | ... | |
+---+---+---+---+---+---+---+---+ +---+-----+-------+
\_____________/
|
Count
Skip count bytes.
(3) 3 bytes
byte word
+---+---+---+---+---+---+---+---+ +---+---+-----+-------+
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 1 | 0 | ... | |
+---+---+---+---+---+---+---+---+ +---+---+-----+-------+
\_____________/
|
Count
Xor next count bytes. That means xor count bytes from Source with bytes
in Dest.
(4) 4 bytes
byte word byte
+---+---+---+---+---+---+---+---+ +---+---+-----+-------+ +-------+
| 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | 1 | 1 | ... | | | |
+---+---+---+---+---+---+---+---+ +---+---+-----+-------+ +-------+
\_____________/ value
|
Count
Xor next count bytes in Dest with value.
5) 1 byte
byte
+---+---+---+---+---+---+---+---+
| 0 | | | | | | | |
+---+---+---+---+---+---+---+---+
\___________________________/
|
Count
Xor next count bytes from source with dest.
6) 3 bytes
byte byte byte
+---+---+---+---+---+---+---+---+ +-------+ +-------+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | | | | |
+---+---+---+---+---+---+---+---+ +-------+ +-------+
Count Value
Xor next count bytes with value.
All images end with a 80h 00h 00h command.
I think these are all the commands, but there might be some other.
If you find anything new, please e-mail me.
As before here's some code :
DP = destination pointer
SP = source pointer
Source is buffer containing the Format40 data
Dest is the buffer containing the image over which the second has
to be xor-ed
SP:=0;
DP:=0;
repeat
Com:=Source[SP];
Inc(SP);
if (Com and $80)<>0 then {if bit 7 set}
begin
if Com<>$80 then {small skip command (1)}
begin
Count:=Com and $7F;
Inc(DP,Count);
end
else {Big commands}
begin
Count:=Word(Source[SP]);
if Count=0 then break;
Inc(SP,2);
Tc:=(Count and $C000) shr 14; {Tc=two topmost bits of count}
case Tc of
0,1 : begin {Big skip (2)}
Inc(DP,Count);
end;
2 : begin {big xor (3)}
Count:=Count and $3FFF;
for i:=1 to Count do
begin
Dest[DP]:=Dest[DP] xor Source[SP];
Inc(DP);
Inc(SP);
end;
end;
3 : begin {big repeated xor (4)}
Count:=Count and $3FFF;
b:=Source[SP];
Inc(SP);
for i:=1 to Count do
begin
Dest[DP]:=Dest[DP] xor b;
Inc(DP);
end;
end;
end;
end;
end else {xor command}
begin
Count:=Com;
if Count=0 then
begin {repeated xor (6)}
Count:=Source[SP];
Inc(SP);
b:=Source[SP];
Inc(SP);
for i:=1 to Count do
begin
Dest[DP]:=Dest[DP] xor b;
Inc(DP);
end;
end else {copy xor (5)}
for i:=1 to Count do
begin
Dest[DP]:=Dest[DP] xor Source[SP];
Inc(DP);
Inc(SP);
end;
end;
until false;
===================
6. THE .CPS FILES
===================
The .CPS files contain 320x200x256 images. The images are compressed with
the Format80 compression method. They may or may not contain a palette.
The header has the following structure :
Header : record
Size : word; {File size - 2}
Unknown : word; {Always 0004h}
ImSize : word; {Size of uncompressed image (always 0FA00h)}
Palette : longint; {Is there a palette ?}
end;
If Palette is 03000000h then there's a palette after the header, otherwise
the image follows. CPS file without palette can be found in the SETUP.MIX
file, and they all use the Palette that can be found inside the same .MIX.
The image that follows the palette (or the Header) is in Format80 which is
explained above.
===================
7. THE .WSA FILES
===================
WSA files contain short animations and can be found in the GENERAL.MIX
files.
They are basically a series of Format40 images, that are then compressed
with
Format80.
The header is :
Header : record
NumFrames : word; {Number of frames}
X,Y : word; {Position on screen of the upper left
corner}
W,H : word; {Width and height of the images}
Delta : longint; {Frames/Sec = Delta/(2^10)}
end;
Following that there's an array of offsets :
Offsets : array [0..NumFrames+1] of longint;
The obtain the actual offset, you have to add 300h. That is the size of the
palette that follows the Offsets array.
As for .SHP files the two last offsets have a special meaning.
If the last offset is 0 then the one before it points to the end of file
(after you added 300h of course).
If the last one is <>0 then it points to the end of the file, and the
one before it points to a special frame that gives you the difference
between the last and the first frame. This is used when you have to loop the
animation.
As I said before, the images are in Format40 but are then compressed with
Format80. That means that you first have to uncompress the Format80 and then
decode the Format40 image you obtain.
The first frame should be xor-ed over a black image (filled with zeros), all
the other are xor-ed over the previous one.
There is a variant of the file without the palette that can be found in
SETUP.MIX but I wasn't able to decode it (maybe there are some commands I
don't know about)...
=====================
8. ADDITIONAL NOTES
=====================
The VQA files (that contain movies) have been decoded by Aaron Glover
(arn@ibm.net), and are explained in a document he wrote up.
You can find the document on my homepage (or ask him directly).
What is still missing are the .AUD files.
It seems that the AUD files use some kind of lossy sound compression,
which means that it is almost impossible to decode them.
However if someone manages to work them out, I'd really appreciate some
info.
I know my explanations are not very good, but you'll have to bear them,
unless someone else wants to rewrite this.
============
9. CREDITS
============
I wish to thank the following people :
-Andrew Griffin (buggy@adam.com.au) for starting it all.
-Aaron Glover (arn@ibm.net) and
Denis Moeller (d.moeller@rendsburg.netsurf.de) for their work on .SHP
files.
-Aaron Glover for decoding the VQA files.
-Carl Kenner (andrew.kenner@unisa.edu.au) for the info on .CPS files.
Vladan Bato (bat22@geocities.com)
http://www.geocities.com/SiliconValley/8682

500
doc/cncmap.txt Normal file
View File

@@ -0,0 +1,500 @@
THE COMMAND & CONQUER MAPS
Rev. 1f
by Vladan Bato (bat22@geocities.com)
This document explains the format of the maps and the associated graphics
files. It has also a complete listing of all available map values.
This document is meant for people who want to make a C&C scenery editor.
You can also use it to edit manually the maps but IMHO it's a suicide if you
can't see what you are doing.
ABOUT .MIX FILES
First of all I will explain the structure of MIX files, since all the
graphics are in the TEMPERAT, DESERT, and WINTER.MIX files.
Each MIX file contains several internal files that can be extracted. I will
refer to the internal files as just "files".
The MIX file is made up of two parts: the first one is the Header, the
second one is the Body that contains all the files.
The structure of the header is:
(I will use pascal notation here)
Header = record
NumFiles : word; {Number of internal files}
BodyLength : longint; {Length of the body}
Index : array [1..NumFiles] of
record
ID : longint; {ID of file, computed from filename}
Start : longint; {Offset of file in the body}
Size : longint; {Size of the file}
end;
end;
Of course you can't use directly such a structure in pascal because its
length is not fixed.
Note that the offsets are relative to the start of the body so to find the
actual offset in the MIX you have to add the size of the header which is
NumFiles*12+6
Note also that the records in the Index are not in the same order as the
files are physically stored in the MIX. In this document I will always refer
to the record number in the index and not to the file's actual position in
the MIX.
ABOUT THE MAP
All the maps are 64x64 squares large. There are 2 bytes of information for
each square, thus the file is 8192 bytes long.
The two bytes indicate which image should be placed there. The first one
indicates in which of the internal files of TEMPERAT, DESERT or WINTER.MIX
it is. Each of these internal files contains several images; the second byte
tells which of those images should be taken. The images inside one of the
internal files, if put together, form a larger image. It can be a bridge,
a road section, etc. These are called "templates". Thus, each internal file
contains a template. Each template is made of several "tiles" (images).
Each square of the map contains one tile. It's not necessary to put all
the tiles of a template on the map.
There are templates of various sizes: from 1x1 to 6x8.
(I will always write the dimensions as WidthxHeight)
The tile numbers range from 0 to WxH-1.
However there are some tiles, which I call "empty tiles", that don't have
any images associated with them. If you try using them C&C will display
the default terrain for that Theater instead. There are empty tiles
especially in the corners of large templates.
An example may be useful:
The SW-NE bridge in the temperate theater has two empty tiles:
XXOr X - Empty tile O - other tiles
wbbO b - Bridge
Obbw w - Water (This is probably wrong, I can't check now)
rOOO r - road
We can see that in the upper left corner there are two empty tiles.
We can put the values 00 and 01 in our map (as the second byte; the first
would be A5h for the bridge), in that case we'll see some grass there. But
we can replace those two tiles with anything else without disturbing the
rest. What I mean, is that if we changed any other tile, a piece of river
would be missing or a rock could be cut, ruining the map; but if we
replace the empty tiles everything is OK.
So, when we have an empty tile, we can leave it there, or replace it with
anything else. There are two exceptions to this rule however:
1) Sometimes the empty tiles should be water, but if we don't replace them
C&C will show land in the middle of our lake or sea;
2) There are templates containing roads that finish in one of the corners,
so that the next template must have an empty tile in the opposite corner
to stick to the other.
An example might help:
Imagine that we have two road sections like these:
OOrr OOrr
Orrr Orrr
rrrO rrrO
rrOO rrOO
and we want to stick them diagonally:
OOrr
Orrr
rrrO
rrOO
OOrr
Orrr
rrrO
rrOO
Something is obviously missing. We need to add some tiles to fill it like
this:
OOrr
Orrr
rrrO
RrrOO
OOrrR
Orrr
rrrO
rrOO
The solution could be to have two templates like these:
OOOO ROOO
OOOO OOOO
OOOO OOOO
OOOR OOOO
or to have one little template with empty tiles:
RX
XR (Where X are empty tiles)
Now we can put this between the two road sections, replacing the empty
tiles with the corner tiles of the road sections.
There are many road sections like this and I've indicated them with
"(Conn.)" in the table at the end of this document.
ABOUT THE DESERT, TEMPERAT, and WINTER.MIX FILES
These are the files that hold the graphics for the templates. There's one
file inside the MIX for each template, and each file has several tiles
inside.
There are other files inside the MIXes, probably for the trees and other
overlay elements but I don't know the format. If somebody knows their
format, please let me know.
In each MIX there's also a palette, the entries are:
DESERT.MIX entry n. 26
TEMPERAT.MIX entry n. 62
WINTER.MIX entry n. 62
I will now explain the format of the files with map graphics.
First of all there's a header with the following structure:
Header = record
Width : word; {Width of images, always 24 (18h)}
Heigth : word; {Heigth of images, always 24}
NumTil : word; {Number of Tiles (may differ from num. of Images}
Zero1 : word; {Seems to be always 0000h}
Size : longint; {size of file}
ImgStart : longint; {Offset of first image}
Zero2 : longint; {Seems to be always 00000000h}
ID1 : word; {Always FFFFh}
ID2 : word; {Always 1A0Dh (or 0D1Ah I can't remeber}
Index2 : longint; {Offset of Index2}
Index1 : longint; {Offset of Index1} {I will explain these
later}
end;
The images follow the header but I suppose they could be anywhere.
They are all 24x24 pixel, uncompressed and are one after the other.
Note that the number of images may differ from the number of tiles if
there are some empty tiles. If there are empty tiles, the actual number of
images can be smaller than the number of tiles. To work out the number
of images you can use the formula : (Index1-ImgStart)/(24*24).
However, you won't have to do this if the index is not corrupt.
Index1 has the following format:
Index1 : array [0..NumTil-1] of byte;
where NumTil is the number of tiles.
Each entry in Index1 corresponds to one tile, and indicates which image
(of that file) is associated with it. If the entry is FFh than that tile
is empty.
Index2 is an array of NumImages bytes where NumImages is the real
number of images in the file. However it's always filled with zeros
(sometimes there's a 1 somewhere but I don't know it's meaning).
Note that there's no way to know the dimensions (Width and Height) of the
template. If there are 6 tiles it could be 6x1, 1x6, 3x2, 2x3. I worked out
the dimensions of all templates myself (It's easy, all you have to do is to
try different widths and look at the screen).
For example a procedure that has to display template 61h, tile 3 of the
Desert theater would do:
1) Look in the table and find in which file it is in (entry 168 of
DESERT.MIX)
2) Open that file (seek it inside the MIX)
3) Read the Header
4) Read Index1 and read the 4th byte (for tile 3), let it be N
5) Seek ImgStart+Width*Height*N
6) Read the Image and display it
AND FINALLY THE TABLE
Here is the table of all available map values (template numbers), the
dimensions and the relative entries in the DESERT, WINTER, and TEMPERAT.MIX.
There's also a brief description for those that don't want or don't know
how to write an editor. However, I think that it will be difficult to stick
the templates together without seeing them.
An "x" means that the template doesn't exist in that theater. There are many
templates that exist only in one theater and will show as black holes in the
others (causing the HOM effect). The WINTER and TEMPERATE theaters are
however very similar, and differ only in a few templates.
The roads and cliffs are mostly the same for the three theaters, but be
careful about the river and coast templates because they are not the same.
V | DES | TEM | WIN | Dim. | Name | Description
-----+-----+-----+-----+-------+----------+---------------------------------
--
00h | 007 | 011 | 028 | [4x4] | CLEAR1 | Default terrain
01h | 002 | 007 | 007 | [1x1] | W1 | Water (not animated)
02h | x | 009 | 009 | [2x2] | W2 | Water
03h | x | 087 | 087 | [3x3] | SH1 | Coast WD (1)
04h | x | 106 | 105 | [3x3] | SH2 | Coast WD
05h | x | 126 | 124 | [1x1] | SH3 | Rock in water
06h | x | 143 | 140 | [2x1] | SH4 | Rock in water
07h | x | 159 | 157 | [3x3] | SH5 | Coast WD
08h | x | 018 | 017 | [3x3] | SH11 | Fjord WD
09h | x | 024 | 023 | [3x3] | SH12 | Coast WU
0Ah | x | 031 | 031 | [3x3] | SH13 | Coast WU
0Bh | x | 037 | 037 | [3x3] | SH14 | Coast WU
0Ch | x | 042 | 042 | [3x3] | SH15 | Coast WU
0Dh | 106 | 074 | 074 | [2x2] | S01 | Cliff Left Edge
0Eh | 122 | 093 | 092 | [2x3] | S02 | Cliff Wu-Wd (2)
0Fh | 138 | 112 | 110 | [2x2] | S03 | Cliff W-E
10h | 154 | 131 | 128 | [2x2] | S04 | Cliff W-E
11h | 170 | 147 | 144 | [2x2] | S05 | Cliff W-E
12h | 185 | 163 | 161 | [2x3] | S06 | Cliff Wd-Eu
13h | 200 | 180 | 179 | [2x2] | S07 | Cliff Right Edge
14h | 212 | 195 | 195 | [2x2] | S08 | Cliff Top Edge
15h | 225 | 208 | 209 | [3x2] | S09 | Cliff N-S
16h | 096 | 064 | 064 | [2x2] | S10 | Cliff N-S
17h | 108 | 078 | 078 | [2x2] | S11 | Cliff N-S
18h | 124 | 097 | 096 | [2x2] | S12 | Cliff N-S
19h | 140 | 117 | 115 | [3x2] | S13 | Cliff N-S
1Ah | 157 | 135 | 132 | [2x2] | S14 | Cliff Bottom Edge
1Bh | 172 | 151 | 149 | [2x2] | S15 | Cliff Left Edge
1Ch | 187 | 167 | 166 | [2x3] | S16 | Cliff Wu-Ed
1Dh | 202 | 184 | 184 | [2x2] | S17 | Cliff W-E
1Eh | 215 | 199 | 200 | [2x2] | S18 | Cliff W-E
1Fh | 228 | 211 | 213 | [2x2] | S19 | Cliff W-E
20h | 098 | 068 | 069 | [2x3] | S20 | Cliff Wu-Ed
21h | 110 | 082 | 082 | [1x2] | S21 | Cliff Right Edge
22h | 126 | 101 | 100 | [2x1] | S22 | Cliff Corner S-E Internal
23h | 142 | 121 | 119 | [3x2] | S23 | Cliff Sl-Nr
24h | 159 | 139 | 136 | [2x2] | S24 | Cliff N-S
25h | 174 | 155 | 153 | [2x2] | S25 | Cliff N-S
26h | 189 | 171 | 170 | [2x2] | S26 | Cliff N-S
27h | 204 | 188 | 188 | [3x2] | S27 | Cliff Nl-Sr
28h | 218 | 202 | 203 | [2x2] | S28 | Cliff Bottom Edge
29h | 230 | 213 | 215 | [2x2] | S29 | Cliff Corner N-E External
2Ah | 101 | 070 | 071 | [2x2] | S30 | Cliff Corner S-E Ext
2Bh | 113 | 084 | 084 | [2x2] | S31 | Cliff Corner W-S Ext
2Ch | 129 | 103 | 102 | [2x2] | S32 | Cliff Corner N-W Ext
2Dh | 145 | 123 | 121 | [2x2] | S33 | Cliff Corner N-E Internal
2Eh | 162 | 141 | 138 | [2x2] | S34 | Cliff Corner S-E Int
2Fh | 177 | 157 | 155 | [2x2] | S35 | Cliff Corner W-S Int
30h | 192 | 173 | 172 | [2x2] | S36 | Cliff Corner W-N Int
31h | 207 | 190 | 190 | [2x2] | S37 | Cliff Junction NW-SE
32h | 221 | 204 | 205 | [2x2] | S38 | Cliff Junction SW-NE
33h | x | 027 | 026 | [3x3] | SH32 | Coast Corner N-W Int
34h | x | 033 | 033 | [3x3] | SH33 | Coast Corner N-E Int
35h | 017 | x | x | [4x1] | SH20 | Coast WD
36h | 024 | x | x | [3x1] | SH21 | Coast WD
37h | 041 | x | x | [6x2] | SH22 | Coast WD
38h | 049 | x | x | [2x2] | SH23 | Coast WD
39h | 118 | x | x | [1x1] | BR1 | Bush
3Ah | 134 | x | x | [1x1] | BR2 | Bush
3Bh | 150 | x | x | [1x1] | BR3 | Cactus
3Ch | 166 | x | x | [1x1] | BR4 | Cactus
3Dh | 181 | x | x | [1x1] | BR5 | ??? Purple square (bug ?)
3Eh | 196 | x | x | [2x2] | BR6 | Bushes
3Fh | 210 | x | x | [2x2] | BR7 | Bushes
40h | 223 | x | x | [3x2] | BR8 | Bushes
41h | 234 | x | x | [3x2] | BR9 | Bushes
42h | 016 | x | x | [2x1] | BR10 | ??? Purple squares (bug ?)
43h | 105 | 073 | x | [1x1] | P01 | Bones / Wall (3)
44h | 121 | 092 | x | [1x1] | P02 | Bones / Wall (3)
45h | 137 | 111 | x | [1x1] | P03 | Mud / UFO (3) (6)
46h | 153 | 130 | x | [1x1] | P04 | Rock / UFO (3) (6)
47h | 169 | x | x | [2x2] | P05 | Gray Sand
48h | 184 | x | x | [6x4] | P06 | Gray Sand
49h | 199 | 179 | 178 | [4x2] | P07 | Mud
4Ah | x | 194 | 194 | [3x2] | P08 | Mud
4Bh | x | 045 | 045 | [3x2] | SH16 | Fjord WU
4Ch | 072 | 047 | 047 | [2x2] | SH17 | Water (anim.)
4Dh | 078 | 049 | 049 | [2x2] | SH18 | Water (anim.)
4Eh | 084 | x | x | [3x2] | SH19 | Coast WD
4Fh | x | 116 | 114 | [3x2] | P13 | Destroyed House
50h | x | 134 | 131 | [2x1] | P14 | Walls
51h | x | x | 148 | [4x2] | P15 | Snow
52h | 001 | 006 | 006 | [1x1] | B1 | Rock
53h | 003 | 008 | 008 | [2x1] | B2 | Rock
54h | x | 010 | 010 | [3x1] | B3 | Rock
55h | 004 | x | x | [1x1] | B4 | ?? Rock (7)
56h | 005 | x | x | [1x1] | B5 | ?? Rock (7)
57h | 006 | x | x | [1x1] | B6 | ?? Rock (7)
58h | x | 175 | 174 | [3x3] | SH6 | Coast WD
59h | x | 191 | 191 | [2x2] | SH7 | Coast Corner W-N External
5Ah | x | 205 | 206 | [3x3] | SH8 | Coast Corner S-E Ext
5Bh | x | 215 | 217 | [3x3] | SH9 | Coast Corner W-S Ext
5Ch | x | 012 | 011 | [2x2] | SH10 | Coast Corner N-E Ext
5Dh | 104 | 072 | 073 | [2x2] | D01 | Road Bottom End
5Eh | 120 | 091 | 091 | [2x2] | D02 | Road Left End
5Fh | 136 | 110 | 109 | [1x2] | D03 | Road Top End
60h | 152 | 129 | 127 | [2x2] | D04 | Road Right End
61h | 168 | 146 | 143 | [3x4] | D05 | Road S-N
62h | 183 | 162 | 160 | [2x3] | D06 | Road S-N
63h | 198 | 178 | 177 | [3x2] | D07 | Road S-N
64h | 211 | 193 | 193 | [3x2] | D08 | Road S-N
65h | 224 | 207 | 208 | [4x3] | D09 | Road W-E
66h | 095 | 063 | 063 | [4x2] | D10 | Road W-E
67h | 107 | 077 | 077 | [2x3] | D11 | Road W-E
68h | 123 | 096 | 095 | [2x2] | D12 | Road W-E
69h | 139 | 115 | 113 | [4x3] | D13 | Road Wu-Ed
6Ah | 156 | 133 | 130 | [3x3] | D14 | Road T N--W+E (4)
6Bh | 171 | 150 | 147 | [3x3] | D15 | Road Y S--N+E (4)
6Ch | 186 | 166 | 164 | [3x3] | D16 | Road Y S--N+E
6Dh | 201 | 183 | 182 | [3x2] | D17 | Road T S--W+E
6Eh | 214 | 198 | 198 | [3x3] | D18 | Road T W--N+S
6Fh | 227 | 210 | 211 | [3x3] | D19 | Road + W-N-E-S
70h | 097 | 067 | 067 | [3x3] | D20 | Road Corner N-E
71h | 109 | 081 | 081 | [3x2] | D21 | Road Corner S-E
72h | 125 | 100 | 099 | [3x3] | D22 | Road Corner W-S
73h | 141 | 120 | 118 | [3x3] | D23 | Road Corner W-N
74h | 158 | 138 | 135 | [3x3] | D24 | Road Diagonal NW-SE (5)
75h | 173 | 154 | 152 | [3x3] | D25 | Road Diag NW-SE
76h | 188 | 170 | 169 | [2x2] | D26 | Road Diag NW-SE (Conn.) (5)
77h | 203 | 187 | 187 | [2x2] | D27 | Road Diag NW-SE (Conn.)
78h | 217 | 201 | 202 | [2x2] | D28 | Road Corner W-SE (Conn.)
79h | 229 | 212 | 214 | [2x2] | D29 | Road Corner N-SE (Conn.)
7Ah | 100 | 069 | 070 | [2x2] | D30 | Road Y SE--N+W (Conn.)
7Bh | 112 | 083 | 083 | [2x2] | D31 | Road Corner E-NW (Conn.)
7Ch | 128 | 102 | 101 | [2x2] | D32 | Road Corner S-NW (Conn.)
7Dh | 144 | 122 | 120 | [2x2] | D33 | Road Y NW--S+E (Conn.)
7Eh | 161 | 140 | 137 | [3x3] | D34 | Road Diag SW-NE
7Fh | 176 | 156 | 154 | [3x3] | D35 | Road Diag SW-NE
80h | 191 | 172 | 171 | [2x2] | D36 | Road Diag SW-NE (Conn.)
81h | 206 | 189 | 189 | [2x2] | D37 | Road Diag SW-NE (Conn.)
82h | 220 | 203 | 204 | [2x2] | D38 | Road Corner E-SW (Conn.)
83h | 232 | 214 | 216 | [2x2] | D39 | Road Corner N-SW (Conn.)
84h | 103 | 071 | 072 | [2x2] | D40 | Road Y SW--N+E (Conn.)
85h | 115 | 085 | 085 | [2x2] | D41 | Road Corner W-NE (Conn.)
86h | 131 | 104 | 103 | [2x2] | D42 | Road Corner S-NE (Conn.)
87h | 147 | 124 | 122 | [2x2] | D43 | Road Y NE--W+S (Conn.)
88h | x | 017 | 016 | [5x4] | RV01 | River W-E
89h | x | 023 | 022 | [5x3] | RV02 | River W-E
8Ah | x | 030 | 030 | [4x4] | RV03 | River Wu-Ed
8Bh | x | 036 | 036 | [4x4] | RV04 | River Wd-Eu
8Ch | x | 041 | 041 | [3x3] | RV05 | River N-S
8Dh | x | 044 | 044 | [3x2] | RV06 | River N-S
8Eh | x | 046 | 046 | [3x2] | RV07 | River N-S
8Fh | x | 048 | 048 | [2x2] | RV08 | River Corner S-E
90h | x | 052 | 052 | [2x2] | RV09 | River Corner W-S
91h | x | 014 | 013 | [2x2] | RV10 | River Corner N-E
92h | x | 020 | 019 | [2x2] | RV11 | River Corner W-N
93h | x | 026 | 025 | [3x4] | RV12 | River Y N--W+S
94h | x | 032 | 032 | [4x4] | RV13 | River Y Eu--W+S
95h | 055 | x | x | [4x3] | RV14 | River W-E
96h | 060 | x | x | [4x3] | RV15 | River W-E
97h | 067 | x | x | [6x4] | RV16 | River Wd-Eu
98h | 073 | x | x | [6x5] | RV17 | River Wu-Ed
99h | 079 | x | x | [4x4] | RV18 | River N-S
9Ah | 085 | x | x | [4x4] | RV19 | River N-S
9Bh | 018 | x | x | [6x8] | RV20 | River Nr-Sl
9Ch | 025 | x | x | [5x8] | RV21 | River Nl-Sr
9Dh | 042 | x | x | [3x3] | RV22 | River Corner E-S
9Eh | 050 | x | x | [3x3] | RV23 | River Corner W-S
9Fh | 057 | x | x | [3x3] | RV24 | River Corner N-E
A0h | 062 | x | x | [3x3] | RV25 | River Corner N-W
A1h | 009 | 002 | 004 | [3x3] | FORD1 | River Crossing (Road W-E)
A2h | 010 | 003 | 005 | [3x3] | FORD2 | River Crossing (Road N-S)
A3h | 047 | 057 | 057 | [3x3] | FALLS1 | Falls W-E
A4h | 048 | 058 | 058 | [3x2] | FALLS2 | Falls N-S
A5h | x | 218 | 220 | [4x4] | BRIDGE1 | Bridge SW-NE
A6h | x | 059 | 059 | [4x4] | BRIDGE1D | Fallen Bridge SW-NE
A7h | x | 219 | 221 | [5x5] | BRIDGE2 | Bridge NW-SE
A8h | x | 060 | 060 | [5x5] | BRIDGE2D | Fallen Bridge NW-SE
A9h | 236 | x | x | [6x5] | BRIDGE3 | Bridge SW-NE
AAh | 092 | x | x | [6x5] | BRIDGE3D | Fallen Bridge SW-NE
ABh | 237 | x | x | [6x4] | BRIDGE4 | Bridge NW-SE
ACh | 093 | x | x | [6x4] | BRIDGE4D | Fallen Bridge NW-SE
ADh | 056 | x | x | [3x3] | SH24 | Fjord WD
AEh | 061 | x | x | [3x2] | SH25 | Coast WU
AFh | 068 | x | x | [3x2] | SH26 | Coast WU
B0h | 074 | x | x | [4x1] | SH27 | Coast WU
B1h | 080 | x | x | [3x1] | SH28 | Coast WU
B2h | 086 | x | x | [6x2] | SH29 | Coast WU
B3h | 019 | x | x | [2x2] | SH30 | Coast WU
B4h | 027 | x | x | [3x3] | SH31 | Fjord WU
B5h | x | x | 165 | [2x2] | P16 | Snow
B6h | x | x | 183 | [4x2] | P17 | Snow
B7h | x | x | 199 | [4x3] | P18 | Snow
B8h | x | x | 212 | [4x3] | P19 | Snow
B9h | x | x | 068 | [4x3] | P20 | Snow
BAh | x | 038 | 038 | [3x3] | SH34 | Coast WR
BBh | x | 043 | 043 | [3x3] | SH35 | Coast WL
BCh | 069 | x | x | [1x1] | SH36 | Coast Corner S-E Int
BDh | 075 | x | x | [1x1] | SH37 | Coast Corner W-S Int
BEh | 081 | x | x | [1x1] | SH38 | Coast Corner N-E Int
BFh | 087 | x | x | [1x1] | SH39 | Coast Corner N-W Int
C0h | 020 | x | x | [3x3] | SH40 | Coast Corner S-E Int
C1h | 028 | x | x | [3x3] | SH41 | Coast Corner N-W Int
C2h | 043 | x | x | [1x2] | SH42 | Coast WL
C3h | 051 | x | x | [1x3] | SH43 | Coast WL
C4h | 058 | x | x | [1x3] | SH44 | Coast WR
C5h | 063 | x | x | [1x2] | SH45 | Coast WR
C6h | 070 | x | x | [3x3] | SH46 | Coast Corner S-E Int
C7h | 076 | x | x | [3x3] | SH47 | Coast Corner S-E Int
C8h | 082 | x | x | [3x3] | SH48 | Coast Corner N-E Int
C9h | 088 | x | x | [3x3] | SH49 | Coast Corner N-W Int
CAh | 021 | x | x | [4x3] | SH50 | Coast Corner S-E Ext
CBh | 029 | x | x | [4x3] | SH51 | Coast Corner W-S Ext
CCh | 044 | x | x | [4x3] | SH52 | Coast Corner N-E Ext
CDh | 052 | x | x | [4x3] | SH53 | Coast Corner N-W Ext
CEh | 059 | x | x | [3x2] | SH54 | Coast WD
CFh | 064 | x | x | [3x2] | SH55 | Coast WD
D0h | 071 | x | x | [3x2] | SH56 | Coast WU
D1h | 077 | x | x | [3x2] | SH57 | Coast WU
D2h | 083 | x | x | [2x3] | SH58 | Coast WR
D3h | 089 | x | x | [2x3] | SH59 | Coast WR
D4h | 022 | x | x | [2x3] | SH60 | Coast WL
D5h | 030 | x | x | [2x3] | SH61 | Coast WL
D6h | 045 | x | x | [6x1] | SH62 | Coast WD
D7h | 053 | x | x | [4x1] | SH63 | Coast WD
!! Warning !!
Values from D8h-FEh will cause the game to crash (it just locks up on
my computer)!!!
The value FFh indicates the default terrain (I think the 4x4 template is
used).
Notes:
(0a) There may be some errors in this table because I typed it in a hurry
(you don't know how much time it takes), so if you find any errors
please report them to me.
(0b) The names are taken from the GAME.DAT file. I matched them with the
files in the theater mix files. The complete filenames are the names
above plus an extension that depends on the theater (.DES, .TEM, .WIN).
(1) For Coasts, WD, WU, WL, and WR mean : Water on the bottom
(Down), Top (Up), left and right.
(2) For cliffs and roads the two letters indicate from which to which side
the Road (or cliff) goes. The lowercase letter means up, down, left,
right to indicate in which part of that side it starts.
For Example:
River Wu-Ed :
OOOOO
rOOOO
OrrOO
OOOrr
OOOOO
(3) These templates exist in both the DESERT and the TEMPERATE theaters but
are not the same. I've put a description of both.
(4) For Roads:
Roads T and Y mean that the road splits in the shape of a T or a Y.
E--N+S means it starts from the east edge then splits in two parts, one
going to the north and the other to the south edge
(5) NW or any other corner means that the road ends in that corner and if it
says (Conn.), that means that it has an empty tile in that corner.
So you have to use the (Conn.) templates to stick together the other
ones.
(6) In TEMPERAT.MIX these two files are buggy, they report there are 67h
tiles, but if you look at the index you'll see they all point to the
second image which (I think) is of uniform color. Only the first tile is
ever used.
(7) These three templates don't work in C&C (HOM effect), but their graphics
exist in the DESERT.MIX file. Do not use them !
That's all. I hope this info will be used by somebody to make a scenery
editor.
Report any errors to me. Also, if you have any info about other file formats
please share it with me.

Binary file not shown.

BIN
mods/cnc/chem.shp Normal file

Binary file not shown.

View File

@@ -32,10 +32,3 @@ BR3:
Footprint: ____ ____
Dimensions: 4,2
HP: 1000
MINE:
Inherits: ^Building
RenderBuilding:
Palette: terrain
SeedsOre:
-Selectable:

View File

@@ -43,7 +43,6 @@
Building:
Dimensions: 1,1
Footprint: x
BaseNormal: no
BuildSounds: constru2.aud, hvydoor1.aud
SellSounds: cashturn.aud
RenderBuilding:
@@ -64,6 +63,7 @@
SellSounds: cashturn.aud
Capturable: false
Bib: no
BaseNormal: no
Crewed: no
Sight: 0
Wall:

BIN
mods/cnc/flame.shp Normal file

Binary file not shown.

View File

@@ -10,8 +10,6 @@ URepairPercent=20% ; [units only] percent cost to fully repair as ratio of
URepairStep=10 ; [units only] hit points to heal per repair 'tick' for units
BuildSpeed=.1 ; general build speed [time (in minutes) to produce a 1000 credit cost item]
GemValue=50 ; gem credits per 'bail' carried by a harvester
GoldValue=25 ; gold credits per 'bail' carried by a harvester
LowPowerSlowdown=3 ; slowdown factor for low power
GapRegenInterval=.1 ; gap generators will regenerate their shroud at this time interval

17
mods/cnc/missions.pkt Normal file
View File

@@ -0,0 +1,17 @@
[Missions]
SCM01EA.INI=Green Acres
SCM02EA.INI=Sand Trap
SCM03EA.INI=Lost Arena
SCM04EA.INI=River Raid
SCM05EA.INI=Eye of the Storm
SCM06EA.INI=Lakefront Clash
SCM07EA.INI=Desert Madness
SCM08EA.INI=Pitfall
SCM09EA.INI=Moosehead Barrons
SCM70EA.INI=Winter Waterland
SCM71EA.INI=One Pass Fits All
SCM72EA.INI=Straight and Narrow
SCM73EA.INI=King takes Pawn
SCM74EA.INI=Nowhere to Hide
SCM77EA.INI=Marooned
SCM96EA.INI=Tiberium Garden

View File

@@ -11,20 +11,16 @@ Packages:
mods/cnc/packages/cclocal.mix
mods/cnc/packages/speech.mix
mods/cnc/packages/conquer.mix
# mods/cnc/packages/general.mix
mods/cnc/packages/general.mix
mods/cnc/packages/sounds.mix
mods/cnc/packages/tempicnh.mix
mods/cnc/packages/updatec.mix
# Cannot qualify the RA names because they may live inside a mix
mods/cnc/packages/temperat.mix
mods/cnc/packages/winter.mix
mods/cnc/packages/desert.mix
# TODO: Obsolete conquer.mix
~main.mix
redalert.mix
speech.mix
temperat.mix
snow.mix
interior.mix
general.mix
conquer.mix
sounds.mix
LegacyRules:
mods/cnc/minimal.ini: Minimal rules definitions
@@ -44,6 +40,7 @@ Sequences:
mods/cnc/sequences-structures.xml:
mods/cnc/sequences-vehicles.xml:
mods/cnc/sequences-infantry.xml:
mods/cnc/sequences-map.xml: Trees etc
mods/cnc/sequences.xml: Obsolete; will disappear once done converting
Chrome:
@@ -52,7 +49,3 @@ Chrome:
Assemblies:
mods/cnc/OpenRA.Mods.Cnc.dll: Cnc mod traits
mods/ra/OpenRA.Mods.RA.dll: Red alert mod traits
Maps:
scg01ea.ini: GDI Mission 1
scm01ea.ini: Green Acres

207
mods/cnc/sequences-map.xml Normal file
View File

@@ -0,0 +1,207 @@
<?xml version="1.0" encoding="utf-8"?>
<sequences>
<!-- Blossom Trees -->
<unit name="split2">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="split3">
<sequence name="idle" start="0" length="1" />
</unit>
<!-- Rocks -->
<unit name="rock1">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="rock2">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="rock3">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="rock4">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="rock5">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="rock6">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="rock7">
<sequence name="idle" start="0" length="1" />
</unit>
<!-- Trees -->
<unit name="tc04">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="tc05">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="tc03">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="tc02">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="tc01">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t18">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t17">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t16">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t15">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="10" />
</unit>
<unit name="t14">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t13">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t12">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t11">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="10" />
</unit>
<unit name="t10">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t09">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t08">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t07">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t06">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t05">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t04">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t03">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t02">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t01">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<!-- Civilian Structures -->
<unit name="v01">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v02">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v03">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v04">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="2" length="1" />
<sequence name="active" start="1" length="1" />
<sequence name="damaged-active" start="3" length="1" />
</unit>
<unit name="v05">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="2" length="1" />
</unit>
<unit name="v06">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v07">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="2" length="1" />
</unit>
<unit name="v08">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v09">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v10">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v11">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v12">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v13">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v14">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v15">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v16">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v17">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v18">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v19">
<sequence name="idle" start="0" length="14" />
<sequence name="damaged-idle" start="14" length="15" />
</unit>
</sequences>

View File

@@ -85,116 +85,6 @@
<sequence name="loop" start="49" length="42" />
<sequence name="end" start="0" length="26" />
</unit>
<unit name="tc04">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="tc05">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="tc03">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="tc02">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="tc01">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t17">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t16">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t15">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="10" />
</unit>
<unit name="t14">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t13">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t12">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t11">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="10" />
</unit>
<unit name="t10">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t08">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t07">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t06">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t05">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t03">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t02">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="t01">
<sequence name="idle" start="0" length="1" />
<sequence name="burn" start="1" length="9" />
</unit>
<unit name="boxes01">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="boxes02">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="boxes03">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="boxes04">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="boxes05">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="boxes06">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="boxes07">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="boxes08">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="boxes09">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="mine">
<sequence name="idle" start="0" length="1" />
</unit>
<unit name="dragon">
<sequence name="idle" start="0" facings="32" />
</unit>
@@ -210,90 +100,6 @@
<unit name="moveflsh">
<sequence name="idle" start="0" length="5" />
</unit>
<unit name="v01">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v02">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v03">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v04">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="2" length="1" />
<sequence name="active" start="1" length="1" />
<sequence name="damaged-active" start="3" length="1" />
</unit>
<unit name="v05">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="2" length="1" />
</unit>
<unit name="v06">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v07">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="2" length="1" />
</unit>
<unit name="v08">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v09">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v10">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v11">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v12">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v13">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v14">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v15">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v16">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v17">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v18">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="1" length="1" />
</unit>
<unit name="v19">
<sequence name="idle" start="0" length="14" />
<sequence name="damaged-idle" start="14" length="15" />
</unit>
<unit name="barl">
<sequence name="idle" start="0" length="3" />
</unit>
<unit name="brl3">
<sequence name="idle" start="0" length="3" />
</unit>
<unit name="miss">
<sequence name="idle" start="0" length="1" />
<sequence name="damaged-idle" start="2" length="1" />

Binary file not shown.

View File

@@ -25,18 +25,34 @@ Player:
SpawnDefaultUnits:
World:
WaterPaletteRotation:
# WaterPaletteRotation:
BuildingInfluence:
UnitInfluence:
BridgeLoadHook:
PaletteFromFile@terrain_temperat:
# BridgeLoadHook:
PaletteFromFile@terrain_desert:
Name: terrain
Theater: desert
Filename: desert.pal
PaletteFromFile@terrain_temperate:
Name: terrain
Theater: temperat
Filename: temperat_ra.pal
Filename: temperat.pal
PaletteFromFile@terrain_winter:
Name: terrain
Theater: winter
Filename: winter.pal
PaletteFromFile@player_desert:
Name: player
Theater: desert
Filename: desert.pal
PaletteFromFile@player_temperat:
Name: player
Theater: temperat
Filename: temperat.pal
PaletteFromFile@player_winter:
Name: player
Theater: winter
Filename: winter.pal
PaletteFromFile@terrain_snow:
Name: terrain
Theater: snow
@@ -153,11 +169,6 @@ World:
B: 0
A: 180
ShroudPalette:
OreGrowth:
Interval: .3
Chance: .02
Spreads: yes
Grows: yes
Country@gdi:
Name: GDI
Race: gdi
@@ -165,4 +176,50 @@ World:
Name: Nod
Race: nod
SellButton:
RepairButton:
RepairButton:
ChoosePaletteOnSelect:
WallLoadHook@sbag:
ActorType: sbag
OverlayTypes: sbag
WallLoadHook@cycl:
ActorType: cycl
OverlayTypes: cycl
WallLoadHook@brik:
ActorType: brik
OverlayTypes: brik
WallLoadHook@fenc:
ActorType: fenc
OverlayTypes: fenc
WallLoadHook@wood:
ActorType: wood
OverlayTypes: wood
WallLoadHook@barb:
ActorType: barb
OverlayTypes: barb
ResourceLayer:
ResourceType@green-tib:
Overlays: ti1,ti2,ti3,ti4,ti5,ti6,ti7,ti8,ti9,ti10,ti11,ti12
Palette: player
SpriteNames: ti1,ti2,ti3,ti4,ti5,ti6,ti7,ti8,ti9,ti10,ti11,ti12
ValuePerUnit: 30
Name: Tiberium
GrowthInterval: 1
SpreadInterval: 6
Theater@DESERT:
Name:Desert
Theater:DESERT
Suffix:des
Templates:templates.ini
Tileset:tileSet.til
Theater@WINTER:
Name:Winter
Theater:WINTER
Suffix:win
Templates:templates.ini
Tileset:tileSet.til
Theater@TEMPERAT:
Name:Temperate
Theater:TEMPERAT
Suffix:tem
Templates:templates.ini
Tileset:tileSet.til

File diff suppressed because it is too large Load Diff

View File

@@ -1,350 +1,580 @@
; clear ground
TSI
DTW
1
ff
clear1
; clear ground
TSI
DTW
1
ffff
0000
clear1
; sandy shorelines
TS-
56
0003
sh{0:00}
; plain water
TS-
2
DTW
1
0001
w{0}
w1
; cliffs
TS-
-TW
1
0002
w2
; Coastline
-TW
5
0003
sh{0}
-TW
1
0008
sh11
-TW
1
0009
sh12
-TW
1
000A
sh13
-TW
1
000B
sh14
-TW
1
000C
sh15
; Cliffs
DTW
38
0087
000D
s{0:00}
; rocky coast
TS-
38
003b
wc{0:00}
; More Coast
-TW
1
0033
sh32
-TW
1
0034
sh33
D--
1
0035
sh20
D--
1
0036
sh21
D--
1
0037
sh22
D--
1
0038
sh23
; Bushes
D--
10
0039
br{0}
; Bones/Wall/Mud/UFO/Rock
DT-
4
0043
p{0:00}
D--
1
0047
p05
D--
1
0048
p06
DTW
1
0049
p07
-TW
1
004A
p08
; Ford WU
-TW
1
004B
sh16
; Water
DTW
1
004C
sh17
; Water
DTW
1
004D
sh18
D--
1
004E
sh19
; Destroyed house
-TW
1
004F
p13
; Walls
-TW
1
0050
p14
; Snow
--W
1
0051
p15
; Rocks
DTW
2
0052
b{0}
-TW
1
0054
b3
D--
1
0055
b4
D--
1
0056
b5
D--
1
0057
b6
; More coast
-TW
1
0058
sh6
-TW
1
0059
sh7
-TW
1
005A
sh8
-TW
1
005B
sh9
-TW
1
005C
sh10
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; ROADS
; roads
TS-
DTW
43
00ad
005d
d{0:00}
; road
TS-
1
00e3
d44
; road
TS-
1
00e4
d45
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; RIVERS
; river emerging from cliff
TS-
4
00e7
rc{0:00}
; rivers
TS-
-TW
13
0070
0088
rv{0:00}
; river
TS-
D--
1
00e5
0095
rv14
; river
TS-
D--
1
00e6
0096
rv15
D--
1
0097
rv16
D--
1
0098
rv17
D--
1
0099
rv18
D--
1
009A
rv19
D--
1
009B
rv20
D--
1
009C
rv21
D--
1
009D
rv22
D--
1
009E
rv23
D--
1
009F
rv24
D--
1
00A0
rv25
; River Crossing
DTW
2
00A1
ford{0}
; Waterfalls
DTW
2
00A3
falls{0}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; BRIDGES
; long bridge (north end)
TS-
1
00eb
br1a
; long bridge (north end, damaged)
TS-
1
00ec
br1b
; long bridge (north end, broken)
TS-
1
00ed
br1c
; long bridge (south end)
TS-
1
00ee
br2a
; long bridge (south end, damaged)
TS-
1
00ef
br2b
; long bridge (south end, broken)
TS-
1
00f0
br2c
; long bridge (middle)
TS-
1
00f1
br3a
; long bridge (middle, damaged)
TS-
1
00f2
br3b
; long bridge (middle, broken)
TS-
1
00f3
br3c
; long bridge (middle, broken, south end missing)
TS-
1
00f4
br3d
; long bridge (middle, broken, north end missing)
TS-
1
00f5
br3e
; long bridge (water / totally broken)
TS-
1
00f6
br3f
; long bridge (north surround)
TS-
1
017c
br1x
; long bridge (south surround)
TS-
1
017d
br2x
; short bridge "/"
TS-
-TW
1
0083
00A5
bridge1
; short bridge "/" (destroyed)
TS-
-TW
1
0084
00A6
bridge1d
; short bridge "/" (damaged)
TS-
1
017a
bridge1h
; short bridge "/" (surround)
TS-
1
017e
bridge1x
; short bridge "\"
TS-
-TW
1
0085
00A7
bridge2
; short bridge "\" (destroyed)
TS-
-TW
1
0086
00A8
bridge2d
; short bridge "\" (damaged)
TS-
; desert short bridge "/"
D--
1
017b
bridge2h
00A9
bridge3
; short bridge "\" (surround)
TS-
; desert short bridge "/" (destroyed)
D--
1
017f
bridge2x
00AA
bridge3d
; fyords
TS-
6
00f7
f{0:00}
; short fyord
TS-
2
0081
ford{0}
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; WATERFALLS
; waterfall (E-W)
TS-
; desert short bridge "\"
D--
1
007d
falls1
00AB
bridge4
; waterfall (E-W, into sea)
TS-
; desert short bridge "\" (destroyed)
D--
1
007e
falls1a
00AC
bridge4d
; waterfall (N-S)
TS-
; Desert coast
D--
1
007f
falls2
00AD
sh24
; waterfall (N-S, into sea)
TS-
D--
1
0080
falls2a
00AE
sh25
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; DEBRIS / ROCKS
; random rock chunks (impassable)
TS-
3
0061
b{0}
; random rock chunks (passable)
TS-
11
00d8
rf{0:00}
; random debris
TS-
4
0067
p{0:00}
; random debris
TS-
D--
1
006b
p07
00AF
sh26
; random debris
TS-
D--
1
006c
p08
00B0
sh27
; random debris
TS-
D--
1
006d
p13
00B1
sh28
; random debris
TS-
D--
1
006e
p14
00B2
sh29
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; INTERIOR
D--
1
00B3
sh30
; walls
--I
49
0149
wall{0:0000}
D--
1
00B4
sh31
; black floor
--I
7
010c
flor{0:0000}
; Snow
--W
1
00B5
p16
; walls with stuff, random tiles
--I
16
0180
xtra{0:0000}
--W
1
00B6
p17
; black/yellow stripe
--I
11
013e
strp{0:0000}
--W
1
00B7
p18
; red stripe
--I
15
00fd
arro{0:0000}
--W
1
00B8
p19
; white floor
--I
5
0113
gflr{0:0000}
--W
1
00B9
p20
; white floor with black/yellow stripe
--I
11
0118
gstr{0:0000}
; More coast
-TW
1
00BA
sh34
-TW
1
00BB
sh35
D--
1
00BC
sh36
D--
1
00BD
sh37
D--
1
00BE
sh38
D--
1
00BF
sh39
D--
1
00C0
sh40
D--
1
00C1
sh41
D--
1
00C2
sh42
D--
1
00C3
sh43
D--
1
00C4
sh44
D--
1
00C5
sh45
D--
1
00C6
sh46
D--
1
00C7
sh47
D--
1
00C8
sh48
D--
1
00C9
sh49
D--
1
00CA
sh50
D--
1
00CB
sh51
D--
1
00CC
sh52
D--
1
00CD
sh53
D--
1
00CE
sh54
D--
1
00CF
sh55
D--
1
00D0
sh56
D--
1
00D1
sh57
D--
1
00D2
sh58
D--
1
00D3
sh59
D--
1
00D4
sh60
D--
1
00D5
sh61
D--
1
00D6
sh62
D--
1
00D7
sh63
; bogus
TS-

View File

@@ -1,3 +1,31 @@
SPLIT2:
Inherits: ^Building
RenderBuilding:
Palette: terrain
SeedsResource:
ResourceType:Tiberium
-Selectable:
SPLIT3:
Inherits: ^Building
RenderBuilding:
Palette: terrain
SeedsResource:
ResourceType:Tiberium
-Selectable:
ROCK1:
Inherits: ^Tree
ROCK2:
Inherits: ^Tree
ROCK3:
Inherits: ^Tree
ROCK4:
Inherits: ^Tree
ROCK5:
Inherits: ^Tree
ROCK6:
Inherits: ^Tree
ROCK7:
Inherits: ^Tree
T01:
Inherits: ^Tree
@@ -8,6 +36,9 @@ T02:
T03:
Inherits: ^Tree
T04:
Inherits: ^Tree
T05:
Inherits: ^Tree
@@ -23,6 +54,12 @@ T08:
Footprint: x_
Dimensions: 2,1
T09:
Inherits: ^Tree
Building:
Footprint: x_
Dimensions: 2,1
T10:
Inherits: ^Tree
Building:
@@ -55,7 +92,12 @@ T16:
T17:
Inherits: ^Tree
Building:
Footprint: ___ xx_
Dimensions: 3,2
T18:
Inherits: ^Tree
TC01:
Inherits: ^Tree
Building:

View File

@@ -21,7 +21,7 @@ MCV:
Offset:-1,-1
DeployDirections: 96
TransformSounds: constru2.aud, hvydoor1.aud
NoTransformSounds: nodeply1.aud
NoTransformSounds: deploy1.aud
RenderUnit:
HARV:
@@ -37,6 +37,8 @@ HARV:
Selectable:
Priority: 7
Harvester:
Resources: Tiberium
Capacity: 28
Unit:
HP: 600
Armor: light

View File

@@ -49,8 +49,6 @@ Incoming=10 ; If an incoming projectile is as slow or slower than th
; income and production
BuildSpeed=.8 ; general build speed [time (in minutes) to produce a 1000 credit cost item]
BuildupTime=.06 ; average minutes that building build-up animation runs
GemValue=50 ; gem credits per 'bail' carried by a harvester
GoldValue=25 ; gold credits per 'bail' carried by a harvester
OreTruckRate=1 ; speed that harvester truck manages ore [larger means slower]
SeparateAircraft=no ; Is first helicopter to be purchased separately from helipad?
SurvivorRate=.4 ; fraction of building cost to be converted to survivors when sold

View File

@@ -184,11 +184,6 @@ World:
B: 0
A: 180
ShroudPalette:
OreGrowth:
Interval: .3
Chance: .02
Spreads: yes
Grows: yes
Country@0:
Name: England
Race: allies
@@ -216,7 +211,58 @@ World:
SellButton:
RepairButton:
PowerDownButton:
WallLoadHook@sbag:
ActorType: sbag
OverlayTypes: sbag
WallLoadHook@cycl:
ActorType: cycl
OverlayTypes: cycl
WallLoadHook@brik:
ActorType: brik
OverlayTypes: brik
WallLoadHook@fenc:
ActorType: fenc
OverlayTypes: fenc
WallLoadHook@wood:
ActorType: wood
OverlayTypes: wood
WallLoadHook@barb:
ActorType: barb
OverlayTypes: barb
ResourceLayer:
ResourceType@ore:
Overlays: gold01,gold02,gold03,gold04
Palette: terrain
SpriteNames: gold01,gold02,gold03,gold04
ValuePerUnit: 25
Name: Ore
GrowthInterval: .3
SpreadInterval: .7
ResourceType@gem:
Overlays: gem01,gem02,gem03,gem04
Palette: terrain
SpriteNames: gem01,gem02,gem03,gem04
ValuePerUnit: 50
Name: Gems
Theater@SNOW:
Name:Snow
Theater:SNOW
Suffix:sno
Templates:templates.ini
Tileset:tileSet.til
Theater@TEMPERAT:
Name:Temperate
Theater:TEMPERAT
Suffix:tem
Templates:templates.ini
Tileset:tileSet.til
Theater@INTERIOR:
Name:Interior
Theater:INTERIOR
Suffix:int
Templates:templates.ini
Tileset:tileSet.til
MGG:
GeneratesGap:
Range: 10
@@ -773,6 +819,8 @@ HARV:
Selectable:
Priority: 7
Harvester:
Capacity: 28
Resources: Ore,Gems
Unit:
HP: 600
Armor: heavy
@@ -1892,7 +1940,7 @@ TC05:
MINE:
Inherits: ^Building
SeedsOre:
SeedsResource:
-Selectable:
BOXES01: