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

This commit is contained in:
Caleb Anderson
2010-01-06 00:51:22 -06:00
59 changed files with 1206 additions and 178 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

@@ -34,7 +34,7 @@ namespace OpenRa.Game
typing += c; typing += c;
} }
static readonly Color[] paletteColors = public static readonly Color[] paletteColors =
{ {
Color.FromArgb(228, 200, 112), Color.FromArgb(228, 200, 112),
Color.FromArgb(56, 72, 125), Color.FromArgb(56, 72, 125),

View File

@@ -26,7 +26,8 @@ namespace OpenRa.Game
readonly Animation repairButton; readonly Animation repairButton;
readonly Animation sellButton; readonly Animation sellButton;
readonly Animation pwrdownButton;
readonly SpriteRenderer buildPaletteRenderer; readonly SpriteRenderer buildPaletteRenderer;
readonly Animation cantBuild; readonly Animation cantBuild;
readonly Animation ready; readonly Animation ready;
@@ -40,6 +41,8 @@ namespace OpenRa.Game
readonly int paletteColumns; readonly int paletteColumns;
readonly int2 paletteOrigin; readonly int2 paletteOrigin;
const int MinRows = 4;
public Chrome(Renderer r) public Chrome(Renderer r)
{ {
@@ -75,6 +78,9 @@ namespace OpenRa.Game
sellButton = new Animation("sell"); sellButton = new Animation("sell");
sellButton.PlayRepeating("normal"); sellButton.PlayRepeating("normal");
pwrdownButton = new Animation("repair");
pwrdownButton.PlayRepeating("normal");
blank = SheetBuilder.Add(new Size(64, 48), 16); blank = SheetBuilder.Add(new Size(64, 48), 16);
@@ -96,7 +102,7 @@ namespace OpenRa.Game
cantBuild = new Animation("clock"); cantBuild = new Animation("clock");
cantBuild.PlayFetchIndex("idle", () => 0); cantBuild.PlayFetchIndex("idle", () => 0);
digitSprites = OpenRa.Game.Graphics.Util.MakeArray(10, a => a) digitSprites = Graphics.Util.MakeArray(10, a => a)
.Select(n => new Sprite(specialBin, new Rectangle(32 + 13 * n, 0, 13, 17), TextureChannel.Alpha)).ToList(); .Select(n => new Sprite(specialBin, new Rectangle(32 + 13 * n, 0, 13, 17), TextureChannel.Alpha)).ToList();
shimSprites = new[] shimSprites = new[]
@@ -130,6 +136,8 @@ namespace OpenRa.Game
PerfHistory.Render(renderer, Game.worldRenderer.lineRenderer); PerfHistory.Render(renderer, Game.worldRenderer.lineRenderer);
DrawMinimap();
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 +151,18 @@ namespace OpenRa.Game
DrawChat(); DrawChat();
} }
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) void DrawBuildTabs(int paletteHeight)
{ {
const int tabWidth = 24; const int tabWidth = 24;
@@ -215,6 +233,9 @@ namespace OpenRa.Game
x -= 14; x -= 14;
} }
} }
float? lastPowerProvidedPos;
float? lastPowerDrainedPos;
void DrawPower() void DrawPower()
{ {
@@ -228,16 +249,32 @@ namespace OpenRa.Game
float2 bottom = powerOrigin + new float2(0, powerLevelTopSprite.size.Y + powerLevelBottomSprite.size.Y) - new float2(0, 50); float2 bottom = powerOrigin + new float2(0, powerLevelTopSprite.size.Y + powerLevelBottomSprite.size.Y) - new float2(0, 50);
var scale = 100; var scale = 100;
while(Game.LocalPlayer.PowerProvided >= scale) scale += 100; while(Math.Max(Game.LocalPlayer.PowerProvided, Game.LocalPlayer.PowerDrained) >= scale) scale *= 2;
//draw bar //draw bar
float2 powerTop = new float2(bottom.X, bottom.Y + (top.Y - bottom.Y) * (Game.LocalPlayer.PowerProvided / (float)scale));
var powerTopY = bottom.Y + (top.Y - bottom.Y) * (Game.LocalPlayer.PowerProvided / (float)scale) - Game.viewport.Location.Y;
lastPowerProvidedPos = float2.Lerp(lastPowerProvidedPos.GetValueOrDefault(powerTopY), powerTopY, .3f);
float2 powerTop = new float2(bottom.X, lastPowerProvidedPos.Value + Game.viewport.Location.Y);
var color = Color.LimeGreen;
if (Game.LocalPlayer.GetPowerState() == PowerState.Low)
color = Color.Orange;
if (Game.LocalPlayer.GetPowerState() == PowerState.Critical)
color = Color.Red;
var color2 = Graphics.Util.Lerp(0.25f, color, Color.Black);
for(int i = 11; i < 13; i++)
lineRenderer.DrawLine(bottom + new float2(i, 0), powerTop + new float2(i, 0), color, color);
for (int i = 13; i < 15; i++)
lineRenderer.DrawLine(bottom + new float2(i, 0), powerTop + new float2(i, 0), color2, color2);
for(int i = 7; i < 11; i++)
lineRenderer.DrawLine(bottom + new float2(i, 0), powerTop + new float2(i, 0), Color.LimeGreen, Color.LimeGreen);
lineRenderer.Flush(); lineRenderer.Flush();
var drainedPositionY = bottom.Y + (top.Y - bottom.Y)*(Game.LocalPlayer.PowerDrained/(float) scale) - powerIndicatorSprite.size.Y /2 - Game.viewport.Location.Y;
lastPowerDrainedPos = float2.Lerp(lastPowerDrainedPos.GetValueOrDefault(drainedPositionY), drainedPositionY, .3f);
//draw indicator //draw indicator
float2 drainedPosition = new float2(bottom.X , bottom.Y + (top.Y - bottom.Y)*(Game.LocalPlayer.PowerDrained/(float) scale)); float2 drainedPosition = new float2(bottom.X + 2, lastPowerDrainedPos.Value + Game.viewport.Location.Y);
buildPaletteRenderer.DrawSprite(powerIndicatorSprite, drainedPosition, PaletteType.Chrome); buildPaletteRenderer.DrawSprite(powerIndicatorSprite, drainedPosition, PaletteType.Chrome);
buildPaletteRenderer.Flush(); buildPaletteRenderer.Flush();
@@ -245,8 +282,24 @@ namespace OpenRa.Game
void DrawButtons() void DrawButtons()
{ {
// Chronoshift
Rectangle chronoshiftRect = new Rectangle(6, 14, repairButton.Image.bounds.Width, repairButton.Image.bounds.Height);
var chronoshiftDrawPos = Game.viewport.Location + new float2(chronoshiftRect.Location);
var hasChronosphere = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains<Chronosphere>());
if (!hasChronosphere)
repairButton.ReplaceAnim("disabled");
else
{
//repairButton.ReplaceAnim(Game.controller.orderGenerator is RepairOrderGenerator ? "pressed" : "normal");
AddButton(chronoshiftRect, isLmb => HandleChronosphereButton());
}
buildPaletteRenderer.DrawSprite(repairButton.Image, chronoshiftDrawPos, PaletteType.Chrome);
// Repair // 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 repairDrawPos = Game.viewport.Location + new float2(repairRect.Location);
var hasFact = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains<ConstructionYard>()); var hasFact = Game.world.Actors.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains<ConstructionYard>());
@@ -261,7 +314,7 @@ namespace OpenRa.Game
buildPaletteRenderer.DrawSprite(repairButton.Image, repairDrawPos, PaletteType.Chrome); buildPaletteRenderer.DrawSprite(repairButton.Image, repairDrawPos, PaletteType.Chrome);
// Sell // 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); sellButton.Image.bounds.Width, sellButton.Image.bounds.Height);
var sellDrawPos = Game.viewport.Location + new float2(sellRect.Location); var sellDrawPos = Game.viewport.Location + new float2(sellRect.Location);
@@ -271,6 +324,27 @@ namespace OpenRa.Game
AddButton(sellRect, isLmb => Game.controller.ToggleInputMode<SellOrderGenerator>()); AddButton(sellRect, isLmb => Game.controller.ToggleInputMode<SellOrderGenerator>());
buildPaletteRenderer.DrawSprite(sellButton.Image, sellDrawPos, PaletteType.Chrome); buildPaletteRenderer.DrawSprite(sellButton.Image, sellDrawPos, PaletteType.Chrome);
buildPaletteRenderer.Flush(); 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()
{
if (Game.controller.ToggleInputMode<ChronosphereSelectOrderGenerator>())
Sound.Play("slcttgt1.aud");
} }
void DrawChat() void DrawChat()
@@ -392,7 +466,7 @@ namespace OpenRa.Game
if (++x == columns) { x = 0; y++; } if (++x == columns) { x = 0; y++; }
} }
while (x != 0) while (x != 0 || y < MinRows)
{ {
var rect = new Rectangle(origin.X + x * 64, origin.Y + 48 * y, 64, 48); var rect = new Rectangle(origin.X + x * 64, origin.Y + 48 * y, 64, 48);
var drawPos = Game.viewport.Location + new float2(rect.Location); var drawPos = Game.viewport.Location + new float2(rect.Location);

View File

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

View File

@@ -27,12 +27,18 @@ namespace OpenRa.Game
orderGenerator = new UnitOrderGenerator(new Actor[] { }); orderGenerator = new UnitOrderGenerator(new Actor[] { });
} }
public void ToggleInputMode<T>() where T : IOrderGenerator, new() public bool ToggleInputMode<T>() where T : IOrderGenerator, new()
{ {
if (orderGenerator is T) if (orderGenerator is T)
{
CancelInputMode(); CancelInputMode();
return false;
}
else else
{
orderGenerator = new T(); orderGenerator = new T();
return true;
}
} }
List<Order> recentOrders = new List<Order>(); List<Order> recentOrders = new List<Order>();

