Added support for filtering exits by production type.

This commit is contained in:
GSonderling
2017-12-09 22:22:48 +00:00
committed by reaperrr
parent 1bf59e885d
commit 488cec64b8
16 changed files with 116 additions and 34 deletions

View File

@@ -40,7 +40,7 @@ namespace OpenRA.Mods.Cnc.Traits
faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName; 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! // No recursive cloning!
if (producer.Owner != self.Owner || producer.Info.HasTraitInfo<ClonesProducedUnitsInfo>()) 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)) new FactionInit(BuildableInfo.GetInitialFaction(produced.Info, faction))
}; };
production.Produce(self, produced.Info, inits); production.Produce(self, produced.Info, productionType, inits);
} }
} }
} }

View File

@@ -39,7 +39,7 @@ namespace OpenRA.Mods.Cnc.Traits
this.info = info; 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 owner = self.Owner;
var aircraftInfo = self.World.Map.Rules.Actors[info.ActorType].TraitInfo<AircraftInfo>(); 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>()) foreach (var cargo in self.TraitsImplementing<INotifyDelivery>())
cargo.Delivered(self); 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); Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName);
})); }));

View File

@@ -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; var offset = (exit != null) ? exit.SpawnOffset : WVec.Zero;
if (ShouldLandAtBuilding(self, dest)) if (ShouldLandAtBuilding(self, dest))

View File

@@ -33,8 +33,9 @@ namespace OpenRA.Mods.Common.Scripting
} }
[ScriptActorPropertyActivity] [ScriptActorPropertyActivity]
[Desc("Build a unit, ignoring the production queue. The activity will wait if the exit is blocked.")] [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) "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; ActorInfo actorInfo;
if (!Self.World.Map.Rules.Actors.TryGetValue(actorType, out actorInfo)) if (!Self.World.Map.Rules.Actors.TryGetValue(actorType, out actorInfo))
@@ -47,7 +48,7 @@ namespace OpenRA.Mods.Common.Scripting
new FactionInit(faction) new FactionInit(faction)
}; };
Self.QueueActivity(new WaitFor(() => p.Produce(Self, actorInfo, inits))); Self.QueueActivity(new WaitFor(() => p.Produce(Self, actorInfo, productionType, inits)));
} }
} }

View File

@@ -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) if (world.Disposing)
return; return;

View File

@@ -731,7 +731,7 @@ namespace OpenRA.Mods.Common.Traits
Action enter = () => 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; var offset = (exit != null) ? exit.SpawnOffset : WVec.Zero;
self.QueueActivity(new HeliFly(self, Target.FromPos(order.TargetActor.CenterPosition + offset))); self.QueueActivity(new HeliFly(self, Target.FromPos(order.TargetActor.CenterPosition + offset)));

View File

@@ -9,6 +9,9 @@
*/ */
#endregion #endregion
using System;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Traits; using OpenRA.Traits;
namespace OpenRA.Mods.Common.Traits namespace OpenRA.Mods.Common.Traits
@@ -23,6 +26,9 @@ namespace OpenRA.Mods.Common.Traits
public readonly CVec ExitCell = CVec.Zero; public readonly CVec ExitCell = CVec.Zero;
public readonly int Facing = -1; 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.")] [Desc("AttackMove to a RallyPoint or stay where you are spawned.")]
public readonly bool MoveIntoWorld = true; public readonly bool MoveIntoWorld = true;
@@ -31,4 +37,38 @@ namespace OpenRA.Mods.Common.Traits
} }
public class Exit { } 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);
}
}
} }

View File

@@ -120,7 +120,7 @@ namespace OpenRA.Mods.Common.Traits
tokens[a] = external.GrantCondition(a, self); 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 the produced Actor doesn't occupy space, it can't be in range
if (produced.OccupiesSpace == null) if (produced.OccupiesSpace == null)

View File

@@ -90,7 +90,7 @@ namespace OpenRA.Mods.Common.Traits
var bi = unit.TraitInfo<BuildableInfo>(); var bi = unit.TraitInfo<BuildableInfo>();
// Some units may request a specific production type, which is ignored if the AllTech cheat is enabled // 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>() var producers = self.World.ActorsWithTrait<Production>()
.Where(x => x.Actor.Owner == self.Owner .Where(x => x.Actor.Owner == self.Owner
@@ -112,7 +112,7 @@ namespace OpenRA.Mods.Common.Traits
new FactionInit(BuildableInfo.GetInitialFaction(unit, p.Trait.Faction)) 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(); FinishProduction();
return true; return true;

View File

@@ -389,7 +389,7 @@ namespace OpenRA.Mods.Common.Traits
}; };
var sp = self.TraitsImplementing<Production>().FirstOrDefault(p => p.Info.Produces.Contains(Info.Type)); 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(); FinishProduction();
return true; return true;

