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

This commit is contained in:
Chris Forbes
2009-11-12 00:26:00 +13:00
29 changed files with 290 additions and 165 deletions

View File

@@ -37,4 +37,12 @@ namespace OpenRa
public Tuple(A a, B b, C c, D d) { this.a = a; this.b = b; this.c = c; this.d = d; } public Tuple(A a, B b, C c, D d) { this.a = a; this.b = b; this.c = c; this.d = d; }
} }
public static class Tuple
{
public static Tuple<A, B, C> New<A, B, C>(A a, B b, C c)
{
return new Tuple<A, B, C>(a, b, c);
}
}
} }

View File

@@ -8,7 +8,9 @@ namespace OpenRa.FileFormats
{ {
public class PaletteRemap public class PaletteRemap
{ {
int offset;
List<Color> remapColors = new List<Color>(); List<Color> remapColors = new List<Color>();
Color shadowColor;
public PaletteRemap(Stream s) public PaletteRemap(Stream s)
{ {
@@ -23,14 +25,26 @@ namespace OpenRa.FileFormats
remapColors.Add(Color.FromArgb(r, g, b)); remapColors.Add(Color.FromArgb(r, g, b));
} }
} }
offset = 80;
}
public PaletteRemap( Color shadowColor )
{
this.shadowColor = shadowColor;
} }
public Color GetRemappedColor(Color original, int index) public Color GetRemappedColor(Color original, int index)
{ {
if (index < 80 || index >= 96) if (remapColors.Count > 0)
{
if (index < offset || index >= offset + remapColors.Count)
return original; return original;
return remapColors[index - 80]; return remapColors[index - offset];
}
return original.A > 0 ? shadowColor : original;
} }
} }
} }

View File

@@ -59,9 +59,9 @@ namespace OpenRa.Game
} }
public float2 CenterLocation; public float2 CenterLocation;
public float2 SelectedSize { get { return Render().First().First.size; } } public float2 SelectedSize { get { return Render().First().a.size; } }
public IEnumerable<Pair<Sprite, float2>> Render() public IEnumerable<Tuple<Sprite, float2, int>> Render()
{ {
return traits.WithInterface<Traits.IRender>().SelectMany( x => x.Render( this ) ); return traits.WithInterface<Traits.IRender>().SelectMany( x => x.Render( this ) );
} }

View File

@@ -11,7 +11,7 @@ namespace OpenRa.Game
interface IEffect interface IEffect
{ {
void Tick(); void Tick();
IEnumerable<Pair<Sprite, float2>> Render(); IEnumerable<Tuple<Sprite, float2, int>> Render();
Player Owner { get; } Player Owner { get; }
} }
@@ -96,14 +96,30 @@ namespace OpenRa.Game
} }
} }
public IEnumerable<Pair<Sprite, float2>> Render() const float height = .1f;
public IEnumerable<Tuple<Sprite, float2, int>> Render()
{ {
if (anim != null) if (anim != null)
yield return Pair.New(anim.Image, {
float2.Lerp( var pos = float2.Lerp(
Src.ToFloat2(), Src.ToFloat2(),
VisualDest.ToFloat2(), VisualDest.ToFloat2(),
(float)t / TotalTime()) - 0.5f * anim.Image.size); (float)t / TotalTime()) - 0.5f * anim.Image.size;
if (Projectile.High || Projectile.Arcing)
{
if (Projectile.Shadow)
yield return Tuple.New(anim.Image, pos, 8); /* todo: shadow pal */
var at = (float)t / TotalTime();
var highPos = pos - new float2(0, (VisualDest - Src).Length * height * 4 * at * (1 - at));
yield return Tuple.New(anim.Image, highPos, Owner.Palette);
}
else
yield return Tuple.New(anim.Image, pos, Owner.Palette);
}
} }
float GetMaximumSpread() float GetMaximumSpread()

View File

@@ -24,9 +24,9 @@ namespace OpenRa.Game
public void Tick() { anim.Tick(); } public void Tick() { anim.Tick(); }
public IEnumerable<Pair<Sprite, float2>> Render() public IEnumerable<Tuple<Sprite, float2, int>> Render()
{ {
yield return Pair.New(anim.Image, pos.ToFloat2() - 0.5f * anim.Image.size); yield return Tuple.New(anim.Image, pos.ToFloat2() - 0.5f * anim.Image.size, 0);
} }
public Player Owner { get { return null; } } public Player Owner { get { return null; } }

View File

