Added Sound Propagation plugin by boris (https://github.com/biwa/soundpropagationmode).

Changes since 5bf51d0:
Large scale cleanup & refactoring.
Sound Propagation Mode: you can now click on doublesided linedefs to toggle sound blocking flag.
Sound Environment Mode: you can now click on doublesided linedefs to toggle sound zone boundary flag.
Sound Propagation Mode: reimplemented the highlighting of non-deaf things. Things are now highlighted using selection color.
Removed "Show tooltips" checkbox. Tooltips are now always enabled.
Fixed, Sound Environment Mode: thing tooltip about multiple active sound environment things was shown even when there was only one active sound environment thing.
Fixed, Sound Environment Mode: "Configure colors" toolbar button was not working because of missing action.
Fixed, cosmetic: "Configure colors" toolbar icon was missing a tooltip.
Fixed, cosmetic: display was not redrawn after changing colors using "Color configuration" window.
This commit is contained in:
MaxED 2015-01-23 12:36:43 +00:00
parent 1f3ebad9b7
commit 0a7bd9f636
27 changed files with 2827 additions and 0 deletions

View file

@ -19,6 +19,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TagRange", "Source\Plugins\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisplaneExplorer", "Source\Plugins\VisplaneExplorer\VisplaneExplorer.csproj", "{CF670175-7099-4090-A330-EE25C7230139}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SoundPropagation", "Source\Plugins\SoundPropagationMode\SoundPropagation.csproj", "{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -139,6 +141,20 @@ Global
{CF670175-7099-4090-A330-EE25C7230139}.Release|Win32.ActiveCfg = Release|x86
{CF670175-7099-4090-A330-EE25C7230139}.Release|x86.ActiveCfg = Release|x86
{CF670175-7099-4090-A330-EE25C7230139}.Release|x86.Build.0 = Release|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Debug|Mixed Platforms.Build.0 = Debug|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Debug|Win32.ActiveCfg = Debug|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Debug|x86.ActiveCfg = Debug|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Debug|x86.Build.0 = Debug|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Release|Any CPU.Build.0 = Release|Any CPU
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Release|Mixed Platforms.ActiveCfg = Release|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Release|Mixed Platforms.Build.0 = Release|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Release|Win32.ActiveCfg = Release|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Release|x86.ActiveCfg = Release|x86
{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -0,0 +1,24 @@
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
// As per http://stackoverflow.com/questions/10362988/treeview-flickering
// Gets rid of the flickering default TreeView
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
public class BufferedTreeView : TreeView
{
protected override void OnHandleCreated(EventArgs e)
{
SendMessage(this.Handle, TVM_SETEXTENDEDSTYLE, (IntPtr)TVS_EX_DOUBLEBUFFER, (IntPtr)TVS_EX_DOUBLEBUFFER);
base.OnHandleCreated(e);
}
// Pinvoke:
private const int TVM_SETEXTENDEDSTYLE = 0x1100 + 44;
private const int TVM_GETEXTENDEDSTYLE = 0x1100 + 45;
private const int TVS_EX_DOUBLEBUFFER = 0x0004;
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
}

View file

@ -0,0 +1,379 @@

#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
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.ComponentModel;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Plugins;
#endregion
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
//
// MANDATORY: The plug!
// This is an important class to the Doom Builder core. Every plugin must
// have exactly 1 class that inherits from Plug. When the plugin is loaded,
// this class is instantiated and used to receive events from the core.
// Make sure the class is public, because only public classes can be seen
// by the core.
//
public class BuilderPlug : Plug
{
#region ================== Variables
// Interface
private MenusForm menusform;
// Colors
private PixelColor highlightcolor;
private PixelColor level1color;
private PixelColor level2color;
private PixelColor blocksoundcolor;
private PixelColor nosoundcolor;
private List<PixelColor> distinctcolors;
private List<SoundEnvironment> soundenvironments;
private List<Linedef> blockinglinedefs;
private FlatVertex[] overlayGeometry;
private bool soundenvironmentisupdated;
private bool dataisdirty;
#endregion
#region ================== Properties
// Interface
public MenusForm MenusForm { get { return menusform; } }
// Colors
public PixelColor HighlightColor { get { return highlightcolor; } set { highlightcolor = value; } }
public PixelColor Level1Color { get { return level1color; } set { level1color = value; } }
public PixelColor Level2Color { get { return level2color; } set { level2color = value; } }
public PixelColor BlockSoundColor { get { return blocksoundcolor; } set { blocksoundcolor = value; } }
public PixelColor NoSoundColor { get { return nosoundcolor; } set { nosoundcolor = value; } }
public List<SoundEnvironment> SoundEnvironments { get { return soundenvironments; } set { soundenvironments = value; } }
public List<Linedef> BlockingLinedefs { get { return blockinglinedefs; } set { blockinglinedefs = value; } }
public FlatVertex[] OverlayGeometry { get { return overlayGeometry; } set { overlayGeometry = value; } }
public bool SoundEnvironmentIsUpdated { get { return soundenvironmentisupdated; } }
public bool DataIsDirty { get { return dataisdirty; } set { dataisdirty = value; } }
#endregion
// Static instance. We can't use a real static class, because BuilderPlug must
// be instantiated by the core, so we keep a static reference. (this technique
// should be familiar to object-oriented programmers)
private static BuilderPlug me;
// Static property to access the BuilderPlug
public static BuilderPlug Me { get { return me; } }
// This plugin relies on some functionality that wasn't there in older versions
public override int MinimumRevision { get { return 1885; } }
// This event is called when the plugin is initialized
public override void OnInitialize()
{
base.OnInitialize();
highlightcolor = PixelColor.FromInt(General.Settings.ReadPluginSetting("highlightcolor", new PixelColor(255, 0, 192, 0).ToInt()));
level1color = PixelColor.FromInt(General.Settings.ReadPluginSetting("level1color", new PixelColor(255, 0, 255, 0).ToInt()));
level2color = PixelColor.FromInt(General.Settings.ReadPluginSetting("level2color", new PixelColor(255, 255, 255, 0).ToInt()));
nosoundcolor = PixelColor.FromInt(General.Settings.ReadPluginSetting("nosoundcolor", new PixelColor(255, 160, 160, 160).ToInt()));
blocksoundcolor = PixelColor.FromInt(General.Settings.ReadPluginSetting("blocksoundcolor", new PixelColor(255, 255, 0, 0).ToInt()));
distinctcolors = new List<PixelColor>
{
PixelColor.FromColor(Color.Blue),
PixelColor.FromColor(Color.Orange),
PixelColor.FromColor(Color.ForestGreen),
PixelColor.FromColor(Color.Sienna),
PixelColor.FromColor(Color.LightPink),
PixelColor.FromColor(Color.Purple),
PixelColor.FromColor(Color.Cyan),
PixelColor.FromColor(Color.LawnGreen),
PixelColor.FromColor(Color.PaleGoldenrod),
PixelColor.FromColor(Color.Red),
PixelColor.FromColor(Color.Yellow),
PixelColor.FromColor(Color.LightSkyBlue),
PixelColor.FromColor(Color.Magenta)
};
soundenvironments = new List<SoundEnvironment>();
blockinglinedefs = new List<Linedef>();
soundenvironmentisupdated = false;
dataisdirty = true;
// This binds the methods in this class that have the BeginAction
// and EndAction attributes with their actions. Without this, the
// attributes are useless. Note that in classes derived from EditMode
// this is not needed, because they are bound automatically when the
// editing mode is engaged.
General.Actions.BindMethods(this);
menusform = new MenusForm();
// TODO: Add DB2 version check so that old DB2 versions won't crash
// General.ErrorLogger.Add(ErrorType.Error, "zomg!");
// Keep a static reference
me = this;
}
public override void OnMapOpenBegin()
{
base.OnMapOpenBegin();
ResetData();
}
public override void OnMapNewBegin()
{
base.OnMapNewBegin();
ResetData();
}
public override void OnMapSaveBegin(SavePurpose purpose)
{
base.OnMapSaveBegin(purpose);
soundenvironmentisupdated = false;
}
public override void OnEditEngage(EditMode oldmode, EditMode newmode)
{
base.OnEditEngage(oldmode, newmode);
ResetData();
}
// This is called when the plugin is terminated
public override void Dispose()
{
base.Dispose();
// This must be called to remove bound methods for actions.
General.Actions.UnbindMethods(this);
}
// Resets all data. This will trigger both rediscovering sound environments and recalculating
// sound propagation domains
private void ResetData()
{
dataisdirty = true;
soundenvironmentisupdated = false;
soundenvironments = new List<SoundEnvironment>();
blockinglinedefs = new List<Linedef>();
}
public void UpdateSoundEnvironments(object sender, DoWorkEventArgs e)
{
List<Sector> sectorstocheck = new List<Sector>();
List<Sector> checkedsectors = new List<Sector>();
List<Sector> allsectors = new List<Sector>();
BackgroundWorker worker = sender as BackgroundWorker;
List<FlatVertex> vertsList = new List<FlatVertex>();
Dictionary<Thing, PixelColor> secolor = new Dictionary<Thing, PixelColor>();
Dictionary<Thing, int> senumber = new Dictionary<Thing, int>();
soundenvironments.Clear();
blockinglinedefs.Clear();
// Keep track of all the sectors in the map. Sectors that belong to a sound environment
// will be removed from the list, so in the end only sectors that don't belong to any
// sound environment will be in this list
foreach (Sector s in General.Map.Map.Sectors) allsectors.Add(s);
List<Thing> soundenvironmenthings = GetSoundEnvironmentThings(General.Map.Map.Sectors.ToList());
int numthings = soundenvironmenthings.Count;
// Assign each thing a color and a number, so each sound environment will always have the same color
// and id, no matter in what order they are discovered
for (int i = 0; i < soundenvironmenthings.Count; i++)
{
secolor[soundenvironmenthings[i]] = distinctcolors[i % distinctcolors.Count];
senumber.Add(soundenvironmenthings[i], i + 1);
}
while (soundenvironmenthings.Count > 0 && !worker.CancellationPending)
{
// Sort things by distance to center of the screen, so that sound environments the user want to look at will (hopefully) be discovered first
Vector2D center = General.Map.Renderer2D.DisplayToMap(new Vector2D(General.Interface.Display.Width / 2, General.Interface.Display.Height / 2));
soundenvironmenthings = soundenvironmenthings.OrderBy(o => Math.Abs(Vector2D.Distance(center, o.Position))).ToList();
Thing thing = soundenvironmenthings[0];
if (thing.Sector == null) thing.DetermineSector();
// Ignore things that are outside the map
if (thing.Sector == null)
{
soundenvironmenthings.Remove(thing);
continue;
}
SoundEnvironment environment = new SoundEnvironment();
// Add initial sector. Additional adjacant sectors will be added later
// as they are discovered
sectorstocheck.Add(thing.Sector);
while (sectorstocheck.Count > 0)
{
Sector sector = sectorstocheck[0];
Sector oppositesector;
if (!environment.Sectors.Contains(sector)) environment.Sectors.Add(sector);
if (!checkedsectors.Contains(sector)) checkedsectors.Add(sector);
sectorstocheck.Remove(sector);
allsectors.Remove(sector);
// Find adjacant sectors and add them to the list of sectors to check if necessary
foreach (Sidedef sd in sector.Sidedefs)
{
if (LinedefBlocksSoundEnvironment(sd.Line))
{
if (!environment.Linedefs.Contains(sd.Line)) environment.Linedefs.Add(sd.Line);
continue;
}
if (sd.Line.Back == null) continue;
if (sd.Line.Front.Sector == sector)
oppositesector = sd.Line.Back.Sector;
else
oppositesector = sd.Line.Front.Sector;
if (!sectorstocheck.Contains(oppositesector) && !checkedsectors.Contains(oppositesector))
sectorstocheck.Add(oppositesector);
}
}
// Get all things that are in the current sound environment...
environment.Things = GetSoundEnvironmentThings(environment.Sectors);
// ... and remove them from the list of sound environment things to check, because we know that
// they already belong to a sound environment
foreach (Thing t in environment.Things)
{
if (soundenvironmenthings.Contains(t)) soundenvironmenthings.Remove(t);
}
// Set color and id of the sound environment
environment.Color = secolor[environment.Things[0]];
environment.ID = senumber[environment.Things[0]];
// Create the data for the overlay geometry
foreach (Sector s in environment.Sectors)
{
FlatVertex[] fv = new FlatVertex[s.FlatVertices.Length];
s.FlatVertices.CopyTo(fv, 0);
for (int j = 0; j < fv.Length; j++) fv[j].c = environment.Color.WithAlpha(128).ToInt();
vertsList.AddRange(fv);
// Get all Linedefs that will block sound environments
foreach (Sidedef sd in s.Sidedefs)
{
if (LinedefBlocksSoundEnvironment(sd.Line))
lock (blockinglinedefs)
{
blockinglinedefs.Add(sd.Line);
}
}
}
// Update the overlay geometry with the newly added sectors
if (overlayGeometry == null)
{
overlayGeometry = vertsList.ToArray();
}
else
{
lock (overlayGeometry)
{
overlayGeometry = vertsList.ToArray();
}
}
environment.Things = environment.Things.OrderBy(o => o.Index).ToList();
environment.Linedefs = environment.Linedefs.OrderBy(o => o.Index).ToList();
lock (soundenvironments)
{
soundenvironments.Add(environment);
}
// Tell the worker that discovering a sound environment is finished. This will update the tree view, and also
// redraw the interface, so the sectors of this sound environment are colored
worker.ReportProgress((int)((1.0f - (soundenvironmenthings.Count / (float)numthings)) * 100), environment);
}
// Create overlay geometry for sectors that don't belong to a sound environment
foreach (Sector s in allsectors)
{
FlatVertex[] fv = new FlatVertex[s.FlatVertices.Length];
s.FlatVertices.CopyTo(fv, 0);
for (int j = 0; j < fv.Length; j++) fv[j].c = NoSoundColor.WithAlpha(128).ToInt();
vertsList.AddRange(fv);
}
if (overlayGeometry == null)
{
overlayGeometry = vertsList.ToArray();
}
else
{
lock (overlayGeometry)
{
overlayGeometry = vertsList.ToArray();
}
}
soundenvironmentisupdated = true;
}
private static List<Thing> GetSoundEnvironmentThings(List<Sector> sectors)
{
List<Thing> things = new List<Thing>();
foreach (Thing thing in General.Map.Map.Things)
{
// SoundEnvironment thing, see http://zdoom.org/wiki/Classes:SoundEnvironment
if (thing.Type != 9048) continue;
if (thing.Sector == null) thing.DetermineSector();
if (thing.Sector != null && sectors.Contains(thing.Sector)) things.Add(thing);
}
return things;
}
private static bool LinedefBlocksSoundEnvironment(Linedef linedef)
{
if(General.Map.UDMF) return linedef.IsFlagSet("zoneboundary"); //mxd. Fancier this way :)
// In Hexen format the line must have action 121 (Line_SetIdentification) and bit 1 of
// the second argument set (see http://zdoom.org/wiki/Line_SetIdentification)
return (linedef.Action == 121 && (linedef.Args[1] & 1) == 1); //mxd. Fancier this way :)
}
}
}

View file

@ -0,0 +1,62 @@
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
partial class SoundEnvironmentPanel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.soundenvironments = new CodeImp.DoomBuilder.SoundPropagationMode.BufferedTreeView();
this.SuspendLayout();
//
// soundenvironments
//
this.soundenvironments.Dock = System.Windows.Forms.DockStyle.Fill;
this.soundenvironments.Location = new System.Drawing.Point(0, 0);
this.soundenvironments.Name = "soundenvironments";
this.soundenvironments.ShowNodeToolTips = true;
this.soundenvironments.Size = new System.Drawing.Size(214, 512);
this.soundenvironments.TabIndex = 0;
this.soundenvironments.NodeMouseClick += new System.Windows.Forms.TreeNodeMouseClickEventHandler(this.soundenvironments_NodeMouseClick);
this.soundenvironments.BeforeSelect += new System.Windows.Forms.TreeViewCancelEventHandler(this.soundenvironments_BeforeSelect);
//
// SoundEnvironmentPanel
//
this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
this.Controls.Add(this.soundenvironments);
this.Name = "SoundEnvironmentPanel";
this.Size = new System.Drawing.Size(214, 512);
this.ResumeLayout(false);
}
#endregion
private BufferedTreeView soundenvironments;
}
}

