move Combat, AttackBase, and associates into mod

This commit is contained in:
Bob
2010-07-08 16:32:25 +12:00
parent bd74b29ea3
commit 281d013c3b
20 changed files with 95 additions and 91 deletions

View File

@@ -1,198 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Linq;
using OpenRA.Effects;
using OpenRA.GameRules;
using OpenRA.Traits;
using OpenRA.FileFormats;
namespace OpenRA
{
public static class Combat /* some utility bits that are shared between various things */
{
static string GetImpactSound(WarheadInfo warhead, bool isWater)
{
if (isWater && warhead.WaterImpactSound != null)
return warhead.WaterImpactSound + ".aud";
if (warhead.ImpactSound != null)
return warhead.ImpactSound + ".aud";
return null;
}
public static void DoImpact(WarheadInfo warhead, ProjectileArgs args)
{
var world = args.firedBy.World;
var targetTile = Util.CellContaining(args.dest);
if (!world.Map.IsInMap(targetTile))
return;
var isWater = world.GetTerrainInfo(targetTile).IsWater;
var explosionType = isWater ? warhead.WaterExplosion : warhead.Explosion;
if (explosionType != null)
world.AddFrameEndTask(
w => w.Add(new Explosion(w, args.dest, explosionType, isWater)));
Sound.Play(GetImpactSound(warhead, isWater), args.dest);
if (warhead.SmudgeType != null)
{
var smudgeLayer = world.WorldActor.traits.WithInterface<SmudgeLayer>()
.FirstOrDefault(x => x.Info.Type == warhead.SmudgeType);
if (smudgeLayer == null)
throw new NotImplementedException("Unknown smudge type `{0}`".F(warhead.SmudgeType));
if (warhead.Size[0] > 0)
{
var smudgeCells = world.FindTilesInCircle(targetTile, warhead.Size[0])
.Except(world.FindTilesInCircle(targetTile, warhead.Size[1]));
foreach (var sc in smudgeCells)
smudgeLayer.AddSmudge(sc);
}
else
smudgeLayer.AddSmudge(targetTile);
}
if (warhead.Ore)
world.WorldActor.traits.Get<ResourceLayer>().Destroy(targetTile);
var firepowerModifier = args.firedBy.traits
.WithInterface<IFirepowerModifier>()
.Select(a => a.GetFirepowerModifier())
.Product();
switch (warhead.DamageModel)
{
case DamageModel.Normal:
{
var maxSpread = warhead.Spread * (float)Math.Log(Math.Abs(warhead.Damage), 2);
var hitActors = world.FindUnitsInCircle(args.dest, maxSpread);
foreach (var victim in hitActors)
{
var damage = (int)GetDamageToInflict(victim, args, warhead, firepowerModifier);
victim.InflictDamage(args.firedBy, damage, warhead);
}
} break;
case DamageModel.PerCell:
{
foreach (var t in world.FindTilesInCircle(targetTile, warhead.Size[0]))
foreach (var unit in world.FindUnits(Game.CellSize * t, Game.CellSize * (t + new float2(1,1))))
unit.InflictDamage(args.firedBy,
(int)(warhead.Damage * warhead.EffectivenessAgainst(
unit.Info.Traits.Get<OwnedActorInfo>().Armor)), warhead);
} break;
}
}
public static void DoImpacts(ProjectileArgs args)
{
foreach (var warhead in args.weapon.Warheads)
{
Action a = () => DoImpact(warhead, args);
if (warhead.Delay > 0)
args.firedBy.World.AddFrameEndTask(
w => w.Add(new DelayedAction(warhead.Delay, a)));
else
a();
}
}
public static void DoExplosion(Actor attacker, string weapontype, int2 location, int altitude)
{
var args = new ProjectileArgs
{
src = location,
dest = location,
srcAltitude = altitude,
destAltitude = altitude,
firedBy = attacker,
target = null,
weapon = Rules.Weapons[ weapontype.ToLowerInvariant() ],
facing = 0
};
DoImpacts(args);
}
static readonly float[] falloff =
{
1f, 0.3678795f, 0.1353353f, 0.04978707f,
0.01831564f, 0.006737947f, 0.002478752f, 0.000911882f
};
static float GetDamageFalloff(float x)
{
var u = (int)x;
if (u >= falloff.Length - 1) return 0;
var t = x - u;
return (falloff[u] * (1 - t)) + (falloff[u + 1] * t);
}
static float GetDamageToInflict(Actor target, ProjectileArgs args, WarheadInfo warhead, float modifier)
{
// don't hit air units with splash from ground explosions, etc
if (!WeaponValidForTarget(args.weapon, target)) return 0f;
var selectable = target.Info.Traits.GetOrDefault<SelectableInfo>();
var radius = selectable != null ? selectable.Radius : 0;
var distance = (int)Math.Max(0, (target.CenterLocation - args.dest).Length - radius);
var falloff = (float)GetDamageFalloff(distance / warhead.Spread);
var rawDamage = (float)(warhead.Damage * modifier * falloff);
var multiplier = (float)warhead.EffectivenessAgainst(target.Info.Traits.Get<OwnedActorInfo>().Armor);
return (float)(rawDamage * multiplier);
}
public static bool WeaponValidForTarget(WeaponInfo weapon, Actor target)
{
var ownedInfo = target.Info.Traits.GetOrDefault<OwnedActorInfo>();
if (!weapon.ValidTargets.Contains(ownedInfo.TargetType))
return false;
if (weapon.Warheads.All( w => w.EffectivenessAgainst(ownedInfo.Armor) <= 0))
return false;
if (weapon.Underwater && !ownedInfo.WaterBound)
return false;
return true;
}
public static bool HasAnyValidWeapons(Actor self, Actor target)
{
var info = self.Info.Traits.Get<AttackBaseInfo>();
if (info.PrimaryWeapon != null &&
WeaponValidForTarget(self.GetPrimaryWeapon(), target)) return true;
if (info.SecondaryWeapon != null &&
WeaponValidForTarget(self.GetSecondaryWeapon(), target)) return true;
return false;
}
}
}

