예제 #1
0
        /// <summary>
        /// Does various calculations for each difficulty.
        /// </summary>
        /// <param name="difficulty">The difficulty to process</param>
        /// <param name="quick">Skips score normalisation if true</param>
        /// <returns>Score multiplier if quick is not set to true, else 0</returns>
        private static double processDifficulty(Difficulty difficulty, bool quick = false)
        {
            Console.Write("Processing " + difficulty);

            double comboMultiplier = 1;

            Player.Difficulty = difficulty;
            Player.Autoplay   = true;

            Score s = null;

            using (Player p = new PlayCombinate())
            {
                p.Initialize();

                if (p.HitObjectManager.ActiveStreamObjects == null || p.HitObjectManager.ActiveStreamObjects.Count == 0)
                {
                    Console.WriteLine(" Failed!");
                    return(0);
                }

                HitObject switchHpObject = null;
                if (difficulty == Difficulty.Normal && Player.Beatmap.StreamSwitchPoints != null && Player.Beatmap.StreamSwitchPoints.Count > 0)
                {
                    //stream mode specific. make sure we have enough hp to hit the first stream switch
                    int testStreamSwitch = Player.Beatmap.StreamSwitchPoints[0] - DifficultyManager.PreEmpt;
                    int index            = p.HitObjectManager.ActiveStreamObjects.FindIndex(h => { return(h.StartTime > testStreamSwitch); }) - 1;
                    //take one from the index. we need to be at max HP *before* the preempt-take-switch.

                    while (index >= 0 && p.HitObjectManager.ActiveStreamObjects[index].EndTime > testStreamSwitch)
                    {
                        index--;
                    }

                    if (index <= 0)
                    {
                        Console.WriteLine("\nERROR: Bookmark exists before first object! Please only use bookmarks for stream switch points.");
                        index = 0;
                    }

                    switchHpObject = p.HitObjectManager.ActiveStreamObjects[index];
                }

                FakeAudioTimeSource source = new FakeAudioTimeSource();
                Clock.AudioTimeSource = source;

                while (true)
                {
                    if (source.InternalTime % 10 < 0.01)
                    {
                        Console.Write(".");
                    }

                    Clock.UpdateCustom(0.01);
                    source.InternalTime      += 0.01;
                    Clock.ElapsedMilliseconds = 10;

                    p.Update();

                    if (switchHpObject != null && switchHpObject.IsHit)
                    {
                        double currentHp = p.healthBar.CurrentHpUncapped;

                        healthMultiplier = (HealthBar.HP_BAR_MAXIMUM - HealthBar.HP_BAR_INITIAL + 5) / (currentHp - HealthBar.HP_BAR_INITIAL);
                        Player.Beatmap.HpStreamAdjustmentMultiplier = healthMultiplier;

                        switchHpObject = null;
                    }

                    if (p.Completed)
                    {
                        s = p.CurrentScore;
                        s.UseAccuracyBonus = true;

                        int excess = s.totalScore - s.spinnerBonusScore - 1000000;

                        double testMultiplier = (double)(s.comboBonusScore - excess) / s.comboBonusScore;

                        comboMultiplier = testMultiplier * 0.97f;
                        break;
                    }
                }
            }

            Console.WriteLine();

            if (Analysis)
            {
                checkOverlaps(difficulty);
            }

            int finalScore = 0;

            if (!quick)
            {
                double adjustment = 0;

                while (finalScore < 1000000)
                {
                    Console.Write(".");

                    Player.Difficulty = difficulty;
                    Player.Autoplay   = true;

                    Player.Beatmap.DifficultyInfo[difficulty] = new BeatmapDifficultyInfo(difficulty)
                    {
                        ComboMultiplier = comboMultiplier
                    };

                    //let's do some test runs
                    using (Player p = new PlayCombinate())
                    {
                        p.Initialize();

                        FakeAudioTimeSource source = new FakeAudioTimeSource();
                        Clock.AudioTimeSource = source;

                        while (true)
                        {
                            Clock.UpdateCustom(0.01);
                            source.InternalTime += 0.01;

                            p.Update();

                            if (p.Completed)
                            {
                                s = p.CurrentScore;
                                s.UseAccuracyBonus = true;

                                finalScore = (s.totalScore - s.spinnerBonusScore);

                                if (finalScore < 1000000)
                                {
                                    int fellShortBy = 1000000 - finalScore;
                                    adjustment       = (double)fellShortBy / s.comboBonusScore;
                                    comboMultiplier += adjustment;
                                }

                                break;
                            }
                        }
                    }
                }
            }


            if (!quick)
            {
                Console.WriteLine("HP multiplier: ".PadRight(25) + healthMultiplier);
                Console.WriteLine("Using combo multiplier: ".PadRight(25) + comboMultiplier);
                Console.WriteLine("Hitobject score: ".PadRight(25) + s.hitScore);
                Console.WriteLine("Combo score: ".PadRight(25) + s.comboBonusScore);
                Console.WriteLine("Spin score: ".PadRight(25) + s.spinnerBonusScore);
                Console.WriteLine("Accuracy score: ".PadRight(25) + s.accuracyBonusScore);
                Console.WriteLine("Total score: ".PadRight(25) + s.totalScore);
                Console.WriteLine("Total score (no spin): ".PadRight(25) + finalScore);
            }

            //i guess the best thing to do might be to aim slightly above 1m and ignore the excess...
            //okay now we have numbers roughly around 1mil (always higher or equal to).
            //need to do something about this static, then load them up in osu!s.
            return(comboMultiplier);
        }
