diff --git a/Source/Core/Geometry/Tools.cs b/Source/Core/Geometry/Tools.cs index 76253048..114cbc0a 100644 --- a/Source/Core/Geometry/Tools.cs +++ b/Source/Core/Geometry/Tools.cs @@ -1431,50 +1431,15 @@ namespace CodeImp.DoomBuilder.Geometry } //mxd - if(autoAlignTextureOffsets) { + if(autoAlignTextureOffsets && !General.Map.UDMF) { //Auto-align new lines if(newlines.Count > 1) { - float curLength = 0f; float totalLength = 0f; foreach(Linedef l in newlines) totalLength += l.Length; - bool reversed = newlines[0].End != newlines[1].Start; - - foreach(Linedef l in newlines) { - if(l.Front != null) { - ImageData texture = null; - - if(l.Front.MiddleRequired() && l.Front.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.MiddleTexture)) { - texture = General.Map.Data.GetFlatImage(l.Front.MiddleTexture); - } else if(l.Front.HighRequired() && l.Front.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.HighTexture)) { - texture = General.Map.Data.GetFlatImage(l.Front.HighTexture); - } else if(l.Front.LowRequired() && l.Front.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.LowTexture)) { - texture = General.Map.Data.GetFlatImage(l.Front.LowTexture); - } - - if(texture != null) - l.Front.OffsetX = (int)Math.Round((reversed ? totalLength - curLength - l.Length : curLength));// % texture.Width); - } - - if(l.Back != null) { - ImageData texture = null; - - if(l.Back.MiddleRequired() && l.Back.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.MiddleTexture)) { - texture = General.Map.Data.GetFlatImage(l.Back.MiddleTexture); - } else if(l.Back.HighRequired() && l.Back.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.HighTexture)) { - texture = General.Map.Data.GetFlatImage(l.Back.HighTexture); - } else if(l.Back.LowRequired() && l.Back.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.LowTexture)) { - texture = General.Map.Data.GetFlatImage(l.Back.LowTexture); - } - - if(texture != null) - l.Back.OffsetX = (int)Math.Round((reversed ? totalLength - curLength - l.Length : curLength));// % texture.Width); - } - - curLength += l.Length; - } + autoAlignTexturesOnSides(newlines, totalLength, (newlines[0].End != newlines[1].Start)); } } @@ -1491,38 +1456,6 @@ namespace CodeImp.DoomBuilder.Geometry } } - foreach(Linedef l in changedLines) { - if(l.Front != null) { - ImageData texture = null; - - if(l.Front.MiddleRequired() && l.Front.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.MiddleTexture)) { - texture = General.Map.Data.GetFlatImage(l.Front.MiddleTexture); - } else if(l.Front.HighRequired() && l.Front.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.HighTexture)) { - texture = General.Map.Data.GetFlatImage(l.Front.HighTexture); - } else if(l.Front.LowRequired() && l.Front.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.LowTexture)) { - texture = General.Map.Data.GetFlatImage(l.Front.LowTexture); - } - - if (texture != null) - l.Front.OffsetX %= texture.Width; - } - - if(l.Back != null) { - ImageData texture = null; - - if(l.Back.MiddleRequired() && l.Back.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.MiddleTexture)) { - texture = General.Map.Data.GetFlatImage(l.Back.MiddleTexture); - } else if(l.Back.HighRequired() && l.Back.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.HighTexture)) { - texture = General.Map.Data.GetFlatImage(l.Back.HighTexture); - } else if(l.Back.LowRequired() && l.Back.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.LowTexture)) { - texture = General.Map.Data.GetFlatImage(l.Back.LowTexture); - } - - if (texture != null) - l.Back.OffsetX %= texture.Width; - } - } - // Mark new geometry only General.Map.Map.ClearMarkedLinedefs(false); General.Map.Map.ClearMarkedVertices(false); @@ -1532,6 +1465,45 @@ namespace CodeImp.DoomBuilder.Geometry return true; } + + //mxd + private static void autoAlignTexturesOnSides(List lines, float totalLength, bool reversed) { + float curLength = 0f; + + foreach(Linedef l in lines) { + if(l.Front != null) { + ImageData texture = null; + + if(l.Front.MiddleRequired() && l.Front.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.MiddleTexture)) { + texture = General.Map.Data.GetFlatImage(l.Front.MiddleTexture); + } else if(l.Front.HighRequired() && l.Front.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.HighTexture)) { + texture = General.Map.Data.GetFlatImage(l.Front.HighTexture); + } else if(l.Front.LowRequired() && l.Front.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Front.LowTexture)) { + texture = General.Map.Data.GetFlatImage(l.Front.LowTexture); + } + + if(texture != null) + l.Front.OffsetX = (int)Math.Round((reversed ? totalLength - curLength - l.Length : curLength)) % texture.Width; + } + + if(l.Back != null) { + ImageData texture = null; + + if(l.Back.MiddleRequired() && l.Back.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.MiddleTexture)) { + texture = General.Map.Data.GetFlatImage(l.Back.MiddleTexture); + } else if(l.Back.HighRequired() && l.Back.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.HighTexture)) { + texture = General.Map.Data.GetFlatImage(l.Back.HighTexture); + } else if(l.Back.LowRequired() && l.Back.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(l.Back.LowTexture)) { + texture = General.Map.Data.GetFlatImage(l.Back.LowTexture); + } + + if(texture != null) + l.Back.OffsetX = (int)Math.Round((reversed ? totalLength - curLength - l.Length : curLength)) % texture.Width; + } + + curLength += l.Length; + } + } #endregion @@ -1777,7 +1749,7 @@ namespace CodeImp.DoomBuilder.Geometry #endregion - #region Thing Alignment (mxd) + #region ================== Thing Alignment (mxd) public static bool TryAlignThingToLine(Thing t, Linedef l) { if(l.Back == null) { diff --git a/Source/Core/Map/Linedef.cs b/Source/Core/Map/Linedef.cs index b95eb5d7..8d413185 100644 --- a/Source/Core/Map/Linedef.cs +++ b/Source/Core/Map/Linedef.cs @@ -27,6 +27,8 @@ using CodeImp.DoomBuilder.Rendering; using SlimDX.Direct3D9; using System.Drawing; using CodeImp.DoomBuilder.IO; +using CodeImp.DoomBuilder.Types; +using CodeImp.DoomBuilder.Data; #endregion @@ -816,7 +818,8 @@ namespace CodeImp.DoomBuilder.Map nsd.Marked = front.Marked; // Make texture offset adjustments - nsd.OffsetX += (int)Vector2D.Distance(this.start.Position, this.end.Position); + if(nsd.OffsetX != 0 || !General.Map.UDMF) //mxd + nsd.OffsetX += (int)Vector2D.Distance(this.start.Position, this.end.Position); } // Copy back sidedef if exists @@ -828,7 +831,18 @@ namespace CodeImp.DoomBuilder.Map nsd.Marked = back.Marked; // Make texture offset adjustments - back.OffsetX += (int)Vector2D.Distance(nl.start.Position, nl.end.Position); + //mxd + int distance = (int)Vector2D.Distance(nl.start.Position, nl.end.Position); + if(back.OffsetX != 0 || General.Map.UDMF) + if(distance != 0) applyTextureOffsetUDMF(back, distance); + else + back.OffsetX += distance; + } + + //mxd. Both sides of line are required, so we do it here... + if(nl.Front != null && General.Map.UDMF) { + int distance = (int)Vector2D.Distance(this.start.Position, this.end.Position); + if(distance != 0) applyTextureOffsetUDMF(nl.Front, distance); } // Return result @@ -1055,6 +1069,73 @@ namespace CodeImp.DoomBuilder.Map colorPresetIndex = -1; } + //mxd + private void applyTextureOffsetUDMF(Sidedef side, int distance) { + float scaleTop = side.Fields.GetValue("scalex_top", 1.0f); + float scaleMid = side.Fields.GetValue("scalex_mid", 1.0f); + float scaleLow = side.Fields.GetValue("scalex_bottom", 1.0f); + + //top + if(side.HighTexture.Length > 1 && General.Map.Data.GetFlatExists(side.HighTexture)) { + ImageData texture = General.Map.Data.GetFlatImage(side.HighTexture); + + if(side.Fields.ContainsKey("offsetx_top")) { + float value = side.Fields.GetValue("offsetx_top", 0f); + float offset = (float)(Math.Round((value + distance) * scaleTop) % texture.Width); + + if(offset != 0) + side.Fields["offsetx_top"].Value = offset; + else + side.Fields.Remove("offsetx_top"); + } else if(side.HighRequired()) { + float offset = (float)(Math.Round(distance * scaleTop) % texture.Width); + + if(offset != 0) + side.Fields.Add("offsetx_top", new UniValue(UniversalType.Float, offset)); + } + } + + //middle + if(side.MiddleTexture.Length > 1 && General.Map.Data.GetFlatExists(side.MiddleTexture)){ + ImageData texture = General.Map.Data.GetFlatImage(side.MiddleTexture); + + if(side.Fields.ContainsKey("offsetx_mid")) { + float value = side.Fields.GetValue("offsetx_mid", 0f); + float offset = (float)(Math.Round((value + distance) * scaleMid) % texture.Width); + + if(offset != 0) + side.Fields["offsetx_mid"].Value = offset; + else + side.Fields.Remove("offsetx_mid"); + } else if(side.MiddleRequired()) { + float offset = (float)(Math.Round(distance * scaleMid) % texture.Width); + + if(offset != 0) + side.Fields.Add("offsetx_mid", new UniValue(UniversalType.Float, offset)); + } + } + + //bottom + if(side.LowTexture.Length > 1 && General.Map.Data.GetFlatExists(side.LowTexture)){ + ImageData texture = General.Map.Data.GetFlatImage(side.LowTexture); + + if(side.Fields.ContainsKey("offsetx_bottom")) { + float value = side.Fields.GetValue("offsetx_bottom", 0f); + float offset = (float)(Math.Round((value + distance) * scaleLow) % texture.Width); + + if(offset != 0) + side.Fields["offsetx_bottom"].Value = offset; + else + side.Fields.Remove("offsetx_bottom"); + } else if(side.LowRequired()) { + float offset = (float)(Math.Round(distance * scaleLow) % texture.Width); + + if(offset != 0) + side.Fields.Add("offsetx_bottom", new UniValue(UniversalType.Float, offset)); + } + } + } + // String representation public override string ToString() { diff --git a/Source/Plugins/BuilderModes/Resources/Actions.cfg b/Source/Plugins/BuilderModes/Resources/Actions.cfg index 66c38f8a..382dfaa8 100644 --- a/Source/Plugins/BuilderModes/Resources/Actions.cfg +++ b/Source/Plugins/BuilderModes/Resources/Actions.cfg @@ -494,6 +494,30 @@ raisesector1 repeat = true; } +//mxd +lowersectortonearest +{ + title = "Lower Floor/Ceiling to adjacent sector"; + category = "visual"; + description = "Lowers the targeted or selected floors/ceilings to match the height of adjacent sector. Also drops selected things to floor."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 34; //PgDn +} + +//mxd +raisesectortonearest +{ + title = "Raise Floor/Ceiling to adjacent sector"; + category = "visual"; + description = "Raises the targeted or selected floors/ceilings to match the height of adjacent sector. Also aligns selected things to ceiling."; + allowkeys = true; + allowmouse = true; + allowscroll = true; + default = 33; //PgUp +} + showvisualthings { title = "Show Things"; diff --git a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs index 396dc28a..a73f97b2 100644 --- a/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs +++ b/Source/Plugins/BuilderModes/VisualModes/BaseVisualMode.cs @@ -1624,6 +1624,273 @@ namespace CodeImp.DoomBuilder.BuilderModes PostAction(); } + //mxd + [BeginAction("raisesectortonearest")] + public void RaiseSectorToNearest() { + Dictionary floors = new Dictionary(); + Dictionary ceilings = new Dictionary(); + List things = new List(); + bool undoGroupCreated = false; + string alignFailDescription = string.Empty; + + //get selection + if(selectedobjects.Count == 0) { + IVisualEventReceiver i = (target.picked as IVisualEventReceiver); + if(i is VisualFloor) { + VisualFloor vf = i as VisualFloor; + floors.Add(vf.Level.sector, vf); + } else if(i is VisualCeiling) { + VisualCeiling vc = i as VisualCeiling; + ceilings.Add(vc.Level.sector, vc); + } else if(i is BaseVisualThing) { + things.Add(i as BaseVisualThing); + } + } else { + foreach(IVisualEventReceiver i in selectedobjects) { + if(i is VisualFloor) { + VisualFloor vf = i as VisualFloor; + floors.Add(vf.Level.sector, vf); + } else if(i is VisualCeiling) { + VisualCeiling vc = i as VisualCeiling; + ceilings.Add(vc.Level.sector, vc); + } else if(i is BaseVisualThing) { + things.Add(i as BaseVisualThing); + } + } + } + + //process floors... + int maxSelectedHeight = int.MinValue; + int minSelectedCeilingHeight = int.MaxValue; + int targetHeight = int.MaxValue; + + //get maximum floor height from selection + foreach(KeyValuePair group in floors) { + if(group.Key.FloorHeight > maxSelectedHeight) + maxSelectedHeight = group.Key.FloorHeight; + + if(group.Key.CeilHeight < minSelectedCeilingHeight) + minSelectedCeilingHeight = group.Key.CeilHeight; + } + + //get next higher floor from surrounding unselected sectors + foreach(KeyValuePair group in floors) { + foreach(Sidedef side in group.Key.Sidedefs) { + if(side.Other == null || ceilings.ContainsKey(side.Other.Sector) || floors.ContainsKey(side.Other.Sector)) + continue; + if(side.Other.Sector.FloorHeight > maxSelectedHeight && side.Other.Sector.FloorHeight < targetHeight && side.Other.Sector.FloorHeight <= minSelectedCeilingHeight) + targetHeight = side.Other.Sector.FloorHeight; + } + } + + //change floor height + if(targetHeight != int.MaxValue) { + PreAction(UndoGroup.SectorHeightChange); + undoGroupCreated = true; + + foreach(KeyValuePair group in floors) { + if(targetHeight != group.Key.FloorHeight) + group.Value.OnChangeTargetHeight(targetHeight - group.Key.FloorHeight); + } + } else if(floors.Count > 0) { + alignFailDescription = floors.Count > 1 ? "floors" : "floor"; + } + + //ceilings... + maxSelectedHeight = int.MinValue; + targetHeight = int.MaxValue; + + //get highest ceiling height from selection + foreach(KeyValuePair group in ceilings) { + if(group.Key.CeilHeight > maxSelectedHeight) + maxSelectedHeight = group.Key.CeilHeight; + } + + //get next higher ceiling from surrounding unselected sectors + foreach(KeyValuePair group in ceilings) { + foreach(Sidedef side in group.Key.Sidedefs) { + if(side.Other == null || ceilings.ContainsKey(side.Other.Sector) || floors.ContainsKey(side.Other.Sector)) + continue; + if(side.Other.Sector.CeilHeight < targetHeight && side.Other.Sector.CeilHeight > maxSelectedHeight) + targetHeight = side.Other.Sector.CeilHeight; + } + } + + //change ceiling height + if(targetHeight != int.MaxValue) { + if(!undoGroupCreated) { + PreAction(UndoGroup.SectorHeightChange); + undoGroupCreated = true; + } + + foreach(KeyValuePair group in ceilings) { + if(targetHeight != group.Key.CeilHeight) + group.Value.OnChangeTargetHeight(targetHeight - group.Key.CeilHeight); + } + } else if(ceilings.Count > 0) { + if(!string.IsNullOrEmpty(alignFailDescription)) + alignFailDescription += " and "; + + alignFailDescription += ceilings.Count > 1 ? "ceilings" : "ceiling"; + } + + //and things. Just align them to ceiling + if(General.Map.FormatInterface.HasThingHeight) { + foreach(BaseVisualThing vt in things) { + if(vt.Thing.Sector == null) continue; + ThingTypeInfo ti = General.Map.Data.GetThingInfo(vt.Thing.Type); + int zvalue = (int)(vt.Thing.Sector.FloorHeight + vt.Thing.Position.z); + + if(zvalue != vt.Thing.Sector.CeilHeight - ti.Height) { + if(!undoGroupCreated) { + PreAction(UndoGroup.SectorHeightChange); + undoGroupCreated = true; + } + + vt.OnChangeTargetHeight((int)(vt.Thing.Sector.CeilHeight - ti.Height) - zvalue); + } + } + } + + if(!string.IsNullOrEmpty(alignFailDescription)) + General.Interface.DisplayStatus(StatusType.Warning, "Unable to align selected " + alignFailDescription + "!"); + + PostAction(); + } + + //mxd + [BeginAction("lowersectortonearest")] + public void LowerSectorToNearest() { + Dictionary floors = new Dictionary(); + Dictionary ceilings = new Dictionary(); + List things = new List(); + bool undoGroupCreated = false; + string alignFailDescription = string.Empty; + + //get selection + if(selectedobjects.Count == 0) { + IVisualEventReceiver i = (target.picked as IVisualEventReceiver); + if(i is VisualFloor) { + VisualFloor vf = i as VisualFloor; + floors.Add(vf.Level.sector, vf); + } else if(i is VisualCeiling) { + VisualCeiling vc = i as VisualCeiling; + ceilings.Add(vc.Level.sector, vc); + } else if(i is BaseVisualThing) { + things.Add(i as BaseVisualThing); + } + }else{ + foreach(IVisualEventReceiver i in selectedobjects) { + if(i is VisualFloor) { + VisualFloor vf = i as VisualFloor; + floors.Add(vf.Level.sector, vf); + } else if(i is VisualCeiling) { + VisualCeiling vc = i as VisualCeiling; + ceilings.Add(vc.Level.sector, vc); + } else if(i is BaseVisualThing) { + things.Add(i as BaseVisualThing); + } + } + } + + //process floors... + int minSelectedHeight = int.MaxValue; + int targetHeight = int.MinValue; + + //get minimum floor height from selection + foreach(KeyValuePair group in floors) { + if(group.Key.FloorHeight < minSelectedHeight) + minSelectedHeight = group.Key.FloorHeight; + } + + //get next floor lower height from surrounding unselected sectors + foreach(KeyValuePair group in floors){ + foreach(Sidedef side in group.Key.Sidedefs) { + if(side.Other == null || ceilings.ContainsKey(side.Other.Sector) || floors.ContainsKey(side.Other.Sector)) + continue; + if(side.Other.Sector.FloorHeight > targetHeight && side.Other.Sector.FloorHeight < minSelectedHeight) + targetHeight = side.Other.Sector.FloorHeight; + } + } + + //change floor height + if(targetHeight != int.MinValue) { + PreAction(UndoGroup.SectorHeightChange); + undoGroupCreated = true; + + foreach(KeyValuePair group in floors) { + if(targetHeight != group.Key.FloorHeight) + group.Value.OnChangeTargetHeight(targetHeight - group.Key.FloorHeight); + } + } else if(floors.Count > 0) { + alignFailDescription = floors.Count > 1 ? "floors" : "floor"; + } + + //ceilings... + minSelectedHeight = int.MaxValue; + int maxSelectedFloorHeight = int.MinValue; + targetHeight = int.MinValue; + + //get minimum ceiling and maximum floor heights from selection + foreach(KeyValuePair group in ceilings) { + if(group.Key.CeilHeight < minSelectedHeight) + minSelectedHeight = group.Key.CeilHeight; + + if(group.Key.FloorHeight > maxSelectedFloorHeight) + maxSelectedFloorHeight = group.Key.FloorHeight; + } + + //get next lower ceiling height from surrounding unselected sectors + foreach(KeyValuePair group in ceilings) { + foreach(Sidedef side in group.Key.Sidedefs) { + if(side.Other == null || ceilings.ContainsKey(side.Other.Sector) || floors.ContainsKey(side.Other.Sector)) + continue; + if(side.Other.Sector.CeilHeight > targetHeight && side.Other.Sector.CeilHeight < minSelectedHeight && side.Other.Sector.CeilHeight >= maxSelectedFloorHeight) + targetHeight = side.Other.Sector.CeilHeight; + } + } + + //change ceiling height + if(targetHeight != int.MinValue) { + if(!undoGroupCreated) { + PreAction(UndoGroup.SectorHeightChange); + undoGroupCreated = true; + } + + foreach(KeyValuePair group in ceilings) { + if(targetHeight != group.Key.CeilHeight) + group.Value.OnChangeTargetHeight(targetHeight - group.Key.CeilHeight); + } + } else if(ceilings.Count > 0) { + if(!string.IsNullOrEmpty(alignFailDescription)) + alignFailDescription += " and "; + + alignFailDescription += ceilings.Count > 1 ? "ceilings" : "ceiling"; + } + + //and things. Just drop them to ground + if(General.Map.FormatInterface.HasThingHeight){ + foreach(BaseVisualThing vt in things) { + if(vt.Thing.Sector == null) continue; + ThingTypeInfo ti = General.Map.Data.GetThingInfo(vt.Thing.Type); + + if(vt.Thing.Position.z != 0){ + if(!undoGroupCreated) { + PreAction(UndoGroup.SectorHeightChange); + undoGroupCreated = true; + } + + vt.OnChangeTargetHeight((int)-vt.Thing.Position.z); + } + } + } + + if(!string.IsNullOrEmpty(alignFailDescription)) + General.Interface.DisplayStatus(StatusType.Warning, "Unable to align selected " + alignFailDescription + "!"); + + PostAction(); + } + [BeginAction("showvisualthings")] public void ShowVisualThings() {