@@ -46,7 +46,8 @@ namespace OpenRa.Game
Rules.LoadRules(mapName); Rules.LoadRules(mapName);
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
players.Add(i, new Player(i, i, string.Format("Multi{0}", i), Race.Allies)); players.Add(i, new Player(i, i, string.Format("Multi{0}", i),
Race.Allies));
localPlayerIndex = localPlayer; localPlayerIndex = localPlayer;
@@ -84,8 +85,7 @@ namespace OpenRa.Game
PlaySound("intro.aud", false); PlaySound("intro.aud", false);
skipMakeAnims = false; skipMakeAnims = false;
PerfHistory.items["render"].hasNormalTick = false;
sw = new Stopwatch();
} }
static void LoadMapBuildings( IniFile mapfile ) static void LoadMapBuildings( IniFile mapfile )
@@ -143,11 +143,6 @@ namespace OpenRa.Game
const int oreFrequency = 30; const int oreFrequency = 30;
static int oreTicks = oreFrequency; static int oreTicks = oreFrequency;
public static int RenderFrame = 0; public static int RenderFrame = 0;
public static double RenderTime = 0.0;
public static double TickTime = 0.0;
public static double OreTime = 0.0;
public static Stopwatch sw;
public static void Tick() public static void Tick()
{ {
@@ -157,7 +152,6 @@ namespace OpenRa.Game
{ {
using (new PerfSample("tick_time")) using (new PerfSample("tick_time"))
{ {
sw.Reset();
lastTime += timestep; lastTime += timestep;
if (orderManager.Tick()) if (orderManager.Tick())
@@ -166,29 +160,27 @@ namespace OpenRa.Game
controller.orderGenerator.Tick(); controller.orderGenerator.Tick();
if (--oreTicks == 0) if (--oreTicks == 0)
{ using( new PerfSample("ore"))
var oresw = new Stopwatch();
map.GrowOre(SharedRandom); map.GrowOre(SharedRandom);
OreTime = oresw.ElapsedTime();
oreTicks = oreFrequency;
}
world.Tick(); world.Tick();
UnitInfluence.Tick(); UnitInfluence.Tick();
foreach (var player in players.Values) foreach (var player in players.Values)
player.Tick(); player.Tick();
} }
TickTime = sw.ElapsedTime();
} }
PerfHistory.Tick(); PerfHistory.Tick();
} }
sw.Reset(); using (new PerfSample("render"))
{
++RenderFrame; ++RenderFrame;
viewport.cursor = controller.ChooseCursor(); viewport.cursor = controller.ChooseCursor();
viewport.DrawRegions(); viewport.DrawRegions();
RenderTime = sw.ElapsedTime(); }
PerfHistory.items["render"].Tick();
} }
public static bool IsCellBuildable(int2 a, UnitMovementType umt) public static bool IsCellBuildable(int2 a, UnitMovementType umt)

View File

