diff --git a/rabi_splitter_WPF/BossFightConfig.cs b/rabi_splitter_WPF/BossFightConfig.cs new file mode 100644 index 0000000..8695aa4 --- /dev/null +++ b/rabi_splitter_WPF/BossFightConfig.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace rabi_splitter_WPF +{ + // The descriptions of all the boss fights are listed here. + // Fields can be omitted to keep them unspecified. + public partial class BossFight + { + public static BossFight UNKNOWN = + new BossFight ( + displayName: "UNKNOWN" + ); + + public static BossFight Cocoa1 = + new BossFight ( + displayName: "Cocoa1", + music: Music.GET_ON_WITH_IT, + map: Map.SouthernWoodland, + startingBosses: new[] {Boss.Cocoa} + ); + + public static BossFight Ribbon = + new BossFight ( + displayName: "Ribbon", + music: Music.GET_ON_WITH_IT, + map: Map.SouthernWoodland, + startingBosses: new[] {Boss.Ribbon} + ); + + public static BossFight Ashuri1 = + new BossFight ( + displayName: "Ashuri1", + music: Music.GET_ON_WITH_IT, + map: Map.WesternCoast, + startingBosses: new[] {Boss.Ashuri} + ); + + public static BossFight Ashuri2 = + new BossFight ( + displayName: "Ashuri2", + music: Music.BRAWL_BREAKS_VER_2, + map: Map.EasternHighlands, + startingBosses: new[] {Boss.Ashuri} + ); + + public static BossFight BigBox = + new BossFight ( + displayName: "Big Box", + music: Music.MIDBOSS_BATTLE, + map: Map.EasternHighlands, + startingBosses: new[] {Boss.BigBox} + ); + + public static BossFight RainbowMaid = + new BossFight ( + displayName: "Rainbow Maid", + music: Music.MIDBOSS_BATTLE, + map: Map.EasternHighlands, + startingBosses: new[] {Boss.RainbowMaid} + ); + + public static BossFight Seana1 = + new BossFight ( + displayName: "Seana1", + music: Music.KITTY_ATTACK, + map: Map.NorthernTundra, + startingBosses: new[] {Boss.Seana} + ); + + public static BossFight Seana2 = + new BossFight ( + displayName: "Seana2", + music: Music.BOUNCE_BOUNCE, + map: Map.IslandCore, + startingBosses: new[] {Boss.Seana} + ); + + public static BossFight Kotri1 = + new BossFight ( + displayName: "Kotri1", + music: Music.BRAWL_BREAKS, + map: Map.IslandCore, + startingBosses: new[] {Boss.Kotri} + ); + + public static BossFight Kotri2 = + new BossFight ( + displayName: "Kotri2", + music: Music.BRAWL_BREAKS, + map: Map.WesternCoast, + startingBosses: new[] {Boss.Kotri} + ); + + public static BossFight Kotri3 = + new BossFight ( + displayName: "Kotri3", + music: Music.BRAWL_BREAKS, + map: Map.SubterraneanArea, + startingBosses: new[] {Boss.Kotri} + ); + + public static BossFight Alius1 = + new BossFight ( + displayName: "Alius1", + music: Music.SUDDEN_DEATH, + map: Map.WarpDestination, + startingBosses: new[] {Boss.IllusionAlius} + ); + + public static BossFight Alius2 = + new BossFight ( + displayName: "Alius2", + music: Music.SUDDEN_DEATH, + map: Map.WarpDestination, + startingBosses: new[] {Boss.Noah1, Boss.IllusionAlius} + ); + + public static BossFight Alius3 = + new BossFight ( + displayName: "Alius3", + music: Music.SUDDEN_DEATH, + map: Map.WarpDestination, + startingBosses: new[] {Boss.Noah1} + ); + + public static BossFight Miru = + new BossFight ( + displayName: "Miru", + music: Music.M_R_, + map: Map.WarpDestination, + startingBosses: new[] {Boss.Miru} + ); + + public static BossFight Noah1 = + new BossFight ( + displayName: "Noah1", + music: Music.THE_TRUTH_NEVER_SPOKEN, + map: Map.WarpDestination, + startingBosses: new[] {Boss.Noah1} + ); + + public static BossFight Noah3 = + new BossFight ( + displayName: "Noah3", + music: Music.RFN_III, + map: Map.WarpDestination, + startingBosses: new[] {Boss.Noah3} + ); + + public static BossFight SideChapter = + new BossFight ( + displayName: "Side Chapter", + music: Music.GET_ON_WITH_IT, + map: Map.RabiRabiTown, + extraCondition: (startingBosses, music, map) => { + return startingBosses.Count == 3; + } + ); + } +} diff --git a/rabi_splitter_WPF/BossFightEnum.cs b/rabi_splitter_WPF/BossFightEnum.cs new file mode 100644 index 0000000..e5c3e5c --- /dev/null +++ b/rabi_splitter_WPF/BossFightEnum.cs @@ -0,0 +1,99 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace rabi_splitter_WPF +{ + /// + /// enum + /// + public partial class BossFight + { + // Do not make these properties public. + private static int nextAvailableValue = 0; + private readonly int _value; + private static BossFight[] _bossFights; + + // Do not make these properties static. + public readonly string displayName; + public readonly Music? music; + public readonly Map? map; + public readonly HashSet startingBosses; + public readonly Func, Music, Map, bool> extraCondition; + + private BossFight(string displayName = null, + Music? music = null, + Map? map = null, + Boss[] startingBosses = null, + Func, Music, Map, bool> extraCondition = null) + { + _value = nextAvailableValue++; + this.displayName = displayName; + this.music = music; + this.map = map; + this.startingBosses = (startingBosses != null) ? new HashSet(startingBosses) : null; + this.extraCondition = extraCondition; + } + + public int Value + { + get { return _value; } + } + + public override string ToString() + { + return displayName; + } + + private static BossFight[] GetAllBossFights() + { + var type = typeof(BossFight); + var fields = type.GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly); + + var bossFights = new List(); + foreach (var info in fields) + { + var instance = new BossFight(); + var locatedValue = info.GetValue(instance) as BossFight; + + if (locatedValue != null && locatedValue != BossFight.UNKNOWN) + { + bossFights.Add(locatedValue); + } + } + + return bossFights.ToArray(); + } + + public static IEnumerable GetBossFights() + { + if (_bossFights == null) _bossFights = GetAllBossFights(); + + foreach (var bossFight in _bossFights) + { + yield return bossFight; + } + } + + public override bool Equals(object obj) + { + var otherValue = obj as BossFight; + + if (otherValue == null) + { + return false; + } + + var typeMatches = GetType().Equals(obj.GetType()); + var valueMatches = _value.Equals(otherValue.Value); + + return typeMatches && valueMatches; + } + + public override int GetHashCode() + { + return _value.GetHashCode(); + } + } +} diff --git a/rabi_splitter_WPF/BossFightIdentifier.cs b/rabi_splitter_WPF/BossFightIdentifier.cs new file mode 100644 index 0000000..795ef44 --- /dev/null +++ b/rabi_splitter_WPF/BossFightIdentifier.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace rabi_splitter_WPF +{ + public static partial class BossFightIdentifier + { + private static ILookup, BossFight> _matchMusicAndMap; + private static ILookup _matchMusic; + private static ILookup _matchMap; + private static BossFight[] _remainingDescriptions; + + static BossFightIdentifier() + { + // Build dictionaries here. + var specifiedMusicAndMap = new List(); + var specifiedMusicOnly = new List(); + var specifiedMapOnly = new List(); + var specifiedNeither = new List(); + + foreach (var bossFight in BossFight.GetBossFights()) + { + //if (bossFight == null) continue; + if (bossFight.music != null) + { + if (bossFight.map != null) specifiedMusicAndMap.Add(bossFight); + else specifiedMusicOnly.Add(bossFight); + } + else + { + if (bossFight.map != null) specifiedMapOnly.Add(bossFight); + else specifiedNeither.Add(bossFight); + } + } + + _matchMusicAndMap = specifiedMusicAndMap.ToLookup(bossFight => new Tuple(bossFight.music.Value, bossFight.map.Value), bossFight => bossFight); + _matchMusic = specifiedMusicOnly.ToLookup(bossFight => bossFight.music.Value, bossFight => bossFight); + _matchMap = specifiedMapOnly.ToLookup(bossFight => bossFight.map.Value, bossFight => bossFight); + _remainingDescriptions = specifiedNeither.ToArray(); + } + + private static bool Matches(BossFight bossFight, HashSet startingBosses, Music music, Map map) + { + if (bossFight.startingBosses != null && !bossFight.startingBosses.SetEquals(startingBosses)) return false; + if (bossFight.extraCondition == null) return true; + return bossFight.extraCondition(startingBosses, music, map); + } + + public static BossFight IdentifyBossFight(MemorySnapshot snapshot) + { + Music? music_ = StaticData.GetMusic(snapshot.musicid); + Map? map_ = StaticData.GetMap(snapshot.mapid); + if (music_ == null || map_ == null) return BossFight.UNKNOWN; + + var music = music_.Value; + var map = map_.Value; + var startingBosses = new HashSet(snapshot.bossList + .Select(bossStats => StaticData.GetBoss(bossStats.id)) + .Where(boss => boss != null) + .Select(boss => boss.Value) + ); + + foreach (var bossFight in _matchMusicAndMap[new Tuple(music, map)]) + { + if (Matches(bossFight, startingBosses, music, map)) return bossFight; + } + + foreach (var bossFight in _matchMusic[music]) + { + if (Matches(bossFight, startingBosses, music, map)) return bossFight; + } + + foreach (var bossFight in _matchMap[map]) + { + if (Matches(bossFight, startingBosses, music, map)) return bossFight; + } + + return _remainingDescriptions.FirstOrDefault(bossFight => Matches(bossFight, startingBosses, music, map)) ?? BossFight.UNKNOWN; + } + + } +} diff --git a/rabi_splitter_WPF/MemorySnapshot.cs b/rabi_splitter_WPF/MemorySnapshot.cs index f2226f8..970dbac 100644 --- a/rabi_splitter_WPF/MemorySnapshot.cs +++ b/rabi_splitter_WPF/MemorySnapshot.cs @@ -6,7 +6,7 @@ using System.Text; namespace rabi_splitter_WPF { - struct BossStats + public struct BossStats { public int entityArrayIndex; public int id; @@ -15,7 +15,7 @@ namespace rabi_splitter_WPF public int maxHp; } - class MemorySnapshot + public class MemorySnapshot { public readonly int t_playtime; public readonly int playtime; diff --git a/rabi_splitter_WPF/RabiRibiDisplay.cs b/rabi_splitter_WPF/RabiRibiDisplay.cs index 3f0b00e..0ccb907 100644 --- a/rabi_splitter_WPF/RabiRibiDisplay.cs +++ b/rabi_splitter_WPF/RabiRibiDisplay.cs @@ -94,7 +94,8 @@ namespace rabi_splitter_WPF DebugLog($"Minimap Shift! {prevSnapshot.minimapPosition} -> {snapshot.minimapPosition}"); if (snapshot.minimapPosition == 1) { - DebugLog($"Fighting Boss: {string.Join(", ", snapshot.bossList.Select(boss => StaticData.GetBossName(boss.id)))}"); + DebugLog($"BOSS FIGHT: {BossFightIdentifier.IdentifyBossFight(snapshot).displayName}"); + DebugLog($"Fighting Bosses: {string.Join(", ", snapshot.bossList.Select(boss => StaticData.GetBossName(boss.id)))}"); } } diff --git a/rabi_splitter_WPF/rabi_display.csproj b/rabi_splitter_WPF/rabi_display.csproj index 8d15352..8572f5a 100644 --- a/rabi_splitter_WPF/rabi_display.csproj +++ b/rabi_splitter_WPF/rabi_display.csproj @@ -55,6 +55,9 @@ MSBuild:Compile Designer + + +