diff --git a/OpenRA.Mods.Common/Lint/CheckInteractable.cs b/OpenRA.Mods.Common/Lint/CheckInteractable.cs new file mode 100644 index 0000000000..67e4c5551c --- /dev/null +++ b/OpenRA.Mods.Common/Lint/CheckInteractable.cs @@ -0,0 +1,71 @@ +#region Copyright & License Information +/* + * Copyright (c) The OpenRA Developers and Contributors + * 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 OpenRA.Mods.Common.Traits; +using OpenRA.Primitives; +using OpenRA.Server; + +namespace OpenRA.Mods.Common.Lint +{ + class CheckInteractable : ILintRulesPass, ILintServerMapPass + { + void ILintRulesPass.Run(Action emitError, Action emitWarning, ModData modData, Ruleset rules) + { + Run(emitError, rules, modData); + } + + void ILintServerMapPass.Run(Action emitError, Action emitWarning, ModData modData, MapPreview map, Ruleset mapRules) + { + Run(emitError, mapRules, modData); + } + + void Run(Action emitError, Ruleset rules, ModData modData) + { + // As the map has not been created we need to get MapGrid info directly from manifest. + var grid = modData.Manifest.Get(); + var tileScale = grid.Type == MapGridType.RectangularIsometric ? 1448 : 1024; + + foreach (var actorInfo in rules.Actors) + { + // Catch TypeDictionary errors. + try + { + var interactable = actorInfo.Value.TraitInfoOrDefault(); + if (interactable == null) + continue; + + if (HasInvalidBounds(interactable.Bounds, grid.TileSize, tileScale)) + emitError($"{nameof(interactable.Bounds)} of actor {actorInfo.Key} are empty or negative."); + + if (HasInvalidBounds(interactable.DecorationBounds, grid.TileSize, tileScale)) + emitError($"{nameof(interactable.DecorationBounds)} of actor {actorInfo.Key} are empty or negative."); + } + catch (InvalidOperationException e) + { + emitError($"{e.Message} (Actor type `{actorInfo.Key}`)"); + } + } + } + + static bool HasInvalidBounds(WDist[] bounds, Size tileSize, int tileScale) + { + if (bounds == null) + return false; + + if (bounds.Length != 2 && bounds.Length != 4) + return true; + + var size = new int2(bounds[0].Length * tileSize.Width / tileScale, bounds[1].Length * tileSize.Height / tileScale); + return size.X <= 0 || size.Y <= 0; + } + } +}