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

This commit is contained in:
Alli
2010-01-06 19:48:03 +13:00
47 changed files with 750 additions and 127 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="IniFile.cs" />
<Compile Include="IniWriter.cs" />
<Compile Include="IPaletteRemap.cs" />
<Compile Include="Map.cs" />
<Compile Include="PackageEntry.cs" />
<Compile Include="Package.cs" />
@@ -62,6 +63,7 @@
<Compile Include="PaletteRemap.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ShpReader.cs" />
<Compile Include="ShroudPaletteRemap.cs" />
<Compile Include="Terrain.cs" />
<Compile Include="TileReference.cs" />
<Compile Include="TileSet.cs" />

View File

@@ -31,7 +31,7 @@ namespace OpenRa.FileFormats
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++)
colors.Add(r.GetRemappedColor(p.GetColor(i), i));

View File

@@ -4,7 +4,7 @@ using System.IO;
namespace OpenRa.FileFormats
{
public class PaletteRemap
public class PaletteRemap : IPaletteRemap
{
int offset;
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 IsInWorld { get; set; }
public DamageState GetDamageState()
{

View File

@@ -26,6 +26,7 @@ namespace OpenRa.Game
readonly Animation repairButton;
readonly Animation sellButton;
readonly Animation pwrdownButton;
readonly SpriteRenderer buildPaletteRenderer;
readonly Animation cantBuild;
@@ -77,6 +78,9 @@ namespace OpenRa.Game
sellButton = new Animation("sell");
sellButton.PlayRepeating("normal");
pwrdownButton = new Animation("repair");
pwrdownButton.PlayRepeating("normal");
blank = SheetBuilder.Add(new Size(64, 48), 16);
@@ -132,6 +136,8 @@ namespace OpenRa.Game
PerfHistory.Render(renderer, Game.worldRenderer.lineRenderer);
DrawMinimap();
chromeRenderer.DrawSprite(specialBinSprite, float2.Zero, PaletteType.Chrome);
chromeRenderer.DrawSprite(moneyBinSprite, new float2(Game.viewport.Width - 320, 0), PaletteType.Chrome);
@@ -143,12 +149,20 @@ namespace OpenRa.Game
int paletteHeight = DrawBuildPalette(currentTab);
DrawBuildTabs(paletteHeight);
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 DrawMinimap()
{
var hasRadar = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer
&& a.traits.Contains<ProvidesRadar>()
&& a.traits.Get<ProvidesRadar>().IsActive());
if (hasRadar)
Game.minimap.Draw(new float2(Game.viewport.Width - 256, 8));
}
void AddButton(Rectangle r, Action<bool> b) { buttons.Add(Pair.New(r, b)); }
void DrawBuildTabs(int paletteHeight)
{
const int tabWidth = 24;
@@ -285,7 +299,7 @@ namespace OpenRa.Game
// Repair
Rectangle repairRect = new Rectangle(Game.viewport.Width - 100, 5, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height);
Rectangle repairRect = new Rectangle(Game.viewport.Width - 120, 5, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height);
var repairDrawPos = Game.viewport.Location + new float2(repairRect.Location);
var hasFact = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains<ConstructionYard>());
@@ -300,7 +314,7 @@ namespace OpenRa.Game
buildPaletteRenderer.DrawSprite(repairButton.Image, repairDrawPos, PaletteType.Chrome);
// Sell
Rectangle sellRect = new Rectangle(Game.viewport.Width - 60, 5,
Rectangle sellRect = new Rectangle(Game.viewport.Width - 80, 5,
sellButton.Image.bounds.Width, sellButton.Image.bounds.Height);
var sellDrawPos = Game.viewport.Location + new float2(sellRect.Location);
@@ -310,6 +324,21 @@ namespace OpenRa.Game
AddButton(sellRect, isLmb => Game.controller.ToggleInputMode<SellOrderGenerator>());
buildPaletteRenderer.DrawSprite(sellButton.Image, sellDrawPos, PaletteType.Chrome);
buildPaletteRenderer.Flush();
if (Game.Settings.PowerDownBuildings)
{
// Power Down
Rectangle pwrdownRect = new Rectangle(Game.viewport.Width - 40, 5,
pwrdownButton.Image.bounds.Width, pwrdownButton.Image.bounds.Height);
var pwrdownDrawPos = Game.viewport.Location + new float2(pwrdownRect.Location);
pwrdownButton.ReplaceAnim(Game.controller.orderGenerator is PowerDownOrderGenerator ? "pressed" : "normal");
AddButton(pwrdownRect, isLmb => Game.controller.ToggleInputMode<PowerDownOrderGenerator>());
buildPaletteRenderer.DrawSprite(pwrdownButton.Image, pwrdownDrawPos, PaletteType.Chrome);
}
buildPaletteRenderer.Flush();
}
void HandleChronosphereButton()

View File

@@ -32,7 +32,7 @@ namespace OpenRa.Game
var maxSpread = GetMaximumSpread(weapon, warhead);
var hitActors = Game.FindUnitsInCircle(loc, maxSpread);
foreach (var victim in hitActors)
victim.InflictDamage(firedBy, (int)GetDamageToInflict(victim, loc, weapon, warhead), warhead);
}
@@ -46,11 +46,10 @@ namespace OpenRa.Game
{
if (!WeaponValidForTarget(weapon, target))
return 0f;
var distance = (target.CenterLocation - loc).Length;
var distance = (target.CenterLocation - loc).Length*1/24f;
var rawDamage = weapon.Damage * (float)Math.Exp(-distance / warhead.Spread);
var multiplier = warhead.EffectivenessAgainst(target.Info.Armor);
return rawDamage * multiplier;
}

View File

@@ -32,5 +32,6 @@ namespace OpenRa.Game
public static Cursor SellBlocked { get { return new Cursor("sell-blocked"); } }
public static Cursor Repair { get { return new Cursor("repair"); } }
public static Cursor RepairBlocked { get { return new Cursor("repair-blocked"); } }
public static Cursor PowerDown { get { return new Cursor("powerdown"); } }
}
}

