#region Copyright & License Information /* * Copyright 2007-2022 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.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic { public class ActorSelectorLogic : CommonSelectorLogic { class ActorSelectorActor { public readonly ActorInfo Actor; public readonly string[] Categories; public readonly string[] SearchTerms; public readonly string Tooltip; public ActorSelectorActor(ActorInfo actor, string[] categories, string[] searchTerms, string tooltip) { Actor = actor; Categories = categories; SearchTerms = searchTerms; Tooltip = tooltip; } } readonly DropDownButtonWidget ownersDropDown; readonly Ruleset mapRules; readonly ActorSelectorActor[] allActors; readonly EditorCursorLayer editorCursor; [TranslationReference] static readonly string Type = "type"; PlayerReference selectedOwner; [ObjectCreator.UseCtor] public ActorSelectorLogic(Widget widget, ModData modData, World world, WorldRenderer worldRenderer) : base(widget, modData, world, worldRenderer, "ACTORTEMPLATE_LIST", "ACTORPREVIEW_TEMPLATE") { mapRules = world.Map.Rules; ownersDropDown = widget.Get("OWNERS_DROPDOWN"); editorCursor = world.WorldActor.Trait(); var editorLayer = world.WorldActor.Trait(); selectedOwner = editorLayer.Players.Players.Values.First(); Func setupItem = (option, template) => { var item = ScrollItemWidget.Setup(template, () => selectedOwner == option, () => SelectOwner(option)); item.Get("LABEL").GetText = () => option.Name; item.GetColor = () => option.Color; return item; }; editorLayer.OnPlayerRemoved = () => { if (editorLayer.Players.Players.Values.Any(p => p.Name == selectedOwner.Name)) return; SelectOwner(editorLayer.Players.Players.Values.First()); }; ownersDropDown.OnClick = () => { var owners = editorLayer.Players.Players.Values.OrderBy(p => p.Name); ownersDropDown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 270, owners, setupItem); }; ownersDropDown.Text = selectedOwner.Name; ownersDropDown.TextColor = selectedOwner.Color; var tileSetId = world.Map.Rules.TerrainInfo.Id; var allActorsTemp = new List(); foreach (var a in mapRules.Actors.Values) { // Partial templates are not allowed if (a.Name.Contains('^')) continue; // Actor must have a preview associated with it if (!a.HasTraitInfo()) continue; var editorData = a.TraitInfoOrDefault(); // Actor must be included in at least one category if (editorData == null || editorData.Categories == null) continue; // Excluded by tileset if (editorData.ExcludeTilesets != null && editorData.ExcludeTilesets.Contains(tileSetId)) continue; if (editorData.RequireTilesets != null && !editorData.RequireTilesets.Contains(tileSetId)) continue; var tooltip = a.TraitInfos().FirstOrDefault(ti => ti.EnabledByDefault) as TooltipInfoBase ?? a.TraitInfos().FirstOrDefault(ti => ti.EnabledByDefault); var searchTerms = new List() { a.Name }; if (tooltip != null) searchTerms.Add(tooltip.Name); var type = modData.Translation.GetString(Type); var tooltipText = (tooltip == null ? $"{type}: " : tooltip.Name + $"\n{type}: ") + a.Name; allActorsTemp.Add(new ActorSelectorActor(a, editorData.Categories, searchTerms.ToArray(), tooltipText)); } allActors = allActorsTemp.ToArray(); allCategories = allActors.SelectMany(ac => ac.Categories) .Distinct() .OrderBy(x => x) .ToArray(); foreach (var c in allCategories) { SelectedCategories.Add(c); FilteredCategories.Add(c); } SearchTextField.OnTextEdited = () => { searchFilter = SearchTextField.Text.Trim(); FilteredCategories.Clear(); if (!string.IsNullOrEmpty(searchFilter)) FilteredCategories.AddRange( allActors.Where(t => t.SearchTerms.Any( s => s.IndexOf(searchFilter, StringComparison.OrdinalIgnoreCase) >= 0)) .SelectMany(t => t.Categories) .Distinct() .OrderBy(x => x)); else FilteredCategories.AddRange(allCategories); InitializePreviews(); }; InitializePreviews(); } void SelectOwner(PlayerReference option) { selectedOwner = option; ownersDropDown.Text = option.Name; ownersDropDown.TextColor = option.Color; InitializePreviews(); var actor = editorCursor.Actor; if (actor != null) { actor.Owner = option; actor.ReplaceInit(new OwnerInit(option.Name)); actor.ReplaceInit(new FactionInit(option.Faction)); } } protected override void InitializePreviews() { Panel.RemoveChildren(); if (SelectedCategories.Count == 0) return; foreach (var a in allActors) { if (!SelectedCategories.Overlaps(a.Categories)) continue; if (!string.IsNullOrEmpty(searchFilter) && !a.SearchTerms.Any(s => s.IndexOf(searchFilter, StringComparison.OrdinalIgnoreCase) >= 0)) continue; var actor = a.Actor; var td = new TypeDictionary { new OwnerInit(selectedOwner.Name), new FactionInit(selectedOwner.Faction) }; foreach (var api in actor.TraitInfos()) foreach (var o in api.ActorPreviewInits(actor, ActorPreviewType.MapEditorSidebar)) td.Add(o); try { var item = ScrollItemWidget.Setup(ItemTemplate, () => editorCursor.Type == EditorCursorType.Actor && editorCursor.Actor.Info == actor, () => Editor.SetBrush(new EditorActorBrush(Editor, actor, selectedOwner, WorldRenderer))); var preview = item.Get("ACTOR_PREVIEW"); preview.SetPreview(actor, td); // Scale templates to fit within the panel var scale = 1f; if (scale * preview.IdealPreviewSize.X > ItemTemplate.Bounds.Width) scale = (ItemTemplate.Bounds.Width - Panel.ItemSpacing) / (float)preview.IdealPreviewSize.X; preview.GetScale = () => scale; preview.Bounds.Width = (int)(scale * preview.IdealPreviewSize.X); preview.Bounds.Height = (int)(scale * preview.IdealPreviewSize.Y); item.Bounds.Width = preview.Bounds.Width + 2 * preview.Bounds.X; item.Bounds.Height = preview.Bounds.Height + 2 * preview.Bounds.Y; item.IsVisible = () => true; item.GetTooltipText = () => a.Tooltip; Panel.AddChild(item); } catch { Log.Write("debug", "Map editor ignoring actor {0}, because of missing sprites for tileset {1}.", actor.Name, World.Map.Rules.TerrainInfo.Id); continue; } } } } }