View File

@@ -19,10 +19,12 @@ namespace OpenRa.Game
public static Cursor Select { get { return new Cursor("select"); } } public static Cursor Select { get { return new Cursor("select"); } }
public static Cursor MoveBlocked { get { return new Cursor("move-blocked"); } } public static Cursor MoveBlocked { get { return new Cursor("move-blocked"); } }
public static Cursor Attack { get { return new Cursor("attack"); } } public static Cursor Attack { get { return new Cursor("attack"); } }
public static Cursor AttackMove { get { return new Cursor("attackmove"); } }
public static Cursor Deploy { get { return new Cursor("deploy"); } } public static Cursor Deploy { get { return new Cursor("deploy"); } }
public static Cursor Enter { get { return new Cursor("enter"); } } public static Cursor Enter { get { return new Cursor("enter"); } }
public static Cursor DeployBlocked { get { return new Cursor("deploy-blocked"); } } public static Cursor DeployBlocked { get { return new Cursor("deploy-blocked"); } }
public static Cursor Chronoshift { get { return new Cursor("chrono"); } } public static Cursor Chronoshift { get { return new Cursor("chrono-target"); } }
public static Cursor ChronoshiftSelect { get { return new Cursor("chrono-select"); } }
public static Cursor C4 { get { return new Cursor("c4"); } } public static Cursor C4 { get { return new Cursor("c4"); } }
public static Cursor Capture { get { return new Cursor("capture"); } } public static Cursor Capture { get { return new Cursor("capture"); } }
public static Cursor Heal { get { return new Cursor("heal"); } } public static Cursor Heal { get { return new Cursor("heal"); } }
@@ -30,5 +32,6 @@ namespace OpenRa.Game
public static Cursor SellBlocked { get { return new Cursor("sell-blocked"); } } public static Cursor SellBlocked { get { return new Cursor("sell-blocked"); } }
public static Cursor Repair { get { return new Cursor("repair"); } } public static Cursor Repair { get { return new Cursor("repair"); } }
public static Cursor RepairBlocked { get { return new Cursor("repair-blocked"); } } public static Cursor RepairBlocked { get { return new Cursor("repair-blocked"); } }
public static Cursor PowerDown { get { return new Cursor("powerdown"); } }
} }
} }

View File