View File

@@ -59,6 +59,11 @@ namespace OpenRa.Game
palette = new HardwarePalette(renderer, Rules.Map);
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++)
{
@@ -100,7 +105,7 @@ namespace OpenRa.Game
chrome = new Chrome(renderer);
oreFrequency = (int)(Rules.General.GrowthRate * 60 * 1000);
oreFrequency = (int)(Rules.General.GrowthRate * 60 * 25);
oreTicks = oreFrequency;
}
@@ -182,11 +187,12 @@ namespace OpenRa.Game
controller.orderGenerator.Tick();
if (--oreTicks == 0)
{
using (new PerfSample("ore"))
{
Rules.Map.GrowOre(SharedRandom);
oreTicks = oreFrequency;
}
minimap.InvalidateOre();
oreTicks = oreFrequency;
}
world.Tick();
UnitInfluence.Tick();

View File

@@ -55,13 +55,15 @@ namespace OpenRa.Game.GameRules
public readonly int OrePips = 0;
public readonly string Icon = 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 class MobileInfo : UnitInfo
{
public readonly int Passengers = 0;
public readonly int Speed = 0;
public readonly bool NoMovingFire = false;
public readonly string Voice = "GenericVoice";

View File

@@ -27,6 +27,7 @@ namespace OpenRa.Game.GameRules
// Gameplay options
public readonly bool RepairRequiresConyard = true;
public readonly bool PowerDownBuildings = true;
}
}

View File

@@ -11,8 +11,16 @@ namespace OpenRa.Game.Graphics
static Sprite[] LoadCursors(string filename)
{
var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts));
return shp.Select(a => SheetBuilder.Add(a.Image, a.Size)).ToArray();
try
{
var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts));
return shp.Select(a => SheetBuilder.Add(a.Image, a.Size)).ToArray();
}
catch (System.IndexOutOfRangeException) // This will occur when loading a custom (RA-format) .shp
{
var shp = new ShpReader(FileSystem.OpenWithExts(filename, exts));
return shp.Select(a => SheetBuilder.Add(a.Image, shp.Size)).ToArray();
}
}
public static Sprite[] LoadAllSprites(string filename) { return cursors[filename]; }

