Facing -> new trait ("Unit")

This commit is contained in:
Bob
2009-11-25 18:11:44 +13:00
parent 7ccb2aa9ee
commit 2aea110497
21 changed files with 120 additions and 87 deletions

View File

@@ -352,7 +352,7 @@ namespace OpenRa.Game
return;
}
Actor unit;
Actor newActor;
if (producerTypes.Contains("spen") || producerTypes.Contains("syrd"))
{
@@ -362,9 +362,9 @@ namespace OpenRa.Game
if (space == null)
return;
unit = new Actor(name, space.Value, player);
var mobile = unit.traits.Get<Mobile>();
mobile.facing = SharedRandom.Next(256);
newActor = new Actor(name, space.Value, player);
var unit = newActor.traits.Get<Unit>();
unit.Facing = SharedRandom.Next(256);
}
else
{
@@ -372,26 +372,27 @@ namespace OpenRa.Game
if (UnitInfluence.GetUnitAt(productionPoint) != null)
return;
unit = new Actor(name, (1 / 24f * producer.CenterLocation).ToInt2(), player);
newActor = new Actor(name, (1 / 24f * producer.CenterLocation).ToInt2(), player);
var rp = producer.traits.GetOrDefault<RallyPoint>();
var dest = rp != null ? rp.rallyPoint : (unit.Location + new int2(0, 3));
var dest = rp != null ? rp.rallyPoint : (newActor.Location + new int2(0, 3));
var mobile = unit.traits.GetOrDefault<Mobile>();
if (mobile != null)
var unit = newActor.traits.Get<Unit>();
var mobile = newActor.traits.GetOrDefault<Mobile>();
if( mobile != null )
{
mobile.facing = 128;
unit.Facing = 128;
mobile.QueueActivity(new Traits.Activities.Move(dest, 1));
}
var heli = unit.traits.GetOrDefault<Helicopter>();
var heli = newActor.traits.GetOrDefault<Helicopter>();
if (heli != null)
{
heli.facing = 20;
unit.Facing = 20;
heli.targetLocation = dest;
}
}
world.Add(unit);
world.Add(newActor);
player.FinishProduction(Rules.UnitCategory[name]);
if (producer.traits.Contains<RenderWarFactory>())

View File

@@ -159,6 +159,7 @@
<Compile Include="Traits\TraitsInterfaces.cs" />
<Compile Include="Traits\Tree.cs" />
<Compile Include="Traits\Turreted.cs" />
<Compile Include="Traits\Unit.cs" />
<Compile Include="UnitOrders.cs" />
<Compile Include="Traits\Util.cs" />
<Compile Include="UiOverlay.cs" />

View File

@@ -14,8 +14,9 @@ namespace OpenRa.Game.Traits
w =>
{ /* create the free harvester! */
var harvester = new Actor("harv", self.Location + new int2(1, 2), self.Owner);
var unit = harvester.traits.Get<Unit>();
var mobile = harvester.traits.Get<Mobile>();
mobile.facing = 64;
unit.Facing = 64;
mobile.QueueActivity(new Harvest());
w.Add(harvester);
});

View File

