Added, Map Analysis mode: added "Check Polyobjects" checker.

Changed, Game configurations: "Polyobject Anchor" things should not trigger "Thing outside the map geometry" error in the Map Analysis mode.
Updated ZDoom_DECORATE.cfg (A_SetScale).
This commit is contained in:
MaxED 2016-03-09 14:52:18 +00:00 committed by spherallic
parent 41e4b26d79
commit 16e476f02e
7 changed files with 356 additions and 1 deletions

View file

@ -1518,6 +1518,7 @@ other
title = "Polyobject Anchor";
sprite = "internal:anchor";
fixedrotation = true;
error = 0; // Can be outside of map geometry
}
3001
{

View file

@ -1091,6 +1091,7 @@ zdoom
sprite = "internal:anchor";
class = "$PolyAnchor";
fixedrotation = true;
error = 0; // Can be outside of map geometry
}
9301

View file

@ -238,7 +238,7 @@ keywords
A_SetRipMin = "A_SetRipMin(int min)";
A_SetRipMax = "A_SetRipMax(int max)";
A_SetRoll = "A_SetRoll(float roll[, int flags = 0[, int pointer = AAPTR_DEFAULT]])";
A_SetScale = "A_SetScale(float scaleX[, float scaleY = scaleX[, int pointer = AAPTR_DEFAULT]])";
A_SetScale = "A_SetScale(float scaleX[, float scaleY = scaleX[, int pointer = AAPTR_DEFAULT[, bool usezero = false]]])";
A_SetShadow = "A_SetShadow";
A_SetShootable = "A_SetShootable";
A_SetSolid = "A_SetSolid";

View file

@ -347,6 +347,7 @@
<Compile Include="ErrorChecks\CheckMissingTextures.cs" />
<Compile Include="ErrorChecks\CheckObsoleteThings.cs" />
<Compile Include="ErrorChecks\CheckOverlappingVertices.cs" />
<Compile Include="ErrorChecks\CheckPolyobjects.cs" />
<Compile Include="ErrorChecks\CheckShortLinedefs.cs" />
<Compile Include="ErrorChecks\CheckStrayVertices.cs" />
<Compile Include="ErrorChecks\CheckTextureAlignment.cs" />
@ -355,6 +356,8 @@
<Compile Include="ErrorChecks\CheckUnknownThings.cs" />
<Compile Include="ErrorChecks\CheckUnusedTextures.cs" />
<Compile Include="ErrorChecks\CheckUnusedThings.cs" />
<Compile Include="ErrorChecks\ResultInvalidPolyobjectLines.cs" />
<Compile Include="ErrorChecks\ResultInvalidPolyobjectThings.cs" />
<Compile Include="ErrorChecks\ResultMapTooBig.cs" />
<Compile Include="ErrorChecks\ResultMissingFlat.cs" />
<Compile Include="ErrorChecks\ResultNoErrors.cs" />

View file