View File

@@ -3,7 +3,12 @@ using OpenRa.FileFormats;
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
{
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(pal); // iron curtain. todo: remap!
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)

View File

@@ -1,6 +1,7 @@
using System.Drawing;
using System.Linq;
using OpenRa.Game.Traits;
using OpenRa.FileFormats;
namespace OpenRa.Game.Graphics
{
@@ -9,7 +10,7 @@ namespace OpenRa.Game.Graphics
Sheet sheet;
SpriteRenderer spriteRenderer;
Sprite sprite;
Bitmap terrain;
Bitmap terrain, oreLayer;
public void Tick() { }
@@ -21,20 +22,29 @@ namespace OpenRa.Game.Graphics
}
// todo: extract these from the palette
static readonly Color[] terrainTypeColors = {
Color.Green,
Color.Red,
Color.Blue,
Color.Yellow,
Color.Purple,
Color.Turquoise,
Color.Violet,
Color.Tomato,
Color.Teal,
};
Color[] terrainTypeColors;
public void InvalidateOre() { oreLayer = null; }
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)
{
terrain = new Bitmap(128, 128);
@@ -45,14 +55,23 @@ namespace OpenRa.Game.Graphics
: 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 x = 0; x < 128; x++)
{
var b = Game.BuildingInfluence.GetBuildingAt(new int2(x, y));
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>()))
@@ -63,7 +82,7 @@ namespace OpenRa.Game.Graphics
public void Draw(float2 pos)
{
spriteRenderer.DrawSprite(sprite, pos, PaletteType.Chrome);
spriteRenderer.DrawSprite(sprite, pos, PaletteType.Chrome, new float2(256,256));
spriteRenderer.Flush();
}
}

View File

@@ -57,7 +57,7 @@ namespace OpenRa.Game.Graphics
public static Sequence GetSequence(string unitName, string sequenceName)
{
try { return units[unitName][sequenceName]; }
catch (KeyNotFoundException e)
catch (KeyNotFoundException)
{
throw new InvalidOperationException(
"Unit `{0}` does not have a sequence `{1}`".F(unitName, sequenceName));

View File

@@ -19,10 +19,10 @@ namespace OpenRa.Game.Graphics
this.channel = channel;
uv = new RectangleF(
(float)(bounds.Left + 0.5f) / sheet.Size.Width,
(float)(bounds.Top + 0.5f) / sheet.Size.Height,
(float)(bounds.Width) / sheet.Size.Width,
(float)(bounds.Height) / sheet.Size.Height);
(float)(bounds.Left + .5f) / sheet.Size.Width,
(float)(bounds.Top + .5f) / sheet.Size.Height,
(float)(bounds.Width - .5f) / sheet.Size.Width,
(float)(bounds.Height - .5f) / sheet.Size.Height);
uvhax = new float2[]
{

View File

@@ -55,12 +55,17 @@ namespace OpenRa.Game.Graphics
}
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)
Flush();
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;
if (++sprites >= spritesPerBatch)
Flush();

View File

@@ -34,7 +34,7 @@ namespace OpenRa.Game.Graphics
for( int i = map.XOffset ; i < map.XOffset + map.Width; i++ )
{
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;
ni += 6;
}

View File

@@ -8,18 +8,6 @@ namespace OpenRa.Game.Graphics
{
static class Util
{
static float2 KLerp(float2 o, float2 d, int k)
{
switch (k)
{
case 0: return o;
case 1: return new float2(o.X + d.X, o.Y);
case 2: return new float2(o.X, o.Y + d.Y);
case 3: return new float2(o.X + d.X, o.Y + d.Y);
default: throw new InvalidOperationException();
}
}
public static string[] ReadAllLines(Stream s)
{
List<string> result = new List<string>();
@@ -57,14 +45,18 @@ namespace OpenRa.Game.Graphics
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]);
vertices[nv] = new Vertex(KLerp(o, r.size, 0), r.FastMapTextureCoords(0), attrib);
vertices[nv + 1] = new Vertex(KLerp(o, r.size, 1), r.FastMapTextureCoords(1), attrib);
vertices[nv + 2] = new Vertex(KLerp(o, r.size, 2), r.FastMapTextureCoords(2), attrib);
vertices[nv + 3] = new Vertex(KLerp(o, r.size, 3), r.FastMapTextureCoords(3), attrib);
vertices[nv] = new Vertex(o,
r.FastMapTextureCoords(0), attrib);
vertices[nv + 1] = new Vertex(new float2(o.X + size.X, o.Y),
r.FastMapTextureCoords(1), attrib);
vertices[nv + 2] = new Vertex(new float2(o.X, o.Y + size.Y),
r.FastMapTextureCoords(2), attrib);
vertices[nv + 3] = new Vertex(new float2(o.X + size.X, o.Y + size.Y),
r.FastMapTextureCoords(3), attrib);
indices[ni] = (ushort)(nv);
indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1);

