#region Copyright & License Information /* * Copyright 2007-2017 The OpenRA Developers (see AUTHORS) * This file is part of OpenRA, which is free software. It is made * available to you under the terms of the GNU General Public License * as published by the Free Software Foundation, either version 3 of * the License, or (at your option) any later version. For more * information, see COPYING. */ #endregion using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common; using OpenRA.Mods.Common.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Mods.Common.Traits.Render; using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Cnc.Traits.Render { [Desc("Renders the cargo loaded into the unit.")] public class WithCargoInfo : ITraitInfo, Requires, Requires { [Desc("Cargo position relative to turret or body in (forward, right, up) triples. The default offset should be in the middle of the list.")] public readonly WVec[] LocalOffset = { WVec.Zero }; [Desc("Passenger CargoType to display.")] public readonly HashSet DisplayTypes = new HashSet(); public object Create(ActorInitializer init) { return new WithCargo(init.Self, this); } } public class WithCargo : ITick, IRender, INotifyPassengerEntered, INotifyPassengerExited { readonly WithCargoInfo info; readonly Cargo cargo; readonly BodyOrientation body; readonly IFacing facing; Dictionary previews = new Dictionary(); public WithCargo(Actor self, WithCargoInfo info) { this.info = info; cargo = self.Trait(); body = self.Trait(); facing = self.TraitOrDefault(); } void ITick.Tick(Actor self) { foreach (var actorPreviews in previews.Values) if (actorPreviews != null) foreach (var preview in actorPreviews) preview.Tick(); } IEnumerable IRender.Render(Actor self, WorldRenderer wr) { var bodyOrientation = body.QuantizeOrientation(self, self.Orientation); var pos = self.CenterPosition; var i = 0; // Generate missing previews var missing = previews .Where(kv => kv.Value == null) .Select(kv => kv.Key) .ToList(); foreach (var p in missing) { var passengerInits = new TypeDictionary() { new OwnerInit(p.Owner), new DynamicFacingInit(() => body.QuantizeFacing(facing.Facing)), }; foreach (var api in p.TraitsImplementing()) api.ModifyActorPreviewInit(p, passengerInits); var init = new ActorPreviewInitializer(p.Info, wr, passengerInits); previews[p] = p.Info.TraitInfos() .SelectMany(rpi => rpi.RenderPreview(init)) .ToArray(); } foreach (var p in previews.Values.SelectMany(p => p)) { var index = cargo.PassengerCount > 1 ? i++ % info.LocalOffset.Length : info.LocalOffset.Length / 2; var localOffset = info.LocalOffset[index]; foreach (var pp in p.Render(wr, pos + body.LocalToWorld(localOffset.Rotate(bodyOrientation)))) yield return pp.WithZOffset(1); } } void INotifyPassengerEntered.OnPassengerEntered(Actor self, Actor passenger) { if (info.DisplayTypes.Contains(passenger.Trait().Info.CargoType)) previews.Add(passenger, null); } void INotifyPassengerExited.OnPassengerExited(Actor self, Actor passenger) { previews.Remove(passenger); } } }