public void SaveZone(bool backup) { if (BUSY && !backup) { if ( MessageBox.Show("Zone Edit is busy - save as backup?", "Warning!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes) { return; } backup = true; } BUSY = true; string path = (backup ? PATH + "_backup" : PATH); if (!backup) { //if (Directory.Exists(PATH + "_backup")) { // Directory.Delete(PATH + "_backup", true); //} Loading.Update("Initiating save process.."); Loading.ShowLoading(true); } else { if (Directory.Exists(PATH + "_backup")) { if (Directory.Exists(PATH + "_backup_bak")) { Directory.Delete(PATH + "_backup_bak", true); } Directory.Move(PATH + "_backup", PATH + "_backup_bak"); } } if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } #region datXXX.mpk int bwillow = 0; { string p = string.Format("dat{0}.mpk", ZoneID.ToString("D3")); string pp = path + "\\" + p; Loading.Update("Saving " + p + "..."); if (!Directory.Exists(pp)) { Directory.CreateDirectory(pp); } #region SECTOR.DAT { Loading.Update("Saving " + p + "... - sector.dat"); var s = new INIStreamer(string.Format("{0}\\SECTOR.DAT" + (backup ? ".bak" : ""), pp)); s.ReadIni(); s.Header.Clear(); s.SetItem("terrain", "scalefactor", TerrainScaleFactor.ToString()); s.SetItem("terrain", "offsetfactor", TerrainOffsetFactor.ToString()); //Rivers int rivers = 0; foreach (Polygon pl in Polygon.Polygons) { if (pl.Type != ePolygon.Water) { continue; } //Save this river string pre = string.Format("river{0:D2}", rivers); if (s.GetTopic(pre) != null) { s.RemTopic(pre); } s.SetItem(pre, "texture", pl.WTexture); s.SetItem(pre, "multitexture", pl.WMultiTexture); s.SetItem(pre, "flow", pl.WFlow.ToString()); s.SetItem(pre, "height", pl.WHeight.ToString()); s.SetItem(pre, "Tesselation", pl.WTesselation.ToString()); s.SetItem(pre, "type", pl.WType.ToString().ToUpper()); s.SetItem(pre, "bankpoints", (pl.Points.Count / 2).ToString()); s.SetItem(pre, "name", pre); for (int a = 0; a < pl.Points.Count; a += 2) { var x1 = (int)(pl.Points[a].X / 256); var y1 = (int)(pl.Points[a].Y / 256); var x2 = (int)(pl.Points[a + 1].X / 256); var y2 = (int)(pl.Points[a + 1].Y / 256); lock (pl.Points) { pl.Points[a] = new Vector2(x1 * 256, y1 * 256); pl.Points[a + 1] = new Vector2(x2 * 256, y2 * 256); } s.SetItem(pre, string.Format("left{0:D2}", a / 2), string.Format("{0},{1},0", x1, y1)); s.SetItem(pre, string.Format("right{0:D2}", a / 2), string.Format("{0},{1},0", x2, y2)); } rivers++; } s.SetItem("waterdefs", "num", rivers.ToString()); s.WriteIni(); } #endregion #region terrain.pcx/offset.pcx { Loading.Update("Saving " + p + "... - Heightmaps"); var terrain = new byte[256, 256]; var offset = new byte[256, 256]; for (int x = 0; x <= 255; x++) { for (int y = 0; y <= 255; y++) { int o = HeightMap[x, y] / TerrainOffsetFactor; if (o > 255) { o = 255; } int remaining = HeightMap[x, y] - o * TerrainOffsetFactor; int t = remaining / TerrainScaleFactor; if (t > 255) { t = 255; //some information may get lost } offset[x, y] = (byte)(o); terrain[x, y] = (byte)(t); } } PCXImage.Save(string.Format("{0}\\terrain.pcx", pp), terrain); PCXImage.Save(string.Format("{0}\\offset.pcx", pp), offset); } #endregion #region bound.csv { Loading.Update("Saving " + p + "... - Bounds"); var rs = new StreamWriter(string.Format("{0}\\bound.csv", pp)); foreach (Polygon pl in Polygon.Polygons) { if (pl.Type != ePolygon.Bounding) { continue; } //?, count, x,y, x2,y2, x3, y3, .. string vectors = ""; foreach (Vector2 v in pl.Points) { vectors += string.Format(",{0},{1}", (int)v.X, (int)v.Y); } rs.WriteLine("{0},{1}{2}", 0, pl.Points.Count, vectors); } rs.Flush(); rs.Close(); } #endregion #region water.pcx { Loading.Update("Saving " + p + "... - Water"); var data = new byte[256, 256]; for (int x = 0; x < 256; x++) { for (int y = 0; y < 256; y++) { int height = (HeightMap[x, y]); bool isUnderwater = false; int n = -1; foreach (Polygon pl in Polygon.Polygons) { if (pl.Type != ePolygon.Water) { continue; } if (!isUnderwater) { n++; } if (pl.WHeight < height) { continue; } //Check if point is inside var pnt = new Vector2(x, y); for (int idx = 0; idx < pl.Points.Count - 2; idx += 2) { Vector2 l1 = pl.Points[idx]; Vector2 r1 = pl.Points[idx + 1]; Vector2 l2 = pl.Points[idx + 2]; Vector2 r2 = pl.Points[idx + 3]; var _l1 = new Vector3(l1.X, l1.Y, 0); var _r1 = new Vector3(r1.X, r1.Y, 0); var _l2 = new Vector3(l2.X, l2.Y, 0); var _r2 = new Vector3(r2.X, r2.Y, 0); var rayStart = new Vector3(pnt.X * 256, pnt.Y * 256, 1.0f); var rayDir = new Vector3(0, 0, -1); IntersectInformation i; if (Geometry.IntersectTri(_l1, _r1, _l2, rayStart, rayDir, out i) || Geometry.IntersectTri(_r1, _r2, _l2, rayStart, rayDir, out i)) { isUnderwater = true; break; } } } data[x, y] = (byte)(isUnderwater ? n : 255); } } for (int i = 0; i < 2; i++) { WaterDilatation(data); } PCXImage.Save(string.Format("{0}\\water.pcx", pp), data); } #endregion #region nifs.csv { Loading.Update("Saving " + p + "... - NIFs"); var rs = new StreamWriter(string.Format("{0}\\nifs.csv", pp)); rs.WriteLine("Grid Nifs,,,Ambient,Merlin Data"); rs.WriteLine( "NIF,Textual Name,Filename,Only,Shadow,Color,Animate,Collide,Ground,MinAngle,MaxAngle,MinScale,MaxScale,Radius,LOD 1,LOD 2,LOD 3,LOD 4,Ref Height,Ref Width,Unique,Local,Terrain"); int id = 1; var clones = new List <Objects.NIF>(); foreach (Objects.NIF n in Objects.NIFs.Values) { clones.Add(n); } Objects.NIFs.Clear(); foreach (Objects.NIF n in clones) { n.ID = id; Objects.NIFs.Add(n.ID, n); rs.Write(id); rs.Write("," + n.FileName + "|" + n.Group); //name rs.Write("," + n.FileName); //filename rs.Write("," + 0); //ambient only rs.Write("," + 0); //merlin shadow data rs.Write("," + 0); //color rs.Write("," + (n.IsDoor ? 1 : 0)); //animate rs.Write("," + n.Collision); //collide rs.Write("," + 1); //on ground rs.Write("," + 0); //min angle rs.Write("," + 0); //max angle rs.Write("," + n.MinScale); //min scale rs.Write("," + n.MaxScale); //max scale rs.Write("," + n.CollideRadius); //radius rs.Write("," + 0); //lod1 rs.Write("," + 0); //lod2 rs.Write("," + 0); //lod3 rs.Write("," + 0); //lod4 rs.Write("," + 16); //ref height rs.Write("," + 16); //ref width rs.Write("," + 0); //unique rs.Write("," + 0); //local rs.Write("," + 0); //terrain rs.WriteLine(); id++; } rs.Flush(); rs.Close(); } #endregion #region fixtures.csv { Loading.Update("Saving " + p + "... - Fixtures"); var rs = new StreamWriter(string.Format("{0}\\fixtures.csv", pp)); rs.WriteLine("Fixtures,,,,,,,,NIF,Collide,,On,,,,,,,"); rs.WriteLine( "ID,NIF #,Textual Name,X,Y,Z,A,Scale,Collide,Radius,Animate,Ground,Flip,Cave,Unique ID, 3D Angle, 3D Axis X, 3D Axis Y, 3D Axis Z"); var freeIDs = new List <int>(); int id = 1; foreach (Objects.Fixture f in Objects.Fixtures) { if (f.NIF.FileName.ToLower().Contains("bwillow")) { bwillow++; } if (f.NIF.IsDoor) //lock ID { for (int i = id; i < f.ID; i++) { freeIDs.Add(i); } id = f.ID + 1; } else { if (freeIDs.Count > 0) { f.ID = freeIDs[0]; freeIDs.RemoveAt(0); } else { f.ID = id++; } } rs.Write(f.ID); rs.Write("," + f.NIF_ID); rs.Write("," + f.Name); rs.Write("," + f.X.ToString().Replace(',', '.')); rs.Write("," + f.Y.ToString().Replace(',', '.')); rs.Write("," + f.Z.ToString().Replace(',', '.')); rs.Write("," + 0); //A (angle?) rs.Write("," + f.Scale); rs.Write("," + f.NIF.Collision); //collide rs.Write("," + f.NIF.CollideRadius); //radius rs.Write("," + 0); //animate rs.Write("," + (f.OnGround ? 1 : 0)); //ground rs.Write("," + 0); //flip rs.Write("," + 0); //cave rs.Write("," + f.ID); //uid rs.Write("," + f.Rotation.ToString().Replace(',', '.')); //3d angle rs.Write("," + f.AxisX.ToString().Replace(',', '.')); //); //3d x rs.Write("," + f.AxisY.ToString().Replace(',', '.')); //3d y rs.Write("," + f.AxisZ.ToString().Replace(',', '.')); //3d z rs.WriteLine(); } rs.Flush(); rs.Close(); } #endregion #region zonejump.csv { Loading.Update("Saving " + p + "... - Zonejumps"); var rs = new StreamWriter(string.Format("{0}\\zonejump.csv", pp)); int index = 1; foreach (Zonejump j in Zonejump.Zonejumps) { //ID, Name, X, Y, X2, Y2, Z1, Z2, JID rs.Write(index++); rs.Write("," + j.Name); rs.Write("," + (int)j.First.X); rs.Write("," + (int)j.First.Y); rs.Write("," + (int)j.Second.X); rs.Write("," + (int)j.Second.Y); rs.Write("," + (int)j.First.Z); rs.Write("," + (int)j.Second.Z); rs.Write("," + j.ID); rs.WriteLine(); } rs.Flush(); rs.Close(); } #endregion #region lights.csv { Loading.Update("Saving " + p + "... - Lights"); var rs = new StreamWriter(string.Format("{0}\\lights.csv", pp)); foreach (Light l in Light.Lights) { //46473, 54362, 2910, 3 //X, Y, Z, Intensity rs.Write(l.X); rs.Write(", " + l.Y); rs.Write(", " + (l.Z + l.ZOffset)); rs.Write(", " + ((int)l.Color + ((l.Intensity - 1) * 10))); rs.WriteLine(); } rs.Flush(); rs.Close(); } #endregion } #endregion #region terXXX.mpk { string p = string.Format("ter{0}.mpk", ZoneID.ToString("D3")); string pp = path + "\\" + p; Loading.Update("Saving Textures.."); if (!Directory.Exists(pp)) { Directory.CreateDirectory(pp); } PatchMap.Save(pp + "\\", backup); } #endregion Loading.Update("Saving Sounds..."); SoundMgr.Save(path); if (!backup) { if (bwillow >= 2000) { MessageBox.Show("For every bwillow you will now have to close one Messagebox..!"); Loading.Update("Annoying you..."); for (int i = bwillow; i <= bwillow; i++) { MessageBox.Show("bwillow " + bwillow); } } } if (!backup) { LAST_SAVE = DateTime.UtcNow.Ticks; } GC.Collect(); Loading.Update("Done."); Loading.CloseLoading(); BUSY = false; }