mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-23 20:32:34 +00:00
0a7bd9f636
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.
379 lines
13 KiB
C#
379 lines
13 KiB
C#
|
|
#region ================== Copyright (c) 2014 Boris Iwanski
|
|
|
|
/*
|
|
* Copyright (c) 2014 Boris Iwanski
|
|
* This program is released under GNU General Public License
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#endregion
|
|
|
|
#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 :)
|
|
}
|
|
}
|
|
}
|