mirror of
https://git.do.srb2.org/STJr/UltimateZoneBuilder.git
synced 2024-11-25 13:21:28 +00:00
774 lines
22 KiB
C#
Executable file
774 lines
22 KiB
C#
Executable file
|
|
#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 CodeImp.DoomBuilder.Config;
|
|
using CodeImp.DoomBuilder.Geometry;
|
|
using CodeImp.DoomBuilder.GZBuilder.Data;
|
|
using CodeImp.DoomBuilder.IO;
|
|
using CodeImp.DoomBuilder.Rendering;
|
|
using CodeImp.DoomBuilder.Types;
|
|
using CodeImp.DoomBuilder.VisualModes;
|
|
using CodeImp.DoomBuilder.GZBuilder;
|
|
|
|
#endregion
|
|
|
|
namespace CodeImp.DoomBuilder.Map
|
|
{
|
|
public sealed class Thing : SelectableElement, ITaggedMapElement
|
|
{
|
|
#region ================== Constants
|
|
|
|
public const int NUM_ARGS = 10;
|
|
public const int NUM_STRING_ARGS = 2;
|
|
public static readonly HashSet<ThingRenderMode> AlignableRenderModes = new HashSet<ThingRenderMode>
|
|
{
|
|
ThingRenderMode.FLATSPRITE, ThingRenderMode.WALLSPRITE, ThingRenderMode.MODEL
|
|
};
|
|
|
|
#endregion
|
|
|
|
#region ================== Variables
|
|
|
|
// Map
|
|
private MapSet map;
|
|
|
|
// Sector
|
|
private Sector sector;
|
|
|
|
// List items
|
|
private LinkedListNode<Thing> selecteditem;
|
|
|
|
// Properties
|
|
private int type;
|
|
private GZGeneral.LightData dynamiclighttype;
|
|
private Vector3D pos;
|
|
private int angledoom; // Angle as entered / stored in file
|
|
private double anglerad; // Angle in radians
|
|
private Dictionary<string, bool> flags;
|
|
private int tag;
|
|
private int action;
|
|
private int[] args;
|
|
private double scaleX; //mxd
|
|
private double scaleY; //mxd
|
|
private SizeF spritescale; //mxd
|
|
private int pitch; //mxd. Used in model rendering
|
|
private int roll; //mxd. Used in model rendering
|
|
private double pitchrad; //mxd
|
|
private double rollrad; //mxd
|
|
private bool highlighted; //mxd
|
|
|
|
//mxd. GZDoom rendering properties
|
|
private ThingRenderMode rendermode;
|
|
private bool rollsprite; //mxd
|
|
|
|
// Configuration
|
|
private float size;
|
|
private float rendersize;
|
|
private float height; //mxd
|
|
private PixelColor color;
|
|
private bool fixedsize;
|
|
private bool directional; //mxd. If true, we need to render an arrow
|
|
|
|
// biwa. This should only ever be used for temporary player starts for the "test from current position" action
|
|
private bool recordundo;
|
|
|
|
// Rendering
|
|
private int lastProcessed;
|
|
|
|
#endregion
|
|
|
|
#region ================== Properties
|
|
|
|
public MapSet Map { get { return map; } }
|
|
public int Type { get { return type; } set { BeforePropsChange(); type = value; } } //mxd
|
|
public GZGeneral.LightData DynamicLightType { get { return dynamiclighttype; } internal set { BeforePropsChange(); dynamiclighttype = value; } }
|
|
public Vector3D Position { get { return pos; } }
|
|
public double ScaleX { get { return scaleX; } } //mxd. This is UDMF property, not actual scale!
|
|
public double ScaleY { get { return scaleY; } } //mxd. This is UDMF property, not actual scale!
|
|
public int Pitch { get { return pitch; } } //mxd
|
|
public double PitchRad { get { return pitchrad; } }
|
|
public int Roll { get { return roll; } } //mxd
|
|
public double RollRad { get { return rollrad; } }
|
|
public SizeF ActorScale { get { return spritescale; } } //mxd. Actor scale set in DECORATE
|
|
public double Angle { get { return anglerad; } }
|
|
public int AngleDoom { get { return angledoom; } }
|
|
internal Dictionary<string, bool> Flags { get { return flags; } }
|
|
public int Action { get { return action; } set { BeforePropsChange(); action = value; } }
|
|
public int[] Args { get { return args; } }
|
|
public float Size { get { return size * GetObjectScale(); } }
|
|
public float RenderSize { get { return rendersize; } }
|
|
public float Height { get { return height * GetObjectScale(); } } //mxd
|
|
public PixelColor Color { get { return color; } }
|
|
public bool FixedSize { get { return fixedsize; } }
|
|
public int Tag { get { return tag; } set { BeforePropsChange(); tag = value; if((tag < General.Map.FormatInterface.MinTag) || (tag > General.Map.FormatInterface.MaxTag)) throw new ArgumentOutOfRangeException("Tag", "Invalid tag number"); } }
|
|
public Sector Sector { get { return sector; } }
|
|
public ThingRenderMode RenderMode { get { return rendermode; } } //mxd
|
|
public bool IsDirectional { get { return directional; } } //mxd
|
|
public bool Highlighted { get { return highlighted; } set { highlighted = value; } } //mxd
|
|
internal int LastProcessed { get { return lastProcessed; } set { lastProcessed = value; } }
|
|
public bool Flip { get { return General.Map.UDMF && IsFlagSet("flip"); } }
|
|
public bool AbsoluteZ { get { return General.Map.UDMF && IsFlagSet("absolutez"); } }
|
|
public bool IsFlipped
|
|
{
|
|
get
|
|
{
|
|
ThingTypeInfo ti = General.Map.Data.GetThingInfo(Type);
|
|
return ti.Hangs ^ Flip;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Constructor / Disposer
|
|
|
|
// Constructor
|
|
internal Thing(MapSet map, int listindex, bool recordundo = true)
|
|
{
|
|
// Initialize
|
|
this.elementtype = MapElementType.THING; //mxd
|
|
this.map = map;
|
|
this.listindex = listindex;
|
|
this.flags = new Dictionary<string, bool>(StringComparer.Ordinal);
|
|
this.args = new int[NUM_ARGS];
|
|
this.scaleX = 1.0f;
|
|
this.scaleY = 1.0f;
|
|
this.spritescale = new SizeF(1.0f, 1.0f);
|
|
this.recordundo = recordundo;
|
|
|
|
if(map == General.Map.Map && recordundo)
|
|
General.Map.UndoRedo.RecAddThing(this);
|
|
|
|
// We have no destructor
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
// Disposer
|
|
public override void Dispose()
|
|
{
|
|
// Not already disposed?
|
|
if(!isdisposed)
|
|
{
|
|
if(map == General.Map.Map && recordundo)
|
|
General.Map.UndoRedo.RecRemThing(this);
|
|
|
|
// Remove from main list
|
|
map.RemoveThing(listindex);
|
|
|
|
// Clean up
|
|
map = null;
|
|
sector = null;
|
|
|
|
// Dispose base
|
|
base.Dispose();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Management
|
|
|
|
// Call this before changing properties
|
|
protected override void BeforePropsChange()
|
|
{
|
|
if(map == General.Map.Map)
|
|
General.Map.UndoRedo.RecPrpThing(this);
|
|
}
|
|
|
|
// Serialize / deserialize
|
|
new internal void ReadWrite(IReadWriteStream s)
|
|
{
|
|
if(!s.IsWriting) BeforePropsChange();
|
|
|
|
base.ReadWrite(s);
|
|
|
|
if(s.IsWriting)
|
|
{
|
|
s.wInt(flags.Count);
|
|
|
|
foreach(KeyValuePair<string, bool> f in flags)
|
|
{
|
|
s.wString(f.Key);
|
|
s.wBool(f.Value);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int c; s.rInt(out c);
|
|
|
|
flags = new Dictionary<string, bool>(c, StringComparer.Ordinal);
|
|
for(int i = 0; i < c; i++)
|
|
{
|
|
string t; s.rString(out t);
|
|
bool b; s.rBool(out b);
|
|
flags.Add(t, b);
|
|
}
|
|
}
|
|
|
|
s.rwInt(ref type);
|
|
s.rwVector3D(ref pos);
|
|
s.rwInt(ref angledoom);
|
|
s.rwInt(ref pitch); //mxd
|
|
s.rwInt(ref roll); //mxd
|
|
s.rwDouble(ref scaleX); //mxd
|
|
s.rwDouble(ref scaleY); //mxd
|
|
s.rwInt(ref tag);
|
|
s.rwInt(ref action);
|
|
for(int i = 0; i < args.Length; i++) s.rwInt(ref args[i]);
|
|
|
|
if(!s.IsWriting)
|
|
{
|
|
anglerad = Angle2D.DoomToReal(angledoom);
|
|
UpdateCache(); //mxd
|
|
}
|
|
}
|
|
|
|
// This copies all properties to another thing
|
|
public void CopyPropertiesTo(Thing t)
|
|
{
|
|
t.BeforePropsChange();
|
|
|
|
// Copy properties
|
|
t.type = type;
|
|
t.dynamiclighttype = dynamiclighttype;
|
|
t.anglerad = anglerad;
|
|
t.angledoom = angledoom;
|
|
t.roll = roll; //mxd
|
|
t.pitch = pitch; //mxd
|
|
t.rollrad = rollrad; //mxd
|
|
t.pitchrad = pitchrad; //mxd
|
|
t.scaleX = scaleX; //mxd
|
|
t.scaleY = scaleY; //mxd
|
|
t.spritescale = spritescale; //mxd
|
|
t.pos = pos;
|
|
t.flags = new Dictionary<string,bool>(flags);
|
|
t.tag = tag;
|
|
t.action = action;
|
|
t.args = (int[])args.Clone();
|
|
t.size = size;
|
|
t.rendersize = rendersize;
|
|
t.height = height; //mxd
|
|
t.color = color;
|
|
t.directional = directional;
|
|
t.fixedsize = fixedsize;
|
|
t.rendermode = rendermode; //mxd
|
|
t.rollsprite = rollsprite; //mxd
|
|
|
|
base.CopyPropertiesTo(t);
|
|
}
|
|
|
|
// This determines which sector the thing is in and links it
|
|
public void DetermineSector()
|
|
{
|
|
//mxd
|
|
sector = map.GetSectorByCoordinates(pos);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines what sector a thing is in, given a blockmap
|
|
/// </summary>
|
|
/// <param name="blockmap">The blockmap to use</param>
|
|
public void DetermineSector(BlockMap<BlockEntry> blockmap)
|
|
{
|
|
BlockEntry be = blockmap.GetBlockAt(pos);
|
|
List<Sector> sectors = new List<Sector>(1);
|
|
|
|
foreach (Sector s in be.Sectors)
|
|
if (s.Intersect(pos))
|
|
sectors.Add(s);
|
|
|
|
if(sectors.Count == 0)
|
|
{
|
|
sector = null;
|
|
}
|
|
else if (sectors.Count == 1)
|
|
{
|
|
sector = sectors[0];
|
|
}
|
|
else
|
|
{
|
|
// Having multiple intersections indicates that there are self-referencing sectors in this spot.
|
|
// In this case we have to check which side of the nearest linedef pos is on, and then use that sector
|
|
HashSet<Linedef> linedefs = new HashSet<Linedef>(sectors[0].Sidedefs.Count * sectors.Count);
|
|
|
|
foreach (Sector s in sectors)
|
|
foreach (Sidedef sd in s.Sidedefs)
|
|
linedefs.Add(sd.Line);
|
|
|
|
Linedef nearest = MapSet.NearestLinedef(linedefs, pos);
|
|
double d = nearest.SideOfLine(pos);
|
|
|
|
if (d <= 0.0 && nearest.Front != null)
|
|
sector = nearest.Front.Sector;
|
|
else if (nearest.Back != null)
|
|
sector = nearest.Back.Sector;
|
|
else
|
|
sector = null;
|
|
}
|
|
}
|
|
|
|
// This determines which sector the thing is in and links it
|
|
public void DetermineSector(VisualBlockMap blockmap)
|
|
{
|
|
sector = blockmap.GetSectorAt(pos);
|
|
}
|
|
|
|
// This translates the flags into UDMF fields
|
|
internal void TranslateToUDMF()
|
|
{
|
|
// First make a single integer with all flags
|
|
int bits = 0;
|
|
int flagbit;
|
|
foreach(KeyValuePair<string, bool> f in flags)
|
|
if(int.TryParse(f.Key, out flagbit) && f.Value) bits |= flagbit;
|
|
|
|
// Now make the new flags
|
|
flags.Clear();
|
|
foreach(FlagTranslation f in General.Map.Config.ThingFlagsTranslation)
|
|
{
|
|
// Flag found in bits?
|
|
if((bits & f.Flag) == f.Flag)
|
|
{
|
|
// Add fields and remove bits
|
|
bits &= ~f.Flag;
|
|
for(int i = 0; i < f.Fields.Count; i++)
|
|
flags[f.Fields[i]] = f.FieldValues[i];
|
|
}
|
|
else
|
|
{
|
|
// Add fields with inverted value
|
|
for(int i = 0; i < f.Fields.Count; i++)
|
|
flags[f.Fields[i]] = !f.FieldValues[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
// This translates UDMF fields back into the normal flags
|
|
internal void TranslateFromUDMF()
|
|
{
|
|
//mxd. Clear UDMF-related properties
|
|
this.Fields.Clear();
|
|
scaleX = 1.0f;
|
|
scaleY = 1.0f;
|
|
pitch = 0;
|
|
pitchrad = 0;
|
|
roll = 0;
|
|
rollrad = 0;
|
|
|
|
// Make copy of the flags
|
|
Dictionary<string, bool> oldfields = new Dictionary<string, bool>(flags);
|
|
|
|
// Make the flags
|
|
flags.Clear();
|
|
foreach(KeyValuePair<string, string> f in General.Map.Config.ThingFlags)
|
|
{
|
|
// Flag must be numeric
|
|
int flagbit;
|
|
if(int.TryParse(f.Key, out flagbit))
|
|
{
|
|
foreach(FlagTranslation ft in General.Map.Config.ThingFlagsTranslation)
|
|
{
|
|
if(ft.Flag == flagbit)
|
|
{
|
|
// Only set this flag when the fields match
|
|
bool fieldsmatch = true;
|
|
for(int i = 0; i < ft.Fields.Count; i++)
|
|
{
|
|
if(!oldfields.ContainsKey(ft.Fields[i]) || (oldfields[ft.Fields[i]] != ft.FieldValues[i]))
|
|
{
|
|
fieldsmatch = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Field match? Then add the flag.
|
|
if(fieldsmatch)
|
|
{
|
|
flags.Add(f.Key, true);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Selected
|
|
protected override void DoSelect()
|
|
{
|
|
base.DoSelect();
|
|
selecteditem = map.SelectedThings.AddLast(this);
|
|
}
|
|
|
|
// Deselect
|
|
protected override void DoUnselect()
|
|
{
|
|
base.DoUnselect();
|
|
if(selecteditem.List != null) selecteditem.List.Remove(selecteditem);
|
|
selecteditem = null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Changes
|
|
|
|
// This moves the thing
|
|
// NOTE: This does not update sector! (call DetermineSector)
|
|
public void Move(Vector3D newpos)
|
|
{
|
|
if (newpos != pos)
|
|
{
|
|
BeforePropsChange();
|
|
|
|
// Change position
|
|
this.pos = newpos;
|
|
|
|
if (type != General.Map.Config.Start3DModeThingType)
|
|
General.Map.IsChanged = true;
|
|
}
|
|
}
|
|
|
|
// This moves the thing
|
|
// NOTE: This does not update sector! (call DetermineSector)
|
|
public void Move(Vector2D newpos)
|
|
{
|
|
Vector3D p = new Vector3D(newpos.x, newpos.y, pos.z);
|
|
|
|
if (p != pos)
|
|
{
|
|
BeforePropsChange();
|
|
|
|
// Change position
|
|
this.pos = p;
|
|
|
|
if (type != General.Map.Config.Start3DModeThingType)
|
|
General.Map.IsChanged = true;
|
|
}
|
|
}
|
|
|
|
// This moves the thing
|
|
// NOTE: This does not update sector! (call DetermineSector)
|
|
public void Move(double x, double y, double zoffset)
|
|
{
|
|
Move(new Vector3D(x, y, zoffset));
|
|
}
|
|
|
|
// This rotates the thing
|
|
public void Rotate(double newangle)
|
|
{
|
|
BeforePropsChange();
|
|
|
|
// Change angle
|
|
this.anglerad = newangle;
|
|
this.angledoom = Angle2D.RealToDoom(newangle);
|
|
|
|
if(type != General.Map.Config.Start3DModeThingType)
|
|
General.Map.IsChanged = true;
|
|
}
|
|
|
|
// This rotates the thing
|
|
public void Rotate(int newangle)
|
|
{
|
|
BeforePropsChange();
|
|
|
|
// Change angle
|
|
anglerad = Angle2D.DoomToReal(newangle);
|
|
angledoom = newangle;
|
|
|
|
if(type != General.Map.Config.Start3DModeThingType)
|
|
General.Map.IsChanged = true;
|
|
}
|
|
|
|
//mxd
|
|
public void SetPitch(int newpitch)
|
|
{
|
|
BeforePropsChange();
|
|
|
|
pitch = General.ClampAngle(newpitch);
|
|
|
|
switch (rendermode)
|
|
{
|
|
case ThingRenderMode.MODEL:
|
|
double pmult = General.Map.Config.BuggyModelDefPitch ? 1 : -1;
|
|
ModelData md = General.Map.Data.ModeldefEntries[type];
|
|
if (md.InheritActorPitch || md.UseActorPitch)
|
|
pitchrad = Angle2D.DegToRad(pmult * (md.InheritActorPitch ? -pitch : pitch));
|
|
else
|
|
pitchrad = 0;
|
|
break;
|
|
|
|
case ThingRenderMode.FLATSPRITE:
|
|
pitchrad = Angle2D.DegToRad(pitch);
|
|
break;
|
|
|
|
default:
|
|
pitchrad = 0;
|
|
break;
|
|
}
|
|
|
|
if(type != General.Map.Config.Start3DModeThingType)
|
|
General.Map.IsChanged = true;
|
|
}
|
|
|
|
//mxd
|
|
public void SetRoll(int newroll)
|
|
{
|
|
BeforePropsChange();
|
|
|
|
roll = General.ClampAngle(newroll);
|
|
rollrad = ((rollsprite || (rendermode == ThingRenderMode.MODEL && General.Map.Data.ModeldefEntries[type].UseActorRoll))
|
|
? Angle2D.DegToRad(roll) : 0);
|
|
|
|
if(type != General.Map.Config.Start3DModeThingType)
|
|
General.Map.IsChanged = true;
|
|
}
|
|
|
|
//mxd
|
|
public void SetScale(double scalex, double scaley)
|
|
{
|
|
BeforePropsChange();
|
|
|
|
scaleX = scalex;
|
|
scaleY = scaley;
|
|
|
|
if(type != General.Map.Config.Start3DModeThingType)
|
|
General.Map.IsChanged = true;
|
|
}
|
|
|
|
// This updates all properties
|
|
// NOTE: This does not update sector! (call DetermineSector)
|
|
public void Update(int type, double x, double y, double zoffset, int angle, int pitch, int roll, double scaleX, double scaleY,
|
|
Dictionary<string, bool> flags, int tag, int action, int[] args)
|
|
{
|
|
// Apply changes
|
|
this.type = type;
|
|
this.anglerad = Angle2D.DoomToReal(angle);
|
|
this.angledoom = angle;
|
|
this.pitch = pitch; //mxd
|
|
this.roll = roll; //mxd
|
|
this.scaleX = (scaleX == 0 ? 1.0f : scaleX); //mxd
|
|
this.scaleY = (scaleY == 0 ? 1.0f : scaleY); //mxd
|
|
this.flags = new Dictionary<string, bool>(flags);
|
|
this.tag = tag;
|
|
this.action = action;
|
|
this.args = new int[NUM_ARGS];
|
|
args.CopyTo(this.args, 0);
|
|
this.Move(x, y, zoffset);
|
|
|
|
UpdateCache(); //mxd
|
|
}
|
|
|
|
// This updates the settings from configuration
|
|
public void UpdateConfiguration()
|
|
{
|
|
// Lookup settings
|
|
ThingTypeInfo ti = General.Map.Data.GetThingInfo(type);
|
|
|
|
// Apply size
|
|
dynamiclighttype = GZGeneral.GetGZLightTypeByClass(ti.Actor);
|
|
if (dynamiclighttype == null)
|
|
dynamiclighttype = ti.DynamicLightType;
|
|
//General.ErrorLogger.Add(ErrorType.Warning, string.Format("thing dynamiclighttype is {0}; class is {1}", dynamiclighttype, ti.Actor.ClassName));
|
|
size = ti.Radius;
|
|
rendersize = ti.RenderRadius;
|
|
height = ti.Height; //mxd
|
|
fixedsize = ti.FixedSize;
|
|
spritescale = ti.SpriteScale; //mxd
|
|
|
|
//mxd. Apply radius and height overrides?
|
|
for(int i = 0; i < ti.Args.Length; i++)
|
|
{
|
|
if(ti.Args[i] == null) continue;
|
|
if(ti.Args[i].Type == (int)UniversalType.ThingRadius && args[i] > 0)
|
|
size = args[i];
|
|
else if(ti.Args[i].Type == (int)UniversalType.ThingHeight && args[i] > 0)
|
|
height = args[i];
|
|
}
|
|
|
|
// Color valid?
|
|
if((ti.Color >= 0) && (ti.Color < ColorCollection.NUM_THING_COLORS))
|
|
{
|
|
// Apply color
|
|
color = General.Colors.Colors[ti.Color + ColorCollection.THING_COLORS_OFFSET];
|
|
}
|
|
else
|
|
{
|
|
// Unknown thing color
|
|
color = General.Colors.Colors[ColorCollection.THING_COLORS_OFFSET];
|
|
}
|
|
|
|
directional = ti.Arrow; //mxd
|
|
rendermode = ti.RenderMode; //mxd
|
|
rollsprite = ti.RollSprite; //mxd
|
|
UpdateCache(); //mxd
|
|
}
|
|
|
|
//mxd. This checks if the thing has model override and whether pitch/roll values should be used
|
|
internal void UpdateCache()
|
|
{
|
|
if(General.Map.Data == null) return;
|
|
|
|
// Check if the thing has model override
|
|
if(General.Map.Data.ModeldefEntries.ContainsKey(type))
|
|
{
|
|
ModelData md = General.Map.Data.ModeldefEntries[type];
|
|
if((md.LoadState == ModelLoadState.None && General.Map.Data.ProcessModel(type)) || md.LoadState != ModelLoadState.None)
|
|
rendermode = (General.Map.Data.ModeldefEntries[type].IsVoxel ? ThingRenderMode.VOXEL : ThingRenderMode.MODEL);
|
|
}
|
|
else // reset rendermode if we SUDDENLY became a sprite out of a model. otherwise it crashes violently.
|
|
{
|
|
ThingTypeInfo ti = General.Map.Data.GetThingInfo(Type);
|
|
rendermode = (ti != null) ? ti.RenderMode : ThingRenderMode.NORMAL;
|
|
}
|
|
|
|
// Update radian versions of pitch and roll
|
|
switch(rendermode)
|
|
{
|
|
case ThingRenderMode.MODEL:
|
|
float pmult = General.Map.Config.BuggyModelDefPitch ? 1 : -1;
|
|
ModelData md = General.Map.Data.ModeldefEntries[type];
|
|
rollrad = (md.UseActorRoll ? Angle2D.DegToRad(roll) : 0);
|
|
pitchrad = ((md.InheritActorPitch || md.UseActorPitch) ? Angle2D.DegToRad(pmult * (md.InheritActorPitch ? -pitch : pitch)) : 0);
|
|
break;
|
|
|
|
case ThingRenderMode.FLATSPRITE:
|
|
rollrad = Angle2D.DegToRad(roll);
|
|
pitchrad = Angle2D.DegToRad(pitch);
|
|
break;
|
|
|
|
case ThingRenderMode.WALLSPRITE:
|
|
rollrad = Angle2D.DegToRad(roll);
|
|
pitchrad = 0;
|
|
break;
|
|
|
|
case ThingRenderMode.NORMAL:
|
|
rollrad = (rollsprite ? Angle2D.DegToRad(roll) : 0);
|
|
pitchrad = 0;
|
|
break;
|
|
|
|
case ThingRenderMode.VOXEL:
|
|
rollrad = 0;
|
|
pitchrad = 0;
|
|
break;
|
|
|
|
default: throw new NotImplementedException("Unknown ThingRenderMode");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ================== Methods
|
|
|
|
// This checks and returns a flag without creating it
|
|
public bool IsFlagSet(string flagname)
|
|
{
|
|
return flags.ContainsKey(flagname) && flags[flagname];
|
|
}
|
|
|
|
// This sets a flag
|
|
public void SetFlag(string flagname, bool value)
|
|
{
|
|
if(!flags.ContainsKey(flagname) || (IsFlagSet(flagname) != value))
|
|
{
|
|
BeforePropsChange();
|
|
|
|
flags[flagname] = value;
|
|
}
|
|
}
|
|
|
|
// This returns a copy of the flags dictionary
|
|
public Dictionary<string, bool> GetFlags()
|
|
{
|
|
return new Dictionary<string,bool>(flags);
|
|
}
|
|
|
|
//mxd. This returns enabled flags
|
|
public HashSet<string> GetEnabledFlags()
|
|
{
|
|
HashSet<string> result = new HashSet<string>();
|
|
foreach(KeyValuePair<string, bool> group in flags)
|
|
if(group.Value) result.Add(group.Key);
|
|
return result;
|
|
}
|
|
|
|
// This clears all flags
|
|
public void ClearFlags()
|
|
{
|
|
BeforePropsChange();
|
|
|
|
flags.Clear();
|
|
}
|
|
|
|
// This snaps the vertex to the grid
|
|
public void SnapToGrid()
|
|
{
|
|
// Calculate nearest grid coordinates
|
|
this.Move(General.Map.Grid.SnappedToGrid(pos));
|
|
}
|
|
|
|
// This snaps the vertex to the map format accuracy
|
|
public void SnapToAccuracy()
|
|
{
|
|
SnapToAccuracy(!General.Settings.SnapToInteger);
|
|
}
|
|
|
|
// This snaps the vertex to the map format accuracy
|
|
public void SnapToAccuracy(bool usepreciseposition)
|
|
{
|
|
// Round the coordinates
|
|
Vector3D newpos = new Vector3D(Math.Round(pos.x, (usepreciseposition ? General.Map.FormatInterface.VertexDecimals : 0)),
|
|
Math.Round(pos.y, (usepreciseposition ? General.Map.FormatInterface.VertexDecimals : 0)),
|
|
Math.Round(pos.z, (usepreciseposition ? General.Map.FormatInterface.VertexDecimals : 0)));
|
|
this.Move(newpos);
|
|
}
|
|
|
|
// This returns the distance from given coordinates
|
|
public double DistanceToSq(Vector2D p)
|
|
{
|
|
return Vector2D.DistanceSq(p, pos);
|
|
}
|
|
|
|
// This returns the distance from given coordinates
|
|
public double DistanceTo(Vector2D p)
|
|
{
|
|
return Vector2D.Distance(p, pos);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Changes the thing's index to a new index.
|
|
/// </summary>
|
|
/// <param name="newindex">The new index to set</param>
|
|
public void ChangeIndex(int newindex)
|
|
{
|
|
General.Map.UndoRedo.RecIndexThing(Index, newindex);
|
|
map?.ChangeThingIndex(Index, newindex);
|
|
}
|
|
|
|
public float GetObjectScale()
|
|
{
|
|
if (General.Map.Config.EngineName == "srb2")
|
|
return (float)UniFields.GetFloat(this.Fields, "mobjscale", 1.0f) * General.Map.Data.MapInfo.ObjectScale;
|
|
else
|
|
return 1.0f;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|