@@ -10,7 +10,7 @@ namespace OpenRa.Game.Graphics
{ {
class HardwarePalette : Sheet class HardwarePalette : Sheet
{ {
const int maxEntries = 8; const int maxEntries = 16;
int allocated = 0; int allocated = 0;
public HardwarePalette(Renderer renderer, Map map, int rotate) public HardwarePalette(Renderer renderer, Map map, int rotate)
@@ -22,6 +22,8 @@ namespace OpenRa.Game.Graphics
foreach (string remap in new string[] { "blue", "red", "orange", "teal", "salmon", "green", "gray" }) foreach (string remap in new string[] { "blue", "red", "orange", "teal", "salmon", "green", "gray" })
AddPalette(new Palette(pal, new PaletteRemap(FileSystem.Open(remap + ".rem")))); AddPalette(new Palette(pal, new PaletteRemap(FileSystem.Open(remap + ".rem"))));
AddPalette(new Palette(pal, new PaletteRemap(Color.FromArgb(140, 0, 0, 0))));
using (var bitmapCopy = new Bitmap(bitmap)) using (var bitmapCopy = new Bitmap(bitmap))
for (int j = 0; j < maxEntries; j++) for (int j = 0; j < maxEntries; j++)
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)

View File

@@ -58,7 +58,7 @@ namespace OpenRa.Game.Graphics
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 attrib = new float2(palette / 8.0f, channelSelect[(int)r.channel]); float2 attrib = new float2(palette / 16.0f, channelSelect[(int)r.channel]);
vertices[nv] = new Vertex(KLerp(o, r.size, 0), r.FastMapTextureCoords(0), attrib); vertices[nv] = new Vertex(KLerp(o, r.size, 0), r.FastMapTextureCoords(0), attrib);
vertices[nv + 1] = new Vertex(KLerp(o, r.size, 1), r.FastMapTextureCoords(1), attrib); vertices[nv + 1] = new Vertex(KLerp(o, r.size, 1), r.FastMapTextureCoords(1), attrib);

View File

@@ -36,22 +36,21 @@ namespace OpenRa.Game.Graphics
uiOverlay = new UiOverlay(spriteRenderer); uiOverlay = new UiOverlay(spriteRenderer);
} }
void DrawSpriteList(Player owner, RectangleF rect, void DrawSpriteList(RectangleF rect,
IEnumerable<Pair<Sprite, float2>> images) IEnumerable<Tuple<Sprite, float2, int>> images)
{ {
foreach (var image in images) foreach (var image in images)
{ {
var loc = image.Second; var loc = image.b;
if (loc.X > rect.Right || loc.X < rect.Left if (loc.X > rect.Right || loc.X < rect.Left
- image.First.bounds.Width) - image.a.bounds.Width)
continue; continue;
if (loc.Y > rect.Bottom || loc.Y < rect.Top if (loc.Y > rect.Bottom || loc.Y < rect.Top
- image.First.bounds.Height) - image.a.bounds.Height)
continue; continue;
spriteRenderer.DrawSprite(image.First, loc, spriteRenderer.DrawSprite(image.a, loc, image.c);
(owner != null) ? owner.Palette : 0);
} }
} }
@@ -63,15 +62,15 @@ namespace OpenRa.Game.Graphics
region.Size.ToSizeF()); region.Size.ToSizeF());
foreach (Actor a in Game.world.Actors.OrderBy(u => u.CenterLocation.Y)) foreach (Actor a in Game.world.Actors.OrderBy(u => u.CenterLocation.Y))
DrawSpriteList(a.Owner, rect, a.Render()); DrawSpriteList(rect, a.Render());
foreach (var a in Game.world.Actors foreach (var a in Game.world.Actors
.Where(u => u.traits.Contains<Traits.RenderWarFactory>()) .Where(u => u.traits.Contains<Traits.RenderWarFactory>())
.Select(u => u.traits.Get<Traits.RenderWarFactory>())) .Select(u => u.traits.Get<Traits.RenderWarFactory>()))
DrawSpriteList(a.self.Owner, rect, a.RenderRoof(a.self)); /* RUDE HACK */ DrawSpriteList(rect, a.RenderRoof(a.self)); /* RUDE HACK */
foreach (IEffect e in Game.world.Effects) foreach (IEffect e in Game.world.Effects)
DrawSpriteList(e.Owner, rect, e.Render()); DrawSpriteList(rect, e.Render());
uiOverlay.Draw(); uiOverlay.Draw();
@@ -100,14 +99,13 @@ namespace OpenRa.Game.Graphics
lineRenderer.Flush(); lineRenderer.Flush();
renderer.DrawText(string.Format("RenderFrame {0} ({2:F1} ms)\nTick {1} ({3:F1} ms)\nOre ({4:F1} ms)\n$ {5}\nPower {7}\nTiles Expanded {6:F0}", renderer.DrawText(string.Format("RenderFrame {0} ({2:F1} ms)\nTick {1} ({3:F1} ms)\n$ {4}\nPower {5}",
Game.RenderFrame, Game.orderManager.FrameNumber, Game.RenderFrame,
Game.RenderTime * 1000, Game.orderManager.FrameNumber,
Game.TickTime * 1000, PerfHistory.items["render"].LastValue,
Game.OreTime * 1000, PerfHistory.items["tick_time"].LastValue,
Game.LocalPlayer.Cash, Game.LocalPlayer.Cash,
PerfHistory.items[ "nodes_expanded" ].LastValue, Game.LocalPlayer.GetTotalPower()
Game.LocalPlayer.Power
), new int2(5, 5), Color.White); ), new int2(5, 5), Color.White);
PerfHistory.Render(renderer, lineRenderer); PerfHistory.Render(renderer, lineRenderer);

View File