View File

@@ -87,6 +87,8 @@ namespace OpenRa.Game.Graphics
if (Game.controller.orderGenerator != null)
Game.controller.orderGenerator.Render();
Game.LocalPlayer.Shroud.Draw(spriteRenderer);
lineRenderer.Flush();
spriteRenderer.Flush();
}
@@ -211,7 +213,7 @@ namespace OpenRa.Game.Graphics
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");
pipImages.PlayRepeating(pipStrings[(int)pip]);

View File

@@ -103,6 +103,7 @@
<Compile Include="Orders\NetworkOrderSource.cs" />
<Compile Include="Orders\OrderIO.cs" />
<Compile Include="Orders\OrderManager.cs" />
<Compile Include="Orders\PowerDownOrderGenerator.cs" />
<Compile Include="Orders\RepairOrderGenerator.cs" />
<Compile Include="Orders\SellOrderGenerator.cs" />
<Compile Include="Orders\ChronoshiftDestinationOrderGenerator.cs" />
@@ -110,6 +111,7 @@
<Compile Include="PathSearch.cs" />
<Compile Include="ProductionItem.cs" />
<Compile Include="Orders\ReplayOrderSource.cs" />
<Compile Include="Shroud.cs" />
<Compile Include="Smudge.cs" />
<Compile Include="Sound.cs" />
<Compile Include="Support\Stopwatch.cs" />
@@ -118,6 +120,7 @@
<Compile Include="Traits\Activities\Attack.cs" />
<Compile Include="Traits\Activities\CaptureBuilding.cs" />
<Compile Include="Traits\Activities\Demolish.cs" />
<Compile Include="Traits\Activities\EnterTransport.cs" />
<Compile Include="Traits\Activities\Fly.cs" />
<Compile Include="Traits\Activities\FlyAttack.cs" />
<Compile Include="Traits\Activities\FlyTimed.cs" />
@@ -185,6 +188,7 @@
<Compile Include="Traits\Activities\Follow.cs" />
<Compile Include="Traits\Activities\Turn.cs" />
<Compile Include="Traits\Activities\UndeployMcv.cs" />
<Compile Include="Traits\Activities\UnloadCargo.cs" />
<Compile Include="Traits\APMine.cs" />
<Compile Include="Traits\ATMine.cs" />
<Compile Include="Traits\AttackBase.cs" />
@@ -198,6 +202,7 @@
<Compile Include="Traits\BelowUnits.cs" />
<Compile Include="Traits\Building.cs" />
<Compile Include="Traits\C4Demolition.cs" />
<Compile Include="Traits\Cargo.cs" />
<Compile Include="Traits\Chronoshiftable.cs" />
<Compile Include="Traits\ChronoshiftPaletteEffect.cs" />
<Compile Include="Traits\Chronosphere.cs" />
@@ -213,6 +218,8 @@
<Compile Include="Traits\MineImmune.cs" />
<Compile Include="Traits\Minelayer.cs" />
<Compile Include="Traits\LimitedAmmo.cs" />
<Compile Include="Traits\Passenger.cs" />
<Compile Include="Traits\ProvidesRadar.cs" />
<Compile Include="Traits\Repairable.cs" />
<Compile Include="Traits\Reservable.cs" />
<Compile Include="Traits\SquishByTank.cs" />
@@ -297,4 +304,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRa.Game.GameRules;
using OpenRa.Game.Traits;
namespace OpenRa.Game.Orders
{
class PowerDownOrderGenerator : IOrderGenerator
{
public IEnumerable<Order> Order(int2 xy, MouseInput mi)
{
if (mi.Button == MouseButton.Right)
Game.controller.CancelInputMode();
return OrderInner(xy, mi);
}
IEnumerable<Order> OrderInner(int2 xy, MouseInput mi)
{
if (mi.Button == MouseButton.Left)
{
var loc = mi.Location + Game.viewport.Location;
var underCursor = Game.FindUnits(loc, loc)
.Where(a => a.Owner == Game.LocalPlayer
&& a.traits.Contains<Building>()
&& a.Info.Selectable).FirstOrDefault();
var building = underCursor != null ? underCursor.Info as BuildingInfo : null;
if (building != null)
yield return new Order("PowerDown", underCursor, null, int2.Zero, null);
}
}
public void Tick() { }
public void Render() { }
public Cursor GetCursor(int2 xy, MouseInput mi)
{
mi.Button = MouseButton.Left;
return OrderInner(xy, mi).Any()
? Cursor.PowerDown : Cursor.PowerDown;
}
}
}