@ -0,0 +1,167 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Map;
using System.Threading;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
[ErrorChecker("Check Polyobjects", true, 100)]
public class CheckPolyobjects : ErrorChecker
{
#region ================== Constants
private const int PROGRESS_STEP = 1000;
#endregion
#region ================== Constructor / Destructor
public CheckPolyobjects()
{
// Total progress is somewhat done when all linedefs and things are checked
SetTotalProgress((General.Map.Map.Linedefs.Count + General.Map.Map.Things.Count) / PROGRESS_STEP);
}
#endregion
#region ================== Methods
// This runs the check
public override void Run()
{
int progress = 0;
int stepprogress = 0;
const string Polyobj_StartLine = "Polyobj_StartLine";
// <Polyobj_Action, <Polyobj_number, Lines using this number>>
Dictionary<string, Dictionary<int, List<Linedef>>> polyobjlines = new Dictionary<string, Dictionary<int, List<Linedef>>>();
// All polyobject-related actions
HashSet<string> allactions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
Polyobj_StartLine, "Polyobj_RotateLeft",
"Polyobj_RotateRight", "Polyobj_Move",
"Polyobj_MoveTimes8", "Polyobj_DoorSwing",
"Polyobj_DoorSlide", "Polyobj_OR_MoveToSpot",
"Polyobj_MoveToSpot", "Polyobj_Stop",
"Polyobj_MoveTo", "Polyobj_OR_MoveTo",
"Polyobj_OR_RotateLeft", "Polyobj_OR_RotateRight",
"Polyobj_OR_Move", "Polyobj_OR_MoveTimes8"
};
Dictionary<int, List<Thing>> anchors = new Dictionary<int, List<Thing>>();
Dictionary<int, List<Thing>> startspots = new Dictionary<int, List<Thing>>();
// Collect Linedefs...
foreach(Linedef l in General.Map.Map.Linedefs)
{
if(l.Action > 0 && General.Map.Config.LinedefActions.ContainsKey(l.Action) && allactions.Contains(General.Map.Config.LinedefActions[l.Action].Id))
{
string id = General.Map.Config.LinedefActions[l.Action].Id;
if(!polyobjlines.ContainsKey(id))
polyobjlines.Add(id, new Dictionary<int, List<Linedef>>());
// Polyobj number is always the first arg
if(!polyobjlines[id].ContainsKey(l.Args[0]))
polyobjlines[id].Add(l.Args[0], new List<Linedef>());
polyobjlines[id][l.Args[0]].Add(l);
}
UpdateProgress(ref progress, ref stepprogress);
}
// Collect Things...
foreach(Thing t in General.Map.Map.Things)
{
ThingTypeInfo info = General.Map.Data.GetThingInfoEx(t.Type);
if(info == null) continue;
switch(info.ClassName.ToLowerInvariant())
{
case "$polyanchor":
if(!anchors.ContainsKey(t.AngleDoom)) anchors.Add(t.AngleDoom, new List<Thing>());
anchors[t.AngleDoom].Add(t);
break;
case "$polyspawn":
case "$polyspawncrush":
case "$polyspawnhurt":
if(!startspots.ContainsKey(t.AngleDoom)) startspots.Add(t.AngleDoom, new List<Thing>());
startspots[t.AngleDoom].Add(t);
break;
}
UpdateProgress(ref progress, ref stepprogress);
}
// Check Linedefs. These can connect 1 - multiple (except Polyobj_StartLine)
// Polyobject number is always arg0.
foreach(KeyValuePair<string, Dictionary<int, List<Linedef>>> group in polyobjlines)
{
foreach(KeyValuePair<int, List<Linedef>> linesbytype in group.Value)
{
if(!startspots.ContainsKey(linesbytype.Key))
SubmitResult(new ResultInvalidPolyobjectLines(linesbytype.Value, "\"" + group.Key + "\" action targets non-existing Polyobject Start Spot (" + linesbytype.Key + ")"));
}
}
// Check Linedefs with Polyobj_StartLine action. These must connect 1 - 1.
// Polyobject number is arg0, Mirror polyobject number is arg1
foreach(KeyValuePair<int, List<Linedef>> linesbytype in polyobjlines[Polyobj_StartLine])
{
// Should be only one Polyobj_StartLine per Polyobject number
if(linesbytype.Value.Count > 1)
SubmitResult(new ResultInvalidPolyobjectLines(linesbytype.Value, "Several \"" + Polyobj_StartLine + "\" actions have the same Polyobject Number assigned (" + linesbytype.Key + "). They won't function correctly ingame."));
// Check if Mirror Polyobject Number exists
foreach(Linedef linedef in linesbytype.Value)
{
if(!startspots.ContainsKey(linedef.Args[1]))
SubmitResult(new ResultInvalidPolyobjectLines(new List<Linedef> { linedef }, "\"" + Polyobj_StartLine + "\" action have non-existing Mirror Polyobject Number assigned (" + linedef.Args[1] + "). It won't function correctly ingame."));
}
}
// Check Polyobject Anchors. These must connect 1 - 1.
foreach(KeyValuePair<int, List<Thing>> group in anchors)
{
if(!startspots.ContainsKey(group.Key))
SubmitResult(new ResultInvalidPolyobjectThings(group.Value, "Polyobject " + (group.Value.Count > 1 ? "Anchors target" : "Anchor targets") + " non-existing Polyobject Start Spot (" + group.Key + ")"));
if(group.Value.Count > 1)
SubmitResult(new ResultInvalidPolyobjectThings(group.Value, "Several Polyobject Anchors target the same Polyobject Start Spot (" + group.Key + "). They won't function correctly ingame."));
}
// Check Polyobject Start Spots. These must connect 1 - 1.
foreach(KeyValuePair<int, List<Thing>> group in startspots)
{
if(!anchors.ContainsKey(group.Key))
SubmitResult(new ResultInvalidPolyobjectThings(group.Value, "Polyobject Start " + (group.Value.Count > 1 ? "Spots are not targeted" : "Spot " + group.Key + " is not targeted") + " by any Polyobject Anchor"));
if(group.Value.Count > 1)
SubmitResult(new ResultInvalidPolyobjectThings(group.Value, "Several Polyobject Start Spots have the same Polyobject number (" + group.Key + "). They won't function correctly ingame."));
}
}
private void UpdateProgress(ref int progress, ref int stepprogress)
{
// Handle thread interruption
try { Thread.Sleep(0); }
catch(ThreadInterruptedException) { return; }
// We are making progress!
if((++progress / PROGRESS_STEP) > stepprogress)
{
stepprogress = (progress / PROGRESS_STEP);
AddProgress(1);
}
}
#endregion
}
}