@@ -61,10 +61,13 @@ namespace OpenRa.Game
Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 5, 5 ), Game.players[ 1 ]) ); Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 5, 5 ), Game.players[ 1 ]) );
Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 7, 5 ), Game.players[ 2 ] ) ); Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 7, 5 ), Game.players[ 2 ] ) );
Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 9, 5 ), Game.players[ 0 ] ) ); Game.world.Add( new Actor( "mcv", Game.map.Offset + new int2( 9, 5 ), Game.players[ 0 ] ) );
Game.world.Add( new Actor( "jeep", Game.map.Offset + new int2( 9, 15 ), Game.players[ 1 ] ) ); Game.world.Add( new Actor( "jeep", Game.map.Offset + new int2( 9, 14 ), Game.players[ 1 ] ) );
Game.world.Add( new Actor( "3tnk", Game.map.Offset + new int2( 12, 7 ), Game.players[ 1 ] ) ); Game.world.Add( new Actor( "3tnk", Game.map.Offset + new int2( 12, 7 ), Game.players[ 1 ] ) );
Game.world.Add(new Actor("apc", Game.map.Offset + new int2(13, 7), Game.players[1]));
Game.world.Add(new Actor("ca", Game.map.Offset + new int2(40, 7), Game.players[1])); Game.world.Add(new Actor("ca", Game.map.Offset + new int2(40, 7), Game.players[1]));
Game.world.Add(new Actor("e1", Game.map.Offset + new int2(9, 13), Game.players[1])); Game.world.Add(new Actor("e1", Game.map.Offset + new int2(9, 13), Game.players[1]));
Game.world.Add(new Actor("arty", Game.map.Offset + new int2(10, 13), Game.players[1]));
Game.world.Add(new Actor("heli", Game.map.Offset + new int2(11, 12), Game.players[1]));
renderer.BuildPalette(Game.map); renderer.BuildPalette(Game.map);
sidebar = new Sidebar(renderer, Game.LocalPlayer); sidebar = new Sidebar(renderer, Game.LocalPlayer);

View File

@@ -153,6 +153,7 @@
<Compile Include="Traits\RenderBuildingWarFactory.cs" /> <Compile Include="Traits\RenderBuildingWarFactory.cs" />
<Compile Include="Traits\RenderSimple.cs" /> <Compile Include="Traits\RenderSimple.cs" />
<Compile Include="Traits\RenderUnit.cs" /> <Compile Include="Traits\RenderUnit.cs" />
<Compile Include="Traits\RenderUnitMuzzleFlash.cs" />
<Compile Include="Traits\RenderUnitRotor.cs" /> <Compile Include="Traits\RenderUnitRotor.cs" />
<Compile Include="Traits\RenderUnitTurreted.cs" /> <Compile Include="Traits\RenderUnitTurreted.cs" />
<Compile Include="Traits\TraitsInterfaces.cs" /> <Compile Include="Traits\TraitsInterfaces.cs" />

View File

@@ -11,7 +11,8 @@ namespace OpenRa.Game
public Race Race; public Race Race;
public readonly int Index; public readonly int Index;
public int Cash; public int Cash;
public int Power; int powerProvided;
int powerDrained;
public Player( int index, int palette, string playerName, Race race ) public Player( int index, int palette, string playerName, Race race )
{ {
@@ -20,7 +21,20 @@ namespace OpenRa.Game
this.PlayerName = playerName; this.PlayerName = playerName;
this.Race = race; this.Race = race;
this.Cash = 10000; this.Cash = 10000;
this.Power = 0; this.powerProvided = this.powerDrained = 0;
}
public void ChangePower(int dPower)
{
if (dPower > 0)
powerProvided += dPower;
if (dPower < 0)
powerDrained -= dPower;
}
public int GetTotalPower()
{
return powerProvided - powerDrained;
} }
public float GetSiloFullness() public float GetSiloFullness()

View File

