Added support for filtering exits by production type.
This commit is contained in:
@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName;
|
||||
}
|
||||
|
||||
public void UnitProducedByOther(Actor self, Actor producer, Actor produced)
|
||||
public void UnitProducedByOther(Actor self, Actor producer, Actor produced, string productionType)
|
||||
{
|
||||
// No recursive cloning!
|
||||
if (producer.Owner != self.Owner || producer.Info.HasTraitInfo<ClonesProducedUnitsInfo>())
|
||||
@@ -56,7 +56,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
new FactionInit(BuildableInfo.GetInitialFaction(produced.Info, faction))
|
||||
};
|
||||
|
||||
production.Produce(self, produced.Info, inits);
|
||||
production.Produce(self, produced.Info, productionType, inits);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common;
|
||||
@@ -39,7 +39,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
this.info = info;
|
||||
}
|
||||
|
||||
public override bool Produce(Actor self, ActorInfo producee, TypeDictionary inits)
|
||||
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
|
||||
{
|
||||
var owner = self.Owner;
|
||||
var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo<AircraftInfo>();
|
||||
@@ -80,7 +80,7 @@ namespace OpenRA.Mods.Cnc.Traits
|
||||
foreach (var cargo in self.TraitsImplementing<INotifyDelivery>())
|
||||
cargo.Delivered(self);
|
||||
|
||||
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, inits));
|
||||
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, productionType, inits));
|
||||
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName);
|
||||
}));
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ namespace OpenRA.Mods.Common.Activities
|
||||
}
|
||||
}
|
||||
|
||||
var exit = dest.Info.TraitInfos<ExitInfo>().FirstOrDefault();
|
||||
var exit = dest.Info.FirstExitOrDefault(null);
|
||||
var offset = (exit != null) ? exit.SpawnOffset : WVec.Zero;
|
||||
|
||||
if (ShouldLandAtBuilding(self, dest))
|
||||
|
||||
@@ -33,8 +33,9 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
|
||||
[ScriptActorPropertyActivity]
|
||||
[Desc("Build a unit, ignoring the production queue. The activity will wait if the exit is blocked.")]
|
||||
public void Produce(string actorType, string factionVariant = null)
|
||||
[Desc("Build a unit, ignoring the production queue. The activity will wait if the exit is blocked.",
|
||||
"If productionType is nil or unavailable, then an exit will be selected based on Buildable info.")]
|
||||
public void Produce(string actorType, string factionVariant = null, string productionType = null)
|
||||
{
|
||||
ActorInfo actorInfo;
|
||||
if (!Self.World.Map.Rules.Actors.TryGetValue(actorType, out actorInfo))
|
||||
@@ -47,7 +48,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
new FactionInit(faction)
|
||||
};
|
||||
|
||||
Self.QueueActivity(new WaitFor(() => p.Produce(Self, actorInfo, inits)));
|
||||
Self.QueueActivity(new WaitFor(() => p.Produce(Self, actorInfo, productionType, inits)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -404,7 +404,7 @@ namespace OpenRA.Mods.Common.Scripting
|
||||
}
|
||||
}
|
||||
|
||||
public void UnitProducedByOther(Actor self, Actor producee, Actor produced)
|
||||
public void UnitProducedByOther(Actor self, Actor producee, Actor produced, string productionType)
|
||||
{
|
||||
if (world.Disposing)
|
||||
return;
|
||||
|
||||
@@ -731,7 +731,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
Action enter = () =>
|
||||
{
|
||||
var exit = order.TargetActor.Info.TraitInfos<ExitInfo>().FirstOrDefault();
|
||||
var exit = order.TargetActor.Info.FirstExitOrDefault(null);
|
||||
var offset = (exit != null) ? exit.SpawnOffset : WVec.Zero;
|
||||
|
||||
self.QueueActivity(new HeliFly(self, Target.FromPos(order.TargetActor.CenterPosition + offset)));
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
#endregion
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenRA.Traits;
|
||||
|
||||
namespace OpenRA.Mods.Common.Traits
|
||||
@@ -23,6 +26,9 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public readonly CVec ExitCell = CVec.Zero;
|
||||
public readonly int Facing = -1;
|
||||
|
||||
[Desc("Type tags on this exit.")]
|
||||
public readonly HashSet<string> ProductionTypes = new HashSet<string>();
|
||||
|
||||
[Desc("AttackMove to a RallyPoint or stay where you are spawned.")]
|
||||
public readonly bool MoveIntoWorld = true;
|
||||
|
||||
@@ -31,4 +37,38 @@ namespace OpenRA.Mods.Common.Traits
|
||||
}
|
||||
|
||||
public class Exit { }
|
||||
|
||||
public static class ExitExts
|
||||
{
|
||||
public static ExitInfo FirstExitOrDefault(this ActorInfo info, string productionType = null)
|
||||
{
|
||||
var all = info.TraitInfos<ExitInfo>();
|
||||
if (string.IsNullOrEmpty(productionType))
|
||||
return all.FirstOrDefault(e => e.ProductionTypes.Count == 0);
|
||||
return all.FirstOrDefault(e => e.ProductionTypes.Count == 0 || e.ProductionTypes.Contains(productionType));
|
||||
}
|
||||
|
||||
public static IEnumerable<ExitInfo> Exits(this ActorInfo info, string productionType = null)
|
||||
{
|
||||
var all = info.TraitInfos<ExitInfo>();
|
||||
if (string.IsNullOrEmpty(productionType))
|
||||
return all.Where(e => e.ProductionTypes.Count == 0);
|
||||
return all.Where(e => e.ProductionTypes.Count == 0 || e.ProductionTypes.Contains(productionType));
|
||||
}
|
||||
|
||||
public static ExitInfo RandomExitOrDefault(this ActorInfo info, World world, string productionType, Func<ExitInfo, bool> p = null)
|
||||
{
|
||||
var allOfType = Exits(info, productionType);
|
||||
if (!allOfType.Any())
|
||||
return null;
|
||||
|
||||
var shuffled = allOfType.Shuffle(world.SharedRandom);
|
||||
return p != null ? shuffled.FirstOrDefault(p) : shuffled.First();
|
||||
}
|
||||
|
||||
public static ExitInfo RandomExitOrDefault(this Actor self, string productionType, Func<ExitInfo, bool> p = null)
|
||||
{
|
||||
return RandomExitOrDefault(self.Info, self.World, productionType, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,7 +120,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
tokens[a] = external.GrantCondition(a, self);
|
||||
}
|
||||
|
||||
public void UnitProducedByOther(Actor self, Actor producer, Actor produced)
|
||||
public void UnitProducedByOther(Actor self, Actor producer, Actor produced, string productionType)
|
||||
{
|
||||
// If the produced Actor doesn't occupy space, it can't be in range
|
||||
if (produced.OccupiesSpace == null)
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
var bi = unit.TraitInfo<BuildableInfo>();
|
||||
|
||||
// Some units may request a specific production type, which is ignored if the AllTech cheat is enabled
|
||||
var type = developerMode.AllTech ? Info.Type : bi.BuildAtProductionType ?? Info.Type;
|
||||
var type = developerMode.AllTech ? Info.Type : (bi.BuildAtProductionType ?? Info.Type);
|
||||
|
||||
var producers = self.World.ActorsWithTrait<Production>()
|
||||
.Where(x => x.Actor.Owner == self.Owner
|
||||
@@ -112,7 +112,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
new FactionInit(BuildableInfo.GetInitialFaction(unit, p.Trait.Faction))
|
||||
};
|
||||
|
||||
if (p.Trait.Produce(p.Actor, unit, inits))
|
||||
if (p.Trait.Produce(p.Actor, unit, type, inits))
|
||||
{
|
||||
FinishProduction();
|
||||
return true;
|
||||
|
||||
@@ -389,7 +389,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
};
|
||||
|
||||
var sp = self.TraitsImplementing<Production>().FirstOrDefault(p => p.Info.Produces.Contains(Info.Type));
|
||||
if (sp != null && !self.IsDisabled() && sp.Produce(self, unit, inits))
|
||||
if (sp != null && !self.IsDisabled() && sp.Produce(self, unit, developerMode.AllTech ? null : Info.Type, inits))
|
||||
{
|
||||
FinishProduction();
|
||||
return true;
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
building = self.TraitOrDefault<Building>();
|
||||
}
|
||||
|
||||
public virtual void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo, TypeDictionary inits)
|
||||
public virtual void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo, string productionType, TypeDictionary inits)
|
||||
{
|
||||
var exit = CPos.Zero;
|
||||
var exitLocation = CPos.Zero;
|
||||
@@ -113,25 +113,35 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>();
|
||||
foreach (var notify in notifyOthers)
|
||||
notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit);
|
||||
notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit, productionType);
|
||||
|
||||
foreach (var t in newUnit.TraitsImplementing<INotifyBuildComplete>())
|
||||
t.BuildingComplete(newUnit);
|
||||
});
|
||||
}
|
||||
|
||||
public virtual bool Produce(Actor self, ActorInfo producee, TypeDictionary inits)
|
||||
protected virtual ExitInfo SelectExit(Actor self, ActorInfo producee, string productionType, Func<ExitInfo, bool> p)
|
||||
{
|
||||
return self.RandomExitOrDefault(productionType, p);
|
||||
}
|
||||
|
||||
protected ExitInfo SelectExit(Actor self, ActorInfo producee, string productionType)
|
||||
{
|
||||
return SelectExit(self, producee, productionType, e => CanUseExit(self, producee, e));
|
||||
}
|
||||
|
||||
public virtual bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
|
||||
{
|
||||
if (Reservable.IsReserved(self) || (building != null && building.Locked))
|
||||
return false;
|
||||
|
||||
// Pick a spawn/exit point pair
|
||||
var exit = self.Info.TraitInfos<ExitInfo>().Shuffle(self.World.SharedRandom)
|
||||
.FirstOrDefault(e => CanUseExit(self, producee, e));
|
||||
var exit = SelectExit(self, producee, productionType);
|
||||
|
||||
if (exit != null || self.OccupiesSpace == null)
|
||||
{
|
||||
DoProduction(self, producee, exit, inits);
|
||||
DoProduction(self, producee, exit, productionType, inits);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#endregion
|
||||
|
||||
using System.Drawing;
|
||||
using OpenRA;
|
||||
using OpenRA.Primitives;
|
||||
using OpenRA.Traits;
|
||||
|
||||
@@ -41,7 +40,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
rp = self.TraitOrDefault<RallyPoint>();
|
||||
}
|
||||
|
||||
public override bool Produce(Actor self, ActorInfo producee, TypeDictionary inits)
|
||||
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
|
||||
{
|
||||
var aircraftInfo = producee.TraitInfoOrDefault<AircraftInfo>();
|
||||
var mobileInfo = producee.TraitInfoOrDefault<MobileInfo>();
|
||||
@@ -96,7 +95,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>();
|
||||
foreach (var notify in notifyOthers)
|
||||
notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit);
|
||||
notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit, productionType);
|
||||
|
||||
foreach (var t in newUnit.TraitsImplementing<INotifyBuildComplete>())
|
||||
t.BuildingComplete(newUnit);
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
|
||||
using System;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using OpenRA.Activities;
|
||||
using OpenRA.Mods.Common.Activities;
|
||||
using OpenRA.Primitives;
|
||||
@@ -44,16 +43,15 @@ namespace OpenRA.Mods.Common.Traits
|
||||
rp = Exts.Lazy(() => init.Self.IsDead ? null : init.Self.TraitOrDefault<RallyPoint>());
|
||||
}
|
||||
|
||||
public override bool Produce(Actor self, ActorInfo producee, TypeDictionary inits)
|
||||
public override bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)
|
||||
{
|
||||
var owner = self.Owner;
|
||||
|
||||
// Assume a single exit point for simplicity
|
||||
var exit = self.Info.TraitInfos<ExitInfo>().First();
|
||||
var exit = SelectExit(self, producee, productionType);
|
||||
|
||||
// Start a fixed distance away: the width of the map.
|
||||
// This makes the production timing independent of spawnpoint
|
||||
var dropPos = self.Location + exit.ExitCell;
|
||||
var dropPos = exit != null ? self.Location + exit.ExitCell : self.Location;
|
||||
var startPos = dropPos + new CVec(owner.World.Map.Bounds.Width, 0);
|
||||
var endPos = new CPos(owner.World.Map.Bounds.Left - 5, dropPos.Y);
|
||||
|
||||
@@ -85,7 +83,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
foreach (var cargo in self.TraitsImplementing<INotifyDelivery>())
|
||||
cargo.Delivered(self);
|
||||
|
||||
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, inits));
|
||||
self.World.AddFrameEndTask(ww => DoProduction(self, producee, exit, productionType, inits));
|
||||
Game.Sound.Play(SoundType.World, info.ChuteSound, self.CenterPosition);
|
||||
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName);
|
||||
}));
|
||||
@@ -97,7 +95,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo, TypeDictionary inits)
|
||||
public override void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo, string productionType, TypeDictionary inits)
|
||||
{
|
||||
var exit = CPos.Zero;
|
||||
var exitLocation = CPos.Zero;
|
||||
@@ -155,7 +153,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
|
||||
var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>();
|
||||
foreach (var notify in notifyOthers)
|
||||
notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit);
|
||||
notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit, productionType);
|
||||
|
||||
foreach (var t in newUnit.TraitsImplementing<INotifyBuildComplete>())
|
||||
t.BuildingComplete(newUnit);
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
new FactionInit(BuildableInfo.GetInitialFaction(ai, faction))
|
||||
};
|
||||
|
||||
activated |= sp.Produce(self, ai, inits);
|
||||
activated |= sp.Produce(self, ai, info.Type, inits);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Common.Traits
|
||||
public interface INotifyBurstComplete { void FiredBurst(Actor self, Target target, Armament a); }
|
||||
public interface INotifyChat { bool OnChat(string from, string message); }
|
||||
public interface INotifyProduction { void UnitProduced(Actor self, Actor other, CPos exit); }
|
||||
public interface INotifyOtherProduction { void UnitProducedByOther(Actor self, Actor producer, Actor produced); }
|
||||
public interface INotifyOtherProduction { void UnitProducedByOther(Actor self, Actor producer, Actor produced, string productionType); }
|
||||
public interface INotifyDelivery { void IncomingDelivery(Actor self); void Delivered(Actor self); }
|
||||
public interface INotifyDocking { void Docked(Actor self, Actor harvester); void Undocked(Actor self, Actor harvester); }
|
||||
public interface INotifyParachute { void OnParachute(Actor self); void OnLanded(Actor self, Actor ignore); }
|
||||
|
||||
@@ -144,18 +144,42 @@ SPEN:
|
||||
SpawnOffset: 0,-213,0
|
||||
Facing: 96
|
||||
ExitCell: -1,2
|
||||
ProductionTypes: Submarine
|
||||
Exit@2:
|
||||
SpawnOffset: 0,-213,0
|
||||
Facing: 160
|
||||
ExitCell: 3,2
|
||||
ProductionTypes: Submarine
|
||||
Exit@3:
|
||||
SpawnOffset: 0,0,0
|
||||
Facing: 32
|
||||
ExitCell: 0,0
|
||||
ProductionTypes: Submarine
|
||||
Exit@4:
|
||||
SpawnOffset: 0,0,0
|
||||
Facing: 224
|
||||
ExitCell: 2,0
|
||||
ProductionTypes: Submarine
|
||||
Exit@b1:
|
||||
SpawnOffset: -1024,1024,0
|
||||
Facing: 160
|
||||
ExitCell: 0,2
|
||||
ProductionTypes: Ship
|
||||
Exit@b2:
|
||||
SpawnOffset: 1024,1024,0
|
||||
Facing: 224
|
||||
ExitCell: 2,2
|
||||
ProductionTypes: Ship
|
||||
Exit@b3:
|
||||
SpawnOffset: -1024,-1024,0
|
||||
Facing: 96
|
||||
ExitCell: 0,0
|
||||
ProductionTypes: Ship
|
||||
Exit@b4:
|
||||
SpawnOffset: 1024,-1024,0
|
||||
Facing: 32
|
||||
ExitCell: 2,0
|
||||
ProductionTypes: Ship
|
||||
Production:
|
||||
Produces: Ship, Submarine
|
||||
PrimaryBuilding:
|
||||
@@ -248,18 +272,22 @@ SYRD:
|
||||
SpawnOffset: -1024,1024,0
|
||||
Facing: 160
|
||||
ExitCell: 0,2
|
||||
ProductionTypes: Ship
|
||||
Exit@2:
|
||||
SpawnOffset: 1024,1024,0
|
||||
Facing: 224
|
||||
ExitCell: 2,2
|
||||
ProductionTypes: Ship
|
||||
Exit@3:
|
||||
SpawnOffset: -1024,-1024,0
|
||||
Facing: 96
|
||||
ExitCell: 0,0
|
||||
ProductionTypes: Ship
|
||||
Exit@4:
|
||||
SpawnOffset: 1024,-1024,0
|
||||
Facing: 32
|
||||
ExitCell: 2,0
|
||||
ProductionTypes: Ship
|
||||
Production:
|
||||
Produces: Ship, Boat
|
||||
PrimaryBuilding:
|
||||
@@ -1557,9 +1585,11 @@ BARR:
|
||||
Exit@1:
|
||||
SpawnOffset: -170,810,0
|
||||
ExitCell: 1,2
|
||||
ProductionTypes: Soldier, Infantry
|
||||
Exit@2:
|
||||
SpawnOffset: -725,640,0
|
||||
ExitCell: 0,2
|
||||
ProductionTypes: Soldier, Infantry
|
||||
Production:
|
||||
Produces: Infantry, Soldier
|
||||
PrimaryBuilding:
|
||||
@@ -1631,9 +1661,11 @@ KENN:
|
||||
Exit@1:
|
||||
SpawnOffset: -280,400,0
|
||||
ExitCell: 0,1
|
||||
ProductionTypes: Dog, Infantry
|
||||
Exit@2:
|
||||
SpawnOffset: -280,400,0
|
||||
ExitCell: -1,0
|
||||
ProductionTypes: Dog, Infantry
|
||||
Production:
|
||||
Produces: Infantry, Dog
|
||||
PrimaryBuilding:
|
||||
@@ -1684,9 +1716,11 @@ TENT:
|
||||
Exit@1:
|
||||
SpawnOffset: -42,810,0
|
||||
ExitCell: 1,2
|
||||
ProductionTypes: Soldier, Infantry
|
||||
Exit@2:
|
||||
SpawnOffset: -725,640,0
|
||||
ExitCell: 0,2
|
||||
ProductionTypes: Soldier, Infantry
|
||||
Production:
|
||||
Produces: Infantry, Soldier
|
||||
PrimaryBuilding:
|
||||
|
||||
Reference in New Issue
Block a user