View File

@@ -24,7 +24,7 @@ using OpenRA.Traits;
namespace OpenRA.Effects
{
class FlashTarget : IEffect
public class FlashTarget : IEffect
{
Actor target;
int remainingTicks = 4;

View File

@@ -21,9 +21,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Traits;
using OpenRA.Support;
using OpenRA.Traits;
namespace OpenRA
{
@@ -45,28 +44,6 @@ namespace OpenRA
return xs.Aggregate(1f, (a, x) => a * x);
}
public static WeaponInfo GetPrimaryWeapon(this Actor self)
{
var info = self.Info.Traits.GetOrDefault<AttackBaseInfo>();
if (info == null) return null;
var weapon = info.PrimaryWeapon;
if (weapon == null) return null;
return Rules.Weapons[weapon.ToLowerInvariant()];
}
public static WeaponInfo GetSecondaryWeapon(this Actor self)
{
var info = self.Info.Traits.GetOrDefault<AttackBaseInfo>();
if (info == null) return null;
var weapon = info.SecondaryWeapon;
if (weapon == null) return null;
return Rules.Weapons[weapon.ToLowerInvariant()];
}
public static int GetMaxHP(this Actor self)
{
var oai = self.Info.Traits.GetOrDefault<OwnedActorInfo>();

View File

@@ -89,7 +89,6 @@
<Compile Include="Traits\Modifiers\HiddenUnderFog.cs" />
<Compile Include="Traits\Render\RenderBuilding.cs" />
<Compile Include="Traits\Render\RenderBuildingTurreted.cs" />
<Compile Include="Traits\Render\RenderUnitTurreted.cs" />
<Compile Include="Traits\World\Shroud.cs" />
<Compile Include="Widgets\Delegates\ConnectionDialogsDelegate.cs" />
<Compile Include="Widgets\Delegates\CreateServerMenuDelegate.cs" />
@@ -102,7 +101,6 @@
<Compile Include="Widgets\MapPreviewWidget.cs" />
<Compile Include="Widgets\PostGameWidget.cs" />
<Compile Include="Widgets\WidgetUtils.cs" />
<Compile Include="Combat.cs" />
<Compile Include="Effects\DelayedAction.cs" />
<Compile Include="Effects\FlashTarget.cs" />
<Compile Include="Effects\MoveFlash.cs" />
@@ -136,7 +134,6 @@
<Compile Include="Traits\World\SpatialBins.cs" />
<Compile Include="Traits\World\ChoosePaletteOnSelect.cs" />
<Compile Include="Traits\World\Country.cs" />
<Compile Include="Traits\Activities\Attack.cs" />
<Compile Include="Traits\Activities\TransformIntoActor.cs" />
<Compile Include="Actor.cs" />
<Compile Include="Controller.cs" />
@@ -175,7 +172,6 @@
<Compile Include="Graphics\TerrainRenderer.cs" />
<Compile Include="Traits\Activities\Move.cs" />
<Compile Include="Traits\Activities\Turn.cs" />
<Compile Include="Traits\AttackBase.cs" />
<Compile Include="Traits\Buildable.cs" />
<Compile Include="Traits\Building.cs" />
<Compile Include="Traits\World\BuildingInfluence.cs" />
@@ -235,7 +231,6 @@
<Compile Include="Traits\SharesCell.cs" />
<Compile Include="Traits\World\AircraftInfluence.cs" />
<Compile Include="Traits\World\HazardLayer.cs" />
<Compile Include="Traits\Hazardous.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\OpenRA.FileFormats\OpenRA.FileFormats.csproj">

View File

@@ -52,9 +52,9 @@ namespace OpenRA.Orders
world.WorldRenderer.DrawSelectionBox(a, Color.White, true);
if (a.Owner == world.LocalPlayer)
{
if (a.traits.Contains<RenderRangeCircle>())
world.WorldRenderer.DrawRangeCircle(Color.FromArgb(128, Color.Yellow),
a.CenterLocation, (int)a.GetPrimaryWeapon().Range);
//if (a.traits.Contains<RenderRangeCircle>())
// world.WorldRenderer.DrawRangeCircle(Color.FromArgb(128, Color.Yellow),
// a.CenterLocation, (int)a.GetPrimaryWeapon().Range);
if (a.traits.Contains<DetectCloaked>())
world.WorldRenderer.DrawRangeCircle(Color.FromArgb(128, Color.LimeGreen),

View File

@@ -1,69 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
namespace OpenRA.Traits.Activities
{
/* non-turreted attack */
public class Attack : IActivity
{
Actor Target;
int Range;
public Attack(Actor target, int range)
{
Target = target;
Range = range;
}
public IActivity NextActivity { get; set; }
public IActivity Tick( Actor self )
{
var unit = self.traits.Get<Unit>();
if (Target == null || Target.IsDead)
return NextActivity;
if ((Target.Location - self.Location).LengthSquared >= Range * Range)
return new Move( Target, Range ) { NextActivity = this };
var desiredFacing = Util.GetFacing((Target.Location - self.Location).ToFloat2(), 0);
var renderUnit = self.traits.GetOrDefault<RenderUnit>();
var numDirs = (renderUnit != null)
? renderUnit.anim.CurrentSequence.Facings : 8;
if (Util.QuantizeFacing(unit.Facing, numDirs)
!= Util.QuantizeFacing(desiredFacing, numDirs))
{
return new Turn( desiredFacing ) { NextActivity = this };
}
var attack = self.traits.Get<AttackBase>();
attack.target = Target;
attack.DoAttack(self);
return this;
}
public void Cancel(Actor self)
{
Target = null;
}
}
}

View File

@@ -1,264 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Effects;
using OpenRA.FileFormats;
using OpenRA.GameRules;
namespace OpenRA.Traits
{
public class AttackBaseInfo : ITraitInfo
{
[WeaponReference]
public readonly string PrimaryWeapon = null;
[WeaponReference]
public readonly string SecondaryWeapon = null;
public readonly int Recoil = 0;
public readonly int[] PrimaryLocalOffset = { };
public readonly int[] SecondaryLocalOffset = { };
public readonly int[] PrimaryOffset = { 0, 0 };
public readonly int[] SecondaryOffset = null;
public readonly bool MuzzleFlash = false;
public readonly int FireDelay = 0;
public virtual object Create(ActorInitializer init) { return new AttackBase(init.self); }
}
public class AttackBase : IIssueOrder, IResolveOrder, ITick, IExplodeModifier
{
[Sync] public Actor target;
// time (in frames) until each weapon can fire again.
[Sync]
protected int primaryFireDelay = 0;
[Sync]
protected int secondaryFireDelay = 0;
int primaryBurst;
int secondaryBurst;
public float primaryRecoil = 0.0f, secondaryRecoil = 0.0f;
public AttackBase(Actor self)
{
var primaryWeapon = self.GetPrimaryWeapon();
var secondaryWeapon = self.GetSecondaryWeapon();
primaryBurst = primaryWeapon != null ? primaryWeapon.Burst : 1;
secondaryBurst = secondaryWeapon != null ? secondaryWeapon.Burst : 1;
}
protected virtual bool CanAttack(Actor self)
{
if (target == null) return false;
if ((primaryFireDelay > 0) && (secondaryFireDelay > 0)) return false;
if (self.traits.WithInterface<IDisable>().Any(d => d.Disabled)) return false;
return true;
}
public bool ShouldExplode(Actor self) { return !IsReloading(); }
public bool IsReloading()
{
return (primaryFireDelay > 0) || (secondaryFireDelay > 0);
}
List<Pair<int, Action>> delayedActions = new List<Pair<int, Action>>();
public virtual void Tick(Actor self)
{
if (primaryFireDelay > 0) --primaryFireDelay;
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. */
for (var i = 0; i < delayedActions.Count; i++)
{
var x = delayedActions[i];
if (--x.First <= 0)
x.Second();
delayedActions[i] = x;
}
delayedActions.RemoveAll(a => a.First <= 0);
}
void ScheduleDelayedAction(int t, Action a)
{
if (t > 0)
delayedActions.Add(Pair.New(t, a));
else
a();
}
public void DoAttack(Actor self)
{
if( !CanAttack( self ) ) return;
var unit = self.traits.GetOrDefault<Unit>();
var info = self.Info.Traits.Get<AttackBaseInfo>();
if (info.PrimaryWeapon != null && CheckFire(self, unit, info.PrimaryWeapon, ref primaryFireDelay,
info.PrimaryOffset, ref primaryBurst, info.PrimaryLocalOffset))
{
primaryRecoil = 1;
return;
}
if (info.SecondaryWeapon != null && CheckFire(self, unit, info.SecondaryWeapon, ref secondaryFireDelay,
info.SecondaryOffset ?? info.PrimaryOffset, ref secondaryBurst, info.SecondaryLocalOffset))
{
if (info.SecondaryOffset != null) secondaryRecoil = 1;
else primaryRecoil = 1;
return;
}
}
bool CheckFire(Actor self, Unit unit, string weaponName, ref int fireDelay, int[] offset, ref int burst, int[] localOffset)
{
if (fireDelay > 0) return false;
var limitedAmmo = self.traits.GetOrDefault<LimitedAmmo>();
if (limitedAmmo != null && !limitedAmmo.HasAmmo())
return false;
var weapon = Rules.Weapons[weaponName.ToLowerInvariant()];
if (weapon.Range * weapon.Range < (target.Location - self.Location).LengthSquared) return false;
if (!Combat.WeaponValidForTarget(weapon, target)) return false;
var numOffsets = (localOffset.Length + 2) / 3;
if (numOffsets == 0) numOffsets = 1;
var localOffsetForShot = burst % numOffsets;
var thisLocalOffset = localOffset.Skip(3 * localOffsetForShot).Take(3).ToArray();
var fireOffset = new[] {
offset.ElementAtOrDefault(0) + thisLocalOffset.ElementAtOrDefault(0),
offset.ElementAtOrDefault(1) + thisLocalOffset.ElementAtOrDefault(1),
offset.ElementAtOrDefault(2),
offset.ElementAtOrDefault(3) };
if (--burst > 0)
fireDelay = weapon.BurstDelay;
else
{
fireDelay = weapon.ROF;
burst = weapon.Burst;
}
var destUnit = target.traits.GetOrDefault<Unit>();
var args = new ProjectileArgs
{
weapon = Rules.Weapons[weaponName.ToLowerInvariant()],
firedBy = self,
target = target,
src = self.CenterLocation.ToInt2() + Util.GetTurretPosition(self, unit, fireOffset, 0f).ToInt2(),
srcAltitude = unit != null ? unit.Altitude : 0,
dest = target.CenterLocation.ToInt2(),
destAltitude = destUnit != null ? destUnit.Altitude : 0,
facing = thisLocalOffset.ElementAtOrDefault(2) +
(self.traits.Contains<Turreted>() ? self.traits.Get<Turreted>().turretFacing :
unit != null ? unit.Facing : Util.GetFacing(target.CenterLocation - self.CenterLocation, 0)),
};
ScheduleDelayedAction( FireDelay( self, self.Info.Traits.Get<AttackBaseInfo>() ), () =>
{
if (args.weapon.Projectile != null)
{
var projectile = args.weapon.Projectile.Create(args);
if (projectile != null)
self.World.Add(projectile);
if (!string.IsNullOrEmpty(args.weapon.Report))
Sound.Play(args.weapon.Report + ".aud", self.CenterLocation);
}
});
foreach (var na in self.traits.WithInterface<INotifyAttack>())
na.Attacking(self);
return true;
}
public virtual int FireDelay( Actor self, AttackBaseInfo info )
{
return info.FireDelay;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button == MouseButton.Left || underCursor == null || underCursor.Owner == null) return null;
if (self == underCursor) return null;
var isHeal = self.GetPrimaryWeapon().Warheads.First().Damage < 0;
var forceFire = mi.Modifiers.HasModifier(Modifiers.Ctrl);
if (isHeal)
{
if (underCursor.Owner == null)
return null;
if (self.Owner.Stances[ underCursor.Owner ] != Stance.Ally && !forceFire)
return null;
if (underCursor.Health >= underCursor.GetMaxHP())
return null; // don't allow healing of fully-healed stuff!
}
else
if ((self.Owner.Stances[ underCursor.Owner ] != Stance.Enemy) && !forceFire)
return null;
if (!Combat.HasAnyValidWeapons(self, underCursor)) return null;
return new Order(isHeal ? "Heal" : "Attack", self, underCursor);
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Attack" || order.OrderString == "Heal")
{
self.CancelActivity();
QueueAttack(self, order);
if (self.Owner == self.World.LocalPlayer)
self.World.AddFrameEndTask(w => w.Add(new FlashTarget(order.TargetActor)));
}
else
target = null;
}
protected virtual void QueueAttack(Actor self, Order order)
{
/* todo: choose the appropriate weapon, when only one works against this target */
var weapon = self.GetPrimaryWeapon() ?? self.GetSecondaryWeapon();
self.QueueActivity(new Activities.Attack(order.TargetActor,
Math.Max(0, (int)weapon.Range)));
}
}
}

View File

@@ -1,52 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using System.Linq;
using System.Collections.Generic;
namespace OpenRA.Traits
{
class AntiAirInfo : ITraitInfo
{
public readonly float Badness = 1000f;
public object Create( ActorInitializer init ) { return new AntiAir( init.self ); }
}
class AntiAir : IProvideHazard
{
public AntiAir(Actor self)
{
self.World.WorldActor.traits.Get<HazardLayer>().Add( self, this );
}
public IEnumerable<HazardLayer.Hazard> HazardCells(Actor self)
{
var info = self.Info.Traits.Get<AntiAirInfo>();
return self.World.FindTilesInCircle(self.Location, (int)self.GetPrimaryWeapon().Range).Select(
t => new HazardLayer.Hazard(){location = t, type = "antiair", intensity = info.Badness});
}
}
class AvoidsAAInfo : TraitInfo<AvoidsAA> {}
class AvoidsAA : IAvoidHazard
{
public string Type { get { return "antiair"; } }
}
}

View File

@@ -1,67 +0,0 @@
#region Copyright & License Information
/*
* Copyright 2007,2009,2010 Chris Forbes, Robert Pepperell, Matthew Bowra-Dean, Paul Chote, Alli Witheford.
* This file is part of OpenRA.
*
* OpenRA is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenRA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenRA. If not, see <http://www.gnu.org/licenses/>.
*/
#endregion
using OpenRA.Graphics;
namespace OpenRA.Traits
{
class RenderUnitTurretedInfo : RenderUnitInfo
{
public override object Create(ActorInitializer init) { return new RenderUnitTurreted(init.self); }
}
class RenderUnitTurreted : RenderUnit
{
public RenderUnitTurreted(Actor self)
: base(self)
{
var unit = self.traits.Get<Unit>();
var turreted = self.traits.Get<Turreted>();
var attack = self.traits.GetOrDefault<AttackBase>();
var attackInfo = self.Info.Traits.Get<AttackBaseInfo>();
var turretAnim = new Animation(GetImage(self), () => turreted.turretFacing );
turretAnim.Play( "turret" );
if( attackInfo.PrimaryOffset != null )
anims.Add("turret_1", new AnimationWithOffset(
turretAnim,
() => Util.GetTurretPosition(self, unit, attackInfo.PrimaryOffset, attack.primaryRecoil),
null) { ZOffset = 1 });
if (attackInfo.SecondaryOffset != null)
anims.Add("turret_2", new AnimationWithOffset(
turretAnim,
() => Util.GetTurretPosition(self, unit, attackInfo.SecondaryOffset, attack.secondaryRecoil),
null) { ZOffset = 1 });
if( attackInfo.MuzzleFlash )
{
var muzzleFlash = new Animation( GetImage(self), () => self.traits.Get<Turreted>().turretFacing );
muzzleFlash.PlayFetchIndex( "muzzle",
() => (int)( attack.primaryRecoil * 5.9f ) ); /* hack: recoil can be 1.0f, but don't overflow into next anim */
anims.Add( "muzzle_flash", new AnimationWithOffset(
muzzleFlash,
() => Util.GetTurretPosition(self, unit, attackInfo.PrimaryOffset, attack.primaryRecoil),
() => attack.primaryRecoil <= 0 ) );
}
}
}
}

View File

@@ -21,7 +21,6 @@
using System;
using System.Drawing;
using System.Linq;
using OpenRA.GameRules;
using OpenRA.Graphics;
namespace OpenRA.Traits
@@ -82,7 +81,7 @@ namespace OpenRA.Traits
return a / step;
}
static float2 RotateVectorByFacing(float2 v, int facing, float ecc)
public static float2 RotateVectorByFacing(float2 v, int facing, float ecc)
{
var angle = (facing / 256f) * (2 * (float)Math.PI);
var sinAngle = (float)Math.Sin(angle);
@@ -93,17 +92,6 @@ namespace OpenRA.Traits
ecc * (cosAngle * v.Y - sinAngle * v.X));
}
static float2 GetRecoil(Actor self, float recoil)
{
var abInfo = self.Info.Traits.GetOrDefault<AttackBaseInfo>();
if (abInfo == null || abInfo.Recoil == 0) return float2.Zero;
var rut = self.traits.GetOrDefault<RenderUnitTurreted>();
if (rut == null) return float2.Zero;
var facing = self.traits.Get<Turreted>().turretFacing;
return RotateVectorByFacing(new float2(0, recoil * self.Info.Traits.Get<AttackBaseInfo>().Recoil), facing, .7f);
}
public static float2 CenterOfCell(int2 loc)
{
return new float2(12, 12) + Game.CellSize * (float2)loc;
@@ -114,19 +102,6 @@ namespace OpenRA.Traits
return 0.5f * (CenterOfCell(from) + CenterOfCell(to));
}
public static float2 GetTurretPosition(Actor self, Unit unit, int[] offset, float recoil)
{
if( unit == null ) return offset.AbsOffset(); /* things that don't have a rotating base don't need the turrets repositioned */
var ru = self.traits.GetOrDefault<RenderUnit>();
var numDirs = (ru != null) ? ru.anim.CurrentSequence.Facings : 8;
var bodyFacing = unit.Facing;
var quantizedFacing = QuantizeFacing(bodyFacing, numDirs) * (256 / numDirs);
return (RotateVectorByFacing(offset.RelOffset(), quantizedFacing, .7f) + GetRecoil(self, recoil))
+ offset.AbsOffset();
}
public static int2 AsInt2(this int[] xs) { return new int2(xs[0], xs[1]); }
public static float2 RelOffset(this int[] offset) { return new float2(offset[0], offset[1]); }
public static float2 AbsOffset(this int[] offset) { return new float2(offset.ElementAtOrDefault(2), offset.ElementAtOrDefault(3)); }
@@ -144,12 +119,6 @@ namespace OpenRA.Traits
(next, a) => { a.NextActivity = next; return a; });
}
public static float GetMaximumRange(Actor self)
{
return new[] { self.GetPrimaryWeapon(), self.GetSecondaryWeapon() }
.Where(w => w != null).Max(w => w.Range);
}
public static Color ArrayToColor(int[] x) { return Color.FromArgb(x[0], x[1], x[2]); }
public static int2 CellContaining(float2 pos) { return (1f / Game.CellSize * pos).ToInt2(); }

View File

@@ -26,7 +26,7 @@ using OpenRA.FileFormats;
namespace OpenRA.Traits
{
class SmudgeLayerInfo : ITraitInfo
public class SmudgeLayerInfo : ITraitInfo
{
public readonly string Type = "Scorch";
public readonly string[] Types = {"sc1", "sc2", "sc3", "sc4", "sc5", "sc6"};
@@ -34,7 +34,7 @@ namespace OpenRA.Traits
public object Create(ActorInitializer init) { return new SmudgeLayer(this); }
}
class SmudgeLayer: IRenderOverlay, ILoadWorldHook
public class SmudgeLayer: IRenderOverlay, ILoadWorldHook
{
public SmudgeLayerInfo Info;
SpriteRenderer spriteRenderer;