#region Copyright & License Information /* * Copyright 2007-2020 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; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; using OpenRA.Traits; namespace OpenRA.Mods.Common.Graphics { public interface IActorPreview { void Tick(); IEnumerable Render(WorldRenderer wr, WPos pos); IEnumerable RenderUI(WorldRenderer wr, int2 pos, float scale); IEnumerable ScreenBounds(WorldRenderer wr, WPos pos); } public class ActorPreviewInitializer : IActorInitializer { public readonly ActorInfo Actor; public readonly WorldRenderer WorldRenderer; public World World { get { return WorldRenderer.World; } } readonly TypeDictionary dict; public ActorPreviewInitializer(ActorInfo actor, WorldRenderer worldRenderer, TypeDictionary dict) { Actor = actor; WorldRenderer = worldRenderer; this.dict = dict; } public T GetOrDefault(TraitInfo info) where T : ActorInit { var inits = dict.WithInterface(); // Traits tagged with an instance name prefer inits with the same name. // If a more specific init is not available, fall back to an unnamed init. // If duplicate inits are defined, take the last to match standard yaml override expectations if (info != null && !string.IsNullOrEmpty(info.InstanceName)) return inits.LastOrDefault(i => i.InstanceName == info.InstanceName) ?? inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName)); // Untagged traits will only use untagged inits return inits.LastOrDefault(i => string.IsNullOrEmpty(i.InstanceName)); } public T Get(TraitInfo info) where T : ActorInit { var init = GetOrDefault(info); if (init == null) throw new InvalidOperationException("TypeDictionary does not contain instance of type `{0}`".F(typeof(T))); return init; } public U GetValue(TraitInfo info) where T : ValueActorInit { return Get(info).Value; } public U GetValue(TraitInfo info, U fallback) where T : ValueActorInit { var init = GetOrDefault(info); return init != null ? init.Value : fallback; } public bool Contains() where T : ActorInit, ISingleInstanceInit { return GetOrDefault() != null; } public T GetOrDefault() where T : ActorInit, ISingleInstanceInit { return dict.GetOrDefault(); } public T Get() where T : ActorInit, ISingleInstanceInit { return dict.Get(); } public U GetValue() where T : ValueActorInit, ISingleInstanceInit { return Get().Value; } public U GetValue(U fallback) where T : ValueActorInit, ISingleInstanceInit { var init = GetOrDefault(); return init != null ? init.Value : fallback; } public bool Contains(TraitInfo info) where T : ActorInit { return GetOrDefault(info) != null; } public Func GetOrientation() { var facingInfo = Actor.TraitInfoOrDefault(); if (facingInfo == null) return () => WRot.Zero; // Dynamic facing takes priority var dynamicInit = dict.GetOrDefault(); if (dynamicInit != null) { // TODO: Account for terrain slope var getFacing = dynamicInit.Value; return () => WRot.FromFacing(getFacing()); } // Fall back to initial actor facing if an Init isn't available var facingInit = dict.GetOrDefault(); var facing = facingInit != null ? facingInit.Value : facingInfo.GetInitialFacing(); var orientation = WRot.FromFacing(facing); return () => orientation; } public Func GetFacing() { var facingInfo = Actor.TraitInfoOrDefault(); if (facingInfo == null) return () => WAngle.Zero; // Dynamic facing takes priority var dynamicInit = dict.GetOrDefault(); if (dynamicInit != null) { var getFacing = dynamicInit.Value; return () => WAngle.FromFacing(getFacing()); } // Fall back to initial actor facing if an Init isn't available var facingInit = dict.GetOrDefault(); var facing = WAngle.FromFacing(facingInit != null ? facingInit.Value : facingInfo.GetInitialFacing()); return () => facing; } public DamageState GetDamageState() { var health = dict.GetOrDefault(); if (health == null) return DamageState.Undamaged; var hf = health.Value; if (hf <= 0) return DamageState.Dead; if (hf < 25) return DamageState.Critical; if (hf < 50) return DamageState.Heavy; if (hf < 75) return DamageState.Medium; if (hf < 100) return DamageState.Light; return DamageState.Undamaged; } } }