View file

@ -0,0 +1,94 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
public class ResultInvalidPolyobjectLines : ErrorResult
{
#region ================== Variables
private readonly List<Linedef> lines;
private readonly string linesinfo;
#endregion
#region ================== Properties
public override int Buttons { get { return 0; } }
#endregion
#region ================== Constructor / Destructor
public ResultInvalidPolyobjectLines(List<Linedef> lines, string details)
{
// Initialize
this.lines = lines;
this.hidden = true;
foreach(Linedef l in lines)
{
this.viewobjects.Add(l);
this.hidden &= l.IgnoredErrorChecks.Contains(this.GetType());
}
if(lines.Count == 1)
{
linesinfo = "Incorrect Polyobject setup for linedef " + lines[0].Index;
}
else
{
linesinfo = "Incorrect Polyobject setup for linedefs " + lines[0].Index;
for(int i = 1; i < lines.Count - 1; i++) linesinfo += ", " + lines[i].Index;
linesinfo += " and " + lines[lines.Count - 1].Index;
}
this.description = linesinfo + ": " + details;
}
#endregion
#region ================== Methods
// This sets if this result is displayed in ErrorCheckForm (mxd)
internal override void Hide(bool hide)
{
hidden = hide;
Type t = this.GetType();
if(hide)
{
foreach(Linedef l in lines)
l.IgnoredErrorChecks.Add(t);
}
else
{
foreach(Linedef l in lines)
if(l.IgnoredErrorChecks.Contains(t)) l.IgnoredErrorChecks.Remove(t);
}
}
// This must return the string that is displayed in the listbox
public override string ToString()
{
return linesinfo;
}
// Rendering
public override void PlotSelection(IRenderer2D renderer)
{
foreach(Linedef l in lines)
{
renderer.PlotLinedef(l, General.Colors.Selection);
renderer.PlotVertex(l.Start, ColorCollection.VERTICES);
renderer.PlotVertex(l.End, ColorCollection.VERTICES);
}
}
#endregion
}
}

View file

@ -0,0 +1,89 @@
#region ================== Namespaces
using System;
using System.Collections.Generic;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
public class ResultInvalidPolyobjectThings : ErrorResult
{
#region ================== Variables
private readonly List<Thing> things;
private readonly string thingsinfo;
#endregion
#region ================== Properties
public override int Buttons { get { return 0; } }
#endregion
#region ================== Constructor / Destructor
public ResultInvalidPolyobjectThings(List<Thing> things, string details)
{
// Initialize
this.things = things;
this.hidden = true;
foreach(Thing t in things)
{
this.viewobjects.Add(t);
this.hidden &= t.IgnoredErrorChecks.Contains(this.GetType());
}
if(things.Count == 1)
{
thingsinfo = "Incorrect Polyobject setup for thing " + things[0].Index;
}
else
{
thingsinfo = "Incorrect Polyobject setup for things " + things[0].Index;
for(int i = 1; i < things.Count - 1; i++) thingsinfo += ", " + things[i].Index;
thingsinfo += " and " + things[things.Count - 1].Index;
}
this.description = thingsinfo + ": " + details;
}
#endregion
#region ================== Methods
// This sets if this result is displayed in ErrorCheckForm (mxd)
internal override void Hide(bool hide)
{
hidden = hide;
Type t = this.GetType();
if(hide)
{
foreach(Thing thing in things) thing.IgnoredErrorChecks.Add(t);
}
else
{
foreach(Thing thing in things)
if(thing.IgnoredErrorChecks.Contains(t)) thing.IgnoredErrorChecks.Remove(t);
}
}
// This must return the string that is displayed in the listbox
public override string ToString()
{
return thingsinfo;
}
// Rendering
public override void RenderOverlaySelection(IRenderer2D renderer)
{
foreach(Thing thing in things)
renderer.RenderThing(thing, General.Colors.Selection, Presentation.THINGS_ALPHA);
}
#endregion
}
}