@@ -48,6 +48,7 @@ namespace OpenRa.Game
static bool usingAftermath; static bool usingAftermath;
static int2 clientSize; static int2 clientSize;
static HardwarePalette palette; static HardwarePalette palette;
public static Minimap minimap;
public static void ChangeMap(string mapName) public static void ChangeMap(string mapName)
{ {
@@ -58,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++)
{ {
@@ -73,10 +79,12 @@ namespace OpenRa.Game
var worldActor = new Actor(null, new int2(int.MaxValue, int.MaxValue), null); var worldActor = new Actor(null, new int2(int.MaxValue, int.MaxValue), null);
worldActor.traits.Add(new Traits.WaterPaletteRotation(worldActor)); worldActor.traits.Add(new Traits.WaterPaletteRotation(worldActor));
worldActor.traits.Add(new Traits.ChronoshiftPaletteEffect(worldActor));
Game.world.Add(worldActor); Game.world.Add(worldActor);
Rules.Map.InitOreDensity(); Rules.Map.InitOreDensity();
worldRenderer = new WorldRenderer(renderer); worldRenderer = new WorldRenderer(renderer);
minimap = new Minimap(renderer);
SequenceProvider.Initialize(usingAftermath); SequenceProvider.Initialize(usingAftermath);
viewport = new Viewport(clientSize, Rules.Map.Offset, Rules.Map.Offset + Rules.Map.Size, renderer); viewport = new Viewport(clientSize, Rules.Map.Offset, Rules.Map.Offset + Rules.Map.Size, renderer);
@@ -97,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;
} }
@@ -168,6 +176,8 @@ namespace OpenRa.Game
lastTime += Settings.Timestep; lastTime += Settings.Timestep;
UpdatePalette(world.Actors.SelectMany( UpdatePalette(world.Actors.SelectMany(
a => a.traits.WithInterface<IPaletteModifier>())); a => a.traits.WithInterface<IPaletteModifier>()));
minimap.Update();
orderManager.TickImmediate(); orderManager.TickImmediate();
if (orderManager.IsReadyForNextFrame) if (orderManager.IsReadyForNextFrame)
@@ -177,11 +187,12 @@ 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);
oreTicks = oreFrequency; minimap.InvalidateOre();
} oreTicks = oreFrequency;
}
world.Tick(); world.Tick();
UnitInfluence.Tick(); UnitInfluence.Tick();

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

@@ -26,7 +26,8 @@ namespace OpenRa.Game.GameRules
public readonly string Replay = ""; public readonly string Replay = "";
// Gameplay options // Gameplay options
public readonly bool RepairRequiresConyard = false; 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) static Sprite[] LoadCursors(string filename)
{ {
var shp = new Dune2ShpReader(FileSystem.OpenWithExts(filename, exts)); try
return shp.Select(a => SheetBuilder.Add(a.Image, a.Size)).ToArray(); {
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]; } public static Sprite[] LoadAllSprites(string filename) { return cursors[filename]; }

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

@@ -0,0 +1,89 @@
using System.Drawing;
using System.Linq;
using OpenRa.Game.Traits;
using OpenRa.FileFormats;
namespace OpenRa.Game.Graphics
{
class Minimap
{
Sheet sheet;
SpriteRenderer spriteRenderer;
Sprite sprite;
Bitmap terrain, oreLayer;
public void Tick() { }
public Minimap(Renderer r)
{
sheet = new Sheet(r, new Size(128, 128));
spriteRenderer = new SpriteRenderer(r, true, r.RgbaSpriteShader);
sprite = new Sprite(sheet, new Rectangle(0, 0, 128, 128), TextureChannel.Alpha);
}
// todo: extract these from the palette
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);
for (var y = 0; y < 128; y++)
for (var x = 0; x < 128; x++)
terrain.SetPixel(x, y, Rules.Map.IsInMap(x, y)
? terrainTypeColors[Rules.TileSet.GetWalkability(Rules.Map.MapTiles[x, y])]
: Color.Black);
}
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] : terrainTypeColors[4]);
}
foreach (var a in Game.world.Actors.Where(a => a.traits.Contains<Unit>()))
bitmap.SetPixel(a.Location.X, a.Location.Y, Chat.paletteColors[(int)a.Owner.Palette]);
sheet.Texture.SetData(bitmap);
}
public void Draw(float2 pos)
{
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) public static Sequence GetSequence(string unitName, string sequenceName)
{ {
try { return units[unitName][sequenceName]; } try { return units[unitName][sequenceName]; }
catch (KeyNotFoundException e) catch (KeyNotFoundException)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
"Unit `{0}` does not have a sequence `{1}`".F(unitName, sequenceName)); "Unit `{0}` does not have a sequence `{1}`".F(unitName, sequenceName));

View File

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

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

@@ -2,23 +2,12 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing.Imaging; using System.Drawing.Imaging;
using System.IO; using System.IO;
using System.Drawing;
namespace OpenRa.Game.Graphics namespace OpenRa.Game.Graphics
{ {
static class Util 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) public static string[] ReadAllLines(Stream s)
{ {
List<string> result = new List<string>(); List<string> result = new List<string>();
@@ -56,14 +45,18 @@ 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(o,
vertices[nv + 1] = new Vertex(KLerp(o, r.size, 1), r.FastMapTextureCoords(1), attrib); r.FastMapTextureCoords(0), attrib);
vertices[nv + 2] = new Vertex(KLerp(o, r.size, 2), r.FastMapTextureCoords(2), attrib); vertices[nv + 1] = new Vertex(new float2(o.X + size.X, o.Y),
vertices[nv + 3] = new Vertex(KLerp(o, r.size, 3), r.FastMapTextureCoords(3), attrib); 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] = (ushort)(nv);
indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1); indices[ni + 1] = indices[ni + 3] = (ushort)(nv + 1);
@@ -111,5 +104,19 @@ namespace OpenRa.Game.Graphics
bitmap.UnlockBits(bits); bitmap.UnlockBits(bits);
} }
} }
public static Color Lerp(float t, Color a, Color b)
{
return Color.FromArgb(
LerpChannel(t, a.A, b.A),
LerpChannel(t, a.R, b.R),
LerpChannel(t, a.G, b.G),
LerpChannel(t, a.B, b.B));
}
public static int LerpChannel(float t, int a, int b)
{
return (int)((1 - t) * a + t * b);
}
} }
} }

View File

@@ -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

