/// <summary> /// This returns the height of the upper wall part. Returns 0 when no upper part exists. /// </summary> /*public int GetHighHeight() * { * Sidedef other = this.Other; * if(other != null) * { * int top = this.sector.CeilHeight; * int bottom = other.sector.CeilHeight; * int height = top - bottom; * return (height > 0) ? height : 0; * } * else * { * return 0; * } * }*/ /// <summary> /// This returns the height of the middle wall part. /// </summary> /*public int GetMiddleHeight() * { * Sidedef other = this.Other; * if(other != null) * { * int top = Math.Min(this.Sector.CeilHeight, other.Sector.CeilHeight); * int bottom = Math.Max(this.Sector.FloorHeight, other.Sector.FloorHeight); * int height = top - bottom; * return (height > 0) ? height : 0; * } * else * { * int top = this.Sector.CeilHeight; * int bottom = this.Sector.FloorHeight; * int height = top - bottom; * return (height > 0) ? height : 0; * } * }*/ /// <summary> /// This returns the height of the lower wall part. Returns 0 when no lower part exists. /// </summary> /*public int GetLowHeight() * { * Sidedef other = this.Other; * if(other != null) * { * int top = other.sector.FloorHeight; * int bottom = this.sector.FloorHeight; * int height = top - bottom; * return (height > 0) ? height : 0; * } * else * { * return 0; * } * }*/ // This copies textures to another sidedef // And possibly also the offsets public void AddTexturesTo(Sidedef s) { // s cannot be null if (s == null) { return; } s.BeforePropsChange(); bool copyoffsets = false; //TODO: investigate "masking wall" / "bottoms of invisible walls swapped" wall flags // Texture set? if (tileindex > 0) { s.tileindex = tileindex; copyoffsets = true; } // Masked texture set? if (maskedtileindex > 0 && s.Other != null) { s.maskedtileindex = maskedtileindex; copyoffsets = true; } // Copy offsets? if (copyoffsets) { s.offsetx = offsetx; s.offsety = offsety; } General.Map.IsChanged = true; }
// This detaches a sidedef internal void DetachSidedefP(LinkedListNode <Sidedef> l) { // Not disposing? if (!isdisposed) { // Remove sidedef updateneeded = true; ceilingplaneupdateneeded = true; floorplaneupdateneeded = true; triangulationneeded = true; sidedefs.Remove(l); // No more sidedefs left? if (sidedefs.Count == 0 && map.AutoRemove) { // This sector is now useless, dispose it this.Dispose(); } //mxd. FirstWall was detached? else if (l.Value == firstwall) { firstwall = (sidedefs.Count > 0 ? sidedefs.First.Value : null); } } }
// This detaches a sidedef from the front internal void DetachSidedefP(Sidedef s) { // Sidedef is on the front? if (front == s) { // Remove sidedef reference if (front != null) { front.SetLinedefP(null); } front = null; updateneeded = true; } // Sidedef is on the back? else if (back == s) { // Remove sidedef reference if (back != null) { back.SetLinedefP(null); } back = null; updateneeded = true; } }
// This changes sidedefs (used for joining lines) // target: The linedef on which to remove or create a new sidedef // front: Side on which to remove or create the sidedef (true for front side) // newside: The side from which to copy the properties to the new sidedef. // If this is null, no sidedef will be created (only removed) // Returns false when the operation could not be completed. private bool JoinChangeSidedefs(Linedef target, bool front, Sidedef newside) { // Change sidedefs if (front) { if (target.front != null) { target.front.Dispose(); } } else { if (target.back != null) { target.back.Dispose(); } } if (newside != null) { Sidedef sd = map.CreateSidedef(target, front, newside.Sector); if (sd == null) { return(false); } newside.CopyPropertiesTo(sd); sd.Marked = newside.Marked; } return(true); }
// Disposer public override void Dispose() { // Not already disposed? if (!isdisposed) { // Already set isdisposed so that changes can be prohibited isdisposed = true; // Dispose sidedefs if ((front != null) && map.AutoRemove) { front.Dispose(); } else { AttachFrontP(null); } if ((back != null) && map.AutoRemove) { back.Dispose(); } else { AttachBackP(null); } if (map == General.Map.Map) { General.Map.UndoRedo.RecRemLinedef(this); } // Remove from main list map.RemoveLinedef(listindex); // Detach from vertices if (startvertexlistitem != null) { start.DetachLinedefP(startvertexlistitem); } startvertexlistitem = null; start = null; if (endvertexlistitem != null) { end.DetachLinedefP(endvertexlistitem); } endvertexlistitem = null; end = null; // Clean up start = null; end = null; front = null; back = null; map = null; // Clean up base base.Dispose(); } }
// Passive version, does not record the change internal void AttachBackP(Sidedef s) { // Attach and recalculate back = s; if (back != null) { back.SetLinedefP(this); } updateneeded = true; }
// This attaches a sidedef on the back internal void AttachBack(Sidedef s) { if (map == General.Map.Map) { General.Map.UndoRedo.RecRefLinedefBack(this); } // Attach and recalculate AttachBackP(s); }
// Passive version, does not record the change internal void AttachFrontP(Sidedef s) { // Attach and recalculate front = s; if (front != null) { front.SetLinedefP(this); } updateneeded = true; }
// This attaches a sidedef and returns the listitem internal LinkedListNode <Sidedef> AttachSidedefP(Sidedef sd) { //mxd. Set first wall? if (firstwall == null) { firstwall = sd; } updateneeded = true; triangulationneeded = true; ceilingplaneupdateneeded = true; floorplaneupdateneeded = true; return(sidedefs.AddLast(sd)); }
// This splits this line by vertex v // Returns the new line resulting from the split, or null when it failed public Linedef Split(Vertex v) { // Copy linedef and change vertices Linedef nl = map.CreateLinedef(v, end); if (nl == null) { return(null); } CopyPropertiesTo(nl); SetEndVertex(v); nl.Selected = this.Selected; nl.marked = this.marked; // Copy front wall if exists if (front != null) { Sidedef nsd = map.CreateSidedef(nl, true, front.Sector); if (nsd == null) { return(null); } front.CopyPropertiesTo(nsd); nsd.Marked = front.Marked; // Make texture offset adjustments //TODO: works differently in Build... nsd.OffsetX += (int)Vector2D.Distance(this.start.Position, this.end.Position); } // Copy back wall if exists if (back != null) { Sidedef nsd = map.CreateSidedef(nl, false, back.Sector); if (nsd == null) { return(null); } back.CopyPropertiesTo(nsd); nsd.Marked = back.Marked; // Make texture offset adjustments //TODO: works differently in Build... back.OffsetX += (int)Vector2D.Distance(nl.start.Position, nl.end.Position); } // Return result General.Map.IsChanged = true; return(nl); }
// Disposer public override void Dispose() { // Not already disposed? if (!isdisposed) { // Already set isdisposed so that changes can be prohibited isdisposed = true; // Dispose the sidedefs that are attached to this sector // because a sidedef cannot exist without reference to its sector. if (map.AutoRemove) { foreach (Sidedef sd in sidedefs) { sd.Dispose(); } } else { foreach (Sidedef sd in sidedefs) { sd.SetSectorP(null); } } if (map == General.Map.Map) { General.Map.UndoRedo.RecRemSector(this); } // Remove from main list map.RemoveSector(listindex); // Free surface entry General.Map.CRenderer2D.Surfaces.FreeSurfaces(surfaceentries); // Clean up firstwall = null; sidedefs = null; map = null; // Dispose base base.Dispose(); } }
// This copies all properties to another sidedef public void CopyPropertiesTo(Sidedef s) { s.BeforePropsChange(); // Copy build properties s.flags = new Dictionary <string, bool>(flags, StringComparer.Ordinal); s.offsetx = offsetx; s.offsety = offsety; s.repeatx = repeatx; s.repeaty = repeaty; s.tileindex = tileindex; s.maskedtileindex = maskedtileindex; s.shade = shade; s.paletteindex = paletteindex; s.hitag = hitag; s.lotag = lotag; s.extra = extra; }
// This flips the sidedefs public void FlipSidedefs() { //mxd. FirstWalls may need updating if (front != null && front == front.Sector.FirstWall) { front.Sector.FirstWall = (back ?? General.GetByIndex(front.Sector.Sidedefs, 0)); } if (back != null && back == back.Sector.FirstWall) { back.Sector.FirstWall = (front ?? General.GetByIndex(back.Sector.Sidedefs, 0)); } // Flip sidedefs Sidedef oldfront = front; Sidedef oldback = back; AttachFront(oldback); AttachBack(oldfront); General.Map.IsChanged = true; }
// Constructor internal Sector(MapSet map, int listindex) { // Initialize this.map = map; this.listindex = listindex; this.sidedefs = new LinkedList <Sidedef>(); this.updateneeded = true; this.ceilingplaneupdateneeded = true; this.floorplaneupdateneeded = true; this.triangulationneeded = true; this.surfaceentries = new SurfaceEntryCollection(); this.floorflags = new Dictionary <string, bool>(StringComparer.Ordinal); this.ceilingflags = new Dictionary <string, bool>(StringComparer.Ordinal); this.firstwall = null; this.extra = -1; this.floorslope = Angle2D.PIHALF; //mxd this.ceilingslope = Angle2D.PIHALF; //mxd if (map == General.Map.Map) { General.Map.UndoRedo.RecAddSector(this); } }
//mxd. Moved here from Tools. This finds the ideal label position for a sector private static LabelPositionInfo FindLabelPosition(Sector s) { // Do we have a triangulation? Triangulation triangles = s.Triangles; if (triangles != null) { // Go for all islands foreach (int iv in triangles.IslandVertices) { Dictionary <Sidedef, Linedef> sides = new Dictionary <Sidedef, Linedef>(iv >> 1); List <Vector2D> candidatepositions = new List <Vector2D>(iv >> 1); float founddistance = float.MinValue; Vector2D foundposition = new Vector2D(); float minx = float.MaxValue; float miny = float.MaxValue; float maxx = float.MinValue; float maxy = float.MinValue; // Make candidate lines that are not along sidedefs // We do this before testing the candidate against the sidedefs so that // we can collect the relevant sidedefs first in the same run for (int t = 0; t < iv; t += 3) { Vector2D v1 = triangles.Vertices[t + 2]; Sidedef sd = triangles.Sidedefs[t + 2]; for (int v = 0; v < 3; v++) { Vector2D v2 = triangles.Vertices[t + v]; // Not along a sidedef? Then this line is across the sector // and guaranteed to be inside the sector! if (sd == null) { // Make the line candidatepositions.Add(v1 + (v2 - v1) * 0.5f); } else { // This sidedefs is part of this island and must be checked // so add it to the dictionary sides[sd] = sd.Line; } // Make bbox of this island minx = Math.Min(minx, v1.x); miny = Math.Min(miny, v1.y); maxx = Math.Max(maxx, v1.x); maxy = Math.Max(maxy, v1.y); // Next sd = triangles.Sidedefs[t + v]; v1 = v2; } } // Any candidate lines found at all? if (candidatepositions.Count > 0) { // Start with the first line foreach (Vector2D candidatepos in candidatepositions) { // Check distance against other lines float smallestdist = int.MaxValue; foreach (KeyValuePair <Sidedef, Linedef> sd in sides) { // Check the distance float distance = sd.Value.DistanceToSq(candidatepos, true); smallestdist = Math.Min(smallestdist, distance); } // Keep this candidate if it is better than previous if (smallestdist > founddistance) { foundposition = candidatepos; founddistance = smallestdist; } } // No cceptable line found, just use the first! return(new LabelPositionInfo(foundposition, (float)Math.Sqrt(founddistance))); } // No candidate lines found. // Check to see if the island is a triangle if (iv == 3) { // Use the center of the triangle // TODO: Use the 'incenter' instead, see http://mathworld.wolfram.com/Incenter.html Vector2D v = (triangles.Vertices[0] + triangles.Vertices[1] + triangles.Vertices[2]) / 3.0f; float d = Line2D.GetDistanceToLineSq(triangles.Vertices[0], triangles.Vertices[1], v, false); d = Math.Min(d, Line2D.GetDistanceToLineSq(triangles.Vertices[1], triangles.Vertices[2], v, false)); d = Math.Min(d, Line2D.GetDistanceToLineSq(triangles.Vertices[2], triangles.Vertices[0], v, false)); return(new LabelPositionInfo(v, (float)Math.Sqrt(d))); } else { // Use the center of this island. float d = Math.Min((maxx - minx) * 0.5f, (maxy - miny) * 0.5f); return(new LabelPositionInfo(new Vector2D(minx + (maxx - minx) * 0.5f, miny + (maxy - miny) * 0.5f), d)); } } } else { // No triangulation was made. FAIL! General.Fail("No triangulation exists for sector " + s + " Triangulation is required to create label positions for a sector."); } // Nothing found... return(new LabelPositionInfo()); }
// Serialize / deserialize (passive: this doesn't record) internal void ReadWrite(IReadWriteStream s) { if (!s.IsWriting) { BeforePropsChange(); updateneeded = true; ceilingplaneupdateneeded = true; floorplaneupdateneeded = true; } ReadWrite(s, ref floorflags); ReadWrite(s, ref ceilingflags); if (s.IsWriting) { int firstwallindex = (firstwall == null ? -1 : firstwall.Index); s.rwInt(ref firstwallindex); } else { int firstwallindex = -1; s.rwInt(ref firstwallindex); if (firstwallindex != -1) { foreach (var wall in sidedefs) { if (wall.Index == firstwallindex) { firstwall = wall; break; } } if (firstwall == null) { throw new Exception("Failed to restore FirstWall!"); } } } s.rwInt(ref ceilingheight); s.rwInt(ref floorheight); s.rwInt(ref ceilingtileindex); s.rwFloat(ref ceilingslope); s.rwInt(ref ceilingshade); s.rwInt(ref ceilingpaletteindex); s.rwInt(ref ceilingoffsetx); s.rwInt(ref ceilingoffsety); s.rwInt(ref floortileindex); s.rwFloat(ref floorslope); s.rwInt(ref floorshade); s.rwInt(ref floorpaletteindex); s.rwInt(ref flooroffsetx); s.rwInt(ref flooroffsety); s.rwInt(ref visibility); s.rwInt(ref hitag); s.rwInt(ref lotag); s.rwInt(ref extra); }