using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using IjwFramework.Types; using OpenRa.FileFormats; using OpenRa.Game.GameRules; using OpenRa.Game.Graphics; using System.Drawing; using OpenRa.Game.Traits; namespace OpenRa.Game { class Actor { public readonly TypeDictionary traits = new TypeDictionary(); public readonly UnitInfo unitInfo; public readonly uint ActorID; public int2 Location; public Player Owner; public int Health; public Actor( string name, int2 location, Player owner ) { ActorID = Game.world.NextAID(); unitInfo = Rules.UnitInfo[ name ]; Location = location; CenterLocation = new float2( 12, 12 ) + Game.CellSize * (float2)Location; Owner = owner; Health = unitInfo.Strength; /* todo: handle cases where this is not true! */ if( unitInfo.Traits != null ) { foreach( var traitName in unitInfo.Traits ) { var type = typeof( Traits.Mobile ).Assembly.GetType( typeof( Traits.Mobile ).Namespace + "." + traitName, true, false ); var ctor = type.GetConstructor( new[] { typeof( Actor ) } ); traits.Add( type, ctor.Invoke( new object[] { this } ) ); } } else throw new InvalidOperationException( "No Actor traits for " + unitInfo.Name + "; add Traits= to units.ini for appropriate unit" ); } public Actor( TreeReference tree, TreeCache treeRenderer ) { Location = new int2( tree.Location ); traits.Add( new Traits.Tree( treeRenderer.GetImage( tree.Image ) ) ); } public void Tick() { foreach (var tick in traits.WithInterface()) tick.Tick(this); } public float2 CenterLocation; public float2 SelectedSize { get { return Render().First().First.size; } } public IEnumerable> Render() { return traits.WithInterface().SelectMany( x => x.Render( this ) ); } public Order Order( int2 xy, bool lmb ) { if (Owner != Game.LocalPlayer) return null; var underCursor = Game.UnitInfluence.GetUnitAt( xy ) ?? Game.BuildingInfluence.GetBuildingAt( xy ); return traits.WithInterface() .Select( x => x.Order( this, xy, lmb, underCursor ) ) .FirstOrDefault( x => x != null ); } public RectangleF Bounds { get { var size = SelectedSize; var loc = CenterLocation - 0.5f * size; return new RectangleF(loc.X, loc.Y, size.X, size.Y); } } public bool IsDead { get { return Health <= 0; } } public void InflictDamage(Actor attacker, Bullet inflictor, int damage) { /* todo: auto-retaliate, etc */ /* todo: death sequence for infantry based on inflictor */ /* todo: start smoking if < conditionYellow and took damage, and not already smoking */ if (Health <= 0) return; /* overkill! don't count extra hits as more kills! */ Health -= damage; if (Health <= 0) { Health = 0; if (attacker.Owner != null) attacker.Owner.Kills++; Game.world.AddFrameEndTask(w => w.Remove(this)); if (Owner == Game.LocalPlayer && !traits.Contains()) Game.PlaySound("unitlst1.aud", false); if (traits.Contains()) { Game.PlaySound("kaboom22.aud", false); // todo: spawn explosion sprites } } var halfStrength = unitInfo.Strength * Rules.General.ConditionYellow; if (Health < halfStrength && (Health + damage) >= halfStrength) { /* we just went below half health! */ foreach (var nd in traits.WithInterface()) nd.Damaged(this, DamageState.Half); } } } }