@@ -29,6 +29,7 @@ namespace OpenRa.Game.Support
public static void Tick() public static void Tick()
{ {
foreach (var item in items.Values) foreach (var item in items.Values)
if (item.hasNormalTick)
item.Tick(); item.Tick();
} }
@@ -65,6 +66,7 @@ namespace OpenRa.Game.Support
public double[] samples = new double[100]; public double[] samples = new double[100];
public double val = 0.0; public double val = 0.0;
int head = 1, tail = 0; int head = 1, tail = 0;
public bool hasNormalTick = true;
public PerfItem(string name, Color c) public PerfItem(string name, Color c)
{ {

View File

@@ -13,6 +13,8 @@ namespace OpenRa.Game.Traits
protected int primaryFireDelay = 0; protected int primaryFireDelay = 0;
protected int secondaryFireDelay = 0; protected int secondaryFireDelay = 0;
public float primaryRecoil = 0.0f, secondaryRecoil = 0.0f;
public AttackBase(Actor self) { } public AttackBase(Actor self) { }
protected bool CanAttack( Actor self ) protected bool CanAttack( Actor self )
@@ -25,6 +27,9 @@ namespace OpenRa.Game.Traits
if (primaryFireDelay > 0) --primaryFireDelay; if (primaryFireDelay > 0) --primaryFireDelay;
if (secondaryFireDelay > 0) --secondaryFireDelay; if (secondaryFireDelay > 0) --secondaryFireDelay;
primaryRecoil = Math.Max(0f, primaryRecoil - .2f);
secondaryRecoil = Math.Max(0f, secondaryRecoil - .2f);
if (target != null && target.IsDead) target = null; /* he's dead, jim. */ if (target != null && target.IsDead) target = null; /* he's dead, jim. */
} }
@@ -36,20 +41,15 @@ namespace OpenRa.Game.Traits
self.unitInfo.PrimaryOffset ) ) self.unitInfo.PrimaryOffset ) )
{ {
secondaryFireDelay = Math.Max( 4, secondaryFireDelay ); secondaryFireDelay = Math.Max( 4, secondaryFireDelay );
if (rut != null) rut.primaryRecoil = 1; primaryRecoil = 1;
return; return;
} }
if (self.unitInfo.Secondary != null && CheckFire(self, self.unitInfo.Secondary, ref secondaryFireDelay, if (self.unitInfo.Secondary != null && CheckFire(self, self.unitInfo.Secondary, ref secondaryFireDelay,
self.unitInfo.SecondaryOffset ?? self.unitInfo.PrimaryOffset)) self.unitInfo.SecondaryOffset ?? self.unitInfo.PrimaryOffset))
{ {
if (rut != null) if (self.unitInfo.SecondaryOffset != null) secondaryRecoil = 1;
{ else primaryRecoil = 1;
if (self.unitInfo.SecondaryOffset != null)
rut.secondaryRecoil = 1;
else
rut.primaryRecoil = 1;
}
return; return;
} }
} }

View File

@@ -26,7 +26,7 @@ namespace OpenRa.Game.Traits
UnitInfo.BuildingInfo bi = self.unitInfo as UnitInfo.BuildingInfo; UnitInfo.BuildingInfo bi = self.unitInfo as UnitInfo.BuildingInfo;
if (bi == null) return; if (bi == null) return;
self.Owner.Power += bi.Power; self.Owner.ChangePower(bi.Power);
} }
} }
} }

View File

@@ -35,11 +35,11 @@ namespace OpenRa.Game.Traits
self.CenterLocation.ToInt2() + elementOffsets[elements.Count][i], self); self.CenterLocation.ToInt2() + elementOffsets[elements.Count][i], self);
} }
public IEnumerable<Pair<Sprite, float2>> Render(Actor self) public IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{ {
return elements.Select( return elements.Select(
e => Util.Centered(e.anim.Image, e.location)) e => Util.Centered(self, e.anim.Image, e.location))
.OrderBy( a => a.Second.Y ); /* important to y-order elements of a squad! */ .OrderBy( a => a.b.Y ); /* important to y-order elements of a squad! */
} }
} }

View File

@@ -21,11 +21,11 @@ namespace OpenRa.Game.Traits
anim.PlayRepeating("idle"); anim.PlayRepeating("idle");
} }
public IEnumerable<Pair<Sprite, float2>> Render(Actor self) public IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{ {
var uog = Game.controller.orderGenerator as UnitOrderGenerator; var uog = Game.controller.orderGenerator as UnitOrderGenerator;
if (uog != null && self.Owner == Game.LocalPlayer && uog.selection.Contains(self)) if (uog != null && self.Owner == Game.LocalPlayer && uog.selection.Contains(self))
yield return Util.Centered( yield return Util.Centered( self,
anim.Image, Game.CellSize * (new float2(.5f, .5f) + rallyPoint.ToFloat2())); anim.Image, Game.CellSize * (new float2(.5f, .5f) + rallyPoint.ToFloat2()));
} }

View File

@@ -64,9 +64,9 @@ namespace OpenRa.Game.Traits
} }
} }
public override IEnumerable<Pair<Sprite, float2>> Render(Actor self) public override IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{ {
yield return Pair.New(anim.Image, 24f * (float2)self.Location); yield return Tuple.New(anim.Image, 24f * (float2)self.Location, self.Owner.Palette);
} }
public virtual void Damaged(Actor self, DamageState state) public virtual void Damaged(Actor self, DamageState state)

View File

@@ -29,10 +29,11 @@ namespace OpenRa.Game.Traits
}, self); }, self);
} }
public IEnumerable<Pair<Sprite, float2>> RenderRoof(Actor self) public IEnumerable<Tuple<Sprite, float2, int>> RenderRoof(Actor self)
{ {
if (doneBuilding) if (doneBuilding)
yield return Pair.New(roof.Image, 24f * (float2)self.Location); yield return Tuple.New(roof.Image,
24f * (float2)self.Location, self.Owner.Palette);
} }
public override void Tick(Actor self) public override void Tick(Actor self)

