mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-23 12:22:35 +00:00
17ac1c6db5
Sectors mode: removed "Move Things in Selected Sectors" toolbar button. Sectors mode: added "Synchronized Things Editing" menu item, action and toolbar button. When enabled, things dragging will be synchronized to sector dragging. Regular, Paint and Rectangular selection will also select things (holding Alt while selecting inverts this behavior). Deleting sectors will also delete selected things. Linedefs mode: added "Synchronized Things Editing" menu item, action and toolbar button. When enabled, selected things will be dragged when dragging linedefs. Rectangular selection will also select things (holding Alt while selecting inverts this behavior). Changed: selection info now always displays info about all types of map elements. Fixed: .dbs containing only current map settings was created when trying to save unchanged map. Fixed, Visual mode, "Copy Texture" action: when "Use long texture names" option is disabled, texture name as stored in Sidedef should be used, not the 8-char texture name. Updated documentation.
954 lines
29 KiB
C#
954 lines
29 KiB
C#
|
|
#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.Drawing;
|
|
using System.Windows.Forms;
|
|
using CodeImp.DoomBuilder.Data;
|
|
using CodeImp.DoomBuilder.IO;
|
|
using CodeImp.DoomBuilder.Map;
|
|
using CodeImp.DoomBuilder.Rendering;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using CodeImp.DoomBuilder.VisualModes;
|
|
using CodeImp.DoomBuilder.Windows;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.BuilderModes
|
|
{
|
|
internal abstract class BaseVisualGeometrySector : VisualGeometry, IVisualEventReceiver
|
|
{
|
|
#region ================== Constants
|
|
|
|
private const float DRAG_ANGLE_TOLERANCE = 0.06f;
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
protected BaseVisualMode mode;
|
|
protected long setuponloadedtexture;
|
|
|
|
// This is only used to see if this object has already received a change
|
|
// in a multiselection. The Changed property on the BaseVisualSector is
|
|
// used to indicate a rebuild is needed.
|
|
protected bool changed;
|
|
|
|
protected SectorLevel level;
|
|
protected Effect3DFloor extrafloor;
|
|
|
|
// Undo/redo
|
|
private int undoticket;
|
|
|
|
// UV dragging
|
|
private float dragstartanglexy;
|
|
private float dragstartanglez;
|
|
private Vector3D dragorigin;
|
|
private int startoffsetx;
|
|
private int startoffsety;
|
|
protected bool uvdragging;
|
|
private int prevoffsetx; // We have to provide delta offsets, but I don't
|
|
private int prevoffsety; // want to calculate with delta offsets to prevent
|
|
// inaccuracy in the dragging.
|
|
|
|
private static List<BaseVisualSector> updateList; //mxd
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
new public BaseVisualSector Sector { get { return (BaseVisualSector)base.Sector; } }
|
|
public bool Changed { get { return changed; } set { changed = value; } }
|
|
public SectorLevel Level { get { return level; } }
|
|
public Effect3DFloor ExtraFloor { get { return extrafloor; } }
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor / Destructor
|
|
|
|
// Constructor
|
|
protected BaseVisualGeometrySector(BaseVisualMode mode, VisualSector vs) : base(vs)
|
|
{
|
|
this.mode = mode;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Methods
|
|
|
|
// This changes the height
|
|
protected abstract void ChangeHeight(int amount);
|
|
protected abstract void ChangeTextureScale(int incrementX, int incrementY); //mxd
|
|
public virtual void SelectNeighbours(bool select, bool withSameTexture, bool withSameHeight) { } //mxd
|
|
|
|
// This swaps triangles so that the plane faces the other way
|
|
protected static void SwapTriangleVertices(WorldVertex[] verts)
|
|
{
|
|
// Swap some vertices to flip all triangles
|
|
for(int i = 0; i < verts.Length; i += 3)
|
|
{
|
|
// Swap
|
|
WorldVertex v = verts[i];
|
|
verts[i] = verts[i + 1];
|
|
verts[i + 1] = v;
|
|
}
|
|
}
|
|
|
|
// This is called to update UV dragging
|
|
protected virtual void UpdateDragUV()
|
|
{
|
|
float u_ray = 1.0f;
|
|
|
|
// Calculate intersection position
|
|
this.Level.plane.GetIntersection(General.Map.VisualCamera.Position, General.Map.VisualCamera.Target, ref u_ray);
|
|
Vector3D intersect = General.Map.VisualCamera.Position + (General.Map.VisualCamera.Target - General.Map.VisualCamera.Position) * u_ray;
|
|
|
|
// Calculate offsets
|
|
Vector3D dragdelta = intersect - dragorigin;
|
|
float offsetx = dragdelta.x;
|
|
float offsety = dragdelta.y;
|
|
|
|
bool lockX = General.Interface.CtrlState && !General.Interface.ShiftState;
|
|
bool lockY = !General.Interface.CtrlState && General.Interface.ShiftState;
|
|
|
|
if(lockX || lockY)
|
|
{
|
|
float camAngle = Angle2D.RadToDeg(General.Map.VisualCamera.AngleXY);
|
|
|
|
if(camAngle > 315 || camAngle < 46)
|
|
{
|
|
if(lockX) offsetx = 0;
|
|
if(lockY) offsety = 0;
|
|
}
|
|
else if(camAngle > 225)
|
|
{
|
|
if(lockX) offsety = 0;
|
|
if(lockY) offsetx = 0;
|
|
}
|
|
else if(camAngle > 135)
|
|
{
|
|
if(lockX) offsetx = 0;
|
|
if(lockY) offsety = 0;
|
|
}
|
|
else
|
|
{
|
|
if(lockX) offsety = 0;
|
|
if(lockY) offsetx = 0;
|
|
}
|
|
}
|
|
|
|
//mxd. Modify offsets based on surface and camera angles
|
|
float angle;
|
|
|
|
if(GeometryType == VisualGeometryType.CEILING)
|
|
angle = Angle2D.DegToRad(level.sector.Fields.GetValue("rotationceiling", 0f));
|
|
else
|
|
angle = Angle2D.DegToRad(level.sector.Fields.GetValue("rotationfloor", 0f));
|
|
|
|
Vector2D v = new Vector2D(offsetx, offsety).GetRotated(angle);
|
|
|
|
offsetx = (int)Math.Round(v.x);
|
|
offsety = (int)Math.Round(v.y);
|
|
|
|
// Apply offsets
|
|
if(General.Interface.CtrlState && General.Interface.ShiftState)
|
|
{
|
|
//mxd. Clamp to grid size?
|
|
int newoffsetx = startoffsetx - (int)Math.Round(offsetx);
|
|
int newoffsety = startoffsety + (int)Math.Round(offsety);
|
|
int dx = prevoffsetx - newoffsetx;
|
|
int dy = prevoffsety - newoffsety;
|
|
|
|
if(Math.Abs(dx) >= General.Map.Grid.GridSize)
|
|
{
|
|
dx = General.Map.Grid.GridSize * Math.Sign(dx);
|
|
prevoffsetx = newoffsetx;
|
|
}
|
|
else
|
|
{
|
|
dx = 0;
|
|
}
|
|
|
|
if(Math.Abs(dy) >= General.Map.Grid.GridSize)
|
|
{
|
|
dy = General.Map.Grid.GridSize * Math.Sign(dy);
|
|
prevoffsety = newoffsety;
|
|
}
|
|
else
|
|
{
|
|
dy = 0;
|
|
}
|
|
|
|
if(dx != 0 || dy != 0) mode.ApplyFlatOffsetChange(dx, dy);
|
|
}
|
|
else
|
|
{
|
|
int newoffsetx = startoffsetx - (int)Math.Round(offsetx);
|
|
int newoffsety = startoffsety + (int)Math.Round(offsety);
|
|
mode.ApplyFlatOffsetChange(prevoffsetx - newoffsetx, prevoffsety - newoffsety);
|
|
prevoffsetx = newoffsetx;
|
|
prevoffsety = newoffsety;
|
|
}
|
|
|
|
mode.ShowTargetInfo();
|
|
}
|
|
|
|
//mxd
|
|
public override Sector GetControlSector()
|
|
{
|
|
return level.sector;
|
|
}
|
|
|
|
//mxd
|
|
protected void OnTextureChanged()
|
|
{
|
|
//mxd. Effects may need updating...
|
|
mode.RebuildElementData();
|
|
|
|
if(level.sector == this.Sector.Sector)
|
|
{
|
|
this.Setup();
|
|
|
|
//mxd. 3D floors may need updating...
|
|
foreach(Sidedef s in level.sector.Sidedefs)
|
|
{
|
|
if(s.Line.Action == 160 && s.Line.Front != null)
|
|
{
|
|
int sectortag = ((General.Map.UDMF || (s.Line.Args[1] & 8) != 0) ? s.Line.Args[0] : s.Line.Args[0] + (s.Line.Args[4] << 8));
|
|
foreach(Sector sector in General.Map.Map.Sectors)
|
|
{
|
|
if(sector.Tags.Contains(sectortag))
|
|
{
|
|
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(sector);
|
|
vs.UpdateSectorGeometry(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//mxd. As well as this sector's geometry
|
|
else if(mode.VisualSectorExists(level.sector))
|
|
{
|
|
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
|
|
vs.UpdateSectorGeometry(false);
|
|
}
|
|
}
|
|
|
|
//mxd
|
|
public virtual bool IsSelected()
|
|
{
|
|
return selected;
|
|
}
|
|
|
|
//mxd
|
|
protected void AlignTextureToClosestLine(bool alignx, bool aligny)
|
|
{
|
|
if(!(mode.HighlightedObject is BaseVisualSector)) return;
|
|
|
|
//do we need to align this? (and also grab texture scale while we are at it)
|
|
float scaleX, scaleY;
|
|
bool isFloor = (geometrytype == VisualGeometryType.FLOOR);
|
|
|
|
if(mode.HighlightedTarget is VisualFloor)
|
|
{
|
|
VisualFloor target = mode.HighlightedTarget as VisualFloor;
|
|
|
|
//check texture
|
|
if(target.Sector.Sector.FloorTexture != (isFloor ? Sector.Sector.FloorTexture : Sector.Sector.CeilTexture)) return;
|
|
|
|
scaleX = target.Sector.Sector.Fields.GetValue("xscalefloor", 1.0f);
|
|
scaleY = target.Sector.Sector.Fields.GetValue("yscalefloor", 1.0f);
|
|
}
|
|
else
|
|
{
|
|
VisualCeiling target = mode.HighlightedTarget as VisualCeiling;
|
|
|
|
//check texture
|
|
if(target.Sector.Sector.CeilTexture != (isFloor ? Sector.Sector.FloorTexture : Sector.Sector.CeilTexture)) return;
|
|
|
|
scaleX = target.Sector.Sector.Fields.GetValue("xscaleceiling", 1.0f);
|
|
scaleY = target.Sector.Sector.Fields.GetValue("yscaleceiling", 1.0f);
|
|
}
|
|
|
|
//find a linedef to align to
|
|
Vector2D hitpos = mode.GetHitPosition();
|
|
if(!hitpos.IsFinite()) return;
|
|
|
|
//align to line of highlighted sector, which is closest to hitpos
|
|
Sector highlightedSector = ((BaseVisualSector)mode.HighlightedObject).Sector;
|
|
List<Linedef> lines = new List<Linedef>();
|
|
foreach(Sidedef side in highlightedSector.Sidedefs) lines.Add(side.Line);
|
|
|
|
Linedef targetLine = MapSet.NearestLinedef(lines, hitpos);
|
|
if(targetLine == null) return;
|
|
|
|
bool isFront = targetLine.SideOfLine(hitpos) > 0;
|
|
Sector.Sector.Fields.BeforeFieldsChange();
|
|
|
|
//find an angle to rotate texture
|
|
float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(targetLine.Angle) + 90 : -Angle2D.RadToDeg(targetLine.Angle) - 90), 1);
|
|
if(!isFront) sourceAngle = General.ClampAngle(sourceAngle + 180);
|
|
|
|
//update angle
|
|
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "rotationfloor" : "rotationceiling"), sourceAngle, 0f);
|
|
|
|
//set scale
|
|
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xscalefloor" : "xscaleceiling"), scaleX, 1.0f);
|
|
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "yscalefloor" : "yscaleceiling"), scaleY, 1.0f);
|
|
|
|
//update offset
|
|
float distToStart = Vector2D.Distance(hitpos, targetLine.Start.Position);
|
|
float distToEnd = Vector2D.Distance(hitpos, targetLine.End.Position);
|
|
Vector2D offset = (distToStart < distToEnd ? targetLine.Start.Position : targetLine.End.Position).GetRotated(Angle2D.DegToRad(sourceAngle));
|
|
|
|
if(alignx)
|
|
{
|
|
if(Texture != null) offset.x %= Texture.Width / scaleX;
|
|
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xpanningfloor" : "xpanningceiling"), (float)Math.Round(-offset.x), 0f);
|
|
}
|
|
|
|
if(aligny)
|
|
{
|
|
if(Texture != null) offset.y %= Texture.Height / scaleY;
|
|
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "ypanningfloor" : "ypanningceiling"), (float)Math.Round(offset.y), 0f);
|
|
}
|
|
|
|
//update geometry
|
|
Sector.UpdateSectorGeometry(false);
|
|
}
|
|
|
|
//mxd
|
|
protected void AlignTextureToSlopeLine(Linedef slopeSource, float slopeAngle, bool isFront, bool alignx, bool aligny)
|
|
{
|
|
bool isFloor = (geometrytype == VisualGeometryType.FLOOR);
|
|
Sector.Sector.Fields.BeforeFieldsChange();
|
|
float sourceAngle = (float)Math.Round(General.ClampAngle(isFront ? -Angle2D.RadToDeg(slopeSource.Angle) + 90 : -Angle2D.RadToDeg(slopeSource.Angle) - 90), 1);
|
|
|
|
if(isFloor)
|
|
{
|
|
if((isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight) ||
|
|
(!isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight))
|
|
{
|
|
sourceAngle = General.ClampAngle(sourceAngle + 180);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight) ||
|
|
(!isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight))
|
|
{
|
|
sourceAngle = General.ClampAngle(sourceAngle + 180);
|
|
}
|
|
}
|
|
|
|
//update angle
|
|
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "rotationfloor" : "rotationceiling"), sourceAngle, 0f);
|
|
|
|
//update scaleY
|
|
string xScaleKey = (isFloor ? "xscalefloor" : "xscaleceiling");
|
|
string yScaleKey = (isFloor ? "yscalefloor" : "yscaleceiling");
|
|
|
|
float scaleX = Sector.Sector.Fields.GetValue(xScaleKey, 1.0f);
|
|
float scaleY;
|
|
|
|
//set scale
|
|
if(aligny)
|
|
{
|
|
scaleY = (float)Math.Round(scaleX * (1 / (float)Math.Cos(slopeAngle)), 2);
|
|
UniFields.SetFloat(Sector.Sector.Fields, yScaleKey, scaleY, 1.0f);
|
|
}
|
|
else
|
|
{
|
|
scaleY = Sector.Sector.Fields.GetValue(yScaleKey, 1.0f);
|
|
}
|
|
|
|
//update texture offsets
|
|
Vector2D offset;
|
|
if(isFloor)
|
|
{
|
|
if((isFront && slopeSource.Front.Sector.FloorHeight < slopeSource.Back.Sector.FloorHeight) ||
|
|
(!isFront && slopeSource.Front.Sector.FloorHeight > slopeSource.Back.Sector.FloorHeight))
|
|
{
|
|
offset = slopeSource.End.Position;
|
|
}
|
|
else
|
|
{
|
|
offset = slopeSource.Start.Position;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if((isFront && slopeSource.Front.Sector.CeilHeight > slopeSource.Back.Sector.CeilHeight) ||
|
|
(!isFront && slopeSource.Front.Sector.CeilHeight < slopeSource.Back.Sector.CeilHeight))
|
|
{
|
|
offset = slopeSource.End.Position;
|
|
}
|
|
else
|
|
{
|
|
offset = slopeSource.Start.Position;
|
|
}
|
|
}
|
|
|
|
offset = offset.GetRotated(Angle2D.DegToRad(sourceAngle));
|
|
|
|
if(alignx)
|
|
{
|
|
if(Texture != null) offset.x %= Texture.Width / scaleX;
|
|
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "xpanningfloor" : "xpanningceiling"), (float)Math.Round(-offset.x), 0f);
|
|
}
|
|
|
|
if(aligny)
|
|
{
|
|
if(Texture != null) offset.y %= Texture.Height / scaleY;
|
|
UniFields.SetFloat(Sector.Sector.Fields, (isFloor ? "ypanningfloor" : "ypanningceiling"), (float)Math.Round(offset.y), 0f);
|
|
}
|
|
|
|
//update geometry
|
|
Sector.UpdateSectorGeometry(false);
|
|
}
|
|
|
|
//mxd
|
|
protected void ClearFields(IEnumerable<string> keys, string undodescription, string resultdescription)
|
|
{
|
|
if(!General.Map.UDMF) return;
|
|
|
|
mode.CreateUndo(undodescription);
|
|
mode.SetActionResult(resultdescription);
|
|
Sector.Sector.Fields.BeforeFieldsChange();
|
|
|
|
foreach(string key in keys)
|
|
{
|
|
if(Sector.Sector.Fields.ContainsKey(key))
|
|
{
|
|
Sector.Sector.Fields.Remove(key);
|
|
Sector.Sector.UpdateNeeded = true;
|
|
}
|
|
}
|
|
|
|
if(Sector.Sector.UpdateNeeded) Sector.UpdateSectorGeometry(false);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Events
|
|
|
|
// Unused
|
|
public virtual void OnEditBegin() { }
|
|
public virtual void OnTextureFit(FitTextureOptions options) { } //mxd
|
|
public virtual void OnToggleUpperUnpegged() { }
|
|
public virtual void OnToggleLowerUnpegged() { }
|
|
public virtual void OnResetTextureOffset() { }
|
|
public virtual void OnResetLocalTextureOffset() { } //mxd
|
|
public virtual void OnCopyTextureOffsets() { }
|
|
public virtual void OnPasteTextureOffsets() { }
|
|
public virtual void OnInsert() { }
|
|
protected virtual void SetTexture(string texturename) { }
|
|
public virtual void ApplyUpperUnpegged(bool set) { }
|
|
public virtual void ApplyLowerUnpegged(bool set) { }
|
|
protected abstract void MoveTextureOffset(Point xy);
|
|
protected abstract Point GetTextureOffset();
|
|
|
|
// Setup this plane
|
|
public bool Setup() { return this.Setup(this.level, this.extrafloor); }
|
|
public virtual bool Setup(SectorLevel level, Effect3DFloor extrafloor)
|
|
{
|
|
this.level = level;
|
|
this.extrafloor = extrafloor;
|
|
return false;
|
|
}
|
|
|
|
// Begin select
|
|
public virtual void OnSelectBegin()
|
|
{
|
|
mode.LockTarget();
|
|
dragstartanglexy = General.Map.VisualCamera.AngleXY;
|
|
dragstartanglez = General.Map.VisualCamera.AngleZ;
|
|
dragorigin = pickintersect;
|
|
startoffsetx = GetTextureOffset().X;
|
|
startoffsety = GetTextureOffset().Y;
|
|
prevoffsetx = GetTextureOffset().X;
|
|
prevoffsety = GetTextureOffset().Y;
|
|
}
|
|
|
|
// Select or deselect
|
|
public virtual void OnSelectEnd()
|
|
{
|
|
mode.UnlockTarget();
|
|
|
|
// Was dragging?
|
|
if(uvdragging)
|
|
{
|
|
// Dragging stops now
|
|
uvdragging = false;
|
|
}
|
|
else
|
|
{
|
|
if(this.selected)
|
|
{
|
|
this.selected = false;
|
|
mode.RemoveSelectedObject(this);
|
|
}
|
|
else
|
|
{
|
|
this.selected = true;
|
|
mode.AddSelectedObject(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Moving the mouse
|
|
public virtual void OnMouseMove(MouseEventArgs e)
|
|
{
|
|
if(!General.Map.UDMF) return; //mxd. Cannot change texture offsets in other map formats...
|
|
|
|
// Dragging UV?
|
|
if(uvdragging)
|
|
{
|
|
UpdateDragUV();
|
|
}
|
|
else
|
|
{
|
|
// Select button pressed?
|
|
if(General.Actions.CheckActionActive(General.ThisAssembly, "visualselect"))
|
|
{
|
|
// Check if tolerance is exceeded to start UV dragging
|
|
float deltaxy = General.Map.VisualCamera.AngleXY - dragstartanglexy;
|
|
float deltaz = General.Map.VisualCamera.AngleZ - dragstartanglez;
|
|
if((Math.Abs(deltaxy) + Math.Abs(deltaz)) > DRAG_ANGLE_TOLERANCE)
|
|
{
|
|
mode.PreAction(UndoGroup.TextureOffsetChange);
|
|
mode.CreateUndo("Change texture offsets");
|
|
|
|
// Start drag now
|
|
uvdragging = true;
|
|
mode.Renderer.ShowSelection = false;
|
|
mode.Renderer.ShowHighlight = false;
|
|
UpdateDragUV();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Delete texture
|
|
public virtual void OnDelete()
|
|
{
|
|
// Remove texture
|
|
mode.CreateUndo("Delete texture");
|
|
mode.SetActionResult("Deleted a texture.");
|
|
SetTexture("-");
|
|
|
|
// Update
|
|
if(mode.VisualSectorExists(level.sector))
|
|
{
|
|
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
|
|
vs.UpdateSectorGeometry(true);
|
|
}
|
|
}
|
|
|
|
// Processing
|
|
public virtual void OnProcess(float deltatime)
|
|
{
|
|
// If the texture was not loaded, but is loaded now, then re-setup geometry
|
|
if(setuponloadedtexture != 0)
|
|
{
|
|
ImageData t = General.Map.Data.GetFlatImage(setuponloadedtexture);
|
|
if(t != null)
|
|
{
|
|
if(t.IsImageLoaded)
|
|
{
|
|
setuponloadedtexture = 0;
|
|
Setup();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flood-fill textures
|
|
public virtual void OnTextureFloodfill()
|
|
{
|
|
if(BuilderPlug.Me.CopiedFlat != null)
|
|
{
|
|
string oldtexture = GetTextureName();
|
|
long oldtexturelong = Lump.MakeLongName(General.Map.Data.GetFullFlatName(oldtexture)); //mxd
|
|
string newtexture = BuilderPlug.Me.CopiedFlat;
|
|
if(newtexture != oldtexture)
|
|
{
|
|
// Get the texture
|
|
ImageData newtextureimage = General.Map.Data.GetFlatImage(newtexture);
|
|
if(newtextureimage != null)
|
|
{
|
|
bool fillceilings = (this is VisualCeiling);
|
|
|
|
if(fillceilings)
|
|
{
|
|
mode.CreateUndo("Flood-fill ceilings with " + newtexture);
|
|
mode.SetActionResult("Flood-filled ceilings with " + newtexture + ".");
|
|
}
|
|
else
|
|
{
|
|
mode.CreateUndo("Flood-fill floors with " + newtexture);
|
|
mode.SetActionResult("Flood-filled floors with " + newtexture + ".");
|
|
}
|
|
|
|
mode.Renderer.SetCrosshairBusy(true);
|
|
General.Interface.RedrawDisplay();
|
|
|
|
if(mode.IsSingleSelection)
|
|
{
|
|
// Clear all marks, this will align everything it can
|
|
General.Map.Map.ClearMarkedSectors(false);
|
|
}
|
|
else
|
|
{
|
|
// Limit the alignment to selection only
|
|
General.Map.Map.ClearMarkedSectors(true);
|
|
List<Sector> sectors = mode.GetSelectedSectors();
|
|
foreach(Sector s in sectors) s.Marked = false;
|
|
}
|
|
|
|
// Do the fill
|
|
Tools.FloodfillFlats(this.Sector.Sector, fillceilings, oldtexturelong, newtexture, false);
|
|
|
|
// Get the changed sectors
|
|
List<Sector> changes = General.Map.Map.GetMarkedSectors(true);
|
|
foreach(Sector s in changes)
|
|
{
|
|
// Update the visual sector
|
|
if(mode.VisualSectorExists(s))
|
|
{
|
|
BaseVisualSector vs = (mode.GetVisualSector(s) as BaseVisualSector);
|
|
if(fillceilings)
|
|
vs.Ceiling.Setup();
|
|
else
|
|
vs.Floor.Setup();
|
|
}
|
|
}
|
|
|
|
General.Map.Data.UpdateUsedTextures();
|
|
mode.Renderer.SetCrosshairBusy(false);
|
|
mode.ShowTargetInfo();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//mxd. Auto-align texture offsets
|
|
public virtual void OnTextureAlign(bool alignx, bool aligny)
|
|
{
|
|
if(!General.Map.UDMF) return;
|
|
|
|
//create undo
|
|
string rest;
|
|
if(alignx && aligny) rest = "(X and Y)";
|
|
else if(alignx) rest = "(X)";
|
|
else rest = "(Y)";
|
|
|
|
mode.CreateUndo("Auto-align textures " + rest);
|
|
mode.SetActionResult("Auto-aligned textures " + rest + ".");
|
|
|
|
//get selection
|
|
List<VisualGeometry> selection = mode.GetSelectedSurfaces();
|
|
|
|
//align textures on slopes
|
|
foreach(VisualGeometry vg in selection)
|
|
{
|
|
if(vg.GeometryType == VisualGeometryType.FLOOR || vg.GeometryType == VisualGeometryType.CEILING)
|
|
{
|
|
if(vg.GeometryType == VisualGeometryType.FLOOR)
|
|
((VisualFloor)vg).AlignTexture(alignx, aligny);
|
|
else
|
|
((VisualCeiling)vg).AlignTexture(alignx, aligny);
|
|
|
|
vg.Sector.Sector.UpdateNeeded = true;
|
|
vg.Sector.Sector.UpdateCache();
|
|
}
|
|
}
|
|
|
|
// Map is changed
|
|
General.Map.Map.Update();
|
|
General.Map.IsChanged = true;
|
|
General.Interface.RefreshInfo();
|
|
}
|
|
|
|
// Copy properties
|
|
public virtual void OnCopyProperties()
|
|
{
|
|
BuilderPlug.Me.CopiedSectorProps = new SectorProperties(level.sector);
|
|
mode.SetActionResult("Copied sector properties.");
|
|
}
|
|
|
|
// Paste properties
|
|
public virtual void OnPasteProperties(bool usecopysettings)
|
|
{
|
|
if(BuilderPlug.Me.CopiedSectorProps != null)
|
|
{
|
|
mode.CreateUndo("Paste sector properties");
|
|
mode.SetActionResult("Pasted sector properties.");
|
|
|
|
//mxd. Glow effect may require SectorData update
|
|
bool oldfloortextureglows = (SectorProperties.CopySettings.FloorTexture && General.Map.Data.GlowingFlats.ContainsKey(level.sector.LongFloorTexture));
|
|
bool oldceiltextureglows = (SectorProperties.CopySettings.CeilingTexture && General.Map.Data.GlowingFlats.ContainsKey(level.sector.LongCeilTexture));
|
|
|
|
//mxd. Added "usecopysettings"
|
|
BuilderPlug.Me.CopiedSectorProps.Apply(level.sector, usecopysettings);
|
|
|
|
//mxd. Glow effect may require SectorData update
|
|
if(oldfloortextureglows || oldceiltextureglows
|
|
|| (SectorProperties.CopySettings.FloorTexture && General.Map.Data.GlowingFlats.ContainsKey(level.sector.LongFloorTexture))
|
|
|| (SectorProperties.CopySettings.CeilingTexture && General.Map.Data.GlowingFlats.ContainsKey(level.sector.LongCeilTexture)))
|
|
{
|
|
mode.RebuildElementData();
|
|
SectorData sd = mode.GetSectorData(level.sector);
|
|
sd.UpdateForced();
|
|
}
|
|
|
|
if(mode.VisualSectorExists(level.sector))
|
|
{
|
|
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
|
|
vs.UpdateSectorGeometry(true);
|
|
}
|
|
|
|
mode.ShowTargetInfo();
|
|
}
|
|
}
|
|
|
|
// Select texture
|
|
public virtual void OnSelectTexture()
|
|
{
|
|
if(General.Interface.IsActiveWindow)
|
|
{
|
|
string oldtexture = GetTextureName();
|
|
string newtexture = General.Interface.BrowseFlat(General.Interface, oldtexture);
|
|
if(newtexture != oldtexture)
|
|
{
|
|
mode.ApplySelectTexture(newtexture, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply Texture
|
|
public virtual void ApplyTexture(string texture)
|
|
{
|
|
mode.CreateUndo("Change flat '" + texture + "'");
|
|
SetTexture(texture);
|
|
OnTextureChanged(); //mxd
|
|
}
|
|
|
|
// Copy texture
|
|
public virtual void OnCopyTexture()
|
|
{
|
|
//mxd. When UseLongTextureNames is disabled, use texture name as stored in Sidedef, otherwise use full name.
|
|
string texturename = ((General.Map.Options.UseLongTextureNames && Texture != null && Texture.UsedInMap) ? Texture.FullName : GetTextureName());
|
|
BuilderPlug.Me.CopiedFlat = texturename;
|
|
if(General.Map.Config.MixTexturesFlats) BuilderPlug.Me.CopiedTexture = texturename;
|
|
mode.SetActionResult("Copied flat '" + texturename + "'.");
|
|
}
|
|
|
|
public virtual void OnPasteTexture() { }
|
|
|
|
// Return texture name
|
|
public virtual string GetTextureName() { return ""; }
|
|
|
|
// Edit button released
|
|
public virtual void OnEditEnd()
|
|
{
|
|
if(General.Interface.IsActiveWindow)
|
|
{
|
|
//mxd
|
|
List<Sector> sectors = mode.GetSelectedSectors();
|
|
updateList = new List<BaseVisualSector>();
|
|
|
|
foreach(Sector s in sectors)
|
|
{
|
|
if(mode.VisualSectorExists(s))
|
|
updateList.Add((BaseVisualSector)mode.GetVisualSector(s));
|
|
}
|
|
|
|
General.Interface.OnEditFormValuesChanged += Interface_OnEditFormValuesChanged; //mxd
|
|
mode.StartRealtimeInterfaceUpdate(SelectionType.Sectors); //mxd
|
|
DialogResult result = General.Interface.ShowEditSectors(sectors);
|
|
mode.StopRealtimeInterfaceUpdate(SelectionType.Sectors); //mxd
|
|
General.Interface.OnEditFormValuesChanged -= Interface_OnEditFormValuesChanged; //mxd
|
|
|
|
updateList.Clear(); //mxd
|
|
updateList = null; //mxd
|
|
|
|
if(result == DialogResult.OK) mode.RebuildElementData(); //mxd
|
|
}
|
|
}
|
|
|
|
//mxd
|
|
private void Interface_OnEditFormValuesChanged(object sender, EventArgs e)
|
|
{
|
|
foreach(BaseVisualSector vs in updateList) vs.UpdateSectorGeometry(true);
|
|
}
|
|
|
|
// Sector height change
|
|
public virtual void OnChangeTargetHeight(int amount)
|
|
{
|
|
changed = true;
|
|
|
|
ChangeHeight(amount);
|
|
|
|
// Rebuild sector
|
|
BaseVisualSector vs;
|
|
if(mode.VisualSectorExists(level.sector))
|
|
{
|
|
vs = (BaseVisualSector)mode.GetVisualSector(level.sector);
|
|
}
|
|
else
|
|
{
|
|
//mxd. Need this to apply changes to 3d-floor even if control sector doesn't exist
|
|
//as BaseVisualSector
|
|
vs = mode.CreateBaseVisualSector(level.sector);
|
|
}
|
|
|
|
if(vs != null) vs.UpdateSectorGeometry(true);
|
|
}
|
|
|
|
// Sector brightness change
|
|
public virtual void OnChangeTargetBrightness(bool up)
|
|
{
|
|
mode.CreateUndo("Change sector brightness", UndoGroup.SectorBrightnessChange, Sector.Sector.FixedIndex);
|
|
|
|
if(up)
|
|
Sector.Sector.Brightness = General.Map.Config.BrightnessLevels.GetNextHigher(Sector.Sector.Brightness);
|
|
else
|
|
Sector.Sector.Brightness = General.Map.Config.BrightnessLevels.GetNextLower(Sector.Sector.Brightness);
|
|
|
|
mode.SetActionResult("Changed sector brightness to " + Sector.Sector.Brightness + ".");
|
|
|
|
Sector.Sector.UpdateCache();
|
|
|
|
// Rebuild sector
|
|
Sector.UpdateSectorGeometry(false);
|
|
}
|
|
|
|
// Texture offset change
|
|
public virtual void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection)
|
|
{
|
|
if(horizontal == 0 && vertical == 0) return; //mxd
|
|
|
|
//mxd
|
|
if (!General.Map.UDMF)
|
|
{
|
|
General.Interface.DisplayStatus(StatusType.Warning, "Floor/ceiling texture offsets cannot be changed in this map format!");
|
|
return;
|
|
}
|
|
|
|
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
|
|
undoticket = mode.CreateUndo("Change texture offsets");
|
|
|
|
//mxd
|
|
if(doSurfaceAngleCorrection)
|
|
{
|
|
Point p = new Point(horizontal, vertical);
|
|
float angle = Angle2D.RadToDeg(General.Map.VisualCamera.AngleXY);
|
|
if(GeometryType == VisualGeometryType.CEILING)
|
|
angle += level.sector.Fields.GetValue("rotationceiling", 0f);
|
|
else
|
|
angle += level.sector.Fields.GetValue("rotationfloor", 0f);
|
|
|
|
angle = General.ClampAngle(angle);
|
|
|
|
if(angle > 315 || angle < 46)
|
|
{
|
|
//already correct
|
|
}
|
|
else if(angle > 225)
|
|
{
|
|
vertical = p.X;
|
|
horizontal = -p.Y;
|
|
}
|
|
else if(angle > 135)
|
|
{
|
|
horizontal = -p.X;
|
|
vertical = -p.Y;
|
|
}
|
|
else
|
|
{
|
|
vertical = -p.X;
|
|
horizontal = p.Y;
|
|
}
|
|
}
|
|
|
|
// Apply offsets
|
|
MoveTextureOffset(new Point(-horizontal, -vertical));
|
|
|
|
// Update sector geometry
|
|
Sector s = GetControlSector();
|
|
if(s.Index != Sector.Sector.Index)
|
|
{
|
|
s.UpdateNeeded = true;
|
|
s.UpdateCache();
|
|
mode.GetSectorData(s).Update();
|
|
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(s);
|
|
vs.UpdateSectorGeometry(false);
|
|
vs.Rebuild();
|
|
}
|
|
|
|
Sector.Sector.UpdateNeeded = true;
|
|
Sector.Sector.UpdateCache();
|
|
Sector.UpdateSectorGeometry(false);
|
|
Sector.Rebuild();
|
|
}
|
|
|
|
public virtual void OnChangeTextureRotation(float angle)
|
|
{
|
|
if(!General.Map.UDMF) return;
|
|
|
|
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
|
|
undoticket = mode.CreateUndo("Change texture rotation");
|
|
|
|
string key = (GeometryType == VisualGeometryType.FLOOR ? "rotationfloor" : "rotationceiling");
|
|
mode.SetActionResult( (GeometryType == VisualGeometryType.FLOOR ? "Floor" : "Ceiling") + " rotation changed to " + angle);
|
|
|
|
//set value
|
|
Sector s = GetControlSector();
|
|
s.Fields.BeforeFieldsChange();
|
|
UniFields.SetFloat(s.Fields, key, angle, 0.0f);
|
|
|
|
if(s.Index != Sector.Sector.Index)
|
|
{
|
|
s.UpdateNeeded = true;
|
|
s.UpdateCache();
|
|
mode.GetSectorData(s).Update();
|
|
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(s);
|
|
vs.UpdateSectorGeometry(false);
|
|
}
|
|
|
|
Sector.Sector.UpdateNeeded = true;
|
|
Sector.Sector.UpdateCache();
|
|
Sector.UpdateSectorGeometry(false);
|
|
}
|
|
|
|
//mxd
|
|
public virtual void OnChangeScale(int incrementX, int incrementY)
|
|
{
|
|
if(!General.Map.UDMF || !Texture.IsImageLoaded) return;
|
|
|
|
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
|
|
undoticket = mode.CreateUndo("Change texture scale");
|
|
|
|
ChangeTextureScale(incrementX, incrementY);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|