private static bool HasTileSurface( Map map, int X, int Y, int Z ) { if( map == null ) return false; #if(RUNUO2RC1) ArrayList tiles = map.GetTilesAt(new Point2D(X, Y), true, true, true); #else LandTile tile = map.Tiles.GetLandTile( X, Y ); StaticTile[] statics = map.Tiles.GetStaticTiles( X, Y, true ); #endif if( statics == null ) return false; // go through the tiles and see if any are at the Z location for( int i = 0; i < statics.Length; i++ ) { StaticTile st = statics[i]; if( (st.Z + st.Height) == Z ) { return true; } } return false; }
private static bool HasTileSurface(Map map, int X, int Y, int Z) { if (map == null) return false; #if(RUNUO2RC1) ArrayList tiles = map.GetTilesAt(new Point2D(X, Y), true, true, true); #else StaticTile[] tiles = map.Tiles.GetStaticTiles(X, Y, true); //List<Server.Tile> tiles = map.GetTilesAt(new Point2D(X, Y), true, true, true); #endif if (tiles == null) return false; // go through the tiles and see if any are at the Z location foreach (object o in tiles) { if (o is StaticTile) { StaticTile i = (StaticTile)o; if ((i.Z + i.Height) == Z) { return true; } } } return false; }
private static bool HasTileSurface(Map map, int X, int Y, int Z) { if (map == null) return false; ArrayList tiles = map.GetTilesAt(new Point2D(X, Y), true, true, true); if (tiles == null) return false; // go through the tiles and see if any are at the Z location foreach (object o in tiles) { if (o is Tile) { Tile i = (Tile)o; if ((i.Z + i.Height) == Z) { return true; } } } return false; }
/// <summary> /// Callback for the bounding box picker /// </summary> private void AddRangeCallback( Mobile m, Map map, Point3D start, Point3D end, object state ) { for ( int x = Math.Min( start.X, end.X ); x <= Math.Max( start.X, end.X ); x++ ) { for ( int y = Math.Min( start.Y, end.Y ); y <= Math.Max( start.Y, end.Y ); y++ ) { Point2D p = new Point2D( x, y ); ArrayList tiles = map.GetTilesAt( p, false, false, true ); #region Z Range Filter if ( m_ZRange ) { ArrayList remove = new ArrayList(); foreach( Tile t in tiles ) { if ( t.Z < m_MinZ || t.Z > m_MaxZ ) remove.Add( t ); } foreach( Tile t in remove ) { tiles.Remove( t ); } } #endregion if ( tiles.Count == 0 ) continue; ArrayList current = m_Tiles[ p ] as ArrayList; if ( current != null ) { foreach( Tile t in tiles ) { if ( ! current.Contains( t ) ) { current.Add( t ); } } } else { m_Tiles[ p ] = tiles; } } } ResendGump(); }
private static void PickerCallback(Mobile from, Map map, Point3D start, Point3D end, object state) { object[] args = state as object[]; int m_SimpleComponents = 0; int m_ComplexComponents = 0; int m_NamedComponents = 0; int m_TotalComponents = 0; if (start.X > end.X) { int x = start.X; start.X = end.X; end.X = x; } if (start.Y > end.Y) { int y = start.Y; start.Y = end.Y; end.Y = y; } Rectangle2D bounds = new Rectangle2D(start, end); string name = args[0] as string; string ns = args[1] as string; bool getStatics = (bool)args[2]; bool getItems = (bool)args[3]; bool getTiles = (bool)args[4]; bool includeStaticRange = (bool)args[5]; bool includeItemRange = (bool)args[6]; bool includeTileRange = (bool)args[7]; bool includeZRange = (bool)args[8]; bool generateTest = (bool)args[17]; sbyte min = sbyte.MinValue; sbyte max = sbyte.MaxValue; short minStaticID = 2; short maxStaticID = 16384; short minItemID = 2; short maxItemID = 16384; short minTileID = 2; short maxTileID = 16384; try { min = sbyte.Parse(args[9] as string); } catch { } try { max = sbyte.Parse(args[10] as string); } catch { } try { minStaticID = short.Parse(args[11] as string); } catch { } try { maxStaticID = short.Parse(args[12] as string); } catch { } try { minItemID = short.Parse(args[13] as string); } catch { } try { maxItemID = short.Parse(args[14] as string); } catch { } try { minTileID = short.Parse(args[15] as string); } catch { } try { maxTileID = short.Parse(args[16] as string); } catch { } Hashtable tiles = new Hashtable(); if (getTiles) { for (int x = start.X; x <= end.X; x++) { for (int y = start.Y; y <= end.Y; y++) { #if RC2 List<Server.Tile> list = map.GetTilesAt(new Point2D(x, y), false, false, true); List<Server.Tile> remove = new List<Server.Tile>(); #else ArrayList list = map.GetTilesAt(new Point2D(x, y), false, false, true); ArrayList remove = new ArrayList(); #endif foreach (Tile t in list) { int id = t.ID - 16384; if (id < 2 || id > 16382) remove.Add(t); else if (includeZRange && (t.Z < min || t.Z > max)) remove.Add(t); else if (!includeZRange && (t.Z >= min && t.Z <= max)) remove.Add(t); else if (includeTileRange && (id < minTileID || id > maxTileID)) remove.Add(t); else if (!includeTileRange && (id >= minTileID && id <= maxTileID)) remove.Add(t); } foreach (Tile t in remove) { list.Remove(t); } if (list != null && list.Count > 0) { tiles[new Point2D(x, y)] = list; } } } } IPooledEnumerable en = map.GetItemsInBounds(bounds); ArrayList target = new ArrayList(); bool fail = false; try { foreach (object o in en) { if (getStatics) { Static s = o as Static; if (s == null) { } else if (s.Deleted) {} else if (includeZRange && (s.Z < min || s.Z > max)) continue; else if (!includeZRange && (s.Z >= min && s.Z <= max)) continue; else if (includeStaticRange && (s.ItemID < minStaticID || s.ItemID > maxStaticID)) continue; else if (!includeStaticRange && (s.ItemID >= minStaticID && s.ItemID <= maxStaticID)) continue; else { target.Add(o); #if DEBUG Console.WriteLine("Static={0}:{1}", s.GetType().ToString(), s.ItemID); #endif continue; } } if (getItems) { Static s = o as Static; if (s != null) // Don't want a static continue; Item i = o as Item; if (i == null) continue; else if (i.Deleted) continue; else if (i is BaseAddon) // Not a good idea to add a BaseAddOn for obvious reasons continue; else if (i.ItemID < 2 || i.ItemID > 16382) // This is not an Item within the normal artwork.. multi... etc.. Toss it continue; else if (includeZRange && (i.Z < min || i.Z > max)) continue; else if (!includeZRange && (i.Z >= min && i.Z <= max)) continue; else if (includeItemRange && (i.ItemID < minItemID || i.ItemID > maxItemID)) continue; else if (!includeItemRange && (i.ItemID >= minItemID && i.ItemID <= maxItemID)) continue; #if DEBUG Console.WriteLine("item={0}:{1}, {2}-map{3}", i.GetType().ToString(), i.ItemID, i.Deleted, i.Map); #endif target.Add(o); } } } catch (Exception err) { Console.WriteLine(err.ToString()); from.SendMessage(0x40, "The targeted components have been modified. Please retry."); fail = true; } finally { en.Free(); } if (fail) return; if (target.Count == 0 && tiles.Keys.Count == 0) { from.SendMessage(0x40, "No components have been selected."); from.SendGump(new InternalGump(from, args)); return; } // Get center Point3D center = new Point3D(); center.Z = 127; int x1 = bounds.End.X; int y1 = bounds.End.Y; int x2 = bounds.Start.X; int y2 = bounds.Start.Y; // Get correct bounds foreach (Item item in target) { if (item.Z < center.Z) { center.Z = item.Z; } x1 = Math.Min(x1, item.X); y1 = Math.Min(y1, item.Y); x2 = Math.Max(x2, item.X); y2 = Math.Max(y2, item.Y); } CEOIdentifyAddon IdentifyAddon = null; if (generateTest) IdentifyAddon = new CEOIdentifyAddon("init"); foreach (Point2D p in tiles.Keys) { #if RC2 List<Server.Tile> list = tiles[p] as List<Server.Tile>; #else ArrayList list = tiles[p] as ArrayList; #endif if (list == null) { Console.WriteLine("The list is null... "); return; } foreach (Tile t in list) { if (t.Z < center.Z) { center.Z = t.Z; } } x1 = Math.Min(x1, p.X); y1 = Math.Min(y1, p.Y); x2 = Math.Max(x2, p.X); y2 = Math.Max(y2, p.Y); } center.X = x1 + ((x2 - x1) / 2); center.Y = y1 + ((y2 - y1) / 2); // Build items System.Text.StringBuilder nc = new System.Text.StringBuilder(); nc.Append("\n"); System.Text.StringBuilder sl = new System.Text.StringBuilder(); sl.Append("private static int[,] m_AddOnSimpleComponents = new int[,] {\n\t\t\t "); System.Text.StringBuilder cl = new System.Text.StringBuilder(); cl.Append("private static int[,] m_AddOnComplexComponents = new int[,] {\n\t\t\t "); System.Text.StringBuilder sc = new System.Text.StringBuilder(); sc.Append("// "); System.Text.StringBuilder cc = new System.Text.StringBuilder(); cc.Append("// "); int simplecount = 0; int complexcount = 0; // Tiles foreach (Point2D p in tiles.Keys) { #if RC2 List<Server.Tile> list = tiles[p] as List<Server.Tile>; #else ArrayList list = tiles[p] as ArrayList; #endif int xOffset = p.X - center.X; int yOffset = p.Y - center.Y; foreach (Tile t in list) { int zOffset = t.Z - center.Z; int id = t.ID - 16384; m_SimpleComponents++; simplecount++; m_TotalComponents++; sc.AppendFormat("{0}\t ", m_TotalComponents); if (simplecount > 1) sl.Append(", "); sl.Append("{"); sl.AppendFormat("{0}, {1}, {2}, {3}", id, xOffset, yOffset, zOffset); sl.Append("}"); if (simplecount % 3 == 0) { sl.AppendFormat("{0}\n\t\t\t", sc.ToString()); sc.Length = 0; sc.Append("// "); } if (generateTest) AddIdentifyAddOnComponent(IdentifyAddon, id, xOffset, yOffset, zOffset, 0, -1, string.Format("({0}):{1},{2},{3}", m_TotalComponents, xOffset, yOffset, zOffset), 0); } } // Statics & Items foreach (Item item in target) { if (item.Deleted) continue; int xOffset = item.X - center.X; int yOffset = item.Y - center.Y; int zOffset = item.Z - center.Z; int id = item.ItemID; if (((item.ItemData.Flags & TileFlag.LightSource) == TileFlag.LightSource) || (item.Hue != 0) || (item.Name != null) || item.Amount > 1) // Use old method { if (item.Name != null || item.Amount != 0) // Have to do this one the old method { m_NamedComponents++; m_TotalComponents++; int lightsource = -1; if ((item.ItemData.Flags & TileFlag.LightSource) == TileFlag.LightSource) lightsource = (int)item.Light; nc.AppendFormat("\t\t\tAddComplexComponent( (BaseAddon) this, {0}, {1}, {2}, {3}, {4}, {5}, \"{6}\", {7});// {8}\n", id, xOffset, yOffset, zOffset, item.Hue, lightsource, item.Name, item.Amount, m_TotalComponents); if (generateTest) AddIdentifyAddOnComponent(IdentifyAddon, id, xOffset, yOffset, zOffset, item.Hue, -1, string.Format("({0},{1}): {2}, {3}, {4}", m_TotalComponents, id, xOffset, yOffset, zOffset), item.Amount); } else //if (item.Hue != 0 || (item.ItemData.Flags & TileFlag.LightSource) == TileFlag.LightSource) { int lightsource = -1; if ((item.ItemData.Flags & TileFlag.LightSource) == TileFlag.LightSource) lightsource = (int)item.Light; m_ComplexComponents++; m_TotalComponents++; cc.AppendFormat("{0}\t", m_TotalComponents); complexcount++; if (complexcount > 1) cl.Append(", "); cl.Append("{"); cl.AppendFormat("{0}, {1}, {2}, {3}, {4}, {5} ", id, xOffset, yOffset, zOffset, item.Hue, lightsource); cl.Append("}"); if (complexcount % 3 == 0) { cl.AppendFormat("{0}\n\t\t\t", cc.ToString()); cc.Length = 0; cc.Append("// "); } if (generateTest) AddIdentifyAddOnComponent(IdentifyAddon, id, xOffset, yOffset, zOffset, item.Hue, -1, string.Format("({0},{1}): {2}, {3}, {4}", m_TotalComponents, id, xOffset, yOffset, zOffset), 0); } } else // Add data to static table { m_SimpleComponents++; m_TotalComponents++; sc.AppendFormat("{0}\t", m_TotalComponents); simplecount++; if (simplecount > 1) sl.Append(", "); sl.Append("{"); sl.AppendFormat("{0}, {1}, {2}, {3}", id, xOffset, yOffset, zOffset); sl.Append("}"); if (simplecount % 3 == 0) { sl.AppendFormat("{0}\n\t\t\t", sc.ToString()); sc.Length = 0; sc.Append("// "); } if (generateTest) AddIdentifyAddOnComponent(IdentifyAddon, id, xOffset, yOffset, zOffset, item.Hue, -1, string.Format("({0},{1}): {2}, {3}, {4}", m_TotalComponents, id, xOffset, yOffset, zOffset), 0); } } if (sc.Length > 4) sl.AppendFormat("{0}\n", sc.ToString()); if (cc.Length > 4) cl.AppendFormat("{0}\n", cc.ToString()); if (m_SimpleComponents > 0) sl.Append("\t\t};\n\n"); if (m_ComplexComponents > 0) cl.Append("\t\t};\n\n"); string output = m_Template.Replace("{name}", name); output = output.Replace("{simplelist}", m_SimpleComponents > 0 ? sl.ToString() : ""); output = output.Replace("{simplecomponentscode}", m_SimpleComponents > 0 ? m_SimpleCode : ""); output = output.Replace("{complexlist}", m_ComplexComponents > 0 ? cl.ToString() : ""); output = output.Replace("{complexcomponentscode}", m_ComplexComponents > 0 ? m_ComplexCode : ""); output = output.Replace("{namedcomponentscode}", m_NamedComponents > 0 ? nc.ToString() : ""); output = output.Replace("{complexnamecomponentscode}", (m_ComplexComponents > 0 || m_NamedComponents > 0) ? m_ComplexNameCode : ""); output = output.Replace("{namespace}", ns); StreamWriter writer = null; string path = null; if (m_CustomOutputDirectory != null) path = Path.Combine(m_CustomOutputDirectory, string.Format(@"TheBox\{0}Addon.cs", name)); else path = Path.Combine(Core.BaseDirectory, string.Format(@"TheBox\{0}Addon.cs", name)); fail = false; try { string folder = Path.GetDirectoryName(path); if (!Directory.Exists(folder)) { Directory.CreateDirectory(folder); } writer = new StreamWriter(path, false); writer.Write(output); } catch { from.SendMessage(0x40, "An error occurred when writing the file."); fail = true; } finally { if (writer != null) writer.Close(); } if (!fail) { from.SendMessage(0x40, "Script saved to {0}", path); from.SendMessage(0x40, "Total components in AddOn: {0}", m_TotalComponents); if (generateTest && IdentifyAddon != null) { from.SendMessage(0x37, "Now target a land tile to place a your addon."); from.Target = new InternalTarget(IdentifyAddon); } } }
private static void PickerCallback( Mobile from, Map map, Point3D start, Point3D end, object state ) { object[] args = state as object[]; if ( start.X > end.X ) { int x = start.X; start.X = end.X; end.X = x; } if ( start.Y > end.Y ) { int y = start.Y; start.Y = end.Y; end.Y = y; } Rectangle2D bounds = new Rectangle2D( start, end ); string name = args[ 0 ] as string; string ns = args[ 1 ] as string; bool items = (bool) args[ 2 ]; bool statics = (bool) args[ 3 ]; bool range = (bool) args[ 4 ]; sbyte min = sbyte.MinValue; sbyte max = sbyte.MaxValue; try { min = sbyte.Parse( args[ 5 ] as string ); } catch {} try { max = sbyte.Parse( args[ 6 ] as string ); } catch {} if ( max < min ) { sbyte temp = max; max = min; min = temp; } Hashtable tiles = new Hashtable(); if ( statics ) { for ( int x = start.X; x <= end.X; x++ ) { for ( int y = start.Y; y <= end.Y; y++ ) { ArrayList list = map.GetTilesAt( new Point2D( x, y ), items, false, statics ); if ( range ) { ArrayList remove = new ArrayList(); foreach ( Tile t in list ) { if ( t.Z < min || t.Z > max ) remove.Add( t ); } foreach( Tile t in remove ) list.Remove( t ); } if ( list != null && list.Count > 0 ) { tiles[ new Point2D( x, y ) ] = list; } } } } IPooledEnumerable en = map.GetItemsInBounds( bounds ); ArrayList target = new ArrayList(); bool fail = false; try { foreach( object o in en ) { Static s = o as Static; if ( s == null ) continue; if ( range && ( s.Z < min || s.Z > max ) ) continue; target.Add( o ); } } catch ( Exception err ) { Console.WriteLine( err.ToString() ); from.SendMessage( 0x40, "The targeted items have been modified. Please retry." ); fail = true; } finally { en.Free(); } if ( fail ) return; if ( target.Count == 0 && tiles.Keys.Count == 0 ) { from.SendMessage( 0x40, "No items have been selected" ); return; } // Get center Point3D center = new Point3D(); center.Z = 127; int x1 = bounds.End.X; int y1 = bounds.End.Y; int x2 = bounds.Start.X; int y2 = bounds.Start.Y; // Get correct bounds foreach( Static item in target ) { if ( item.Z < center.Z ) { center.Z = item.Z; } x1 = Math.Min( x1, item.X ); y1 = Math.Min( y1, item.Y ); x2 = Math.Max( x2, item.X ); y2 = Math.Max( y2, item.Y ); } foreach( Point2D p in tiles.Keys ) { ArrayList list = tiles[ p ] as ArrayList; foreach( Tile t in list ) { if ( t.Z < center.Z ) { center.Z = t.Z; } } x1 = Math.Min( x1, p.X ); y1 = Math.Min( y1, p.Y ); x2 = Math.Max( x2, p.X ); y2 = Math.Max( y2, p.Y ); } center.X = x1 + ( ( x2 - x1 ) / 2 ); center.Y = y1 + ( ( y2 - y1 ) / 2 ); // Build items System.Text.StringBuilder sb = new System.Text.StringBuilder(); // Statics foreach( Point2D p in tiles.Keys ) { ArrayList list = tiles[ p ] as ArrayList; int xOffset = p.X - center.X; int yOffset = p.Y - center.Y; foreach( Tile t in list ) { int zOffset = t.Z - center.Z; int id = t.ID - 16384; sb.AppendFormat( "\t\t\tAddComponent( new AddonComponent( {0} ), {1}, {2}, {3} );\n", id, xOffset, yOffset, zOffset ); } } sb.AppendFormat( "\t\t\tAddonComponent ac;\n" ); foreach( Static item in target ) { int xOffset = item.X - center.X; int yOffset = item.Y - center.Y; int zOffset = item.Z - center.Z; int id = item.ItemID; sb.AppendFormat( "\t\t\tac = new AddonComponent( {0} );\n", item.ItemID ); if ( ( item.ItemData.Flags & TileFlag.LightSource ) == TileFlag.LightSource ) { sb.AppendFormat( "\t\t\tac.Light = LightType.{0};\n", item.Light.ToString() ); } if ( item.Hue != 0 ) { sb.AppendFormat( "\t\t\tac.Hue = {0};\n", item.Hue ); } if ( item.Name != null ) { sb.AppendFormat( "\t\t\tac.Name = \"{0}\";\n", item.Name ); } sb.AppendFormat( "\t\t\tAddComponent( ac, {0}, {1}, {2} );\n", xOffset, yOffset, zOffset ); } string output = m_Template.Replace( "{name}", "AG_" + name ); output = output.Replace( "{namespace}", ns ); output = output.Replace( "{components}", sb.ToString() ); StreamWriter writer = null; string path = null; if ( m_CustomOutputDirectory != null ) path = Path.Combine( m_CustomOutputDirectory, string.Format( @"Items\{0}Addon.cs", name ) ); else path = Path.Combine( Core.BaseDirectory, string.Format( @"Items\{0}Addon.cs", name ) ); fail = false; try { string folder = Path.GetDirectoryName( path ); if ( ! Directory.Exists( folder ) ) { Directory.CreateDirectory( folder ); } writer = new StreamWriter( path, false ); writer.Write( output ); } catch { from.SendMessage( 0x40, "An error occurred when writing the file." ); fail = true; } finally { if ( writer != null ) writer.Close(); } if ( ! fail ) { from.SendMessage( 0x40, "Script saved to {0}", path ); } }
private static void PickerCallback(Mobile from, Map map, Point3D start, Point3D end, object state) { object[] args = state as object[]; string name = args[0] as string; // house name string description = args[1] as string; // house description bool items = false; // we capture items separatly bool statics = true; // we alwaye want statics when capturing a house bool land = false; // we never land tiles when capturing a house bool raw = (bool)args[2]; // raw capture will ignore BaseHouse info bool no_foundation = (bool)args[3]; // remove the foundation bool range = (bool)args[4]; // specify Z bool patch = (bool)args[7]; // create a FixerAddon to patch missing tiles (expensive, don't use unless you must) BaseHouse houseInRect = null; // when not doing raw capture, we extract data directly from the house // normalize bounding rect if (start.X > end.X) { int x = start.X; start.X = end.X; end.X = x; } if (start.Y > end.Y) { int y = start.Y; start.Y = end.Y; end.Y = y; } // if we have a house here and we're not in 'raw capture' mode, extract // the actual multi rect instead of leaving it up to the user ;p switching_to_Raw_mode: if (raw == false) { // check each tile looking for a house for (int ix = start.X; ix < end.X; ix++) { for (int iy = start.Y; iy < end.Y; iy++) { Point3D point = new Point3D(ix, iy, 16); houseInRect = BaseHouse.FindHouseAt(point, from.Map, 16); if (houseInRect != null) { // get the *real* location/dimentions from the multi from.SendMessage(0x40, "Progress: Found a house at this location, extracting info."); MultiComponentList mcl = houseInRect.Components; int x = houseInRect.X + mcl.Min.X; int y = houseInRect.Y + mcl.Min.Y; start.X = x; end.X = start.X + mcl.Width; start.Y = y; end.Y = start.Y + mcl.Height; // for houses with plots, use the plot dimentions if (houseInRect is HouseFoundation) { // patch width based on plot size and not multi int MultiID = houseInRect.ItemID; int width = 0; int height = 0; StaticHouseHelper.GetFoundationSize(MultiID, out width, out height); end.X = start.X + width; end.Y = start.Y + height; } goto exit_house_info; } } } // if we can't find a house here, switch to raw capture mode if (houseInRect == null) { from.SendMessage(0x40, "Info: No house at this location, switching to Raw mode."); raw = true; goto switching_to_Raw_mode; } } // we're in raw capture mode, help the user by snapping to the next valid plot size // if the rect they selected is not a valid size else { if (!StaticHouseHelper.IsFoundationSizeValid(end.X - start.X, end.Y - start.Y)) { int tempWidth = end.X - start.X; int tempHeight = end.Y - start.Y; int ix = 0, iy = 0; while (true) { if (StaticHouseHelper.IsFoundationSizeValid(tempWidth + ix, tempHeight)) { end.X += ix; from.SendMessage(0x40, String.Format("Info: Snapping to next leagal X plot size.")); goto exit_house_info; } else if (StaticHouseHelper.IsFoundationSizeValid(tempWidth, tempHeight + iy)) { end.Y += iy; from.SendMessage(0x40, String.Format("Info: Snapping to next leagal Y plot size.")); goto exit_house_info; } if (ix == 18 && iy == 18) break; // we should exit before hitting this case if (ix < 18) ix++; // next valid X if (iy < 18) iy++; // next valid Y } } } // we now have the 'perfect' rect for BaseHouse being captured. exit_house_info: // do we have a valid plot size? if (!StaticHouseHelper.IsFoundationSizeValid(end.X - start.X, end.Y - start.Y)) { from.SendMessage(0x22, "Error: House size " + Convert.ToString(end.X - start.X) + "x" + Convert.ToString(end.Y - start.Y) + " is invalid!"); from.SendGump(new InternalGump(from, (object[])state)); return; } else from.SendMessage(0x40, String.Format("Info: Selected plot size {0}x{1}.", end.X - start.X, end.Y - start.Y)); // calc price based on plot size // THIS is the portion an NPC architect will refund... not the tile(plot size) assessment // added below. int BasePrice = StaticHouseHelper.GetBasePrice(end.X - start.X, end.Y - start.Y); //pla: Check if the house blueprint file already exists, and if not then create one if (!Directory.Exists("Data")) Directory.CreateDirectory("Data"); if (!File.Exists(StaticHouseHelper.BlueprintDatabase)) using (XmlTextWriter writer = new XmlTextWriter(StaticHouseHelper.BlueprintDatabase, System.Text.Encoding.Unicode)) { writer.WriteStartElement("StaticHousing"); writer.WriteEndElement(); } //pla: Create document object for manipulation XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(StaticHouseHelper.BlueprintDatabase); if (xmlDoc == null) return; // version of this XML format // do not confuse this version with the version of the house. XmlNode root = xmlDoc.FirstChild; double formatVersion = 1.0; XmlAttributeCollection attrColl = root.Attributes; XmlAttribute attr = null; if (attrColl != null) attr = (XmlAttribute)attrColl.GetNamedItem("Version"); if (attr == null) { // we don't have a version stamp, add one XmlNode vattr = xmlDoc.CreateNode(XmlNodeType.Attribute, "Version", ""); vattr.Value = formatVersion.ToString(); root.Attributes.SetNamedItem(vattr); } // a new house XmlElement newHouse = xmlDoc.CreateElement("HouseID"); Rectangle2D bounds = new Rectangle2D(start, end); // house name XmlElement nameElement = xmlDoc.CreateElement("id"); nameElement.InnerText = name; newHouse.AppendChild(nameElement); // house description // a deed name is constructed as follows: // "deed to a " + sh.Description // an example name: "deed to a marble house with patio" XmlElement descriptionElement = xmlDoc.CreateElement("Description"); if (description != null && description.Length > 0) descriptionElement.InnerText = description; else descriptionElement.InnerText = "(none)"; newHouse.AppendChild(descriptionElement); // version of the house. // do not confuse with the version of this XML format // this is not the XML format of the house either, it's the construction version // Displayed in the House Gump. double houseVersion = 1.0; XmlElement houseVersionElement = xmlDoc.CreateElement("Version"); houseVersionElement.InnerText = houseVersion.ToString(); newHouse.AppendChild(houseVersionElement); // Date/Time this version of this house was captured // Displayed in the House Gump. DateTime CaptureDate = DateTime.Now; XmlElement CaptureElement = xmlDoc.CreateElement("Capture"); CaptureElement.InnerText = CaptureDate.ToString(); newHouse.AppendChild(CaptureElement); // Record region info // we will also capture the (HouseRegion) region info. We don't like the dumb // complete-plot-is-the-region system introduced with Custom Housing, but prefer // the old-school OSI bodle where the region is an array of well defined rects. // we use the rect editing tools on copy1 of the Custom House, then recapture to // record the region info if (raw == false && houseInRect != null) { Region r = houseInRect.Region; if (!(r == null || r.Coords == null || r.Coords.Count == 0)) { ArrayList c = r.Coords; XmlElement regionElement = xmlDoc.CreateElement("Region"); XmlElement rectElement = null; for (int i = 0; i < c.Count; i++) { if (c[i] is Rectangle2D) { int width = ((Rectangle2D)(c[i])).Width; int height = ((Rectangle2D)(c[i])).Height; int x = ((Rectangle2D)(c[i])).Start.X - start.X; int y = ((Rectangle2D)(c[i])).Start.Y - start.Y; if (x < 0) x = 0; if (y < 0) y = 0; XmlElement CoordsElement = xmlDoc.CreateElement("Rectangle2D"); rectElement = xmlDoc.CreateElement("x"); rectElement.InnerText = x.ToString(); CoordsElement.AppendChild(rectElement); rectElement = xmlDoc.CreateElement("y"); rectElement.InnerText = y.ToString(); CoordsElement.AppendChild(rectElement); rectElement = xmlDoc.CreateElement("width"); rectElement.InnerText = width.ToString(); CoordsElement.AppendChild(rectElement); rectElement = xmlDoc.CreateElement("height"); rectElement.InnerText = height.ToString(); CoordsElement.AppendChild(rectElement); regionElement.AppendChild(CoordsElement); } } newHouse.AppendChild(regionElement); } } sbyte min = sbyte.MinValue; sbyte max = sbyte.MaxValue; try { min = sbyte.Parse(args[5] as string); } catch (Exception ex) { EventSink.InvokeLogException(new LogExceptionEventArgs(ex)); } try { max = sbyte.Parse(args[6] as string); } catch (Exception ex) { EventSink.InvokeLogException(new LogExceptionEventArgs(ex)); } if (max < min) { sbyte temp = max; max = min; min = temp; } Hashtable tiles = new Hashtable(); // (x == end.X || y == end.Y) will be steps or other deco outside the plot // see below where we output statics and set TileType 'flags' if (statics) { for (int x = start.X; x <= end.X; x++) { for (int y = start.Y; y <= end.Y; y++) { ArrayList list = map.GetTilesAt(new Point2D(x, y), items, land, statics); if (range) { ArrayList remove = new ArrayList(); foreach (Tile t in list) { if (t.Z < min || t.Z > max) remove.Add(t); } foreach (Tile t in remove) list.Remove(t); } if (list != null && list.Count > 0) { tiles[new Point2D(x, y)] = list; } } } } // we increase the bounds by one here to match the way we scan for static above, that is: including end.X and end.Y // the end.X and end.Y allows us to pick up things on the steps, and the house sign hanger Rectangle2D iBounds = new Rectangle2D(bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1); IPooledEnumerable en = map.GetItemsInBounds(iBounds); ArrayList target = new ArrayList(); bool foundSign = false; // info pulled from captured house DateTime BuiltOn = DateTime.MaxValue; string OriginalOwnerName = "(unknown)"; string OriginalOwnerAccount = "(unknown)"; Serial OriginalOwnerSerial = Serial.MinusOne; Point3D SignLocation = Point3D.Zero; // not used int SignHangerGraphic = 0xB98; // default int SignpostGraphic = 0x09; // default try { // (x == end.X || y == end.Y) will be steps or other deco outside the plot // see below where we output statics and set TileType 'flags' foreach (object o in en) { // remove all doors if (o is BaseDoor) { from.SendMessage(0x40, "Progress: removing door."); continue; } // Remove SignHanger from the outside of a Custom House // we look for it at a particular location if (raw == false && houseInRect != null && houseInRect is HouseFoundation == true) if (IsSignHanger(o) && (o as Item).Y == bounds.Y + bounds.Height) { from.SendMessage(0x40, "Progress: removing sign hanger."); SignHangerGraphic = (o as Item).ItemID; continue; } // Remove Signpost from the outside of a Custom House // we look for it at a particular location if (raw == false && houseInRect != null && houseInRect is HouseFoundation == true) if (IsSignpost(o) && (o as Item).Y == bounds.Y + bounds.Height - 1) { from.SendMessage(0x40, "Progress: removing Signpost."); SignpostGraphic = (o as Item).ItemID; continue; } // any BaseHouse if (o is HouseSign) { // suck the meaningful info from the house sign from.SendMessage(0x40, "Progress: Processing house sign."); HouseSign sign = o as HouseSign; BaseHouse house = sign.Owner; if (house != null) { // from house BuiltOn = house.BuiltOn; } // from sign OriginalOwnerName = sign.OriginalOwner.Name; OriginalOwnerAccount = sign.OriginalOwner.Account.ToString(); OriginalOwnerSerial = sign.OriginalOwner.Serial; SignLocation = sign.Location; foundSign = true; continue; } // GM built or OSI town static structure if (o is StaticHouseSign) { // suck the meaningful info from the house sign from.SendMessage(0x40, "Progress: Processing static house sign."); StaticHouseSign sign = o as StaticHouseSign; // from sign BuiltOn = sign.BuiltOn; OriginalOwnerName = sign.OriginalOwner.Name; OriginalOwnerAccount = sign.OriginalOwner.Account.ToString(); OriginalOwnerSerial = sign.OriginalOwner.Serial; SignLocation = sign.Location; foundSign = true; continue; } // outside the rect? if (range && ((o as Static).Z < min || (o as Static).Z > max)) continue; target.Add(o); } // on OSI houses the sign falls outside the multi-rect if (raw == false && houseInRect != null && foundSign == false) { // suck the meaningful info from the house sign from.SendMessage(0x40, "Progress: Processing house sign."); HouseSign sign = houseInRect.Sign; BaseHouse house = sign.Owner; if (house != null) { // from house BuiltOn = house.BuiltOn; } // from sign OriginalOwnerName = sign.OriginalOwner.Name; OriginalOwnerAccount = sign.OriginalOwner.Account.ToString(); OriginalOwnerSerial = sign.OriginalOwner.Serial; SignLocation = sign.Location; foundSign = true; } } catch (Exception er) { LogHelper.LogException(er); Console.WriteLine(er.ToString()); from.SendMessage(0x40, "Info: The targeted items have been modified. Please retry."); return; } finally { en.Free(); } // captured houses need a sign. for static houses, [add StaticHouseSign and set props // we also use the location if the sign as the location for the real sign during construction if (foundSign == false) { from.SendMessage(0x22, "Warning: No StaticHouseSign found for static house."); from.SendMessage(0x22, "Warning: [add StaticHouseSign and set props."); // don't fail.. assume the XML will be hand edited } if (target.Count == 0 && tiles.Keys.Count == 0) { from.SendMessage(0x22, "Error: No items have been selected."); return; } /* -- save the house builder / designed info -- * BuiltOn = sign.BuiltOn; * OriginalOwnerName = sign.OriginalOwner.Name; * OriginalOwnerAccount = sign.OriginalOwner.Account.ToString(); * OriginalOwnerSerial = sign.OriginalOwner.Serial; * SignLocation = sign.Location; */ // Date/Time this version of this house was created // Displayed in the House Gump as the revision date XmlElement BuiltOnElement = xmlDoc.CreateElement("BuiltOn"); CaptureElement.InnerText = BuiltOn.ToString(); newHouse.AppendChild(CaptureElement); // OriginalOwnerName // Displayed in the House Gump. XmlElement OriginalOwnerNameElement = xmlDoc.CreateElement("OriginalOwnerName"); OriginalOwnerNameElement.InnerText = OriginalOwnerName.ToString(); newHouse.AppendChild(OriginalOwnerNameElement); // OriginalOwnerAccount XmlElement OriginalOwnerAccountElement = xmlDoc.CreateElement("OriginalOwnerAccount"); OriginalOwnerAccountElement.InnerText = OriginalOwnerAccount.ToString(); newHouse.AppendChild(OriginalOwnerAccountElement); // OriginalOwnerSerial // Displayed in the House Gump as the 'designer's licence number' XmlElement OriginalOwnerSerialElement = xmlDoc.CreateElement("OriginalOwnerSerial"); OriginalOwnerSerialElement.InnerText = OriginalOwnerSerial.ToString(); newHouse.AppendChild(OriginalOwnerSerialElement); // SignLocation // not used XmlElement SignLocationElement = xmlDoc.CreateElement("SignLocation"); SignLocationElement.InnerText = "(unused)" + SignLocation.ToString(); newHouse.AppendChild(SignLocationElement); // SignHangerGraphic XmlElement SignHangerGraphicElement = xmlDoc.CreateElement("SignHangerGraphic"); SignHangerGraphicElement.InnerText = SignHangerGraphic.ToString(); newHouse.AppendChild(SignHangerGraphicElement); // SignpostGraphic XmlElement SignpostGraphicElement = xmlDoc.CreateElement("SignpostGraphic"); SignpostGraphicElement.InnerText = SignpostGraphic.ToString(); newHouse.AppendChild(SignpostGraphicElement); // Get center Point3D center = new Point3D(); center.Z = 127; int x1 = bounds.End.X; int y1 = bounds.End.Y; int x2 = bounds.Start.X; int y2 = bounds.Start.Y; // Get correct bounds foreach (object o in target) { Item item = o as Item; if (item == null) continue; // don't factor these tiles as they are outside the bounding rect. // (steps most likely) if (item.X >= end.X || item.Y >= end.Y) continue; if (item.Z < center.Z) { center.Z = item.Z; } x1 = Math.Min(x1, item.X); y1 = Math.Min(y1, item.Y); x2 = Math.Max(x2, item.X); y2 = Math.Max(y2, item.Y); } // Get correct bounds foreach (Point2D p in tiles.Keys) { ArrayList list = tiles[p] as ArrayList; // don't factor these tiles as they are outside the bounding rect. // (steps most likely) if (p.X >= end.X || p.Y >= end.Y) continue; foreach (Tile t in list) { if (t.Z < center.Z) { center.Z = t.Z; } } x1 = Math.Min(x1, p.X); y1 = Math.Min(y1, p.Y); x2 = Math.Max(x2, p.X); y2 = Math.Max(y2, p.Y); } center.X = x1 + ((x2 - x1) / 2); center.Y = y1 + ((y2 - y1) / 2); // width int PlotWidth = end.X - start.X; XmlElement widthElement = xmlDoc.CreateElement("Width"); widthElement.InnerText = PlotWidth.ToString(); newHouse.AppendChild(widthElement); // height int PlotHeight = end.Y - start.Y; XmlElement heightElement = xmlDoc.CreateElement("Height"); heightElement.InnerText = PlotHeight.ToString(); newHouse.AppendChild(heightElement); XmlElement multiElement = xmlDoc.CreateElement("Multi"); XmlElement tempElement = null; ArrayList tileTable = new ArrayList(); // Statics - add to master list foreach (Point2D p in tiles.Keys) { ArrayList list = tiles[p] as ArrayList; int xOffset = p.X - center.X; int yOffset = p.Y - center.Y; StaticHouseHelper.TileType flags = StaticHouseHelper.TileType.Normal; // mark these tiles as existing outside the bounding rect (steps most likely) if (p.X >= end.X || p.Y >= end.Y) flags |= StaticHouseHelper.TileType.OutsideRect; foreach (Tile t in list) { int zOffset = t.Z - center.Z; int id = t.ID & 0x3FFF; Unit unit = new Unit(xOffset, yOffset, zOffset, id, flags); bool add = true; foreach (Unit existing in tileTable) { if (existing.m_xOffset == unit.m_xOffset && existing.m_yOffset == unit.m_yOffset && existing.m_zOffset == unit.m_zOffset) { if (existing.m_id == unit.m_id) { add = false; break; } else { if (patch == true) unit.m_flags |= StaticHouseHelper.TileType.Overlapped; } } } // only add if unique if (add == true) tileTable.Add(unit); else from.SendMessage(0x40, "Progress: Ignoring duplicate tile."); } } // Items - add to master list foreach (Object o in target) { Item item = o as Item; if (item == null) continue; int xOffset = item.X - center.X; int yOffset = item.Y - center.Y; int zOffset = item.Z - center.Z; int id = item.ItemID; StaticHouseHelper.TileType flags = StaticHouseHelper.TileType.Normal; // aftermarket addons are 'patched' later if (item is AddonComponent || item is StaticHouseHelper.FixerAddon) flags |= StaticHouseHelper.TileType.Patch; // mark these tiles as existing outside the bounding rect (steps most likely) if (item.X >= end.X || item.Y >= end.Y) flags |= StaticHouseHelper.TileType.OutsideRect; Unit unit = new Unit(xOffset, yOffset, zOffset, id, flags); bool add = true; foreach (Unit existing in tileTable) { if (existing.m_xOffset == unit.m_xOffset && existing.m_yOffset == unit.m_yOffset && existing.m_zOffset == unit.m_zOffset) { if (existing.m_id == unit.m_id) { if ((unit.m_flags & StaticHouseHelper.TileType.Patch) != 0) // if the one we are trying the add is a patch.. existing.m_flags |= StaticHouseHelper.TileType.Patch; // convert the existing one to the patch and don't add this one add = false; break; } else { if (patch == true) unit.m_flags |= StaticHouseHelper.TileType.Overlapped; } } } // only add if unique if (add == true) tileTable.Add(unit); else from.SendMessage(0x40, "Progress: Ignoring duplicate tile."); } // Preprocess the list - pass I ArrayList removeList = new ArrayList(); foreach (Unit unit in tileTable) { Item o = new Item(unit.m_id); // remove the foundation and fixup the if (no_foundation == true) { if (unit.m_zOffset == 0) { // remove foundation tiles if (unit.m_xOffset == start.X - center.X || unit.m_yOffset == start.Y - center.Y || unit.m_xOffset == (end.X - center.X) - 1 || unit.m_yOffset == (end.Y - center.Y) - 1) removeList.Add(unit); // steps if ((unit.m_flags & StaticHouseHelper.TileType.OutsideRect) != 0) removeList.Add(unit); } // dirt tiles else if (unit.m_zOffset == 7 && unit.m_id == 12788) removeList.Add(unit); else // move all tiles down 7 // bug: when we move down 7, tiles at 0 get clipped. unit.m_zOffset -= 7; } // Remove SignHanger on outside of an OSI house if (raw == false && houseInRect != null && houseInRect is HouseFoundation == false) if (IsSignHanger(o) && unit.m_yOffset * 2 == PlotHeight) { from.SendMessage(0x40, "Progress: removing sign hanger."); removeList.Add(unit); } // remove all doors if (o is BaseDoor) { from.SendMessage(0x40, "Progress: removing door."); removeList.Add(unit); } } // preprocess - pass II // remove / process all things found in pass I foreach (Unit unit in removeList) { if (tileTable.Contains(unit)) tileTable.Remove(unit); } // house price // price is a base price based on the plot size + a per tile cost (PTC). // PTC is greater as the plot gets bigger .. this will encourage smaller houses, and 'tax' the big land hogs. // the numbers were heuristically derived by looking at a very large and complex 18x18 and deciding that we wanted to add // about 1,000,000 to the price, then dividing that by the number of tiles on this house (1130) and came up with a cost of approx // 885 per tile. We then use the logic 18x18 = 36 and 885/32 == 24 which gives us the base PTC. We then multiply // 24 * (width + height) to get the actual PTC for this house. // so using this system above, a small 8x8 house has a PTC of 384 where a large 18x18 has a pertile cost of 864 int price = BasePrice + (tileTable.Count * (24 * (PlotWidth + PlotWidth))); XmlElement priceElement = xmlDoc.CreateElement("Price"); priceElement.InnerText = BasePrice.ToString(); newHouse.AppendChild(priceElement); // Okay, write out all tile data foreach (Unit unit in tileTable) { XmlElement singleMultiElement = xmlDoc.CreateElement("Graphic"); tempElement = xmlDoc.CreateElement("id"); tempElement.InnerText = unit.m_id.ToString(); singleMultiElement.AppendChild(tempElement); tempElement = xmlDoc.CreateElement("x"); tempElement.InnerText = unit.m_xOffset.ToString(); singleMultiElement.AppendChild(tempElement); tempElement = xmlDoc.CreateElement("y"); tempElement.InnerText = unit.m_yOffset.ToString(); singleMultiElement.AppendChild(tempElement); tempElement = xmlDoc.CreateElement("z"); tempElement.InnerText = unit.m_zOffset.ToString(); singleMultiElement.AppendChild(tempElement); tempElement = xmlDoc.CreateElement("flags"); tempElement.InnerText = ((int)unit.m_flags).ToString(); singleMultiElement.AppendChild(tempElement); multiElement.AppendChild(singleMultiElement); } // stats /*int total = tileTable.Count, deco = 0, patch = 0; foreach (Unit existing in tileTable) { //StaticHouseHelper.TileType flags = StaticHouseHelper.TileType.Normal; //StaticHouseHelper.TileType.OutsideRect; //StaticHouseHelper.TileType.Overlapped; if (existing.m_flags & StaticHouseHelper.TileType.OutsideRect != 0) deco++; if (existing.m_flags & StaticHouseHelper.TileType.Overlapped != 0) patch++; } from.SendMessage(String.Format("{0} total tiles of which {1} were outside the foundation and {2} were patch tiles.",total, deco, patch));*/ newHouse.AppendChild(multiElement); xmlDoc["StaticHousing"].AppendChild(newHouse); xmlDoc.Save(StaticHouseHelper.BlueprintDatabase); from.SendMessage("Blueprint creation successful"); // give the GM a deed to test with from.SendMessage("A deed has been placed in your backpack"); from.AddToBackpack( new StaticDeed(name, description)); }
private static void PickerCallback( Mobile from, Map map, Point3D start, Point3D end, object state ) { object[] args = state as object[]; if ( start.X > end.X ) { int x = start.X; start.X = end.X; end.X = x; } if ( start.Y > end.Y ) { int y = start.Y; start.Y = end.Y; end.Y = y; } Rectangle2D bounds = new Rectangle2D( start, end ); string name = args[ 0 ] as string; string ns = args[ 1 ] as string; bool items = (bool) args[ 2 ]; bool statics = (bool) args[ 3 ]; bool range = (bool) args[ 4 ]; sbyte min = sbyte.MinValue; sbyte max = sbyte.MaxValue; try { min = sbyte.Parse( args[ 5 ] as string ); } catch {} try { max = sbyte.Parse( args[ 6 ] as string ); } catch {} if ( max < min ) { sbyte temp = max; max = min; min = temp; } Hashtable tiles = new Hashtable(); if ( statics ) { for ( int x = start.X; x <= end.X; x++ ) { for ( int y = start.Y; y <= end.Y; y++ ) { ArrayList list = map.GetTilesAt( new Point2D( x, y ), items, false, statics ); if ( range ) { ArrayList remove = new ArrayList(); foreach ( Tile t in list ) { if ( t.Z < min || t.Z > max ) remove.Add( t ); } foreach( Tile t in remove ) list.Remove( t ); } if ( list != null && list.Count > 0 ) { tiles[ new Point2D( x, y ) ] = list; } } } } IPooledEnumerable en = map.GetItemsInBounds( bounds ); ArrayList target = new ArrayList(); bool fail = false; try { foreach( object o in en ) { Static s = o as Static; if ( s == null ) continue; if ( range && ( s.Z < min || s.Z > max ) ) continue; target.Add( o ); } } catch ( Exception err ) { Console.WriteLine( err.ToString() ); from.SendMessage( 0x40, "The targeted items have been modified. Please retry." ); fail = true; } finally { en.Free(); } if ( fail ) return; if ( target.Count == 0 && tiles.Keys.Count == 0 ) { from.SendMessage( 0x40, "No items have been selected" ); return; } // Get center Point3D center = new Point3D(); center.Z = 127; int x1 = bounds.End.X; int y1 = bounds.End.Y; int x2 = bounds.Start.X; int y2 = bounds.Start.Y; // Get correct bounds foreach( Static item in target ) { if ( item.Z < center.Z ) { center.Z = item.Z; } x1 = Math.Min( x1, item.X ); y1 = Math.Min( y1, item.Y ); x2 = Math.Max( x2, item.X ); y2 = Math.Max( y2, item.Y ); } foreach( Point2D p in tiles.Keys ) { ArrayList list = tiles[ p ] as ArrayList; foreach( Tile t in list ) { if ( t.Z < center.Z ) { center.Z = t.Z; } } x1 = Math.Min( x1, p.X ); y1 = Math.Min( y1, p.Y ); x2 = Math.Max( x2, p.X ); y2 = Math.Max( y2, p.Y ); } center.X = x1 + ( ( x2 - x1 ) / 2 ); center.Y = y1 + ( ( y2 - y1 ) / 2 ); // Build items ArrayList itemlist = new ArrayList(); string toAdd = "0x1 0 0 1 0x0 0"; itemlist.Add( toAdd ); // Statics foreach( Point2D p in tiles.Keys ) { ArrayList list = tiles[ p ] as ArrayList; int xOffset = p.X - center.X; int yOffset = p.Y - center.Y; foreach( Tile t in list ) { int zOffset = t.Z - center.Z; int id = t.ID - 16384; toAdd = id.ToString() + " " + xOffset.ToString() + " " + yOffset.ToString() + " " + zOffset.ToString() + " 0x0 1"; itemlist.Add( toAdd ); } } foreach( Static item in target ) { int xOffset = item.X - center.X; int yOffset = item.Y - center.Y; int zOffset = item.Z - center.Z; int id = item.ItemID; toAdd = id.ToString() + " " + xOffset.ToString() + " " + yOffset.ToString() + " " + zOffset.ToString() + " 0x0 1"; itemlist.Add( toAdd ); } StreamWriter writer = null; string path = null; if ( m_CustomOutputDirectory != null ) path = Path.Combine( m_CustomOutputDirectory, string.Format( @"Items\{0}.txt", name ) ); else path = Path.Combine( Core.BaseDirectory, string.Format( @"Items\{0}.txt", name ) ); fail = false; try { string folder = Path.GetDirectoryName( path ); if ( ! Directory.Exists( folder ) ) { Directory.CreateDirectory( folder ); } writer = new StreamWriter( path, false ); for( int i = 0; i < itemlist.Count; ++i ) { writer.WriteLine( (string)itemlist[i] ); } } catch { from.SendMessage( 0x40, "An error occurred when writing the file." ); fail = true; } finally { if ( writer != null ) writer.Close(); } if ( ! fail ) { from.SendMessage( 0x40, "Script saved to {0}", path ); } }