View File

@@ -16,7 +16,7 @@ namespace OpenRa.Game.Traits
anim = new Animation(self.unitInfo.Image ?? self.unitInfo.Name); anim = new Animation(self.unitInfo.Image ?? self.unitInfo.Name);
} }
public abstract IEnumerable<Pair<Sprite, float2>> Render(Actor self); public abstract IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self);
public virtual void Tick(Actor self) public virtual void Tick(Actor self)
{ {

View File

@@ -29,11 +29,11 @@ namespace OpenRa.Game.Traits
anim.PlayThen(newAnim, () => { PlayFacingAnim(self); if (after != null) after(); }); anim.PlayThen(newAnim, () => { PlayFacingAnim(self); if (after != null) after(); });
} }
public override IEnumerable<Pair<Sprite, float2>> Render(Actor self) public override IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{ {
yield return Util.Centered(anim.Image, self.CenterLocation); yield return Util.Centered(self, anim.Image, self.CenterLocation);
if (isSmoking) if (isSmoking)
yield return Util.Centered(smoke.Image, self.CenterLocation); yield return Util.Centered(self, smoke.Image, self.CenterLocation);
} }
bool isSmoking; bool isSmoking;

View File

@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRa.Game.Graphics;
namespace OpenRa.Game.Traits
{
class RenderUnitMuzzleFlash : RenderUnit
{
Animation muzzleFlash;
public RenderUnitMuzzleFlash(Actor self)
: base(self)
{
if (!self.unitInfo.MuzzleFlash) throw new InvalidOperationException("wtf??");
muzzleFlash = new Animation(self.unitInfo.Name);
muzzleFlash.PlayFetchIndex("muzzle",
() =>
{
var attack = self.traits.WithInterface<AttackBase>().First();
var mobile = self.traits.WithInterface<Mobile>().First();
return (Util.QuantizeFacing(
mobile.facing, 8)) * 6 + (int)(attack.primaryRecoil * 5.9f);
});
}
public override void Tick(Actor self)
{
base.Tick(self);
muzzleFlash.Tick();
}
public override IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{
var attack = self.traits.WithInterface<AttackBase>().First();
if (attack.primaryRecoil > 0)
return base.Render(self).Concat(new[] {Util.Centered(self,
muzzleFlash.Image, self.CenterLocation + new float2(
self.unitInfo.PrimaryOffset.ElementAtOrDefault(2),
self.unitInfo.PrimaryOffset.ElementAtOrDefault(3)))});
else
return base.Render(self);
}
}
}

View File

@@ -23,18 +23,31 @@ namespace OpenRa.Game.Traits
} }
} }
public override IEnumerable<Pair<Sprite, float2>> Render(Actor self) public override IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{ {
var mobile = self.traits.Get<Mobile>(); var mobile = self.traits.Get<Mobile>();
yield return Util.Centered(anim.Image, self.CenterLocation); yield return Util.CenteredShadow(self, anim.Image, self.CenterLocation);
yield return Util.Centered(rotorAnim.Image, self.CenterLocation yield return Util.CenteredShadow(self, rotorAnim.Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, 0)); + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, 0));
if (self.unitInfo.SecondaryOffset != null) if (self.unitInfo.SecondaryOffset != null)
yield return Util.Centered((secondRotorAnim ?? rotorAnim).Image, self.CenterLocation yield return Util.CenteredShadow(self, (secondRotorAnim ?? rotorAnim).Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, 0));
var p = self.CenterLocation - new float2( 0, altitude );
yield return Util.Centered(self, anim.Image, p);
yield return Util.Centered(self, rotorAnim.Image, p
+ Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, 0));
if (self.unitInfo.SecondaryOffset != null)
yield return Util.Centered(self, (secondRotorAnim ?? rotorAnim).Image, p
+ Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, 0)); + Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, 0));
} }
int altitude = 0;
const int climbRate = 1;
const int cruiseAltitude = 20;
public override void Tick(Actor self) public override void Tick(Actor self)
{ {
base.Tick(self); base.Tick(self);
@@ -44,12 +57,18 @@ namespace OpenRa.Game.Traits
var mobile = self.traits.Get<Mobile>(); var mobile = self.traits.Get<Mobile>();
var isFlying = mobile.HasActivity; var isFlying = mobile.HasActivity;
if (isFlying ^ (rotorAnim.CurrentSequence.Name != "rotor"))
if (isFlying && altitude < cruiseAltitude)
altitude = Math.Min(altitude + climbRate, cruiseAltitude);
else if (!isFlying && altitude > 0)
altitude = Math.Max(altitude - climbRate, 0);
if ((altitude >0) ^ (rotorAnim.CurrentSequence.Name != "rotor"))
return; return;
rotorAnim.PlayRepeatingPreservingPosition(isFlying ? "rotor" : "slow-rotor"); rotorAnim.PlayRepeatingPreservingPosition((altitude>0) ? "rotor" : "slow-rotor");
if (secondRotorAnim != null) if (secondRotorAnim != null)
secondRotorAnim.PlayRepeatingPreservingPosition(isFlying ? "rotor2" : "slow-rotor2"); secondRotorAnim.PlayRepeatingPreservingPosition((altitude>0) ? "rotor2" : "slow-rotor2");
} }
} }
} }

