diff --git a/Build/Configurations/Includes/ZDoom_things.cfg b/Build/Configurations/Includes/ZDoom_things.cfg index 9165e08b..5fdc58d5 100755 --- a/Build/Configurations/Includes/ZDoom_things.cfg +++ b/Build/Configurations/Includes/ZDoom_things.cfg @@ -853,6 +853,7 @@ zdoom title = "Interpolation Point"; sprite = "internal:InterpolationPoint"; class = "InterpolationPoint"; + thinglink = 9075; arg0 { title = "Pitch"; @@ -885,6 +886,7 @@ zdoom title = "Interpolation Special"; sprite = "internal:InterpolationSpecial"; class = "InterpolationSpecial"; + thinglink = -9070; } 9072 @@ -952,6 +954,7 @@ zdoom { title = "Patrol Special"; class = "PatrolSpecial"; + thinglink = -9024; } } @@ -1248,6 +1251,7 @@ zdoom title = "Patrol Point"; sprite = "internal:PathFollower"; class = "PatrolPoint"; + thinglink = 9047; arg0 { title = "Next Patrol Point Tag"; diff --git a/Source/Core/Config/ThingTypeInfo.cs b/Source/Core/Config/ThingTypeInfo.cs index a0d66cdd..a70a9b72 100755 --- a/Source/Core/Config/ThingTypeInfo.cs +++ b/Source/Core/Config/ThingTypeInfo.cs @@ -87,6 +87,7 @@ namespace CodeImp.DoomBuilder.Config private bool obsolete; //mxd private string obsoletemessage; //mxd private Dictionary> flagsrename; //mxd. > + private int thinglink; //mxd. GZDoom rendering properties private ThingRenderMode rendermode; @@ -137,6 +138,8 @@ namespace CodeImp.DoomBuilder.Config public bool RollSprite { get { return rollsprite; } } public bool RollCenter { get { return rollcenter; } } + public int ThingLink { get { return thinglink; } } + //mxd. Ambinent sound info public AmbientSoundInfo AmbientSound { get { return ambientsound; } internal set { ambientsound = value; } } @@ -176,6 +179,7 @@ namespace CodeImp.DoomBuilder.Config this.xybillboard = false; this.locksprite = false; //mxd this.flagsrename = new Dictionary>(StringComparer.OrdinalIgnoreCase); //mxd + this.thinglink = 0; // We have no destructor GC.SuppressFinalize(this); @@ -215,6 +219,7 @@ namespace CodeImp.DoomBuilder.Config this.spritescale = new SizeF(sscale, sscale); this.locksprite = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".locksprite", false); //mxd this.classname = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".class", String.Empty); //mxd + this.thinglink = cfg.ReadSetting("thingtypes." + cat.Name + "." + key + ".thinglink", 0); //mxd. Read flagsrename this.flagsrename = new Dictionary>(StringComparer.OrdinalIgnoreCase); diff --git a/Source/Core/GZBuilder/Data/LinksCollector.cs b/Source/Core/GZBuilder/Data/LinksCollector.cs index 63cb4683..a45256c0 100755 --- a/Source/Core/GZBuilder/Data/LinksCollector.cs +++ b/Source/Core/GZBuilder/Data/LinksCollector.cs @@ -19,7 +19,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data private class SpecialThings { public readonly Dictionary> PatrolPoints; // PatrolPoint tag, list of PatrolPoints + public readonly List PatrolSpecials; public readonly Dictionary> InterpolationPoints; // InterpolationPoint tag, list of InterpolationPoints + public readonly List InterpolationSpecials; public readonly List ThingsWithGoal; public readonly List Cameras; public readonly Dictionary> ActorMovers; // ActorMover target tag, list of ActorMovers @@ -30,7 +32,9 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data public SpecialThings() { PatrolPoints = new Dictionary>(); + PatrolSpecials = new List(); InterpolationPoints = new Dictionary>(); + InterpolationSpecials = new List(); ThingsWithGoal = new List(); Cameras = new List(); ActorMovers = new Dictionary>(); @@ -158,6 +162,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data } break; + case "patrolspecial": + result.PatrolSpecials.Add(t); + break; + case "$polyanchor": if(!result.PolyobjectAnchors.ContainsKey(t.AngleDoom)) result.PolyobjectAnchors[t.AngleDoom] = new List(); result.PolyobjectAnchors[t.AngleDoom].Add(t); @@ -194,6 +202,10 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data result.InterpolationPoints[t.Tag].Add(new PathNode(t, blockmap)); break; + case "interpolationspecial": + result.InterpolationSpecials.Add(t); + break; + case "movingcamera": if(t.Args[0] != 0 || t.Args[1] != 0) result.Cameras.Add(t); break; @@ -269,6 +281,23 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data } } + // Process patrol specials + foreach (Thing t in result.PatrolSpecials) + { + if (!result.PatrolPoints.ContainsKey(t.Tag)) continue; + + start = t.Position; + start.z += GetCorrectHeight(t, blockmap, true); + + foreach (Thing tt in result.PatrolPoints[t.Tag]) + { + end = tt.Position; + end.z += GetCorrectHeight(tt, blockmap, true); + + lines.Add(new Line3D(start, end, General.Colors.Selection)); + } + } + // Process cameras [CAN USE INTERPOLATION] foreach(Thing t in result.Cameras) { @@ -382,6 +411,24 @@ namespace CodeImp.DoomBuilder.GZBuilder.Data foreach(PathNode node in group.Value) node.PropagateCurvedFlag(); } + // Process interpolation specials + foreach (Thing t in result.InterpolationSpecials) + { + int targettag = t.Tag; + if (targettag == 0 || !result.InterpolationPoints.ContainsKey(targettag)) continue; //no target / target doesn't exist + + start = t.Position; + start.z += GetCorrectHeight(t, blockmap, true); + + foreach (PathNode node in result.InterpolationPoints[targettag]) + { + //Do not connect specials to the first or last node of a curved path, since those are used as spline control points only + if (node.IsCurved && (node.PreviousNodes.Count == 0 || node.NextNodes.Count == 0)) + continue; + lines.Add(new Line3D(start, node.Position, General.Colors.Selection)); + } + } + // 3. Make lines HashSet processedindices = new HashSet(); foreach(KeyValuePair> group in result.InterpolationPoints) diff --git a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs index 933a44bd..cffed8b8 100755 --- a/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs +++ b/Source/Plugins/BuilderModes/ClassicModes/ThingsMode.cs @@ -56,6 +56,7 @@ namespace CodeImp.DoomBuilder.BuilderModes // Highlighted item private Thing highlighted; private readonly Association[] association = new Association[Thing.NUM_ARGS]; + private readonly Association directasso = new Association(); private readonly Association highlightasso = new Association(); // Interface @@ -88,6 +89,7 @@ namespace CodeImp.DoomBuilder.BuilderModes { //mxd. Associations now requre initializing... for(int i = 0; i < association.Length; i++) association[i] = new Association(); + directasso = new Association(); } //mxd @@ -244,6 +246,7 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, General.Settings.HiddenThingsAlpha); renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, alpha); for(int i = 0; i < Thing.NUM_ARGS; i++) BuilderPlug.RenderAssociations(renderer, association[i], eventlines); + BuilderPlug.RenderAssociations(renderer, directasso, eventlines); if(highlighted != null && !highlighted.IsDisposed) { @@ -353,8 +356,14 @@ namespace CodeImp.DoomBuilder.BuilderModes //mxd. Update label color? if(labels.ContainsKey(t)) labels[t].Color = General.Colors.Selection; + //check if this thing directly links to another type of thing + ThingTypeInfo ti = General.Map.Data.GetThingInfoEx(t.Type); + int linktype = 0; + if (ti != null) + linktype = ti.ThingLink; + // New association highlights something? - if(t.Tag != 0) highlightasso.Set(t.Position, t.Tag, UniversalType.ThingTag); + if(t.Tag != 0) highlightasso.Set(t.Position, t.Tag, UniversalType.ThingTag, linktype); } else { @@ -382,6 +391,11 @@ namespace CodeImp.DoomBuilder.BuilderModes LinedefActionInfo action = General.Map.Config.LinedefActions[t.Action]; for(int i = 0; i < Thing.NUM_ARGS; i++) association[i].Set(t.Position, t.Args[i], action.Args[i].Type); + + //Some things, such as Patrol and Interpolation specials, are associated via a shared tag rather than an argument + ThingTypeInfo ti = General.Map.Data.GetThingInfoEx(t.Type); + if (ti != null && ti.ThingLink < 0) + directasso.Set(t.Position, t.Tag, (int)UniversalType.ThingTag); } //mxd. Check if we can use thing arguments else if(t.Action == 0) @@ -398,7 +412,11 @@ namespace CodeImp.DoomBuilder.BuilderModes // mxd. Clear associations? if(clearassociations) - for(int i = 0; i < Thing.NUM_ARGS; i++) association[i].Set(new Vector2D(), 0, 0); + { + for (int i = 0; i < Thing.NUM_ARGS; i++) + association[i].Set(new Vector2D(), 0, 0); + directasso.Set(new Vector2D(), 0, 0); + } // Set new highlight and redraw display highlighted = t; diff --git a/Source/Plugins/BuilderModes/General/Association.cs b/Source/Plugins/BuilderModes/General/Association.cs index 78ecedee..7b6e4809 100755 --- a/Source/Plugins/BuilderModes/General/Association.cs +++ b/Source/Plugins/BuilderModes/General/Association.cs @@ -29,10 +29,12 @@ namespace CodeImp.DoomBuilder.BuilderModes private HashSet tags; private Vector2D center; private UniversalType type; + private int directlinktype; public HashSet Tags { get { return tags; } } public Vector2D Center { get { return center; } } public UniversalType Type { get { return type; } } + public int DirectLinkType { get { return directlinktype; } } //mxd. This sets up the association public Association() @@ -74,34 +76,51 @@ namespace CodeImp.DoomBuilder.BuilderModes // This sets up the association public void Set(Vector2D center, int tag, int type) + { + this.Set(center, tag, type, 0); + } + + public void Set(Vector2D center, int tag, int type, int directlinktype) { this.tags = new HashSet { tag }; //mxd this.type = (UniversalType)type; this.center = center; + this.directlinktype = directlinktype; } // This sets up the association public void Set(Vector2D center, int tag, UniversalType type) + { + this.Set(center, tag, type, 0); + } + + public void Set(Vector2D center, int tag, UniversalType type, int directlinktype) { this.tags = new HashSet { tag }; //mxd this.type = type; this.center = center; + this.directlinktype = directlinktype; } //mxd. This also sets up the association public void Set(Vector2D center, IEnumerable tags, int type) { - this.tags = new HashSet(tags); //mxd - this.type = (UniversalType)type; - this.center = center; + this.Set(center, tags, (UniversalType)type, 0); } //mxd. This also sets up the association public void Set(Vector2D center, IEnumerable tags, UniversalType type) + { + this.Set(center, tags, type, 0); + } + + //mxd. This also sets up the association + public void Set(Vector2D center, IEnumerable tags, UniversalType type, int directlinktype) { this.tags = new HashSet(tags); //mxd this.type = type; this.center = center; + this.directlinktype = directlinktype; } // This compares an association diff --git a/Source/Plugins/BuilderModes/General/BuilderPlug.cs b/Source/Plugins/BuilderModes/General/BuilderPlug.cs index 1d306917..b315a234 100755 --- a/Source/Plugins/BuilderModes/General/BuilderPlug.cs +++ b/Source/Plugins/BuilderModes/General/BuilderPlug.cs @@ -632,6 +632,12 @@ namespace CodeImp.DoomBuilder.BuilderModes foreach(Thing t in General.Map.Map.Things) { if(!asso.Tags.Contains(t.Tag)) continue; + + //Do not draw the association if the user is hovering over a child link + ThingTypeInfo ti = General.Map.Data.GetThingInfoEx(t.Type); + if (ti != null && ti.ThingLink < 0) + continue; + renderer.RenderThing(t, General.Colors.Indication, General.Settings.ActiveThingsAlpha); if(General.Settings.GZShowEventLines) eventlines.Add(new Line3D(asso.Center, t.Position)); //mxd } @@ -699,9 +705,17 @@ namespace CodeImp.DoomBuilder.BuilderModes // Things foreach(Thing t in General.Map.Map.Things) { + // Get the thing type info + ThingTypeInfo ti = General.Map.Data.GetThingInfoEx(t.Type); + // Known action on this thing? if((t.Action > 0) && General.Map.Config.LinedefActions.ContainsKey(t.Action)) { + //Do not draw the association if this is a child link. + // This prevents a reverse link to a thing via an argument, when it should be a direct tag-to-tag link instead. + if(ti != null && asso.DirectLinkType < 0 && asso.DirectLinkType != -t.Type) + continue; + LinedefActionInfo action = General.Map.Config.LinedefActions[t.Action]; if( ((action.Args[0].Type == (int)asso.Type) && (asso.Tags.Contains(t.Args[0]))) || ((action.Args[1].Type == (int)asso.Type) && (asso.Tags.Contains(t.Args[1]))) || @@ -712,12 +726,20 @@ namespace CodeImp.DoomBuilder.BuilderModes renderer.RenderThing(t, General.Colors.Indication, General.Settings.ActiveThingsAlpha); if(General.Settings.GZShowEventLines) eventlines.Add(new Line3D(t.Position, asso.Center)); //mxd } + + //If there is a link setup on this thing, and it matches the association, then draw a direct link to any matching tag + if(ti != null && asso.DirectLinkType == t.Type && asso.Tags.Contains(t.Tag)) + { + renderer.RenderThing(t, General.Colors.Indication, General.Settings.ActiveThingsAlpha); + if (General.Settings.GZShowEventLines) eventlines.Add(new Line3D(t.Position, asso.Center)); + } } //mxd. Thing action on this thing? else if(t.Action == 0) { - ThingTypeInfo ti = General.Map.Data.GetThingInfoEx(t.Type); - if(ti != null) + //Draw the association, unless it is a child link. + // This prevents a reverse link to a thing via an argument, when it should be a direct tag-to-tag link instead. + if(ti != null && asso.DirectLinkType >= 0 && Math.Abs(asso.DirectLinkType) != t.Type) { if( ((ti.Args[0].Type == (int)asso.Type) && (asso.Tags.Contains(t.Args[0]))) || ((ti.Args[1].Type == (int)asso.Type) && (asso.Tags.Contains(t.Args[1]))) ||