UltimateZoneBuilder/Source/Plugins/BuilderModes/VisualModes/BaseVisualThing.cs
MaxED 13068b1137 Added, Visual mode: added support for ROLLCENTER sprite rendering flag, updated ROLLSPRITE implementation.
Fixed, DECORATE support: the editor was unable to determine actor sprite when the actor itself had no sprites defined and the actor it inherited from was only defined in the game configuration.
Fixed, Drag geometry modes: linedefs without both sides were removed after dragging them when "Replace with Dragged Geometry" mode was active.
Updated ZDoom_DECORATE.cfg.
2016-07-13 21:53:48 +00:00

880 lines
26 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.Globalization;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.Rendering;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.VisualModes;
using CodeImp.DoomBuilder.Data;
#endregion
namespace CodeImp.DoomBuilder.BuilderModes
{
internal class BaseVisualThing : VisualThing, IVisualEventReceiver
{
#region ================== Constants
#endregion
#region ================== Variables
private readonly BaseVisualMode mode;
private bool isloaded;
private bool nointeraction; //mxd
private ImageData[] sprites;
private float cageradius2;
private Vector2D pos2d;
private Vector3D boxp1;
private Vector3D boxp2;
private static List<BaseVisualThing> updateList; //mxd
// Undo/redo
private int undoticket;
// If this is set to true, the thing will be rebuilt after the action is performed.
private bool changed;
#endregion
#region ================== Properties
public bool Changed { get { return changed; } set { changed |= value; } }
#endregion
#region ================== Constructor / Setup
// Constructor
public BaseVisualThing(BaseVisualMode mode, Thing t) : base(t)
{
this.mode = mode;
// Find thing information
info = General.Map.Data.GetThingInfo(Thing.Type);
//mxd. When true, the thing can be moved below floor/above ceiling
nointeraction = (info.Actor != null && info.Actor.GetFlagValue("nointeraction", false));
//mxd. Find sprite textures
sprites = new ImageData[info.SpriteFrame.Length];
for(int i = 0; i < info.SpriteFrame.Length; i++)
{
sprites[i] = General.Map.Data.GetSpriteImage(info.SpriteFrame[i].Sprite);
if(sprites[i] != null) sprites[i].AddReference();
}
//mxd
if(mode.UseSelectionFromClassicMode && t.Selected)
{
this.selected = true;
mode.AddSelectedObject(this);
}
// We have no destructor
GC.SuppressFinalize(this);
}
// This builds the thing geometry. Returns false when nothing was created.
public bool Setup()
{
// Find the sector in which the thing resides
Thing.DetermineSector(mode.BlockMap);
//mxd. If the thing is inside a sector, apply DECORATE/UDMF alpha/renderstyle overrides
byte alpha = 255;
if(Thing.Sector != null)
{
string renderstyle = info.RenderStyle;
alpha = info.AlphaByte;
if(General.Map.UDMF)
{
if(Thing.IsFlagSet("translucent"))
{
renderstyle = "translucent";
alpha = 64;
}
else if(Thing.IsFlagSet("invisible"))
{
renderstyle = "none";
alpha = 0;
}
else if(Thing.Fields.ContainsKey("renderstyle"))
{
renderstyle = Thing.Fields.GetValue("renderstyle", renderstyle);
}
if((renderstyle == "add" || renderstyle == "translucent" || renderstyle == "subtract" || renderstyle == "stencil")
&& Thing.Fields.ContainsKey("alpha"))
{
alpha = (byte)(General.Clamp(Thing.Fields.GetValue("alpha", info.Alpha), 0f, 1f) * 255);
}
}
else if(General.Map.HEXEN)
{
if(Thing.IsFlagSet("2048"))
{
renderstyle = "translucent";
alpha = 64;
}
else if(Thing.IsFlagSet("4096"))
{
renderstyle = "none";
alpha = 0;
}
}
// Set appropriate RenderPass
switch(renderstyle)
{
case "translucent":
case "subtract":
case "stencil":
RenderPass = RenderPass.Alpha;
break;
case "add":
RenderPass = RenderPass.Additive;
break;
case "none":
RenderPass = RenderPass.Mask;
alpha = 0;
break;
// Many render styles are not supported yet...
default:
RenderPass = RenderPass.Mask;
alpha = 255;
break;
}
}
// Don't bother when alpha is unchanged, unless Additive RenderStyle is used
if(RenderPass != RenderPass.Additive && alpha == 255) RenderPass = RenderPass.Mask;
int sectorcolor = new PixelColor(alpha, 255, 255, 255).ToInt();
fogfactor = 0f; //mxd
//mxd. Check thing size
float thingradius = Thing.Size; // Thing.Size has ThingRadius arg override applied
thingheight = Thing.Height; // Thing.Height has ThingHeight arg override applied
if(thingradius < 0.1f || thingheight < 0.1f)
{
thingradius = FIXED_RADIUS;
thingheight = FIXED_RADIUS;
sizeless = true;
}
else
{
sizeless = false;
}
Plane floor = new Plane(); //mxd
Plane ceiling = new Plane(); //mxd
if(Thing.Sector != null)
{
SectorData sd = mode.GetSectorData(Thing.Sector);
floor = sd.Floor.plane; //mxd
ceiling = sd.Ceiling.plane; //mxd
if(!info.Bright)
{
Vector3D thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position));
SectorLevel level = sd.GetLevelAboveOrAt(thingpos);
//mxd. Let's use point on floor plane instead of Thing.Sector.FloorHeight;
if(nointeraction && level == null && sd.LightLevels.Count > 0) level = sd.LightLevels[sd.LightLevels.Count - 1];
//mxd. Use the light level of the highest surface when a thing is above highest sector level.
if(level != null)
{
// TECH: In GZDoom, ceiling glow doesn't affect thing brightness
// Use sector brightness for color shading
int brightness = level.brightnessbelow;
// Level is glowing
if(level.affectedbyglow && level.type == SectorLevelType.Floor)
{
// Extrafloor glow doesn't affect thing brightness
if(level.sector == Thing.Sector)
{
float planez = level.plane.GetZ(thingpos);
// Get glow brightness
int glowbrightness = sd.FloorGlow.Brightness / 2;
SectorLevel nexthigher = sd.GetLevelAbove(new Vector3D(thingpos, planez));
// Interpolate thing brightness between glow and regular ones
if(nexthigher != null)
{
float higherz = nexthigher.plane.GetZ(thingpos);
float delta = General.Clamp(1.0f - (thingpos.z - planez) / (higherz - planez), 0f, 1f);
brightness = (int)((glowbrightness + level.sector.Brightness / 2) * delta + nexthigher.sector.Brightness * (1.0f - delta));
}
}
}
// Level below this one is glowing. Only possible for floor glow(?)
else if(level.type == SectorLevelType.Glow)
{
// Interpolate thing brightness between glow and regular ones
if(sd.Floor != null && sd.FloorGlow != null)
{
// Get glow brightness
float glowz = level.plane.GetZ(thingpos);
float floorz = floor.GetZ(thingpos);
float delta = General.Clamp((thingpos.z - floorz) / (glowz - floorz), 0f, 1f);
brightness = (int)((sd.FloorGlow.Brightness / 2 + sd.Floor.sector.Brightness / 2) * (1.0f - delta) + sd.Floor.sector.Brightness * delta);
}
}
PixelColor areabrightness = PixelColor.FromInt(mode.CalculateBrightness(brightness));
PixelColor areacolor = PixelColor.Modulate(level.colorbelow, areabrightness);
sectorcolor = areacolor.WithAlpha(alpha).ToInt();
//mxd. Calculate fogfactor
fogfactor = VisualGeometry.CalculateFogFactor(level.sector.FogMode, brightness);
}
}
//TECH: even Bright Thing frames are affected by custom fade...
else
{
Vector3D thingpos = new Vector3D(Thing.Position.x, Thing.Position.y, Thing.Position.z + sd.Floor.plane.GetZ(Thing.Position));
SectorLevel level = sd.GetLevelAboveOrAt(thingpos);
if(level != null && level.sector.FogMode > SectorFogMode.CLASSIC)
{
//mxd. Calculate fogfactor
fogfactor = VisualGeometry.CalculateFogFactor(level.sector.FogMode, level.brightnessbelow);
}
}
}
//mxd. Create verts for all sprite angles
WorldVertex[][] allverts = new WorldVertex[info.SpriteFrame.Length][];
Vector2D[] alloffsets = new Vector2D[info.SpriteFrame.Length];
base.textures = new ImageData[info.SpriteFrame.Length];
isloaded = true;
for(int i = 0; i < sprites.Length; i++)
{
Vector2D offsets = new Vector2D();
// Check if the texture is loaded
ImageData sprite = sprites[i];
sprite.LoadImage();
if(sprite.IsImageLoaded)
{
base.textures[i] = sprite;
// Determine sprite size and offset
float radius = sprite.ScaledWidth * 0.5f;
float height = sprite.ScaledHeight;
ISpriteImage spriteimg = sprite as ISpriteImage;
if(spriteimg != null)
{
offsets.x = radius - spriteimg.OffsetX;
offsets.y = spriteimg.OffsetY - height;
}
// Scale by thing type/actor scale
// We do this after the offset x/y determination above, because that is entirely in sprite pixels space
radius *= info.SpriteScale.Width;
height *= info.SpriteScale.Height;
offsets.x *= info.SpriteScale.Width;
offsets.y *= info.SpriteScale.Height;
// Make vertices
WorldVertex[] verts = new WorldVertex[6];
//mxd. Sprite mirroring
float ul = (info.SpriteFrame[i].Mirror ? 1f : 0f);
float ur = (info.SpriteFrame[i].Mirror ? 0f : 1f);
if(sizeless) //mxd
{
float hh = height / 2;
verts[0] = new WorldVertex(-radius + offsets.x, 0.0f, offsets.y - hh, sectorcolor, ul, 1.0f);
verts[1] = new WorldVertex(-radius + offsets.x, 0.0f, hh + offsets.y, sectorcolor, ul, 0.0f);
verts[2] = new WorldVertex(+radius + offsets.x, 0.0f, hh + offsets.y, sectorcolor, ur, 0.0f);
verts[3] = verts[0];
verts[4] = verts[2];
verts[5] = new WorldVertex(+radius + offsets.x, 0.0f, offsets.y - hh, sectorcolor, ur, 1.0f);
}
else
{
verts[0] = new WorldVertex(-radius + offsets.x, 0.0f, offsets.y, sectorcolor, ul, 1.0f);
verts[1] = new WorldVertex(-radius + offsets.x, 0.0f, height + offsets.y, sectorcolor, ul, 0.0f);
verts[2] = new WorldVertex(+radius + offsets.x, 0.0f, height + offsets.y, sectorcolor, ur, 0.0f);
verts[3] = verts[0];
verts[4] = verts[2];
verts[5] = new WorldVertex(+radius + offsets.x, 0.0f, offsets.y, sectorcolor, ur, 1.0f);
}
allverts[i] = verts;
}
else
{
isloaded = false;
base.textures[i] = General.Map.Data.Hourglass3D;
// Determine sprite size
float radius = Math.Min(thingradius, thingheight / 2f);
float height = Math.Min(thingradius * 2f, thingheight);
//mxd. Determine sprite offsets
offsets.x = radius;
offsets.y = height / 2;
// Make vertices
WorldVertex[] verts = new WorldVertex[6];
verts[0] = new WorldVertex(-radius, 0.0f, 0.0f, sectorcolor, 0.0f, 1.0f);
verts[1] = new WorldVertex(-radius, 0.0f, height, sectorcolor, 0.0f, 0.0f);
verts[2] = new WorldVertex(+radius, 0.0f, height, sectorcolor, 1.0f, 0.0f);
verts[3] = verts[0];
verts[4] = verts[2];
verts[5] = new WorldVertex(+radius, 0.0f, 0.0f, sectorcolor, 1.0f, 1.0f);
allverts[i] = verts;
}
//mxd. Store offsets
alloffsets[i] = offsets;
}
//mxd
SetVertices(allverts, alloffsets/*, floor, ceiling*/);
// Determine position
Vector3D pos = Thing.Position;
if(Thing.Type == 9501)
{
if(Thing.Sector != null) //mxd
{
// This is a special thing that needs special positioning
SectorData sd = mode.GetSectorData(Thing.Sector);
pos.z = sd.Ceiling.sector.CeilHeight + Thing.Position.z;
}
}
else if(Thing.Type == 9500)
{
if(Thing.Sector != null) //mxd
{
// This is a special thing that needs special positioning
SectorData sd = mode.GetSectorData(Thing.Sector);
pos.z = sd.Floor.sector.FloorHeight + Thing.Position.z;
}
}
else if(info.AbsoluteZ)
{
// Absolute Z position
pos.z = Thing.Position.z;
}
else if(info.Hangs)
{
// Hang from ceiling
if(Thing.Sector != null)
{
SectorData sd = mode.GetSectorData(Thing.Sector);
float maxz = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height;
pos.z = maxz;
if(Thing.Position.z > 0 || nointeraction) pos.z -= Thing.Position.z;
// Check if below floor
if(!nointeraction)
{
float minz = sd.Floor.plane.GetZ(Thing.Position);
if(pos.z < minz) pos.z = Math.Min(minz, maxz);
}
}
}
else
{
// Stand on floor
if(Thing.Sector != null)
{
SectorData sd = mode.GetSectorData(Thing.Sector);
float minz = sd.Floor.plane.GetZ(Thing.Position);
pos.z = minz;
if(Thing.Position.z > 0 || nointeraction) pos.z += Thing.Position.z;
// Check if above ceiling
if(!nointeraction)
{
float maxz = sd.Ceiling.plane.GetZ(Thing.Position) - info.Height;
if(pos.z > maxz) pos.z = Math.Max(minz, maxz);
}
}
}
// Apply settings
SetPosition(pos);
SetCageColor(Thing.Color);
// Keep info for object picking
cageradius2 = thingradius * Angle2D.SQRT2;
cageradius2 = cageradius2 * cageradius2;
pos2d = pos;
if(sizeless) //mxd
{
boxp1 = new Vector3D(pos.x - thingradius, pos.y - thingradius, pos.z - thingradius/2);
boxp2 = new Vector3D(pos.x + thingradius, pos.y + thingradius, pos.z + thingradius/2);
}
else
{
boxp1 = new Vector3D(pos.x - thingradius, pos.y - thingradius, pos.z);
boxp2 = new Vector3D(pos.x + thingradius, pos.y + thingradius, pos.z + thingheight);
}
// Done
changed = false;
return true;
}
// Disposing
public override void Dispose()
{
if(!IsDisposed)
{
if(sprites != null) //mxd
{
foreach(ImageData sprite in sprites) sprite.RemoveReference();
sprites = null;
}
base.Dispose();
}
}
#endregion
#region ================== Methods
// This forces to rebuild the whole thing
public void Rebuild()
{
// Find thing information
info = General.Map.Data.GetThingInfo(Thing.Type);
//mxd. When true, the thing can be moved below floor/above ceiling
nointeraction = (info.Actor != null && info.Actor.GetFlagValue("nointeraction", false));
//mxd. Find sprite textures
sprites = new ImageData[info.SpriteFrame.Length];
for(int i = 0; i < info.SpriteFrame.Length; i++)
{
sprites[i] = General.Map.Data.GetSpriteImage(info.SpriteFrame[i].Sprite);
if(sprites[i] != null) sprites[i].AddReference();
}
// Setup visual thing
Setup();
}
// This updates the thing when needed
public override void Update()
{
if(!isloaded)
{
//mxd. Rebuild sprite geometry when all sprites are loaded
isloaded = true;
foreach(ImageData sprite in sprites)
{
if(!sprite.IsImageLoaded)
{
isloaded = false;
break;
}
}
if(isloaded) Setup();
}
// Let the base update
base.Update();
}
// This performs a fast test in object picking
public override bool PickFastReject(Vector3D from, Vector3D to, Vector3D dir)
{
//mxd. Don't highlight when thing sprite is not rendered and thing cages are disabled
if(!General.Map.Renderer3D.DrawThingCages && info.DistanceCheckSq < int.MaxValue
&& (Thing.Position - General.Map.VisualCamera.Position).GetLengthSq() > info.DistanceCheckSq)
return false;
float distance2 = Line2D.GetDistanceToLineSq(from, to, pos2d, false);
return (distance2 <= cageradius2);
}
// This performs an accurate test for object picking
public override bool PickAccurate(Vector3D from, Vector3D to, Vector3D dir, ref float u_ray)
{
Vector3D delta = to - from;
float tfar = float.MaxValue;
float tnear = float.MinValue;
// Ray-Box intersection code
// See http://www.masm32.com/board/index.php?topic=9941.0
// Check X slab
if(delta.x == 0.0f)
{
if(from.x > boxp2.x || from.x < boxp1.x)
{
// Ray is parallel to the planes & outside slab
return false;
}
}
else
{
float tmp = 1.0f / delta.x;
float t1 = (boxp1.x - from.x) * tmp;
float t2 = (boxp2.x - from.x) * tmp;
if(t1 > t2) General.Swap(ref t1, ref t2);
if(t1 > tnear) tnear = t1;
if(t2 < tfar) tfar = t2;
if(tnear > tfar || tfar < 0.0f)
{
// Ray missed box or box is behind ray
return false;
}
}
// Check Y slab
if(delta.y == 0.0f)
{
if(from.y > boxp2.y || from.y < boxp1.y)
{
// Ray is parallel to the planes & outside slab
return false;
}
}
else
{
float tmp = 1.0f / delta.y;
float t1 = (boxp1.y - from.y) * tmp;
float t2 = (boxp2.y - from.y) * tmp;
if(t1 > t2) General.Swap(ref t1, ref t2);
if(t1 > tnear) tnear = t1;
if(t2 < tfar) tfar = t2;
if(tnear > tfar || tfar < 0.0f)
{
// Ray missed box or box is behind ray
return false;
}
}
// Check Z slab
if(delta.z == 0.0f)
{
if(from.z > boxp2.z || from.z < boxp1.z)
{
// Ray is parallel to the planes & outside slab
return false;
}
}
else
{
float tmp = 1.0f / delta.z;
float t1 = (boxp1.z - from.z) * tmp;
float t2 = (boxp2.z - from.z) * tmp;
if(t1 > t2) General.Swap(ref t1, ref t2);
if(t1 > tnear) tnear = t1;
if(t2 < tfar) tfar = t2;
if(tnear > tfar || tfar < 0.0f)
{
// Ray missed box or box is behind ray
return false;
}
}
// Set interpolation point
u_ray = (tnear > 0.0f) ? tnear : tfar;
return true;
}
//mxd
public bool IsSelected()
{
return selected;
}
#endregion
#region ================== Events
// Unused
public void OnSelectBegin() { }
public void OnEditBegin() { }
public void OnMouseMove(MouseEventArgs e) { }
public void OnChangeTargetBrightness(bool up) { }
public void OnChangeTextureOffset(int horizontal, int vertical, bool doSurfaceAngleCorrection) { }
public void OnSelectTexture() { }
public void OnCopyTexture() { }
public void OnPasteTexture() { }
public void OnCopyTextureOffsets() { }
public void OnPasteTextureOffsets() { }
public void OnTextureAlign(bool alignx, bool aligny) { }
public void OnToggleUpperUnpegged() { }
public void OnToggleLowerUnpegged() { }
public void OnProcess(long deltatime) { }
public void OnTextureFloodfill() { }
public void OnInsert() { }
public void OnTextureFit(FitTextureOptions options) { } //mxd
public void ApplyTexture(string texture) { }
public void ApplyUpperUnpegged(bool set) { }
public void ApplyLowerUnpegged(bool set) { }
public void SelectNeighbours(bool select, bool withSameTexture, bool withSameHeight) { } //mxd
// Return texture name
public string GetTextureName() { return ""; }
// Select or deselect
public void OnSelectEnd()
{
if(this.selected)
{
this.selected = false;
mode.RemoveSelectedObject(this);
}
else
{
this.selected = true;
mode.AddSelectedObject(this);
}
}
//mxd. Delete thing
public void OnDelete()
{
mode.CreateUndo("Delete thing");
mode.SetActionResult("Deleted a thing.");
this.Thing.Fields.BeforeFieldsChange();
this.Thing.Dispose();
this.Dispose();
General.Map.IsChanged = true;
General.Map.ThingsFilter.Update();
}
// Copy properties
public void OnCopyProperties()
{
BuilderPlug.Me.CopiedThingProps = new ThingProperties(Thing);
mode.SetActionResult("Copied thing properties.");
}
// Paste properties
public void OnPasteProperties(bool usecopysettings)
{
if(BuilderPlug.Me.CopiedThingProps != null)
{
mode.CreateUndo("Paste thing properties");
mode.SetActionResult("Pasted thing properties.");
BuilderPlug.Me.CopiedThingProps.Apply(Thing, usecopysettings); //mxd. Added "usecopysettings"
Thing.UpdateConfiguration();
this.Rebuild();
mode.ShowTargetInfo();
}
}
// Edit button released
public void OnEditEnd()
{
if(General.Interface.IsActiveWindow)
{
List<Thing> things = mode.GetSelectedThings();
//mxd
updateList = new List<BaseVisualThing>();
foreach(Thing t in things)
{
VisualThing vt = mode.GetVisualThing(t);
if(vt != null) updateList.Add((BaseVisualThing)vt);
}
General.Interface.OnEditFormValuesChanged += Interface_OnEditFormValuesChanged;
mode.StartRealtimeInterfaceUpdate(SelectionType.Things);
General.Interface.ShowEditThings(things);
mode.StopRealtimeInterfaceUpdate(SelectionType.Things);
General.Interface.OnEditFormValuesChanged -= Interface_OnEditFormValuesChanged;
updateList.Clear();
updateList = null;
}
}
//mxd
private void Interface_OnEditFormValuesChanged(object sender, EventArgs e)
{
foreach(BaseVisualThing vt in updateList) vt.Changed = true;
}
//mxd
public void OnResetTextureOffset()
{
mode.CreateUndo("Reset thing scale");
mode.SetActionResult("Thing scale reset.");
Thing.SetScale(1.0f, 1.0f);
// Update what must be updated
this.Changed = true;
}
//mxd
public void OnResetLocalTextureOffset()
{
mode.CreateUndo("Reset thing scale, pitch and roll");
mode.SetActionResult("Thing scale, pitch and roll reset.");
Thing.SetScale(1.0f, 1.0f);
Thing.SetPitch(0);
Thing.SetRoll(0);
// Update what must be updated
this.Changed = true;
}
// Raise/lower thing
public void OnChangeTargetHeight(int amount)
{
if(General.Map.FormatInterface.HasThingHeight)
{
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
undoticket = mode.CreateUndo("Change thing height");
Thing.Move(Thing.Position + new Vector3D(0.0f, 0.0f, (info.Hangs ? -amount : amount)));
mode.SetActionResult("Changed thing height to " + Thing.Position.z + ".");
// Update what must be updated
ThingData td = mode.GetThingData(this.Thing);
foreach(KeyValuePair<Sector, bool> s in td.UpdateAlso)
{
if(mode.VisualSectorExists(s.Key))
{
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(s.Key);
vs.UpdateSectorGeometry(s.Value);
}
}
this.Changed = true;
}
}
//mxd
public void OnChangeScale(int incrementX, int incrementY)
{
if(!General.Map.UDMF || sprites == null || !sprites[0].IsImageLoaded) return;
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
undoticket = mode.CreateUndo("Change thing scale");
float scaleX = Thing.ScaleX;
float scaleY = Thing.ScaleY;
ImageData sprite = sprites[0];
if(incrementX != 0)
{
float pix = (int)Math.Round(sprite.Width * scaleX) + incrementX;
float newscaleX = (float)Math.Round(pix / sprite.Width, 3);
scaleX = (newscaleX == 0 ? scaleX * -1 : newscaleX);
}
if(incrementY != 0)
{
float pix = (int)Math.Round(sprite.Height * scaleY) + incrementY;
float newscaleY = (float)Math.Round(pix / sprite.Height, 3);
scaleY = (newscaleY == 0 ? scaleY * -1 : newscaleY);
}
Thing.SetScale(scaleX, scaleY);
mode.SetActionResult("Changed thing scale to " + scaleX.ToString("F03", CultureInfo.InvariantCulture) + ", " + scaleY.ToString("F03", CultureInfo.InvariantCulture) + " (" + (int)Math.Round(sprite.Width * scaleX) + " x " + (int)Math.Round(sprite.Height * scaleY) + ").");
// Update what must be updated
this.Changed = true;
}
//mxd
public void OnMove(Vector3D newposition)
{
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
undoticket = mode.CreateUndo("Move thing");
Thing.Move(newposition);
mode.SetActionResult("Changed thing position to " + Thing.Position + ".");
// Update what must be updated
ThingData td = mode.GetThingData(this.Thing);
foreach(KeyValuePair<Sector, bool> s in td.UpdateAlso)
{
if(mode.VisualSectorExists(s.Key))
{
BaseVisualSector vs = (BaseVisualSector)mode.GetVisualSector(s.Key);
vs.UpdateSectorGeometry(s.Value);
}
}
this.Changed = true;
}
//mxd
public void SetAngle(int newangle)
{
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
undoticket = mode.CreateUndo("Change thing angle");
Thing.Rotate(newangle);
mode.SetActionResult("Changed thing angle to " + Thing.AngleDoom + ".");
this.Changed = true;
}
//mxd
public void SetPitch(int newpitch)
{
if(!General.Map.UDMF) return;
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
undoticket = mode.CreateUndo("Change thing pitch");
Thing.SetPitch(newpitch);
mode.SetActionResult("Changed thing pitch to " + Thing.Pitch + ".");
this.Changed = true;
}
//mxd
public void SetRoll(int newroll)
{
if(!General.Map.UDMF) return;
if((General.Map.UndoRedo.NextUndo == null) || (General.Map.UndoRedo.NextUndo.TicketID != undoticket))
undoticket = mode.CreateUndo("Change thing roll");
Thing.SetRoll(newroll);
mode.SetActionResult("Changed thing roll to " + Thing.Roll + ".");
this.Changed = true;
}
#endregion
}
}