@@ -4,9 +4,9 @@ using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
using OpenRa.FileFormats; using OpenRa.FileFormats;
using OpenRa.Game.GameRules;
using OpenRa.Game.Graphics; using OpenRa.Game.Graphics;
using OpenRa.Game.Orders; using OpenRa.Game.Orders;
using OpenRa.Game.GameRules;
namespace OpenRa.Game namespace OpenRa.Game
@@ -31,51 +31,33 @@ namespace OpenRa.Game
[DllImport("user32")] [DllImport("user32")]
static extern int ShowCursor([MarshalAs(UnmanagedType.Bool)] bool visible); static extern int ShowCursor([MarshalAs(UnmanagedType.Bool)] bool visible);
public MainWindow(Settings settings) public MainWindow(Settings settings)
{ {
FormBorderStyle = FormBorderStyle.None; FormBorderStyle = FormBorderStyle.None;
BackColor = Color.Black; BackColor = Color.Black;
StartPosition = FormStartPosition.Manual; StartPosition = FormStartPosition.Manual;
Location = Point.Empty; Location = Point.Empty;
Visible = true; Visible = true;
// Load user settings
Game.Settings = new UserSettings();
while (!File.Exists("redalert.mix")) while (!File.Exists("redalert.mix"))
{ {
var current = Directory.GetCurrentDirectory(); var current = Directory.GetCurrentDirectory();
if (Directory.GetDirectoryRoot(current) == current) if (Directory.GetDirectoryRoot(current) == current)
throw new InvalidOperationException("Unable to load MIX files."); throw new InvalidOperationException("Unable to load MIX files.");
Directory.SetCurrentDirectory(".."); Directory.SetCurrentDirectory("..");
try
{
// settings.ini should be located with the mix files
FileSystem.MountTemporary(new Folder("./"));
IniFile SettingsRules = new IniFile(FileSystem.Open("settings.ini"));
FieldLoader.Load(Game.Settings, SettingsRules.GetSection("Settings"));
FileSystem.UnmountTemporaryPackages();
}
catch (FileNotFoundException) { }
} }
LoadUserSettings(settings);
UiOverlay.ShowUnitDebug = Game.Settings.UnitDebug; UiOverlay.ShowUnitDebug = Game.Settings.UnitDebug;
UiOverlay.ShowBuildDebug = Game.Settings.BuildingDebug; UiOverlay.ShowBuildDebug = Game.Settings.BuildingDebug;
WorldRenderer.ShowUnitPaths = Game.Settings.PathDebug; WorldRenderer.ShowUnitPaths = Game.Settings.PathDebug;
Renderer.SheetSize = Game.Settings.SheetSize; Renderer.SheetSize = Game.Settings.SheetSize;
FileSystem.MountDefaultPackages(); FileSystem.MountDefaultPackages();
if (Game.Settings.UseAftermath) if (Game.Settings.UseAftermath)
{
FileSystem.MountAftermathPackages(); FileSystem.MountAftermathPackages();
}
bool windowed = !Game.Settings.Fullscreen; bool windowed = !Game.Settings.Fullscreen;
renderer = new Renderer(this, GetResolution(settings), windowed); renderer = new Renderer(this, GetResolution(settings), windowed);
@@ -89,6 +71,17 @@ namespace OpenRa.Game
Game.ResetTimer(); Game.ResetTimer();
} }
static void LoadUserSettings(Settings settings)
{
Game.Settings = new UserSettings();
var settingsFile = settings.GetValue("settings", "settings.ini");
FileSystem.MountTemporary(new Folder("./"));
if (FileSystem.Exists(settingsFile))
FieldLoader.Load(Game.Settings,
new IniFile(FileSystem.Open(settingsFile)).GetSection("Settings"));
FileSystem.UnmountTemporaryPackages();
}
internal void Run() internal void Run()
{ {
while (Created && Visible) while (Created && Visible)

View File

@@ -95,19 +95,23 @@
<Compile Include="GameRules\UserSettings.cs" /> <Compile Include="GameRules\UserSettings.cs" />
<Compile Include="GameRules\VoiceInfo.cs" /> <Compile Include="GameRules\VoiceInfo.cs" />
<Compile Include="Effects\IEffect.cs" /> <Compile Include="Effects\IEffect.cs" />
<Compile Include="Graphics\Minimap.cs" />
<Compile Include="Orders\ChronosphereSelectOrderGenerator.cs" />
<Compile Include="Orders\IOrderSource.cs" /> <Compile Include="Orders\IOrderSource.cs" />
<Compile Include="Orders\LocalOrderSource.cs" /> <Compile Include="Orders\LocalOrderSource.cs" />
<Compile Include="Effects\Missile.cs" /> <Compile Include="Effects\Missile.cs" />
<Compile Include="Orders\NetworkOrderSource.cs" /> <Compile Include="Orders\NetworkOrderSource.cs" />
<Compile Include="Orders\OrderIO.cs" /> <Compile Include="Orders\OrderIO.cs" />
<Compile Include="Orders\OrderManager.cs" /> <Compile Include="Orders\OrderManager.cs" />
<Compile Include="Orders\PowerDownOrderGenerator.cs" />
<Compile Include="Orders\RepairOrderGenerator.cs" /> <Compile Include="Orders\RepairOrderGenerator.cs" />
<Compile Include="Orders\SellOrderGenerator.cs" /> <Compile Include="Orders\SellOrderGenerator.cs" />
<Compile Include="Orders\TeleportOrderGenerator.cs" /> <Compile Include="Orders\ChronoshiftDestinationOrderGenerator.cs" />
<Compile Include="Ore.cs" /> <Compile Include="Ore.cs" />
<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" />
@@ -116,6 +120,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" />
@@ -183,6 +188,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" />
@@ -196,6 +202,11 @@
<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\ChronoshiftPaletteEffect.cs" />
<Compile Include="Traits\Chronosphere.cs" />
<Compile Include="Traits\DemoTruck.cs" />
<Compile Include="Traits\EngineerCapture.cs" /> <Compile Include="Traits\EngineerCapture.cs" />
<Compile Include="Traits\Explodes.cs" /> <Compile Include="Traits\Explodes.cs" />
<Compile Include="Traits\ChronoshiftDeploy.cs" /> <Compile Include="Traits\ChronoshiftDeploy.cs" />
@@ -207,6 +218,8 @@
<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\ProvidesRadar.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" />
@@ -291,4 +304,4 @@
<Target Name="AfterBuild"> <Target Name="AfterBuild">
</Target> </Target>
--> -->
</Project> </Project>

View File

@@ -3,14 +3,15 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Drawing; using System.Drawing;
using OpenRa.Game.Traits;
namespace OpenRa.Game.Orders namespace OpenRa.Game.Orders
{ {
class TeleportOrderGenerator : IOrderGenerator class ChronoshiftDestinationOrderGenerator : IOrderGenerator
{ {
public readonly Actor self; public readonly Actor self;
public TeleportOrderGenerator(Actor self) public ChronoshiftDestinationOrderGenerator(Actor self)
{ {
this.self = self; this.self = self;
} }
@@ -34,7 +35,8 @@ namespace OpenRa.Game.Orders
public Cursor GetCursor(int2 xy, MouseInput mi) public Cursor GetCursor(int2 xy, MouseInput mi)
{ {
return Cursor.Chronoshift; var movement = self.traits.WithInterface<IMovement>().FirstOrDefault();
return (movement.CanEnterCell(xy)) ? Cursor.Chronoshift : Cursor.MoveBlocked;
} }
} }
} }

View File

@@ -0,0 +1,57 @@
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 ChronosphereSelectOrderGenerator : 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.WithInterface<IChronoshiftable>().Any()
&& a.Info.Selectable).FirstOrDefault();
var unit = underCursor != null ? underCursor.Info as UnitInfo : null;
if (unit != null)
{
yield return new Order("ChronosphereSelect", underCursor, null, int2.Zero, null);
}
}
}
public void Tick()
{
var hasChronosphere = Game.world.Actors
.Any(a => a.Owner == Game.LocalPlayer && a.traits.Contains<Chronosphere>());
if (!hasChronosphere)
Game.controller.CancelInputMode();
}
public void Render() { }
public Cursor GetCursor(int2 xy, MouseInput mi)
{
mi.Button = MouseButton.Left;
return OrderInner(xy, mi).Any()
? Cursor.ChronoshiftSelect : Cursor.MoveBlocked;
}
}
}

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() public void Tick()
{ {
selection.RemoveAll(a => a.IsDead); selection.RemoveAll(a => !a.IsInWorld);
} }
public void Render() public void Render()
@@ -81,10 +81,11 @@ 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;
case "Harvest": return Cursor.Attack; // TODO: special harvest cursor? case "Harvest": return Cursor.AttackMove;
default: default:
return null; return null;
} }

