private void ParseDeleste(string path) { FileStream file = null; StreamReader scanner = null; try { file = new FileStream(path, FileMode.Open, FileAccess.Read); scanner = new StreamReader(file, System.Text.Encoding.UTF8); } catch { Game.ThrowError(ErrorMode.NoDeresimuFile); return; } int d, MaxBlockNum = -1, ID = 1; char[] div = { ' ', ',', ':' }; DelesteGlobalData StaticData = new DelesteGlobalData(); Dictionary <int, DelesteBlockData> Blocks = new Dictionary <int, DelesteBlockData>(); double Time = 0, SongTime = 0, Speed = 1.0; byte[] Color = new byte[] { 255, 255, 255, 255 }; if (PlayerPrefs.HasKey("gamesync")) { SongTime = PlayerPrefs.GetFloat("gamesync") / 60; } while (scanner.Peek() != -1) { try { string dataLine = scanner.ReadLine(); string[] data = dataLine.Split(div, StringSplitOptions.RemoveEmptyEntries); if (data.Length > 1 && data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("BPM") || data[0].Substring(1).ToUpper().Equals("TEMPO"))) { StaticData.CurrentBPM = double.Parse(data[1]); } else if (data.Length > 1 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("OFFSET")) { Time += (int.Parse(data[1]) / 1000d); } else if (data.Length > 1 && data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("SONGOFFSET") || data[0].Substring(1).ToUpper().Equals("MUSICOFFSET") || data[0].Substring(1).ToUpper().Equals("BGMOFFSET"))) { Time -= (int.Parse(data[1]) / 1000d); } else if (data.Length > 1 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("ATTRIBUTE")) { if (data[1].ToUpper().Equals("CUTE") || data[1].ToUpper().Equals("CU") || data[1].Equals("1")) { Color = new byte[] { 255, 100, 200, 255 }; } else if (data[1].ToUpper().Equals("COOL") || data[1].ToUpper().Equals("CO") || data[1].Equals("2")) { Color = new byte[] { 85, 135, 255, 255 }; } else if (data[1].ToUpper().Equals("PASSION") || data[1].ToUpper().Equals("PA") || data[1].Equals("3")) { Color = new byte[] { 255, 220, 50, 255 }; } else if (data[1].ToUpper().Equals("ALL") || data[1].Equals("4")) { Color = new byte[] { 230, 255, 255, 255 }; } } else if (data.Length > 2 && data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("MEASURE") || data[0].Substring(1).ToUpper().Equals("MEAS") || data[0].Substring(1).ToUpper().Equals("MEA"))) { if (data[2].Contains("/")) { string[] numbers = data[2].Split(new char[] { '/' }); StaticData.Measure.Add(double.Parse(numbers[0]) / double.Parse(numbers[1])); } else { StaticData.Measure.Add(double.Parse(data[2])); } StaticData.MeasurePos.Add(double.Parse(data[1])); } else if (data.Length > 2 && (data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("CHANGEBPM") || data[0].Substring(1).ToUpper().Equals("CHANGETEMPO")))) { StaticData.ChangeBPM.Add(double.Parse(data[2])); StaticData.ChangeBPMPos.Add(double.Parse(data[1])); } else if (data.Length > 1 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("CHANGEATTRIBUTE")) { if (data[1].ToUpper().Equals("CUTE") || data[1].ToUpper().Equals("CU") || data[1].Equals("1")) { Color = new byte[] { 255, 100, 200, 255 }; } else if (data[1].ToUpper().Equals("COOL") || data[1].ToUpper().Equals("CO") || data[1].Equals("2")) { Color = new byte[] { 85, 135, 255, 255 }; } else if (data[1].ToUpper().Equals("PASSION") || data[1].ToUpper().Equals("PA") || data[1].Equals("3")) { Color = new byte[] { 255, 220, 50, 255 }; } else if (data[1].ToUpper().Equals("ALL") || data[1].Equals("4")) { Color = new byte[] { 230, 255, 255, 255 }; } } else if (data.Length > 1 && data[0].StartsWith("#") && (data[0].Substring(1).ToUpper().Equals("HISPEED") || data[0].Substring(1).ToUpper().Equals("HS"))) { Speed = double.Parse(data[1]); if (Speed <= 0) { throw new Exception(LocaleManager.instance.GetLocaleText("errordetail_speed")); } } /* * else if(data.Length > 2 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("HS2")) * { * if(double.Parse(data[2]) <= 0){ throw new Exception(LocaleManager.instance.GetLocaleText("errordetail_speed")); } * StaticData.HS2.Add(double.Parse(data[2])); * StaticData.HS2Pos.Add(double.Parse(data[1])); * } */ else if (data.Length > 2 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("DELAY")) { StaticData.Delay.Add(double.Parse(data[2])); StaticData.DelayPos.Add(double.Parse(data[1])); } else if (data.Length > 3 && data[0].StartsWith("#") && data[0].Substring(1).ToUpper().Equals("SCROLL")) { StaticData.Scroll.Add(new double[2] { double.Parse(data[2]) / 1000d, double.Parse(data[3]) / 1000d }); StaticData.ScrollPos.Add(double.Parse(data[1])); } else if (data.Length > 0 && data[0].StartsWith("#") && int.TryParse(data[0].Substring(1, 1), out d)) { int CurBlock = int.Parse(data[1]); if (!Blocks.ContainsKey(CurBlock)) { Blocks.Add(CurBlock, new DelesteBlockData(CurBlock)); } Blocks[CurBlock].DataLines.Add(dataLine); Blocks[CurBlock].Channel.Add(int.Parse(data[0].Substring(1))); Blocks[CurBlock].Color.Add(Color); Blocks[CurBlock].Speed.Add(Speed * Game.GlobalNoteSpeed); if (MaxBlockNum < CurBlock) { MaxBlockNum = CurBlock; } } } catch (Exception e) { Game.ThrowError(ErrorMode.NotemapSyntaxError, e.Message + e.StackTrace); Debug.Log(e); } } scanner.Close(); file.Close(); Game.Dispensor.CreateNote(0, 0, new Color32(255, 255, 255, 255), NoteInfo.SystemNoteStarter, FlickMode.None, (float)SongTime * 60, (float)Speed * Game.GlobalNoteSpeed, 1, 1, new List <int>()); for (int i = 0; i <= MaxBlockNum; i++) { if (Blocks.ContainsKey(i)) { Blocks[i].ParseBlock(Game, ref ID, ref Time, ref StaticData); } else { Time += ((240 / StaticData.CurrentBPM) * StaticData.BeatMultiplier); } } }
public void ParseBlock(GameManager Game, ref int ID, ref double Time, ref DelesteGlobalData StaticData) { int MaxBit = 1; List <int> DataIndex = new List <int>(), StartIndex = new List <int>(), EndIndex = new List <int>(); List <string[]> Datas = new List <string[]>(); // DataIndex, StartIndex, EndIndex의 초기화와 MaxBit의 갱신, 그리고 Datas의 값 대입을 수행합니다. for (int i = 0; i < DataLines.Count; i++) { Datas.Add(DataLines[i].Split(new char[] { ':' })); DataIndex.Add(-1); StartIndex.Add(0); EndIndex.Add(0); MaxBit = LCM(MaxBit, Datas[i][1].Length); } // MaxBit를 기반으로, 차례차례 데이터를 해석하고 추가하는 전체 과정입니다. for (int i = 0; i < MaxBit; i++) { // 선제적으로, 각종 큐들을 살펴보면서 조건을 만족한다면 데이터를 그에 맞춥니다. if (StaticData.Measure.Count > 0 && StaticData.MeasurePos[0] <= Measure + (i / (double)MaxBit)) { StaticData.BeatMultiplier = StaticData.Measure[0]; StaticData.Measure.RemoveAt(0); StaticData.MeasurePos.RemoveAt(0); } if (StaticData.ChangeBPM.Count > 0 && StaticData.ChangeBPMPos[0] <= Measure + (i / (double)MaxBit)) { StaticData.CurrentBPM = StaticData.ChangeBPM[0]; StaticData.ChangeBPM.RemoveAt(0); StaticData.ChangeBPMPos.RemoveAt(0); } if (StaticData.HS2.Count > 0 && StaticData.HS2Pos[0] <= Measure + (i / (double)MaxBit)) { StaticData.SpeedMultiplier = StaticData.HS2[0]; StaticData.HS2.RemoveAt(0); StaticData.HS2Pos.RemoveAt(0); } if (StaticData.Delay.Count > 0 && StaticData.DelayPos[0] <= Measure + (i / (double)MaxBit)) { Time += StaticData.Delay[0]; StaticData.Delay.RemoveAt(0); StaticData.DelayPos.RemoveAt(0); } if (StaticData.Scroll.Count > 0 && StaticData.ScrollPos[0] <= Measure + (i / (double)MaxBit)) { Game.Dispensor.CreateNote(ID++, 0, new Color32(255, 255, 255, 0), NoteInfo.SystemNoteScroller, FlickMode.None, (float)Time * 60, (float)StaticData.Scroll[0][0], 0, 0, new List <int>()); StaticData.EndScrollTime = Time + StaticData.Scroll[0][1]; StaticData.IsScrollModified = true; StaticData.Scroll.RemoveAt(0); StaticData.ScrollPos.RemoveAt(0); } if (StaticData.EndScrollTime <= Time && StaticData.IsScrollModified) { Game.Dispensor.CreateNote(ID++, 0, new Color32(255, 255, 255, 0), NoteInfo.SystemNoteScroller, FlickMode.None, (float)Time * 60, 1, 0, 0, new List <int>()); StaticData.IsScrollModified = false; } // Data를 병렬적으로 처리합니다. for (int j = 0; j < Datas.Count; j++) { // 만약 현재의 i 지점에 해당하는 해당 데이터의 계산된 인덱스가 기존의 인덱스보다 크다면 값을 그에 맞춥니다. if (i / (MaxBit / Datas[j][1].Length) > DataIndex[j]) { DataIndex[j]++; } else { continue; } // 데이터를 가져옵니다. 코드 간략화를 위함입니다. char Command = Datas[j][1][DataIndex[j]]; // 필요한 개별 변수를 설정합니다. (1차) NoteInfo Mode; FlickMode Flick; // 노트의 기본 정보를 해석합니다. if (Command.Equals('1') || char.ToUpper(Command).Equals('L')) { Mode = NoteInfo.NormalNote; Flick = DataSender.ReturnMirror() ? FlickMode.Right : FlickMode.Left; // 미러 모드 적용 코드입니다. } else if (Command.Equals('2') || char.ToUpper(Command).Equals('T')) { Mode = NoteInfo.NormalNote; Flick = FlickMode.None; } else if (Command.Equals('3') || char.ToUpper(Command).Equals('R')) { Mode = NoteInfo.NormalNote; Flick = DataSender.ReturnMirror() ? FlickMode.Left : FlickMode.Right; // 미러 모드 적용 코드입니다. } else if (Command.Equals('4') || char.ToUpper(Command).Equals('H')) { Mode = NoteInfo.LongNoteStart; Flick = FlickMode.None; } else if (Command.Equals('5') || char.ToUpper(Command).Equals('S')) { Mode = NoteInfo.SlideNoteStart; Flick = FlickMode.None; } else { continue; } // 필요한 개별 변수를 설정합니다. (2차) double Start = 3, End = 3; // 시작 지점과 끝 지점을 설정합니다. // 위 코드의 continue 때문에, 유효한 노트 데이터가 해석이 되어야 이 부분부터의 코드에 도달합니다. if (Datas[j].Length >= 3) { int DummyStart; char StartText = Datas[j][2][StartIndex[j]]; if (int.TryParse(StartText.ToString(), out DummyStart)) { Start = DummyStart; } else { if (char.ToUpper(StartText).Equals('A')) { Start = 1.5; } else if (char.ToUpper(StartText).Equals('B')) { Start = 2.5; } else if (char.ToUpper(StartText).Equals('C')) { Start = 3.5; } else if (char.ToUpper(StartText).Equals('D')) { Start = 4.5; } else { throw new Exception("Error in start point parsing. Please check your beatmap."); } } StartIndex[j]++; if (Datas[j].Length.Equals(3)) { End = Start; } } if (Datas[j].Length >= 4) { int DummyEnd; char EndText = Datas[j][3][EndIndex[j]]; if (int.TryParse(EndText.ToString(), out DummyEnd)) { End = DummyEnd; if (DataSender.ReturnMirror()) { End = 6 - End; } // 미러 모드 적용 코드입니다. } else { if (char.ToUpper(EndText).Equals('A')) { End = 1.5; } else if (char.ToUpper(EndText).Equals('B')) { End = 2.5; } else if (char.ToUpper(EndText).Equals('C')) { End = 3.5; } else if (char.ToUpper(EndText).Equals('D')) { End = 4.5; } else { throw new Exception("Error in end point parsing. Please check your beatmap."); } } EndIndex[j]++; } // 필요한 개별 변수를 설정합니다. (3차) List <int> Prevs = new List <int>(); if (!StaticData.HoldPrev.ContainsKey(End)) { StaticData.HoldPrev.Add(End, 0); } if (!StaticData.TailPrev.ContainsKey(Channel[j])) { StaticData.TailPrev.Add(Channel[j], 0); } if (!StaticData.ConnectorPrev.ContainsKey(Channel[j])) { StaticData.ConnectorPrev.Add(Channel[j], new List <double[]>(3)); } if (!StaticData.BeforeTime.ContainsKey(Channel[j])) { StaticData.BeforeTime.Add(Channel[j], 0); } // 연결할 이전 노트가 있으면 연결합니다. Tail, Connector 순서로 연결합니다. // Tail은 홀드 노트를 우선으로 합니다 (추후 변경 가능). 중복은 허용하지 않습니다. if (Mode.Equals(NoteInfo.NormalNote) && StaticData.HoldPrev[End] > 0) { Mode = NoteInfo.LongNoteEnd; Prevs.Add(StaticData.HoldPrev[End]); StaticData.HoldPrev[End] = 0; } else if (Mode.Equals(NoteInfo.NormalNote) && StaticData.TailPrev[Channel[j]] > 0) { Mode = NoteInfo.SlideNoteEnd; Prevs.Add(StaticData.TailPrev[Channel[j]]); StaticData.TailPrev[Channel[j]] = 0; } if (Mode.Equals(NoteInfo.SlideNoteStart) && StaticData.TailPrev[Channel[j]] > 0) { Mode = NoteInfo.SlideNoteCheckpoint; Prevs.Add(StaticData.TailPrev[Channel[j]]); } // Connector를 연결합니다. // ConnectorPrev[Channel[j]][x]에서 x=0의 값은 방향(증가 방향이 양수), x=1의 값은 비교 지점, x=2의 값은 노트 ID입니다. if (!Flick.Equals(FlickMode.None) && StaticData.ConnectorPrev[Channel[j]].Count > 0) { for (int k = 0; k < StaticData.ConnectorPrev[Channel[j]].Count; k++) { if (Time - StaticData.BeforeTime[Channel[j]] > 0 && Time - StaticData.BeforeTime[Channel[j]] <= 1) { if (StaticData.ConnectorPrev[Channel[j]][k][0] > 0) { if (End > StaticData.ConnectorPrev[Channel[j]][k][1]) { Prevs.Add((int)StaticData.ConnectorPrev[Channel[j]][k][2]); } } else if (StaticData.ConnectorPrev[Channel[j]][k][0] < 0) { if (End < StaticData.ConnectorPrev[Channel[j]][k][1]) { Prevs.Add((int)StaticData.ConnectorPrev[Channel[j]][k][2]); } } else { Prevs.Add((int)StaticData.ConnectorPrev[Channel[j]][k][2]); } } } if (StaticData.BeforeTime[Channel[j]] != Time) { StaticData.ConnectorPrev[Channel[j]].Clear(); } } else if (Flick.Equals(FlickMode.None) && StaticData.ConnectorPrev[Channel[j]].Count > 0 && StaticData.BeforeTime[Channel[j]] != Time) { StaticData.ConnectorPrev[Channel[j]].Clear(); } // 다음 노트와 연결될 노트에 대한 데이터를 전역 데이터에 입력합니다. if (Mode.Equals(NoteInfo.LongNoteStart)) { StaticData.HoldPrev[End] = ID; } else if (Mode.Equals(NoteInfo.SlideNoteStart) || Mode.Equals(NoteInfo.SlideNoteCheckpoint)) { StaticData.TailPrev[Channel[j]] = ID; } if (!Flick.Equals(FlickMode.None)) { if (Channel[j] % 4 == 0 || Channel[j] % 4 == 1) { if (Flick.Equals(FlickMode.Left)) { StaticData.ConnectorPrev[Channel[j]].Add(new double[3] { -1, End, ID }); } else if (Flick.Equals(FlickMode.Right)) { StaticData.ConnectorPrev[Channel[j]].Add(new double[3] { 1, End, ID }); } } else if (Channel[j] % 4 == 2 || Channel[j] % 4 == 3) { StaticData.ConnectorPrev[Channel[j]].Add(new double[3] { 0, End, ID }); } StaticData.BeforeTime[Channel[j]] = Time; } if (DataSender.ReturnRandWave()) { Start = UnityEngine.Random.Range(1, 6); } if (DataSender.ReturnMirror()) { Start = 6 - Start; } // 노트를 게임에 추가합니다. 이 때 시간을 프레임으로 보정합니다 (60배수). Game.Dispensor.CreateNote(ID++, 1, new Color32(Color[j][0], Color[j][1], Color[j][2], Color[j][3]), Mode, Flick, (float)Time * 60, (float)(Speed[j] * StaticData.SpeedMultiplier), DataSender.ReturnRandWave() ? UnityEngine.Random.Range(1, 6) : (float)Start, (float)End, Prevs); } Time += (((240 / StaticData.CurrentBPM) * StaticData.BeatMultiplier) / MaxBit); } }