Merge branches 'bugfixes', 'shroud', 'cargo' and 'minimap'

This commit is contained in:
Chris Forbes
2010-01-05 22:23:00 +13:00
37 changed files with 526 additions and 89 deletions

View File

@@ -0,0 +1,9 @@
using System;
using System.Drawing;
namespace OpenRa.FileFormats
{
public interface IPaletteRemap
{
Color GetRemappedColor(Color original, int index);
}
}

View File

@@ -55,6 +55,7 @@
<Compile Include="Format80.cs" /> <Compile Include="Format80.cs" />
<Compile Include="IniFile.cs" /> <Compile Include="IniFile.cs" />
<Compile Include="IniWriter.cs" /> <Compile Include="IniWriter.cs" />
<Compile Include="IPaletteRemap.cs" />
<Compile Include="Map.cs" /> <Compile Include="Map.cs" />
<Compile Include="PackageEntry.cs" /> <Compile Include="PackageEntry.cs" />
<Compile Include="Package.cs" /> <Compile Include="Package.cs" />
@@ -62,6 +63,7 @@
<Compile Include="PaletteRemap.cs" /> <Compile Include="PaletteRemap.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ShpReader.cs" /> <Compile Include="ShpReader.cs" />
<Compile Include="ShroudPaletteRemap.cs" />
<Compile Include="Terrain.cs" /> <Compile Include="Terrain.cs" />
<Compile Include="TileReference.cs" /> <Compile Include="TileReference.cs" />
<Compile Include="TileSet.cs" /> <Compile Include="TileSet.cs" />

View File

