Add Interactable lint test

Since # 19174 bounds are calculated from WDist instead of pixels. This causes many of the older maps to crash games, And this lint test makes it easier for map makers to detect these issues.

As calculating the bounds requires MapGrid, we need to write a separate lint test for Interactable instead of defaulting to RulesetLoaded checks.
This commit is contained in:
Gustas
2023-03-08 21:07:51 +02:00
committed by Matthias Mailänder
parent c525c48d25
commit 925e042455

View File

@@ -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<string> emitError, Action<string> emitWarning, ModData modData, Ruleset rules)
{
Run(emitError, rules, modData);
}
void ILintServerMapPass.Run(Action<string> emitError, Action<string> emitWarning, ModData modData, MapPreview map, Ruleset mapRules)
{
Run(emitError, mapRules, modData);
}
void Run(Action<string> 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<MapGrid>();
var tileScale = grid.Type == MapGridType.RectangularIsometric ? 1448 : 1024;
foreach (var actorInfo in rules.Actors)
{
// Catch TypeDictionary errors.
try
{
var interactable = actorInfo.Value.TraitInfoOrDefault<InteractableInfo>();
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;
}
}
}