View file

@ -0,0 +1,245 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Geometry;
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
public partial class SoundEnvironmentPanel : UserControl
{
public BufferedTreeView SoundEnvironments { get { return soundenvironments; } set { soundenvironments = value; } }
public SoundEnvironmentPanel()
{
InitializeComponent();
soundenvironments.ImageList = new ImageList();
soundenvironments.ImageList.Images.Add(global::SoundPropagationMode.Properties.Resources.Status0);
soundenvironments.ImageList.Images.Add(global::SoundPropagationMode.Properties.Resources.Warning);
}
public void AddSoundEnvironment(SoundEnvironment se)
{
TreeNode topnode = new TreeNode("Sound environment " + se.ID);
TreeNode thingsnode = new TreeNode("Things (" + se.Things.Count + ")");
TreeNode linedefsnode = new TreeNode("Linedefs (" + se.Linedefs.Count + ")");
int notdormant = 0;
int topindex = 0;
// Add things
foreach (Thing t in se.Things)
{
TreeNode thingnode = new TreeNode("Thing " + t.Index);
thingnode.Tag = t;
thingsnode.Nodes.Add(thingnode);
if(!ThingDormant(t))
{
notdormant++;
}
else
{
thingnode.Text += " (dormant)";
}
}
// Set the icon to warning sign and add the tooltip when there are more than 1 non-dormant things
if (notdormant > 1)
{
thingsnode.ImageIndex = 1;
thingsnode.SelectedImageIndex = 1;
topindex = 1;
foreach (TreeNode tn in thingsnode.Nodes)
{
if (!ThingDormant((Thing)tn.Tag))
{
tn.ImageIndex = 1;
tn.SelectedImageIndex = 1;
tn.ToolTipText = "More than one thing in this\nsound environment is set to be\nactive. Set all but one thing\nto dormant.";
}
}
}
// Add linedefs
foreach (Linedef ld in se.Linedefs)
{
bool showwarning = false;
TreeNode linedefnode = new TreeNode("Linedef " + ld.Index);
linedefnode.Tag = ld;
if (ld.Back == null)
{
showwarning = true;
linedefnode.ToolTipText = "This line is single-sided, but has\nthe sound boundary flag set.";
}
else if (se.Sectors.Contains(ld.Front.Sector) && se.Sectors.Contains(ld.Back.Sector))
{
showwarning = true;
linedefnode.ToolTipText = "More than one thing in this\nThe sectors on both sides of\nthe line belong to the same\nsound environment.";
}
if (showwarning)
{
linedefnode.ImageIndex = 1;
linedefnode.SelectedImageIndex = 1;
linedefsnode.ImageIndex = 1;
linedefsnode.SelectedImageIndex = 1;
topindex = 1;
}
linedefsnode.Nodes.Add(linedefnode);
}
topnode.Nodes.Add(thingsnode);
topnode.Nodes.Add(linedefsnode);
topnode.Tag = se;
topnode.ImageIndex = topindex;
topnode.SelectedImageIndex = topindex;
topnode.Expand();
// Sound environments will no be added in consecutive order, so we'll have to find
// out where in the tree to add the node
Regex seid = new Regex(@"\d+$");
int insertionplace = 0;
foreach (TreeNode tn in soundenvironments.Nodes)
{
Match match = seid.Match(tn.Text);
int num = int.Parse(match.Value);
if (se.ID < num) break;
insertionplace++;
}
soundenvironments.Nodes.Insert(insertionplace, topnode);
}
public void HighlightSoundEnvironment(SoundEnvironment se)
{
soundenvironments.BeginUpdate();
foreach (TreeNode tn in soundenvironments.Nodes)
{
if (se != null && tn.Text == "Sound environment " + se.ID)
{
if (tn.NodeFont == null || tn.NodeFont.Style != FontStyle.Bold)
{
tn.NodeFont = new Font(soundenvironments.Font.FontFamily, soundenvironments.Font.Size, FontStyle.Bold);
tn.Text += string.Empty;
}
}
else
{
if(tn.NodeFont == null || tn.NodeFont.Style != FontStyle.Regular)
tn.NodeFont = new Font(soundenvironments.Font.FontFamily, soundenvironments.Font.Size);
}
}
soundenvironments.EndUpdate();
}
private static bool ThingDormant(Thing thing)
{
return thing.IsFlagSet(General.Map.UDMF ? "dormant" : "14");
}
private static bool IsClickOnText(TreeView treeView, TreeNode node, Point location)
{
var hitTest = treeView.HitTest(location);
return hitTest.Node == node && (hitTest.Location == TreeViewHitTestLocations.Label || hitTest.Location == TreeViewHitTestLocations.Image);
}
private static void ProcessNodeClick(TreeNode node)
{
if (node == null) return;
List<Vector2D> points = new List<Vector2D>();
RectangleF area = MapSet.CreateEmptyArea();
if (node.Parent == null)
{
if (node.Text.StartsWith("Sound environment"))
{
SoundEnvironment se = (SoundEnvironment)node.Tag;
foreach (Sector s in se.Sectors)
{
foreach (Sidedef sd in s.Sidedefs)
{
points.Add(sd.Line.Start.Position);
points.Add(sd.Line.End.Position);
}
}
}
else
{
// Don't zoom if the wrong nodes are selected
return;
}
}
else
{
if (node.Parent.Text.StartsWith("Things"))
{
Thing t = (Thing)node.Tag;
// We don't want to be zoomed too closely, so add somepadding
points.Add(t.Position - 200);
points.Add(t.Position + 200);
}
else if (node.Parent.Text.StartsWith("Linedefs"))
{
Linedef ld = (Linedef)node.Tag;
points.Add(ld.Start.Position);
points.Add(ld.End.Position);
}
else
{
// Don't zoom if the wrong nodes are selected
return;
}
}
area = MapSet.IncreaseArea(area, points);
// Add padding
area.Inflate(100f, 100f);
// Zoom to area
ClassicMode editmode = (General.Editing.Mode as ClassicMode);
editmode.CenterOnArea(area, 0.0f);
}
#region ================== Events
private void soundenvironments_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
{
if (IsClickOnText(soundenvironments, e.Node, e.Location))
{
ProcessNodeClick(e.Node);
}
}
private void soundenvironments_BeforeSelect(object sender, TreeViewCancelEventArgs e)
{
if(e.Action != TreeViewAction.ByMouse) return;
var position = soundenvironments.PointToClient(Cursor.Position);
e.Cancel = !IsClickOnText(soundenvironments, e.Node, position);
}
#endregion
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// Allgemeine Informationen über eine Assembly werden über die folgenden
// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
// die mit einer Assembly verknüpft sind.
[assembly: AssemblyTitle("SoundPropagationMode")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("SoundPropagationMode")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Durch Festlegen von ComVisible auf "false" werden die Typen in dieser Assembly unsichtbar
// für COM-Komponenten. Wenn Sie auf einen Typ in dieser Assembly von
// COM zugreifen müssen, legen Sie das ComVisible-Attribut für diesen Typ auf "true" fest.
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
[assembly: Guid("626ea88b-7e0f-4486-acca-32027c4133de")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
// Hauptversion
// Nebenversion
// Buildnummer
// Revision
//
// Sie können alle Werte angeben oder die standardmäßigen Build- und Revisionsnummern
// übernehmen, indem Sie "*" eingeben:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("0.1.0.0")]

View file

@ -0,0 +1,84 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.5485
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace SoundPropagationMode.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SoundPropagationMode.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
internal static System.Drawing.Bitmap ColorManagement {
get {
object obj = ResourceManager.GetObject("ColorManagement", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
internal static System.Drawing.Bitmap Status0 {
get {
object obj = ResourceManager.GetObject("Status0", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
internal static System.Drawing.Bitmap Warning {
get {
object obj = ResourceManager.GetObject("Warning", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
}
}

View file

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="ColorManagement" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\ColorManagement.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Status0" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Status0.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="Warning" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Resources\Warning.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View file

@ -0,0 +1,56 @@
//
// This file defines which actions there are, what description they have and
// some behaviour options. The Doom Builder core will bind to these actions
// with delegates (function pointers) where you use the BeginAction and
// EndAction attributes. This file must be named Actions.cfg and must be
// included in the plugin project as "Embedded Resource".
//
//
// Options:
//
// allowkeys: Allows the user to bind standard keys to this action.
// allowmouse: Allows the user to bind mouse buttons to this action.
// allowscroll: Allows the user to bind the scrollwheel to this action.
// disregardshift: This action will trigger regardless if Shift or Control is used.
// repeat: BeginAction will be called for automatic key repetition.
// default: Default key is only used when the action is loaded for the first
// time and the default key is not used by any other action.
//
// allowkeys and allowmouse are true by default, the others are false by default.
//
soundpropagationmode
{
title = "Sound propagation mode";
category = "modes";
description = "Shows how sound propagates through the map";
allowkeys = true;
allowmouse = true;
allowscroll = true;
}
soundenvironmentmode
{
title = "Sound environment mode";
category = "modes";
description = "Shows ZDoom sound environments";
allowkeys = true;
allowmouse = true;
allowscroll = true;
}
categories
{
soundpropagationmode = "Sound propagation mode";
}
soundpropagationcolorconfiguration
{
title = "Configure colors";
category = "soundpropagationmode";
description = "Configure colors for sound propagation mode";
allowkeys = true;
allowmouse = true;
allowscroll = true;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 642 B

View file

@ -0,0 +1,2 @@
The ear and color management icons used were made by FatCow (http://www.fatcow.com/). The license for
the icons is CC BY 3.0 US (http://creativecommons.org/licenses/by/3.0/us/)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 922 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 794 B

View file

@ -0,0 +1,32 @@
#region ================== Namespaces
using System.Collections.Generic;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
#endregion
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
public class SoundEnvironment
{
#region ================== Properties
public List<Sector> Sectors { get; private set; }
public List<Thing> Things { get; set; }
public List<Linedef> Linedefs { get; set; }
public PixelColor Color { get; set; }
public int ID { get; set; }
#endregion
public SoundEnvironment()
{
Sectors = new List<Sector>();
Things = new List<Thing>();
Linedefs = new List<Linedef>();
Color = General.Colors.Background;
ID = -1;
}
}
}

View file

@ -0,0 +1,398 @@

#region ================== Copyright (c) 2007 Pascal vd Heiden, 2014 Boris Iwanski
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* 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
#region ================== Namespaces
using System;
using System.Windows.Forms;
using System.ComponentModel;
using CodeImp.DoomBuilder.Actions;
using CodeImp.DoomBuilder.Windows;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Controls;
#endregion
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
[EditMode(DisplayName = "Sound Environment Mode",
SwitchAction = "soundenvironmentmode", // Action name used to switch to this mode
ButtonImage = "ZDoomSoundEnvironment.png", // Image resource name for the button
ButtonOrder = int.MinValue + 501, // Position of the button (lower is more to the left)
ButtonGroup = "000_editing",
UseByDefault = true,
SafeStartMode = false,
Volatile = false)]
public class SoundEnvironmentMode : ClassicMode
{
#region ================== Variables
// Highlighted item
private Sector highlighted;
private SoundEnvironment highlightedsoundenvironment;
private Linedef highlightedline; //mxd
// Interface
private SoundEnvironmentPanel panel;
private Docker docker;
private BackgroundWorker worker;
#endregion
#region ================== Properties
public override object HighlightedObject { get { return highlighted; } }
#endregion
#region ================== Constructor / Disposer
// Disposer
public override void Dispose()
{
// Not already disposed?
if (!isdisposed)
{
// Dispose base
base.Dispose();
}
}
#endregion
#region ================== Methods
// This highlights a new item
private void Highlight(Sector s)
{
// Set new highlight
highlighted = s;
highlightedsoundenvironment = null;
if (highlighted != null)
{
foreach (SoundEnvironment se in BuilderPlug.Me.SoundEnvironments)
{
if (se.Sectors.Contains(highlighted))
{
highlightedsoundenvironment = se;
break;
}
}
}
if (highlightedsoundenvironment != null)
{
panel.HighlightSoundEnvironment(highlightedsoundenvironment);
}
else
{
panel.HighlightSoundEnvironment(null);
}
// Show highlight info
if ((highlighted != null) && !highlighted.IsDisposed)
General.Interface.ShowSectorInfo(highlighted);
else
General.Interface.HideInfo();
}
private void UpdateData()
{
BuilderPlug.Me.DataIsDirty = false;
panel.SoundEnvironments.Nodes.Clear();
// Only update if map has changed or the sound environments were never updated at all (i.e. first time engaging this mode)
if((General.Map.IsChanged || !BuilderPlug.Me.SoundEnvironmentIsUpdated) && !worker.IsBusy)
{
General.Interface.DisplayStatus(StatusType.Busy, "Updating sound environments");
worker.RunWorkerAsync();
}
else if(!worker.IsBusy)
{
foreach(SoundEnvironment se in BuilderPlug.Me.SoundEnvironments)
panel.AddSoundEnvironment(se);
}
}
#endregion
#region ================== Events
public override void OnHelp()
{
General.ShowHelp("e_sectors.html");
}
// Cancel mode
public override void OnCancel()
{
base.OnCancel();
// Return to this mode
General.Editing.ChangeMode(new SoundEnvironmentMode());
}
// Mode engages
public override void OnEngage()
{
base.OnEngage();
General.Interface.AddButton(BuilderPlug.Me.MenusForm.ColorConfiguration);
panel = new SoundEnvironmentPanel();
docker = new Docker("soundenvironments", "Sound Environments", panel);
General.Interface.AddDocker(docker);
General.Interface.SelectDocker(docker);
worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.WorkerSupportsCancellation = true;
worker.DoWork += BuilderPlug.Me.UpdateSoundEnvironments;
worker.ProgressChanged += worker_ProgressChanged;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
UpdateData();
CustomPresentation presentation = new CustomPresentation();
presentation.AddLayer(new PresentLayer(RendererLayer.Background, BlendingMode.Mask, General.Settings.BackgroundAlpha));
presentation.AddLayer(new PresentLayer(RendererLayer.Grid, BlendingMode.Mask));
presentation.AddLayer(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1f, true));
presentation.AddLayer(new PresentLayer(RendererLayer.Things, BlendingMode.Alpha, 1.0f));
presentation.AddLayer(new PresentLayer(RendererLayer.Geometry, BlendingMode.Alpha, 1f, true));
renderer.SetPresentation(presentation);
}
//mxd. If a linedef is highlighted, toggle the sound blocking flag
protected override void OnSelectEnd()
{
if(highlightedline == null || !General.Map.UDMF) return;
// Make undo
General.Map.UndoRedo.CreateUndo("Toggle Sound Zone Boundary");
// Toggle flag
highlightedline.SetFlag("zoneboundary", !highlightedline.IsFlagSet("zoneboundary"));
// Update
UpdateData();
General.Interface.RedrawDisplay();
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
General.Interface.DisplayStatus(StatusType.Ready, "Finished updating sound environments");
}
private void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
SoundEnvironment se = e.UserState as SoundEnvironment;
General.Interface.DisplayStatus(StatusType.Busy, "Updating sound environments (" + e.ProgressPercentage + "%)");
panel.AddSoundEnvironment(se);
General.Interface.RedrawDisplay();
}
// Mode disengages
public override void OnDisengage()
{
base.OnDisengage();
worker.CancelAsync();
General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.ColorConfiguration);
General.Interface.RemoveDocker(docker);
// Hide highlight info
General.Interface.HideInfo();
}
// This redraws the display
public override void OnRedrawDisplay()
{
if (BuilderPlug.Me.DataIsDirty) UpdateData();
// Render lines and vertices
if (renderer.StartPlotter(true))
{
// Plot lines by hand, so that no coloring (line specials, 3D floors etc.) distracts from
// the sound environments. Also don't draw the line's normal. They are not needed here anyway
// and can make it harder to see the sound environment colors
foreach (Linedef ld in General.Map.Map.Linedefs)
{
PixelColor c;
if(ld.IsFlagSet(General.Map.Config.ImpassableFlag))
c = General.Colors.Linedefs;
else
c = General.Colors.Linedefs.WithAlpha(General.Settings.DoubleSidedAlphaByte);
renderer.PlotLine(ld.Start.Position, ld.End.Position, c);
}
// Since there will usually be way less blocking linedefs than total linedefs, it's presumably
// faster to draw them on their own instead of checking if each linedef is in BlockingLinedefs
lock (BuilderPlug.Me.BlockingLinedefs)
{
foreach (Linedef ld in BuilderPlug.Me.BlockingLinedefs)
{
renderer.PlotLine(ld.Start.Position, ld.End.Position, BuilderPlug.Me.BlockSoundColor);
}
}
//mxd. Render highlighted line
if(highlightedline != null)
{
renderer.PlotLine(highlightedline.Start.Position, highlightedline.End.Position, General.Colors.Highlight);
}
renderer.PlotVerticesSet(General.Map.Map.Vertices);
renderer.Finish();
}
// Render things
if (renderer.StartThings(true))
{
renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_BACK_ALPHA);
renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, Presentation.THINGS_HIDDEN_ALPHA);
lock (BuilderPlug.Me.SoundEnvironments)
{
foreach (SoundEnvironment se in BuilderPlug.Me.SoundEnvironments)
{
if (se.Things.Count > 0) renderer.RenderThingSet(se.Things, 1.0f);
}
}
renderer.Finish();
}
// Render overlay geometry (sectors)
if (BuilderPlug.Me.OverlayGeometry != null)
{
lock (BuilderPlug.Me.OverlayGeometry)
{
if (BuilderPlug.Me.OverlayGeometry.Length > 0 && renderer.StartOverlay(true))
{
renderer.RenderGeometry(BuilderPlug.Me.OverlayGeometry, General.Map.Data.WhiteTexture, true);
renderer.Finish();
}
}
}
renderer.Present();
}
// Mouse moves
public override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
// Not holding any buttons?
if (e.Button == MouseButtons.None)
{
General.Interface.SetCursor(Cursors.Default);
// Find the nearest linedef within highlight range
Linedef l = General.Map.Map.NearestLinedef(mousemappos);
if (l != null)
{
// Check on which side of the linedef the mouse is
float side = l.SideOfLine(mousemappos);
if (side > 0)
{
// Is there a sidedef here?
if (l.Back != null)
{
// Highlight if not the same
if (l.Back.Sector != highlighted) Highlight(l.Back.Sector);
}
else
{
// Highlight nothing
Highlight(null);
}
}
else
{
// Is there a sidedef here?
if (l.Front != null)
{
// Highlight if not the same
if (l.Front.Sector != highlighted) Highlight(l.Front.Sector);
}
else
{
// Highlight nothing
Highlight(null);
}
}
}
else
{
// Highlight nothing
Highlight(null);
}
//mxd. Find the nearest linedef within default highlight range
l = General.Map.Map.NearestLinedefRange(mousemappos, 20 / renderer.Scale);
//mxd. We are not interested in single-sided lines, unless they have zoneboundary flag...
if(l != null && ((l.Front == null || l.Back == null) && (General.Map.UDMF && !l.IsFlagSet("zoneboundary"))))
{
l = null;
}
//mxd. Set as highlighted
if(highlightedline != l)
{
highlightedline = l;
General.Interface.RedrawDisplay();
}
}
}
// Mouse leaves
public override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
// Highlight nothing
Highlight(null);
}
#endregion
#region ================== Actions
[BeginAction("soundpropagationcolorconfiguration")]
public void ConfigureColors()
{
ColorConfiguration cc = new ColorConfiguration();
if(cc.ShowDialog((Form)General.Interface) == DialogResult.OK)
{
General.Interface.RedrawDisplay();
}
}
#endregion
}
}

View file

@ -0,0 +1,135 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{F59B344C-DD50-4DB7-ADDD-56AAD66450AF}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>SoundPropagationMode</RootNamespace>
<AssemblyName>SoundPropagationMode</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<DebugSymbols>true</DebugSymbols>
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>..\..\..\Build\Plugins\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="BufferedTreeView.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="BuilderPlug.cs" />
<Compile Include="Interface\SoundEnvironmentPanel.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Interface\SoundEnvironmentPanel.Designer.cs">
<DependentUpon>SoundEnvironmentPanel.cs</DependentUpon>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="SoundEnvironment.cs" />
<Compile Include="SoundEnvironmentMode.cs" />
<Compile Include="SoundPropagationDomain.cs" />
<Compile Include="Windows\ColorConfiguration.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\ColorConfiguration.designer.cs">
<DependentUpon>ColorConfiguration.cs</DependentUpon>
</Compile>
<Compile Include="Windows\MenusForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Windows\MenusForm.designer.cs">
<DependentUpon>MenusForm.cs</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SoundPropagationMode.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Core\Builder.csproj">
<Project>{818B3D10-F791-4C3F-9AF5-BB2D0079B63C}</Project>
<Name>Builder</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Actions.cfg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<EmbeddedResource Include="Resources\ColorManagement.png" />
<EmbeddedResource Include="Windows\ColorConfiguration.resx">
<DependentUpon>ColorConfiguration.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Windows\MenusForm.resx">
<DependentUpon>MenusForm.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Resources\SoundPropagationIcon.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Interface\SoundEnvironmentPanel.resx">
<DependentUpon>SoundEnvironmentPanel.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Resources\ZDoomSoundEnvironment.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Warning.png" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Status0.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,123 @@
#region ================== Namespaces
using System.Collections.Generic;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
#endregion
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
public class SoundPropagationDomain
{
#region ================== Variables
private List<Sector> sectors;
private List<Sector> adjacentsectors;
private List<Linedef> blockinglines;
private FlatVertex[] level1geometry;
private FlatVertex[] level2geometry;
#endregion
#region ================== Properties
public List<Sector> Sectors { get { return sectors; } }
public List<Sector> AdjacentSectors { get { return adjacentsectors; } }
public List<Linedef> BlockingLines { get { return blockinglines; } }
public FlatVertex[] Level1Geometry { get { return level1geometry; } }
public FlatVertex[] Level2Geometry { get { return level2geometry; } }
#endregion
#region ================== Constructor
public SoundPropagationDomain(Sector sector)
{
sectors = new List<Sector>();
adjacentsectors = new List<Sector>();
blockinglines = new List<Linedef>();
CreateSoundPropagationDomain(sector);
}
#endregion
#region ================== Methods
private void CreateSoundPropagationDomain(Sector sourcesector)
{
List<Sector> sectorstocheck = new List<Sector> { sourcesector };
while (sectorstocheck.Count > 0)
{
// Make sure to first check all sectors that are not behind a sound blocking line
Sector sector = sectorstocheck[0];
foreach (Sidedef sd in sector.Sidedefs)
{
bool blocksound = sd.Line.IsFlagSet(General.Map.UDMF ? "blocksound" : "64");
bool blockheight = false;
Sector oppositesector;
if (blocksound) blockinglines.Add(sd.Line);
// If the line is one sided, the sound can travel nowhere, so try the next one
if (sd.Line.Back == null || blocksound) continue;
// Get the sector on the other side of the line we're checking right now
if (sd.Line.Front.Sector == sector)
oppositesector = sd.Line.Back.Sector;
else
oppositesector = sd.Line.Front.Sector;
// Check if the sound will be blocked because of sector floor and ceiling heights
// (like closed doors, raised lifts etc.)
if (sector.CeilHeight <= oppositesector.FloorHeight || sector.FloorHeight >= oppositesector.CeilHeight
|| oppositesector.CeilHeight <= oppositesector.FloorHeight || sector.CeilHeight <= sector.FloorHeight)
{
blockheight = true;
}
// Try next line if sound will not pass through the current one. The last check makes
// sure that the next line is tried if the current line is blocking sound, and the current
// sector is already behind a sound blocking line
if (oppositesector == null || blockheight) continue;
// If the opposite sector was not regarded at all yet...
if (!sectors.Contains(oppositesector) && !sectorstocheck.Contains(oppositesector))
{
sectorstocheck.Add(oppositesector);
}
}
sectorstocheck.Remove(sector);
sectors.Add(sector);
}
foreach (Linedef ld in blockinglines)
{
if (!sectors.Contains(ld.Front.Sector)) adjacentsectors.Add(ld.Front.Sector);
if (ld.Back != null && !sectors.Contains(ld.Back.Sector)) adjacentsectors.Add(ld.Back.Sector);
}
List<FlatVertex> vertices = new List<FlatVertex>();
foreach (Sector s in sectors)
{
vertices.AddRange(s.FlatVertices);
}
level1geometry = vertices.ToArray();
level2geometry = vertices.ToArray();
for (int i = 0; i < level1geometry.Length; i++)
{
level1geometry[i].c = BuilderPlug.Me.Level1Color.WithAlpha(128).ToInt();
level2geometry[i].c = BuilderPlug.Me.Level2Color.WithAlpha(128).ToInt();
}
}
#endregion
}
}

View file

@ -0,0 +1,429 @@
#region ================== Copyright (c) 2007 Pascal vd Heiden, 2014 Boris Iwanski
/*
* Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com
* 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
#region ================== Namespaces
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Editing;
using CodeImp.DoomBuilder.Actions;
#endregion
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
[EditMode(DisplayName = "Sound Propagation Mode",
SwitchAction = "soundpropagationmode", // Action name used to switch to this mode
ButtonImage = "SoundPropagationIcon.png", // Image resource name for the button
ButtonOrder = int.MinValue + 501, // Position of the button (lower is more to the left)
ButtonGroup = "000_editing",
UseByDefault = true,
SafeStartMode = false,
Volatile = false)]
public class SoundPropagationMode : ClassicMode
{
#region ================== Variables
// Highlighted item
private Sector highlighted;
private Linedef highlightedline; //mxd
private FlatVertex[] overlayGeometry;
private List<Thing> huntingThings;
private List<SoundPropagationDomain> propagationdomains;
private Dictionary<Sector, SoundPropagationDomain> sector2domain;
#endregion
#region ================== Properties
public override object HighlightedObject { get { return highlighted; } }
#endregion
#region ================== Constructor / Disposer
// Disposer
public override void Dispose()
{
// Not already disposed?
if(!isdisposed)
{
// Dispose base
base.Dispose();
}
}
#endregion
#region ================== Methods
private void UpdateData()
{
BuilderPlug.Me.DataIsDirty = false;
List<FlatVertex> vertsList = new List<FlatVertex>();
// Go for all selected sectors
foreach (Sector s in General.Map.Map.Sectors) vertsList.AddRange(s.FlatVertices);
overlayGeometry = vertsList.ToArray();
for (int i = 0; i < overlayGeometry.Length; i++)
overlayGeometry[i].c = BuilderPlug.Me.NoSoundColor.WithAlpha(128).ToInt();
}
// This highlights a new item
private void Highlight(Sector s)
{
// Set new highlight
highlighted = s;
UpdateSoundPropagation();
// Show highlight info
if ((highlighted != null) && !highlighted.IsDisposed)
General.Interface.ShowSectorInfo(highlighted);
else
General.Interface.HideInfo();
General.Interface.RedrawDisplay();
}
//mxd
private void ResetSoundPropagation()
{
sector2domain.Clear();
propagationdomains.Clear();
BuilderPlug.Me.BlockingLinedefs.Clear();
UpdateSoundPropagation();
}
private void UpdateSoundPropagation()
{
huntingThings.Clear();
if (highlighted == null || highlighted.IsDisposed) return;
if (!sector2domain.ContainsKey(highlighted))
{
SoundPropagationDomain spd = new SoundPropagationDomain(highlighted);
foreach (Sector s in spd.Sectors) sector2domain[s] = spd;
propagationdomains.Add(spd);
BuilderPlug.Me.BlockingLinedefs.AddRange(spd.BlockingLines);
}
foreach (Sector adjacent in sector2domain[highlighted].AdjacentSectors)
{
if(!sector2domain.ContainsKey(adjacent))
{
SoundPropagationDomain aspd = new SoundPropagationDomain(adjacent);
foreach (Sector s in aspd.Sectors) sector2domain[s] = aspd;
BuilderPlug.Me.BlockingLinedefs.AddRange(aspd.BlockingLines);
}
}
//mxd. Create the list of sectors, which will be affected by nose made in highlighted sector
SoundPropagationDomain curdomain = sector2domain[highlighted];
Dictionary<int, Sector> noisysectors = new Dictionary<int, Sector>(curdomain.Sectors.Count);
foreach(Sector s in curdomain.Sectors)
{
noisysectors.Add(s.Index, s);
}
foreach (Sector s in curdomain.AdjacentSectors)
{
SoundPropagationDomain aspd = sector2domain[s];
foreach (Sector adjs in aspd.Sectors)
{
if(!noisysectors.ContainsKey(adjs.Index)) noisysectors.Add(adjs.Index, adjs);
}
}
// Update the list of things that will actually go for the player when hearing a noise
foreach (Thing thing in General.Map.Map.Things)
{
if(!General.Map.ThingsFilter.VisibleThings.Contains(thing)) continue;
if(thing.IsFlagSet(General.Map.UDMF ? "ambush" : "8")) continue;
if(thing.Sector == null) thing.DetermineSector();
if(thing.Sector != null && noisysectors.ContainsKey(thing.Sector.Index)) huntingThings.Add(thing);
}
}
#endregion
#region ================== Events
public override void OnHelp()
{
General.ShowHelp("e_sectors.html");
}
// Cancel mode
public override void OnCancel()
{
// Cancel base class
base.OnCancel();
// Return to previous mode
General.Editing.ChangeMode(new SoundPropagationMode());
}
// Mode engages
public override void OnEngage()
{
base.OnEngage();
huntingThings = new List<Thing>();
propagationdomains = new List<SoundPropagationDomain>();
sector2domain = new Dictionary<Sector, SoundPropagationDomain>();
BuilderPlug.Me.BlockingLinedefs = new List<Linedef>();
UpdateData();
General.Interface.AddButton(BuilderPlug.Me.MenusForm.ColorConfiguration);
CustomPresentation presentation = new CustomPresentation();
presentation.AddLayer(new PresentLayer(RendererLayer.Background, BlendingMode.Mask, General.Settings.BackgroundAlpha));
presentation.AddLayer(new PresentLayer(RendererLayer.Grid, BlendingMode.Mask));
presentation.AddLayer(new PresentLayer(RendererLayer.Overlay, BlendingMode.Alpha, 1.0f, true));
presentation.AddLayer(new PresentLayer(RendererLayer.Things, BlendingMode.Alpha, 1.0f));
presentation.AddLayer(new PresentLayer(RendererLayer.Geometry, BlendingMode.Alpha, 1.0f, true));
renderer.SetPresentation(presentation);
// Convert geometry selection to sectors only
General.Map.Map.ConvertSelection(SelectionType.Sectors);
}
// Mode disengages
public override void OnDisengage()
{
base.OnDisengage();
General.Interface.RemoveButton(BuilderPlug.Me.MenusForm.ColorConfiguration);
// Hide highlight info
General.Interface.HideInfo();
}
// This redraws the display
public override void OnRedrawDisplay()
{
List<SoundPropagationDomain> renderedspds = new List<SoundPropagationDomain>();
if (BuilderPlug.Me.DataIsDirty) UpdateData();
// Render lines and vertices
if(renderer.StartPlotter(true))
{
// Plot lines by hand, so that no coloring (line specials, 3D floors etc.) distracts from
// the sound propagation. Also don't draw the line's normal. They are not needed here anyway
// and can make it harder to see the sound environment propagation
foreach (Linedef ld in General.Map.Map.Linedefs)
{
PixelColor c;
if(ld.IsFlagSet(General.Map.Config.ImpassableFlag))
c = General.Colors.Linedefs;
else
c = General.Colors.Linedefs.WithAlpha(General.Settings.DoubleSidedAlphaByte);
renderer.PlotLine(ld.Start.Position, ld.End.Position, c);
}
// Since there will usually be way less blocking linedefs than total linedefs, it's presumably
// faster to draw them on their own instead of checking if each linedef is in BlockingLinedefs
foreach (Linedef ld in BuilderPlug.Me.BlockingLinedefs)
{
renderer.PlotLine(ld.Start.Position, ld.End.Position, BuilderPlug.Me.BlockSoundColor);
}
//mxd. Render highlighted line
if(highlightedline != null)
{
renderer.PlotLine(highlightedline.Start.Position, highlightedline.End.Position, General.Colors.Highlight);
}
renderer.PlotVerticesSet(General.Map.Map.Vertices);
renderer.Finish();
}
// Render things
if(renderer.StartThings(true))
{
renderer.RenderThingSet(General.Map.ThingsFilter.HiddenThings, Presentation.THINGS_BACK_ALPHA);
renderer.RenderThingSet(General.Map.ThingsFilter.VisibleThings, Presentation.THINGS_HIDDEN_ALPHA);
foreach (Thing thing in huntingThings)
{
renderer.RenderThing(thing, General.Colors.Selection, 1.0f);
}
renderer.Finish();
}
if (renderer.StartOverlay(true))
{
renderer.RenderGeometry(overlayGeometry, General.Map.Data.WhiteTexture, true);
if (highlighted != null && !highlighted.IsDisposed)
{
SoundPropagationDomain spd = sector2domain[highlighted];
renderer.RenderGeometry(spd.Level1Geometry, General.Map.Data.WhiteTexture, true);
foreach (Sector s in spd.AdjacentSectors)
{
SoundPropagationDomain aspd = sector2domain[s];
if (!renderedspds.Contains(aspd))
{
renderer.RenderGeometry(aspd.Level2Geometry, General.Map.Data.WhiteTexture, true);
renderedspds.Add(aspd);
}
}
RenderColoredSector(highlighted, BuilderPlug.Me.HighlightColor.WithAlpha(128));
}
renderer.Finish();
}
renderer.Present();
}
private void RenderColoredSector(Sector sector, PixelColor color)
{
RenderColoredSector(sector.FlatVertices, color);
}
private void RenderColoredSector(FlatVertex[] flatvertices, PixelColor color)
{
FlatVertex[] fv = new FlatVertex[flatvertices.Length];
flatvertices.CopyTo(fv, 0);
for (int i = 0; i < fv.Length; i++) fv[i].c = color.ToInt();
renderer.RenderGeometry(fv, General.Map.Data.WhiteTexture, true);
}
//mxd. If a linedef is highlighted, toggle the sound blocking flag
protected override void OnSelectEnd()
{
if(highlightedline == null) return;
// Make undo
General.Map.UndoRedo.CreateUndo("Toggle Linedef Sound Blocking");
// Toggle flag
string flag = (General.Map.UDMF ? "blocksound" : "64");
highlightedline.SetFlag(flag, !highlightedline.IsFlagSet(flag));
// Update
ResetSoundPropagation();
General.Interface.RedrawDisplay();
}
// Mouse moves
public override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
// Not holding any buttons?
if (e.Button == MouseButtons.None)
{
General.Interface.SetCursor(Cursors.Default);
// Find the nearest linedef within highlight range
Linedef l = General.Map.Map.NearestLinedef(mousemappos);
if (l != null)
{
// Check on which side of the linedef the mouse is
float side = l.SideOfLine(mousemappos);
if (side > 0)
{
// Is there a sidedef here?
if (l.Back != null)
{
// Highlight if not the same
if (l.Back.Sector != highlighted) Highlight(l.Back.Sector);
}
else
{
// Highlight nothing
Highlight(null);
}
}
else
{
// Is there a sidedef here?
if (l.Front != null)
{
// Highlight if not the same
if (l.Front.Sector != highlighted) Highlight(l.Front.Sector);
}
else
{
// Highlight nothing
Highlight(null);
}
}
}
else
{
// Highlight nothing
Highlight(null);
}
//mxd. Find the nearest linedef within default highlight range
l = General.Map.Map.NearestLinedefRange(mousemappos, 20 / renderer.Scale);
//mxd. We are not interested in single-sided lines...
if(l != null && (l.Front == null || l.Back == null)) l = null;
//mxd. Set as highlighted
if(highlightedline != l)
{
highlightedline = l;
General.Interface.RedrawDisplay();
}
}
}
// Mouse leaves
public override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
// Highlight nothing
Highlight(null);
}
#endregion
#region ================== Actions
[BeginAction("soundpropagationcolorconfiguration")]
public void ConfigureColors()
{
ColorConfiguration cc = new ColorConfiguration();
if(cc.ShowDialog((Form)General.Interface) == DialogResult.OK)
{
General.Interface.RedrawDisplay();
}
}
#endregion
}
}

View file

@ -0,0 +1,46 @@
using System;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Rendering;
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
public partial class ColorConfiguration : Form
{
public ColorConfiguration()
{
InitializeComponent();
highlightcolor.Color = BuilderPlug.Me.HighlightColor;
level1color.Color = BuilderPlug.Me.Level1Color;
level2color.Color = BuilderPlug.Me.Level2Color;
nosoundcolor.Color = BuilderPlug.Me.NoSoundColor;
blocksoundcolor.Color = BuilderPlug.Me.BlockSoundColor;
}
private void okbutton_Click(object sender, EventArgs e)
{
BuilderPlug.Me.HighlightColor = highlightcolor.Color;
BuilderPlug.Me.Level1Color = level1color.Color;
BuilderPlug.Me.Level2Color = level2color.Color;
BuilderPlug.Me.NoSoundColor = nosoundcolor.Color;
BuilderPlug.Me.BlockSoundColor = blocksoundcolor.Color;
General.Settings.WritePluginSetting("highlightcolor", highlightcolor.Color.ToInt());
General.Settings.WritePluginSetting("level1color", level1color.Color.ToInt());
General.Settings.WritePluginSetting("level2color", level2color.Color.ToInt());
General.Settings.WritePluginSetting("nosoundcolor", nosoundcolor.Color.ToInt());
General.Settings.WritePluginSetting("blocksoundcolor", blocksoundcolor.Color.ToInt());
this.Close();
}
private void resetcolors_Click(object sender, EventArgs e)
{
highlightcolor.Color = new PixelColor(255, 0, 192, 0);
level1color.Color = new PixelColor(255, 0, 255, 0);
level2color.Color = new PixelColor(255, 255, 255, 0);
nosoundcolor.Color = new PixelColor(255, 160, 160, 160);
blocksoundcolor.Color = new PixelColor(255, 255, 0, 0);
}
}
}

View file

@ -0,0 +1,169 @@
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
partial class ColorConfiguration
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.highlightcolor = new CodeImp.DoomBuilder.Controls.ColorControl();
this.level1color = new CodeImp.DoomBuilder.Controls.ColorControl();
this.level2color = new CodeImp.DoomBuilder.Controls.ColorControl();
this.nosoundcolor = new CodeImp.DoomBuilder.Controls.ColorControl();
this.blocksoundcolor = new CodeImp.DoomBuilder.Controls.ColorControl();
this.okbutton = new System.Windows.Forms.Button();
this.cancelbutton = new System.Windows.Forms.Button();
this.resetcolors = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// highlightcolor
//
this.highlightcolor.BackColor = System.Drawing.Color.Transparent;
this.highlightcolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.highlightcolor.Label = "Highlight color:";
this.highlightcolor.Location = new System.Drawing.Point(12, 12);
this.highlightcolor.MaximumSize = new System.Drawing.Size(10000, 23);
this.highlightcolor.MinimumSize = new System.Drawing.Size(100, 23);
this.highlightcolor.Name = "highlightcolor";
this.highlightcolor.Size = new System.Drawing.Size(168, 23);
this.highlightcolor.TabIndex = 1;
//
// level1color
//
this.level1color.BackColor = System.Drawing.Color.Transparent;
this.level1color.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.level1color.Label = "Level 1 color:";
this.level1color.Location = new System.Drawing.Point(12, 41);
this.level1color.MaximumSize = new System.Drawing.Size(10000, 23);
this.level1color.MinimumSize = new System.Drawing.Size(100, 23);
this.level1color.Name = "level1color";
this.level1color.Size = new System.Drawing.Size(168, 23);
this.level1color.TabIndex = 2;
//
// level2color
//
this.level2color.BackColor = System.Drawing.Color.Transparent;
this.level2color.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.level2color.Label = "Level 2 color:";
this.level2color.Location = new System.Drawing.Point(12, 70);
this.level2color.MaximumSize = new System.Drawing.Size(10000, 23);
this.level2color.MinimumSize = new System.Drawing.Size(100, 23);
this.level2color.Name = "level2color";
this.level2color.Size = new System.Drawing.Size(168, 23);
this.level2color.TabIndex = 3;
//
// nosoundcolor
//
this.nosoundcolor.BackColor = System.Drawing.Color.Transparent;
this.nosoundcolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.nosoundcolor.Label = "No sound color:";
this.nosoundcolor.Location = new System.Drawing.Point(12, 99);
this.nosoundcolor.MaximumSize = new System.Drawing.Size(10000, 23);
this.nosoundcolor.MinimumSize = new System.Drawing.Size(100, 23);
this.nosoundcolor.Name = "nosoundcolor";
this.nosoundcolor.Size = new System.Drawing.Size(168, 23);
this.nosoundcolor.TabIndex = 4;
//
// blocksoundcolor
//
this.blocksoundcolor.BackColor = System.Drawing.Color.Transparent;
this.blocksoundcolor.Font = new System.Drawing.Font("Arial", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.blocksoundcolor.Label = "Block sound color:";
this.blocksoundcolor.Location = new System.Drawing.Point(12, 128);
this.blocksoundcolor.MaximumSize = new System.Drawing.Size(10000, 23);
this.blocksoundcolor.MinimumSize = new System.Drawing.Size(100, 23);
this.blocksoundcolor.Name = "blocksoundcolor";
this.blocksoundcolor.Size = new System.Drawing.Size(168, 23);
this.blocksoundcolor.TabIndex = 5;
//
// okbutton
//
this.okbutton.Location = new System.Drawing.Point(105, 186);
this.okbutton.Name = "okbutton";
this.okbutton.Size = new System.Drawing.Size(75, 23);
this.okbutton.TabIndex = 6;
this.okbutton.Text = "OK";
this.okbutton.UseVisualStyleBackColor = true;
this.okbutton.Click += new System.EventHandler(this.okbutton_Click);
//
// cancelbutton
//
this.cancelbutton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelbutton.Location = new System.Drawing.Point(12, 186);
this.cancelbutton.Name = "cancelbutton";
this.cancelbutton.Size = new System.Drawing.Size(75, 23);
this.cancelbutton.TabIndex = 7;
this.cancelbutton.Text = "Cancel";
this.cancelbutton.UseVisualStyleBackColor = true;
//
// resetcolors
//
this.resetcolors.Location = new System.Drawing.Point(12, 157);
this.resetcolors.Name = "resetcolors";
this.resetcolors.Size = new System.Drawing.Size(168, 23);
this.resetcolors.TabIndex = 8;
this.resetcolors.Text = "Reset colors";
this.resetcolors.UseVisualStyleBackColor = true;
this.resetcolors.Click += new System.EventHandler(this.resetcolors_Click);
//
// ColorConfiguration
//
this.AcceptButton = this.okbutton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelbutton;
this.ClientSize = new System.Drawing.Size(196, 223);
this.Controls.Add(this.resetcolors);
this.Controls.Add(this.cancelbutton);
this.Controls.Add(this.okbutton);
this.Controls.Add(this.blocksoundcolor);
this.Controls.Add(this.nosoundcolor);
this.Controls.Add(this.level2color);
this.Controls.Add(this.level1color);
this.Controls.Add(this.highlightcolor);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "ColorConfiguration";
this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Color configuration";
this.ResumeLayout(false);
}
#endregion
private CodeImp.DoomBuilder.Controls.ColorControl highlightcolor;
private CodeImp.DoomBuilder.Controls.ColorControl level1color;
private CodeImp.DoomBuilder.Controls.ColorControl level2color;
private CodeImp.DoomBuilder.Controls.ColorControl nosoundcolor;
private CodeImp.DoomBuilder.Controls.ColorControl blocksoundcolor;
private System.Windows.Forms.Button okbutton;
private System.Windows.Forms.Button cancelbutton;
private System.Windows.Forms.Button resetcolors;
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -0,0 +1,21 @@
using System;
using System.Windows.Forms;
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
public partial class MenusForm : Form
{
public ToolStripButton ColorConfiguration { get { return colorconfiguration; } }
public MenusForm()
{
InitializeComponent();
}
// This invokes an action from control event
private void InvokeTaggedAction(object sender, EventArgs e)
{
General.Interface.InvokeTaggedAction(sender, e);
}
}
}

View file

@ -0,0 +1,77 @@
namespace CodeImp.DoomBuilder.SoundPropagationMode
{
partial class MenusForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.toolStrip1 = new System.Windows.Forms.ToolStrip();
this.colorconfiguration = new System.Windows.Forms.ToolStripButton();
this.toolStrip1.SuspendLayout();
this.SuspendLayout();
//
// toolStrip1
//
this.toolStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.colorconfiguration});
this.toolStrip1.Location = new System.Drawing.Point(0, 0);
this.toolStrip1.Name = "toolStrip1";
this.toolStrip1.Size = new System.Drawing.Size(284, 25);
this.toolStrip1.TabIndex = 0;
this.toolStrip1.Text = "toolStrip1";
//
// colorconfiguration
//
this.colorconfiguration.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image;
this.colorconfiguration.Image = global::SoundPropagationMode.Properties.Resources.ColorManagement;
this.colorconfiguration.ImageTransparentColor = System.Drawing.Color.Magenta;
this.colorconfiguration.Name = "colorconfiguration";
this.colorconfiguration.Size = new System.Drawing.Size(23, 22);
this.colorconfiguration.Tag = "soundpropagationcolorconfiguration";
this.colorconfiguration.Text = "Configure colors";
this.colorconfiguration.Click += new System.EventHandler(this.InvokeTaggedAction);
//
// MenusForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.toolStrip1);
this.Name = "MenusForm";
this.Text = "MenuForm";
this.toolStrip1.ResumeLayout(false);
this.toolStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ToolStrip toolStrip1;
private System.Windows.Forms.ToolStripButton colorconfiguration;
}
}

View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="toolStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>