View File

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

View File

@@ -1,5 +1,6 @@
using System;
using System.Linq;
using System.Collections.Generic;
using OpenRa.Game.GameRules;
using OpenRa.Game.Graphics;
using OpenRa.Game.Traits;
@@ -17,15 +18,17 @@ namespace OpenRa.Game
public string InternalName;
public Race Race;
public readonly int Index;
public int Cash;
public int Ore;
public int Cash = 10000;
public int Ore = 0;
public int OreCapacity;
public int DisplayCash;
public int PowerProvided;
public int PowerDrained;
public int DisplayCash = 0;
public int PowerProvided = 0;
public int PowerDrained = 0;
public bool IsReady;
public Shroud Shroud = new Shroud();
public Player( Actor playerActor, int index, PaletteType palette, string playerName, Race race, string internalName )
{
this.PlayerActor = playerActor;
@@ -34,10 +37,6 @@ namespace OpenRa.Game
this.InternalName = internalName;
this.PlayerName = playerName;
this.Race = race;
this.Cash = 10000;
this.Ore = 0;
this.DisplayCash = 0;
this.PowerProvided = this.PowerDrained = 0;
}
void UpdatePower()
@@ -52,11 +51,11 @@ namespace OpenRa.Game
foreach (var a in myBuildings)
{
var bi = a.Info as BuildingInfo;
if (bi.Power > 0) /* todo: is this how real-ra scales it? */
PowerProvided += (a.Health * bi.Power) / bi.Strength;
var p = a.traits.Get<Building>().GetPowerUsage();
if (p > 0)
PowerProvided += p;
else
PowerDrained -= bi.Power;
PowerDrained -= p;
}
if (PowerProvided - PowerDrained < 0)

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

@@ -0,0 +1,110 @@
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 = 1; j < 127; j++)
{
var starti = 1;
for (var i = 1; i < 127; i++)
{
if (sprites[i, j] == shadowBits[0x0f])
continue;
if (starti != i)
{
r.DrawSprite(sprites[starti,j],
Game.CellSize * new float2(starti, j),
PaletteType.Shroud,
new float2(Game.CellSize * (i - starti), Game.CellSize));
starti = i+1;
}
r.DrawSprite(sprites[i, j],
Game.CellSize * new float2(i, j),
PaletteType.Shroud);
starti = i+1;
}
if (starti < 127)
r.DrawSprite(sprites[starti, j],
Game.CellSize * new float2(starti, j),
PaletteType.Shroud,
new float2(Game.CellSize * (127 - starti), Game.CellSize));
}
}
}
}

View File

