UltimateZoneBuilder/Source/Plugins/GZDoomEditing/General/BuilderPlug.cs
MaxED c86d92ce63 3d-floor's floor and ceiling can be dragged properly in GZDoom Visual mode.
Things with "hangs" flag are now aligned to ceiling properly in GZDoom Visual mode.
Things can now be added and deleted in GZDoom Visual mode.
Several fixes in Doom, Doom 2, Heretic and Hexen configs (based on Doom Builder 2 SVN 1553 and 1560)
Added "countsecret" thing UDMF flag to configs.

UDMF Controls plugin:
Scale of 3d-floor's sidedefs textures is now applied properly.
Translation of 3d-floor's sidedefs textures is now applied properly. 
Added "hidden" UDMF flag.

Tag Explorer plugin:
TreeView is now updated when thing is deleted.
Tag Explorer plugin is now compatible with Doom Builder 2.
2012-07-05 00:03:40 +00:00

435 lines
17 KiB
C#

#region ================== Copyright (c) 2010 Pascal vd Heiden
/*
* Copyright (c) 2010 Pascal vd Heiden
* 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
#region ================== Namespaces
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
using CodeImp.DoomBuilder.Controls;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.IO;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using System.Drawing;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Plugins;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.Types;
using CodeImp.DoomBuilder.Config;
using CodeImp.DoomBuilder.Data;
#endregion
namespace CodeImp.DoomBuilder.GZDoomEditing
{
public class BuilderPlug : Plug
{
#region ================== Structures
private struct SidedefAlignJob
{
public Sidedef sidedef;
public float offsetx;
// When this is true, the previous sidedef was on the left of
// this one and the texture X offset of this sidedef can be set
// directly. When this is false, the length of this sidedef
// must be subtracted from the X offset first.
public bool forward;
}
#endregion
#region ================== Variables
// Static instance
private static BuilderPlug me;
// Settings
private int showvisualthings; // 0 = none, 1 = sprite only, 2 = sprite caged
private int changeheightbysidedef; // 0 = nothing, 1 = change ceiling, 2 = change floor
private bool visualmodeclearselection;
private bool usegravity;
private bool usehighlight;
private float stitchrange;
//mxd
private bool editnewthing;
// Copy/paste
private string copiedtexture;
private string copiedflat;
private Point copiedoffsets;
private VertexProperties copiedvertexprops;
private SectorProperties copiedsectorprops;
private SidedefProperties copiedsidedefprops;
private LinedefProperties copiedlinedefprops;
private ThingProperties copiedthingprops;
#endregion
#region ================== Properties
// Static property to access the BuilderPlug
public static BuilderPlug Me { get { return me; } }
// This is the lowest Doom Builder core revision that is required for this plugin to work
public override int MinimumRevision { get { return 1394; } }
// Settings
public int ShowVisualThings { get { return showvisualthings; } set { showvisualthings = value; } }
public int ChangeHeightBySidedef { get { return changeheightbysidedef; } }
public bool VisualModeClearSelection { get { return visualmodeclearselection; } }
public bool UseGravity { get { return usegravity; } set { usegravity = value; } }
public bool UseHighlight { get { return usehighlight; } set { usehighlight = value; } }
public float StitchRange { get { return stitchrange; } }
//mxd
public bool EditNewThing { get { return editnewthing; } }
// Copy/paste
public string CopiedTexture { get { return copiedtexture; } set { copiedtexture = value; } }
public string CopiedFlat { get { return copiedflat; } set { copiedflat = value; } }
public Point CopiedOffsets { get { return copiedoffsets; } set { copiedoffsets = value; } }
public VertexProperties CopiedVertexProps { get { return copiedvertexprops; } set { copiedvertexprops = value; } }
public SectorProperties CopiedSectorProps { get { return copiedsectorprops; } set { copiedsectorprops = value; } }
public SidedefProperties CopiedSidedefProps { get { return copiedsidedefprops; } set { copiedsidedefprops = value; } }
public LinedefProperties CopiedLinedefProps { get { return copiedlinedefprops; } set { copiedlinedefprops = value; } }
public ThingProperties CopiedThingProps { get { return copiedthingprops; } set { copiedthingprops = value; } }
#endregion
#region ================== Initialize / Dispose
// This event is called when the plugin is initialized
public override void OnInitialize()
{
base.OnInitialize();
// Keep a static reference
me = this;
// Settings
showvisualthings = 2;
usegravity = false;
usehighlight = true;
LoadSettings();
}
// This is called when the plugin is terminated
public override void Dispose()
{
base.Dispose();
}
#endregion
#region ================== Methods
// This loads the plugin settings
private void LoadSettings()
{
changeheightbysidedef = General.Settings.ReadPluginSetting("BuilderModes", "changeheightbysidedef", 0);
visualmodeclearselection = General.Settings.ReadPluginSetting("BuilderModes", "visualmodeclearselection", false);
stitchrange = (float)General.Settings.ReadPluginSetting("BuilderModes", "stitchrange", 20);
//mxd
editnewthing = General.Settings.ReadPluginSetting("BuilderModes", "editnewthing", true);
}
#endregion
#region ================== Events
// When the Preferences dialog is closed
public override void OnClosePreferences(PreferencesController controller)
{
base.OnClosePreferences(controller);
// Apply settings that could have been changed
LoadSettings();
}
#endregion
#region ================== Classic Mode Surfaces
// This is called when the vertices are created for the classic mode surfaces
public override void OnSectorFloorSurfaceUpdate(Sector s, ref FlatVertex[] vertices)
{
ImageData img = General.Map.Data.GetFlatImage(s.LongFloorTexture);
if((img != null) && img.IsImageLoaded)
{
// Fetch ZDoom fields
Vector2D offset = new Vector2D(s.Fields.GetValue("xpanningfloor", 0.0f),
s.Fields.GetValue("ypanningfloor", 0.0f));
Vector2D scale = new Vector2D(s.Fields.GetValue("xscalefloor", 1.0f),
s.Fields.GetValue("yscalefloor", 1.0f));
float rotate = s.Fields.GetValue("rotationfloor", 0.0f);
int color = s.Fields.GetValue("lightcolor", -1);
int light = s.Fields.GetValue("lightfloor", 0);
bool absolute = s.Fields.GetValue("lightfloorabsolute", false);
// Setup the vertices with the given settings
SetupSurfaceVertices(vertices, s, img, offset, scale, rotate, color, light, absolute);
}
}
// This is called when the vertices are created for the classic mode surfaces
public override void OnSectorCeilingSurfaceUpdate(Sector s, ref FlatVertex[] vertices)
{
ImageData img = General.Map.Data.GetFlatImage(s.LongFloorTexture);
if((img != null) && img.IsImageLoaded)
{
// Fetch ZDoom fields
Vector2D offset = new Vector2D(s.Fields.GetValue("xpanningceiling", 0.0f),
s.Fields.GetValue("ypanningceiling", 0.0f));
Vector2D scale = new Vector2D(s.Fields.GetValue("xscaleceiling", 1.0f),
s.Fields.GetValue("yscaleceiling", 1.0f));
float rotate = s.Fields.GetValue("rotationceiling", 0.0f);
int color = s.Fields.GetValue("lightcolor", -1);
int light = s.Fields.GetValue("lightceiling", 0);
bool absolute = s.Fields.GetValue("lightceilingabsolute", false);
// Setup the vertices with the given settings
SetupSurfaceVertices(vertices, s, img, offset, scale, rotate, color, light, absolute);
}
}
// This applies the given values on the vertices
private void SetupSurfaceVertices(FlatVertex[] vertices, Sector s, ImageData img, Vector2D offset,
Vector2D scale, float rotate, int color, int light, bool absolute)
{
// Prepare for math!
rotate = Angle2D.DegToRad(rotate);
Vector2D texscale = new Vector2D(1.0f / img.ScaledWidth, 1.0f / img.ScaledHeight);
if(!absolute) light = s.Brightness + light;
PixelColor lightcolor = PixelColor.FromInt(color);
PixelColor brightness = PixelColor.FromInt(General.Map.Renderer2D.CalculateBrightness(light));
PixelColor finalcolor = PixelColor.Modulate(lightcolor, brightness);
color = finalcolor.WithAlpha(255).ToInt();
// Do the math for all vertices
for(int i = 0; i < vertices.Length; i++)
{
Vector2D pos = new Vector2D(vertices[i].x, vertices[i].y);
pos = pos.GetRotated(rotate);
pos.y = -pos.y;
pos = (pos + offset) * scale * texscale;
vertices[i].u = pos.x;
vertices[i].v = pos.y;
vertices[i].c = color;
}
}
#endregion
#region ================== Texture Alignment
// This performs texture alignment along all walls that match with the same texture
// NOTE: This method uses the sidedefs marking to indicate which sides have been aligned
// When resetsidemarks is set to true, all sidedefs will first be marked false (not aligned).
// Setting resetsidemarks to false is usefull to align only within a specific selection
// (set the marked property to true for the sidedefs outside the selection)
public static void AutoAlignTextures(Sidedef start, SidedefPart part, ImageData texture, bool alignx, bool aligny, bool resetsidemarks)
{
Stack<SidedefAlignJob> todo = new Stack<SidedefAlignJob>(50);
float scalex = (General.Map.Config.ScaledTextureOffsets && !texture.WorldPanning) ? texture.Scale.x : 1.0f;
float scaley = (General.Map.Config.ScaledTextureOffsets && !texture.WorldPanning) ? texture.Scale.y : 1.0f;
// Mark all sidedefs false (they will be marked true when the texture is aligned)
if(resetsidemarks) General.Map.Map.ClearMarkedSidedefs(false);
if(!texture.IsImageLoaded) return;
// Determine the Y alignment
float ystartalign = start.OffsetY;
switch(part)
{
case SidedefPart.Upper: ystartalign += start.Fields.GetValue("offsety_top", 0.0f); break;
case SidedefPart.Middle: ystartalign += start.Fields.GetValue("offsety_mid", 0.0f); break;
case SidedefPart.Lower: ystartalign += start.Fields.GetValue("offsety_bottom", 0.0f); break;
}
// Begin with first sidedef
SidedefAlignJob first = new SidedefAlignJob();
first.sidedef = start;
first.offsetx = start.OffsetX;
switch(part)
{
case SidedefPart.Upper: first.offsetx += start.Fields.GetValue("offsetx_top", 0.0f); break;
case SidedefPart.Middle: first.offsetx += start.Fields.GetValue("offsetx_mid", 0.0f); break;
case SidedefPart.Lower: first.offsetx += start.Fields.GetValue("offsetx_bottom", 0.0f); break;
}
first.forward = true;
todo.Push(first);
// Continue until nothing more to align
while(todo.Count > 0)
{
Vertex v;
float forwardoffset;
float backwardoffset;
float offsetscalex = 1.0f;
// Get the align job to do
SidedefAlignJob j = todo.Pop();
bool matchtop = ((j.sidedef.LongHighTexture == texture.LongName) && j.sidedef.HighRequired());
bool matchbottom = ((j.sidedef.LongLowTexture == texture.LongName) && j.sidedef.LowRequired());
bool matchmid = ((j.sidedef.LongMiddleTexture == texture.LongName) && (j.sidedef.MiddleRequired() || ((j.sidedef.MiddleTexture.Length > 0) && (j.sidedef.MiddleTexture[0] != '-'))));
if(matchtop) offsetscalex = j.sidedef.Fields.GetValue("scalex_top", 1.0f);
else if(matchbottom) offsetscalex = j.sidedef.Fields.GetValue("scalex_bottom", 1.0f);
else if(matchmid) offsetscalex = j.sidedef.Fields.GetValue("scalex_mid", 1.0f);
if(j.forward)
{
// Apply alignment
if(alignx)
{
//j.sidedef.OffsetX = j.offsetx;
float offset = j.offsetx;
offset %= (float)texture.Height;
offset -= j.sidedef.OffsetX;
j.sidedef.Fields.BeforeFieldsChange();
if(matchtop) j.sidedef.Fields["offsetx_top"] = new UniValue(UniversalType.Float, offset);
if(matchbottom) j.sidedef.Fields["offsetx_bottom"] = new UniValue(UniversalType.Float, offset);
if(matchmid) j.sidedef.Fields["offsetx_mid"] = new UniValue(UniversalType.Float, offset);
}
if(aligny)
{
//j.sidedef.OffsetY = (int)Math.Round((start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + start.OffsetY;
float offset = ((float)(start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + ystartalign;
offset %= (float)texture.Height;
offset -= j.sidedef.OffsetY;
j.sidedef.Fields.BeforeFieldsChange();
if(matchtop) j.sidedef.Fields["offsety_top"] = new UniValue(UniversalType.Float, offset);
if(matchbottom) j.sidedef.Fields["offsety_bottom"] = new UniValue(UniversalType.Float, offset);
if(matchmid) j.sidedef.Fields["offsety_mid"] = new UniValue(UniversalType.Float, offset);
}
forwardoffset = j.offsetx + (int)Math.Round(j.sidedef.Line.Length / scalex * offsetscalex);
backwardoffset = j.offsetx;
// Done this sidedef
j.sidedef.Marked = true;
// Add sidedefs backward (connected to the left vertex)
v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End;
AddSidedefsForAlignment(todo, v, false, backwardoffset, texture.LongName);
// Add sidedefs forward (connected to the right vertex)
v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start;
AddSidedefsForAlignment(todo, v, true, forwardoffset, texture.LongName);
}
else
{
// Apply alignment
if(alignx)
{
//j.sidedef.OffsetX = j.offsetx - (int)Math.Round(j.sidedef.Line.Length / scalex);
float offset = j.offsetx - (int)Math.Round(j.sidedef.Line.Length / scalex);
offset %= (float)texture.Height;
offset -= j.sidedef.OffsetX;
j.sidedef.Fields.BeforeFieldsChange();
if(matchtop) j.sidedef.Fields["offsetx_top"] = new UniValue(UniversalType.Float, offset);
if(matchbottom) j.sidedef.Fields["offsetx_bottom"] = new UniValue(UniversalType.Float, offset);
if(matchmid) j.sidedef.Fields["offsetx_mid"] = new UniValue(UniversalType.Float, offset);
}
if(aligny)
{
//j.sidedef.OffsetY = (int)Math.Round((start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + start.OffsetY;
float offset = ((float)(start.Sector.CeilHeight - j.sidedef.Sector.CeilHeight) / scaley) + ystartalign;
offset %= (float)texture.Height;
offset -= j.sidedef.OffsetY;
j.sidedef.Fields.BeforeFieldsChange();
if(matchtop) j.sidedef.Fields["offsety_top"] = new UniValue(UniversalType.Float, offset);
if(matchbottom) j.sidedef.Fields["offsety_bottom"] = new UniValue(UniversalType.Float, offset);
if(matchmid) j.sidedef.Fields["offsety_mid"] = new UniValue(UniversalType.Float, offset);
}
forwardoffset = j.offsetx;
backwardoffset = j.offsetx - (int)Math.Round(j.sidedef.Line.Length / scalex * offsetscalex);
// Done this sidedef
j.sidedef.Marked = true;
// Add sidedefs forward (connected to the right vertex)
v = j.sidedef.IsFront ? j.sidedef.Line.End : j.sidedef.Line.Start;
AddSidedefsForAlignment(todo, v, true, forwardoffset, texture.LongName);
// Add sidedefs backward (connected to the left vertex)
v = j.sidedef.IsFront ? j.sidedef.Line.Start : j.sidedef.Line.End;
AddSidedefsForAlignment(todo, v, false, backwardoffset, texture.LongName);
}
}
}
// This adds the matching, unmarked sidedefs from a vertex for texture alignment
private static void AddSidedefsForAlignment(Stack<SidedefAlignJob> stack, Vertex v, bool forward, float offsetx, long texturelongname)
{
foreach(Linedef ld in v.Linedefs)
{
Sidedef side1 = forward ? ld.Front : ld.Back;
Sidedef side2 = forward ? ld.Back : ld.Front;
if((ld.Start == v) && (side1 != null) && !side1.Marked)
{
if(SidedefTextureMatch(side1, texturelongname))
{
SidedefAlignJob nj = new SidedefAlignJob();
nj.forward = forward;
nj.offsetx = offsetx;
nj.sidedef = side1;
stack.Push(nj);
}
}
else if((ld.End == v) && (side2 != null) && !side2.Marked)
{
if(SidedefTextureMatch(side2, texturelongname))
{
SidedefAlignJob nj = new SidedefAlignJob();
nj.forward = forward;
nj.offsetx = offsetx;
nj.sidedef = side2;
stack.Push(nj);
}
}
}
}
// This checks if any of the sidedef texture match the given texture
private static bool SidedefTextureMatch(Sidedef sd, long texturelongname)
{
return ((sd.LongHighTexture == texturelongname) && sd.HighRequired()) ||
((sd.LongLowTexture == texturelongname) && sd.LowRequired()) ||
((sd.LongMiddleTexture == texturelongname) && (sd.MiddleRequired() || ((sd.MiddleTexture.Length > 0) && (sd.MiddleTexture[0] != '-'))));
}
#endregion
}
}