예제 #2
0
        private static void checkOverlaps(Difficulty s1, Difficulty s2, int switchTime = 0)
        {
            bool streamSwitch = s2 != Difficulty.None;

            PlayTest.AllowStreamSwitch = streamSwitch;

            PlayTest.StartTime = switchTime > 0 ? switchTime - 10000 : 0;
            int endTime = switchTime > 0 ? switchTime + 10000 : 0;

            PlayTest.InitialDifficulty = s1;

            Player.Autoplay = true;

            if (s2 > s1)
            {
                PlayTest.InitialHp = 200;
            }
            else
            {
                PlayTest.InitialHp = 0;
            }

            using (PlayTest p = new PlayTest())
            {
                p.Initialize();

                FakeAudioTimeSource source = new FakeAudioTimeSource();
                source.InternalTime   = Math.Max((PlayTest.StartTime - 10000) / 1000f, 0);
                Clock.AudioTimeSource = source;

                HitObjectManager hitObjectManager = p.HitObjectManager;

                List <HitObjectPair> pairs = new List <HitObjectPair>();

                while ((switchTime == 0 && p.Progress < 1) || Clock.AudioTime < switchTime + 10000)
                {
                    if (source.InternalTime % 20 < 0.01)
                    {
                        Console.Write(".");
                    }

                    Clock.UpdateCustom(0.01);
                    source.InternalTime += 0.01;

                    List <HitObject> objects = hitObjectManager.ActiveStreamObjects.FindAll(h => h.IsVisible);
                    foreach (HitObject h1 in objects)
                    {
                        foreach (HitObject h2 in objects)
                        {
                            if (h1 == h2)
                            {
                                continue;
                            }

                            HitObjectPair hop = new HitObjectPair(h1, h2);

                            if (pairs.IndexOf(hop) >= 0)
                            {
                                continue;
                            }

                            //hack in the current snaking point for added security.
                            Vector2 h1pos2 = h1 is Slider ? ((Slider)h1).SnakingEndPosition : h1.Position2;
                            Vector2 h2pos2 = h2 is Slider ? ((Slider)h2).SnakingEndPosition : h2.Position2;

                            if (pMathHelper.Distance(h1.Position, h2.Position) < DifficultyManager.HitObjectRadiusSprite ||
                                pMathHelper.Distance(h1.Position, h2pos2) < DifficultyManager.HitObjectRadiusSprite ||
                                pMathHelper.Distance(h1pos2, h2.Position) < DifficultyManager.HitObjectRadiusSprite ||
                                pMathHelper.Distance(h1pos2, h2pos2) < DifficultyManager.HitObjectRadiusSprite)
                            {
                                pairs.Add(hop);
                                if (s2 != Difficulty.None)
                                {
                                    Console.WriteLine("[mod] [{1}->{2}] Overlap at {0}", Clock.AudioTime, s1.ToString()[0], s2.ToString()[0]);
                                }
                                else
                                {
                                    Console.WriteLine("[mod] [{1}] Overlap at {0}", Clock.AudioTime, s1, s2);
                                }
                            }
                        }
                    }
                }
            }
        }