View File

@@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Collections.Generic;
using OpenRa.Game.GameRules; using OpenRa.Game.GameRules;
using OpenRa.Game.Graphics; using OpenRa.Game.Graphics;
using OpenRa.Game.Traits; using OpenRa.Game.Traits;
@@ -17,15 +18,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 +37,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()
@@ -52,11 +51,11 @@ namespace OpenRa.Game
foreach (var a in myBuildings) foreach (var a in myBuildings)
{ {
var bi = a.Info as BuildingInfo; var p = a.traits.Get<Building>().GetPowerUsage();
if (bi.Power > 0) /* todo: is this how real-ra scales it? */ if (p > 0)
PowerProvided += (a.Health * bi.Power) / bi.Strength; PowerProvided += p;
else else
PowerDrained -= bi.Power; PowerDrained -= p;
} }
if (PowerProvided - PowerDrained < 0) 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 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

@@ -26,12 +26,9 @@ namespace OpenRa.Game.Traits
protected override void QueueAttack( Actor self, Order order ) protected override void QueueAttack( Actor self, Order order )
{ {
var bi = self.Info as BuildingInfo; var b = self.traits.Get<Building>();
if (bi != null && bi.Powered && self.Owner.GetPowerState() != PowerState.Normal) if (b != null && b.InsuffientPower())
{
if (self.Owner == Game.LocalPlayer) Sound.Play("nopowr1.aud");
return; return;
}
const int RangeTolerance = 1; /* how far inside our maximum range we should try to sit */ 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 */ /* 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.Linq;
using System.Text; using System.Text;
using OpenRa.Game.Effects; using OpenRa.Game.Effects;
using OpenRa.Game.Graphics;
namespace OpenRa.Game.Traits namespace OpenRa.Game.Traits
{ {
class Building : INotifyDamage, IOrder, ITick class Building : INotifyDamage, IOrder, ITick, IRenderModifier
{ {
readonly Actor self;
public readonly BuildingInfo unitInfo; public readonly BuildingInfo unitInfo;
bool isRepairing = false; bool isRepairing = false;
bool isPoweredDown = false;
public Building(Actor self) public Building(Actor self)
{ {
this.self = self;
unitInfo = (BuildingInfo)self.Info; unitInfo = (BuildingInfo)self.Info;
self.CenterLocation = Game.CellSize self.CenterLocation = Game.CellSize
* ((float2)self.Location + .5f * (float2)unitInfo.Dimensions); * ((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) public void Damaged(Actor self, AttackInfo e)
{ {
@@ -43,6 +89,12 @@ namespace OpenRa.Game.Traits
{ {
isRepairing = !isRepairing; isRepairing = !isRepairing;
} }
if (order.OrderString == "PowerDown")
{
isPoweredDown = !isPoweredDown;
Sound.Play((isPoweredDown) ? "bleep12.aud" : "bleep11.aud");
}
} }
int remainingTicks; 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

@@ -6,20 +6,22 @@ namespace OpenRa.Game.Traits
{ {
class ChronoshiftDeploy : IOrder, ISpeedModifier, ITick, IPips class ChronoshiftDeploy : IOrder, ISpeedModifier, ITick, IPips
{ {
public ChronoshiftDeploy(Actor self) { } // Recharge logic
int remainingChargeTime = 0; // How long until we can chronoshift again? int chargeTick = 0; // How long until we can chronoshift again?
int chargeTime = (int)(Rules.Aftermath.ChronoTankDuration * 60 * 25); // How long between shifts? int chargeLength = (int)(Rules.Aftermath.ChronoTankDuration * 60 * 25); // How long between shifts?
public ChronoshiftDeploy(Actor self) { }
public void Tick(Actor self) public void Tick(Actor self)
{ {
if (remainingChargeTime > 0) if (chargeTick > 0)
remainingChargeTime--; chargeTick--;
} }
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{ {
if( mi.Button == MouseButton.Right && xy == self.Location && remainingChargeTime <= 0 ) if (mi.Button == MouseButton.Right && xy == self.Location && chargeTick <= 0)
return new Order( "Deploy", self, null, int2.Zero, null ); return new Order("Deploy", self, null, int2.Zero, null);
return null; return null;
} }
@@ -28,7 +30,7 @@ namespace OpenRa.Game.Traits
{ {
if (order.OrderString == "Deploy") if (order.OrderString == "Deploy")
{ {
Game.controller.orderGenerator = new TeleportOrderGenerator(self); Game.controller.orderGenerator = new ChronoshiftDestinationOrderGenerator(self);
return; return;
} }
@@ -39,23 +41,26 @@ namespace OpenRa.Game.Traits
self.CancelActivity(); self.CancelActivity();
self.QueueActivity(new Activities.Teleport(order.TargetLocation)); self.QueueActivity(new Activities.Teleport(order.TargetLocation));
Sound.Play("chrotnk1.aud"); Sound.Play("chrotnk1.aud");
remainingChargeTime = chargeTime; chargeTick = chargeLength;
foreach (var a in Game.world.Actors.Where(a => a.traits.Contains<ChronoshiftPaletteEffect>()))
a.traits.Get<ChronoshiftPaletteEffect>().DoChronoshift();
} }
} }
public float GetSpeedModifier() public float GetSpeedModifier()
{ {
// ARGH! You must not do this, it will desync! // ARGH! You must not do this, it will desync!
return (Game.controller.orderGenerator is TeleportOrderGenerator) ? 0f : 1f; return (Game.controller.orderGenerator is ChronoshiftDestinationOrderGenerator) ? 0f : 1f;
} }
// 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++)
{ {
if ((1 - remainingChargeTime * 1.0f / chargeTime) * numPips < i + 1) if ((1 - chargeTick * 1.0f / chargeLength) * numPips < i + 1)
{ {
yield return PipType.Transparent; yield return PipType.Transparent;
continue; continue;

View File

@@ -0,0 +1,40 @@
using System.Drawing;
using OpenRa.Game.Graphics;
namespace OpenRa.Game.Traits
{
class ChronoshiftPaletteEffect : IPaletteModifier, ITick
{
const int chronoEffectLength = 20;
int remainingFrames;
public ChronoshiftPaletteEffect(Actor self) { }
public void DoChronoshift()
{
remainingFrames = chronoEffectLength;
}
public void Tick(Actor self)
{
if (remainingFrames > 0)
remainingFrames--;
}
public void AdjustPalette(Bitmap b)
{
if (remainingFrames == 0)
return;
var frac = (float)remainingFrames / chronoEffectLength;
for( var y = 0; y < (int)PaletteType.Chrome; y++ )
for (var x = 0; x < 256; x++)
{
var orig = b.GetPixel(x, y);
var lum = (int)(255 * orig.GetBrightness());
var desat = Color.FromArgb(orig.A, lum, lum, lum);
b.SetPixel(x, y, Graphics.Util.Lerp(frac, orig, desat));
}
}
}
}

View File

@@ -0,0 +1,78 @@
using OpenRa.Game.Traits;
using OpenRa.Game.Orders;
using System.Collections.Generic;
using System.Linq;
using System.Drawing;
namespace OpenRa.Game.Traits
{
class Chronoshiftable : IOrder, ISpeedModifier, ITick, IChronoshiftable
{
// Return-to-sender logic
int2 chronoshiftOrigin;
int chronoshiftReturnTicks = 0;
public Chronoshiftable(Actor self) { }
public void Tick(Actor self)
{
if (chronoshiftReturnTicks <= 0)
return;
if (chronoshiftReturnTicks > 0)
chronoshiftReturnTicks--;
// Return to original location
if (chronoshiftReturnTicks == 0)
{
self.CancelActivity();
// Todo: need a new Teleport method that will move to the closest available cell
self.QueueActivity(new Activities.Teleport(chronoshiftOrigin));
}
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
return null; // Chronoshift order is issued through Chrome.
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "ChronosphereSelect")
{
Game.controller.orderGenerator = new ChronoshiftDestinationOrderGenerator(self);
}
var movement = self.traits.WithInterface<IMovement>().FirstOrDefault();
if (order.OrderString == "Chronoshift" && movement.CanEnterCell(order.TargetLocation))
{
// Set up return-to-sender info
chronoshiftOrigin = self.Location;
chronoshiftReturnTicks = (int)(Rules.General.ChronoDuration * 60 * 25);
// TODO: Kill cargo if Rules.General.ChronoKillCargo says so
// Set up the teleport
Game.controller.CancelInputMode();
self.CancelActivity();
self.QueueActivity(new Activities.Teleport(order.TargetLocation));
Sound.Play("chrono2.aud");
foreach (var a in Game.world.Actors.Where(a => a.traits.Contains<ChronoshiftPaletteEffect>()))
a.traits.Get<ChronoshiftPaletteEffect>().DoChronoshift();
// Play chronosphere active anim
var chronosphere = Game.world.Actors.Where(a => a.Owner == order.Subject.Owner && a.traits.Contains<Chronosphere>()).FirstOrDefault();
if (chronosphere != null)
chronosphere.traits.Get<RenderBuilding>().PlayCustomAnim(chronosphere, "active");
}
}
public float GetSpeedModifier()
{
// ARGH! You must not do this, it will desync!
return (Game.controller.orderGenerator is ChronoshiftDestinationOrderGenerator) ? 0f : 1f;
}
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRa.Game.Traits
{
class Chronosphere
{
public Chronosphere(Actor self) { }
}
}

View File

@@ -0,0 +1,59 @@
using OpenRa.Game.Effects;
using OpenRa.Game.Traits;
using System.Collections.Generic;
using System.Linq;
using OpenRa.Game.Orders;
namespace OpenRa.Game.Traits
{
class DemoTruck : IOrder, ISpeedModifier, INotifyDamage, IChronoshiftable
{
readonly Actor self;
public DemoTruck(Actor self)
{
this.self = self;
}
// Fire primary on Chronoshift
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
return null; // Chronoshift order is issued through Chrome.
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "ChronosphereSelect")
Game.controller.orderGenerator = new ChronoshiftDestinationOrderGenerator(self);
var movement = self.traits.WithInterface<IMovement>().FirstOrDefault();
var chronosphere = Game.world.Actors.Where(a => a.Owner == order.Subject.Owner && a.traits.Contains<Chronosphere>()).FirstOrDefault();
if (order.OrderString == "Chronoshift" && movement.CanEnterCell(order.TargetLocation))
self.InflictDamage(chronosphere, self.Health, Rules.WarheadInfo["Super"]);
}
// Fire primary on death
public void Damaged(Actor self, AttackInfo e)
{
if (e.DamageState == DamageState.Dead)
Detonate(self, e.Attacker);
}
public void Detonate(Actor self, Actor detonatedBy)
{
self.InflictDamage(detonatedBy, self.Health, Rules.WarheadInfo["Super"]);
var unit = self.traits.GetOrDefault<Unit>();
var altitude = unit != null ? unit.Altitude : 0;
int2 detonateLocation = self.CenterLocation.ToInt2();
Game.world.AddFrameEndTask(
w => w.Add(new Bullet(self.Info.Primary, detonatedBy.Owner, detonatedBy,
detonateLocation, detonateLocation, altitude, altitude)));
}
public float GetSpeedModifier()
{
// ARGH! You must not do this, it will desync!
return (Game.controller.orderGenerator is ChronoshiftDestinationOrderGenerator) ? 0f : 1f;
}
}
}

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

@@ -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; 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
{ {
@@ -43,7 +43,7 @@ namespace OpenRa.Game.Traits
bool IsCrushableBy(UnitMovementType umt, Player player); bool IsCrushableBy(UnitMovementType umt, Player player);
bool IsPathableCrush(UnitMovementType umt, Player player); bool IsPathableCrush(UnitMovementType umt, Player player);
} }
interface IChronoshiftable{}
struct Renderable struct Renderable
{ {
public readonly Sprite Sprite; public readonly Sprite Sprite;

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 = tempPal; //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

@@ -7,28 +7,28 @@ QTNK
[STNK] [STNK]
Description=Stealth Tank Description=Stealth Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, Cloak Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, Cloak, Chronoshiftable
Recoil=2 Recoil=2
Voice=VehicleVoice Voice=VehicleVoice
[TTNK] [TTNK]
Description=Tesla Tank Description=Tesla Tank
Traits=Unit, Mobile, AttackBase, RenderUnitSpinner Traits=Unit, Mobile, AttackBase, RenderUnitSpinner, Chronoshiftable
Voice=VehicleVoice Voice=VehicleVoice
[CTNK] [CTNK]
Description=Chrono Tank Description=Chrono Tank
Traits=Unit, Mobile, AttackBase, RenderUnit, ChronoshiftDeploy Traits=Unit, Mobile, AttackBase, RenderUnit, ChronoshiftDeploy, Chronoshiftable
Voice=VehicleVoice Voice=VehicleVoice
[DTRK] [DTRK]
Description=Demo Truck Description=Demo Truck
Traits=Unit, Mobile, AttackBase, RenderUnit Traits=Unit, Mobile, AttackBase, RenderUnit, DemoTruck
Voice=VehicleVoice Voice=VehicleVoice
[QTNK] [QTNK]
Description=M.A.D. Tank Description=M.A.D. Tank
Traits=Unit, Mobile, RenderUnit Traits=Unit, Mobile, RenderUnit, Chronoshiftable
Voice=VehicleVoice Voice=VehicleVoice
@@ -42,7 +42,7 @@ MSUB
Description=Missile Submarine Description=Missile Submarine
WaterBound=yes WaterBound=yes
BuiltAt=spen BuiltAt=spen
Traits=Unit, Mobile, AttackBase, RenderUnit, Submarine Traits=Unit, Mobile, AttackBase, RenderUnit, Submarine, Chronoshiftable
FireDelay=2 FireDelay=2

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">
@@ -368,7 +370,7 @@
<sequence name="move" start="10" length="4" x="12" y="12" /> <sequence name="move" start="10" length="4" x="12" y="12" />
<sequence name="move-blocked" start="14" x="12" y="12" /> <sequence name="move-blocked" start="14" x="12" y="12" />
<sequence name="select" start="15" length="6" x="12" y="12" /> <sequence name="select" start="15" length="6" x="12" y="12" />
<sequence name="attack" start="21" length="8" x="12" y="12" /> <sequence name="attackmove" start="21" length="8" x="12" y="12" />
<sequence name="move-minimap" start="29" length="6" /> <sequence name="move-minimap" start="29" length="6" />
<sequence name="repair" start="35" length="24" /> <sequence name="repair" start="35" length="24" />
<sequence name="deploy" start="59" length="9" x="12" y="12" /> <sequence name="deploy" start="59" length="9" x="12" y="12" />
@@ -376,14 +378,15 @@
<sequence name="default-minimap" start="80" length="1" /> <sequence name="default-minimap" start="80" length="1" />
<sequence name="ability" start="82" length="8" /> <sequence name="ability" start="82" length="8" />
<sequence name="nuke" start="90" length="7" x="12" y="12" /> <sequence name="nuke" start="90" length="7" x="12" y="12" />
<sequence name="chrono" start="105" 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="enter" start="113" length="3" x="12" y="12" />
<sequence name="c4" start="116" length="3" x="12" y="12" /> <sequence name="c4" start="116" length="3" x="12" y="12" />
<sequence name="sell-blocked" start="119" length="1" x="12" y="12" /> <sequence name="sell-blocked" start="119" length="1" x="12" y="12" />
<sequence name="repair-blocked" start="120" length="1" /> <sequence name="repair-blocked" start="120" length="1" />
<sequence name="c4-minimap" start="121" length="3" /> <sequence name="c4-minimap" start="121" length="3" />
<sequence name="scroll-blocked" start="124" length="8" /> <sequence name="scroll-blocked" start="124" length="8" />
<sequence name="attack-minimap" start="134" length="8" /> <sequence name="attackmove-minimap" start="134" length="8" />
<sequence name="enter-minimap" start="142" length="3" /> <sequence name="enter-minimap" start="142" length="3" />
<sequence name="guard-minimap" start="146" length="1" /> <sequence name="guard-minimap" start="146" length="1" />
<sequence name="guard" start="147" length="1" x="12" y="12" /> <sequence name="guard" start="147" length="1" x="12" y="12" />
@@ -393,13 +396,19 @@
<sequence name="capture-minimap" start="167" length="3" /> <sequence name="capture-minimap" start="167" length="3" />
<sequence name="repair2" start="170" length="24" /> <sequence name="repair2" start="170" length="24" />
<sequence name="heal-minimap" start="194" length="1" /> <sequence name="heal-minimap" start="194" length="1" />
<sequence name="attack2" start="195" length="8" x="12" y="12" /> <sequence name="attack" start="195" length="8" x="12" y="12" />
<sequence name="attack2-minimap" start="203" length="8" /> <sequence name="attack-minimap" start="203" length="8" />
<sequence name="deploy-blocked" start="211" length="1" x="12" y="12" /> <sequence name="deploy-blocked" start="211" length="1" x="12" y="12" />
<sequence name="enter-blocked" start="212" length="1" /> <sequence name="enter-blocked" start="212" length="1" />
<sequence name="repair2-blocked" start="213" length="1" /> <sequence name="repair2-blocked" start="213" length="1" />
<sequence name="ability-minimap" start="214" length="8" /> <sequence name="ability-minimap" start="214" length="8" />
</cursor> </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"> <unit name="120mm">
<sequence name="idle" start="0" length="1" /> <sequence name="idle" start="0" length="1" />
</unit> </unit>
@@ -535,6 +544,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" />
@@ -571,6 +582,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" />
@@ -1007,8 +1020,8 @@
<sequence name="disabled" start="2" length="1" /> <sequence name="disabled" start="2" length="1" />
</unit> </unit>
<unit name="sell"> <unit name="sell">
<sequence name="normal" start="0" length="1" /> <sequence name="normal" start="0" length="1" />
<sequence name="pressed" start="1" length="1" /> <sequence name="pressed" start="1" length="1" />
<sequence name="disabled" start="2" length="1" /> <sequence name="disabled" start="2" length="1" />
</unit> </unit>
</sequences> </sequences>

View File

@@ -8,4 +8,6 @@ s0=Multi0,mcv,600,2841,0,Guard,None
s1=Multi2,mcv,600,12445,0,Guard,None s1=Multi2,mcv,600,12445,0,Guard,None
s2=Multi1,mcv,600,12505,0,Guard,None 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
s5=Multi1,apc,600,12510,Gaurd,None

View File

@@ -0,0 +1,4 @@
[Settings]
NetworkHost=localhost
NetworkPort=1234
UseAftermath=yes

View File

@@ -0,0 +1,3 @@
[Settings]
NetworkHost=localhost
NetworkPort=1234

View File

@@ -16,89 +16,91 @@ MNLY.AT
[V2RL] [V2RL]
Description=V2 Rocket Description=V2 Rocket
Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable 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 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 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 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 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 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 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 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 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 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 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 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 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 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
@@ -116,21 +118,21 @@ PT
Description=Submarine Description=Submarine
WaterBound=yes WaterBound=yes
BuiltAt=spen BuiltAt=spen
Traits=Unit, Mobile, RenderUnit, Submarine, AttackBase Traits=Unit, Mobile, RenderUnit, Submarine, AttackBase, Chronoshiftable
FireDelay=2 FireDelay=2
LongDesc=Submerged anti-ship unit armed with \ntorpedoes.\n Strong vs Ships\n Weak vs Everything\n Special Ability: Submerge LongDesc=Submerged anti-ship unit armed with \ntorpedoes.\n Strong vs Ships\n Weak vs Everything\n Special Ability: Submerge
[DD] [DD]
Description=Destroyer Description=Destroyer
WaterBound=yes WaterBound=yes
BuiltAt=syrd BuiltAt=syrd
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable
PrimaryOffset=0,-8,0,-3 PrimaryOffset=0,-8,0,-3
LongDesc=Fast multi-role ship. \n Strong vs Submarines, Aircraft\n Weak vs Infantry, Tanks LongDesc=Fast multi-role ship. \n Strong vs Submarines, Aircraft\n Weak vs Infantry, Tanks
[CA] [CA]
Description=Cruiser Description=Cruiser
WaterBound=yes WaterBound=yes
BuiltAt=syrd BuiltAt=syrd
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable
PrimaryOffset=0,17,0,-2 PrimaryOffset=0,17,0,-2
SecondaryOffset=0,-17,0,-2 SecondaryOffset=0,-17,0,-2
LongDesc=Very slow long-range ship. \n Strong vs Buildings\n Weak vs Ships, Submarines LongDesc=Very slow long-range ship. \n Strong vs Buildings\n Weak vs Ships, Submarines
@@ -138,13 +140,14 @@ 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
BuiltAt=syrd BuiltAt=syrd
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Chronoshiftable
PrimaryOffset=0,-6,0,-1 PrimaryOffset=0,-6,0,-1
LongDesc=Light scout & support ship. \n Strong vs Ships, Submarines\n Weak vs Aircraft LongDesc=Light scout & support ship. \n Strong vs Ships, Submarines\n Weak vs Aircraft
@@ -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
@@ -289,7 +293,7 @@ SelectionPriority=3
LongDesc=Makes a group of units invulnerable for a \nshort time.\n Special Ability: Invulnerability LongDesc=Makes a group of units invulnerable for a \nshort time.\n Special Ability: Invulnerability
[PDOX] [PDOX]
Description=Chronosphere Description=Chronosphere
Traits=Building, RenderBuilding Traits=Building, RenderBuilding, Chronosphere
Dimensions=2,2 Dimensions=2,2
Footprint=xx xx Footprint=xx xx
SelectionPriority=3 SelectionPriority=3
@@ -407,7 +411,7 @@ SpawnOffset=0,-4
LongDesc=Produces and reloads helicopters LongDesc=Produces and reloads helicopters
[DOME] [DOME]
Description=Radar Dome Description=Radar Dome
Traits=Building, RenderBuilding Traits=Building, RenderBuilding, ProvidesRadar
Dimensions=2,2 Dimensions=2,2
Footprint=xx xx Footprint=xx xx
SelectionPriority=3 SelectionPriority=3
@@ -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