UltimateZoneBuilder/Source/Plugins/3DFloorMode/ThreeDFloor.cs
2023-12-06 14:22:37 +01:00

398 lines
12 KiB
C#

#region ================== Copyright (c) 2014 Boris Iwanski
/*
* Copyright (c) 2014 Boris Iwanski
* This program is released under GNU General Public License
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#endregion
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using CodeImp.DoomBuilder.BuilderModes;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Plugins;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data;
namespace CodeImp.DoomBuilder.ThreeDFloorMode
{
public class ThreeDFloor
{
private Sector sector;
private List<Sector> taggedsectors;
private List<Sector> sectorstotag;
private List<Sector> sectorstountag;
private string bordertexture;
private string topflat;
private string bottomflat;
private Vector3D floorslope;
private double floorslopeoffset;
private Vector3D ceilingslope;
private double ceilingslopeoffset;
private int type;
private int flags;
private int alpha;
private int brightness;
private int topheight;
private int bottomheight;
private bool isnew;
private int udmftag;
private List<int> tags;
private LinedefProperties linedefproperties;
private SectorProperties sectorproperties;
public static Rectangle controlsectorarea = new Rectangle(-512, 512, 512, -512);
public Sector Sector { get { return sector; } }
public List<Sector> TaggedSectors { get { return taggedsectors; } set { taggedsectors = value; } }
public List<Sector> SectorsToTag { get { return sectorstotag; } set { sectorstotag = value; } }
public List<Sector> SectorsToUntag { get { return sectorstountag; } set { sectorstountag = value; } }
public string BorderTexture { get { return bordertexture; } set { bordertexture = value; } }
public string TopFlat { get { return topflat; } set { topflat = value; } }
public string BottomFlat { get { return bottomflat; } set { bottomflat = value; } }
public int Type { get { return type; } set { type = value; } }
public int Flags { get { return flags; } set { flags = value; } }
public int Alpha { get { return alpha; } set { alpha = value; } }
public int Brightness { get { return brightness; }set { brightness = value; } }
public int TopHeight { get { return topheight; } set { topheight = value; } }
public int BottomHeight { get { return bottomheight; } set { bottomheight = value; } }
public bool IsNew { get { return isnew; } set { isnew = value; } }
public int UDMFTag { get { return udmftag; } set { udmftag = value; } }
public List<int> Tags { get { return tags; } set { tags = value; } }
public Vector3D FloorSlope { get { return floorslope; } set { floorslope = value; } }
public double FloorSlopeOffset { get { return floorslopeoffset; } set { floorslopeoffset = value; } }
public Vector3D CeilingSlope { get { return ceilingslope; } set { ceilingslope = value; } }
public double CeilingSlopeOffset { get { return ceilingslopeoffset; } set { ceilingslopeoffset = value; } }
public LinedefProperties LinedefProperties { get { return linedefproperties; } }
public SectorProperties SectorProperties { get { return sectorproperties; } }
public ThreeDFloor()
{
sector = null;
taggedsectors = new List<Sector>();
topflat = General.Settings.DefaultCeilingTexture;
bottomflat = General.Settings.DefaultFloorTexture;
topheight = General.Settings.DefaultCeilingHeight;
bottomheight = General.Settings.DefaultFloorHeight;
bordertexture = General.Settings.DefaultTexture;
type = 1;
flags = 0;
tags = new List<int>();
floorslope = new Vector3D(0.0f, 0.0f, 0.0f);
floorslopeoffset = 0.0f;
ceilingslope = new Vector3D(0.0f, 0.0f, 0.0f);
ceilingslopeoffset = 0.0f;
linedefproperties = null;
sectorproperties = null;
alpha = 255;
}
public ThreeDFloor(Sector sector) : this(sector, General.Map.Map.Sectors)
{
// Nothing extra do do here
}
public ThreeDFloor(Sector sector, IEnumerable<Sector> potentialsectors)
{
if (sector == null)
throw new Exception("Sector can't be null");
this.sector = sector;
taggedsectors = new List<Sector>();
topflat = sector.CeilTexture;
bottomflat = sector.FloorTexture;
topheight = sector.CeilHeight;
bottomheight = sector.FloorHeight;
brightness = sector.Brightness;
tags = new List<int>();
floorslope = sector.FloorSlope;
floorslopeoffset = sector.FloorSlopeOffset;
ceilingslope = sector.CeilSlope;
ceilingslopeoffset = sector.CeilSlopeOffset;
foreach (Sidedef sd in sector.Sidedefs)
{
if (sd.Line.Action >= 100 && sd.Line.Action < 300)
{
bordertexture = sd.MiddleTexture;
udmftag = sd.Line.Args[0];
type = sd.Line.Args[1];
flags = sd.Line.Args[2];
alpha = sd.Line.Args[3];
linedefproperties = new LinedefProperties(sd.Line);
sectorproperties = new SectorProperties(sector);
foreach (Sector s in BuilderPlug.GetSectorsByTag(potentialsectors, sd.Line.Args[0]))
{
if(!taggedsectors.Contains(s))
taggedsectors.Add(s);
}
}
}
}
public void BindTag(int tag, LinedefProperties ldprops)
{
Linedef line = null;
// try to find an line without an action
foreach (Sidedef sd in sector.Sidedefs)
{
if (sd.Line.Action == 0 && sd.Line.Tag == 0 && line == null)
line = sd.Line;
// if a line of the control sector already has the tag
// nothing has to be done
if (sd.Line.Args[0] == tag)
{
return;
}
}
// no lines without an action, so a line has to get split
// find the longest line to split
if (line == null)
{
line = sector.Sidedefs.First().Line;
foreach (Sidedef sd in sector.Sidedefs)
{
if (sd.Line.Length > line.Length)
line = sd.Line;
}
// Lines may not have a length of less than 1 after splitting
if (line.Length / 2 < 1)
throw new Exception("Can't split more lines in Sector " + line.Front.Sector.Index.ToString() + ".");
Vertex v = General.Map.Map.CreateVertex(line.Line.GetCoordinatesAt(0.5f));
v.SnapToAccuracy();
line = line.Split(v);
General.Map.Map.Update();
General.Interface.RedrawDisplay();
}
if(ldprops != null)
ldprops.Apply(new List<Linedef>() { line }, false);
line.Action = 100;
line.Args[0] = tag;
line.Args[1] = type;
line.Args[2] = flags;
line.Args[3] = alpha;
}
public void UpdateGeometry()
{
if (sector == null)
throw new Exception("3D floor has no geometry");
sector.CeilHeight = topheight;
sector.FloorHeight = bottomheight;
sector.SetCeilTexture(topflat);
sector.SetFloorTexture(bottomflat);
sector.Brightness = brightness;
sector.Tags = tags;
sector.FloorSlope = floorslope;
sector.FloorSlopeOffset = floorslopeoffset;
sector.CeilSlope = ceilingslope;
sector.CeilSlopeOffset = ceilingslopeoffset;
foreach (Sidedef sd in sector.Sidedefs)
{
sd.SetTextureMid(bordertexture);
if (sd.Line.Action >= 100 && sd.Line.Action < 300)
{
// We need to update the linedef's args, but we can't do it directly because otherwise their state will not be saved for the undo snapshot,
// so we're using the linedef's update method
sd.Line.Update(sd.Line.GetFlags(), sd.Line.Activate, sd.Line.Tags, sd.Line.Action, new int[] { sd.Line.Args[0], type, flags, alpha, sd.Line.Args[4] });
}
}
}
public bool CreateGeometry(List<int> tagblacklist, List<DrawnVertex> alldrawnvertices)
{
int newtag;
return CreateGeometry(tagblacklist, alldrawnvertices, null, null, false, out newtag);
}
public bool CreateGeometry(List<int> tagblacklist, List<DrawnVertex> alldrawnvertices, LinedefProperties ldprops, SectorProperties sectorprops, bool forcenewtag, out int newtag)
{
List<Vertex> vertices = new List<Vertex>();
Vector3D slopetopthingpos = new Vector3D(0, 0, 0);
Vector3D slopebottomthingpos = new Vector3D(0, 0, 0);
Line2D slopeline = new Line2D(0, 0, 0, 0);
newtag = -1;
// We need 5 vertices to draw the control sector
if(alldrawnvertices.Count < 5)
{
General.Interface.DisplayStatus(StatusType.Warning, "Could not draw new sector: not enough vertices");
return false;
}
// Get the first 5 vertices in the list and also remove them from the list, so that creating further
// control sectors won't use them
List<DrawnVertex> drawnvertices = alldrawnvertices.GetRange(0, 5);
alldrawnvertices.RemoveRange(0, 5);
// drawnvertices = BuilderPlug.Me.ControlSectorArea.GetNewControlSectorVertices();
if (Tools.DrawLines(drawnvertices) == false)
{
General.Interface.DisplayStatus(StatusType.Warning, "Could not draw new sector");
return false;
}
sector = General.Map.Map.GetMarkedSectors(true)[0];
if (sectorprops != null)
sectorprops.Apply(new List<Sector>() { sector }, false);
sector.FloorHeight = bottomheight;
sector.CeilHeight = topheight;
sector.SetFloorTexture(bottomflat);
sector.SetCeilTexture(topflat);
sector.Brightness = brightness;
sector.FloorSlope = floorslope;
sector.FloorSlopeOffset = floorslopeoffset;
sector.CeilSlope = ceilingslope;
sector.CeilSlopeOffset = ceilingslopeoffset;
foreach (Sidedef sd in sector.Sidedefs)
{
sd.Line.Front.SetTextureMid(bordertexture);
}
if (!sector.Fields.ContainsKey("user_managed_3d_floor"))
sector.Fields.Add("user_managed_3d_floor", new UniValue(UniversalType.Boolean, true));
sector.Fields["comment"] = new UniValue(UniversalType.String, "[!]DO NOT DELETE! This sector is managed by the 3D floor plugin.");
// With multiple tag support in UDMF only one tag is needed, so bind it right away
if (General.Map.UDMF == true && General.Map.Config.SectorMultiTag)
{
if (isnew || forcenewtag)
{
newtag = udmftag = BuilderPlug.Me.ControlSectorArea.GetNewSectorTag(tagblacklist);
tagblacklist.Add(udmftag);
}
BindTag(udmftag, ldprops);
}
return true;
}
public void Cleanup()
{
int taggedLines = 0;
foreach (Sidedef sd in sector.Sidedefs)
{
if ((sd.Line.Action >= 100 && sd.Line.Action < 300) && BuilderPlug.GetSectorsByTag(sd.Line.Args[0]).Count == 0)
{
sd.Line.Action = 0;
for (int i = 0; i < sd.Line.Args.Length; i++)
sd.Line.Args[i] = 0;
}
if (sd.Line.Action != 0)
taggedLines++;
}
if (taggedLines == 0)
{
DeleteControlSector(sector);
}
}
private void DeleteControlSector(Sector sector)
{
if (sector == null)
return;
General.Map.Map.BeginAddRemove();
// Get all the linedefs
List<Linedef> lines = new List<Linedef>(sector.Sidedefs.Count);
foreach (Sidedef side in sector.Sidedefs) lines.Add(side.Line);
// Dispose the sector
sector.Dispose();
// Check all the lines
for (int i = lines.Count - 1; i >= 0; i--)
{
// If the line has become orphaned, remove it
if ((lines[i].Front == null) && (lines[i].Back == null))
{
// Remove line
lines[i].Dispose();
}
else
{
// If the line only has a back side left, flip the line and sides
if ((lines[i].Front == null) && (lines[i].Back != null))
{
lines[i].FlipVertices();
lines[i].FlipSidedefs();
}
// Check textures.
if (lines[i].Front.MiddleRequired() && (lines[i].Front.MiddleTexture.Length == 0 || lines[i].Front.MiddleTexture == "-"))
{
if (lines[i].Front.HighTexture.Length > 0 && lines[i].Front.HighTexture != "-")
{
lines[i].Front.SetTextureMid(lines[i].Front.HighTexture);
}
else if (lines[i].Front.LowTexture.Length > 0 && lines[i].Front.LowTexture != "-")
{
lines[i].Front.SetTextureMid(lines[i].Front.LowTexture);
}
}
// Do we still need high/low textures?
lines[i].Front.RemoveUnneededTextures(false);
// Update sided flags
lines[i].ApplySidedFlags();
}
}
General.Map.Map.EndAddRemove();
}
public void DeleteControlSector()
{
DeleteControlSector(sector);
}
}
}