View File

@@ -11,7 +11,6 @@ namespace OpenRa.Game.Traits
{ {
public Animation turretAnim; public Animation turretAnim;
public Animation muzzleFlash; public Animation muzzleFlash;
public float primaryRecoil = 0.0f, secondaryRecoil = 0.0f;
public RenderUnitTurreted(Actor self) public RenderUnitTurreted(Actor self)
: base(self) : base(self)
@@ -21,9 +20,11 @@ namespace OpenRa.Game.Traits
{ {
if (self.unitInfo.MuzzleFlash) if (self.unitInfo.MuzzleFlash)
{ {
var attack = self.traits.WithInterface<AttackBase>().First();
muzzleFlash = new Animation(self.unitInfo.Name); muzzleFlash = new Animation(self.unitInfo.Name);
muzzleFlash.PlayFetchIndex("muzzle", muzzleFlash.PlayFetchIndex("muzzle",
() => (Util.QuantizeFacing(self.traits.Get<Turreted>().turretFacing,8)) * 6 + (int)(primaryRecoil * 6)); () => (Util.QuantizeFacing(self.traits.Get<Turreted>().turretFacing,8)) * 6 + (int)(attack.primaryRecoil * 5.9f));
/* hack: recoil can be 1.0f, but don't overflow into next anim */
} }
turretAnim.PlayFetchIndex("turret", turretAnim.PlayFetchIndex("turret",
@@ -33,28 +34,27 @@ namespace OpenRa.Game.Traits
turretAnim.PlayRepeating("turret"); /* not really a turret; it's a spinner */ turretAnim.PlayRepeating("turret"); /* not really a turret; it's a spinner */
} }
public override IEnumerable<Pair<Sprite, float2>> Render(Actor self) public override IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{ {
var mobile = self.traits.Get<Mobile>(); var mobile = self.traits.Get<Mobile>();
var attack = self.traits.WithInterface<AttackBase>().FirstOrDefault();
yield return Util.Centered(anim.Image, self.CenterLocation); yield return Util.Centered(self, anim.Image, self.CenterLocation);
yield return Util.Centered(turretAnim.Image, self.CenterLocation yield return Util.Centered(self, turretAnim.Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, primaryRecoil)); + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, attack.primaryRecoil));
if (self.unitInfo.SecondaryOffset != null) if (self.unitInfo.SecondaryOffset != null)
yield return Util.Centered(turretAnim.Image, self.CenterLocation yield return Util.Centered(self, turretAnim.Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, secondaryRecoil)); + Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, attack.secondaryRecoil));
if (muzzleFlash != null && primaryRecoil > 0) if (muzzleFlash != null && attack.primaryRecoil > 0)
yield return Util.Centered(muzzleFlash.Image, self.CenterLocation yield return Util.Centered(self, muzzleFlash.Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, primaryRecoil)); + Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, attack.primaryRecoil));
} }
public override void Tick(Actor self) public override void Tick(Actor self)
{ {
base.Tick(self); base.Tick(self);
turretAnim.Tick(); turretAnim.Tick();
primaryRecoil = Math.Max(0f, primaryRecoil - .2f);
secondaryRecoil = Math.Max(0f, secondaryRecoil - .2f);
if (muzzleFlash != null) if (muzzleFlash != null)
muzzleFlash.Tick(); muzzleFlash.Tick();
} }

View File

@@ -10,7 +10,7 @@ namespace OpenRa.Game.Traits
enum DamageState { Normal, Half, Dead }; enum DamageState { Normal, Half, Dead };
interface ITick { void Tick(Actor self); } interface ITick { void Tick(Actor self); }
interface IRender { IEnumerable<Pair<Sprite, float2>> Render(Actor self); } interface IRender { IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self); }
interface IOrder { Order Order(Actor self, int2 xy, bool lmb, Actor underCursor); } interface IOrder { Order Order(Actor self, int2 xy, bool lmb, Actor underCursor); }
interface INotifyDamage { void Damaged(Actor self, DamageState ds); } interface INotifyDamage { void Damaged(Actor self, DamageState ds); }
interface INotifyDamageEx : INotifyDamage { void Damaged(Actor self, int damage); } interface INotifyDamageEx : INotifyDamage { void Damaged(Actor self, int damage); }

View File

@@ -16,9 +16,9 @@ namespace OpenRa.Game.Traits
Image = treeImage; Image = treeImage;
} }
public IEnumerable<Pair<Sprite, float2>> Render(Actor self) public IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{ {
yield return Pair.New(Image, Game.CellSize * (float2)self.Location); yield return Tuple.New(Image, Game.CellSize * (float2)self.Location, 0);
} }
} }
} }

View File

@@ -81,7 +81,7 @@ namespace OpenRa.Game.Traits
if (rut == null) return float2.Zero; if (rut == null) return float2.Zero;
var facing = self.traits.Get<Turreted>().turretFacing; var facing = self.traits.Get<Turreted>().turretFacing;
var quantizedFacing = facing - facing % rut.turretAnim.CurrentSequence.Length; var quantizedFacing = QuantizeFacing(facing, rut.turretAnim.CurrentSequence.Length) * (256 / rut.turretAnim.CurrentSequence.Length);
return RotateVectorByFacing(new float2(0, recoil * self.unitInfo.Recoil), quantizedFacing, .7f); return RotateVectorByFacing(new float2(0, recoil * self.unitInfo.Recoil), quantizedFacing, .7f);
} }
@@ -92,17 +92,22 @@ namespace OpenRa.Game.Traits
if (ru == null) return int2.Zero; /* things that don't have a rotating base don't need the turrets repositioned */ if (ru == null) return int2.Zero; /* things that don't have a rotating base don't need the turrets repositioned */
var bodyFacing = self.traits.Get<Mobile>().facing; var bodyFacing = self.traits.Get<Mobile>().facing;
var quantizedFacing = bodyFacing - bodyFacing % ru.anim.CurrentSequence.Length; var quantizedFacing = QuantizeFacing(bodyFacing, ru.anim.CurrentSequence.Length) * (256 / ru.anim.CurrentSequence.Length);
return (RotateVectorByFacing(new float2(offset[0], offset[1]), quantizedFacing, .7f) + GetRecoil(self, recoil)) return (RotateVectorByFacing(new float2(offset[0], offset[1]), quantizedFacing, .7f) + GetRecoil(self, recoil))
+ new float2(offset.ElementAtOrDefault(2), offset.ElementAtOrDefault(3)); + new float2(offset.ElementAtOrDefault(2), offset.ElementAtOrDefault(3));
} }
public static Pair<Sprite, float2> Centered(Sprite s, float2 location) public static Tuple<Sprite, float2, int> Centered(Actor self, Sprite s, float2 location)
{ {
var loc = location - 0.5f * s.size; var loc = location - 0.5f * s.size;
return Pair.New(s, loc.Round()); return Tuple.New(s, loc.Round(), self.Owner.Palette);
} }
public static Tuple<Sprite, float2, int> CenteredShadow(Actor self, Sprite s, float2 location)
{
var loc = location - 0.5f * s.size;
return Tuple.New(s, loc.Round(), 8);
}
} }
} }

View File

@@ -275,6 +275,7 @@
<!-- apc --> <!-- apc -->
<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" />
</unit> </unit>
<!-- mine layer --> <!-- mine layer -->
<unit name="mnly"> <unit name="mnly">

View File

@@ -15,7 +15,7 @@ MNLY
[V2RL] [V2RL]
Description=V2 Rocket Description=V2 Rocket
Traits=Mobile, RenderUnit Traits=Mobile, RenderUnit, AttackBase
[1TNK] [1TNK]
Description=Light Tank Description=Light Tank
Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted
@@ -55,7 +55,9 @@ PrimaryOffset=0,0,0,-2
MuzzleFlash=yes MuzzleFlash=yes
[APC] [APC]
Description=Armored Personnel Carrier Description=Armored Personnel Carrier
Traits=Mobile, RenderUnit, AttackBase Traits=Mobile, AttackBase, RenderUnitMuzzleFlash
PrimaryOffset=0,0,0,-4
MuzzleFlash=yes
[MNLY] [MNLY]
Description=Minelayer Description=Minelayer
Traits=Mobile, RenderUnit Traits=Mobile, RenderUnit