예제 #3
0
        public static string Process(string dir, bool quick = false, bool usem4a = true, bool free = false, bool previewMode = false)
        {
            Console.WriteLine("Combinating beatmap: " + dir.Split('\\').Last());
            Console.WriteLine();

            if (dir.Length < 1)
            {
                Console.WriteLine("No path specified!");
                return(null);
            }

            List <string> osuFiles = new List <string>(Directory.GetFiles(dir, "*.osu"));

            if (osuFiles.Count < 1)
            {
                Console.WriteLine("No .osu files found!");
                return(null);
            }

            List <string> orderedDifficulties = new List <string>();

            orderedDifficulties.Add(osuFiles.Find(f => f.EndsWith("[Easy].osu")));
            orderedDifficulties.Add(osuFiles.Find(f => f.EndsWith("[Normal].osu")));
            orderedDifficulties.Add(osuFiles.Find(f => f.EndsWith("[Hard].osu")));
            orderedDifficulties.Add(osuFiles.Find(f => f.EndsWith("[Expert].osu")));

            if (orderedDifficulties.FindAll(t => t != null).Count < 1)
            {
                return(null);
            }

            Console.WriteLine("Files found:");
            foreach (string s in orderedDifficulties)
            {
                Console.WriteLine("    * " + Path.GetFileName(s));
            }
            Console.WriteLine();
            Console.WriteLine();

            List <BeatmapDifficulty> difficulties = new List <BeatmapDifficulty>();

            foreach (string f in orderedDifficulties)
            {
                if (f == null)
                {
                    difficulties.Add(null);
                    continue;
                }

                BeatmapDifficulty bd = new BeatmapDifficulty();
                difficulties.Add(bd);

                string currentSection = "";

                foreach (string line in File.ReadAllLines(f))
                {
                    string writeLine = line;

                    if (line.StartsWith("Version:"))
                    {
                        bd.VersionName = line.Replace("Version:", "");
                        continue;
                    }

                    if (line.StartsWith("["))
                    {
                        currentSection = line.Replace("[", "").Replace("]", "");
                    }
                    else if (line.Length > 0)
                    {
                        string[] split = line.Split(',');
                        string[] var   = line.Split(':');
                        string   key   = string.Empty;
                        string   val   = string.Empty;
                        if (var.Length > 1)
                        {
                            key = var[0].Trim();
                            val = var[1].Trim();
                        }

                        switch (currentSection)
                        {
                        case "General":
                            switch (key)
                            {
                            case "AudioFilename":
                                writeLine = "AudioFilename: audio.mp3";
                                break;
                            }

                            break;

                        case "Difficulty":
                            switch (key)
                            {
                            case "HPDrainRate":
                                bd.DifficultyHpDrainRate = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val)));
                                break;

                            case "CircleSize":
                                bd.DifficultyCircleSize = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val)));
                                break;

                            case "OverallDifficulty":
                                bd.DifficultyOverall = Math.Min((byte)10, Math.Max((byte)0, byte.Parse(val)));
                                //if (!hasApproachRate) DifficultyApproachRate = DifficultyOverall;
                                break;

                            case "SliderMultiplier":
                                bd.DifficultySliderMultiplier =
                                    Math.Max(0.4, Math.Min(3.6, Double.Parse(val, nfi)));
                                break;

                            case "SliderTickRate":
                                bd.DifficultySliderTickRate =
                                    Math.Max(0.5, Math.Min(8, Double.Parse(val, nfi)));
                                break;
                            }

                            break;

                        case "HitObjects":
                        {
                            HitObjectType type    = (HitObjectType)Int32.Parse(split[3]) & ~HitObjectType.ColourHax;
                            bool          slider  = (type & HitObjectType.Slider) > 0;
                            bool          spinner = (type & HitObjectType.Spinner) > 0;
                            int           time    = (int)Decimal.Parse(split[2], nfi);
                            int           endTime = spinner ? (int)Decimal.Parse(split[5], nfi) : time;

                            int       repeatCount        = 0;
                            double    length             = 0;
                            bool      hadEndpointSamples = false;
                            bool      hold = false;
                            SampleSet ss = SampleSet.None, ssa = SampleSet.None;
                            string[]  samplestring = null;

                            if (slider)
                            {
                                repeatCount        = Int32.Parse(split[6], nfi);
                                length             = double.Parse(split[7], nfi);
                                hadEndpointSamples = split.Length >= 9;

                                hold = (repeatCount > 1 && length < 50) ||
                                       (repeatCount > 4 && length < 100) ||
                                       (hadEndpointSamples && split[4] == "4");

                                if (split.Length > 10)
                                {
                                    samplestring = split[10].Split(':');
                                }
                            }
                            else if (spinner)
                            {
                                if (split.Length > 6)
                                {
                                    samplestring = split[6].Split(':');
                                }
                            }
                            else
                            {
                                if (split.Length > 5)
                                {
                                    samplestring = split[5].Split(':');
                                }
                            }

                            if (samplestring != null)
                            {
                                ss = (SampleSet)Convert.ToInt32(samplestring[0]);
                                if (samplestring.Length > 0)
                                {
                                    ssa = (SampleSet)Convert.ToInt32(samplestring[1]);
                                }
                            }

                            // take the slider's slide sampleset from 20ms after the head in case the head has a different sampleset
                            ControlPoint cp = bd.controlPointAt(slider ? time + 20 : endTime + 5);

                            StringBuilder builder = new StringBuilder();
                            builder.Append(MakeSampleset(cp, ss, ssa));

                            // Object commons
                            builder.Append(',');
                            builder.Append(split[0]);     // X

                            builder.Append(',');
                            builder.Append(split[1]);     // Y

                            builder.Append(',');
                            builder.Append(time.ToString(nfi));     // time

                            HitObjectType type2 = (HitObjectType)Int32.Parse(split[3]);
                            builder.Append(',');
                            builder.Append(hold ? (int)(type2 | HitObjectType.Hold) : (int)type2);     // object type

                            string soundAdditions = MakeSoundAdditions(split[4]);
                            builder.Append(',');
                            builder.Append(soundAdditions);     // sound additions

                            //add addition difficulty-specific information
                            if (slider)
                            {
                                builder.Append(',');
                                builder.Append(split[5]);     // curve type, all control points

                                builder.Append(',');
                                builder.Append(repeatCount.ToString(nfi));     // repeat count

                                builder.Append(',');
                                builder.Append(length.ToString(nfi));     // curve length

                                string[] additions;
                                if (hadEndpointSamples)
                                {
                                    additions = split[8].Split('|');
                                }
                                else
                                {
                                    additions = new string[0];
                                }

                                // nodal hitsamples
                                builder.Append(',');
                                for (int repeatNo = 0; repeatNo <= repeatCount; repeatNo++)
                                {
                                    if (repeatNo > 0)
                                    {
                                        builder.Append('|');
                                    }
                                    if (repeatNo < additions.Length)
                                    {
                                        builder.Append(MakeSoundAdditions(additions[repeatNo]));
                                    }
                                    else
                                    {
                                        builder.Append(soundAdditions);
                                    }
                                }

                                double velocity = bd.VelocityAt(time);

                                //velocity and scoring distance.
                                builder.Append(',');
                                builder.Append(velocity.ToString(nfi));

                                builder.Append(',');
                                builder.Append(bd.ScoringDistanceAt(time).ToString(nfi));

                                double ReboundTime = 1000 * length / velocity;

                                double currTime = time;
                                cp = bd.controlPointAt(currTime + 5);

                                string[] node_samples;
                                if (split.Length > 9)
                                {
                                    // osu!'s separator is different
                                    node_samples = split[9].Split('|');
                                }
                                else
                                {
                                    node_samples = new string[0];
                                }

                                // nodal samplesets
                                for (int repeatNo = 0; repeatNo <= repeatCount; repeatNo++)
                                {
                                    SampleSet node_ss  = ss;
                                    SampleSet node_ssa = ssa;

                                    if (repeatNo < node_samples.Length)
                                    {
                                        string[] pair = node_samples[repeatNo].Split(':');
                                        node_ss = (SampleSet)Convert.ToInt32(pair[0]);
                                        if (pair.Length > 0)
                                        {
                                            node_ssa = (SampleSet)Convert.ToInt32(pair[1]);
                                        }
                                    }

                                    cp = bd.controlPointAt(currTime + 5);
                                    builder.Append(repeatNo == 0 ? ',' : ':');
                                    builder.Append(MakeSampleset(cp, node_ss, node_ssa));
                                    currTime += ReboundTime;
                                }
                            }

                            if (spinner)
                            {
                                builder.Append(',');
                                builder.Append(split[5]);     // end time
                            }

                            bd.HitObjectLines.Add(new HitObjectLine {
                                    StringRepresentation = builder.ToString(), Time = Int32.Parse(line.Split(',')[2])
                                });
                            continue;     //skip direct output
                        }

                        case "TimingPoints":
                        {
                            ControlPoint cp = new ControlPoint(Double.Parse(split[0], nfi),
                                                               Double.Parse(split[1], nfi),
                                                               split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Int32.Parse(split[2]),
                                                               (SampleSet)Int32.Parse(split[3]),
                                                               split.Length > 4
                                        ? (CustomSampleSet)Int32.Parse(split[4])
                                        : CustomSampleSet.Default,
                                                               Int32.Parse(split[5]),
                                                               split.Length > 6 ? split[6][0] == '1' : true,
                                                               split.Length > 7 ? split[7][0] == '1' : false);
                            bd.ControlPoints.Add(cp);
                            break;
                        }
                        }
                    }

                    bd.HeaderLines.Add(writeLine);
                }
            }


            string metadata = dir + "\\metadata.txt";
            string Artist = "", Title = "", Creator = "";

            if (File.Exists(metadata))
            {
                foreach (string line in File.ReadAllLines(metadata))
                {
                    if (line.Length == 0)
                    {
                        continue;
                    }

                    string[] var = line.Split(':');
                    string   key = string.Empty;
                    string   val = string.Empty;
                    if (var.Length > 1)
                    {
                        key = line.Substring(0, line.IndexOf(':'));
                        val = line.Substring(line.IndexOf(':') + 1).Trim();
                        switch (key)
                        {
                        case "Artist":
                            Artist = val;
                            break;

                        case "Title":
                            Title = val;
                            break;

                        case "Creator":
                            Creator = val;
                            break;
                        }
                    }
                }
            }

            string baseName    = Artist + " - " + Title + " (" + Creator + ")";
            string oscFilename = baseName + ".osc";

            foreach (BeatmapDifficulty d in difficulties)
            {
                if (d != null)
                {
                    ListHelper.StableSort(d.HitObjectLines);
                }
            }

            headerContent = difficulties.Find(d => d != null).HeaderLines;

            string[] splitdir = dir.Split('\\');

            string osz2Filename;

            string baseFileWithLocation = baseName.Substring(baseName.LastIndexOf("\\", StringComparison.Ordinal) + 1);

            if (free && DistBuild)
            {
                osz2Filename = baseFileWithLocation + ".osf2";
            }
            else
            {
                osz2Filename = baseFileWithLocation + (usem4a && !DistBuild ? ".m4a.osz2" : ".osz2");
            }

            string audioFilename = null;

            if (usem4a)
            {
                audioFilename = "";
                foreach (string s in Directory.GetFiles(dir, "*.m4a"))
                {
                    if (s.Contains("_lq"))
                    {
                        continue;
                    }

                    audioFilename = s;
                    break;
                }
            }
            else
            {
                audioFilename = Directory.GetFiles(dir, "*.mp3")[0];
            }

            File.Delete(osz2Filename);

            //write the package initially so we can use it for score testing purposes.
            writePackage(oscFilename, osz2Filename, audioFilename, difficulties, orderedDifficulties);

            //scoring

            Player.Beatmap  = new Beatmap(osz2Filename);
            Player.Autoplay = true;

            //Working on the scoring algorithm for osu!s
            //Basically I need to calculate the total possible score from hitobjects before any multipliers kick in...
            //but I need to know this before the beatmap is loaded.

            //So this means running through the beatmap as if it was being played at the time of package creation
            //(inside BeatmapCombinator).  After I find the score that can be achieved, I can figure out what multiplier
            //i need in order to pad it out to a fixed 1,000,000 max score.

            //I am sure I will run into some rounding issues once I get that far, but we'll see how things go :p.

            ITimeSource         oldTimeSource = Clock.AudioTimeSource;
            FakeAudioTimeSource source        = new FakeAudioTimeSource();

            Clock.AudioTimeSource = source;

            SoundEffectPlayer     oldEffect = AudioEngine.Effect;
            BackgroundAudioPlayer oldMusic  = AudioEngine.Music;

            AudioEngine.Music  = null;
            AudioEngine.Effect = null;

            headerContent.Remove("[HitObjects]");

            headerContent.Add(string.Empty);
            headerContent.Add("[ScoringMultipliers]");

            if (quick)
            {
                processDifficulty(Difficulty.Easy, true);
                processDifficulty(Difficulty.Normal, true);
                processDifficulty(Difficulty.Hard, true);
                processDifficulty(Difficulty.Expert, true);
            }
            else
            {
                if (orderedDifficulties[(int)Difficulty.Easy] != null)
                {
                    headerContent.Add("0: " + processDifficulty(Difficulty.Easy).ToString("G17", nfi));
                }
                if (orderedDifficulties[(int)Difficulty.Normal] != null)
                {
                    headerContent.Add("1: " + processDifficulty(Difficulty.Normal).ToString("G17", nfi));
                }
                if (orderedDifficulties[(int)Difficulty.Expert] != null)
                {
                    headerContent.Add("3: " + processDifficulty(Difficulty.Expert).ToString("G17", nfi));
                }
            }

            if (healthMultiplier != 0)
            {
                headerContent.Add("HP:" + healthMultiplier.ToString("G17", nfi));
            }

            headerContent.Add(string.Empty);
            headerContent.Add("[HitObjects]");

            Player.Beatmap.Dispose();

            Clock.AudioTimeSource = oldTimeSource;
            AudioEngine.Effect    = oldEffect;
            AudioEngine.Music     = oldMusic;

            //only change the filename here so it is not treated as a preview above (else previewpoints will not be filled before necessary).
            if (previewMode)
            {
                osz2Filename = osz2Filename.Replace(".osf2", "_preview.osf2");
            }

            //write the package a second time with new multiplier header data.
            writePackage(oscFilename, osz2Filename, audioFilename, difficulties, orderedDifficulties);

            return(osz2Filename);
        }