Files
abysmal-isle/Scripts/Services/Spawner/Spawner.cs
Unstable Kitsune b918192e4e Overwrite
Complete Overwrite of the Folder with the free shard. ServUO 57.3 has been added.
2023-11-28 23:20:26 -05:00

1185 lines
33 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using System.Linq;
using Server.Commands;
using Server.ContextMenus;
using Server.Items;
using CPA = Server.CommandPropertyAttribute;
using Server.Gumps;
namespace Server.Mobiles
{
public class Spawner : Item, ISpawner
{
private static WarnTimer m_WarnTimer;
private int m_Team;
private int m_HomeRange;
private int m_SpawnRange;
private int m_WalkingRange;
private TimeSpan m_MinDelay;
private TimeSpan m_MaxDelay;
private DateTime m_End;
private InternalTimer m_Timer;
private bool m_Running;
private bool m_Group;
private WayPoint m_WayPoint;
private List<SpawnObject> m_SpawnObjects;
private int m_MaxCount;
[Constructable]
public Spawner()
: this(null)
{
}
[Constructable]
public Spawner(string spawnName)
: this(1, 5, 10, 0, 4, spawnName)
{
}
[Constructable]
public Spawner(int amount, int minDelay, int maxDelay, int team, int spawnRange, string spawnName)
: base(0x1f13)
{
List<SpawnObject> objects = new List<SpawnObject>();
if (!String.IsNullOrEmpty(spawnName))
objects.Add(new SpawnObject(spawnName));
InitSpawner(amount, TimeSpan.FromMinutes(minDelay), TimeSpan.FromMinutes(maxDelay), team, spawnRange, objects);
}
public Spawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int spawnRange, List<string> spawnNames)
: base(0x1f13)
{
List<SpawnObject> objects = new List<SpawnObject>();
foreach (var name in spawnNames)
{
objects.Add(new SpawnObject(name));
}
InitSpawner(amount, minDelay, maxDelay, team, spawnRange, objects);
}
public Spawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int spawnRange, List<SpawnObject> objects)
: base(0x1f13)
{
InitSpawner(amount, minDelay, maxDelay, team, spawnRange, objects);
}
public Spawner(Serial serial)
: base(serial)
{
}
public override bool IsVirtualItem { get { return true; } }
public bool IsFull { get { return (SpawnCount >= m_MaxCount); } }
public bool IsEmpty { get { return (SpawnCount == 0); } }
public List<SpawnObject> SpawnObjects
{
get { return m_SpawnObjects; }
set
{
m_SpawnObjects = value;
if (m_SpawnObjects.Count < 1)
Stop();
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.Spawner)]
public int MaxCount
{
get { return m_MaxCount; }
set
{
m_MaxCount = value;
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.Spawner)]
public virtual int SpawnObjectCount { get{ return m_SpawnObjects.Count; } }
[CommandProperty(AccessLevel.Spawner)]
public WayPoint WayPoint { get { return m_WayPoint; } set { m_WayPoint = value; } }
[CommandProperty(AccessLevel.Spawner)]
public bool Running
{
get { return m_Running; }
set
{
if (value)
Start();
else
Stop();
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.Spawner)]
public int HomeRange { get { return m_HomeRange; } set { m_HomeRange = value; InvalidateProperties(); } }
[CommandProperty(AccessLevel.Spawner)]
public int SpawnRange { get { return m_SpawnRange; } set { m_SpawnRange = value; InvalidateProperties(); } }
[CommandProperty(AccessLevel.Spawner)]
public int WalkingRange { get { return m_WalkingRange; } set { m_WalkingRange = value; InvalidateProperties(); } }
[CommandProperty(AccessLevel.Spawner)]
public int Team { get { return m_Team; } set { m_Team = value; InvalidateProperties(); } }
[CommandProperty(AccessLevel.Spawner)]
public TimeSpan MinDelay
{
get { return m_MinDelay; }
set
{
var old = m_MinDelay;
m_MinDelay = value;
if(old != m_MinDelay && m_Running)
DoTimer();
InvalidateProperties();
}
}
[CommandProperty(AccessLevel.Spawner)]
public TimeSpan MaxDelay
{
get { return m_MaxDelay; }
set
{
var old = m_MaxDelay;
m_MaxDelay = value;
if (old != m_MaxDelay && m_Running)
DoTimer();
InvalidateProperties();
}
}
public DateTime End { get { return m_End; } set { m_End = value; } }
[CommandProperty(AccessLevel.Spawner)]
public TimeSpan NextSpawn
{
get
{
if (m_Running)
return m_End - DateTime.UtcNow;
else
return TimeSpan.FromSeconds(0);
}
set
{
Start();
DoTimer(value);
}
}
[CommandProperty(AccessLevel.Spawner)]
public bool Group { get { return m_Group; } set { m_Group = value; InvalidateProperties(); } }
public override string DefaultName { get { return "Spawner"; } }
public Point3D HomeLocation { get { return Location; } }
bool ISpawner.UnlinkOnTaming { get { return true; } }
[CommandProperty(AccessLevel.Spawner)]
public int SpawnCount
{
get
{
int count = 0;
foreach (var so in m_SpawnObjects)
{
count += GetSpawnCount(so);
}
return count;
}
}
[CommandProperty(AccessLevel.GameMaster)]
public bool GuardImmune { get; set; }
public override void OnAfterDuped(Item newItem)
{
Spawner s = newItem as Spawner;
if (s == null)
return;
s.m_SpawnObjects = new List<SpawnObject>(m_SpawnObjects);
base.OnAfterDuped(newItem);
}
public override void OnDoubleClick(Mobile from)
{
if (!from.Player || from.AccessLevel < AccessLevel.Spawner)
return;
SpawnerGump gump = BaseGump.GetGump<SpawnerGump>((PlayerMobile)from, g => g.Spawner == this);
if (gump != null)
{
gump.Refresh();
}
else
{
BaseGump.SendGump(new SpawnerGump(from, this));
}
}
public override void GetProperties(ObjectPropertyList list)
{
base.GetProperties(list);
if (m_Running)
{
list.Add(1060742); // active
list.Add(1060656, m_MaxCount.ToString()); // amount to make: ~1_val~
list.Add(1061169, m_HomeRange.ToString()); // range ~1_val~
list.Add(1060658, "walking range\t{0}", m_WalkingRange); // ~1_val~: ~2_val~
list.Add(1060659, "group\t{0}", m_Group); // ~1_val~: ~2_val~
list.Add(1060660, "team\t{0}", m_Team); // ~1_val~: ~2_val~
list.Add(1060661, "speed\t{0} to {1}", m_MinDelay, m_MaxDelay); // ~1_val~: ~2_val~
if (m_SpawnObjects.Count != 0)
list.Add(SpawnedStats());
}
else
{
list.Add(1060743); // inactive
}
}
public override void OnSingleClick(Mobile from)
{
base.OnSingleClick(from);
if (m_Running)
LabelTo(from, "[Running]");
else
LabelTo(from, "[Off]");
}
public void Start()
{
if (!m_Running)
{
if (SpawnObjectCount > 0)
{
m_Running = true;
DoTimer();
}
}
}
public void Stop()
{
if (m_Running)
{
if (m_Timer != null)
m_Timer.Stop();
m_Running = false;
}
}
public void Defrag()
{
bool removed = false;
List<ISpawnable> toRemove = new List<ISpawnable>();
for (int i = 0; i < m_SpawnObjects.Count; ++i)
{
foreach (var e in m_SpawnObjects[i].SpawnedObjects)
{
bool remove = false;
if (e is Item)
{
Item item = (Item)e;
if (item.Deleted || item.Parent != null)
remove = true;
}
else if (e is Mobile)
{
Mobile m = (Mobile)e;
if (m.Deleted)
{
remove = true;
}
else if (m is BaseCreature)
{
BaseCreature bc = (BaseCreature)m;
if (bc.Controlled || bc.IsStabled)
{
remove = true;
}
}
}
if (remove)
{
toRemove.Add(e);
removed = true;
}
}
}
if (removed)
{
foreach (var spawnable in toRemove)
{
RemoveSpawn(spawnable);
}
InvalidateProperties();
}
ColUtility.Free(toRemove);
}
public IEnumerable<ISpawnable> GetSpawn()
{
if (m_SpawnObjects == null || m_SpawnObjects.Count == 0)
yield break;
foreach (var so in m_SpawnObjects)
{
foreach (var spawnable in so.SpawnedObjects)
{
yield return spawnable;
}
}
}
public void RemoveSpawn(ISpawnable e)
{
SpawnObject so = m_SpawnObjects.FirstOrDefault(s => s.SpawnedObjects.Contains(e));
if (so != null)
{
so.SpawnedObjects.Remove(e);
}
}
public bool AddSpawnObject(SpawnObject so)
{
if(m_SpawnObjects.FirstOrDefault(s => ParseType(s.SpawnName.ToLower()) == ParseType(so.SpawnName.ToLower())) == null
&& m_SpawnObjects.Count < SpawnerGump.MaxEntries)
{
SpawnObjects.Add(so);
return true;
}
return false;
}
public void OnTick()
{
DoTimer();
if (m_Group)
{
Defrag();
if (SpawnCount == 0)
{
Respawn();
}
else
{
return;
}
}
else
{
Spawn();
}
}
public virtual bool CheckSpawnerFull()
{
return (SpawnCount >= m_MaxCount);
}
public virtual void Respawn()
{
RemoveSpawned();
for (int i = 0; i < m_MaxCount; i++)
Spawn();
}
public virtual void Spawn()
{
List<SpawnObject> avail = GetAvailableSpawnObjects();
if (avail != null && avail.Count > 0)
{
Spawn(avail[Utility.Random(avail.Count)]);
}
}
public void Spawn(int index)
{
if (index >= 0 && index < m_SpawnObjects.Count)
{
var so = m_SpawnObjects[index];
if (m_Group)
{
int toSpawn = so.MaxCount - GetSpawnCount(so);
for (int i = 0; i < toSpawn; i++)
{
Spawn(so);
}
}
else
{
Spawn(so);
}
}
}
public void Spawn(SpawnObject so)
{
Map map = Map;
if (map == null || map == Map.Internal || SpawnObjectCount == 0 || so == null || Parent != null || GetSpawnCount(so) >= so.MaxCount)
return;
Defrag();
if (CheckSpawnerFull())
return;
ISpawnable spawned = CreateSpawnedObject(so);
if (spawned == null)
return;
spawned.Spawner = this;
so.SpawnedObjects.Add(spawned);
Point3D loc = (spawned is BaseVendor ? Location : GetSpawnPosition(spawned));
spawned.OnBeforeSpawn(loc, map);
spawned.MoveToWorld(loc, map);
if (spawned is BaseCreature)
{
BaseCreature bc = (BaseCreature)spawned;
if (m_WalkingRange >= 0)
bc.RangeHome = m_WalkingRange;
else
bc.RangeHome = m_HomeRange;
bc.CurrentWayPoint = m_WayPoint;
if (m_Team > 0)
bc.Team = m_Team;
bc.Home = HomeLocation;
}
if (spawned is Mobile)
{
Mobile m = (Mobile)spawned;
m.GuardImmune = GuardImmune;
}
spawned.OnAfterSpawn();
InvalidateProperties();
}
public Point3D GetSpawnPosition()
{
return GetSpawnPosition(null);
}
public Point3D GetSpawnPosition(ISpawnable spawned)
{
Map map = Map;
if (map == null)
return Location;
bool waterMob, waterOnlyMob;
if (spawned is Mobile)
{
Mobile mob = (Mobile)spawned;
waterMob = mob.CanSwim;
waterOnlyMob = (mob.CanSwim && mob.CantWalk);
}
else
{
waterMob = false;
waterOnlyMob = false;
}
// Try 10 times to find a spawnable location.
for (int i = 0; i < 10; ++i)
{
int x, y;
if (m_HomeRange > 0)
{
x = Location.X + (Utility.Random((m_SpawnRange * 2) + 1) - m_HomeRange);
y = Location.Y + (Utility.Random((m_SpawnRange * 2) + 1) - m_HomeRange);
}
else
{
x = Location.X;
y = Location.Y;
}
int mapZ = map.GetAverageZ(x, y);
if (waterMob)
{
if (IsValidWater(map, x, y, Z))
return new Point3D(x, y, Z);
else if (IsValidWater(map, x, y, mapZ))
return new Point3D(x, y, mapZ);
}
if (!waterOnlyMob)
{
if (map.CanSpawnMobile(x, y, Z))
return new Point3D(x, y, Z);
else if (map.CanSpawnMobile(x, y, mapZ))
return new Point3D(x, y, mapZ);
}
}
return Location;
}
public List<SpawnObject> GetAvailableSpawnObjects()
{
if (SpawnObjectCount == 0)
return null;
List<SpawnObject> objects = null;
foreach (var so in m_SpawnObjects)
{
if (so.CurrentCount < so.MaxCount)
{
if(objects == null)
objects = new List<SpawnObject>();
objects.Add(so);
}
}
return objects;
}
public int GetSpawnCount(SpawnObject so)
{
return so != null ? so.CurrentCount : 0;
}
public void DoTimer()
{
if (!m_Running)
return;
int minSeconds = (int)m_MinDelay.TotalSeconds;
int maxSeconds = (int)m_MaxDelay.TotalSeconds;
TimeSpan delay = TimeSpan.FromSeconds(Utility.RandomMinMax(minSeconds, maxSeconds));
DoTimer(delay);
}
public virtual void DoTimer(TimeSpan delay)
{
if (!m_Running)
return;
m_End = DateTime.UtcNow + delay;
if (m_Timer != null)
{
m_Timer.Stop();
m_Timer = null;
}
m_Timer = new InternalTimer(this, delay);
m_Timer.Start();
}
public string SpawnedStats()
{
Defrag();
Dictionary<string, int> counts = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
foreach (var entry in m_SpawnObjects)
{
string name = ParseType(entry.SpawnName);
Type type = ScriptCompiler.FindTypeByName(name);
if (type == null)
counts[name] = 0;
else
counts[type.Name] = 0;
}
foreach (var spawned in GetSpawn())
{
string name = spawned.GetType().Name;
if (counts.ContainsKey(name))
++counts[name];
else
counts[name] = 1;
}
List<string> names = new List<string>(counts.Keys);
names.Sort();
StringBuilder result = new StringBuilder();
for (int i = 0; i < names.Count; ++i)
result.AppendFormat("{0}{1}: {2}", (i == 0) ? "" : "<BR>", names[i], counts[names[i]]);
return result.ToString();
}
public int CountCreatures(SpawnObject so)
{
Defrag();
return so.CurrentCount;
}
public void RemoveSpawned(int index)
{
if (index >= 0 && index < m_SpawnObjects.Count)
{
var so = m_SpawnObjects[index];
if (m_Group)
{
int count = GetSpawnCount(so);
for (int i = 0; i < count; i++)
{
RemoveSpawned(so);
}
}
else
{
RemoveSpawned(so);
}
}
}
public void RemoveSpawned(SpawnObject so)
{
Defrag();
if (so.CurrentCount > 0)
so.SpawnedObjects[0].Delete();
InvalidateProperties();
}
public void RemoveSpawned()
{
Defrag();
List<ISpawnable> toRemove = new List<ISpawnable>();
foreach (var spawn in GetSpawn())
{
toRemove.Add(spawn);
}
foreach (var spawn in toRemove)
spawn.Delete();
ColUtility.Free(toRemove);
InvalidateProperties();
}
public void BringToHome()
{
Defrag();
foreach (var spawn in GetSpawn())
{
spawn.MoveToWorld(Location, Map);
}
}
public override void OnDelete()
{
base.OnDelete();
RemoveSpawned();
if (m_Timer != null)
m_Timer.Stop();
}
public override void Serialize(GenericWriter writer)
{
base.Serialize(writer);
writer.Write((int)7); // version
writer.Write(GuardImmune);
writer.Write(m_SpawnRange);
writer.Write(m_WalkingRange);
writer.Write(m_WayPoint);
writer.Write(m_Group);
writer.Write(m_MinDelay);
writer.Write(m_MaxDelay);
writer.Write(m_MaxCount);
writer.Write(m_Team);
writer.Write(m_HomeRange);
writer.Write(m_Running);
if (m_Running)
writer.WriteDeltaTime(m_End);
writer.Write(m_SpawnObjects.Count);
for (int i = 0; i < m_SpawnObjects.Count; ++i)
{
m_SpawnObjects[i].Serialize(writer);
}
}
public override void Deserialize(GenericReader reader)
{
base.Deserialize(reader);
int version = reader.ReadInt();
switch ( version )
{
case 7:
{
GuardImmune = reader.ReadBool();
goto case 6;
}
case 6:
{
m_SpawnRange = reader.ReadInt();
goto case 5;
}
case 5:
case 4:
{
m_WalkingRange = reader.ReadInt();
goto case 3;
}
case 3:
case 2:
{
m_WayPoint = reader.ReadItem() as WayPoint;
goto case 1;
}
case 1:
{
m_Group = reader.ReadBool();
goto case 0;
}
case 0:
{
m_MinDelay = reader.ReadTimeSpan();
m_MaxDelay = reader.ReadTimeSpan();
m_MaxCount = reader.ReadInt();
m_Team = reader.ReadInt();
m_HomeRange = reader.ReadInt();
m_Running = reader.ReadBool();
TimeSpan ts = TimeSpan.Zero;
if (m_Running)
ts = reader.ReadDeltaTime() - DateTime.UtcNow;
int size = reader.ReadInt();
m_SpawnObjects = new List<SpawnObject>(size);
for (int i = 0; i < size; ++i)
{
if (version > 4)
{
SpawnObject so = new SpawnObject(reader);
if (AddSpawnObject(so))
{
string typeName = ParseType(so.SpawnName);
if (ScriptCompiler.FindTypeByName(typeName) == null)
{
if (m_WarnTimer == null)
m_WarnTimer = new WarnTimer();
m_WarnTimer.Add(Location, Map, typeName);
}
}
}
else
{
string creatureString = reader.ReadString();
AddSpawnObject(new SpawnObject(creatureString));
string typeName = ParseType(creatureString);
if (ScriptCompiler.FindTypeByName(typeName) == null)
{
if (m_WarnTimer == null)
m_WarnTimer = new WarnTimer();
m_WarnTimer.Add(Location, Map, typeName);
}
}
}
if (version < 5)
{
int count = reader.ReadInt();
for (int i = 0; i < count; ++i)
{
ISpawnable e = World.FindEntity(reader.ReadInt()) as ISpawnable;
if (e != null)
{
e.Delete(); // lets make this easy
}
}
}
if (m_Running)
DoTimer(ts);
break;
}
}
if (version < 3 && Weight == 0)
Weight = -1;
}
protected virtual ISpawnable CreateSpawnedObject(SpawnObject obj)
{
if (!m_SpawnObjects.Contains(obj))
return null;
Type type = ScriptCompiler.FindTypeByName(ParseType(obj.SpawnName));
if (type != null)
{
try
{
return Build(type, CommandSystem.Split(obj.SpawnName));
}
catch
{
}
}
return null;
}
private void InitSpawner(int amount, TimeSpan minDelay, TimeSpan maxDelay, int team, int spawnRange, List<SpawnObject> spawnObjects)
{
Visible = false;
Movable = false;
m_Running = true;
m_Group = false;
m_MinDelay = minDelay;
m_MaxDelay = maxDelay;
m_MaxCount = amount;
m_Team = team;
m_SpawnRange = spawnRange;
m_WalkingRange = -1;
m_SpawnObjects = spawnObjects;
DoTimer(TimeSpan.FromSeconds(1));
m_HomeRange = 5;
int max = 1;
if (spawnObjects != null)
{
foreach (var obj in spawnObjects)
{
max += obj.MaxCount;
}
}
}
public virtual void GetSpawnProperties(ISpawnable spawn, ObjectPropertyList list)
{ }
public virtual void GetSpawnContextEntries(ISpawnable spawn, Mobile user, List<ContextMenuEntry> list)
{ }
void ISpawner.Remove(ISpawnable spawn)
{
RemoveSpawn(spawn);
InvalidateProperties();
}
public static string ParseType(string s)
{
return s.Split(null, 2)[0];
}
public static ISpawnable Build(Type type, string[] args)
{
bool isISpawnable = typeof(ISpawnable).IsAssignableFrom(type);
if (!isISpawnable)
{
return null;
}
Add.FixArgs(ref args);
string[,] props = null;
for (int i = 0; i < args.Length; ++i)
{
if (Insensitive.Equals(args[i], "set"))
{
int remains = args.Length - i - 1;
if (remains >= 2)
{
props = new string[remains / 2, 2];
remains /= 2;
for (int j = 0; j < remains; ++j)
{
props[j, 0] = args[i + (j * 2) + 1];
props[j, 1] = args[i + (j * 2) + 2];
}
Add.FixSetString(ref args, i);
}
break;
}
}
PropertyInfo[] realProps = null;
if (props != null)
{
realProps = new PropertyInfo[props.GetLength(0)];
PropertyInfo[] allProps = type.GetProperties(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public);
for (int i = 0; i < realProps.Length; ++i)
{
PropertyInfo thisProp = null;
string propName = props[i, 0];
for (int j = 0; thisProp == null && j < allProps.Length; ++j)
{
if (Insensitive.Equals(propName, allProps[j].Name))
thisProp = allProps[j];
}
if (thisProp != null)
{
CPA attr = Properties.GetCPA(thisProp);
if (attr != null && AccessLevel.Spawner >= attr.WriteLevel && thisProp.CanWrite && !attr.ReadOnly)
realProps[i] = thisProp;
}
}
}
ConstructorInfo[] ctors = type.GetConstructors();
for (int i = 0; i < ctors.Length; ++i)
{
ConstructorInfo ctor = ctors[i];
if (!Add.IsConstructable(ctor, AccessLevel.Spawner))
continue;
ParameterInfo[] paramList = ctor.GetParameters();
if (args.Length == paramList.Length)
{
object[] paramValues = Add.ParseValues(paramList, args);
if (paramValues == null)
continue;
object built = ctor.Invoke(paramValues);
if (built != null && realProps != null)
{
for (int j = 0; j < realProps.Length; ++j)
{
if (realProps[j] == null)
continue;
Properties.InternalSetValue(built, realProps[j], props[j, 1]);
}
}
return (ISpawnable)built;
}
}
return null;
}
public static bool IsValidWater(Map map, int x, int y, int z)
{
if (!Region.Find(new Point3D(x, y, z), map).AllowSpawn() || !map.CanFit(x, y, z, 16, false, true, false))
return false;
LandTile landTile = map.Tiles.GetLandTile(x, y);
if (landTile.Z == z && (TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags & TileFlag.Wet) != 0)
return true;
StaticTile[] staticTiles = map.Tiles.GetStaticTiles(x, y, true);
for (int i = 0; i < staticTiles.Length; ++i)
{
StaticTile staticTile = staticTiles[i];
if (staticTile.Z == z && (TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Flags & TileFlag.Wet) != 0)
return true;
}
return false;
}
private class InternalTimer : Timer
{
private readonly Spawner m_Spawner;
public InternalTimer(Spawner spawner, TimeSpan delay)
: base(delay)
{
if (spawner.IsFull)
Priority = TimerPriority.FiveSeconds;
else
Priority = TimerPriority.OneSecond;
m_Spawner = spawner;
}
protected override void OnTick()
{
if (m_Spawner != null && !m_Spawner.Deleted)
m_Spawner.OnTick();
}
}
private class WarnTimer : Timer
{
private readonly List<WarnEntry> m_List;
public WarnTimer()
: base(TimeSpan.FromSeconds(1.0))
{
m_List = new List<WarnEntry>();
Start();
}
public void Add(Point3D p, Map map, string name)
{
m_List.Add(new WarnEntry(p, map, name));
}
protected override void OnTick()
{
try
{
Console.WriteLine("Warning: {0} bad spawns detected, logged: 'badspawn.log'", m_List.Count);
using (StreamWriter op = new StreamWriter("badspawn.log", true))
{
op.WriteLine("# Bad spawns : {0}", DateTime.UtcNow);
op.WriteLine("# Format: X Y Z F Name");
op.WriteLine();
foreach (WarnEntry e in m_List)
op.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}", e.m_Point.X, e.m_Point.Y, e.m_Point.Z, e.m_Map, e.m_Name);
op.WriteLine();
op.WriteLine();
}
}
catch
{
}
}
private class WarnEntry
{
public readonly Point3D m_Point;
public readonly Map m_Map;
public readonly string m_Name;
public WarnEntry(Point3D p, Map map, string name)
{
m_Point = p;
m_Map = map;
m_Name = name;
}
}
}
}
}