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

This commit is contained in:
Caleb Anderson
2010-01-01 20:00:57 -06:00
38 changed files with 408 additions and 141 deletions

View File

@@ -2,13 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
using System.Windows.Forms;
using IjwFramework.Collections;
using IjwFramework.Types; using IjwFramework.Types;
using OpenRa.Game.Graphics;
using OpenRa.Game.Support;
using OpenRa.Game.GameRules; using OpenRa.Game.GameRules;
using OpenRa.Game.Graphics;
using OpenRa.Game.Orders;
using OpenRa.Game.Support;
namespace OpenRa.Game namespace OpenRa.Game
{ {
@@ -363,7 +361,7 @@ namespace OpenRa.Game
if (producing.Done) if (producing.Done)
{ {
if (group == "Building" || group == "Defense") if (group == "Building" || group == "Defense")
Game.controller.orderGenerator = new PlaceBuilding(player.PlayerActor, item); Game.controller.orderGenerator = new PlaceBuildingOrderGenerator(player.PlayerActor, item);
return; return;
} }

View File

@@ -5,6 +5,7 @@ using IjwFramework.Collections;
using IjwFramework.Types; using IjwFramework.Types;
using OpenRa.Game.GameRules; using OpenRa.Game.GameRules;
using OpenRa.Game.Graphics; using OpenRa.Game.Graphics;
using OpenRa.Game.Orders;
using OpenRa.Game.Traits; using OpenRa.Game.Traits;
namespace OpenRa.Game namespace OpenRa.Game
@@ -18,6 +19,12 @@ namespace OpenRa.Game
public Controller(Func<Modifiers> getModifierKeys) public Controller(Func<Modifiers> getModifierKeys)
{ {
GetModifierKeys = getModifierKeys; GetModifierKeys = getModifierKeys;
CancelInputMode();
}
public void CancelInputMode()
{
orderGenerator = new UnitOrderGenerator(new Actor[] { });
} }
List<Order> recentOrders = new List<Order>(); List<Order> recentOrders = new List<Order>();
@@ -62,7 +69,7 @@ namespace OpenRa.Game
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down) if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Down)
{ {
if (!(orderGenerator is PlaceBuilding)) if (!(orderGenerator is PlaceBuildingOrderGenerator))
dragStart = dragEnd = xy; dragStart = dragEnd = xy;
ApplyOrders(xy, mi); ApplyOrders(xy, mi);
} }
@@ -72,7 +79,7 @@ namespace OpenRa.Game
if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Up) if (mi.Button == MouseButton.Left && mi.Event == MouseInputEvent.Up)
{ {
if (!(orderGenerator is PlaceBuilding)) if (orderGenerator is UnitOrderGenerator)
{ {
var newSelection = Game.SelectActorsInBox(Game.CellSize * dragStart, Game.CellSize * xy); var newSelection = Game.SelectActorsInBox(Game.CellSize * dragStart, Game.CellSize * xy);
CombineSelection(newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy); CombineSelection(newSelection, mi.Modifiers.HasModifier(Modifiers.Shift), dragStart == xy);
@@ -127,13 +134,22 @@ namespace OpenRa.Game
public Cursor ChooseCursor() public Cursor ChooseCursor()
{ {
var mods = GetModifierKeys(); var mods = GetModifierKeys();
var c = (orderGenerator is UnitOrderGenerator) ? orderGenerator.Order(dragEnd.ToInt2(),
new MouseInput { Location = (Game.CellSize * dragEnd - Game.viewport.Location).ToInt2(), Button = MouseButton.Right, Modifiers = mods }) var mi = new MouseInput {
Location = (Game.CellSize * dragEnd - Game.viewport.Location).ToInt2(),
Button = MouseButton.Right,
Modifiers = mods,
IsFake = true,
};
var c = orderGenerator.Order(dragEnd.ToInt2(), mi)
.Where(o => o.Validate()) .Where(o => o.Validate())
.Select(o => CursorForOrderString(o.OrderString, o.Subject, o.TargetLocation)) .Select(o => CursorForOrderString(o.OrderString, o.Subject, o.TargetLocation))
.FirstOrDefault(a => a != null) : null; .FirstOrDefault(a => a != null);
return c ?? (Game.SelectActorsInBox(Game.CellSize * dragEnd, Game.CellSize * dragEnd).Any() ? Cursor.Select : Cursor.Default); return c ??
(Game.SelectActorsInBox(Game.CellSize * dragEnd, Game.CellSize * dragEnd).Any()
? Cursor.Select : Cursor.Default);
} }
Cursor CursorForOrderString( string s, Actor a, int2 location ) Cursor CursorForOrderString( string s, Actor a, int2 location )
@@ -162,8 +178,12 @@ namespace OpenRa.Game
else else
return Cursor.MoveBlocked; return Cursor.MoveBlocked;
case "Enter": return Cursor.Enter; case "Enter": return Cursor.Enter;
case "Infiltrate": return Cursor.Enter;
case "Capture": return Cursor.Capture; case "Capture": return Cursor.Capture;
case "Harvest": return Cursor.Attack; // TODO: special harvest cursor? case "Harvest": return Cursor.Attack; // TODO: special harvest cursor?
case "PlaceBuilding": return Cursor.Default;
case "Sell": return Cursor.Sell;
case "NoSell": return Cursor.SellBlocked;
default: default:
return null; return null;
} }

View File

@@ -26,5 +26,7 @@ namespace OpenRa.Game
public static Cursor C4 { get { return new Cursor("c4"); } } public static Cursor C4 { get { return new Cursor("c4"); } }
public static Cursor Capture { get { return new Cursor("capture"); } } public static Cursor Capture { get { return new Cursor("capture"); } }
public static Cursor Heal { get { return new Cursor("heal"); } } public static Cursor Heal { get { return new Cursor("heal"); } }
public static Cursor Sell { get { return new Cursor("sell"); } }
public static Cursor SellBlocked { get { return new Cursor("sell-blocked"); } }
} }
} }