View File

@@ -49,7 +49,7 @@ namespace OpenRA.Mods.Common.Traits
building = self.TraitOrDefault<Building>(); 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 exit = CPos.Zero;
var exitLocation = CPos.Zero; var exitLocation = CPos.Zero;
@@ -113,25 +113,35 @@ namespace OpenRA.Mods.Common.Traits
var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>(); var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>();
foreach (var notify in notifyOthers) 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>()) foreach (var t in newUnit.TraitsImplementing<INotifyBuildComplete>())
t.BuildingComplete(newUnit); 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)) if (Reservable.IsReserved(self) || (building != null && building.Locked))
return false; return false;
// Pick a spawn/exit point pair // Pick a spawn/exit point pair
var exit = self.Info.TraitInfos<ExitInfo>().Shuffle(self.World.SharedRandom) var exit = SelectExit(self, producee, productionType);
.FirstOrDefault(e => CanUseExit(self, producee, e));
if (exit != null || self.OccupiesSpace == null) if (exit != null || self.OccupiesSpace == null)
{ {
DoProduction(self, producee, exit, inits); DoProduction(self, producee, exit, productionType, inits);
return true; return true;
} }

View File

@@ -10,7 +10,6 @@
#endregion #endregion
using System.Drawing; using System.Drawing;
using OpenRA;
using OpenRA.Primitives; using OpenRA.Primitives;
using OpenRA.Traits; using OpenRA.Traits;
@@ -41,7 +40,7 @@ namespace OpenRA.Mods.Common.Traits
rp = self.TraitOrDefault<RallyPoint>(); 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 aircraftInfo = producee.TraitInfoOrDefault<AircraftInfo>();
var mobileInfo = producee.TraitInfoOrDefault<MobileInfo>(); var mobileInfo = producee.TraitInfoOrDefault<MobileInfo>();
@@ -96,7 +95,7 @@ namespace OpenRA.Mods.Common.Traits
var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>(); var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>();
foreach (var notify in notifyOthers) 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>()) foreach (var t in newUnit.TraitsImplementing<INotifyBuildComplete>())
t.BuildingComplete(newUnit); t.BuildingComplete(newUnit);

View File

@@ -11,7 +11,6 @@
using System; using System;
using System.Drawing; using System.Drawing;
using System.Linq;
using OpenRA.Activities; using OpenRA.Activities;
using OpenRA.Mods.Common.Activities; using OpenRA.Mods.Common.Activities;
using OpenRA.Primitives; using OpenRA.Primitives;
@@ -44,16 +43,15 @@ namespace OpenRA.Mods.Common.Traits
rp = Exts.Lazy(() => init.Self.IsDead ? null : init.Self.TraitOrDefault<RallyPoint>()); 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; var owner = self.Owner;
// Assume a single exit point for simplicity var exit = SelectExit(self, producee, productionType);
var exit = self.Info.TraitInfos<ExitInfo>().First();
// Start a fixed distance away: the width of the map. // Start a fixed distance away: the width of the map.
// This makes the production timing independent of spawnpoint // 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 startPos = dropPos + new CVec(owner.World.Map.Bounds.Width, 0);
var endPos = new CPos(owner.World.Map.Bounds.Left - 5, dropPos.Y); 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>()) foreach (var cargo in self.TraitsImplementing<INotifyDelivery>())
cargo.Delivered(self); 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.Play(SoundType.World, info.ChuteSound, self.CenterPosition);
Game.Sound.PlayNotification(self.World.Map.Rules, self.Owner, "Speech", info.ReadyAudio, self.Owner.Faction.InternalName); 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; 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 exit = CPos.Zero;
var exitLocation = CPos.Zero; var exitLocation = CPos.Zero;
@@ -155,7 +153,7 @@ namespace OpenRA.Mods.Common.Traits
var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>(); var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>();
foreach (var notify in notifyOthers) 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>()) foreach (var t in newUnit.TraitsImplementing<INotifyBuildComplete>())
t.BuildingComplete(newUnit); t.BuildingComplete(newUnit);

View File

@@ -75,7 +75,7 @@ namespace OpenRA.Mods.Common.Traits
new FactionInit(BuildableInfo.GetInitialFaction(ai, faction)) new FactionInit(BuildableInfo.GetInitialFaction(ai, faction))
}; };
activated |= sp.Produce(self, ai, inits); activated |= sp.Produce(self, ai, info.Type, inits);
} }
} }