@@ -21,6 +21,8 @@ namespace OpenRa.Game.Traits.Activities
public IActivity Tick( Actor self, Mobile mobile )
{
var unit = self.traits.Get<Unit>();
if (Target == null || Target.IsDead)
return NextActivity;
@@ -30,7 +32,7 @@ namespace OpenRa.Game.Traits.Activities
var desiredFacing = Util.GetFacing((Target.Location - self.Location).ToFloat2(), 0);
var renderUnit = self.traits.WithInterface<RenderUnit>().First();
if (Util.QuantizeFacing(mobile.facing, renderUnit.anim.CurrentSequence.Length)
if (Util.QuantizeFacing(unit.Facing, renderUnit.anim.CurrentSequence.Length)
!= Util.QuantizeFacing(desiredFacing, renderUnit.anim.CurrentSequence.Length))
{
return new Turn( desiredFacing ) { NextActivity = this };

View File

@@ -23,6 +23,8 @@ namespace OpenRa.Game.Traits.Activities
public IActivity Tick( Actor self, Mobile mobile )
{
var unit = self.traits.Get<Unit>();
if( isDone )
{
self.traits.Get<Harvester>().Deliver( self, refinery );
@@ -61,7 +63,7 @@ namespace OpenRa.Game.Traits.Activities
// no refineries reachable?
return null;
}
else if( mobile.facing != 64 )
else if( unit.Facing != 64 )
return new Turn( 64 ) { NextActivity = this };
var renderUnit = self.traits.WithInterface<RenderUnit>().First();

View File

@@ -12,6 +12,8 @@ namespace OpenRa.Game.Traits.Activities
public IActivity Tick( Actor self, Mobile mobile )
{
var unit = self.traits.Get<Unit>();
if( isHarvesting ) return null;
if( NextActivity != null )
@@ -26,7 +28,7 @@ namespace OpenRa.Game.Traits.Activities
if( Rules.Map.ContainsResource( self.Location ) &&
Rules.Map.Harvest( self.Location, out isGem ) )
{
var harvestAnim = "harvest" + Util.QuantizeFacing( mobile.facing, 8 );
var harvestAnim = "harvest" + Util.QuantizeFacing( unit.Facing, 8 );
var renderUnit = self.traits.WithInterface<RenderUnit>().First(); /* better have one of these! */
if( harvestAnim != renderUnit.anim.CurrentSequence.Name )
{

View File

@@ -51,6 +51,8 @@ namespace OpenRa.Game.Traits.Activities
public IActivity Tick( Actor self, Mobile mobile )
{
var unit = self.traits.Get<Unit>();
if( move != null )
{
move.TickMove( self, mobile, this );
@@ -79,8 +81,8 @@ namespace OpenRa.Game.Traits.Activities
return null;
int2 dir = nextCell.Value - mobile.fromCell;
var firstFacing = Util.GetFacing( dir, mobile.facing );
if( firstFacing != mobile.facing )
var firstFacing = Util.GetFacing( dir, unit.Facing );
if( firstFacing != unit.Facing )
{
path.Add( nextCell.Value );
@@ -92,8 +94,8 @@ namespace OpenRa.Game.Traits.Activities
move = new MoveFirstHalf(
CenterOfCell( mobile.fromCell ),
BetweenCells( mobile.fromCell, mobile.toCell ),
mobile.facing,
mobile.facing,
unit.Facing,
unit.Facing,
0 );
Game.UnitInfluence.Update( mobile );
@@ -183,13 +185,14 @@ namespace OpenRa.Game.Traits.Activities
void UpdateCenterLocation( Actor self, Mobile mobile )
{
var unit = self.traits.Get<Unit>();
var frac = (float)moveFraction / moveFractionTotal;
self.CenterLocation = float2.Lerp( from, to, frac );
if( moveFraction >= moveFractionTotal )
mobile.facing = toFacing & 0xFF;
unit.Facing = toFacing & 0xFF;
else
mobile.facing = ( fromFacing + ( toFacing - fromFacing ) * moveFraction / moveFractionTotal ) & 0xFF;
unit.Facing = ( fromFacing + ( toFacing - fromFacing ) * moveFraction / moveFractionTotal ) & 0xFF;
}
protected abstract MovePart OnComplete( Actor self, Mobile mobile, Move parent );
@@ -204,6 +207,8 @@ namespace OpenRa.Game.Traits.Activities
protected override MovePart OnComplete( Actor self, Mobile mobile, Move parent )
{
var unit = self.traits.Get<Unit>();
var nextCell = parent.PopPath( self, mobile );
if( nextCell != null )
{
@@ -212,8 +217,8 @@ namespace OpenRa.Game.Traits.Activities
var ret = new MoveFirstHalf(
BetweenCells( mobile.fromCell, mobile.toCell ),
BetweenCells( mobile.toCell, nextCell.Value ),
mobile.facing,
Util.GetNearestFacing( mobile.facing, Util.GetFacing( nextCell.Value - mobile.toCell, mobile.facing ) ),
unit.Facing,
Util.GetNearestFacing( unit.Facing, Util.GetFacing( nextCell.Value - mobile.toCell, unit.Facing ) ),
moveFraction - moveFractionTotal );
mobile.fromCell = mobile.toCell;
mobile.toCell = nextCell.Value;
@@ -226,8 +231,8 @@ namespace OpenRa.Game.Traits.Activities
var ret2 = new MoveSecondHalf(
BetweenCells( mobile.fromCell, mobile.toCell ),
CenterOfCell( mobile.toCell ),
mobile.facing,
mobile.facing,
unit.Facing,
unit.Facing,
moveFraction - moveFractionTotal );
mobile.fromCell = mobile.toCell;
Game.UnitInfluence.Update( mobile );

View File

@@ -18,16 +18,20 @@ namespace OpenRa.Game.Traits.Activities
public IActivity Tick( Actor self, Mobile mobile )
{
if( desiredFacing == mobile.facing )
var unit = self.traits.Get<Unit>();
if( desiredFacing == unit.Facing )
return NextActivity;
Util.TickFacing( ref mobile.facing, desiredFacing, self.unitInfo.ROT );
Util.TickFacing( ref unit.Facing, desiredFacing, self.unitInfo.ROT );
return null;
}
public void Cancel( Actor self, Mobile mobile )
{
desiredFacing = mobile.facing;
var unit = self.traits.Get<Unit>();
desiredFacing = unit.Facing;
NextActivity = null;
}
}

View File

@@ -35,9 +35,9 @@ namespace OpenRa.Game.Traits
public void DoAttack( Actor self )
{
var rut = self.traits.GetOrDefault<RenderUnitTurreted>();
var unit = self.traits.Get<Unit>();
if( self.unitInfo.Primary != null && CheckFire( self, self.unitInfo.Primary, ref primaryFireDelay,
if( self.unitInfo.Primary != null && CheckFire( self, unit, self.unitInfo.Primary, ref primaryFireDelay,
self.unitInfo.PrimaryOffset ) )
{
secondaryFireDelay = Math.Max( 4, secondaryFireDelay );
@@ -45,7 +45,7 @@ namespace OpenRa.Game.Traits
return;
}
if (self.unitInfo.Secondary != null && CheckFire(self, self.unitInfo.Secondary, ref secondaryFireDelay,
if (self.unitInfo.Secondary != null && CheckFire(self, unit, self.unitInfo.Secondary, ref secondaryFireDelay,
self.unitInfo.SecondaryOffset ?? self.unitInfo.PrimaryOffset))
{
if (self.unitInfo.SecondaryOffset != null) secondaryRecoil = 1;
@@ -54,7 +54,7 @@ namespace OpenRa.Game.Traits
}
}
bool CheckFire( Actor self, string weaponName, ref int fireDelay, int[] offset )
bool CheckFire( Actor self, Unit unit, string weaponName, ref int fireDelay, int[] offset )
{
if( fireDelay > 0 ) return false;
var weapon = Rules.WeaponInfo[ weaponName ];
@@ -63,7 +63,7 @@ namespace OpenRa.Game.Traits
fireDelay = weapon.ROF;
Game.world.Add( new Bullet( weaponName, self.Owner, self,
self.CenterLocation.ToInt2() + Util.GetTurretPosition( self, offset, 0f ).ToInt2(),
self.CenterLocation.ToInt2() + Util.GetTurretPosition( self, unit, offset, 0f ).ToInt2(),
target.CenterLocation.ToInt2() ) );
return true;

View File

@@ -8,7 +8,6 @@ namespace OpenRa.Game.Traits
{
class Helicopter : ITick, IOrder
{
public int facing;
public int altitude;
public int2 targetLocation;
@@ -32,16 +31,18 @@ namespace OpenRa.Game.Traits
public void Tick(Actor self)
{
var unit = self.traits.Get<Unit>();
if (self.Location != targetLocation)
{
var dist = Game.CellSize * (targetLocation + new float2(.5f,.5f)) - self.CenterLocation;
var desiredFacing = Util.GetFacing(dist, facing);
Util.TickFacing(ref facing, desiredFacing,
var desiredFacing = Util.GetFacing(dist, unit.Facing);
Util.TickFacing(ref unit.Facing, desiredFacing,
self.unitInfo.ROT);
// .6f going the wrong way; .8f going sideways, 1f going forward.
var rawSpeed = .2f * (self.unitInfo as UnitInfo.VehicleInfo).Speed;
var angle = (facing - desiredFacing) / 128f * Math.PI;
var angle = (unit.Facing - desiredFacing) / 128f * Math.PI;
var scale = .4f + .6f * (float)Math.Cos(angle);
if (altitude > CruiseAltitude / 2) // do some movement.

View File

@@ -80,7 +80,7 @@ namespace OpenRa.Game.Traits
anim.Tick();
var d = (desiredLocation - location);
facing = /*Util.GetFacing(d, facing); */ self.traits.Get<Mobile>().facing;
facing = /*Util.GetFacing(d, facing); */ self.traits.Get<Unit>().Facing;
if (float2.WithinEpsilon(d, float2.Zero, .1f))
PlaySequence("stand", true);

View File

@@ -15,7 +15,6 @@ namespace OpenRa.Game.Traits
int2 __fromCell;
public int2 fromCell { get { return __fromCell; } set { Game.UnitInfluence.Remove( this ); __fromCell = value; Game.UnitInfluence.Add( this ); } }
public int2 toCell { get { return self.Location; } set { Game.UnitInfluence.Remove( this ); self.Location = value; Game.UnitInfluence.Add( this ); } }
public int facing;
public int Voice = Game.CosmeticRandom.Next(2);
IActivity currentActivity;

View File

@@ -18,12 +18,11 @@ namespace OpenRa.Game.Traits
void PlayFacingAnim(Actor self)
{
var mobile = self.traits.GetOrDefault<Mobile>();
var heli = self.traits.GetOrDefault<Helicopter>();
var unit = self.traits.Get<Unit>();
anim.PlayFetchIndex("idle",
() => Util.QuantizeFacing(
mobile != null ? mobile.facing : heli.facing,
unit.Facing,
anim.CurrentSequence.Length ));
}

View File

@@ -20,9 +20,9 @@ namespace OpenRa.Game.Traits
() =>
{
var attack = self.traits.WithInterface<AttackBase>().First();
var mobile = self.traits.WithInterface<Mobile>().First();
var unit = self.traits.Get<Unit>();
return (Util.QuantizeFacing(
mobile.facing, 8)) * 6 + (int)(attack.primaryRecoil * 5.9f);
unit.Facing, 8)) * 6 + (int)(attack.primaryRecoil * 5.9f);
});
}

View File

@@ -25,22 +25,24 @@ namespace OpenRa.Game.Traits
public override IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{
var unit = self.traits.Get<Unit>();
yield return Util.CenteredShadow(self, anim.Image, self.CenterLocation);
yield return Util.CenteredShadow(self, rotorAnim.Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, 0));
+ Util.GetTurretPosition(self, unit, self.unitInfo.PrimaryOffset, 0));
if (self.unitInfo.SecondaryOffset != null)
yield return Util.CenteredShadow(self, (secondRotorAnim ?? rotorAnim).Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, 0));
+ Util.GetTurretPosition(self, unit, self.unitInfo.SecondaryOffset, 0));
var heli = self.traits.Get<Helicopter>();
var p = self.CenterLocation - new float2( 0, heli.altitude );
yield return Util.Centered(self, anim.Image, p);
yield return Util.Centered(self, rotorAnim.Image, p
+ Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, 0));
yield return Util.Centered(self, rotorAnim.Image, p
+ Util.GetTurretPosition( self, unit, 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, unit, self.unitInfo.SecondaryOffset, 0 ) );
}
public override void Tick(Actor self)

View File

@@ -36,19 +36,19 @@ namespace OpenRa.Game.Traits
public override IEnumerable<Tuple<Sprite, float2, int>> Render(Actor self)
{
var mobile = self.traits.Get<Mobile>();
var unit = self.traits.Get<Unit>();
var attack = self.traits.WithInterface<AttackBase>().FirstOrDefault();
yield return Util.Centered(self, anim.Image, self.CenterLocation);
yield return Util.Centered(self, turretAnim.Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, attack.primaryRecoil));
+ Util.GetTurretPosition(self, unit, self.unitInfo.PrimaryOffset, attack.primaryRecoil));
if (self.unitInfo.SecondaryOffset != null)
yield return Util.Centered(self, turretAnim.Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.SecondaryOffset, attack.secondaryRecoil));
+ Util.GetTurretPosition(self, unit, self.unitInfo.SecondaryOffset, attack.secondaryRecoil));
if (muzzleFlash != null && attack.primaryRecoil > 0)
yield return Util.Centered(self, muzzleFlash.Image, self.CenterLocation
+ Util.GetTurretPosition(self, self.unitInfo.PrimaryOffset, attack.primaryRecoil));
+ Util.GetTurretPosition(self, unit, self.unitInfo.PrimaryOffset, attack.primaryRecoil));
}
public override void Tick(Actor self)

View File

@@ -16,7 +16,7 @@ namespace OpenRa.Game.Traits
public void Tick( Actor self )
{
var df = desiredFacing ?? ( self.traits.Contains<Mobile>() ? self.traits.Get<Mobile>().facing : turretFacing );
var df = desiredFacing ?? ( self.traits.Contains<Unit>() ? self.traits.Get<Unit>().Facing : turretFacing );
Util.TickFacing( ref turretFacing, df, self.unitInfo.ROT );
}
}

14
OpenRa.Game/Traits/Unit.cs Executable file
View File

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

View File

@@ -86,12 +86,12 @@ namespace OpenRa.Game.Traits
return RotateVectorByFacing(new float2(0, recoil * self.unitInfo.Recoil), quantizedFacing, .7f);
}
public static float2 GetTurretPosition(Actor self, int[] offset, float recoil)
public static float2 GetTurretPosition(Actor self, Unit unit, int[] offset, float recoil)
{
var ru = self.traits.WithInterface<RenderUnit>().FirstOrDefault();
if (ru == null) return int2.Zero; /* things that don't have a rotating base don't need the turrets repositioned */
if( unit == null ) return int2.Zero; /* things that don't have a rotating base don't need the turrets repositioned */
var bodyFacing = self.traits.Contains<Mobile>() ? self.traits.Get<Mobile>().facing : self.traits.Get<Helicopter>().facing;
var ru = self.traits.WithInterface<RenderUnit>().FirstOrDefault();
var bodyFacing = unit.Facing;
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))

View File

@@ -175,4 +175,4 @@ Image=FCOM
TRUK
[TRUK]
Traits=Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit

View File

@@ -15,52 +15,52 @@ MNLY
[V2RL]
Description=V2 Rocket
Traits=Mobile, RenderUnit, AttackBase
Traits=Unit, Mobile, AttackBase, RenderUnit
[1TNK]
Description=Light Tank
Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted
Recoil=2;
[2TNK]
Description=Medium Tank
Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted
Recoil=3;
[3TNK]
Description=Heavy Tank
Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted
Recoil=3;
[4TNK]
Description=Mammoth Tank
Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted
[MRJ]
Description=Radar Jammer
Traits=Mobile, RenderUnitTurreted
Traits=Unit, Mobile, RenderUnitTurreted
PrimaryOffset=0,4,0,-6
[MGG]
Description=Mobile Gap Generator
Traits=Mobile, RenderUnitTurreted
Traits=Unit, Mobile, RenderUnitTurreted
PrimaryOffset=0,6,0,-3
[ARTY]
Description=Artillery
Traits=Mobile, RenderUnit, AttackBase
Traits=Unit, Mobile, AttackBase, RenderUnit
[HARV]
Description=Ore Truck
Traits=Harvester, Mobile, RenderUnit
Traits=Unit, Mobile, Harvester, RenderUnit
[MCV]
Description=Mobile Construction Vehicle
Traits=Mobile, McvDeploy, RenderUnit
Traits=Unit, Mobile, McvDeploy, RenderUnit
[JEEP]
Description=Ranger
Traits=Mobile, Turreted, AttackTurreted, RenderUnitTurreted
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted
PrimaryOffset=0,0,0,-2
MuzzleFlash=yes
[APC]
Description=Armored Personnel Carrier
Traits=Mobile, AttackBase, RenderUnitMuzzleFlash
Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash
PrimaryOffset=0,0,0,-4
MuzzleFlash=yes
[MNLY]
Description=Minelayer
Traits=Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit
@@ -77,29 +77,29 @@ PT
Description=Submarine
WaterBound=yes
BuiltAt=spen
Traits=Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit
[DD]
Description=Destroyer
WaterBound=yes
BuiltAt=syrd
Traits=Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit
[CA]
Description=Cruiser
WaterBound=yes
BuiltAt=syrd
Traits=Mobile, Turreted, RenderUnitTurreted, AttackTurreted
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted
PrimaryOffset=0,17,0,-2
SecondaryOffset=0,-17,0,-2
Recoil=3
[LST]
Description=Transport
WaterBound=yes
Traits=Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit
[PT]
Description=Gunboat
WaterBound=yes
BuiltAt=syrd
Traits=Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit
@@ -118,27 +118,27 @@ HIND
[MIG]
Description=Mig Attack Plane
BuiltAt=afld
Traits=Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit
[YAK]
Description=Yak Attack Plane
BuiltAt=afld
Traits=Mobile, RenderUnit
Traits=Unit, Mobile, RenderUnit
[TRAN]
Description=Transport Helicopter
PrimaryOffset=0,14,0,-4
SecondaryOffset=0,-14,0,-2
BuiltAt=hpad
Traits=Helicopter, RenderUnitRotor
Traits=Unit, Helicopter, RenderUnitRotor
SecondaryAnim=rotor2
[HELI]
Description=Longbow
BuiltAt=hpad
Traits=Helicopter, RenderUnitRotor
Traits=Unit, Helicopter, RenderUnitRotor
PrimaryOffset=0,0,0,-2
[HIND]
Description=Hind
BuiltAt=hpad
Traits=Helicopter, RenderUnitRotor
Traits=Unit, Helicopter, RenderUnitRotor
@@ -399,19 +399,19 @@ Description=Attack Dog
BuiltAt=KENN
[E1]
Description=Rifle Infantry
Traits=InfantrySquad,Mobile
Traits=Unit, Mobile, InfantrySquad
SquadSize=3
[E2]
Description=Grenadier
[E3]
Description=Rocket Soldier
Traits=InfantrySquad,Mobile
Traits=Unit, Mobile, InfantrySquad
SquadSize=2
[E4]
Description=Flamethrower
[E6]
Description=Engineer
Traits=InfantrySquad,Mobile
Traits=Unit, Mobile, InfantrySquad
[SPY]
Description=Spy
[THF]
@@ -505,4 +505,4 @@ ImpactSound=kaboom25
WaterImpactSound=splash9
[General]
OreChance=.02
OreChance=.02