@@ -31,7 +31,7 @@ namespace OpenRa.FileFormats
colors[4] = Color.FromArgb(140, 0, 0, 0); colors[4] = Color.FromArgb(140, 0, 0, 0);
} }
public Palette(Palette p, PaletteRemap r) public Palette(Palette p, IPaletteRemap r)
{ {
for (int i = 0; i < 256; i++) for (int i = 0; i < 256; i++)
colors.Add(r.GetRemappedColor(p.GetColor(i), i)); colors.Add(r.GetRemappedColor(p.GetColor(i), i));

View File

@@ -4,7 +4,7 @@ using System.IO;
namespace OpenRa.FileFormats namespace OpenRa.FileFormats
{ {
public class PaletteRemap public class PaletteRemap : IPaletteRemap
{ {
int offset; int offset;
List<Color> remapColors = new List<Color>(); List<Color> remapColors = new List<Color>();

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
namespace OpenRa.FileFormats
{
public class ShroudPaletteRemap : IPaletteRemap
{
public Color GetRemappedColor(Color original, int index)
{
// false-color version for debug
//return new[] {
// Color.Orange, Color.Green,
// Color.Blue, Color.Yellow,
// Color.Black,
// Color.Red,
// Color.Purple,
// Color.Cyan}[index % 8];
return new[] {
Color.Transparent, Color.Green,
Color.Blue, Color.Yellow,
Color.Black,
Color.FromArgb(192,0,0,0),
Color.FromArgb(128,0,0,0),
Color.FromArgb(64,0,0,0)}[index % 8];
}
}
}

View File

@@ -123,6 +123,7 @@ namespace OpenRa.Game
} }
public bool IsDead { get { return Health <= 0; } } public bool IsDead { get { return Health <= 0; } }
public bool IsInWorld { get; set; }
public DamageState GetDamageState() public DamageState GetDamageState()
{ {

View File

@@ -132,6 +132,8 @@ namespace OpenRa.Game
PerfHistory.Render(renderer, Game.worldRenderer.lineRenderer); PerfHistory.Render(renderer, Game.worldRenderer.lineRenderer);
Game.minimap.Draw(new float2(Game.viewport.Width - 256, 8));
chromeRenderer.DrawSprite(specialBinSprite, float2.Zero, PaletteType.Chrome); chromeRenderer.DrawSprite(specialBinSprite, float2.Zero, PaletteType.Chrome);
chromeRenderer.DrawSprite(moneyBinSprite, new float2(Game.viewport.Width - 320, 0), PaletteType.Chrome); chromeRenderer.DrawSprite(moneyBinSprite, new float2(Game.viewport.Width - 320, 0), PaletteType.Chrome);
@@ -143,8 +145,6 @@ namespace OpenRa.Game
int paletteHeight = DrawBuildPalette(currentTab); int paletteHeight = DrawBuildPalette(currentTab);
DrawBuildTabs(paletteHeight); DrawBuildTabs(paletteHeight);
DrawChat(); DrawChat();
Game.minimap.Draw(new float2(Game.viewport.Width - 128,30));
} }
void AddButton(Rectangle r, Action<bool> b) { buttons.Add(Pair.New(r, b)); } void AddButton(Rectangle r, Action<bool> b) { buttons.Add(Pair.New(r, b)); }

View File

@@ -59,6 +59,11 @@ namespace OpenRa.Game
palette = new HardwarePalette(renderer, Rules.Map); palette = new HardwarePalette(renderer, Rules.Map);
world = new World(); world = new World();
Game.world.ActorAdded += a =>
{
if (a.Owner != null && a.Info != null)
a.Owner.Shroud.Explore(a);
};
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
{ {
@@ -100,7 +105,7 @@ namespace OpenRa.Game
chrome = new Chrome(renderer); chrome = new Chrome(renderer);
oreFrequency = (int)(Rules.General.GrowthRate * 60 * 1000); oreFrequency = (int)(Rules.General.GrowthRate * 60 * 25);
oreTicks = oreFrequency; oreTicks = oreFrequency;
} }
@@ -182,9 +187,10 @@ namespace OpenRa.Game
controller.orderGenerator.Tick(); controller.orderGenerator.Tick();
if (--oreTicks == 0) if (--oreTicks == 0)
{
using (new PerfSample("ore")) using (new PerfSample("ore"))
{
Rules.Map.GrowOre(SharedRandom); Rules.Map.GrowOre(SharedRandom);
minimap.InvalidateOre();
oreTicks = oreFrequency; oreTicks = oreFrequency;
} }

View File

@@ -55,13 +55,15 @@ namespace OpenRa.Game.GameRules
public readonly int OrePips = 0; public readonly int OrePips = 0;
public readonly string Icon = null; public readonly string Icon = null;
public readonly int[] SelectionSize = null; public readonly int[] SelectionSize = null;
public readonly int Passengers = 0;
public readonly int UnloadFacing = 0;
public readonly UnitMovementType[] PassengerTypes = null;
public UnitInfo(string name) { Name = name; } public UnitInfo(string name) { Name = name; }
} }
public class MobileInfo : UnitInfo public class MobileInfo : UnitInfo
{ {
public readonly int Passengers = 0;
public readonly int Speed = 0; public readonly int Speed = 0;
public readonly bool NoMovingFire = false; public readonly bool NoMovingFire = false;
public readonly string Voice = "GenericVoice"; public readonly string Voice = "GenericVoice";

View File

@@ -3,7 +3,12 @@ using OpenRa.FileFormats;
namespace OpenRa.Game.Graphics namespace OpenRa.Game.Graphics
{ {
public enum PaletteType { Gold, Blue, Red, Orange, Teal, Salmon, Green, Gray, Shadow, Invuln, Chrome }; public enum PaletteType
{
Gold, Blue, Red, Orange, Teal, Salmon, Green, Gray,
Shadow, Invuln, Chrome, Shroud,
};
class HardwarePalette : Sheet class HardwarePalette : Sheet
{ {
const int maxEntries = 16; const int maxEntries = 16;
@@ -21,6 +26,7 @@ namespace OpenRa.Game.Graphics
AddPalette(new Palette(pal, new PaletteRemap(Color.FromArgb(140, 0, 0, 0)))); AddPalette(new Palette(pal, new PaletteRemap(Color.FromArgb(140, 0, 0, 0))));
AddPalette(pal); // iron curtain. todo: remap! AddPalette(pal); // iron curtain. todo: remap!
AddPalette(pal); // chrome (it's like gold, but we're not going to hax it in palettemods) AddPalette(pal); // chrome (it's like gold, but we're not going to hax it in palettemods)
AddPalette(new Palette(pal, new ShroudPaletteRemap()));
} }
int AddPalette(Palette p) int AddPalette(Palette p)

View File

@@ -1,6 +1,7 @@
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using OpenRa.Game.Traits; using OpenRa.Game.Traits;
using OpenRa.FileFormats;
namespace OpenRa.Game.Graphics namespace OpenRa.Game.Graphics
{ {
@@ -9,7 +10,7 @@ namespace OpenRa.Game.Graphics
Sheet sheet; Sheet sheet;
SpriteRenderer spriteRenderer; SpriteRenderer spriteRenderer;
Sprite sprite; Sprite sprite;
Bitmap terrain; Bitmap terrain, oreLayer;
public void Tick() { } public void Tick() { }
@@ -21,20 +22,29 @@ namespace OpenRa.Game.Graphics
} }
// todo: extract these from the palette // todo: extract these from the palette
static readonly Color[] terrainTypeColors = { Color[] terrainTypeColors;
Color.Green,
Color.Red, public void InvalidateOre() { oreLayer = null; }
Color.Blue,
Color.Yellow,
Color.Purple,
Color.Turquoise,
Color.Violet,
Color.Tomato,
Color.Teal,
};
public void Update() public void Update()
{ {
if (terrainTypeColors == null)
{
var pal = new Palette(FileSystem.Open(Rules.Map.Theater + ".pal"));
terrainTypeColors = new[] {
pal.GetColor(0x1a),
pal.GetColor(0x63),
pal.GetColor(0x2f),
pal.GetColor(0x1f),
pal.GetColor(0x14),
pal.GetColor(0x64),
pal.GetColor(0x1f),
pal.GetColor(0x68),
pal.GetColor(0x6b),
pal.GetColor(0x6d),
};
}
if (terrain == null) if (terrain == null)
{ {
terrain = new Bitmap(128, 128); terrain = new Bitmap(128, 128);
@@ -45,14 +55,23 @@ namespace OpenRa.Game.Graphics
: Color.Black); : Color.Black);
} }
var bitmap = new Bitmap(terrain); if (oreLayer == null)
{
oreLayer = new Bitmap(terrain);
for (var y = 0; y < 128; y++)
for (var x = 0; x < 128; x++)
if (Rules.Map.ContainsResource(new int2(x, y)))
oreLayer.SetPixel(x, y, terrainTypeColors[(int)TerrainMovementType.Ore]);
}
var bitmap = new Bitmap(oreLayer);
for( var y = 0; y < 128; y++ ) for( var y = 0; y < 128; y++ )
for (var x = 0; x < 128; x++) for (var x = 0; x < 128; x++)
{ {
var b = Game.BuildingInfluence.GetBuildingAt(new int2(x, y)); var b = Game.BuildingInfluence.GetBuildingAt(new int2(x, y));
if (b != null) if (b != null)
bitmap.SetPixel(x, y, b.Owner != null ? Chat.paletteColors[(int)b.Owner.Palette] : Color.Gray); bitmap.SetPixel(x, y, b.Owner != null ? Chat.paletteColors[(int)b.Owner.Palette] : terrainTypeColors[4]);
} }
foreach (var a in Game.world.Actors.Where(a => a.traits.Contains<Unit>())) foreach (var a in Game.world.Actors.Where(a => a.traits.Contains<Unit>()))
@@ -63,7 +82,7 @@ namespace OpenRa.Game.Graphics
public void Draw(float2 pos) public void Draw(float2 pos)
{ {
spriteRenderer.DrawSprite(sprite, pos, PaletteType.Chrome); spriteRenderer.DrawSprite(sprite, pos, PaletteType.Chrome, new float2(256,256));
spriteRenderer.Flush(); spriteRenderer.Flush();
} }
} }

View File

@@ -55,12 +55,17 @@ namespace OpenRa.Game.Graphics
} }
public void DrawSprite(Sprite s, float2 location, PaletteType palette) public void DrawSprite(Sprite s, float2 location, PaletteType palette)
{
DrawSprite(s, location, palette, s.size);
}
public void DrawSprite(Sprite s, float2 location, PaletteType palette, float2 size)
{ {
if (s.sheet != currentSheet) if (s.sheet != currentSheet)
Flush(); Flush();
currentSheet = s.sheet; currentSheet = s.sheet;
Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, (int) palette, nv, ni); Util.FastCreateQuad(vertices, indices, location.ToInt2(), s, (int)palette, nv, ni, size);
nv += 4; ni += 6; nv += 4; ni += 6;
if (++sprites >= spritesPerBatch) if (++sprites >= spritesPerBatch)
Flush(); Flush();

View File

@@ -34,7 +34,7 @@ namespace OpenRa.Game.Graphics
for( int i = map.XOffset ; i < map.XOffset + map.Width; i++ ) for( int i = map.XOffset ; i < map.XOffset + map.Width; i++ )
{ {
Sprite tile = tileMapping[map.MapTiles[i, j]]; Sprite tile = tileMapping[map.MapTiles[i, j]];
Util.FastCreateQuad(vertices, indices, Game.CellSize * new float2(i, j), tile, 0, nv, ni); Util.FastCreateQuad(vertices, indices, Game.CellSize * new float2(i, j), tile, 0, nv, ni, tile.size);
nv += 4; nv += 4;
ni += 6; ni += 6;
} }

View File

@@ -57,14 +57,14 @@ namespace OpenRa.Game.Graphics
static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f }; static float[] channelSelect = { 0.75f, 0.25f, -0.25f, -0.75f };
public static void FastCreateQuad(Vertex[] vertices, ushort[] indices, float2 o, Sprite r, int palette, int nv, int ni) public static void FastCreateQuad(Vertex[] vertices, ushort[] indices, float2 o, Sprite r, int palette, int nv, int ni, float2 size)
{ {
float2 attrib = new float2(palette / 16.0f, channelSelect[(int)r.channel]); float2 attrib = new float2(palette / 16.0f, channelSelect[(int)r.channel]);
vertices[nv] = new Vertex(KLerp(o, r.size, 0), r.FastMapTextureCoords(0), attrib); vertices[nv] = new Vertex(KLerp(o, size, 0), r.FastMapTextureCoords(0), attrib);
vertices[nv + 1] = new Vertex(KLerp(o, r.size, 1), r.FastMapTextureCoords(1), attrib); vertices[nv + 1] = new Vertex(KLerp(o, size, 1), r.FastMapTextureCoords(1), attrib);
vertices[nv + 2] = new Vertex(KLerp(o, r.size, 2), r.FastMapTextureCoords(2), attrib); vertices[nv + 2] = new Vertex(KLerp(o, size, 2), r.FastMapTextureCoords(2), attrib);
vertices[nv + 3] = new Vertex(KLerp(o, r.size, 3), r.FastMapTextureCoords(3), attrib); vertices[nv + 3] = new Vertex(KLerp(o, size, 3), r.FastMapTextureCoords(3), attrib);
indices[ni] = (ushort)(nv); indices[ni] = (ushort)(nv);
indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1); indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1);

View File

@@ -87,6 +87,8 @@ namespace OpenRa.Game.Graphics
if (Game.controller.orderGenerator != null) if (Game.controller.orderGenerator != null)
Game.controller.orderGenerator.Render(); Game.controller.orderGenerator.Render();
Game.LocalPlayer.Shroud.Draw(spriteRenderer);
lineRenderer.Flush(); lineRenderer.Flush();
spriteRenderer.Flush(); spriteRenderer.Flush();
} }
@@ -211,7 +213,7 @@ namespace OpenRa.Game.Graphics
foreach (var pips in selectedUnit.traits.WithInterface<IPips>()) foreach (var pips in selectedUnit.traits.WithInterface<IPips>())
{ {
foreach (var pip in pips.GetPips()) foreach (var pip in pips.GetPips(selectedUnit))
{ {
var pipImages = new Animation("pips"); var pipImages = new Animation("pips");
pipImages.PlayRepeating(pipStrings[(int)pip]); pipImages.PlayRepeating(pipStrings[(int)pip]);

View File

@@ -110,6 +110,7 @@
<Compile Include="PathSearch.cs" /> <Compile Include="PathSearch.cs" />
<Compile Include="ProductionItem.cs" /> <Compile Include="ProductionItem.cs" />
<Compile Include="Orders\ReplayOrderSource.cs" /> <Compile Include="Orders\ReplayOrderSource.cs" />
<Compile Include="Shroud.cs" />
<Compile Include="Smudge.cs" /> <Compile Include="Smudge.cs" />
<Compile Include="Sound.cs" /> <Compile Include="Sound.cs" />
<Compile Include="Support\Stopwatch.cs" /> <Compile Include="Support\Stopwatch.cs" />
@@ -118,6 +119,7 @@
<Compile Include="Traits\Activities\Attack.cs" /> <Compile Include="Traits\Activities\Attack.cs" />
<Compile Include="Traits\Activities\CaptureBuilding.cs" /> <Compile Include="Traits\Activities\CaptureBuilding.cs" />
<Compile Include="Traits\Activities\Demolish.cs" /> <Compile Include="Traits\Activities\Demolish.cs" />
<Compile Include="Traits\Activities\EnterTransport.cs" />
<Compile Include="Traits\Activities\Fly.cs" /> <Compile Include="Traits\Activities\Fly.cs" />
<Compile Include="Traits\Activities\FlyAttack.cs" /> <Compile Include="Traits\Activities\FlyAttack.cs" />
<Compile Include="Traits\Activities\FlyTimed.cs" /> <Compile Include="Traits\Activities\FlyTimed.cs" />
@@ -185,6 +187,7 @@
<Compile Include="Traits\Activities\Follow.cs" /> <Compile Include="Traits\Activities\Follow.cs" />
<Compile Include="Traits\Activities\Turn.cs" /> <Compile Include="Traits\Activities\Turn.cs" />
<Compile Include="Traits\Activities\UndeployMcv.cs" /> <Compile Include="Traits\Activities\UndeployMcv.cs" />
<Compile Include="Traits\Activities\UnloadCargo.cs" />
<Compile Include="Traits\APMine.cs" /> <Compile Include="Traits\APMine.cs" />
<Compile Include="Traits\ATMine.cs" /> <Compile Include="Traits\ATMine.cs" />
<Compile Include="Traits\AttackBase.cs" /> <Compile Include="Traits\AttackBase.cs" />
@@ -198,6 +201,7 @@
<Compile Include="Traits\BelowUnits.cs" /> <Compile Include="Traits\BelowUnits.cs" />
<Compile Include="Traits\Building.cs" /> <Compile Include="Traits\Building.cs" />
<Compile Include="Traits\C4Demolition.cs" /> <Compile Include="Traits\C4Demolition.cs" />
<Compile Include="Traits\Cargo.cs" />
<Compile Include="Traits\Chronoshiftable.cs" /> <Compile Include="Traits\Chronoshiftable.cs" />
<Compile Include="Traits\ChronoshiftPaletteEffect.cs" /> <Compile Include="Traits\ChronoshiftPaletteEffect.cs" />
<Compile Include="Traits\Chronosphere.cs" /> <Compile Include="Traits\Chronosphere.cs" />
@@ -213,6 +217,7 @@
<Compile Include="Traits\MineImmune.cs" /> <Compile Include="Traits\MineImmune.cs" />
<Compile Include="Traits\Minelayer.cs" /> <Compile Include="Traits\Minelayer.cs" />
<Compile Include="Traits\LimitedAmmo.cs" /> <Compile Include="Traits\LimitedAmmo.cs" />
<Compile Include="Traits\Passenger.cs" />
<Compile Include="Traits\Repairable.cs" /> <Compile Include="Traits\Repairable.cs" />
<Compile Include="Traits\Reservable.cs" /> <Compile Include="Traits\Reservable.cs" />
<Compile Include="Traits\SquishByTank.cs" /> <Compile Include="Traits\SquishByTank.cs" />

View File

@@ -27,7 +27,7 @@ namespace OpenRa.Game.Orders
public void Tick() public void Tick()
{ {
selection.RemoveAll(a => a.IsDead); selection.RemoveAll(a => !a.IsInWorld);
} }
public void Render() public void Render()
@@ -81,6 +81,7 @@ namespace OpenRa.Game.Orders
else else
return Cursor.MoveBlocked; return Cursor.MoveBlocked;
case "Enter": return Cursor.Enter; case "Enter": return Cursor.Enter;
case "EnterTransport": return Cursor.Enter;
case "Deliver": return Cursor.Enter; case "Deliver": return Cursor.Enter;
case "Infiltrate": return Cursor.Enter; case "Infiltrate": return Cursor.Enter;
case "Capture": return Cursor.Capture; case "Capture": return Cursor.Capture;

View File

@@ -17,15 +17,17 @@ namespace OpenRa.Game
public string InternalName; public string InternalName;
public Race Race; public Race Race;
public readonly int Index; public readonly int Index;
public int Cash; public int Cash = 10000;
public int Ore; public int Ore = 0;
public int OreCapacity; public int OreCapacity;
public int DisplayCash; public int DisplayCash = 0;
public int PowerProvided; public int PowerProvided = 0;
public int PowerDrained; public int PowerDrained = 0;
public bool IsReady; public bool IsReady;
public Shroud Shroud = new Shroud();
public Player( Actor playerActor, int index, PaletteType palette, string playerName, Race race, string internalName ) public Player( Actor playerActor, int index, PaletteType palette, string playerName, Race race, string internalName )
{ {
this.PlayerActor = playerActor; this.PlayerActor = playerActor;
@@ -34,10 +36,6 @@ namespace OpenRa.Game
this.InternalName = internalName; this.InternalName = internalName;
this.PlayerName = playerName; this.PlayerName = playerName;
this.Race = race; this.Race = race;
this.Cash = 10000;
this.Ore = 0;
this.DisplayCash = 0;
this.PowerProvided = this.PowerDrained = 0;
} }
void UpdatePower() void UpdatePower()

87
OpenRa.Game/Shroud.cs Normal file
View File

@@ -0,0 +1,87 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IjwFramework.Types;
using OpenRa.Game.Graphics;
namespace OpenRa.Game
{
class Shroud
{
bool[,] explored = new bool[128, 128];
Sprite[] shadowBits = SpriteSheetBuilder.LoadAllSprites("shadow");
Sprite[,] sprites = new Sprite[128, 128];
bool dirty;
public void Explore(Actor a)
{
foreach (var t in Game.FindTilesInCircle((1f/Game.CellSize * a.CenterLocation).ToInt2(), a.Info.Sight))
explored[t.X, t.Y] = true;
dirty = true;
}
Sprite ChooseShroud(int i, int j)
{
// bits are for exploredness: left, right, up, down, self
var n = new[] {
0xf,0xf,0xf,0xf,
0xf,0x0f,0x0f,0xf,
0xf,0x0f,0x0f,0xf,
0xf,0xf,0xf,0xf,
0,7,13,0,
14,6,12,4,
11,3,9,1,
0,2,8,0,
};
var v = 0;
if (explored[i-1,j]) v |= 1;
if (explored[i+1,j]) v |= 2;
if (explored[i,j-1]) v |= 4;
if (explored[i,j+1]) v |= 8;
if (explored[i, j]) v |= 16;
var x = n[v];
if (x == 0)
{
// bits are for exploredness: TL, TR, BR, BL
var m = new[] {
46, 41, 42, 38,
43, 45, 39, 35,
40, 37, 44, 34,
36, 33, 32, 47,
};
var u = 0;
if (explored[i - 1, j - 1]) u |= 1;
if (explored[i + 1, j - 1]) u |= 2;
if (explored[i + 1, j + 1]) u |= 4;
if (explored[i - 1, j + 1]) u |= 8;
return shadowBits[m[u]];
}
return shadowBits[x];
}
public void Draw(SpriteRenderer r)
{
if (dirty)
{
dirty = false;
for (int j = 1; j < 127; j++)
for (int i = 1; i < 127; i++)
sprites[i, j] = ChooseShroud(i, j);
}
for (var j = 0; j < 128; j++)
for (var i = 0; i < 128; i++)
if (sprites[i,j] != null)
r.DrawSprite(sprites[i, j],
Game.CellSize * new float2(i, j),
PaletteType.Shroud);
}
}
}

View File

@@ -2,7 +2,7 @@ using OpenRa.Game.Graphics;
namespace OpenRa.Game namespace OpenRa.Game
{ {
enum UnitMovementType : byte public enum UnitMovementType : byte
{ {
Foot = 0, Foot = 0,
Track = 1, Track = 1,

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRa.Game.Traits.Activities
{
class EnterTransport : IActivity
{
public IActivity NextActivity { get; set; }
bool isCanceled;
public Actor transport;
public EnterTransport(Actor self, Actor transport)
{
this.transport = transport;
}
public IActivity Tick(Actor self)
{
if (isCanceled) return NextActivity;
if (transport == null || !transport.IsInWorld) return NextActivity;
var cargo = transport.traits.Get<Cargo>();
if (cargo.IsFull(transport))
return NextActivity;
cargo.Load(transport, self);
Game.world.AddFrameEndTask(w => w.Remove(self));
return this;
}
public void Cancel(Actor self) { isCanceled = true; NextActivity = null; }
}
}

View File

@@ -5,7 +5,7 @@ namespace OpenRa.Game.Traits.Activities
{ {
public IActivity NextActivity { get; set; } public IActivity NextActivity { get; set; }
public int desiredFacing; int desiredFacing;
public Turn( int desiredFacing ) public Turn( int desiredFacing )
{ {

View File

@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRa.Game.Traits.Activities
{
class UnloadCargo : IActivity
{
public IActivity NextActivity { get; set; }
bool isCanceled;
int2? ChooseExitTile(Actor self)
{
// is anyone still hogging this tile?
if (Game.UnitInfluence.GetUnitsAt(self.Location).Count() > 1)
return null;
for (var i = -1; i < 2; i++)
for (var j = -1; j < 2; j++)
if ((i != 0 || j != 0) &&
Game.IsCellBuildable(self.Location + new int2(i, j),
UnitMovementType.Foot))
return self.Location + new int2(i, j);
return null;
}
public IActivity Tick(Actor self)
{
if (isCanceled) return NextActivity;
// if we're a thing that can turn, turn to the
// right facing for the unload animation
var unit = self.traits.GetOrDefault<Unit>();
if (unit != null && unit.Facing != self.Info.UnloadFacing)
return new Turn(self.Info.UnloadFacing) { NextActivity = this };
// todo: handle the BS of open/close sequences, which are inconsistent,
// for reasons that probably make good sense to the westwood guys.
var cargo = self.traits.Get<Cargo>();
if (cargo.IsEmpty(self))
return NextActivity;
var ru = self.traits.WithInterface<RenderUnit>().FirstOrDefault();
if (ru != null)
ru.PlayCustomAnimation(self, "unload", null);
var exitTile = ChooseExitTile(self);
if (exitTile == null)
return this;
var actor = cargo.Unload(self);
Game.world.AddFrameEndTask(w =>
{
w.Add(actor);
actor.traits.Get<Mobile>().TeleportTo(actor, self.Location);
actor.CancelActivity();
actor.QueueActivity(new Move(exitTile.Value, 0));
});
return this;
}
public void Cancel(Actor self) { NextActivity = null; isCanceled = true; }
}
}

View File

@@ -0,0 +1,85 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRa.Game.GameRules;
using OpenRa.Game.Traits.Activities;
namespace OpenRa.Game.Traits
{
class Cargo : IPips, IOrder
{
List<Actor> cargo = new List<Actor>();
public Cargo(Actor self) {}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
// todo: check if there is an unoccupied `land` tile adjacent
if (mi.Button == MouseButton.Right && underCursor == self && cargo.Count > 0)
{
var unit = underCursor.traits.GetOrDefault<Unit>();
if (unit != null && unit.Altitude > 0) return null;
return new Order("Deploy", self, null, int2.Zero, null);
}
return null;
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Deploy")
{
// todo: eject the units
self.CancelActivity();
self.QueueActivity(new UnloadCargo());
}
}
public bool IsFull(Actor self)
{
return cargo.Count == self.Info.Passengers;
}
public bool IsEmpty(Actor self)
{
return cargo.Count == 0;
}
public Actor Unload(Actor self)
{
var a = cargo[0];
cargo.RemoveAt(0);
return a;
}
public IEnumerable<PipType> GetPips( Actor self )
{
for (var i = 0; i < self.Info.Passengers; i++)
if (i >= cargo.Count)
yield return PipType.Transparent;
else
yield return GetPipForPassenger(cargo[i]);
}
static PipType GetPipForPassenger(Actor a)
{
// probably not actually right yet; fix to match real-ra
if (a.traits.Contains<AutoHeal>())
return PipType.Yellow;
if (!a.traits.WithInterface<AttackBase>().Any())
return PipType.Yellow; // noncombat [E6,SPY,THF]
if (a.traits.Contains<C4Demolition>())
return PipType.Red; // E7
return PipType.Green;
}
public void Load(Actor self, Actor a)
{
cargo.Add(a);
}
}
}

View File

@@ -55,7 +55,7 @@ namespace OpenRa.Game.Traits
} }
// Display 5 pips indicating the current charge status // Display 5 pips indicating the current charge status
public IEnumerable<PipType> GetPips() public IEnumerable<PipType> GetPips(Actor self)
{ {
const int numPips = 5; const int numPips = 5;
for (int i = 0; i < numPips; i++) for (int i = 0; i < numPips; i++)

View File

@@ -57,7 +57,7 @@ namespace OpenRa.Game.Traits
} }
} }
public IEnumerable<PipType> GetPips() public IEnumerable<PipType> GetPips(Actor self)
{ {
const int numPips = 7; const int numPips = 7;
for (int i = 0; i < numPips; i++) for (int i = 0; i < numPips; i++)

View File

@@ -23,7 +23,7 @@ namespace OpenRa.Game.Traits
public void Attacking(Actor self) { --ammo; } public void Attacking(Actor self) { --ammo; }
public IEnumerable<PipType> GetPips() public IEnumerable<PipType> GetPips(Actor self)
{ {
return Graphics.Util.MakeArray(self.Info.Ammo, return Graphics.Util.MakeArray(self.Info.Ammo,
i => ammo > i ? PipType.Green : PipType.Transparent); i => ammo > i ? PipType.Green : PipType.Transparent);

View File

@@ -18,7 +18,16 @@ namespace OpenRa.Game.Traits
public int2 toCell public int2 toCell
{ {
get { return self.Location; } get { return self.Location; }
set { Game.UnitInfluence.Remove(self, this); self.Location = value; Game.UnitInfluence.Add(self, this); } set
{
if (self.Location != value)
{
Game.UnitInfluence.Remove(self, this);
self.Location = value;
self.Owner.Shroud.Explore(self);
}
Game.UnitInfluence.Add(self, this);
}
} }
public Mobile(Actor self) public Mobile(Actor self)

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRa.Game.Traits.Activities;
namespace OpenRa.Game.Traits
{
class Passenger : IOrder
{
public Passenger(Actor self) { }
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button != MouseButton.Right)
return null;
if (underCursor == null || underCursor.Owner != self.Owner)
return null;
var cargo = underCursor.traits.GetOrDefault<Cargo>();
if (cargo == null || cargo.IsFull(underCursor))
return null;
var umt = self.traits.WithInterface<IMovement>().First().GetMovementType();
if (!underCursor.Info.PassengerTypes.Contains(umt))
return null;
return new Order("EnterTransport", self, underCursor, int2.Zero, null);
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "EnterTransport")
{
self.CancelActivity();
self.QueueActivity(new Move(order.TargetActor.Location, 1));
self.QueueActivity(new EnterTransport(self, order.TargetActor));
}
}
}
}

View File

@@ -11,7 +11,7 @@ namespace OpenRa.Game.Traits
this.self = self; this.self = self;
} }
public IEnumerable<PipType> GetPips() public IEnumerable<PipType> GetPips(Actor self)
{ {
for (int i = 0; i < self.Info.OrePips; i++) for (int i = 0; i < self.Info.OrePips; i++)
{ {

View File

@@ -29,7 +29,7 @@ namespace OpenRa.Game.Traits
interface IDamageModifier { float GetDamageModifier(); } interface IDamageModifier { float GetDamageModifier(); }
interface ISpeedModifier { float GetSpeedModifier(); } interface ISpeedModifier { float GetSpeedModifier(); }
interface IPaletteModifier { void AdjustPalette(Bitmap b); } interface IPaletteModifier { void AdjustPalette(Bitmap b); }
interface IPips { IEnumerable<PipType> GetPips(); } interface IPips { IEnumerable<PipType> GetPips(Actor self); }
interface ITags { IEnumerable<TagType> GetTags(); } interface ITags { IEnumerable<TagType> GetTags(); }
interface IMovement interface IMovement
{ {

View File

@@ -10,8 +10,19 @@ namespace OpenRa.Game
List<IEffect> effects = new List<IEffect>(); List<IEffect> effects = new List<IEffect>();
List<Action<World>> frameEndActions = new List<Action<World>>(); List<Action<World>> frameEndActions = new List<Action<World>>();
public void Add(Actor a) { actors.Add(a); ActorAdded(a); } public void Add(Actor a)
public void Remove(Actor a) { actors.Remove(a); ActorRemoved(a); } {
a.IsInWorld = true;
actors.Add(a);
ActorAdded(a);
}
public void Remove(Actor a)
{
a.IsInWorld = false;
actors.Remove(a);
ActorRemoved(a);
}
public void Add(IEffect b) { effects.Add(b); } public void Add(IEffect b) { effects.Add(b); }
public void Remove(IEffect b) { effects.Remove(b); } public void Remove(IEffect b) { effects.Remove(b); }

View File

@@ -83,7 +83,8 @@ namespace SequenceEditor
Doc = new XmlDocument(); Doc = new XmlDocument();
Doc.Load(XmlFilename); Doc.Load(XmlFilename);
Pal = new Palette(FileSystem.Open("temperat.pal")); var tempPal = new Palette(FileSystem.Open("temperat.pal"));
Pal = new Palette(tempPal, new ShroudPaletteRemap());
UnitName = args.FirstOrDefault( x => !x.EndsWith(".xml") ); UnitName = args.FirstOrDefault( x => !x.EndsWith(".xml") );
if (UnitName == null) if (UnitName == null)

View File

@@ -21,7 +21,7 @@ All tracked vehicles
Light vehicles Light vehicles
V2RL Works V2RL Works
APC Cargo doesn't work APC Works
MNLY Works MNLY Works
MGG No gap MGG No gap
MRJ No radar MRJ No radar
@@ -31,8 +31,7 @@ HARV Works
ARTY Works ARTY Works
Helicopters Helicopters
- Return to base after attack doesnt work TRAN Works
TRAN Cargo doesn't work
HELI Works HELI Works
HIND Works HIND Works
@@ -47,5 +46,5 @@ CA Works
SS Works SS Works
DD depth charges don't work DD depth charges don't work
PT depth charges don't work PT depth charges don't work
LST Cargo doesn't work LST Works

View File

@@ -283,6 +283,8 @@
<unit name="apc"> <unit name="apc">
<sequence name="idle" start="0" length="32" /> <sequence name="idle" start="0" length="32" />
<sequence name="muzzle" start="0" length="48" src="minigun" /> <sequence name="muzzle" start="0" length="48" src="minigun" />
<sequence name="close" start="32" length="3" />
<sequence name="unload" start="32" length="1" />
</unit> </unit>
<!-- mine layer --> <!-- mine layer -->
<unit name="mnly"> <unit name="mnly">
@@ -536,6 +538,8 @@
</unit> </unit>
<unit name="lst"> <unit name="lst">
<sequence name="idle" start="0" length="1" /> <sequence name="idle" start="0" length="1" />
<sequence name="open" start="1" length="4" />
<sequence name="unload" start="4" length="1" />
</unit> </unit>
<unit name="pips"> <unit name="pips">
<sequence name="groups" start="8" length="10" /> <sequence name="groups" start="8" length="10" />
@@ -572,6 +576,8 @@
<sequence name="rotor2" start="0" length="4" src="rrotor" /> <sequence name="rotor2" start="0" length="4" src="rrotor" />
<sequence name="slow-rotor" start="4" length="8" src="lrotor" /> <sequence name="slow-rotor" start="4" length="8" src="lrotor" />
<sequence name="slow-rotor2" start="4" length="8" src="rrotor" /> <sequence name="slow-rotor2" start="4" length="8" src="rrotor" />
<sequence name="open" start="32" length="4" />
<sequence name="unload" start="35" length="1" />
</unit> </unit>
<unit name="v2"> <unit name="v2">
<sequence name="idle" start="0" length="32" /> <sequence name="idle" start="0" length="32" />

View File

@@ -10,4 +10,4 @@ s2=Multi1,mcv,600,12505,0,Guard,None
;s2=Multi1,e3,600,12505,0,Guard,None ;s2=Multi1,e3,600,12505,0,Guard,None
s3=Multi3,mcv,600,2910,0,Guard,None s3=Multi3,mcv,600,2910,0,Guard,None
;s4=Multi1,ctnk,600,12506,Gaurd,None ;s4=Multi1,ctnk,600,12506,Gaurd,None
s5=Multi1,pdox,600,12510,Gaurd,None s5=Multi1,apc,600,12510,Gaurd,None

View File

@@ -16,89 +16,91 @@ MNLY.AT
[V2RL] [V2RL]
Description=V2 Rocket Description=V2 Rocket
Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable, Chronoshiftable Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable, Chronoshiftable, Passenger
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Long-range rocket artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft LongDesc=Long-range rocket artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft
[1TNK] [1TNK]
Description=Light Tank Description=Light Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
Recoil=2 Recoil=2
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Light Tank, good for scouting.\n Strong vs Light Vehicles\n Weak vs Tanks, Aircraft LongDesc=Light Tank, good for scouting.\n Strong vs Light Vehicles\n Weak vs Tanks, Aircraft
[2TNK] [2TNK]
Description=Medium Tank Description=Medium Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
Recoil=3 Recoil=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Allied Main Battle Tank.\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft LongDesc=Allied Main Battle Tank.\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft
[3TNK] [3TNK]
Description=Heavy Tank Description=Heavy Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
Recoil=3 Recoil=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Soviet Main Battle Tank, with dual cannons\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft LongDesc=Soviet Main Battle Tank, with dual cannons\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft
[4TNK] [4TNK]
Description=Mammoth Tank Description=Mammoth Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Big and slow tank, with anti-air capability.\n Strong vs Tanks, Aircraft\n Weak vs Infantry LongDesc=Big and slow tank, with anti-air capability.\n Strong vs Tanks, Aircraft\n Weak vs Infantry
[ARTY] [ARTY]
Description=Artillery Description=Artillery
Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable, Passenger
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Long-range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft LongDesc=Long-range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft
[JEEP] [JEEP]
Description=Ranger Description=Ranger
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
PrimaryOffset=0,0,0,-2 PrimaryOffset=0,0,0,-2
MuzzleFlash=yes MuzzleFlash=yes
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft LongDesc=Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft
[APC] [APC]
Description=Armored Personnel Carrier Description=Armored Personnel Carrier
Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash, AutoTarget, Repairable, Chronoshiftable Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash, AutoTarget, Repairable, Chronoshiftable, Cargo, Passenger
PrimaryOffset=0,0,0,-4 PrimaryOffset=0,0,0,-4
MuzzleFlash=yes MuzzleFlash=yes
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Tough infantry transport.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft LongDesc=Tough infantry transport.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft
UnloadFacing=220
PassengerTypes=Foot
;; non-combat vehicles ;; non-combat vehicles
[MRJ] [MRJ]
Description=Radar Jammer Description=Radar Jammer
Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger
PrimaryOffset=0,4,0,-6 PrimaryOffset=0,4,0,-6
SelectionPriority=3 SelectionPriority=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Hides nearby units on the enemy's minimap.\n Unarmed LongDesc=Hides nearby units on the enemy's minimap.\n Unarmed
[MGG] [MGG]
Description=Mobile Gap Generator Description=Mobile Gap Generator
Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger
PrimaryOffset=0,6,0,-3 PrimaryOffset=0,6,0,-3
SelectionPriority=3 SelectionPriority=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Regenerates Fog of War in a small area \naround the unit.\n Unarmed LongDesc=Regenerates Fog of War in a small area \naround the unit.\n Unarmed
[HARV] [HARV]
Description=Ore Truck Description=Ore Truck
Traits=Harvester, Unit, Mobile, RenderUnit, Repairable, Chronoshiftable Traits=Harvester, Unit, Mobile, RenderUnit, Repairable, Chronoshiftable, Passenger
SelectionPriority=7 SelectionPriority=7
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Collects Ore and Gems for processing.\n Unarmed LongDesc=Collects Ore and Gems for processing.\n Unarmed
[MCV] [MCV]
Description=Mobile Construction Vehicle Description=Mobile Construction Vehicle
Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable, Chronoshiftable Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable, Chronoshiftable, Passenger
SelectionPriority=3 SelectionPriority=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Deploys into another Construction Yard.\n Unarmed LongDesc=Deploys into another Construction Yard.\n Unarmed
[MNLY.AP] [MNLY.AP]
Description=Minelayer (Anti-Personnel) Description=Minelayer (Anti-Personnel)
Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable, Passenger
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed
Primary=MINP ;; temporary hack Primary=MINP ;; temporary hack
[MNLY.AT] [MNLY.AT]
Description=Minelayer (Anti-Tank) Description=Minelayer (Anti-Tank)
Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable, LimitedAmmo, Chronoshiftable, Passenger
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed
Primary=MINV ;; temporary hack Primary=MINV ;; temporary hack
@@ -138,8 +140,9 @@ Recoil=3
[LST] [LST]
Description=Transport Description=Transport
WaterBound=yes WaterBound=yes
Traits=Unit, Mobile, RenderUnit Traits=Unit, Mobile, RenderUnit, Cargo
LongDesc=General-purpose naval transport.\nCan carry infantry and tanks.\n Unarmed LongDesc=General-purpose naval transport.\nCan carry infantry and tanks.\n Unarmed
PassengerTypes=Foot,Wheel,Track
[PT] [PT]
Description=Gunboat Description=Gunboat
WaterBound=yes WaterBound=yes
@@ -179,9 +182,10 @@ Description=Transport Helicopter
RotorOffset=0,14,0,-4 RotorOffset=0,14,0,-4
RotorOffset2=0,-14,0,-2 RotorOffset2=0,-14,0,-2
BuiltAt=hpad BuiltAt=hpad
Traits=Unit, Helicopter, RenderUnitRotor, WithShadow Traits=Unit, Helicopter, RenderUnitRotor, WithShadow, Cargo
InitialFacing=20 InitialFacing=20
LongDesc=Fast Infantry Transport Helicopter.\n Unarmed LongDesc=Fast Infantry Transport Helicopter.\n Unarmed
PassengerTypes=Foot
[HELI] [HELI]
Description=Longbow Description=Longbow
BuiltAt=hpad BuiltAt=hpad
@@ -544,62 +548,62 @@ MEDI
Description=Attack Dog Description=Attack Dog
BuiltAt=KENN BuiltAt=KENN
Voice=DogVoice Voice=DogVoice
Traits=Unit, Mobile, RenderInfantry ;; AttackBase, SquishByTank, AutoTarget, dog?? Traits=Unit, Mobile, RenderInfantry, Passenger ;; AttackBase, SquishByTank, AutoTarget, dog??
LongDesc=Anti-infantry unit. Not fooled by the \nSpy's disguise.\n Strong vs Infantry\n Weak vs Vehicles LongDesc=Anti-infantry unit. Not fooled by the \nSpy's disguise.\n Strong vs Infantry\n Weak vs Vehicles
SelectionSize=12,17,-1,-4 SelectionSize=12,17,-1,-4
[E1] [E1]
Description=Rifle Infantry Description=Rifle Infantry
Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger
LongDesc=General-purpose infantry. Strong vs Infantry\n Weak vs Vehicles LongDesc=General-purpose infantry. Strong vs Infantry\n Weak vs Vehicles
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9
[E2] [E2]
Description=Grenadier Description=Grenadier
Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger
FireDelay=15 FireDelay=15
PrimaryOffset=0,0,0,-13 PrimaryOffset=0,0,0,-13
LongDesc=Infantry armed with grenades. \n Strong vs Buildings, Infantry\n Weak vs Vehicles LongDesc=Infantry armed with grenades. \n Strong vs Buildings, Infantry\n Weak vs Vehicles
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9
[E3] [E3]
Description=Rocket Soldier Description=Rocket Soldier
Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger
PrimaryOffset=0,0,0,-13 PrimaryOffset=0,0,0,-13
LongDesc=Anti-tank/Anti-aircraft infantry.\n Strong vs Tanks, Aircraft\n Weak vs Infantry LongDesc=Anti-tank/Anti-aircraft infantry.\n Strong vs Tanks, Aircraft\n Weak vs Infantry
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9
[E4] [E4]
Description=Flamethrower Description=Flamethrower
Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger
FireDelay=8 FireDelay=8
PrimaryOffset=0,0,0,-7 PrimaryOffset=0,0,0,-7
LongDesc=Advanced Anti-infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Vehicles LongDesc=Advanced Anti-infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Vehicles
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9
[E6] [E6]
Description=Engineer Description=Engineer
Traits=Unit, Mobile, EngineerCapture, RenderInfantry, TakeCover, SquishByTank Traits=Unit, Mobile, EngineerCapture, RenderInfantry, TakeCover, SquishByTank, Passenger
Voice=EngineerVoice Voice=EngineerVoice
LongDesc=Infiltrates and captures enemy structures.\n Strong vs Nothing\n Weak vs Everything LongDesc=Infiltrates and captures enemy structures.\n Strong vs Nothing\n Weak vs Everything
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9
[SPY] [SPY]
Description=Spy Description=Spy
Voice=SpyVoice Voice=SpyVoice
Traits=Unit, Mobile, RenderInfantry, TakeCover, SquishByTank Traits=Unit, Mobile, RenderInfantry, TakeCover, SquishByTank, Passenger
LongDesc=Infiltrates enemy structures to gather \nintelligence. Exact effect depends on the \nbuilding infiltrated.\n Strong vs Nothing\n Weak vs Everything\n Special Ability: Disguised LongDesc=Infiltrates enemy structures to gather \nintelligence. Exact effect depends on the \nbuilding infiltrated.\n Strong vs Nothing\n Weak vs Everything\n Special Ability: Disguised
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9
[THF] [THF]
Description=Thief Description=Thief
Voice=ThiefVoice Voice=ThiefVoice
Traits=Unit, Mobile, RenderInfantry, TakeCover, SquishByTank Traits=Unit, Mobile, RenderInfantry, TakeCover, SquishByTank, Passenger
LongDesc=Infiltrates enemy refineries & \nsilos, and steals money stored there.\n Unarmed LongDesc=Infiltrates enemy refineries & \nsilos, and steals money stored there.\n Unarmed
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9
[E7] [E7]
Description=Tanya Description=Tanya
Voice=TanyaVoice Voice=TanyaVoice
Traits=Unit, Mobile, RenderInfantry, C4Demolition, AttackBase, TakeCover, SquishByTank, AutoTarget Traits=Unit, Mobile, RenderInfantry, C4Demolition, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger
LongDesc=Elite commando infantry, armed with \ndual pistols and C4.\n Strong vs Infantry, Buildings\n Weak vs Vehicles\n Special Ability: Destroy Building with C4 LongDesc=Elite commando infantry, armed with \ndual pistols and C4.\n Strong vs Infantry, Buildings\n Weak vs Vehicles\n Special Ability: Destroy Building with C4
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9
[MEDI] [MEDI]
Description=Medic Description=Medic
Voice=MedicVoice Voice=MedicVoice
Traits=Unit, Mobile, RenderInfantry, AutoHeal, AttackBase, TakeCover, SquishByTank Traits=Unit, Mobile, RenderInfantry, AutoHeal, AttackBase, TakeCover, SquishByTank, Passenger
LongDesc=Heals nearby infantry.\n Strong vs Nothing\n Weak vs Everything LongDesc=Heals nearby infantry.\n Strong vs Nothing\n Weak vs Everything
SelectionSize=12,17,0,-9 SelectionSize=12,17,0,-9