View File

@@ -6,6 +6,7 @@ using System.Net.Sockets;
using OpenRa.FileFormats; using OpenRa.FileFormats;
using OpenRa.Game.GameRules; using OpenRa.Game.GameRules;
using OpenRa.Game.Graphics; using OpenRa.Game.Graphics;
using OpenRa.Game.Orders;
using OpenRa.Game.Support; using OpenRa.Game.Support;
using OpenRa.Game.Traits; using OpenRa.Game.Traits;

View File

@@ -5,6 +5,7 @@ using System.Runtime.InteropServices;
using System.Windows.Forms; using System.Windows.Forms;
using OpenRa.FileFormats; using OpenRa.FileFormats;
using OpenRa.Game.Graphics; using OpenRa.Game.Graphics;
using OpenRa.Game.Orders;
namespace OpenRa.Game namespace OpenRa.Game
{ {
@@ -132,6 +133,8 @@ namespace OpenRa.Game
/* temporary hack: DO NOT LEAVE IN */ /* temporary hack: DO NOT LEAVE IN */
if (e.KeyCode == Keys.F2) if (e.KeyCode == Keys.F2)
Game.LocalPlayer = Game.players[(Game.LocalPlayer.Index + 1) % 4]; Game.LocalPlayer = Game.players[(Game.LocalPlayer.Index + 1) % 4];
if (e.KeyCode == Keys.F3)
Game.controller.orderGenerator = new SellOrderGenerator();
if (!Game.chat.isChatting) if (!Game.chat.isChatting)
if (e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) if (e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9)
@@ -173,6 +176,7 @@ namespace OpenRa.Game
public int2 Location; public int2 Location;
public MouseButton Button; public MouseButton Button;
public Modifiers Modifiers; public Modifiers Modifiers;
public bool IsFake;
} }
enum MouseInputEvent { Down, Move, Up }; enum MouseInputEvent { Down, Move, Up };

View File

