1 #region Copyright & License Information 2 /* 3 * Copyright 2007-2020 The OpenRA Developers (see AUTHORS) 4 * This file is part of OpenRA, which is free software. It is made 5 * available to you under the terms of the GNU General Public License 6 * as published by the Free Software Foundation, either version 3 of 7 * the License, or (at your option) any later version. For more 8 * information, see COPYING. 9 */ 10 #endregion 11 12 using System; 13 using System.Collections.Generic; 14 using OpenRA.Mods.Common.Activities; 15 using OpenRA.Primitives; 16 using OpenRA.Traits; 17 18 namespace OpenRA.Mods.Common.Traits 19 { 20 [Desc("This unit has access to build queues.")] 21 public class ProductionInfo : PausableConditionalTraitInfo 22 { 23 [FieldLoader.Require] 24 [Desc("e.g. Infantry, Vehicles, Aircraft, Buildings")] 25 public readonly string[] Produces = { }; 26 Create(ActorInitializer init)27 public override object Create(ActorInitializer init) { return new Production(init, this); } 28 } 29 30 public class Production : PausableConditionalTrait<ProductionInfo>, INotifyCreated 31 { 32 readonly Lazy<RallyPoint> rp; 33 34 public string Faction { get; private set; } 35 Production(ActorInitializer init, ProductionInfo info)36 public Production(ActorInitializer init, ProductionInfo info) 37 : base(info) 38 { 39 rp = Exts.Lazy(() => init.Self.IsDead ? null : init.Self.TraitOrDefault<RallyPoint>()); 40 Faction = init.Contains<FactionInit>() ? init.Get<FactionInit, string>() : init.Self.Owner.Faction.InternalName; 41 } 42 DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo, string productionType, TypeDictionary inits)43 public virtual void DoProduction(Actor self, ActorInfo producee, ExitInfo exitinfo, string productionType, TypeDictionary inits) 44 { 45 var exit = CPos.Zero; 46 var exitLocations = new List<CPos>(); 47 48 // Clone the initializer dictionary for the new actor 49 var td = new TypeDictionary(); 50 foreach (var init in inits) 51 td.Add(init); 52 53 if (exitinfo != null && self.OccupiesSpace != null && producee.HasTraitInfo<IOccupySpaceInfo>()) 54 { 55 exit = self.Location + exitinfo.ExitCell; 56 var spawn = self.CenterPosition + exitinfo.SpawnOffset; 57 var to = self.World.Map.CenterOfCell(exit); 58 59 var initialFacing = exitinfo.Facing; 60 if (exitinfo.Facing < 0) 61 { 62 var delta = to - spawn; 63 if (delta.HorizontalLengthSquared == 0) 64 { 65 var fi = producee.TraitInfoOrDefault<IFacingInfo>(); 66 initialFacing = fi != null ? fi.GetInitialFacing() : 0; 67 } 68 else 69 initialFacing = delta.Yaw.Facing; 70 } 71 72 exitLocations = rp.Value != null && rp.Value.Path.Count > 0 ? rp.Value.Path : new List<CPos> { exit }; 73 74 td.Add(new LocationInit(exit)); 75 td.Add(new CenterPositionInit(spawn)); 76 td.Add(new FacingInit(initialFacing)); 77 if (exitinfo != null) 78 td.Add(new CreationActivityDelayInit(exitinfo.ExitDelay)); 79 } 80 81 self.World.AddFrameEndTask(w => 82 { 83 var newUnit = self.World.CreateActor(producee.Name, td); 84 85 var move = newUnit.TraitOrDefault<IMove>(); 86 if (exitinfo != null && move != null) 87 foreach (var cell in exitLocations) 88 newUnit.QueueActivity(new AttackMoveActivity(newUnit, () => move.MoveTo(cell, 1, evaluateNearestMovableCell: true, targetLineColor: Color.OrangeRed))); 89 90 if (!self.IsDead) 91 foreach (var t in self.TraitsImplementing<INotifyProduction>()) 92 t.UnitProduced(self, newUnit, exit); 93 94 var notifyOthers = self.World.ActorsWithTrait<INotifyOtherProduction>(); 95 foreach (var notify in notifyOthers) 96 notify.Trait.UnitProducedByOther(notify.Actor, self, newUnit, productionType, td); 97 }); 98 } 99 SelectExit(Actor self, ActorInfo producee, string productionType, Func<Exit, bool> p)100 protected virtual Exit SelectExit(Actor self, ActorInfo producee, string productionType, Func<Exit, bool> p) 101 { 102 return self.RandomExitOrDefault(self.World, productionType, p); 103 } 104 SelectExit(Actor self, ActorInfo producee, string productionType)105 protected Exit SelectExit(Actor self, ActorInfo producee, string productionType) 106 { 107 return SelectExit(self, producee, productionType, e => CanUseExit(self, producee, e.Info)); 108 } 109 Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits)110 public virtual bool Produce(Actor self, ActorInfo producee, string productionType, TypeDictionary inits) 111 { 112 if (IsTraitDisabled || IsTraitPaused || Reservable.IsReserved(self)) 113 return false; 114 115 // Pick a spawn/exit point pair 116 var exit = SelectExit(self, producee, productionType); 117 118 if (exit != null || self.OccupiesSpace == null || !producee.HasTraitInfo<IOccupySpaceInfo>()) 119 { 120 DoProduction(self, producee, exit == null ? null : exit.Info, productionType, inits); 121 122 return true; 123 } 124 125 return false; 126 } 127 CanUseExit(Actor self, ActorInfo producee, ExitInfo s)128 static bool CanUseExit(Actor self, ActorInfo producee, ExitInfo s) 129 { 130 var mobileInfo = producee.TraitInfoOrDefault<MobileInfo>(); 131 132 self.NotifyBlocker(self.Location + s.ExitCell); 133 134 return mobileInfo == null || 135 mobileInfo.CanEnterCell(self.World, self, self.Location + s.ExitCell, ignoreActor: self); 136 } 137 } 138 } 139