#region ================== Copyright (c) 2007 Pascal vd Heiden /* * Copyright (c) 2007 Pascal vd Heiden, www.codeimp.com * 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.Linq; using System.Reflection; using CodeImp.DoomBuilder.Config; using CodeImp.DoomBuilder.Geometry; using CodeImp.DoomBuilder.Map; #endregion namespace CodeImp.DoomBuilder.BuilderModes { //mxd [AttributeUsage(AttributeTargets.Field)] public class FieldDescription : Attribute { private bool doom = true; private bool hexen = true; private bool udmf = true; private bool srb2 = true; private string description = "Unnamed field"; private string fieldname1; private string fieldname2; public bool DOOM { get { return doom; } set { doom = value; } } public bool HEXEN { get { return hexen; } set { hexen = value; } } public bool UDMF { get { return udmf; } set { udmf = value; } } public bool SRB2 { get { return srb2; } set { srb2 = value; } } public string Description { get { return description; } set { description = value; } } public string Field1 { get { return fieldname1; } set { fieldname1 = value; } } public string Field2 { get { return fieldname2; } set { fieldname2 = value; } } public bool SupportsCurrentMapFormat { get { if(General.Map == null) return false; //if(!string.IsNullOrEmpty(fieldname1) || !string.IsNullOrEmpty(fieldname2)) return General.Map.UDMF; return (General.Map.DOOM && doom || General.Map.HEXEN && hexen || (General.Map.UDMF && udmf && srb2)); } } } public abstract class MapElementPropertiesCopySettings { protected MapElementPropertiesCopySettings() { // Set all properties supported by currect map format to true FieldInfo[] props = this.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); foreach(FieldInfo prop in props) { foreach(Attribute attr in Attribute.GetCustomAttributes(prop)) { if(attr.GetType() != typeof(FieldDescription)) continue; FieldDescription fd = (FieldDescription)attr; prop.SetValue(this, fd.SupportsCurrentMapFormat); } } } } //mxd public abstract class MapElementProperties { private readonly UniFields fields; // Only custom (e.g. not UI-managed) fields here private readonly UniFields uifields; // Only UI-managed fields here private readonly MapElementType elementtype; protected MapElementProperties(UniFields other, MapElementType type) { // Should we bother? if(!General.Map.UDMF) return; // Copy source fields except the ones controlled by the UI fields = new UniFields(); uifields = new UniFields(); elementtype = type; foreach(KeyValuePair group in other) { if(General.Map.FormatInterface.UIFields[elementtype].ContainsKey(group.Key)) uifields.Add(group.Key, new UniValue(group.Value)); else fields.Add(group.Key, new UniValue(group.Value)); } } protected void ApplyUIFields(ICollection collection, MapElementPropertiesCopySettings settings) where T : MapElement { FieldInfo[] props = settings.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); foreach(FieldInfo prop in props) { // Property set? if(!(bool)prop.GetValue(settings)) continue; foreach(Attribute attr in Attribute.GetCustomAttributes(prop)) { if(attr.GetType() != typeof(FieldDescription)) continue; FieldDescription fd = (FieldDescription)attr; if(fd.SupportsCurrentMapFormat && !string.IsNullOrEmpty(fd.Field1)) { if(!string.IsNullOrEmpty(fd.Field2)) foreach(T me in collection) Apply(me.Fields, fd.Field1, fd.Field2); else foreach(T me in collection) Apply(me.Fields, fd.Field1); } } } } protected void Apply(UniFields other, string key) { if(uifields.ContainsKey(key)) other[key] = new UniValue(uifields[key]); else if(other.ContainsKey(key)) other.Remove(key); } protected void Apply(UniFields other, string key1, string key2) { Apply(other, key1); Apply(other, key2); } protected void ApplyCustomFields(UniFields other) { // Remove custom fields string[] keys = new string[other.Keys.Count]; other.Keys.CopyTo(keys, 0); foreach(string key in keys) { if(!General.Map.FormatInterface.UIFields[elementtype].ContainsKey(key)) other.Remove(key); } // Replace with stored custom fields foreach(KeyValuePair group in fields) { other.Add(group.Key, new UniValue(group.Value)); } } } #region ================== Vertex //mxd public class VertexPropertiesCopySettings : MapElementPropertiesCopySettings { [FieldDescription(Description = "Vertex floor height", Field1 = "zfloor")] public bool ZFloor = true; [FieldDescription(Description = "Vertex ceiling height", Field1 = "zceiling")] public bool ZCeiling = true; [FieldDescription(Description = "Custom fields", DOOM = false, HEXEN = false)] public bool Fields = true; } // Vertex public class VertexProperties : MapElementProperties { private static VertexPropertiesCopySettings defaultsettings = new VertexPropertiesCopySettings(); public static VertexPropertiesCopySettings CopySettings = new VertexPropertiesCopySettings(); private readonly double zceiling; //mxd private readonly double zfloor; //mxd public VertexProperties(Vertex v) : base(v.Fields, MapElementType.VERTEX) { zceiling = v.ZCeiling; //mxd zfloor = v.ZFloor; //mxd } //mxd. Applies coped properties public void Apply(ICollection verts, bool usecopysettings) { Apply(verts, (usecopysettings ? CopySettings : defaultsettings)); } //mxd. Applies coped properties using selected settings public void Apply(ICollection verts, VertexPropertiesCopySettings settings) { foreach(Vertex v in verts) { if(settings.ZCeiling) v.ZCeiling = zceiling; if(settings.ZFloor) v.ZFloor = zfloor; if(settings.Fields) { v.Fields.BeforeFieldsChange(); ApplyCustomFields(v.Fields); } } } } #endregion #region ================== Sector //mxd public class SectorPropertiesCopySettings : MapElementPropertiesCopySettings { [FieldDescription(Description = "Floor height")] public bool FloorHeight = true; [FieldDescription(Description = "Ceiling height")] public bool CeilingHeight = true; [FieldDescription(Description = "Floor texture")] public bool FloorTexture = true; [FieldDescription(Description = "Ceiling texture")] public bool CeilingTexture = true; [FieldDescription(Description = "Floor texture offsets", Field1 = "xpanningfloor", Field2 = "ypanningfloor")] public bool FloorTextureOffset = true; [FieldDescription(Description = "Ceiling texture offsets", Field1 = "xpanningceiling", Field2 = "ypanningceiling")] public bool CeilingTextureOffset = true; [FieldDescription(Description = "Floor texture scale", Field1 = "xscalefloor", Field2 = "yscalefloor")] public bool FloorTextureScale = true; [FieldDescription(Description = "Ceiling texture scale", Field1 = "xscaleceiling", Field2 = "yscaleceiling")] public bool CeilingTextureScale = true; [FieldDescription(Description = "Floor texture rotation", Field1 = "rotationfloor")] public bool FloorTextureRotation = true; [FieldDescription(Description = "Ceiling texture rotation", Field1 = "rotationceiling")] public bool CeilingTextureRotation = true; [FieldDescription(Description = "Floor alpha", Field1 = "alphafloor", SRB2 = false)] public bool FloorAlpha = true; [FieldDescription(Description = "Ceiling alpha", Field1 = "alphaceiling", SRB2 = false)] public bool CeilingAlpha = true; [FieldDescription(Description = "Floor portal alpha", Field1 = "portal_floor_alpha", SRB2 = false)] public bool FloorPortalAlpha = true; [FieldDescription(Description = "Ceiling portal alpha", Field1 = "portal_ceil_alpha", SRB2 = false)] public bool CeilingPortalAlpha = true; [FieldDescription(Description = "Sector brightness")] public bool Brightness = true; [FieldDescription(Description = "Floor brightness", Field1 = "lightfloor", Field2 = "lightfloorabsolute")] public bool FloorBrightness = true; [FieldDescription(Description = "Ceiling brightness", Field1 = "lightceiling", Field2 = "lightceilingabsolute")] public bool CeilingBrightness = true; [FieldDescription(Description = "Floor render style", Field1 = "renderstylefloor", SRB2 = false)] public bool FloorRenderStyle = true; [FieldDescription(Description = "Ceiling render style", Field1 = "renderstyleceiling", SRB2 = false)] public bool CeilingRenderStyle = true; [FieldDescription(Description = "Floor portal render style", Field1 = "portal_floor_overlaytype", SRB2 = false)] public bool FloorPortalRenderStyle = true; [FieldDescription(Description = "Ceiling portal render style", Field1 = "portal_ceil_overlaytype", SRB2 = false)] public bool CeilingPortalRenderStyle = true; [FieldDescription(Description = "Floor slope", DOOM = false, HEXEN = false)] public bool FloorSlope = true; [FieldDescription(Description = "Ceiling slope", DOOM = false, HEXEN = false)] public bool CeilingSlope = true; [FieldDescription(Description = "Floor terrain", Field1 = "floorterrain", SRB2 = false)] public bool FloorTerrain = true; [FieldDescription(Description = "Ceiling terrain", Field1 = "ceilingterrain", SRB2 = false)] public bool CeilingTerrain = true; [FieldDescription(Description = "Tags")] public bool Tag = true; [FieldDescription(Description = "Effect")] public bool Special = true; [FieldDescription(Description = "Flags", DOOM = false, HEXEN = false)] public bool Flags = true; [FieldDescription(Description = "Light color", Field1 = "lightcolor")] public bool LightColor = true; [FieldDescription(Description = "Light alpha", Field1 = "lightalpha")] public bool LightAlpha = true; [FieldDescription(Description = "Fade color", Field1 = "fadecolor")] public bool FadeColor = true; [FieldDescription(Description = "Fade alpha", Field1 = "fadealpha")] public bool FadeAlpha = true; [FieldDescription(Description = "Fade start", Field1 = "fadestart")] public bool FadeStart = true; [FieldDescription(Description = "Fade end", Field1 = "fadeend")] public bool Fade = true; [FieldDescription(Description = "Floor color", Field1 = "color_floor", SRB2 = false)] public bool FloorColor = true; [FieldDescription(Description = "Ceiling color", Field1 = "color_ceiling", SRB2 = false)] public bool CeilingColor = true; [FieldDescription(Description = "Top wall color", Field1 = "color_walltop", SRB2 = false)] public bool TopWallColor = true; [FieldDescription(Description = "Bottom wall color", Field1 = "color_wallbottom", SRB2 = false)] public bool BottomWallColor = true; [FieldDescription(Description = "Sprites color", Field1 = "color_sprites", SRB2 = false)] public bool SpritesColor = true; [FieldDescription(Description = "Floor glow", Field1 = "floorglowcolor", Field2 = "floorglowheight", SRB2 = false)] public bool FloorGlow = true; [FieldDescription(Description = "Ceiling glow", Field1 = "ceilingglowcolor", Field2 = "ceilingglowheight", SRB2 = false)] public bool CeilingGlow = true; [FieldDescription(Description = "Fog density", Field1 = "fogdensity", SRB2 = false)] public bool FogDensity = true; [FieldDescription(Description = "Desaturation", Field1 = "desaturation", SRB2 = false)] public bool Desaturation = true; [FieldDescription(Description = "Damage type", Field1 = "damagetype")] public bool DamageType = true; [FieldDescription(Description = "Damage amount", Field1 = "damageamount", SRB2 = false)] public bool DamageAmount = true; [FieldDescription(Description = "Damage interval", Field1 = "damageinterval", SRB2 = false)] public bool DamageInterval = true; [FieldDescription(Description = "Damage leakiness", Field1 = "leakiness", SRB2 = false)] public bool DamageLeakiness = true; [FieldDescription(Description = "Sound sequence", Field1 = "soundsequence", SRB2 = false)] public bool SoundSequence = true; [FieldDescription(Description = "Gravity", Field1 = "gravity")] public bool Gravity = true; [FieldDescription(Description = "Friction", Field1 = "friction")] public bool Friction = true; [FieldDescription(Description = "Trigger tag", Field1 = "triggertag")] public bool TriggerTag = true; [FieldDescription(Description = "Triggerer", Field1 = "triggerer")] public bool Triggerer = true; [FieldDescription(Description = "Custom fields", DOOM = false, HEXEN = false)] public bool Fields = true; [FieldDescription(Description = "Comment", Field1 = "comment")] public bool Comment = true; } // Sector public class SectorProperties : MapElementProperties { //mxd private static SectorPropertiesCopySettings defaultsettings = new SectorPropertiesCopySettings(); public static SectorPropertiesCopySettings CopySettings = new SectorPropertiesCopySettings(); private readonly int floorheight; private readonly int ceilheight; private readonly string floortexture; private readonly string ceilingtexture; private readonly int effect; private readonly int brightness; private readonly double ceilslopeoffset; private readonly double floorslopeoffset; private readonly Vector3D ceilslope; private readonly Vector3D floorslope; private readonly List tags; private readonly Dictionary flags; //mxd public SectorProperties(Sector s) : base(s.Fields, MapElementType.SECTOR) { floorheight = s.FloorHeight; ceilheight = s.CeilHeight; floortexture = s.FloorTexture; ceilingtexture = s.CeilTexture; brightness = s.Brightness; effect = s.Effect; ceilslopeoffset = s.CeilSlopeOffset; floorslopeoffset = s.FloorSlopeOffset; ceilslope = s.CeilSlope; floorslope = s.FloorSlope; tags = new List(s.Tags); //mxd flags = s.GetFlags(); //mxd } //mxd. Applies coped properties public void Apply(ICollection sectors, bool usecopysettings) { Apply(sectors, (usecopysettings ? CopySettings : defaultsettings)); } //mxd. Applies coped properties using selected settings public void Apply(ICollection sectors, SectorPropertiesCopySettings settings) { foreach(Sector s in sectors) { if(settings.FloorHeight) s.FloorHeight = floorheight; if(settings.CeilingHeight) s.CeilHeight = ceilheight; if(settings.FloorTexture) s.SetFloorTexture(floortexture); if(settings.CeilingTexture) s.SetCeilTexture(ceilingtexture); if(settings.Brightness) s.Brightness = brightness; if(settings.Tag) s.Tags = new List(tags); //mxd if(settings.Special) s.Effect = effect; if(settings.CeilingSlope) { s.CeilSlopeOffset = ceilslopeoffset; s.CeilSlope = ceilslope; } if(settings.FloorSlope) { s.FloorSlopeOffset = floorslopeoffset; s.FloorSlope = floorslope; } if(settings.Flags) { s.ClearFlags(); //mxd foreach(KeyValuePair f in flags) //mxd s.SetFlag(f.Key, f.Value); } } // Should we bother? if(!General.Map.UDMF) return; // Apply custom fields foreach(Sector s in sectors) { s.Fields.BeforeFieldsChange(); if(settings.Fields) ApplyCustomFields(s.Fields); } // Apply UI fields ApplyUIFields(sectors, settings); } } #endregion #region ================== Sidedef //mxd public class SidedefPropertiesCopySettings : MapElementPropertiesCopySettings { [FieldDescription(Description = "Upper texture")] public bool UpperTexture = true; [FieldDescription(Description = "Middle texture")] public bool MiddleTexture = true; [FieldDescription(Description = "Lower texture")] public bool LowerTexture = true; [FieldDescription(Description = "Texture offset X")] public bool OffsetX = true; [FieldDescription(Description = "Texture offset Y")] public bool OffsetY = true; [FieldDescription(Description = "Upper texture offsets", Field1 = "offsetx_top", Field2 = "offsety_top")] public bool UpperTextureOffset = true; [FieldDescription(Description = "Middle texture offsets", Field1 = "offsetx_mid", Field2 = "offsety_mid")] public bool MiddleTextureOffset = true; [FieldDescription(Description = "Lower texture offsets", Field1 = "offsetx_bottom", Field2 = "offsety_bottom")] public bool LowerTextureOffset = true; [FieldDescription(Description = "Upper texture scale", Field1 = "scalex_top", Field2 = "scaley_top")] public bool UpperTextureScale = true; [FieldDescription(Description = "Middle texture scale", Field1 = "scalex_mid", Field2 = "scaley_mid")] public bool MiddleTextureScale = true; [FieldDescription(Description = "Lower texture scale", Field1 = "scalex_bottom", Field2 = "scaley_bottom")] public bool LowerTextureScale = true; [FieldDescription(Description = "Brightness", Field1 = "light", Field2 = "lightabsolute")] public bool Brightness = true; [FieldDescription(Description = "Texture repeats", Field1 = "repeatcnt")] public bool TextureRepeats = true; [FieldDescription(Description = "Flags", DOOM = false, HEXEN = false, SRB2 = false)] public bool Flags = true; [FieldDescription(Description = "Custom fields", DOOM = false, HEXEN = false)] public bool Fields = true; } // Sidedef public class SidedefProperties : MapElementProperties { //mxd internal static SidedefPropertiesCopySettings DefaultSettings = new SidedefPropertiesCopySettings(); public static SidedefPropertiesCopySettings CopySettings = new SidedefPropertiesCopySettings(); private readonly string hightexture; private readonly string middletexture; private readonly string lowtexture; private readonly int offsetx; private readonly int offsety; private readonly Dictionary flags; //mxd public SidedefProperties(Sidedef s) : base(s.Fields, MapElementType.SIDEDEF) { hightexture = s.HighTexture; middletexture = s.MiddleTexture; lowtexture = s.LowTexture; offsetx = s.OffsetX; offsety = s.OffsetY; flags = s.GetFlags(); //mxd } //mxd. Applies coped properties with all settings enabled public void Apply(ICollection sides, bool usecopysettings) { Apply(sides, (usecopysettings ? CopySettings : DefaultSettings)); } //mxd. Applies selected settings public void Apply(ICollection sides, SidedefPropertiesCopySettings settings) { foreach(Sidedef s in sides) { if(settings.UpperTexture) s.SetTextureHigh(hightexture); if(settings.MiddleTexture) s.SetTextureMid(middletexture); if(settings.LowerTexture) s.SetTextureLow(lowtexture); if(settings.OffsetX) s.OffsetX = offsetx; if(settings.OffsetY) s.OffsetY = offsety; if(settings.Flags) { s.ClearFlags(); //mxd foreach(KeyValuePair f in flags) //mxd s.SetFlag(f.Key, f.Value); } } // Should we bother? if(!General.Map.UDMF) return; // Apply fields foreach(Sidedef s in sides) { s.Fields.BeforeFieldsChange(); if(settings.Fields) ApplyCustomFields(s.Fields); } // Apply UI fields ApplyUIFields(sides, settings); } } #endregion #region ================== Linedef //mxd public class LinedefPropertiesCopySettings : MapElementPropertiesCopySettings { [FieldDescription(Description = "Action")] public bool Action = true; [FieldDescription(Description = "Action arguments", DOOM = false)] public bool Arguments = true; [FieldDescription(Description = "Activation", DOOM = false, UDMF = false)] public bool Activation = true; [FieldDescription(Description = "Tags", HEXEN = false)] public bool Tag = true; [FieldDescription(Description = "Flags")] public bool Flags = true; [FieldDescription(Description = "Alpha", Field1 = "alpha")] public bool Alpha = true; [FieldDescription(Description = "Render style", Field1 = "renderstyle")] public bool RenderStyle = true; [FieldDescription(Description = "Lock number", Field1 = "locknumber", SRB2 = false)] public bool LockNumber = true; [FieldDescription(Description = "Executor delay", Field1 = "executordelay")] public bool ExecutorDelay = true; [FieldDescription(Description = "Custom fields", DOOM = false, HEXEN = false)] public bool Fields = true; [FieldDescription(Description = "Comment", Field1 = "comment")] public bool Comment = true; } // Linedef public class LinedefProperties : MapElementProperties { //mxd private static LinedefPropertiesCopySettings defaultsettings = new LinedefPropertiesCopySettings(); public static LinedefPropertiesCopySettings CopySettings = new LinedefPropertiesCopySettings(); private readonly SidedefProperties front; private readonly SidedefProperties back; private readonly Dictionary flags; private readonly int action; private readonly int activate; private readonly List tags; private readonly int[] args; public LinedefProperties(Linedef l) : base(l.Fields, MapElementType.LINEDEF) { front = (l.Front != null ? new SidedefProperties(l.Front) : null); back = (l.Back != null ? new SidedefProperties(l.Back) : null); flags = l.GetFlags(); action = l.Action; activate = l.Activate; tags = new List(l.Tags); //mxd args = (int[])(l.Args.Clone()); } //mxd. Applies coped properties with all settings enabled public void Apply(ICollection lines, bool usecopysettings) { Apply(lines, usecopysettings, true); } public void Apply(ICollection lines, bool usecopysettings, bool applytosidedefs) { if(usecopysettings) Apply(lines, CopySettings, (applytosidedefs ? SidedefProperties.CopySettings : null)); else Apply(lines, defaultsettings, (applytosidedefs ? SidedefProperties.DefaultSettings : null)); } //mxd. Applies selected linededf and sidedef settings public void Apply(ICollection lines, LinedefPropertiesCopySettings settings, SidedefPropertiesCopySettings sidesettings) { List frontsides = new List(lines.Count); List backsides = new List(lines.Count); foreach(Linedef l in lines) { if(settings.Flags) { l.ClearFlags(); foreach(KeyValuePair f in flags) l.SetFlag(f.Key, f.Value); } if(settings.Activation) l.Activate = activate; if(settings.Tag) l.Tags = new List(tags); //mxd if(settings.Action) l.Action = action; if(settings.Arguments) { for(int i = 0; i < l.Args.Length; i++) l.Args[i] = args[i]; } if(l.Front != null) frontsides.Add(l.Front); if(l.Back != null) backsides.Add(l.Back); } // Should we bother? if(General.Map.UDMF) { // Apply fields foreach(Linedef l in lines) { l.Fields.BeforeFieldsChange(); // Apply string arguments if(settings.Arguments) { //Apply(l.Fields, "arg0str"); Apply(l.Fields, "stringarg0"); Apply(l.Fields, "stringarg1"); //TODO: re-enable when UI part is ready //Apply(l.Fields, "arg1str"); //Apply(l.Fields, "arg2str"); //Apply(l.Fields, "arg3str"); //Apply(l.Fields, "arg4str"); } // Apply custom fields if(settings.Fields) ApplyCustomFields(l.Fields); } // Apply UI fields ApplyUIFields(lines, settings); } // Apply sidedef settings if(sidesettings != null) { if(front != null) front.Apply(frontsides, sidesettings); if(back != null) back.Apply(backsides, sidesettings); } } } #endregion #region ================== Thing //mxd public class ThingPropertiesCopySettings : MapElementPropertiesCopySettings { [FieldDescription(Description = "Type")] public bool Type = true; [FieldDescription(Description = "Angle")] public bool Angle = true; [FieldDescription(Description = "Z-height", DOOM = false)] public bool ZHeight = true; [FieldDescription(Description = "Pitch", DOOM = false, HEXEN = false)] public bool Pitch = true; [FieldDescription(Description = "Roll", DOOM = false, HEXEN = false)] public bool Roll = true; [FieldDescription(Description = "Scale", DOOM = false, HEXEN = false)] public bool Scale = true; [FieldDescription(Description = "Action", DOOM = false, SRB2 = false)] public bool Action = true; [FieldDescription(Description = "Arguments", DOOM = false)] public bool Arguments = true; [FieldDescription(Description = "Tag", DOOM = false)] public bool Tag = true; [FieldDescription(Description = "Flags")] public bool Flags = true; [FieldDescription(Description = "Conversation ID", Field1 = "conversation", SRB2 = false)] public bool Conversation = true; [FieldDescription(Description = "Gravity", Field1 = "gravity", SRB2 = false)] public bool Gravity = true; [FieldDescription(Description = "Health multiplier", Field1 = "health", SRB2 = false)] public bool Health = true; [FieldDescription(Description = "Score", Field1 = "score", SRB2 = false)] public bool Score = true; [FieldDescription(Description = "Float bob phase", Field1 = "floatbobphase", SRB2 = false)] public bool FloatBobPhase = true; [FieldDescription(Description = "Alpha", Field1 = "alpha", SRB2 = false)] public bool Alpha = true; [FieldDescription(Description = "Fill color", Field1 = "fillcolor", SRB2 = false)] public bool FillColor = true; [FieldDescription(Description = "Render style", Field1 = "renderstyle", SRB2 = false)] public bool RenderStyle = true; [FieldDescription(Description = "Custom fields", DOOM = false, HEXEN = false)] public bool Fields = true; [FieldDescription(Description = "Comment", Field1 = "comment")] public bool Comment = true; } // Thing public class ThingProperties : MapElementProperties { //mxd private static readonly ThingPropertiesCopySettings defaultsettings = new ThingPropertiesCopySettings(); public static readonly ThingPropertiesCopySettings CopySettings = new ThingPropertiesCopySettings(); private readonly int type; private readonly double angle; private readonly double zheight; //mxd private readonly int pitch; //mxd private readonly int roll; //mxd private readonly double scalex; //mxd private readonly double scaley; //mxd private readonly Dictionary flags; private readonly int tag; private readonly int action; private readonly int[] args; public ThingProperties(Thing t) : base(t.Fields, MapElementType.THING) { type = t.Type; angle = t.Angle; zheight = t.Position.z; pitch = t.Pitch; roll = t.Roll; scalex = t.ScaleX; scaley = t.ScaleY; flags = t.GetFlags(); tag = t.Tag; action = t.Action; args = (int[])(t.Args.Clone()); } //mxd. Applies coped properties with all settings enabled public void Apply(ICollection things, bool usecopysettings) { Apply(things, (usecopysettings ? CopySettings : defaultsettings)); } //mxd. Applies selected settings public void Apply(ICollection things, ThingPropertiesCopySettings settings) { foreach(Thing t in things) { if(settings.Type) t.Type = type; // Update the setting from the configuration after changing the type. This makes sure all the following // actions work, since there can be problems when changing the type from or to thing that has a model defined t.UpdateConfiguration(); if(settings.Angle) t.Rotate(angle); if(settings.ZHeight) t.Move(t.Position.x, t.Position.y, zheight); if(settings.Pitch) t.SetPitch(pitch); if(settings.Roll) t.SetRoll(roll); if(settings.Scale) t.SetScale(scalex, scaley); if(settings.Flags) { t.ClearFlags(); foreach(KeyValuePair f in flags) t.SetFlag(f.Key, f.Value); } if(settings.Tag) t.Tag = tag; if(settings.Action) t.Action = action; if(settings.Arguments) { for(int i = 0; i < t.Args.Length; i++) t.Args[i] = args[i]; } } // Should we bother? if(!General.Map.UDMF) return; foreach(Thing t in things) { // Apply fields t.Fields.BeforeFieldsChange(); // Apply string arguments if(settings.Arguments) { //Apply(t.Fields, "arg0str"); Apply(t.Fields, "stringarg0"); Apply(t.Fields, "stringarg1"); //TODO: re-enable when UI part is ready //Apply(t.Fields, "arg1str"); //Apply(t.Fields, "arg2str"); //Apply(t.Fields, "arg3str"); //Apply(t.Fields, "arg4str"); } // Apply custom fields if(settings.Fields) ApplyCustomFields(t.Fields); } // Apply UI fields ApplyUIFields(things, settings); } } #endregion #region ================== Properties Comparer //mxd. A class, which checks whether source and target map element's properties match public static class PropertiesComparer { #region Vertex public static bool PropertiesMatch(VertexPropertiesCopySettings flags, Vertex source, Vertex target) { if(!General.Map.UDMF) return true; // Built-in properties if(flags.ZCeiling && source.ZCeiling != target.ZCeiling) return false; if(flags.ZFloor && source.ZFloor != target.ZFloor) return false; // Custom fields return !flags.Fields || UniFields.CustomFieldsMatch(source.Fields, target.Fields); } #endregion #region Sector public static bool PropertiesMatch(SectorPropertiesCopySettings flags, Sector source, Sector target) { // Built-in properties if(flags.FloorHeight && source.FloorHeight != target.FloorHeight) return false; if(flags.CeilingHeight && source.CeilHeight != target.CeilHeight) return false; if(flags.FloorTexture && source.FloorTexture != target.FloorTexture) return false; if(flags.CeilingTexture && source.CeilTexture != target.CeilTexture) return false; if(flags.Brightness && source.Brightness != target.Brightness) return false; if(flags.Tag && !TagsMatch(source.Tags, target.Tags)) return false; if(flags.Flags && !FlagsMatch(source.GetEnabledFlags(), target.GetEnabledFlags())) return false; // Generalized effects require more tender loving care... if(flags.Special && source.Effect != target.Effect) { if(!General.Map.Config.GeneralizedEffects || source.Effect == 0 || target.Effect == 0) return false; // Get effect bits... SectorEffectData sourcedata = General.Map.Config.GetSectorEffectData(source.Effect); SectorEffectData targetdata = General.Map.Config.GetSectorEffectData(target.Effect); // No bits match when at least one effect is not generalized, or when bits don't overlap if(sourcedata.Effect != targetdata.Effect || sourcedata.GeneralizedBits.Count != targetdata.GeneralizedBits.Count || !sourcedata.GeneralizedBits.Overlaps(targetdata.GeneralizedBits)) return false; } if(!General.Map.UDMF) return true; // UI fields if(!UIFieldsMatch(flags, source, target)) return false; // Custom fields return !flags.Fields || UniFields.CustomFieldsMatch(source.Fields, target.Fields); } #endregion #region Linedef public static bool PropertiesMatch(LinedefPropertiesCopySettings linedefflags, SidedefPropertiesCopySettings sideflags, Linedef source, Linedef target) { // Built-in properties if(linedefflags.Action && source.Action != target.Action) return false; if(linedefflags.Activation && source.Activate != target.Activate) return false; if(linedefflags.Tag && !TagsMatch(source.Tags, target.Tags)) return false; if(linedefflags.Arguments) { // Classic args for(int i = 0; i < source.Args.Length; i++) if(source.Args[i] != target.Args[i]) return false; for (int i = 0; i < source.Args.Length; i++) if (source.Args[i] != target.Args[i]) return false; // String args if (General.Map.UDMF) { if(!UniFields.ValuesMatch("arg0str", source, target)) return false; if(!UniFields.ValuesMatch("arg1str", source, target)) return false; if(!UniFields.ValuesMatch("arg2str", source, target)) return false; if(!UniFields.ValuesMatch("arg3str", source, target)) return false; if(!UniFields.ValuesMatch("arg4str", source, target)) return false; if (!UniFields.ValuesMatch("stringarg0", source, target)) return false; if (!UniFields.ValuesMatch("stringarg1", source, target)) return false; } } if(linedefflags.Flags && !FlagsMatch(source.GetEnabledFlags(), target.GetEnabledFlags())) return false; if(General.Map.UDMF) { // UI fields if(!UIFieldsMatch(linedefflags, source, target)) return false; // Custom fields if(linedefflags.Fields && !UniFields.CustomFieldsMatch(source.Fields, target.Fields)) return false; } // Sidedef properties return (source.Front != null && target.Front != null && PropertiesMatch(sideflags, source.Front, target.Front)) || (source.Front != null && target.Back != null && PropertiesMatch(sideflags, source.Front, target.Back)) || (source.Back != null && target.Front != null && PropertiesMatch(sideflags, source.Back, target.Front)) || (source.Back != null && target.Back != null && PropertiesMatch(sideflags, source.Back, target.Back)); } #endregion #region Sidedef public static bool PropertiesMatch(SidedefPropertiesCopySettings flags, Sidedef source, Sidedef target) { // Built-in properties if(flags.OffsetX && source.OffsetX != target.OffsetX) return false; if(flags.OffsetY && source.OffsetY != target.OffsetY) return false; if(flags.UpperTexture && source.HighTexture != target.HighTexture) return false; if(flags.MiddleTexture && source.MiddleTexture != target.MiddleTexture) return false; if(flags.LowerTexture && source.LowTexture != target.LowTexture) return false; if(!General.Map.UDMF) return true; // UDMF-specific properties if(flags.Flags && !FlagsMatch(source.GetEnabledFlags(), target.GetEnabledFlags())) return false; // UI fields if(!UIFieldsMatch(flags, source, target)) return false; // Custom fields return !flags.Fields || UniFields.CustomFieldsMatch(source.Fields, target.Fields); } #endregion #region Thing public static bool PropertiesMatch(ThingPropertiesCopySettings flags, Thing source, Thing target) { // Built-in properties if(flags.Type && source.Type != target.Type) return false; if(flags.Angle && source.AngleDoom != target.AngleDoom) return false; if(flags.Action && source.Action != target.Action) return false; if(flags.Arguments) { // Classic args for(int i = 0; i < source.Args.Length; i++) if(source.Args[i] != target.Args[i]) return false; // String args if(General.Map.UDMF) { if(!UniFields.ValuesMatch("arg0str", source, target)) return false; if(!UniFields.ValuesMatch("arg1str", source, target)) return false; if(!UniFields.ValuesMatch("arg2str", source, target)) return false; if(!UniFields.ValuesMatch("arg3str", source, target)) return false; if(!UniFields.ValuesMatch("arg4str", source, target)) return false; if (!UniFields.ValuesMatch("stringarg0", source, target)) return false; if (!UniFields.ValuesMatch("stringarg1", source, target)) return false; } } if(flags.Tag && source.Tag != target.Tag) return false; if(flags.Flags && !FlagsMatch(source.GetEnabledFlags(), target.GetEnabledFlags())) return false; if(!General.Map.UDMF) return true; // UDMF-specific properties if(flags.Pitch && source.Pitch != target.Pitch) return false; if(flags.Roll && source.Roll != target.Roll) return false; if(flags.Scale && (source.ScaleX != target.ScaleX) || (source.ScaleY != target.ScaleY)) return false; // UI fields if(!UIFieldsMatch(flags, source, target)) return false; // Custom fields return !flags.Fields || UniFields.CustomFieldsMatch(source.Fields, target.Fields); } #endregion #region Utility private static bool FlagsMatch(HashSet flags1, HashSet flags2) { if(flags1.Count != flags2.Count) return false; foreach(string flag in flags1) { if(!flags2.Contains(flag)) return false; } return true; } //mxd private static bool TagsMatch(List tags1, List tags2) { if(!General.Map.UDMF) return tags1[0] == tags2[0]; if(tags1.Count != tags2.Count) return false; Dictionary count = new Dictionary(); foreach(int s in tags1) { if(count.ContainsKey(s)) count[s]++; else count.Add(s, 1); } foreach(int s in tags2) { if(count.ContainsKey(s)) count[s]--; else return false; } return count.Values.All(c => c == 0); } private static bool UIFieldsMatch(MapElementPropertiesCopySettings settings, MapElement first, MapElement second) { FieldInfo[] props = settings.GetType().GetFields(BindingFlags.Public | BindingFlags.Instance); foreach(FieldInfo prop in props) { // Property set? if(!(bool)prop.GetValue(settings)) continue; foreach(Attribute attr in Attribute.GetCustomAttributes(prop)) { if(attr.GetType() != typeof(FieldDescription)) continue; FieldDescription fd = (FieldDescription)attr; if(fd.SupportsCurrentMapFormat && !string.IsNullOrEmpty(fd.Field1)) { if(!string.IsNullOrEmpty(fd.Field2)) { if(!UniFields.ValuesMatch(fd.Field1, fd.Field2, first, second)) return false; } else { if(!UniFields.ValuesMatch(fd.Field1, first, second)) return false; } } } } return true; } #endregion } #endregion }