View File

@@ -103,7 +103,7 @@ namespace OpenRA.Mods.Common.Traits
public interface INotifyBurstComplete { void FiredBurst(Actor self, Target target, Armament a); } public interface INotifyBurstComplete { void FiredBurst(Actor self, Target target, Armament a); }
public interface INotifyChat { bool OnChat(string from, string message); } public interface INotifyChat { bool OnChat(string from, string message); }
public interface INotifyProduction { void UnitProduced(Actor self, Actor other, CPos exit); } 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 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 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); } public interface INotifyParachute { void OnParachute(Actor self); void OnLanded(Actor self, Actor ignore); }

View File

@@ -144,18 +144,42 @@ SPEN:
SpawnOffset: 0,-213,0 SpawnOffset: 0,-213,0
Facing: 96 Facing: 96
ExitCell: -1,2 ExitCell: -1,2
ProductionTypes: Submarine
Exit@2: Exit@2:
SpawnOffset: 0,-213,0 SpawnOffset: 0,-213,0
Facing: 160 Facing: 160
ExitCell: 3,2 ExitCell: 3,2
ProductionTypes: Submarine
Exit@3: Exit@3:
SpawnOffset: 0,0,0 SpawnOffset: 0,0,0
Facing: 32 Facing: 32
ExitCell: 0,0 ExitCell: 0,0
ProductionTypes: Submarine
Exit@4: Exit@4:
SpawnOffset: 0,0,0 SpawnOffset: 0,0,0
Facing: 224 Facing: 224
ExitCell: 2,0 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: Production:
Produces: Ship, Submarine Produces: Ship, Submarine
PrimaryBuilding: PrimaryBuilding:
@@ -248,18 +272,22 @@ SYRD:
SpawnOffset: -1024,1024,0 SpawnOffset: -1024,1024,0
Facing: 160 Facing: 160
ExitCell: 0,2 ExitCell: 0,2
ProductionTypes: Ship
Exit@2: Exit@2:
SpawnOffset: 1024,1024,0 SpawnOffset: 1024,1024,0
Facing: 224 Facing: 224
ExitCell: 2,2 ExitCell: 2,2
ProductionTypes: Ship
Exit@3: Exit@3:
SpawnOffset: -1024,-1024,0 SpawnOffset: -1024,-1024,0
Facing: 96 Facing: 96
ExitCell: 0,0 ExitCell: 0,0
ProductionTypes: Ship
Exit@4: Exit@4:
SpawnOffset: 1024,-1024,0 SpawnOffset: 1024,-1024,0
Facing: 32 Facing: 32
ExitCell: 2,0 ExitCell: 2,0
ProductionTypes: Ship
Production: Production:
Produces: Ship, Boat Produces: Ship, Boat
PrimaryBuilding: PrimaryBuilding:
@@ -1557,9 +1585,11 @@ BARR:
Exit@1: Exit@1:
SpawnOffset: -170,810,0 SpawnOffset: -170,810,0
ExitCell: 1,2 ExitCell: 1,2
ProductionTypes: Soldier, Infantry
Exit@2: Exit@2:
SpawnOffset: -725,640,0 SpawnOffset: -725,640,0
ExitCell: 0,2 ExitCell: 0,2
ProductionTypes: Soldier, Infantry
Production: Production:
Produces: Infantry, Soldier Produces: Infantry, Soldier
PrimaryBuilding: PrimaryBuilding:
@@ -1631,9 +1661,11 @@ KENN:
Exit@1: Exit@1:
SpawnOffset: -280,400,0 SpawnOffset: -280,400,0
ExitCell: 0,1 ExitCell: 0,1
ProductionTypes: Dog, Infantry
Exit@2: Exit@2:
SpawnOffset: -280,400,0 SpawnOffset: -280,400,0
ExitCell: -1,0 ExitCell: -1,0
ProductionTypes: Dog, Infantry
Production: Production:
Produces: Infantry, Dog Produces: Infantry, Dog
PrimaryBuilding: PrimaryBuilding:
@@ -1684,9 +1716,11 @@ TENT:
Exit@1: Exit@1:
SpawnOffset: -42,810,0 SpawnOffset: -42,810,0
ExitCell: 1,2 ExitCell: 1,2
ProductionTypes: Soldier, Infantry
Exit@2: Exit@2:
SpawnOffset: -725,640,0 SpawnOffset: -725,640,0
ExitCell: 0,2 ExitCell: 0,2
ProductionTypes: Soldier, Infantry
Production: Production:
Produces: Infantry, Soldier Produces: Infantry, Soldier
PrimaryBuilding: PrimaryBuilding: