mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-12-01 16:12:33 +00:00
59c4a75b11
Added "doomthingrotationangles" Game Configuration property. When enabled, editor actions related to changing thing angle will snap the resulting angle to 45 degree increments. This property is set to true for vanilla game configurations. Fixed a crash when changing game configuration from one without Thing actions support to one with them while in Things mode. Fixed, cosmetic, DB2 bug: current editing mode button was deselected after reloading resources. Updated documentation ("Game Configuration - Basic Settings" page).
590 lines
16 KiB
C#
590 lines
16 KiB
C#
#region Namespaces
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Windows.Forms;
|
|
using CodeImp.DoomBuilder.Map;
|
|
using CodeImp.DoomBuilder.VisualModes;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using CodeImp.DoomBuilder.Windows;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.BuilderEffects
|
|
{
|
|
public partial class JitterThingsForm : DelayedForm
|
|
{
|
|
#region Variables
|
|
|
|
private readonly string editingModeName;
|
|
private readonly List<Thing> selection;
|
|
private readonly List<VisualThing> visualSelection;
|
|
private readonly List<ThingData> thingData;
|
|
private readonly int MaxSafeDistance;
|
|
private readonly int MaxSafeHeightDistance;
|
|
|
|
private static bool relativePitch;
|
|
private static bool relativeRoll;
|
|
private static bool allowNegativePitch;
|
|
private static bool allowNegativeRoll;
|
|
private static bool relativeScale;
|
|
private static bool allowNegativeScaleX;
|
|
private static bool allowNegativeScaleY;
|
|
private static bool uniformScale;
|
|
|
|
private struct ThingData
|
|
{
|
|
public Vector3D Position;
|
|
public int Angle;
|
|
public int Pitch;
|
|
public int Roll;
|
|
public float ScaleX;
|
|
public float ScaleY;
|
|
public int SectorHeight;
|
|
public int ZOffset;
|
|
public int SafeDistance;
|
|
public int OffsetAngle; //position jitter angle, not Thing angle!
|
|
public float JitterRotation;
|
|
public float JitterPitch;
|
|
public float JitterRoll;
|
|
public float JitterScaleX;
|
|
public float JitterScaleY;
|
|
public float JitterHeight;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Constructor
|
|
|
|
public JitterThingsForm(string editingModeName)
|
|
{
|
|
this.editingModeName = editingModeName;
|
|
this.HelpRequested += JitterThingsForm_HelpRequested;
|
|
|
|
InitializeComponent();
|
|
|
|
//have thing height?
|
|
heightJitterAmmount.Enabled = General.Map.FormatInterface.HasThingHeight;
|
|
bUpdateHeight.Enabled = General.Map.FormatInterface.HasThingHeight;
|
|
|
|
//disable pitch/roll/scale?
|
|
if(!General.Map.UDMF)
|
|
{
|
|
pitchAmmount.Enabled = false;
|
|
rollAmmount.Enabled = false;
|
|
bUpdatePitch.Enabled = false;
|
|
bUpdateRoll.Enabled = false;
|
|
scalegroup.Enabled = false;
|
|
cbRelativePitch.Enabled = false;
|
|
cbRelativeRoll.Enabled = false;
|
|
cbNegativePitch.Enabled = false;
|
|
cbNegativeRoll.Enabled = false;
|
|
}
|
|
|
|
//get selection
|
|
selection = new List<Thing>();
|
|
|
|
if(editingModeName == "BaseVisualMode")
|
|
{
|
|
visualSelection = ((VisualMode)General.Editing.Mode).GetSelectedVisualThings(false);
|
|
foreach(VisualThing t in visualSelection) selection.Add(t.Thing);
|
|
}
|
|
else
|
|
{
|
|
ICollection<Thing> list = General.Map.Map.GetSelectedThings(true);
|
|
foreach(Thing t in list) selection.Add(t);
|
|
}
|
|
|
|
//update window header
|
|
this.Text = "Randomize " + selection.Count + (selection.Count > 1 ? " things" : " thing");
|
|
|
|
//store intial properties
|
|
thingData = new List<ThingData>();
|
|
|
|
foreach(Thing t in selection)
|
|
{
|
|
ThingData d = new ThingData();
|
|
|
|
Thing closest = MapSet.NearestThing(General.Map.Map.Things, t);
|
|
|
|
if(closest != null)
|
|
{
|
|
d.SafeDistance = (int)Math.Round(Vector2D.Distance(t.Position, closest.Position));
|
|
}
|
|
else
|
|
{
|
|
d.SafeDistance = 512;
|
|
}
|
|
|
|
if(d.SafeDistance > 0) d.SafeDistance /= 2;
|
|
if(MaxSafeDistance < d.SafeDistance) MaxSafeDistance = d.SafeDistance;
|
|
d.Position = t.Position;
|
|
d.Angle = t.AngleDoom;
|
|
d.Pitch = t.Pitch;
|
|
d.Roll = t.Roll;
|
|
d.ScaleX = t.ScaleX;
|
|
d.ScaleY = t.ScaleY;
|
|
|
|
if(General.Map.FormatInterface.HasThingHeight)
|
|
{
|
|
if(t.Sector == null) t.DetermineSector();
|
|
if(t.Sector == null) continue;
|
|
|
|
d.SectorHeight = Math.Max(0, t.Sector.CeilHeight - (int)t.Height - t.Sector.FloorHeight);
|
|
if(MaxSafeHeightDistance < d.SectorHeight) MaxSafeHeightDistance = d.SectorHeight;
|
|
d.ZOffset = (int)t.Position.z;
|
|
}
|
|
|
|
thingData.Add(d);
|
|
}
|
|
|
|
positionJitterAmmount.Maximum = MaxSafeDistance;
|
|
heightJitterAmmount.Maximum = MaxSafeHeightDistance;
|
|
|
|
//create undo
|
|
General.Map.UndoRedo.ClearAllRedos();
|
|
General.Map.UndoRedo.CreateUndo("Randomize " + selection.Count + (selection.Count > 1 ? " things" : " thing"));
|
|
|
|
//update controls
|
|
UpdateOffsetAngles();
|
|
UpdateHeights();
|
|
UpdateRotationAngles();
|
|
UpdatePitchAngles();
|
|
UpdateRollAngles();
|
|
UpdateScaleX();
|
|
UpdateScaleY();
|
|
|
|
//apply settings
|
|
cbRelativeScale.Checked = relativeScale;
|
|
cbUniformScale.Checked = uniformScale;
|
|
cbNegativeScaleX.Checked = allowNegativeScaleX;
|
|
cbNegativeScaleY.Checked = allowNegativeScaleY;
|
|
cbRelativePitch.Checked = relativePitch;
|
|
cbRelativeRoll.Checked = relativeRoll;
|
|
cbNegativePitch.Checked = allowNegativePitch;
|
|
cbNegativeRoll.Checked = allowNegativeRoll;
|
|
|
|
|
|
//add event listeners
|
|
cbRelativeScale.CheckedChanged += cbRelativeScale_CheckedChanged;
|
|
cbUniformScale.CheckedChanged += cbUniformScale_CheckedChanged;
|
|
cbNegativeScaleX.CheckedChanged += cbNegativeScaleX_CheckedChanged;
|
|
cbNegativeScaleY.CheckedChanged += cbNegativeScaleY_CheckedChanged;
|
|
cbRelativePitch.CheckedChanged += cbRelativePitch_CheckedChanged;
|
|
cbRelativeRoll.CheckedChanged += cbRelativeRoll_CheckedChanged;
|
|
cbNegativePitch.CheckedChanged += cbNegativePitch_CheckedChanged;
|
|
cbNegativeRoll.CheckedChanged += cbNegativeRoll_CheckedChanged;
|
|
|
|
//disable controls if necessary
|
|
if(uniformScale) cbUniformScale_CheckedChanged(cbUniformScale, EventArgs.Empty);
|
|
|
|
//tricky way to actually store undo information...
|
|
foreach(Thing t in selection) t.Move(t.Position);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Apply logic
|
|
|
|
private void ApplyTranslation(int ammount)
|
|
{
|
|
for(int i = 0; i < selection.Count; i++)
|
|
{
|
|
int curAmmount = ammount > thingData[i].SafeDistance ? thingData[i].SafeDistance : ammount;
|
|
selection[i].Move(new Vector2D(thingData[i].Position.x + (int)(Math.Sin(thingData[i].OffsetAngle) * curAmmount), thingData[i].Position.y + (int)(Math.Cos(thingData[i].OffsetAngle) * curAmmount)));
|
|
selection[i].DetermineSector();
|
|
}
|
|
|
|
UpdateGeometry();
|
|
}
|
|
|
|
private void ApplyRotation(int ammount)
|
|
{
|
|
for(int i = 0; i < selection.Count; i++)
|
|
{
|
|
int newangle = (int)Math.Round(thingData[i].Angle + ammount * thingData[i].JitterRotation);
|
|
if(General.Map.Config.DoomThingRotationAngles) newangle = newangle / 45 * 45;
|
|
selection[i].Rotate(newangle % 360);
|
|
}
|
|
|
|
// Update view
|
|
if(editingModeName == "ThingsMode") General.Interface.RedrawDisplay();
|
|
}
|
|
|
|
private void ApplyPitch(int ammount)
|
|
{
|
|
for(int i = 0; i < selection.Count; i++)
|
|
{
|
|
int p;
|
|
if(cbRelativePitch.Checked)
|
|
{
|
|
p = (int)((thingData[i].Pitch + ammount * thingData[i].JitterPitch) % 360);
|
|
}
|
|
else
|
|
{
|
|
p = (int)((ammount * thingData[i].JitterPitch) % 360);
|
|
}
|
|
|
|
selection[i].SetPitch(p);
|
|
}
|
|
|
|
//update view
|
|
if(editingModeName == "ThingsMode") General.Interface.RedrawDisplay();
|
|
}
|
|
|
|
private void ApplyRoll(int ammount)
|
|
{
|
|
for(int i = 0; i < selection.Count; i++)
|
|
{
|
|
int r;
|
|
if(cbRelativeRoll.Checked)
|
|
{
|
|
r = (int)((thingData[i].Roll + ammount * thingData[i].JitterRoll) % 360);
|
|
}
|
|
else
|
|
{
|
|
r = (int)((ammount * thingData[i].JitterRoll) % 360);
|
|
}
|
|
|
|
selection[i].SetRoll(r);
|
|
}
|
|
|
|
//update view
|
|
if(editingModeName == "ThingsMode") General.Interface.RedrawDisplay();
|
|
}
|
|
|
|
private void ApplyHeight(int ammount)
|
|
{
|
|
for(int i = 0; i < selection.Count; i++)
|
|
{
|
|
int curAmmount = Math.Min(thingData[i].SectorHeight, Math.Max(0, thingData[i].ZOffset + ammount));
|
|
selection[i].Move(selection[i].Position.x, selection[i].Position.y, curAmmount * thingData[i].JitterHeight);
|
|
}
|
|
|
|
UpdateGeometry();
|
|
}
|
|
|
|
private void ApplyScale()
|
|
{
|
|
ApplyScale((float)minScaleX.Value, (float)maxScaleX.Value, (float)minScaleY.Value, (float)maxScaleY.Value);
|
|
|
|
//update view
|
|
if(editingModeName == "ThingsMode") General.Interface.RedrawDisplay();
|
|
}
|
|
|
|
private void ApplyScale(float minX, float maxX, float minY, float maxY)
|
|
{
|
|
if(cbUniformScale.Checked)
|
|
{
|
|
minY = minX;
|
|
maxY = maxX;
|
|
}
|
|
|
|
if(minX > maxX) General.Swap(ref minX, ref maxX);
|
|
if(minY > maxY) General.Swap(ref minY, ref maxY);
|
|
|
|
float diffX = maxX - minX;
|
|
float diffY = maxY - minY;
|
|
|
|
for(int i = 0; i < selection.Count; i++)
|
|
{
|
|
float jitterX = thingData[i].JitterScaleX;
|
|
float jitterY = (cbUniformScale.Checked ? jitterX : thingData[i].JitterScaleY);
|
|
float sx, sy;
|
|
|
|
if(cbRelativeScale.Checked)
|
|
{
|
|
sx = thingData[i].ScaleX + minX + diffX * jitterX;
|
|
sy = thingData[i].ScaleY + minY + diffY * jitterY;
|
|
}
|
|
else
|
|
{
|
|
sx = minX + diffX * jitterX;
|
|
sy = minY + diffY * jitterY;
|
|
}
|
|
|
|
selection[i].SetScale(sx, sy);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Update logic
|
|
|
|
private void UpdateGeometry()
|
|
{
|
|
// Update what must be updated
|
|
if(editingModeName == "BaseVisualMode")
|
|
{
|
|
VisualMode vm = ((VisualMode)General.Editing.Mode);
|
|
|
|
for(int i = 0; i < selection.Count; i++)
|
|
{
|
|
visualSelection[i].SetPosition(new Vector3D(selection[i].Position.x, selection[i].Position.y, selection[i].Sector.FloorHeight + selection[i].Position.z));
|
|
visualSelection[i].Update();
|
|
|
|
if(vm.VisualSectorExists(visualSelection[i].Thing.Sector))
|
|
vm.GetVisualSector(visualSelection[i].Thing.Sector).UpdateSectorGeometry(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//update view
|
|
General.Interface.RedrawDisplay();
|
|
}
|
|
}
|
|
|
|
private void UpdateOffsetAngles()
|
|
{
|
|
for(int i = 0; i < thingData.Count; i++)
|
|
{
|
|
ThingData td = thingData[i];
|
|
td.OffsetAngle = General.Random(0, 359);
|
|
thingData[i] = td;
|
|
}
|
|
}
|
|
|
|
private void UpdateHeights()
|
|
{
|
|
for(int i = 0; i < thingData.Count; i++)
|
|
{
|
|
ThingData td = thingData[i];
|
|
td.JitterHeight = (General.Random(0, 100) / 100f);
|
|
thingData[i] = td;
|
|
}
|
|
}
|
|
|
|
private void UpdateRotationAngles()
|
|
{
|
|
for(int i = 0; i < thingData.Count; i++)
|
|
{
|
|
ThingData td = thingData[i];
|
|
td.JitterRotation = (General.Random(-100, 100) / 100f);
|
|
thingData[i] = td;
|
|
}
|
|
}
|
|
|
|
private void UpdatePitchAngles()
|
|
{
|
|
int min = (cbNegativePitch.Checked ? -100 : 0);
|
|
for(int i = 0; i < thingData.Count; i++)
|
|
{
|
|
ThingData td = thingData[i];
|
|
td.JitterPitch = (General.Random(min, 100) / 100f);
|
|
thingData[i] = td;
|
|
}
|
|
}
|
|
|
|
private void UpdateRollAngles()
|
|
{
|
|
int min = (cbNegativeRoll.Checked ? -100 : 0);
|
|
for(int i = 0; i < thingData.Count; i++)
|
|
{
|
|
ThingData td = thingData[i];
|
|
td.JitterRoll = (General.Random(min, 100) / 100f);
|
|
thingData[i] = td;
|
|
}
|
|
}
|
|
|
|
private void UpdateScaleX()
|
|
{
|
|
int min = (cbNegativeScaleX.Checked ? -100 : 0);
|
|
for(int i = 0; i < thingData.Count; i++)
|
|
{
|
|
ThingData td = thingData[i];
|
|
td.JitterScaleX = (General.Random(min, 100) / 100f);
|
|
thingData[i] = td;
|
|
}
|
|
}
|
|
|
|
private void UpdateScaleY()
|
|
{
|
|
int min = (cbNegativeScaleY.Checked ? -100 : 0);
|
|
for(int i = 0; i < thingData.Count; i++)
|
|
{
|
|
ThingData td = thingData[i];
|
|
td.JitterScaleY = (General.Random(min, 100) / 100f);
|
|
thingData[i] = td;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
private void bApply_Click(object sender, EventArgs e)
|
|
{
|
|
// Store settings
|
|
relativePitch = cbRelativePitch.Checked;
|
|
relativeRoll = cbRelativeRoll.Checked;
|
|
relativeScale = cbRelativeScale.Checked;
|
|
allowNegativeScaleX = cbNegativeScaleX.Checked;
|
|
allowNegativeScaleY = cbNegativeScaleY.Checked;
|
|
uniformScale = cbUniformScale.Checked;
|
|
allowNegativePitch = cbNegativePitch.Checked;
|
|
allowNegativeRoll = cbNegativeRoll.Checked;
|
|
|
|
// Update
|
|
foreach(Thing t in selection) t.DetermineSector();
|
|
|
|
// Clear selection
|
|
General.Actions.InvokeAction("builder_clearselection");
|
|
|
|
this.DialogResult = DialogResult.OK;
|
|
Close();
|
|
}
|
|
|
|
private void bCancel_Click(object sender, EventArgs e)
|
|
{
|
|
this.DialogResult = DialogResult.Cancel;
|
|
Close();
|
|
}
|
|
|
|
private void JitterThingsForm_FormClosing(object sender, FormClosingEventArgs e)
|
|
{
|
|
if(this.DialogResult == DialogResult.Cancel)
|
|
General.Map.UndoRedo.WithdrawUndo(); //undo changes
|
|
}
|
|
|
|
private void positionJitterAmmount_OnValueChanged(object sender, EventArgs e)
|
|
{
|
|
ApplyTranslation(positionJitterAmmount.Value);
|
|
}
|
|
|
|
private void rotationJitterAmmount_OnValueChanged(object sender, EventArgs e)
|
|
{
|
|
ApplyRotation(rotationJitterAmmount.Value);
|
|
}
|
|
|
|
private void heightJitterAmmount_OnValueChanging(object sender, EventArgs e)
|
|
{
|
|
ApplyHeight(heightJitterAmmount.Value);
|
|
}
|
|
|
|
private void pitchAmmount_OnValueChanging(object sender, EventArgs e)
|
|
{
|
|
ApplyPitch(pitchAmmount.Value);
|
|
}
|
|
|
|
private void rollAmmount_OnValueChanging(object sender, EventArgs e)
|
|
{
|
|
ApplyRoll(rollAmmount.Value);
|
|
}
|
|
|
|
private void minScaleX_ValueChanged(object sender, EventArgs e)
|
|
{
|
|
ApplyScale();
|
|
}
|
|
|
|
private void minScaleY_ValueChanged(object sender, EventArgs e)
|
|
{
|
|
ApplyScale();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Buttons & checkboxes events
|
|
|
|
private void bUpdateTranslation_Click(object sender, EventArgs e)
|
|
{
|
|
UpdateOffsetAngles();
|
|
ApplyTranslation(positionJitterAmmount.Value);
|
|
}
|
|
|
|
private void bUpdateHeight_Click(object sender, EventArgs e)
|
|
{
|
|
UpdateHeights();
|
|
ApplyHeight(heightJitterAmmount.Value);
|
|
}
|
|
|
|
private void bUpdateAngle_Click(object sender, EventArgs e)
|
|
{
|
|
UpdateRotationAngles();
|
|
ApplyRotation(rotationJitterAmmount.Value);
|
|
}
|
|
|
|
private void bUpdatePitch_Click(object sender, EventArgs e)
|
|
{
|
|
UpdatePitchAngles();
|
|
ApplyPitch(pitchAmmount.Value);
|
|
}
|
|
|
|
private void bUpdateRoll_Click(object sender, EventArgs e)
|
|
{
|
|
UpdateRollAngles();
|
|
ApplyRoll(rollAmmount.Value);
|
|
}
|
|
|
|
private void bUpdateScaleX_Click(object sender, EventArgs e)
|
|
{
|
|
UpdateScaleX();
|
|
ApplyScale();
|
|
}
|
|
|
|
private void bUpdateScaleY_Click(object sender, EventArgs e)
|
|
{
|
|
UpdateScaleY();
|
|
ApplyScale();
|
|
}
|
|
|
|
private void cbRelativePitch_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
UpdatePitchAngles();
|
|
ApplyPitch(pitchAmmount.Value);
|
|
}
|
|
|
|
private void cbRelativeRoll_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateRollAngles();
|
|
ApplyRoll(rollAmmount.Value);
|
|
}
|
|
|
|
private void cbNegativePitch_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
UpdatePitchAngles();
|
|
ApplyPitch(pitchAmmount.Value);
|
|
}
|
|
|
|
private void cbNegativeRoll_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateRollAngles();
|
|
ApplyRoll(rollAmmount.Value);
|
|
}
|
|
|
|
private void cbRelativeScale_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
ApplyScale();
|
|
}
|
|
|
|
private void cbUniformScale_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
bUpdateScaleY.Enabled = !cbUniformScale.Checked;
|
|
minScaleY.Enabled = !cbUniformScale.Checked;
|
|
maxScaleY.Enabled = !cbUniformScale.Checked;
|
|
minScaleYLabel.Enabled = !cbUniformScale.Checked;
|
|
maxScaleYLabel.Enabled = !cbUniformScale.Checked;
|
|
ApplyScale();
|
|
}
|
|
|
|
private void cbNegativeScaleX_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateScaleX();
|
|
ApplyScale();
|
|
}
|
|
|
|
private void cbNegativeScaleY_CheckedChanged(object sender, EventArgs e)
|
|
{
|
|
UpdateScaleY();
|
|
ApplyScale();
|
|
}
|
|
|
|
#endregion
|
|
|
|
//HALP!
|
|
private void JitterThingsForm_HelpRequested(object sender, HelpEventArgs hlpevent)
|
|
{
|
|
General.ShowHelp("gzdb/features/all_modes/jitter.html");
|
|
hlpevent.Handled = true;
|
|
}
|
|
}
|
|
}
|