@@ -93,16 +93,17 @@
<Compile Include="GameRules\TechTree.cs" /> <Compile Include="GameRules\TechTree.cs" />
<Compile Include="GameRules\VoiceInfo.cs" /> <Compile Include="GameRules\VoiceInfo.cs" />
<Compile Include="Effects\IEffect.cs" /> <Compile Include="Effects\IEffect.cs" />
<Compile Include="IOrderSource.cs" /> <Compile Include="Orders\IOrderSource.cs" />
<Compile Include="LocalOrderSource.cs" /> <Compile Include="Orders\LocalOrderSource.cs" />
<Compile Include="Effects\Missile.cs" /> <Compile Include="Effects\Missile.cs" />
<Compile Include="NetworkOrderSource.cs" /> <Compile Include="Orders\NetworkOrderSource.cs" />
<Compile Include="OrderIO.cs" /> <Compile Include="Orders\OrderIO.cs" />
<Compile Include="OrderManager.cs" /> <Compile Include="Orders\OrderManager.cs" />
<Compile Include="Orders\SellOrderGenerator.cs" />
<Compile Include="Ore.cs" /> <Compile Include="Ore.cs" />
<Compile Include="PathSearch.cs" /> <Compile Include="PathSearch.cs" />
<Compile Include="ProductionItem.cs" /> <Compile Include="ProductionItem.cs" />
<Compile Include="ReplayOrderSource.cs" /> <Compile Include="Orders\ReplayOrderSource.cs" />
<Compile Include="Smudge.cs" /> <Compile Include="Smudge.cs" />
<Compile Include="Sound.cs" /> <Compile Include="Sound.cs" />
<Compile Include="Support\Stopwatch.cs" /> <Compile Include="Support\Stopwatch.cs" />
@@ -117,6 +118,7 @@
<Compile Include="Traits\Activities\HeliAttack.cs" /> <Compile Include="Traits\Activities\HeliAttack.cs" />
<Compile Include="Traits\Activities\HeliFly.cs" /> <Compile Include="Traits\Activities\HeliFly.cs" />
<Compile Include="Traits\Activities\HeliLand.cs" /> <Compile Include="Traits\Activities\HeliLand.cs" />
<Compile Include="Traits\Activities\HeliReturn.cs" />
<Compile Include="Traits\Activities\IActivity.cs" /> <Compile Include="Traits\Activities\IActivity.cs" />
<Compile Include="Traits\Activities\DeliverOre.cs" /> <Compile Include="Traits\Activities\DeliverOre.cs" />
<Compile Include="Traits\Activities\DeployMcv.cs" /> <Compile Include="Traits\Activities\DeployMcv.cs" />
@@ -145,17 +147,18 @@
<Compile Include="Traits\Activities\Rearm.cs" /> <Compile Include="Traits\Activities\Rearm.cs" />
<Compile Include="Traits\Activities\Repair.cs" /> <Compile Include="Traits\Activities\Repair.cs" />
<Compile Include="Traits\Activities\ReturnToBase.cs" /> <Compile Include="Traits\Activities\ReturnToBase.cs" />
<Compile Include="Traits\Activities\Sell.cs" />
<Compile Include="Traits\Activities\Teleport.cs" /> <Compile Include="Traits\Activities\Teleport.cs" />
<Compile Include="BuildingInfluenceMap.cs" /> <Compile Include="BuildingInfluenceMap.cs" />
<Compile Include="IOrderGenerator.cs" /> <Compile Include="Orders\IOrderGenerator.cs" />
<Compile Include="PlaceBuilding.cs" /> <Compile Include="Orders\PlaceBuildingOrderGenerator.cs" />
<Compile Include="Player.cs" /> <Compile Include="Player.cs" />
<Compile Include="Race.cs" /> <Compile Include="Race.cs" />
<Compile Include="Graphics\Sheet.cs" /> <Compile Include="Graphics\Sheet.cs" />
<Compile Include="Support\Log.cs" /> <Compile Include="Support\Log.cs" />
<Compile Include="PathFinder.cs" /> <Compile Include="PathFinder.cs" />
<Compile Include="Graphics\Sequence.cs" /> <Compile Include="Graphics\Sequence.cs" />
<Compile Include="Order.cs" /> <Compile Include="Orders\Order.cs" />
<Compile Include="Graphics\SequenceProvider.cs" /> <Compile Include="Graphics\SequenceProvider.cs" />
<Compile Include="Graphics\SheetBuilder.cs" /> <Compile Include="Graphics\SheetBuilder.cs" />
<Compile Include="Graphics\HardwarePalette.cs" /> <Compile Include="Graphics\HardwarePalette.cs" />
@@ -198,6 +201,7 @@
<Compile Include="Traits\MineImmune.cs" /> <Compile Include="Traits\MineImmune.cs" />
<Compile Include="Traits\Minelayer.cs" /> <Compile Include="Traits\Minelayer.cs" />
<Compile Include="Traits\LimitedAmmo.cs" /> <Compile Include="Traits\LimitedAmmo.cs" />
<Compile Include="Traits\Repairable.cs" />
<Compile Include="Traits\Reservable.cs" /> <Compile Include="Traits\Reservable.cs" />
<Compile Include="Traits\SquishByTank.cs" /> <Compile Include="Traits\SquishByTank.cs" />
<Compile Include="Traits\Plane.cs" /> <Compile Include="Traits\Plane.cs" />
@@ -231,14 +235,14 @@
<Compile Include="Traits\Unit.cs" /> <Compile Include="Traits\Unit.cs" />
<Compile Include="Traits\WaterPaletteRotation.cs" /> <Compile Include="Traits\WaterPaletteRotation.cs" />
<Compile Include="Traits\WithShadow.cs" /> <Compile Include="Traits\WithShadow.cs" />
<Compile Include="UnitOrders.cs" /> <Compile Include="Orders\UnitOrders.cs" />
<Compile Include="Traits\Util.cs" /> <Compile Include="Traits\Util.cs" />
<Compile Include="UiOverlay.cs" /> <Compile Include="UiOverlay.cs" />
<Compile Include="Graphics\Util.cs" /> <Compile Include="Graphics\Util.cs" />
<Compile Include="Graphics\Vertex.cs" /> <Compile Include="Graphics\Vertex.cs" />
<Compile Include="Graphics\Viewport.cs" /> <Compile Include="Graphics\Viewport.cs" />
<Compile Include="UnitInfluenceMap.cs" /> <Compile Include="UnitInfluenceMap.cs" />
<Compile Include="UnitOrderGenerator.cs" /> <Compile Include="Orders\UnitOrderGenerator.cs" />
<Compile Include="VoicePool.cs" /> <Compile Include="VoicePool.cs" />
<Compile Include="World.cs" /> <Compile Include="World.cs" />
</ItemGroup> </ItemGroup>

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace OpenRa.Game namespace OpenRa.Game.Orders
{ {
interface IOrderSource interface IOrderSource
{ {

View File

@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
namespace OpenRa.Game namespace OpenRa.Game.Orders
{ {
class LocalOrderSource : IOrderSource class LocalOrderSource : IOrderSource
{ {

View File

@@ -4,7 +4,7 @@ using System.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Threading; using System.Threading;
namespace OpenRa.Game namespace OpenRa.Game.Orders
{ {
class NetworkOrderSource : IOrderSource class NetworkOrderSource : IOrderSource
{ {

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
namespace OpenRa.Game namespace OpenRa.Game.Orders
{ {
static class OrderIO static class OrderIO
{ {

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
namespace OpenRa.Game namespace OpenRa.Game.Orders
{ {
class OrderManager class OrderManager
{ {

View File

@@ -0,0 +1,53 @@
using System.Collections.Generic;
using OpenRa.Game.GameRules;
namespace OpenRa.Game.Orders
{
class PlaceBuildingOrderGenerator : IOrderGenerator
{
readonly Actor Producer;
readonly BuildingInfo Building;
public PlaceBuildingOrderGenerator(Actor producer, string name)
{
Producer = producer;
Building = (BuildingInfo)Rules.UnitInfo[ name ];
}
public IEnumerable<Order> Order(int2 xy, MouseInput mi)
{
if (mi.IsFake)
{
// this order is never actually issued, but it's used for choosing a cursor
yield return new Order("PlaceBuilding", Producer.Owner.PlayerActor, null, xy, Building.Name);
yield break;
}
if (mi.Button == MouseButton.Left)
{
if (!Game.CanPlaceBuilding(Building, xy, null, true)
|| !Game.IsCloseEnoughToBase(Producer.Owner, Building, xy))
{
Sound.Play("nodeply1.aud");
yield break;
}
yield return new Order("PlaceBuilding", Producer.Owner.PlayerActor, null, xy, Building.Name);
}
else
Game.controller.CancelInputMode();
}
public void Tick()
{
var producing = Producer.traits.Get<Traits.ProductionQueue>().CurrentItem( Rules.UnitCategory[ Building.Name ] );
if (producing == null || producing.Item != Building.Name || producing.RemainingTime != 0)
Game.controller.CancelInputMode();
}
public void Render()
{
Game.worldRenderer.uiOverlay.DrawBuildingGrid( Building );
}
}
}

View File

@@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
namespace OpenRa.Game namespace OpenRa.Game.Orders
{ {
class ReplayOrderSource : IOrderSource class ReplayOrderSource : IOrderSource
{ {

View File

@@ -0,0 +1,40 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRa.Game.GameRules;
using OpenRa.Game.Traits;
namespace OpenRa.Game.Orders
{
class SellOrderGenerator : IOrderGenerator
{
public IEnumerable<Order> Order(int2 xy, MouseInput mi)
{
if (!mi.IsFake && mi.Button == MouseButton.Right)
{
Game.controller.CancelInputMode();
yield break;
}
var loc = mi.Location + Game.viewport.Location;
var underCursor = Game.FindUnits(loc, loc)
.Where(a => a.Owner == Game.LocalPlayer
&& a.traits.Contains<Building>()
&& a.Info.Selectable).FirstOrDefault();
var building = underCursor != null ? underCursor.Info as BuildingInfo : null;
if (building == null || building.Unsellable)
{
yield return new Order("NoSell", Game.LocalPlayer.PlayerActor, null, int2.Zero, null);
yield break;
}
yield return new Order("Sell", underCursor, null, int2.Zero, null);
}
public void Tick() {}
public void Render() {}
}
}

View File

@@ -2,7 +2,7 @@
using System.Drawing; using System.Drawing;
using System.Linq; using System.Linq;
namespace OpenRa.Game namespace OpenRa.Game.Orders
{ {
class UnitOrderGenerator : IOrderGenerator class UnitOrderGenerator : IOrderGenerator
{ {

View File

@@ -1,11 +1,9 @@
using System; using System.Drawing;
using System.Drawing;
using System.Linq;
using OpenRa.Game.GameRules; using OpenRa.Game.GameRules;
using OpenRa.Game.Traits;
using OpenRa.Game.Graphics; using OpenRa.Game.Graphics;
using OpenRa.Game.Traits;
namespace OpenRa.Game namespace OpenRa.Game.Orders
{ {
static class UnitOrders static class UnitOrders
{ {

View File

@@ -49,7 +49,7 @@ namespace OpenRa.Game
} }
} }
Func<int2, bool> AvoidUnitsNear(int2 p, int dist) public Func<int2, bool> AvoidUnitsNear(int2 p, int dist)
{ {
return q => return q =>
p != q && p != q &&

View File

@@ -15,6 +15,7 @@ namespace OpenRa.Game
Func<int2, bool> customBlock; Func<int2, bool> customBlock;
public bool checkForBlocked; public bool checkForBlocked;
public bool ignoreTerrain; public bool ignoreTerrain;
public Actor ignoreBuilding;
public PathSearch() public PathSearch()
{ {
@@ -28,6 +29,12 @@ namespace OpenRa.Game
return this; return this;
} }
public PathSearch WithIgnoredBuilding(Actor b)
{
ignoreBuilding = b;
return this;
}
public int2 Expand( float[][ , ] passableCost ) public int2 Expand( float[][ , ] passableCost )
{ {
var p = queue.Pop(); var p = queue.Pop();
@@ -49,7 +56,8 @@ namespace OpenRa.Game
{ {
if (passableCost[(int)umt][newHere.X, newHere.Y] == float.PositiveInfinity) if (passableCost[(int)umt][newHere.X, newHere.Y] == float.PositiveInfinity)
continue; continue;
if (!Game.BuildingInfluence.CanMoveHere(newHere)) if (!Game.BuildingInfluence.CanMoveHere(newHere) &&
Game.BuildingInfluence.GetBuildingAt(newHere) != ignoreBuilding)
continue; continue;
if (Rules.Map.IsOverlaySolid(newHere)) if (Rules.Map.IsOverlaySolid(newHere))
continue; continue;

View File

@@ -1,53 +0,0 @@
using System.Collections.Generic;
using OpenRa.Game.GameRules;
namespace OpenRa.Game
{
class PlaceBuilding : IOrderGenerator
{
readonly Actor Producer;
readonly BuildingInfo Building;
public PlaceBuilding(Actor producer, string name)
{
Producer = producer;
Building = (BuildingInfo)Rules.UnitInfo[ name ];
}
public IEnumerable<Order> Order(int2 xy, MouseInput mi)
{
if( mi.Button == MouseButton.Left )
{
if (!Game.CanPlaceBuilding(Building, xy, null, true))
{
Sound.Play("nodeply1.aud");
yield break;
}
if (!Game.IsCloseEnoughToBase(Producer.Owner, Building, xy))
{
Sound.Play("nodeply1.aud");
yield break;
}
yield return new Order("PlaceBuilding", Producer.Owner.PlayerActor, null, xy, Building.Name);
}
else // rmb
{
Game.world.AddFrameEndTask( _ => { Game.controller.orderGenerator = null; } );
}
}
public void Tick()
{
var producing = Producer.traits.Get<Traits.ProductionQueue>().CurrentItem( Rules.UnitCategory[ Building.Name ] );
if( producing == null || producing.Item != Building.Name || producing.RemainingTime != 0 )
Game.world.AddFrameEndTask( _ => { Game.controller.orderGenerator = null; } );
}
public void Render()
{
Game.worldRenderer.uiOverlay.DrawBuildingGrid( Building );
}
}
}

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRa.Game.GameRules;
namespace OpenRa.Game.Traits.Activities
{
class HeliReturn : IActivity
{
public IActivity NextActivity { get; set; }
bool isCanceled;
static Actor ChooseHelipad(Actor self)
{
return Game.world.Actors.FirstOrDefault(
a => a.Info == Rules.UnitInfo["HPAD"] &&
a.Owner == self.Owner &&
!Reservable.IsReserved(a));
}
public IActivity Tick(Actor self)
{
if (isCanceled) return NextActivity;
var dest = ChooseHelipad(self);
if (dest == null)
return Util.SequenceActivities(
new Turn(self.Info.InitialFacing),
new HeliLand(true),
NextActivity);
var res = dest.traits.GetOrDefault<Reservable>();
if (res != null)
self.traits.Get<Helicopter>().reservation = res.Reserve(self);
var offset = (dest.Info as BuildingInfo).SpawnOffset;
var offsetVec = new float2(offset[0], offset[1]);
return Util.SequenceActivities(
new HeliFly(dest.CenterLocation + offsetVec),
new Turn(self.Info.InitialFacing),
new HeliLand(false),
new Rearm(),
NextActivity);
}
public void Cancel(Actor self) { isCanceled = true; NextActivity = null; }
}
}

View File

@@ -14,6 +14,7 @@ namespace OpenRa.Game.Traits.Activities
int nearEnough; int nearEnough;
public List<int2> path; public List<int2> path;
Func<Actor, Mobile, List<int2>> getPath; Func<Actor, Mobile, List<int2>> getPath;
public Actor ignoreBuilding;
MovePart move; MovePart move;
@@ -26,6 +27,18 @@ namespace OpenRa.Game.Traits.Activities
this.nearEnough = nearEnough; this.nearEnough = nearEnough;
} }
public Move(int2 destination, Actor ignoreBuilding)
{
this.getPath = (self, mobile) =>
Game.PathFinder.FindPath(
PathSearch.FromPoint( self.Location, destination, mobile.GetMovementType(), false )
.WithCustomBlocker( Game.PathFinder.AvoidUnitsNear( self.Location, 4 )).WithIgnoredBuilding( ignoreBuilding ));
this.destination = destination;
this.nearEnough = 0;
this.ignoreBuilding = ignoreBuilding;
}
public Move( Actor target, int range ) public Move( Actor target, int range )
{ {
this.getPath = ( self, mobile ) => Game.PathFinder.FindUnitPathToRange( this.getPath = ( self, mobile ) => Game.PathFinder.FindUnitPathToRange(
@@ -42,9 +55,11 @@ namespace OpenRa.Game.Traits.Activities
this.nearEnough = 0; this.nearEnough = 0;
} }
static bool CanEnterCell( int2 c, Actor self ) bool CanEnterCell( int2 c, Actor self )
{ {
if (!Game.BuildingInfluence.CanMoveHere(c)) return false; if (!Game.BuildingInfluence.CanMoveHere(c)
&& Game.BuildingInfluence.GetBuildingAt(c) != ignoreBuilding)
return false;
// Cannot enter a cell if any unit inside is uncrushable // Cannot enter a cell if any unit inside is uncrushable
// This will need to be updated for multiple-infantry-in-a-cell // This will need to be updated for multiple-infantry-in-a-cell

View File

@@ -19,12 +19,9 @@ namespace OpenRa.Game.Traits.Activities
if (isCanceled) return NextActivity; if (isCanceled) return NextActivity;
if (--remainingTicks == 0) if (--remainingTicks == 0)
{ {
self.Health += hpPerPoint; self.InflictDamage(self, -hpPerPoint, Rules.WarheadInfo["Super"]);
if (self.Health >= self.Info.Strength) if (self.Health == self.Info.Strength)
{
self.Health = self.Info.Strength;
return NextActivity; return NextActivity;
}
var hostBuilding = Game.FindUnits(self.CenterLocation, self.CenterLocation) var hostBuilding = Game.FindUnits(self.CenterLocation, self.CenterLocation)
.FirstOrDefault(a => a.traits.Contains<RenderBuilding>()); .FirstOrDefault(a => a.traits.Contains<RenderBuilding>());

View File

@@ -0,0 +1,45 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace OpenRa.Game.Traits.Activities
{
class Sell : IActivity
{
public IActivity NextActivity { get; set; }
bool started;
void DoSell(Actor self)
{
var refund = Rules.General.RefundPercent
* self.Health * self.Info.Cost / self.Info.Strength;
self.Owner.GiveCash((int)refund);
self.Health = 0;
foreach (var ns in self.traits.WithInterface<INotifySold>())
ns.Sold(self);
Game.world.Remove(self);
// todo: give dudes
}
public IActivity Tick(Actor self)
{
if (!started)
{
var rb = self.traits.Get<RenderBuilding>();
rb.PlayCustomAnimBackwards(self, "make",
() => Game.world.AddFrameEndTask(w => DoSell(self)));
Sound.Play("cashturn.aud");
started = true;
}
return this;
}
public void Cancel(Actor self) { /* never gonna give you up.. */ }
}
}

View File

@@ -10,7 +10,7 @@ namespace OpenRa.Game.Traits
{ {
target = order.TargetActor; target = order.TargetActor;
self.QueueActivity(new HeliAttack(order.TargetActor)); self.QueueActivity(new HeliAttack(order.TargetActor));
// todo: fly home self.QueueActivity(new HeliReturn());
} }
} }
} }

View File

@@ -1,8 +1,9 @@
using OpenRa.Game.GameRules; using OpenRa.Game.GameRules;
using OpenRa.Game.Traits.Activities;
namespace OpenRa.Game.Traits namespace OpenRa.Game.Traits
{ {
class Building : INotifyDamage class Building : INotifyDamage, IOrder
{ {
public readonly BuildingInfo unitInfo; public readonly BuildingInfo unitInfo;
@@ -18,5 +19,19 @@ namespace OpenRa.Game.Traits
if (e.DamageState == DamageState.Dead) if (e.DamageState == DamageState.Dead)
Sound.Play("kaboom22.aud"); Sound.Play("kaboom22.aud");
} }
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
return null; // sell/repair orders are issued through Chrome, not here.
}
public void ResolveOrder(Actor self, Order order)
{
if (order.OrderString == "Sell")
{
self.CancelActivity();
self.QueueActivity(new Sell());
}
}
} }
} }

View File

@@ -16,13 +16,13 @@ namespace OpenRa.Game.Traits
// todo: other bits // todo: other bits
return new Order(underCursor.Health <= EngineerDamage ? "Capture" : "Enter", return new Order(underCursor.Health <= EngineerDamage ? "Capture" : "Infiltrate",
self, underCursor, int2.Zero, null); self, underCursor, int2.Zero, null);
} }
public void ResolveOrder(Actor self, Order order) public void ResolveOrder(Actor self, Order order)
{ {
if (order.OrderString == "Enter" || order.OrderString == "Capture") if (order.OrderString == "Infiltrate" || order.OrderString == "Capture")
{ {
self.CancelActivity(); self.CancelActivity();
self.QueueActivity(new Move(order.TargetActor, 1)); self.QueueActivity(new Move(order.TargetActor, 1));

View File

@@ -9,6 +9,14 @@ namespace OpenRa.Game.Traits
public IDisposable reservation; public IDisposable reservation;
public Helicopter(Actor self) {} public Helicopter(Actor self) {}
// todo: push into data!
static bool HeliCanEnter(Actor a)
{
if (a.Info == Rules.UnitInfo["HPAD"]) return true;
if (a.Info == Rules.UnitInfo["FIX"]) return true;
return false;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{ {
if (mi.Button == MouseButton.Left) return null; if (mi.Button == MouseButton.Left) return null;
@@ -16,7 +24,7 @@ namespace OpenRa.Game.Traits
if (underCursor == null) if (underCursor == null)
return new Order("Move", self, null, xy, null); return new Order("Move", self, null, xy, null);
if (underCursor.Info == Rules.UnitInfo["HPAD"] if (HeliCanEnter(underCursor)
&& underCursor.Owner == self.Owner && underCursor.Owner == self.Owner
&& !Reservable.IsReserved(underCursor)) && !Reservable.IsReserved(underCursor))
return new Order("Enter", self, underCursor, int2.Zero, null); return new Order("Enter", self, underCursor, int2.Zero, null);
@@ -54,7 +62,8 @@ namespace OpenRa.Game.Traits
self.QueueActivity(new HeliFly(order.TargetActor.CenterLocation + offsetVec)); self.QueueActivity(new HeliFly(order.TargetActor.CenterLocation + offsetVec));
self.QueueActivity(new Turn(self.Info.InitialFacing)); self.QueueActivity(new Turn(self.Info.InitialFacing));
self.QueueActivity(new HeliLand(false)); self.QueueActivity(new HeliLand(false));
self.QueueActivity(new Rearm()); self.QueueActivity(order.TargetActor.Info == Rules.UnitInfo["HPAD"]
? (IActivity)new Rearm() : new Repair());
} }
} }

View File

@@ -12,13 +12,21 @@ namespace OpenRa.Game.Traits
public Plane(Actor self) {} public Plane(Actor self) {}
// todo: push into data!
static bool PlaneCanEnter(Actor a)
{
if (a.Info == Rules.UnitInfo["AFLD"]) return true;
if (a.Info == Rules.UnitInfo["FIX"]) return true;
return false;
}
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor) public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{ {
if (mi.Button == MouseButton.Left) return null; if (mi.Button == MouseButton.Left) return null;
if (underCursor == null) if (underCursor == null)
return new Order("Move", self, null, xy, null); return new Order("Move", self, null, xy, null);
if (underCursor.Info == Rules.UnitInfo["AFLD"] if (PlaneCanEnter(underCursor)
&& underCursor.Owner == self.Owner && underCursor.Owner == self.Owner
&& !Reservable.IsReserved(underCursor)) && !Reservable.IsReserved(underCursor))
return new Order("Enter", self, underCursor, int2.Zero, null); return new Order("Enter", self, underCursor, int2.Zero, null);
@@ -51,18 +59,12 @@ namespace OpenRa.Game.Traits
self.CancelActivity(); self.CancelActivity();
self.QueueActivity(new ReturnToBase(self, order.TargetActor)); self.QueueActivity(new ReturnToBase(self, order.TargetActor));
self.QueueActivity(new Rearm()); /* todo: something else when it's FIX rather than AFLD */ self.QueueActivity(order.TargetActor.Info == Rules.UnitInfo["AFLD"]
? (IActivity)new Rearm() : new Repair());
} }
} }
public UnitMovementType GetMovementType() public UnitMovementType GetMovementType() { return UnitMovementType.Fly; }
{ public bool CanEnterCell(int2 location) { return true; }
return UnitMovementType.Fly;
}
public bool CanEnterCell(int2 location)
{
return true; // Planes can go anywhere (?)
}
} }
} }

View File

@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using OpenRa.Game.Graphics; using OpenRa.Game.Graphics;
using OpenRa.Game.Orders;
namespace OpenRa.Game.Traits namespace OpenRa.Game.Traits
{ {

View File

@@ -5,7 +5,7 @@ using OpenRa.Game.Effects;
namespace OpenRa.Game.Traits namespace OpenRa.Game.Traits
{ {
class RenderBuilding : RenderSimple, INotifyDamage class RenderBuilding : RenderSimple, INotifyDamage, INotifySold
{ {
const int SmallBibStart = 1; const int SmallBibStart = 1;
const int LargeBibStart = 5; const int LargeBibStart = 5;
@@ -62,6 +62,12 @@ namespace OpenRa.Game.Traits
() => anim.PlayRepeating(GetPrefix(self) + "idle")); () => anim.PlayRepeating(GetPrefix(self) + "idle"));
} }
public void PlayCustomAnimBackwards(Actor self, string name, Action a)
{
anim.PlayBackwardsThen(GetPrefix(self) + name,
() => { anim.PlayRepeating(GetPrefix(self) + "idle"); a(); });
}
public virtual void Damaged(Actor self, AttackInfo e) public virtual void Damaged(Actor self, AttackInfo e)
{ {
if (!e.DamageStateChanged) if (!e.DamageStateChanged)
@@ -82,5 +88,7 @@ namespace OpenRa.Game.Traits
break; break;
} }
} }
public void Sold(Actor self) { DoBib(self, true); }
} }
} }

View File

@@ -1,5 +1,4 @@
using System.Collections.Generic; using OpenRa.Game.Graphics;
using OpenRa.Game.Graphics;
namespace OpenRa.Game.Traits namespace OpenRa.Game.Traits
{ {

View File

@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenRa.Game.Traits.Activities;
namespace OpenRa.Game.Traits
{
class Repairable : IOrder
{
IDisposable reservation;
public Repairable(Actor self) { }
public Order IssueOrder(Actor self, int2 xy, MouseInput mi, Actor underCursor)
{
if (mi.Button != MouseButton.Right) return null;
if (underCursor == null) return null;
if (underCursor.Info == Rules.UnitInfo["FIX"]
&& underCursor.Owner == self.Owner
&& !Reservable.IsReserved(underCursor))
return new Order("Enter", self, underCursor, int2.Zero, null);
return null;
}
public void ResolveOrder(Actor self, Order order)
{
if (reservation != null)
{
reservation.Dispose();
reservation = null;
}
if (order.OrderString == "Enter")
{
if (Reservable.IsReserved(order.TargetActor))
return;
var res = order.TargetActor.traits.GetOrDefault<Reservable>();
if (res != null) reservation = res.Reserve(self);
self.CancelActivity();
self.QueueActivity(new Move(((1 / 24f) * order.TargetActor.CenterLocation).ToInt2(), order.TargetActor));
self.QueueActivity(new Rearm());
self.QueueActivity(new Repair());
}
}
}
}

View File

@@ -13,6 +13,7 @@ namespace OpenRa.Game.Traits
interface ITick { void Tick(Actor self); } interface ITick { void Tick(Actor self); }
interface IRender { IEnumerable<Renderable> Render(Actor self); } interface IRender { IEnumerable<Renderable> Render(Actor self); }
interface INotifySold { void Sold(Actor self); }
interface INotifyDamage { void Damaged(Actor self, AttackInfo e); } interface INotifyDamage { void Damaged(Actor self, AttackInfo e); }
interface INotifyBuildComplete { void BuildingComplete (Actor self); } interface INotifyBuildComplete { void BuildingComplete (Actor self); }
interface INotifyProduction { void UnitProduced(Actor self, Actor other); } interface INotifyProduction { void UnitProduced(Actor self, Actor other); }

View File

@@ -22,7 +22,7 @@ All tracked vehicles
Light vehicles Light vehicles
V2RL Works V2RL Works
APC Cargo doesn't work APC Cargo doesn't work
MNLY Can't reload at FIX MNLY Works
MGG No gap MGG No gap
MRJ No radar MRJ No radar
JEEP Works JEEP Works
@@ -31,15 +31,15 @@ HARV Works
ARTY Works ARTY Works
Helicopters Helicopters
- Repair as FIX doesn't work - Return to base after attack doesnt work
TRAN Cargo doesn't work TRAN Cargo doesn't work
HELI Weapon offsets wrong HELI Works
HIND Weapon offsets wrong HIND Works
Planes Planes
- Repair at FIX doesn't work [fix doesn't work?] - Ammo/ROF are funky
YAK Ammo/ROF are funky YAK Works
MIG Ammo/ROF are funky MIG Works
Ships Ships

View File

@@ -372,14 +372,14 @@
<sequence name="move-minimap" start="29" length="6" /> <sequence name="move-minimap" start="29" length="6" />
<sequence name="repair" start="35" length="24" /> <sequence name="repair" start="35" length="24" />
<sequence name="deploy" start="59" length="9" x="12" y="12" /> <sequence name="deploy" start="59" length="9" x="12" y="12" />
<sequence name="sell" start="68" length="12" /> <sequence name="sell" start="68" length="12" x="12" y="12"/>
<sequence name="default-minimap" start="80" length="1" /> <sequence name="default-minimap" start="80" length="1" />
<sequence name="ability" start="82" length="8" /> <sequence name="ability" start="82" length="8" />
<sequence name="nuke" start="90" length="7" x="12" y="12" /> <sequence name="nuke" start="90" length="7" x="12" y="12" />
<sequence name="chrono" start="105" length="8" x="12" y="12" /> <sequence name="chrono" start="105" length="8" x="12" y="12" />
<sequence name="enter" start="113" length="3" x="12" y="12" /> <sequence name="enter" start="113" length="3" x="12" y="12" />
<sequence name="c4" start="116" length="3" x="12" y="12" /> <sequence name="c4" start="116" length="3" x="12" y="12" />
<sequence name="sell-blocked" start="119" length="1" /> <sequence name="sell-blocked" start="119" length="1" x="12" y="12" />
<sequence name="repair-blocked" start="120" length="1" /> <sequence name="repair-blocked" start="120" length="1" />
<sequence name="c4-minimap" start="121" length="3" /> <sequence name="c4-minimap" start="121" length="3" />
<sequence name="scroll-blocked" start="124" length="8" /> <sequence name="scroll-blocked" start="124" length="8" />

View File

@@ -16,47 +16,47 @@ MNLY.AT
[V2RL] [V2RL]
Description=V2 Rocket Description=V2 Rocket
Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget Traits=Unit, Mobile, AttackBase, RenderUnitReload, AutoTarget, Repairable
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Long-range rocket artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft LongDesc=Long-range rocket artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft
[1TNK] [1TNK]
Description=Light Tank Description=Light Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable
Recoil=2 Recoil=2
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Light Tank, good for scouting.\n Strong vs Light Vehicles\n Weak vs Tanks, Aircraft LongDesc=Light Tank, good for scouting.\n Strong vs Light Vehicles\n Weak vs Tanks, Aircraft
[2TNK] [2TNK]
Description=Medium Tank Description=Medium Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable
Recoil=3 Recoil=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Allied Main Battle Tank.\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft LongDesc=Allied Main Battle Tank.\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft
[3TNK] [3TNK]
Description=Heavy Tank Description=Heavy Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable
Recoil=3 Recoil=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Soviet Main Battle Tank, with dual cannons\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft LongDesc=Soviet Main Battle Tank, with dual cannons\n Strong vs Tanks, Light Vehicles\n Weak vs Infantry, Aircraft
[4TNK] [4TNK]
Description=Mammoth Tank Description=Mammoth Tank
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Big and slow tank, with anti-air capability.\n Strong vs Tanks, Aircraft\n Weak vs Infantry LongDesc=Big and slow tank, with anti-air capability.\n Strong vs Tanks, Aircraft\n Weak vs Infantry
[ARTY] [ARTY]
Description=Artillery Description=Artillery
Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget Traits=Unit, Mobile, AttackBase, RenderUnit, Explodes, AutoTarget, Repairable
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Long-range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft LongDesc=Long-range artillery.\n Strong vs Infantry, Buildings\n Weak vs Tanks, Aircraft
[JEEP] [JEEP]
Description=Ranger Description=Ranger
Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget Traits=Unit, Mobile, Turreted, AttackTurreted, RenderUnitTurreted, AutoTarget, Repairable
PrimaryOffset=0,0,0,-2 PrimaryOffset=0,0,0,-2
MuzzleFlash=yes MuzzleFlash=yes
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft LongDesc=Fast scout & anti-infantry vehicle.\n Strong vs Infantry\n Weak vs Tanks, Aircraft
[APC] [APC]
Description=Armored Personnel Carrier Description=Armored Personnel Carrier
Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash, AutoTarget Traits=Unit, Mobile, AttackBase, RenderUnitMuzzleFlash, AutoTarget, Repairable
PrimaryOffset=0,0,0,-4 PrimaryOffset=0,0,0,-4
MuzzleFlash=yes MuzzleFlash=yes
Voice=VehicleVoice Voice=VehicleVoice
@@ -65,40 +65,40 @@ LongDesc=Tough infantry transport.\n Strong vs Infantry, Light Vehicles\n Weak
;; non-combat vehicles ;; non-combat vehicles
[MRJ] [MRJ]
Description=Radar Jammer Description=Radar Jammer
Traits=Unit, Mobile, RenderUnitSpinner Traits=Unit, Mobile, RenderUnitSpinner, Repairable
PrimaryOffset=0,4,0,-6 PrimaryOffset=0,4,0,-6
SelectionPriority=3 SelectionPriority=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Hides nearby units on the enemy's minimap.\n Unarmed LongDesc=Hides nearby units on the enemy's minimap.\n Unarmed
[MGG] [MGG]
Description=Mobile Gap Generator Description=Mobile Gap Generator
Traits=Unit, Mobile, RenderUnitSpinner Traits=Unit, Mobile, RenderUnitSpinner, Repairable
PrimaryOffset=0,6,0,-3 PrimaryOffset=0,6,0,-3
SelectionPriority=3 SelectionPriority=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Regenerates Fog of War in a small area \naround the unit.\n Unarmed LongDesc=Regenerates Fog of War in a small area \naround the unit.\n Unarmed
[HARV] [HARV]
Description=Ore Truck Description=Ore Truck
Traits=Harvester, Unit, Mobile, RenderUnit Traits=Harvester, Unit, Mobile, RenderUnit, Repairable
SelectionPriority=7 SelectionPriority=7
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Collects Ore and Gems for processing.\n Unarmed LongDesc=Collects Ore and Gems for processing.\n Unarmed
[MCV] [MCV]
Description=Mobile Construction Vehicle Description=Mobile Construction Vehicle
Traits=Unit, Mobile, McvDeploy, RenderUnit Traits=Unit, Mobile, McvDeploy, RenderUnit, Repairable
SelectionPriority=3 SelectionPriority=3
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Deploys into another Construction Yard.\n Unarmed LongDesc=Deploys into another Construction Yard.\n Unarmed
[MNLY.AP] [MNLY.AP]
Description=Minelayer (Anti-Personnel) Description=Minelayer (Anti-Personnel)
Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed
Primary=MINP ;; temporary hack Primary=MINP ;; temporary hack
[MNLY.AT] [MNLY.AT]
Description=Minelayer (Anti-Tank) Description=Minelayer (Anti-Tank)
Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune Traits=Unit, Mobile, RenderUnit, Minelayer, MineImmune, Repairable
Voice=VehicleVoice Voice=VehicleVoice
LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed LongDesc=Lays mines to destroy unwary enemy units.\n Unarmed
Primary=MINV ;; temporary hack Primary=MINV ;; temporary hack