Trait-based units. Any unit/building without sequences will cause a crash when built.

This commit is contained in:
Bob
2009-10-10 00:31:16 +13:00
parent a08bcd9a17
commit 3181b055aa
18 changed files with 1377 additions and 1182 deletions

View File

@@ -42,6 +42,7 @@
<Compile Include="int2.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Tuple.cs" />
<Compile Include="TypeDictionary.cs" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace OpenRa
{
public class TypeDictionary
{
Dictionary<Type, object> inner = new Dictionary<Type, object>();
public void Add<T>( T val )
{
inner.Add( typeof( T ), val );
}
public void Remove<T>()
{
inner.Remove( typeof( T ) );
}
public bool Contains<T>()
{
return inner.ContainsKey( typeof( T ) );
}
public T Get<T>()
{
return (T)inner[ typeof( T ) ];
}
public IEnumerable<T> WithInterface<T>()
{
foreach( var i in inner )
if( i.Value is T )
yield return (T)i.Value;
}
}
}

442
OpenRa.Game/Actor.cs Normal file → Executable file
View File

@@ -1,26 +1,442 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Drawing;
using OpenRa.FileFormats;
using System.Windows.Forms;
using OpenRa.Game.Graphics;
using IjwFramework.Types;
using OpenRa.FileFormats;
using OpenRa.Game.GameRules;
using OpenRa.Game.Graphics;
namespace OpenRa.Game
{
abstract class Actor
class Actor
{
public readonly Game game;
public readonly TypeDictionary traits = new TypeDictionary();
public readonly UnitInfo.BaseInfo unitInfo;
public Player owner;
public abstract IEnumerable<Pair<Sprite,float2>> CurrentImages { get; }
public virtual void Tick(Game game, int t) { }
public int2 Location;
public Player Owner;
protected Actor(Game game)
public Actor( string name, int2 location, Player owner )
{
this.game = game;
unitInfo = Rules.UnitInfo.Get( name );
Location = location;
Owner = owner;
switch( name )
{
///// vehicles /////
case "mcv":
traits.Add( new Traits.Mobile( this ) );
traits.Add( new Traits.RenderUnit( this, name ) );
traits.Add( new Traits.McvDeploy( this ) );
break;
case "mnly":
case "apc":
case "v2rl":
case "arty":
traits.Add( new Traits.Mobile( this ) );
traits.Add( new Traits.RenderUnit( this, name ) );
break;
case "jeep":
case "1tnk":
case "2tnk":
case "3tnk":
case "4tnk":
case "mrj":
case "mgg":
traits.Add( new Traits.Mobile( this ) );
traits.Add( new Traits.Turreted( this ) );
traits.Add( new Traits.RenderUnitTurreted( this, name ) );
break;
case "harv":
traits.Add( new Traits.Mobile( this ) );
traits.Add( new Traits.RenderUnit( this, name ) );
break;
///// TODO: infantry /////
///// TODO: boats /////
///// TODO: planes /////
///// buildings /////
case "iron":
case "pdox":
case "mslo":
case "atek":
case "stek":
case "weap":
case "fact":
case "proc":
case "silo":
case "hpad":
case "afld":
case "dome":
case "powr":
case "apwr":
case "barr":
case "tent":
case "kenn":
case "fix":
//SYRD, SPEN
//GAP
//SBAG, BRIK, FENC
//FACF, WEAF, SYRF, SPEF, DOMF
case "pbox":
case "hbox":
case "tsla":
case "ftur":
traits.Add( new Traits.Building( this ) );
traits.Add( new Traits.RenderBuilding( this, name ) );
break;
case "gun":
case "agun":
case "sam":
traits.Add( new Traits.Building( this ) );
traits.Add( new Traits.Turreted( this ) );
traits.Add( new Traits.RenderBuildingTurreted( this, name ) );
break;
default:
throw new NotImplementedException( "Actor traits for " + name );
}
}
public Actor( TreeReference tree, TreeCache treeRenderer, int2 mapOffset )
{
Location = new int2( tree.Location ) - mapOffset;
traits.Add( new Traits.Tree( treeRenderer.GetImage( tree.Image ) ) );
}
public void Tick( Game game, int dt )
{
foreach( var tick in traits.WithInterface<Traits.ITick>() )
tick.Tick( this, game, dt );
}
public float2 CenterLocation { get { return new float2( 12, 12 ) + 24 * (float2)Location; } }
public float2 SelectedSize { get { return Render().FirstOrDefault().First.size; } }
public IEnumerable<Pair<Sprite, float2>> Render()
{
return traits.WithInterface<Traits.IRender>().SelectMany( x => x.Render( this ) );
}
public Order Order( Game game, int2 xy )
{
return traits.WithInterface<Traits.IOrder>()
.Select( x => x.Order( this, game, xy ) )
.Where( x => x != null )
.FirstOrDefault();
}
}
namespace Traits
{
interface ITick
{
void Tick( Actor self, Game game, int dt );
}
interface IRender
{
IEnumerable<Pair<Sprite, float2>> Render( Actor self );
}
interface IOrder
{
Order Order( Actor self, Game game, int2 xy );
}
abstract class RenderSimple : IRender, ITick
{
public Animation anim;
public RenderSimple( Actor self, string unitName )
{
anim = new Animation( unitName );
}
public abstract IEnumerable<Pair<Sprite, float2>> Render( Actor self );
public virtual void Tick( Actor self, Game game, int dt )
{
anim.Tick( dt );
}
}
class RenderBuilding : RenderSimple
{
public RenderBuilding( Actor self, string unitName )
: base( self, unitName )
{
anim.PlayThen( "make", () => anim.Play( "idle" ) );
}
public override IEnumerable<Pair<Sprite, float2>> Render( Actor self )
{
yield return Pair.New( anim.Image, 24f * (float2)self.Location );
}
}
class RenderBuildingTurreted : RenderBuilding
{
public RenderBuildingTurreted( Actor self, string unitName )
: base( self, unitName )
{
anim.PlayThen( "make", () => anim.PlayFetchIndex( "idle", () => self.traits.Get<Turreted>().turretFacing ) );
}
}
class RenderUnit : RenderSimple
{
public RenderUnit( Actor self, string unitName )
:base( self, unitName )
{
anim.PlayFetchIndex( "idle", () => self.traits.Get<Mobile>().facing );
}
protected static Pair<Sprite, float2> Centered( Sprite s, float2 location )
{
var loc = location - 0.5f * s.size;
return Pair.New( s, loc.Round() );
}
public override IEnumerable<Pair<Sprite, float2>> Render( Actor self )
{
var mobile = self.traits.Get<Mobile>();
float fraction = ( mobile.moveFraction > 0 ) ? (float)mobile.moveFraction / mobile.moveFractionTotal : 0f;
var centerLocation = new float2( 12, 12 ) + 24 * float2.Lerp( mobile.fromCell, mobile.toCell, fraction );
yield return Centered( anim.Image, centerLocation );
}
}
class RenderUnitTurreted : RenderUnit
{
public Animation turretAnim;
public RenderUnitTurreted( Actor self, string unitName )
: base( self, unitName )
{
turretAnim = new Animation( unitName );
turretAnim.PlayFetchIndex( "turret", () => self.traits.Get<Turreted>().turretFacing );
}
public override IEnumerable<Pair<Sprite, float2>> Render( Actor self )
{
var mobile = self.traits.Get<Mobile>();
float fraction = ( mobile.moveFraction > 0 ) ? (float)mobile.moveFraction / mobile.moveFractionTotal : 0f;
var centerLocation = new float2( 12, 12 ) + 24 * float2.Lerp( mobile.fromCell, mobile.toCell, fraction );
yield return Centered( anim.Image, centerLocation );
yield return Centered( turretAnim.Image, centerLocation );
}
public override void Tick( Actor self, Game game, int dt )
{
base.Tick( self, game, dt );
turretAnim.Tick( dt );
}
}
class Mobile : ITick, IOrder
{
public Actor self;
public int2 fromCell, destination;
public int2 toCell { get { return self.Location; } }
public int moveFraction, moveFractionTotal;
public int facing;
public Mobile( Actor self )
{
this.self = self;
fromCell = destination = self.Location;
}
public bool Turn( int desiredFacing )
{
if( facing == desiredFacing )
return false;
int df = ( desiredFacing - facing + 32 ) % 32;
facing = ( facing + ( df > 16 ? 31 : 1 ) ) % 32;
return true;
}
static float2[] fvecs = Util.MakeArray<float2>( 32,
i => -float2.FromAngle( i / 16.0f * (float)Math.PI ) * new float2( 1f, 1.3f ) );
int GetFacing( float2 d )
{
if( float2.WithinEpsilon( d, float2.Zero, 0.001f ) )
return facing;
int highest = -1;
float highestDot = -1.0f;
for( int i = 0 ; i < fvecs.Length ; i++ )
{
float dot = float2.Dot( fvecs[ i ], d );
if( dot > highestDot )
{
highestDot = dot;
highest = i;
}
}
return highest;
}
public void Tick( Actor self, Game game, int dt )
{
if( fromCell != toCell )
{
if( Turn( GetFacing( toCell - fromCell ) ) )
return;
moveFraction += dt * ( (UnitInfo.MobileInfo)self.unitInfo ).Speed;
}
if( moveFraction < moveFractionTotal )
return;
moveFraction = 0;
moveFractionTotal = 0;
fromCell = toCell;
if( destination == toCell )
return;
List<int2> res = game.pathFinder.FindUnitPath( toCell, PathFinder.DefaultEstimator( destination ) );
if( res.Count != 0 )
{
self.Location = res[ res.Count - 1 ];
int2 dir = toCell - fromCell;
moveFractionTotal = ( dir.X != 0 && dir.Y != 0 ) ? 2500 : 2000;
}
else
destination = toCell;
}
public Order Order( Actor self, Game game, int2 xy )
{
if( xy != toCell )
return new MoveOrder( self, xy );
return null;
}
}
class McvDeploy : IOrder, ITick
{
public bool Deploying;
public McvDeploy( Actor self )
{
}
public Order Order( Actor self, Game game, int2 xy )
{
// TODO: check that there's enough space at the destination.
if( xy == self.Location )
return new DeployMcvOrder( self );
return null;
}
public void Tick( Actor self, Game game, int dt )
{
if( !Deploying )
return;
if( self.traits.Get<Mobile>().Turn( 12 ) )
return;
game.world.AddFrameEndTask( _ =>
{
game.world.Remove( self );
game.world.Add( new Actor( "fact", self.Location - new int2( 1, 1 ), self.Owner ) );
} );
}
}
class Turreted
: ITick // temporary.
{
public int turretFacing = 24;
public Turreted( Actor self )
{
}
// temporary.
public void Tick( Actor self, Game game, int dt )
{
turretFacing = ( turretFacing + 1 ) % 32;
}
}
class Building : ITick
{
public Building( Actor self )
{
}
bool first = true;
public void Tick( Actor self, Game game, int dt )
{
if( first && self.Owner == game.LocalPlayer )
self.Owner.TechTree.Build( self.unitInfo.Name, true );
first = false;
}
}
class Tree : IRender
{
Sprite Image;
public Tree( Sprite treeImage )
{
Image = treeImage;
}
public IEnumerable<Pair<Sprite, float2>> Render( Actor self )
{
yield return Pair.New( Image, 24 * (float2)self.Location );
}
}
//class WarFactory : Building
//{
// Animation roof;
// public WarFactory( int2 location, Player owner, Game game )
// : base( "weap", location, owner, game )
// {
// animation.PlayThen( "make", () =>
// {
// roof = new Animation( "weap" );
// animation.PlayRepeating( "idle" );
// roof.PlayRepeating( "idle-top" );
// } );
// }
// public override IEnumerable<Pair<Sprite, float2>> CurrentImages
// {
// get
// {
// return ( roof == null )
// ? base.CurrentImages
// : ( base.CurrentImages.Concat(
// new[] { Pair.New( roof.Image, 24 * (float2)location ) } ) );
// }
// }
// public override void Tick( Game game, int t )
// {
// base.Tick( game, t );
// if( roof != null ) roof.Tick( t );
// }
//}
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace OpenRa.Game
{
class Building : PlayerOwned
{
public Building( string name, int2 location, Player owner, Game game )
: base( game, name, location )
{
this.owner = owner;
animation.PlayThen( "make", () => animation.PlayRepeating( "idle" ) );
owner.TechTree.Build( name, true );
}
public override void Tick( Game game, int t )
{
animation.Tick( t );
}
}
}

View File

@@ -43,9 +43,9 @@ namespace OpenRa.Game
if (!(orderGenerator is PlaceBuilding))
{
if (dragStart.HasValue && !(dragStart.Value == GetWorldPos(mi)))
orderGenerator = FindUnit(dragStart.Value, xy); /* band-box select */
orderGenerator = new UnitOrderGenerator( FindUnits( game, 24 * dragStart.Value, 24 * xy ) ); /* band-box select */
else
orderGenerator = FindUnit(xy, xy); /* click select */
orderGenerator = new UnitOrderGenerator( FindUnits( game, 24 * xy, 24 * xy ) ); /* click select */
}
dragStart = dragEnd = null;
@@ -63,22 +63,29 @@ namespace OpenRa.Game
order.Apply( game );
}
public Unit FindUnit(float2 a, float2 b)
public Actor FindUnit(float2 a, float2 b)
{
return FindUnits(game, 24 * a, 24 * b).FirstOrDefault();
}
public static IEnumerable<Unit> FindUnits(Game game, float2 a, float2 b)
public static IEnumerable<Actor> FindUnits(Game game, float2 a, float2 b)
{
var min = new float2(Math.Min(a.X, b.X), Math.Min(a.Y, b.Y));
var max = new float2(Math.Max(a.X, b.X), Math.Max(a.Y, b.Y));
var rect = new RectangleF(min.X, min.Y, max.X - min.X, max.Y - min.Y);
return game.world.Actors.OfType<Unit>()
.Where(x => (x.owner == game.LocalPlayer) && (x.Bounds.IntersectsWith(rect)));
return game.world.Actors
.Where(x => (x.Owner == game.LocalPlayer) && (UnitBounds(x).IntersectsWith(rect)));
}
public static RectangleF UnitBounds( Actor actor )
{
var size = actor.SelectedSize;
var loc = actor.CenterLocation - 0.5f * size;
return new System.Drawing.RectangleF( loc.X, loc.Y, size.X, size.Y );
}
public Pair<float2, float2>? SelectionBox()
{
if (dragStart == null || dragEnd == null)

View File

@@ -23,9 +23,6 @@ namespace OpenRa.Game
public readonly Dictionary<int, Player> players = new Dictionary<int, Player>();
// temporary, until we remove all the subclasses of Building
public Dictionary<string, Func<int2, Player, PlayerOwned>> buildingCreation;
public Player LocalPlayer { get { return players[localPlayerIndex]; } }
public Game(string mapName, Renderer renderer, int2 clientSize)
@@ -44,22 +41,13 @@ namespace OpenRa.Game
world = new World(this);
treeCache = new TreeCache(map);
foreach (TreeReference treeReference in map.Trees)
world.Add(new Tree(treeReference, treeCache, map, this));
foreach( TreeReference treeReference in map.Trees )
world.Add( new Actor( treeReference, treeCache, map.Offset ) );
pathFinder = new PathFinder(map, terrain.tileSet);
network = new Network();
var buildings = new[] { "fact", "powr", "apwr", "barr", "atek", "stek", "dome" };
buildingCreation = buildings.ToDictionary(s => s,
s => (Func<int2, Player, PlayerOwned>)(
(l, o) => new Building(s, l, o, this)));
buildingCreation.Add("proc", (location, owner) => new Refinery(location, owner, this));
buildingCreation.Add("weap", (location, owner) => new WarFactory(location, owner, this));
buildingCreation.Add("3tnk", (location, owner) => new TurretedUnit("3tnk", location, owner, this));
controller = new Controller(this); // CAREFUL THERES AN UGLY HIDDEN DEPENDENCY HERE STILL
worldRenderer = new WorldRenderer(renderer, world);
}

View File

@@ -1,4 +1,4 @@
using System.Drawing;
using System.Drawing;
using System.Windows.Forms;
namespace OpenRa.Game.Graphics
@@ -34,7 +34,7 @@ namespace OpenRa.Game.Graphics
foreach (Actor a in world.Actors)
{
var images = a.CurrentImages;
var images = a.Render();
foreach( var image in images )
{
@@ -45,7 +45,7 @@ namespace OpenRa.Game.Graphics
if( loc.Y > rect.Bottom || loc.Y < rect.Top - image.First.bounds.Height )
continue;
spriteRenderer.DrawSprite( image.First, loc, ( a.owner != null ) ? a.owner.Palette : 0 );
spriteRenderer.DrawSprite( image.First, loc, ( a.Owner != null ) ? a.Owner.Palette : 0 );
}
}
@@ -69,7 +69,7 @@ namespace OpenRa.Game.Graphics
DrawSelectionBox(u, Color.Yellow);
}
var selectedUnit = world.game.controller.orderGenerator as Unit;
var selectedUnit = world.game.controller.orderGenerator as Actor;
if (selectedUnit != null)
DrawSelectionBox(selectedUnit, Color.White);
@@ -77,7 +77,7 @@ namespace OpenRa.Game.Graphics
lineRenderer.Flush();
}
void DrawSelectionBox(Unit selectedUnit, Color c)
void DrawSelectionBox(Actor selectedUnit, Color c)
{
var center = selectedUnit.CenterLocation;
var size = selectedUnit.SelectedSize;

View File

@@ -44,10 +44,10 @@ namespace OpenRa.Game
SequenceProvider.ForcePrecache();
game.world.Add(new Unit("mcv", new int2(5, 5), game.players[3], game));
game.world.Add(new Unit("mcv", new int2(7, 5), game.players[2], game));
game.world.Add(new Unit("mcv", new int2(9, 5), game.players[1], game));
game.world.Add(new TurretedUnit("jeep", new int2(9, 7), game.players[1], game));
game.world.Add( new Actor( "mcv", new int2( 5, 5 ), game.players[ 3 ]) );
game.world.Add( new Actor( "mcv", new int2( 7, 5 ), game.players[ 2 ] ) );
game.world.Add( new Actor( "mcv", new int2( 9, 5 ), game.players[ 1 ] ) );
game.world.Add( new Actor( "jeep", new int2( 9, 7 ), game.players[ 1 ] ) );
sidebar = new Sidebar(Race.Soviet, renderer, game);

View File

@@ -11,10 +11,10 @@ namespace OpenRa.Game
class MoveOrder : Order
{
public readonly Unit Unit;
public readonly Actor Unit;
public readonly int2 Destination;
public MoveOrder(Unit unit, int2 destination)
public MoveOrder( Actor unit, int2 destination )
{
this.Unit = unit;
this.Destination = destination;
@@ -22,37 +22,39 @@ namespace OpenRa.Game
public override void Apply( Game game )
{
Unit.nextOrder = UnitMissions.Move( Unit, Destination );
Unit.traits.Get<Traits.Mobile>().destination = Destination;
}
}
class DeployMcvOrder : Order
{
Unit unit;
Actor Unit;
public DeployMcvOrder( Unit unit )
public DeployMcvOrder( Actor unit )
{
this.unit = unit;
Unit = unit;
}
public override void Apply( Game game )
{
unit.nextOrder = UnitMissions.Deploy( unit );
Unit.traits.Get<Traits.McvDeploy>().Deploying = true;
var mobile = Unit.traits.Get<Traits.Mobile>();
mobile.destination = mobile.toCell;
}
}
class HarvestOrder : Order
{
Unit unit;
//class HarvestOrder : Order
//{
// Unit unit;
public HarvestOrder( Unit unit )
{
this.unit = unit;
}
// public HarvestOrder( Unit unit )
// {
// this.unit = unit;
// }
public override void Apply( Game game )
{
unit.nextOrder = UnitMissions.Harvest( unit );
}
}
// public override void Apply( Game game )
// {
// unit.nextOrder = UnitMissions.Harvest( unit );
// }
//}
}

View File

@@ -75,7 +75,6 @@
<Compile Include="GameRules\Rules.cs" />
<Compile Include="GameRules\UnitInfo.cs" />
<Compile Include="Graphics\Animation.cs" />
<Compile Include="Building.cs" />
<Compile Include="Game.cs" />
<Compile Include="Graphics\LineRenderer.cs" />
<Compile Include="Graphics\WorldRenderer.cs" />
@@ -83,7 +82,6 @@
<Compile Include="TechTree\Item.cs" />
<Compile Include="Network\Packet.cs" />
<Compile Include="Player.cs" />
<Compile Include="PlayerOwned.cs" />
<Compile Include="Race.cs" />
<Compile Include="Support\SharedResources.cs" />
<Compile Include="Graphics\Sheet.cs" />
@@ -101,7 +99,6 @@
</Compile>
<Compile Include="Support\Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Refinery.cs" />
<Compile Include="Graphics\Renderer.cs" />
<Compile Include="Support\Settings.cs" />
<Compile Include="Sidebar.cs" />
@@ -112,15 +109,14 @@
<Compile Include="TechTree\TechTree.cs" />
<Compile Include="TerrainCosts.cs" />
<Compile Include="Graphics\TerrainRenderer.cs" />
<Compile Include="Tree.cs" />
<Compile Include="Graphics\TreeCache.cs" />
<Compile Include="UiOverlay.cs" />
<Compile Include="Unit.cs" />
<Compile Include="UnitMissions.cs" />
<Compile Include="Graphics\UnitSheetBuilder.cs" />
<Compile Include="Graphics\Util.cs" />
<Compile Include="Graphics\Vertex.cs" />
<Compile Include="Graphics\Viewport.cs" />
<Compile Include="UnitOrderGenerator.cs" />
<Compile Include="World.cs" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,25 +0,0 @@
using System.Collections.Generic;
using OpenRa.Game.Graphics;
using IjwFramework.Types;
namespace OpenRa.Game
{
abstract class PlayerOwned : Actor
{
public Animation animation;
protected int2 location;
public UnitMission currentOrder = null;
public UnitMission nextOrder = null;
protected PlayerOwned( Game game, string name, int2 location )
: base( game )
{
animation = new Animation( name );
this.location = location;
}
public override IEnumerable<Pair<Sprite, float2>> CurrentImages { get { yield return Pair.New( animation.Image, 24 * (float2)location ); } }
}
}

View File

@@ -1,61 +0,0 @@
using OpenRa.Game.Graphics;
using System.Linq;
using System.Collections.Generic;
using IjwFramework.Types;
namespace OpenRa.Game
{
class Refinery : Building
{
public Refinery( int2 location, Player owner, Game game )
: base( "proc", location, owner, game )
{
animation.PlayThen("make", () =>
{
animation.PlayRepeating("idle");
game.world.AddFrameEndTask( _ =>
{
Unit harvester = new Unit( "harv", location + new int2( 1, 2 ), owner, game );
harvester.facing = 8;
game.world.Add(harvester);
game.controller.orderGenerator = harvester;
});
});
}
}
class WarFactory : Building
{
Animation roof;
public WarFactory(int2 location, Player owner, Game game)
: base("weap", location, owner, game)
{
animation.PlayThen("make", () =>
{
roof = new Animation("weap");
animation.PlayRepeating("idle");
roof.PlayRepeating("idle-top");
});
}
public override IEnumerable<Pair<Sprite,float2>> CurrentImages
{
get
{
return (roof == null)
? base.CurrentImages
: (base.CurrentImages.Concat(
new[] { Pair.New(roof.Image, 24 * (float2)location) }));
}
}
public override void Tick(Game game, int t)
{
base.Tick(game, t);
if (roof != null) roof.Tick(t);
}
}
}

View File

@@ -211,12 +211,9 @@ namespace OpenRa.Game
{
game.world.AddFrameEndTask(_ =>
{
Func<int2, Player, PlayerOwned> newBuilding;
if (game.buildingCreation.TryGetValue(building.Name, out newBuilding))
{
Log.Write("Player \"{0}\" builds {1}", building.Owner.PlayerName, building.Name);
game.world.Add(newBuilding(xy, building.Owner));
}
Log.Write( "Player \"{0}\" builds {1}", building.Owner.PlayerName, building.Name );
game.world.Add( new Actor( building.Name, xy, building.Owner ) );
game.controller.orderGenerator = null;
game.worldRenderer.uiOverlay.KillOverlay();
});

View File

@@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using OpenRa.FileFormats;
using System.Drawing;
using OpenRa.Game.Graphics;
using IjwFramework.Types;
namespace OpenRa.Game
{
class Tree : Actor
{
int2 location;
public Tree(TreeReference r, TreeCache renderer, Map map, Game game)
: base( game )
{
location = new int2( r.Location ) - map.Offset;
currentImages = new Sprite[] { renderer.GetImage(r.Image) };
}
Sprite[] currentImages;
public override IEnumerable<Pair<Sprite, float2>> CurrentImages
{
get
{
foreach( var x in currentImages )
yield return Pair.New( x, 24 * (float2)location );
}
}
}
}

View File

@@ -1,150 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using IjwFramework.Types;
using OpenRa.Game.GameRules;
using OpenRa.Game.Graphics;
namespace OpenRa.Game
{
class Unit : PlayerOwned, IOrderGenerator
{
public int facing = 0;
public int2 fromCell;
public int2 toCell
{
get { return location; }
set { location = value; }
}
public int moveFraction, moveFractionTotal;
readonly float2 renderOffset;
public readonly UnitInfo.MobileInfo unitInfo;
public Unit( string name, int2 cell, Player owner, Game game )
: base( game, name, cell )
{
fromCell = toCell = cell;
this.owner = owner;
this.unitInfo = (UnitInfo.MobileInfo)Rules.UnitInfo.Get( name );
animation.PlayFetchIndex( "idle", () => facing );
renderOffset = animation.Center;
}
static float2[] fvecs = Util.MakeArray<float2>(32,
i => -float2.FromAngle(i / 16.0f * (float)Math.PI) * new float2(1f, 1.3f));
public int GetFacing( float2 d )
{
if( float2.WithinEpsilon( d, float2.Zero, 0.001f ) )
return facing;
int highest = -1;
float highestDot = -1.0f;
for( int i = 0 ; i < fvecs.Length ; i++ )
{
float dot = float2.Dot( fvecs[ i ], d );
if( dot > highestDot )
{
highestDot = dot;
highest = i;
}
}
return highest;
}
public override void Tick( Game game, int t )
{
animation.Tick( t );
if( currentOrder == null && nextOrder != null )
{
currentOrder = nextOrder;
nextOrder = null;
}
if( currentOrder != null )
currentOrder( t );
}
public override IEnumerable<Pair<Sprite, float2>> CurrentImages
{
get
{
yield return Centered( animation.Image, CenterLocation );
}
}
public float2 CenterLocation
{
get
{
float fraction = ( moveFraction > 0 ) ? (float)moveFraction / moveFractionTotal : 0f;
return new float2( 12, 12 ) + 24 * float2.Lerp( fromCell, toCell, fraction );
}
}
bool SupportsMission( SupportedMissions mission )
{
if( mission == SupportedMissions.Deploy )
return this.unitInfo.Name == "MCV";
if( mission == SupportedMissions.Harvest )
return this.unitInfo.Name == "HARV";
return false;
}
public IEnumerable<Order> Order( Game game, int2 xy )
{
if( ( fromCell == toCell || moveFraction == 0 ) && fromCell == xy )
{
if( SupportsMission( SupportedMissions.Deploy ) )
yield return new DeployMcvOrder( this );
if( SupportsMission( SupportedMissions.Harvest ) )
yield return new HarvestOrder( this );
}
else
yield return new MoveOrder( this, xy );
}
public void PrepareOverlay(Game game, int2 xy) { }
protected static Pair<Sprite, float2> Centered( Sprite s, float2 location )
{
var loc = location - 0.5f * s.size;
return Pair.New( s, loc.Round() );
}
public float2 SelectedSize { get { return this.CurrentImages.First().First.size; } }
public System.Drawing.RectangleF Bounds
{
get
{
var size = SelectedSize;
var loc = CenterLocation - 0.5f * size;
return new System.Drawing.RectangleF(loc.X, loc.Y, size.X, size.Y);
}
}
}
class TurretedUnit : Unit
{
Animation turretAnim;
int turretFacing { get { return facing; } }
public TurretedUnit( string name, int2 cell, Player owner, Game game )
: base( name, cell, owner, game )
{
turretAnim = new Animation( name );
turretAnim.PlayFetchIndex( "turret", () => turretFacing );
}
public override IEnumerable<Pair<Sprite, float2>> CurrentImages
{
get { return base.CurrentImages.Concat(new[] { Centered(turretAnim.Image, CenterLocation) }); }
}
}
}

View File

@@ -42,105 +42,105 @@ namespace OpenRa.Game
Deploy = 2,
}
delegate void UnitMission( int t );
static class UnitMissions
{
public static UnitMission Sleep()
{
return t => { };
}
//delegate void UnitMission( int t );
//static class UnitMissions
//{
// public static UnitMission Sleep()
// {
// return t => { };
// }
public static UnitMission Move( Unit unit, int2 destination )
{
return t =>
{
Game game = unit.game;
// public static UnitMission Move( Unit unit, int2 destination )
// {
// return t =>
// {
// Game game = unit.game;
if( unit.nextOrder != null )
destination = unit.toCell;
// if( unit.nextOrder != null )
// destination = unit.toCell;
if( Turn( unit, unit.GetFacing( unit.toCell - unit.fromCell ) ) )
return;
// if( Turn( unit, unit.GetFacing( unit.toCell - unit.fromCell ) ) )
// return;
unit.moveFraction += t * unit.unitInfo.Speed;
if( unit.moveFraction < unit.moveFractionTotal )
return;
// unit.moveFraction += t * unit.unitInfo.Speed;
// if( unit.moveFraction < unit.moveFractionTotal )
// return;
unit.moveFraction = 0;
unit.moveFractionTotal = 0;
unit.fromCell = unit.toCell;
// unit.moveFraction = 0;
// unit.moveFractionTotal = 0;
// unit.fromCell = unit.toCell;
if( unit.toCell == destination )
{
unit.currentOrder = null;
return;
}
// if( unit.toCell == destination )
// {
// unit.currentOrder = null;
// return;
// }
List<int2> res = game.pathFinder.FindUnitPath( unit.toCell, PathFinder.DefaultEstimator( destination ) );
if( res.Count != 0 )
{
unit.toCell = res[ res.Count - 1 ];
// List<int2> res = game.pathFinder.FindUnitPath( unit.toCell, PathFinder.DefaultEstimator( destination ) );
// if( res.Count != 0 )
// {
// unit.toCell = res[ res.Count - 1 ];
int2 dir = unit.toCell - unit.fromCell;
unit.moveFractionTotal = ( dir.X != 0 && dir.Y != 0 ) ? 2500 : 2000;
}
else
destination = unit.toCell;
};
}
// int2 dir = unit.toCell - unit.fromCell;
// unit.moveFractionTotal = ( dir.X != 0 && dir.Y != 0 ) ? 2500 : 2000;
// }
// else
// destination = unit.toCell;
// };
// }
public static UnitMission Deploy( Unit unit )
{
return t =>
{
Game game = unit.game;
// public static UnitMission Deploy( Unit unit )
// {
// return t =>
// {
// Game game = unit.game;
if( Turn( unit, 12 ) )
return;
// if( Turn( unit, 12 ) )
// return;
game.world.AddFrameEndTask( _ =>
{
game.world.Remove( unit );
game.world.Add( new Building("fact", unit.fromCell - new int2( 1, 1 ), unit.owner, game ) );
} );
unit.currentOrder = null;
};
}
// game.world.AddFrameEndTask( _ =>
// {
// game.world.Remove( unit );
// game.world.Add( new Building( "fact", unit.fromCell - new int2( 1, 1 ), unit.Owner, game ) );
// } );
// unit.currentOrder = null;
// };
// }
public static UnitMission Harvest( Unit unit )
{
UnitMission order = null;
order = t =>
{
// TODO: check that there's actually some ore in this cell :)
// public static UnitMission Harvest( Unit unit )
// {
// UnitMission order = null;
// order = t =>
// {
// // TODO: check that there's actually some ore in this cell :)
// face in one of the 8 directions
if( Turn( unit, ( unit.facing + 1 ) & ~3 ) )
return;
// // face in one of the 8 directions
// if( Turn( unit, ( unit.facing + 1 ) & ~3 ) )
// return;
unit.currentOrder = _ => { };
if( unit.nextOrder == null )
unit.nextOrder = order;
// unit.currentOrder = _ => { };
// if( unit.nextOrder == null )
// unit.nextOrder = order;
string sequenceName = string.Format( "harvest{0}", unit.facing / 4 );
unit.animation.PlayThen( sequenceName, () =>
{
unit.currentOrder = null;
unit.animation.PlayFetchIndex("idle", () => unit.facing);
} );
};
return order;
}
// string sequenceName = string.Format( "harvest{0}", unit.facing / 4 );
// unit.animation.PlayThen( sequenceName, () =>
// {
// unit.currentOrder = null;
// unit.animation.PlayFetchIndex( "idle", () => unit.facing );
// } );
// };
// return order;
// }
static bool Turn( Unit unit, int desiredFacing )
{
if( unit.facing == desiredFacing )
return false;
// static bool Turn( Unit unit, int desiredFacing )
// {
// if( unit.facing == desiredFacing )
// return false;
int df = ( desiredFacing - unit.facing + 32 ) % 32;
unit.facing = ( unit.facing + ( df > 16 ? 31 : 1 ) ) % 32;
return true;
}
}
// int df = ( desiredFacing - unit.facing + 32 ) % 32;
// unit.facing = ( unit.facing + ( df > 16 ? 31 : 1 ) ) % 32;
// return true;
// }
//}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRa.Game
{
class UnitOrderGenerator : IOrderGenerator
{
public readonly List<Actor> selection;
public UnitOrderGenerator( IEnumerable<Actor> selected )
{
selection = selected.ToList();
}
public IEnumerable<Order> Order( Game game, int2 xy )
{
foreach( var unit in selection )
{
var ret = unit.Order( game, xy );
if( ret != null )
yield return ret;
}
//if( ( fromCell == toCell || moveFraction == 0 ) && fromCell == xy )
//{
// if( SupportsMission( SupportedMissions.Deploy ) )
// return new DeployMcvOrder( this );
// if( SupportsMission( SupportedMissions.Harvest ) )
// return new HarvestOrder( this );
//}
//return new MoveOrder( this, xy );
}
public void PrepareOverlay( Game game, int2 xy ) { }
}
}

View File

@@ -17,7 +17,7 @@ namespace OpenRa.Game
public void Remove( Actor a ) { actors.Remove( a ); }
public void AddFrameEndTask( Action<World> a ) { frameEndActions.Add( a ); }
int lastTime = Environment.TickCount;
int lastTime = Environment.TickCount + 2000;
@@ -25,10 +25,13 @@ namespace OpenRa.Game
{
int t = Environment.TickCount;
int dt = t - lastTime;
lastTime = t;
if( dt >= 40 )
{
lastTime += 40;
foreach (Actor a in actors)
a.Tick(game, dt);
foreach( Actor a in actors )
a.Tick( game, 40 );
}
foreach (Action<World> a in frameEndActions) a(this);
frameEndActions.Clear();