@@ -2,7 +2,7 @@ using OpenRa.Game.Graphics;
namespace OpenRa.Game
{
enum UnitMovementType : byte
public enum UnitMovementType : byte
{
Foot = 0,
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 int desiredFacing;
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

@@ -26,12 +26,9 @@ namespace OpenRa.Game.Traits
protected override void QueueAttack( Actor self, Order order )
{
var bi = self.Info as BuildingInfo;
if (bi != null && bi.Powered && self.Owner.GetPowerState() != PowerState.Normal)
{
if (self.Owner == Game.LocalPlayer) Sound.Play("nopowr1.aud");
var b = self.traits.Get<Building>();
if (b != null && b.InsuffientPower())
return;
}
const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */
/* todo: choose the appropriate weapon, when only one works against this target */

View File

@@ -5,20 +5,66 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRa.Game.Effects;
using OpenRa.Game.Graphics;
namespace OpenRa.Game.Traits
{
class Building : INotifyDamage, IOrder, ITick
class Building : INotifyDamage, IOrder, ITick, IRenderModifier
{
readonly Actor self;
public readonly BuildingInfo unitInfo;
bool isRepairing = false;
bool isPoweredDown = false;
public Building(Actor self)
{
this.self = self;
unitInfo = (BuildingInfo)self.Info;
self.CenterLocation = Game.CellSize
* ((float2)self.Location + .5f * (float2)unitInfo.Dimensions);
}
public bool InsuffientPower()
{
return (isPoweredDown || (unitInfo.Powered && self.Owner.GetPowerState() != PowerState.Normal));
}
public int GetPowerUsage()
{
if (isPoweredDown)
return 0;
if (unitInfo.Power > 0) /* todo: is this how real-ra scales it? */
return (self.Health * unitInfo.Power) / unitInfo.Strength;
else
return unitInfo.Power;
}
public Animation iconAnim;
public IEnumerable<Renderable>
ModifyRender(Actor self, IEnumerable<Renderable> rs)
{
if (!InsuffientPower())
return rs;
List<Renderable> nrs = new List<Renderable>(rs);
foreach(var r in rs)
{
// Need 2 shadows to make it dark enough
nrs.Add(r.WithPalette(PaletteType.Shadow));
nrs.Add(r.WithPalette(PaletteType.Shadow));
}
if (isPoweredDown)
{
iconAnim = new Animation("powerdown");
iconAnim.PlayRepeating("disabled");
nrs.Add(new Renderable(iconAnim.Image, self.CenterLocation - 0.5f*iconAnim.Image.size, PaletteType.Chrome));
}
return nrs;
}
public void Damaged(Actor self, AttackInfo e)
{
@@ -43,6 +89,12 @@ namespace OpenRa.Game.Traits
{
isRepairing = !isRepairing;
}
if (order.OrderString == "PowerDown")
{
isPoweredDown = !isPoweredDown;
Sound.Play((isPoweredDown) ? "bleep12.aud" : "bleep11.aud");
}
}
int remainingTicks;

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
public IEnumerable<PipType> GetPips()
public IEnumerable<PipType> GetPips(Actor self)
{
const int numPips = 5;
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;
for (int i = 0; i < numPips; i++)

View File

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

View File

@@ -18,7 +18,16 @@ namespace OpenRa.Game.Traits
public int2 toCell
{
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)

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

@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRa.Game.Traits
{
class ProvidesRadar
{
Actor self;
public ProvidesRadar(Actor self)
{
this.self = self;
}
public bool IsActive()
{
// TODO: Check for nearby MRJ
// Check if powered
var b = self.traits.Get<Building>();
if (b != null && b.InsuffientPower())
return false;
return true;
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -21,7 +21,7 @@ All tracked vehicles
Light vehicles
V2RL Works
APC Cargo doesn't work
APC Works
MNLY Works
MGG No gap
MRJ No radar
@@ -31,8 +31,7 @@ HARV Works
ARTY Works
Helicopters
- Return to base after attack doesnt work
TRAN Cargo doesn't work
TRAN Works
HELI Works
HIND Works
@@ -47,5 +46,5 @@ CA Works
SS Works
DD 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">
<sequence name="idle" start="0" length="32" />
<sequence name="muzzle" start="0" length="48" src="minigun" />
<sequence name="close" start="32" length="3" />
<sequence name="unload" start="32" length="1" />
</unit>
<!-- mine layer -->
<unit name="mnly">
@@ -376,7 +378,7 @@
<sequence name="default-minimap" start="80" length="1" />
<sequence name="ability" start="82" length="8" />
<sequence name="nuke" start="90" length="7" x="12" y="12" />
<sequence name="chrono-select" start="97" length="8" x="12" y="12" />
<sequence name="chrono-select" start="97" length="8" x="12" y="12" />
<sequence name="chrono-target" start="105" length="8" x="12" y="12" />
<sequence name="enter" start="113" length="3" x="12" y="12" />
<sequence name="c4" start="116" length="3" x="12" y="12" />
@@ -401,6 +403,12 @@
<sequence name="repair2-blocked" start="213" length="1" />
<sequence name="ability-minimap" start="214" length="8" />
</cursor>
<cursor src="speed">
<sequence name="powerdown" start="3" length="12" />
</cursor>
<unit name="powerdown">
<sequence name="disabled" start="3" length="1" src="speed" />
</unit>
<unit name="120mm">
<sequence name="idle" start="0" length="1" />
</unit>
@@ -536,6 +544,8 @@
</unit>
<unit name="lst">
<sequence name="idle" start="0" length="1" />
<sequence name="open" start="1" length="4" />
<sequence name="unload" start="4" length="1" />
</unit>
<unit name="pips">
<sequence name="groups" start="8" length="10" />
@@ -572,6 +582,8 @@
<sequence name="rotor2" start="0" length="4" src="rrotor" />
<sequence name="slow-rotor" start="4" length="8" src="lrotor" />
<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 name="v2">
<sequence name="idle" start="0" length="32" />
@@ -1008,8 +1020,8 @@
<sequence name="disabled" start="2" length="1" />
</unit>
<unit name="sell">
<sequence name="normal" start="0" length="1" />
<sequence name="pressed" start="1" length="1" />
<sequence name="disabled" start="2" length="1" />
<sequence name="normal" start="0" length="1" />
<sequence name="pressed" start="1" length="1" />
<sequence name="disabled" start="2" length="1" />
</unit>
</sequences>

View File

@@ -10,4 +10,4 @@ s2=Multi1,mcv,600,12505,0,Guard,None
;s2=Multi1,e3,600,12505,0,Guard,None
s3=Multi3,mcv,600,2910,0,Guard,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]
Description=V2 Rocket
Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable, Chronoshiftable
Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable, Chronoshiftable, Passenger
Voice=VehicleVoice
LongDesc=Long-range rocket artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft
[1TNK]
Description=Light Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
Recoil=2
Voice=VehicleVoice
LongDesc=Light Tank, good for scouting.\n Strong vs Light Vehicles\n Weak vs Tanks, Aircraft
[2TNK]
Description=Medium Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
Recoil=3
Voice=VehicleVoice
LongDesc=Allied Main Battle Tank.\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft
[3TNK]
Description=Heavy Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
Recoil=3
Voice=VehicleVoice
LongDesc=Soviet Main Battle Tank, with dual cannons\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft
[4TNK]
Description=Mammoth Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable, Chronoshiftable, Passenger
Voice=VehicleVoice
LongDesc=Big and slow tank, with anti-air capability.\n Strong vs Tanks, Aircraft\n Weak vs Infantry
[ARTY]
Description=Artillery
Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable
Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable, Chronoshiftable, Passenger
Voice=VehicleVoice
LongDesc=Long-range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft
[JEEP]
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
MuzzleFlash=yes
Voice=VehicleVoice
LongDesc=Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft
[APC]
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
MuzzleFlash=yes
Voice=VehicleVoice
LongDesc=Tough infantry transport.\n Strong vs Infantry, Light Vehicles\n Weak vs Tanks, Aircraft
UnloadFacing=220
PassengerTypes=Foot
;; non-combat vehicles
[MRJ]
Description=Radar Jammer
Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable
Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger
PrimaryOffset=0,4,0,-6
SelectionPriority=3
Voice=VehicleVoice
LongDesc=Hides nearby units on the enemy's minimap.\n Unarmed
[MGG]
Description=Mobile Gap Generator
Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable
Traits=Unit, Mobile, RenderUnitSpinner, Repairable, Chronoshiftable, Passenger
PrimaryOffset=0,6,0,-3
SelectionPriority=3
Voice=VehicleVoice
LongDesc=Regenerates Fog of War in a small area \naround the unit.\n Unarmed
[HARV]
Description=Ore Truck
Traits=Harvester, Unit, Mobile, RenderUnit, Repairable, Chronoshiftable
Traits=Harvester, Unit, Mobile, RenderUnit, Repairable, Chronoshiftable, Passenger
SelectionPriority=7
Voice=VehicleVoice
LongDesc=Collects Ore and Gems for processing.\n Unarmed
[MCV]
Description=Mobile Construction Vehicle
Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable, Chronoshiftable
Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable, Chronoshiftable, Passenger
SelectionPriority=3
Voice=VehicleVoice
LongDesc=Deploys into another Construction Yard.\n Unarmed
[MNLY.AP]
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
LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed
Primary=MINP ;; temporary hack
[MNLY.AT]
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
LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed
Primary=MINV ;; temporary hack
@@ -138,8 +140,9 @@ Recoil=3
[LST]
Description=Transport
WaterBound=yes
Traits=Unit, Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit, Cargo
LongDesc=General-purpose naval transport.\nCan carry infantry and tanks.\n Unarmed
PassengerTypes=Foot,Wheel,Track
[PT]
Description=Gunboat
WaterBound=yes
@@ -179,9 +182,10 @@ Description=Transport Helicopter
RotorOffset=0,14,0,-4
RotorOffset2=0,-14,0,-2
BuiltAt=hpad
Traits=Unit, Helicopter, RenderUnitRotor, WithShadow
Traits=Unit, Helicopter, RenderUnitRotor, WithShadow, Cargo
InitialFacing=20
LongDesc=Fast Infantry Transport Helicopter.\n Unarmed
PassengerTypes=Foot
[HELI]
Description=Longbow
BuiltAt=hpad
@@ -407,7 +411,7 @@ SpawnOffset=0,-4
LongDesc=Produces and reloads helicopters
[DOME]
Description=Radar Dome
Traits=Building, RenderBuilding
Traits=Building, RenderBuilding, ProvidesRadar
Dimensions=2,2
Footprint=xx xx
SelectionPriority=3
@@ -544,62 +548,62 @@ MEDI
Description=Attack Dog
BuiltAt=KENN
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
SelectionSize=12,17,-1,-4
[E1]
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
SelectionSize=12,17,0,-9
[E2]
Description=Grenadier
Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget
Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger
FireDelay=15
PrimaryOffset=0,0,0,-13
LongDesc=Infantry armed with grenades. \n Strong vs Buildings, Infantry\n Weak vs Vehicles
SelectionSize=12,17,0,-9
[E3]
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
LongDesc=Anti-tank/Anti-aircraft infantry.\n Strong vs Tanks, Aircraft\n Weak vs Infantry
SelectionSize=12,17,0,-9
[E4]
Description=Flamethrower
Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget
Traits=Unit, Mobile, RenderInfantry, AttackBase, TakeCover, SquishByTank, AutoTarget, Passenger
FireDelay=8
PrimaryOffset=0,0,0,-7
LongDesc=Advanced Anti-infantry unit.\n Strong vs Infantry, Buildings\n Weak vs Vehicles
SelectionSize=12,17,0,-9
[E6]
Description=Engineer
Traits=Unit, Mobile, EngineerCapture, RenderInfantry, TakeCover, SquishByTank
Traits=Unit, Mobile, EngineerCapture, RenderInfantry, TakeCover, SquishByTank, Passenger
Voice=EngineerVoice
LongDesc=Infiltrates and captures enemy structures.\n Strong vs Nothing\n Weak vs Everything
SelectionSize=12,17,0,-9
[SPY]
Description=Spy
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
SelectionSize=12,17,0,-9
[THF]
Description=Thief
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
SelectionSize=12,17,0,-9
[E7]
Description=Tanya
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
SelectionSize=12,17,0,-9
[MEDI]
Description=Medic
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
SelectionSize=12,17,0,-9