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;
}
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);
}
}
}

View File

@@ -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);
}));

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;
if (ShouldLandAtBuilding(self, dest))

View File

@@ -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)));
}
}

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)
return;

View File

@@ -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)));

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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); }

View File

@@ -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: