public void StormReplayInitDataTest()
        {
            Assert.AreEqual(2079817235, _stormReplay.RandomValue);
            Assert.AreEqual(StormGameMode.QuickMatch, _stormReplay.GameMode);

            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("KaelthasRobotVar1", player0.PlayerLoadout.SkinAndSkinTint);
            Assert.AreEqual("RainbowUnicornGreen", player0.PlayerLoadout.MountAndMountTint);
            Assert.IsFalse(player0.IsSilenced);
            Assert.IsFalse(player0.IsVoiceSilenced);
            Assert.IsFalse(player0.IsBlizzardStaff);
            Assert.IsFalse(player0.HasActiveBoost);
            Assert.AreEqual("BannerWCAllianceRare", player0.PlayerLoadout.Banner);
            Assert.AreEqual(6, player0.HeroMasteryTiersCount);
            Assert.AreEqual(PlayerType.Human, player0.PlayerType);

            StormPlayer player8 = players[8];

            Assert.AreEqual("ZagaraInsectoidBlue", player8.PlayerLoadout.SkinAndSkinTint);
            Assert.AreEqual("ZagaraWings", player8.PlayerLoadout.MountAndMountTint);
            Assert.IsFalse(player8.IsSilenced);
            Assert.IsFalse(player8.IsVoiceSilenced);
            Assert.IsFalse(player8.IsBlizzardStaff);
            Assert.IsFalse(player8.HasActiveBoost);
            Assert.AreEqual("BannerDefault", player8.PlayerLoadout.Banner);
            Assert.AreEqual(1, player8.HeroMasteryTiersCount);
            Assert.AreEqual(PlayerType.Human, player8.PlayerType);
        }
        public void PlayersScoreResultTest()
        {
            StormPlayer player = _stormReplay.StormPlayers.ToList()[3];

            Assert.AreEqual("Kael'thas", player.PlayerHero !.HeroName);

            ScoreResult?scoreResult = player.ScoreResult;

            Assert.AreEqual(3, scoreResult !.Assists);
            Assert.AreEqual(0, scoreResult.ClutchHealsPerformed);
            Assert.AreEqual(0, scoreResult.CreepDamage);
            Assert.AreEqual(10462, scoreResult.DamageSoaked);
            Assert.AreEqual(18385, scoreResult.DamageTaken);
            Assert.AreEqual(new TimeSpan(0, 0, 30), scoreResult.TimeCCdEnemyHeroes);
            Assert.AreEqual(new TimeSpan(0, 0, 0), scoreResult.TimeRootingEnemyHeroes);
            Assert.AreEqual(new TimeSpan(0, 0, 0), scoreResult.TimeSilencingEnemyHeroes);
            Assert.AreEqual(new TimeSpan(0, 1, 23), scoreResult.TimeSpentDead);
            Assert.AreEqual(new TimeSpan(0, 0, 12), scoreResult.TimeStunningEnemyHeroes);
            Assert.AreEqual(2, scoreResult.TownKills);
            Assert.AreEqual(1, scoreResult.VengeancesPerformed);
            Assert.AreEqual(0, scoreResult.WatchTowerCaptures);
            Assert.AreEqual(39, scoreResult.MinionKills);
            Assert.AreEqual(15, scoreResult.RegenGlobes);
            Assert.AreEqual(1, scoreResult.Tier1Talent);
            Assert.AreEqual(3, scoreResult.Tier4Talent);
            Assert.AreEqual(1, scoreResult.Tier7Talent);
            Assert.AreEqual(1, scoreResult.Tier10Talent);
            Assert.AreEqual(2, scoreResult.Tier13Talent);
            Assert.AreEqual(null, scoreResult.Tier16Talent);
            Assert.AreEqual(null, scoreResult.Tier20Talent);
        }
        public void StormReplayDetailsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("lavakill", player0.Name);
            Assert.AreEqual(1, player0.ToonHandle !.Region);
            Assert.AreEqual(1, player0.ToonHandle.Realm);
            Assert.AreEqual(1527252, player0.ToonHandle.Id);
            Assert.AreEqual(1869768008, player0.ToonHandle.ProgramId);
            Assert.AreEqual(1, player0.ToonHandle.Realm);
            Assert.AreEqual(StormTeam.Blue, player0.Team);
            Assert.IsTrue(player0.IsWinner);
            Assert.AreEqual("Qhira", player0.PlayerHero !.HeroName);

            StormPlayer player9 = players[9];

            Assert.AreEqual("Player 10", player9.Name);
            Assert.AreEqual(0, player9.ToonHandle !.Region);
            Assert.AreEqual(0, player9.ToonHandle.Realm);
            Assert.AreEqual(0, player9.ToonHandle.Id);
            Assert.AreEqual(0, player9.ToonHandle.ProgramId);
            Assert.AreEqual("1-Hero-1-1527252", players[0].ToonHandle !.ToString());
            Assert.AreEqual(StormTeam.Red, player9.Team);
            Assert.IsFalse(player9.IsWinner);
            Assert.AreEqual("Valeera", player9.PlayerHero !.HeroName);

            Assert.AreEqual("Dragon Shire", _stormReplay.MapInfo.MapName);
            Assert.AreEqual(637010888527768698, _stormReplay.Timestamp.Ticks);

            Assert.IsFalse(_stormReplay.HasObservers);
            Assert.IsTrue(_stormReplay.HasAI);
        }
Esempio n. 4
0
 private bool TimeSliceContains(StormPlayer result)
 {
     return(result.SpectateEvent == SpectateEvent.QuintupleKill ||
            result.SpectateEvent == SpectateEvent.QuadKill ||
            result.SpectateEvent == SpectateEvent.TripleKill ||
            result.SpectateEvent == SpectateEvent.Taunt);
 }
Esempio n. 5
0
        public void StormReplayDetailsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("Steph", player0.Name);
            Assert.AreEqual(1, player0.ToonHandle !.Region);
            Assert.AreEqual(1, player0.ToonHandle.Realm);
            Assert.AreEqual(StormTeam.Blue, player0.Team);
            Assert.AreEqual("1-Hero-1-1182829", player0.ToonHandle.ToString());
            Assert.IsFalse(player0.IsWinner);
            Assert.AreEqual("Greymane", player0.PlayerHero !.HeroName);
            Assert.AreEqual(StormRegion.US, player0.ToonHandle.StormRegion);
            Assert.AreEqual(StormRegion.US, _stormReplay.Region);
            StormPlayer player9 = players[9];

            Assert.AreEqual("Pinzs", player9.Name);
            Assert.AreEqual(1, player9.ToonHandle !.Region);
            Assert.AreEqual(1, player9.ToonHandle !.Realm);
            Assert.AreEqual(StormTeam.Red, player9.Team);
            Assert.IsTrue(player9.IsWinner);
            Assert.AreEqual("Rehgar", player9.PlayerHero !.HeroName);
            Assert.AreEqual(StormTeam.Red, _stormReplay.WinningTeam);

            Assert.AreEqual("Volskaya Foundry", _stormReplay.MapInfo.MapName);
            Assert.AreEqual(637120547862521860, _stormReplay.Timestamp.Ticks);

            Assert.IsFalse(_stormReplay.HasAI);
            Assert.IsFalse(_stormReplay.HasObservers);

            Assert.AreEqual(0, _stormReplay.StormObservers.ToList().Count);
            Assert.AreEqual(0, _stormReplay.PlayersObserversCount);
        }
Esempio n. 6
0
        public void PlayersScoreResultTest()
        {
            StormPlayer player      = _stormReplay.StormPlayers.ToList()[3];
            ScoreResult?scoreResult = player.ScoreResult;

            Assert.IsNull(scoreResult);
        }
Esempio n. 7
0
        public void StormReplayInitDataTest()
        {
            Assert.AreEqual(36047320, _stormReplay.RandomValue);
            Assert.AreEqual(StormGameMode.Custom, _stormReplay.GameMode);

            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("GreymaneDoctorVar3", player0.PlayerLoadout.SkinAndSkinTint);
            Assert.AreEqual("MountCloudWhimsy", player0.PlayerLoadout.MountAndMountTint);
            Assert.IsFalse(player0.IsSilenced);
            Assert.IsFalse(player0.IsVoiceSilenced);
            Assert.IsFalse(player0.IsBlizzardStaff);
            Assert.IsFalse(player0.HasActiveBoost);
            Assert.AreEqual("BannerDFEsportsWarChestRareDignitas", player0.PlayerLoadout.Banner);
            Assert.AreEqual("SprayStaticHGC2017EUDignitas", player0.PlayerLoadout.Spray);
            Assert.AreEqual("JainaA", player0.PlayerLoadout.AnnouncerPack);
            Assert.AreEqual("GreymaneBase_VoiceLine01", player0.PlayerLoadout.VoiceLine);
            Assert.AreEqual(15, player0.HeroMasteryTiersCount);
            Assert.AreEqual("Barb", player0.HeroMasteryTiers.ToList()[2].HeroAttributeId);
            Assert.AreEqual(1, player0.HeroMasteryTiers.ToList()[2].TierLevel);
            Assert.AreEqual(PlayerType.Human, player0.PlayerType);

            List <StormPlayer> playersWithObs = _stormReplay.StormPlayersWithObservers.ToList();
            StormPlayer        player8        = playersWithObs[8];

            Assert.AreEqual(PlayerType.Observer, player8.PlayerType);
            Assert.AreEqual(PlayerDifficulty.Unknown, player8.PlayerDifficulty);
        }
        public void StormReplayInitDataTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.IsFalse(player0.IsSilenced);
            Assert.IsFalse(player0.IsVoiceSilenced);
            Assert.IsTrue(player0.IsBlizzardStaff);
            Assert.IsFalse(player0.HasActiveBoost);
        }
Esempio n. 9
0
        public void PlayersScoreResultTest()
        {
            StormPlayer player = _stormReplay.StormPlayers.ToList()[8];

            Assert.AreEqual("Malfurion", player.PlayerHero !.HeroName);

            ScoreResult?scoreResult = player.ScoreResult;

            Assert.AreEqual(8, scoreResult !.Assists);
            Assert.AreEqual(3, scoreResult.ClutchHealsPerformed);
            Assert.AreEqual(8266, scoreResult.CreepDamage);
            Assert.AreEqual(18772, scoreResult.DamageSoaked);
            Assert.AreEqual(21863, scoreResult.DamageTaken);
            Assert.AreEqual(1, scoreResult.Deaths);
            Assert.AreEqual(0, scoreResult.EscapesPerformed);
            Assert.AreEqual(7123, scoreResult.ExperienceContribution);
            Assert.AreEqual(65166, scoreResult.Healing);
            Assert.AreEqual(13986, scoreResult.HeroDamage);
            Assert.AreEqual(8, scoreResult.HighestKillStreak);
            Assert.AreEqual(18, scoreResult.Level);
            Assert.AreEqual(0, scoreResult.MercCampCaptures);
            Assert.AreEqual(62359, scoreResult.MetaExperience);
            Assert.AreEqual(12804, scoreResult.MinionDamage);
            Assert.AreEqual(0, scoreResult.Multikill);
            Assert.AreEqual(0, scoreResult.OutnumberedDeaths);
            Assert.AreEqual(null, scoreResult.PhysicalDamage);
            Assert.AreEqual(0, scoreResult.ProtectionGivenToAllies);
            Assert.AreEqual(0, scoreResult.SelfHealing);
            Assert.AreEqual(22012, scoreResult.SiegeDamage);
            Assert.AreEqual(0, scoreResult.SoloKills);
            Assert.AreEqual(null, scoreResult.SpellDamage);
            Assert.AreEqual(9208, scoreResult.StructureDamage);
            Assert.AreEqual(0, scoreResult.SummonDamage);
            Assert.AreEqual(8, scoreResult.Takedowns);
            Assert.AreEqual(12196, scoreResult.TeamfightDamageTaken);
            Assert.AreEqual(0, scoreResult.TeamfightEscapesPerformed);
            Assert.AreEqual(16378, scoreResult.TeamfightHealingDone);
            Assert.AreEqual(5228, scoreResult.TeamfightHeroDamage);
            Assert.AreEqual(new TimeSpan(0, 0, 28), scoreResult.TimeCCdEnemyHeroes);
            Assert.AreEqual(new TimeSpan(0, 0, 25), scoreResult.TimeRootingEnemyHeroes);
            Assert.AreEqual(new TimeSpan(0, 0, 0), scoreResult.TimeSilencingEnemyHeroes);
            Assert.AreEqual(new TimeSpan(0, 0, 56), scoreResult.TimeSpentDead);
            Assert.AreEqual(new TimeSpan(0, 0, 0), scoreResult.TimeStunningEnemyHeroes);
            Assert.AreEqual(0, scoreResult.TownKills);
            Assert.AreEqual(0, scoreResult.VengeancesPerformed);
            Assert.AreEqual(0, scoreResult.WatchTowerCaptures);
            Assert.AreEqual(1, scoreResult.Tier1Talent);
            Assert.AreEqual(3, scoreResult.Tier4Talent);
            Assert.AreEqual(3, scoreResult.Tier7Talent);
            Assert.AreEqual(1, scoreResult.Tier10Talent);
            Assert.AreEqual(3, scoreResult.Tier13Talent);
            Assert.AreEqual(2, scoreResult.Tier16Talent);
            Assert.AreEqual(null, scoreResult.Tier20Talent);
        }
        public void StormReplayDetailsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("kpaxchaos", player0.Name);
            Assert.AreEqual(2, player0.ToonHandle !.Region);
            Assert.AreEqual(1, player0.ToonHandle.Realm);
            Assert.AreEqual(StormRegion.EU, player0.ToonHandle.StormRegion);
            Assert.AreEqual(StormTeam.Blue, _stormReplay.WinningTeam);
        }
Esempio n. 11
0
        public async Task <Uri> SaveClip(StormPlayer stormPlayer, StormReplay stormReplay)
        {
            // Limited api functionality
            // Cannot modify the title of the clip
            // Cannot modify the time of the clip from 30 to 60 seconds
            // When twitch allow it, modify the title to a format like: [Hero] [Map] - [Quintuple, Quad, Tripple] kill
            CreatedClipResponse response = await twitchAPI.Helix.Clips.CreateClipAsync(settings.TwitchBroadcasterId);

            GetClipResponse clip = await twitchAPI.Helix.Clips.GetClipAsync(response.CreatedClips[0].Id);

            return(new Uri(clip.Clips[0].Url));
        }
        public void StormReplayInitDataTest()
        {
            Assert.AreEqual(2143281452, _stormReplay.RandomValue);
            Assert.AreEqual(StormGameMode.QuickMatch, _stormReplay.GameMode);

            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("BrightwingLuxMonkeyWhite", player0.PlayerLoadout.SkinAndSkinTint);
            Assert.AreEqual(string.Empty, player0.PlayerLoadout.MountAndMountTint);
            Assert.IsFalse(player0.IsSilenced);
            Assert.IsFalse(player0.IsVoiceSilenced);
            Assert.IsFalse(player0.IsBlizzardStaff);
            Assert.IsTrue(player0.HasActiveBoost);
            Assert.AreEqual("BannerD3WitchDoctorRareVar2", player0.PlayerLoadout.Banner);
            Assert.AreEqual("SprayStaticWinterRewardReigndeer", player0.PlayerLoadout.Spray);
            Assert.AreEqual("MurkyA", player0.PlayerLoadout.AnnouncerPack);
            Assert.AreEqual("BrightwingMonkey_VoiceLine01", player0.PlayerLoadout.VoiceLine);
            Assert.AreEqual(22, player0.HeroMasteryTiersCount);
            Assert.AreEqual("Arts", player0.HeroMasteryTiers.ToList()[2].HeroAttributeId);
            Assert.AreEqual(3, player0.HeroMasteryTiers.ToList()[2].TierLevel);
        }
Esempio n. 13
0
        public void StormReplayDetailsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("AZTDoubt", player0.Name);
            Assert.AreEqual(1, player0.ToonHandle !.Region);
            Assert.AreEqual(1, player0.ToonHandle.Realm);
            Assert.AreEqual(7027042, player0.ToonHandle.Id);
            Assert.AreEqual(1869768008, player0.ToonHandle.ProgramId);
            Assert.AreEqual(StormTeam.Blue, player0.Team);
            Assert.IsFalse(player0.IsWinner);
            Assert.AreEqual("Greymane", player0.PlayerHero !.HeroName);
            Assert.AreEqual(StormRegion.US, player0.ToonHandle.StormRegion);
            Assert.AreEqual(StormTeam.Red, _stormReplay.WinningTeam);

            StormPlayer player = players[9];

            Assert.AreEqual("FiZiX", player.Name);
            Assert.AreEqual(1, player.ToonHandle !.Region);
            Assert.AreEqual(1, player.ToonHandle.Realm);
            Assert.AreEqual(StormTeam.Red, player.Team);
            Assert.IsTrue(player.IsWinner);
            Assert.AreEqual("Li-Ming", player.PlayerHero !.HeroName);

            Assert.AreEqual("Battlefield of Eternity", _stormReplay.MapInfo.MapName);
            Assert.AreEqual(636619794857150779, _stormReplay.Timestamp.Ticks);

            List <StormPlayer> playersWithObs = _stormReplay.StormPlayersWithObservers.ToList();
            StormPlayer        player8        = playersWithObs[8];

            Assert.AreEqual(StormTeam.Observer, player8.Team);

            Assert.IsTrue(_stormReplay.HasObservers);
            Assert.IsFalse(_stormReplay.HasAI);
            Assert.AreEqual(1, _stormReplay.PlayersObserversCount);
            Assert.IsNull(_stormReplay.StormObservers.ToList()[0].PlayerHero);
        }
Esempio n. 14
0
        public void StormReplayInitDataTest()
        {
            Assert.AreEqual(4013533878, _stormReplay.RandomValue);
            Assert.AreEqual(StormGameMode.QuickMatch, _stormReplay.GameMode);

            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual(string.Empty, player0.PlayerLoadout.SkinAndSkinTint);
            Assert.AreEqual("BattleBeastNexus", player0.PlayerLoadout.MountAndMountTint);
            Assert.IsFalse(player0.IsSilenced);
            Assert.IsFalse(player0.IsVoiceSilenced);
            Assert.IsFalse(player0.IsBlizzardStaff);
            Assert.IsFalse(player0.HasActiveBoost);
            Assert.AreEqual(string.Empty, player0.PlayerLoadout.Banner);
            Assert.AreEqual(string.Empty, player0.PlayerLoadout.Spray);
            Assert.AreEqual(string.Empty, player0.PlayerLoadout.AnnouncerPack);
            Assert.AreEqual(string.Empty, player0.PlayerLoadout.VoiceLine);
            Assert.AreEqual(0, player0.HeroMasteryTiersCount);

            Assert.IsFalse(_stormReplay.HasObservers);
            Assert.IsFalse(_stormReplay.HasAI);
        }
Esempio n. 15
0
        public void StormReplayInitDataTest()
        {
            Assert.AreEqual(1102687070, _stormReplay.RandomValue);
            Assert.AreEqual(StormGameMode.StormLeague, _stormReplay.GameMode);

            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("GreymaneDoctorVar1", player0.PlayerLoadout.SkinAndSkinTint);
            Assert.AreEqual("MountCloud", player0.PlayerLoadout.MountAndMountTint);
            Assert.IsFalse(player0.IsSilenced);
            Assert.IsFalse(player0.IsVoiceSilenced);
            Assert.IsFalse(player0.IsBlizzardStaff);
            Assert.IsFalse(player0.HasActiveBoost);
            Assert.AreEqual("BannerOWDVaIconicRare", player0.PlayerLoadout.Banner);
            Assert.AreEqual("SprayStaticComicSweetChromie", player0.PlayerLoadout.Spray);
            Assert.AreEqual("DeckardA", player0.PlayerLoadout.AnnouncerPack);
            Assert.AreEqual("GreymaneBase_VoiceLine04", player0.PlayerLoadout.VoiceLine);
            Assert.AreEqual("Greymane", player0.PlayerHero !.HeroId);
            Assert.AreEqual(24, player0.HeroMasteryTiersCount);
            Assert.AreEqual("Auri", player0.HeroMasteryTiers.ToList()[2].HeroAttributeId);
            Assert.AreEqual(1, player0.HeroMasteryTiers.ToList()[2].TierLevel);
        }
Esempio n. 16
0
        public void StormReplayAttributeEventsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player  = players[9];

            Assert.AreEqual("5v5", _stormReplay.TeamSize);
            Assert.AreEqual(PlayerDifficulty.Elite, player.PlayerDifficulty);
            Assert.AreEqual(StormGameSpeed.Faster, _stormReplay.GameSpeed);
            Assert.AreEqual(StormGameMode.StormLeague, _stormReplay.GameMode);
            Assert.AreEqual("Rehg", player.PlayerHero !.HeroAttributeId);
            Assert.AreEqual("Reh8", player.PlayerLoadout.SkinAndSkinTintAttributeId);
            Assert.AreEqual(string.Empty, player.PlayerLoadout.MountAndMountTintAttributeId);
            Assert.AreEqual("BN03", player.PlayerLoadout.BannerAttributeId);
            Assert.AreEqual("SY3K", player.PlayerLoadout.SprayAttributeId);
            Assert.AreEqual("RE05", player.PlayerLoadout.VoiceLineAttributeId);
            Assert.AreEqual("DEA0", player.PlayerLoadout.AnnouncerPackAttributeId);
            Assert.AreEqual(20, player.PlayerHero.HeroLevel);

            List <string?> ban0List = _stormReplay.GetTeamBans(StormTeam.Blue).ToList();
            List <string?> ban1List = _stormReplay.GetTeamBans(StormTeam.Red).ToList();

            Assert.AreEqual("Garr", ban0List[1]);
            Assert.AreEqual("DEAT", ban1List[1]);
        }
        public void StormReplayAttributeEventsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player  = players[9];

            Assert.AreEqual("5v5", _stormReplay.TeamSize);
            Assert.AreEqual(PlayerDifficulty.Elite, player.PlayerDifficulty);
            Assert.AreEqual(StormGameSpeed.Faster, _stormReplay.GameSpeed);
            Assert.AreEqual(StormGameMode.QuickMatch, _stormReplay.GameMode);
            Assert.AreEqual("Hanz", player.PlayerHero !.HeroAttributeId);
            Assert.AreEqual("Han2", player.PlayerLoadout.SkinAndSkinTintAttributeId);
            Assert.AreEqual("Arm2", player.PlayerLoadout.MountAndMountTintAttributeId);
            Assert.AreEqual("BNaf", player.PlayerLoadout.BannerAttributeId);
            Assert.AreEqual("SY96", player.PlayerLoadout.SprayAttributeId);
            Assert.AreEqual("HA01", player.PlayerLoadout.VoiceLineAttributeId);
            Assert.AreEqual("AHAN", player.PlayerLoadout.AnnouncerPackAttributeId);
            Assert.AreEqual(20, player.PlayerHero.HeroLevel);

            List <string?> ban0List = _stormReplay.GetTeamBans(StormTeam.Blue).ToList();
            List <string?> ban1List = _stormReplay.GetTeamBans(StormTeam.Red).ToList();

            Assert.AreEqual(string.Empty, ban0List[1]);
            Assert.AreEqual(string.Empty, ban1List[1]);
        }
        public void StormReplayDetailsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player0 = players[0];

            Assert.AreEqual("crazealot", player0.Name);
            Assert.AreEqual(1, player0.ToonHandle !.Region);
            Assert.AreEqual(1, player0.ToonHandle.Realm);
            Assert.AreEqual(StormTeam.Blue, player0.Team);
            Assert.IsTrue(player0.IsWinner);
            Assert.AreEqual("Brightwing", player0.PlayerHero !.HeroName);
            Assert.AreEqual(StormTeam.Blue, _stormReplay.WinningTeam);
            StormPlayer player1 = players[9];

            Assert.AreEqual("DumbleBore", player1.Name);
            Assert.AreEqual(1, player1.ToonHandle !.Region);
            Assert.AreEqual(1, player1.ToonHandle.Realm);
            Assert.AreEqual(StormTeam.Red, player1.Team);
            Assert.IsFalse(player1.IsWinner);
            Assert.AreEqual("Hanzo", player1.PlayerHero !.HeroName);

            Assert.AreEqual("Hanamura Temple", _stormReplay.MapInfo.MapName);
            Assert.AreEqual(636997822244093849, _stormReplay.Timestamp.Ticks);
        }
        public void StormReplayAttributeEventsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player  = players[9];

            Assert.AreEqual("5v5", _stormReplay.TeamSize);
            Assert.AreEqual(PlayerDifficulty.Veteran, player.PlayerDifficulty);
            Assert.AreEqual(StormGameSpeed.Faster, _stormReplay.GameSpeed);
            Assert.AreEqual(StormGameMode.Cooperative, _stormReplay.GameMode);
            Assert.AreEqual(string.Empty, player.PlayerHero !.HeroAttributeId);
            Assert.AreEqual(string.Empty, player.PlayerLoadout.SkinAndSkinTintAttributeId);
            Assert.AreEqual(string.Empty, player.PlayerLoadout.MountAndMountTintAttributeId);
            Assert.AreEqual(string.Empty, player.PlayerLoadout.BannerAttributeId);
            Assert.AreEqual(string.Empty, player.PlayerLoadout.SprayAttributeId);
            Assert.AreEqual(string.Empty, player.PlayerLoadout.VoiceLineAttributeId);
            Assert.AreEqual(string.Empty, player.PlayerLoadout.AnnouncerPackAttributeId);
            Assert.AreEqual(1, player.PlayerHero.HeroLevel);

            List <string?> ban0List = _stormReplay.GetTeamBans(StormTeam.Blue).ToList();
            List <string?> ban1List = _stormReplay.GetTeamBans(StormTeam.Red).ToList();

            Assert.AreEqual(string.Empty, ban0List[1]);
            Assert.AreEqual(string.Empty, ban1List[1]);
        }
Esempio n. 20
0
        public void StormReplayAttributeEventsTest()
        {
            List <StormPlayer> players = _stormReplay.StormPlayers.ToList();
            StormPlayer        player  = players[9];

            Assert.AreEqual("5v5", _stormReplay.TeamSize);
            Assert.AreEqual(PlayerDifficulty.Elite, player.PlayerDifficulty);
            Assert.AreEqual(StormGameSpeed.Faster, _stormReplay.GameSpeed);
            Assert.AreEqual(StormGameMode.Custom, _stormReplay.GameMode);
            Assert.AreEqual("Wiza", player.PlayerHero !.HeroAttributeId);
            Assert.AreEqual("WizI", player.PlayerLoadout.SkinAndSkinTintAttributeId);
            Assert.AreEqual("CLO0", player.PlayerLoadout.MountAndMountTintAttributeId);
            Assert.AreEqual("BN6d", player.PlayerLoadout.BannerAttributeId);
            Assert.AreEqual("SY81", player.PlayerLoadout.SprayAttributeId);
            Assert.AreEqual("WZ04", player.PlayerLoadout.VoiceLineAttributeId);
            Assert.AreEqual("AFIR", player.PlayerLoadout.AnnouncerPackAttributeId);
            Assert.AreEqual(20, player.PlayerHero.HeroLevel);

            List <string?> ban0List = _stormReplay.GetTeamBans(StormTeam.Blue).ToList();
            List <string?> ban1List = _stormReplay.GetTeamBans(StormTeam.Red).ToList();

            Assert.AreEqual("Diab", ban0List[1]);
            Assert.AreEqual("Tra0", ban1List[1]);
        }
Esempio n. 21
0
        private static void PlayerInfo(StormPlayer player, PartyIconColor?partyIcon)
        {
            if (player is null)
            {
                throw new ArgumentNullException(nameof(player));
            }

            if (player.PlayerType != PlayerType.Computer)
            {
                StringBuilder stringBuilder = new();
                StringBuilder playerBuilder = stringBuilder;

                // party
                if (partyIcon.HasValue)
                {
                    playerBuilder.Append($"[{(int)partyIcon}]");
                }
                else
                {
                    playerBuilder.Append($"{"[-]"}");
                }

                // battletag
                if (!string.IsNullOrEmpty(player.BattleTagName))
                {
                    playerBuilder.Append($" {player.BattleTagName,-22}");
                }
                else
                {
                    playerBuilder.Append($" {player.Name,-22}");
                }

                // account level
                if (player.AccountLevel.HasValue && player.AccountLevel.Value > 0)
                {
                    string level = $" [Level:{player.AccountLevel.Value}]";
                    playerBuilder.Append($"{level,-14}");
                }
                else
                {
                    playerBuilder.Append($"{" [Level:???]",-14}");
                }

                // toon handle
                playerBuilder.Append($" [Toon:{player.ToonHandle}]");

                Console.WriteLine(playerBuilder);
            }
            else if (player.PlayerType == PlayerType.Computer)
            {
                Console.WriteLine($"[-] {player.Name}");
            }

            if (player.HeroMasteryTiers.ToDictionary(x => x.HeroAttributeId, x => x.TierLevel).TryGetValue(player.PlayerHero !.HeroAttributeId, out int tierLevel))
            {
                if (tierLevel == 2 && player.PlayerHero.HeroLevel < 25)
                {
                    player.PlayerHero.HeroLevel = 25;
                }
                else if (tierLevel == 3 && player.PlayerHero.HeroLevel < 50)
                {
                    player.PlayerHero.HeroLevel = 50;
                }
                else if (tierLevel == 4 && player.PlayerHero.HeroLevel < 75)
                {
                    player.PlayerHero.HeroLevel = 75;
                }
                else if (tierLevel == 5 && player.PlayerHero.HeroLevel < 100)
                {
                    player.PlayerHero.HeroLevel = 100;
                }
            }

            if (player.PlayerType != PlayerType.Observer)
            {
                // hero name
                StringBuilder heroBuilder = new($"{player.PlayerHero!.HeroName,-16}");

                // hero level
                if (player.IsAutoSelect)
                {
                    heroBuilder.Append($"{" [Level:Auto]",-14}");
                }
                else
                {
                    string level = $" [Level:{player.PlayerHero.HeroLevel}]";
                    heroBuilder.Append($"{level,-14}");
                }

                // hero unit id
                heroBuilder.Append($" [ID:{player.PlayerHero.HeroUnitId}]");

                Console.WriteLine($"    Hero: {heroBuilder}");

                if (player.MatchAwards != null)
                {
                    foreach (MatchAwardType matchAwardType in player.MatchAwards)
                    {
                        Console.WriteLine($"    Award: {matchAwardType}");
                    }
                }

                // talents
                if (_showPlayerTalents)
                {
                    Console.WriteLine();
                    Console.WriteLine("Talents");

                    Console.Write($"{"Level 1:",10}");
                    if (player.Talents.Count >= 1)
                    {
                        Console.WriteLine($" {player.Talents[0].TalentNameId}");
                    }
                    else
                    {
                        Console.WriteLine();
                    }

                    Console.Write($"{"Level 4:",10}");
                    if (player.Talents.Count >= 2)
                    {
                        Console.WriteLine($" {player.Talents[1].TalentNameId}");
                    }
                    else
                    {
                        Console.WriteLine();
                    }

                    Console.Write($"{"Level 7:",10}");
                    if (player.Talents.Count >= 3)
                    {
                        Console.WriteLine($" {player.Talents[2].TalentNameId}");
                    }
                    else
                    {
                        Console.WriteLine();
                    }

                    Console.Write($"{"Level 10:",10}");
                    if (player.Talents.Count >= 4)
                    {
                        Console.WriteLine($" {player.Talents[3].TalentNameId}");
                    }
                    else
                    {
                        Console.WriteLine();
                    }

                    Console.Write($"{"Level 13:",10}");
                    if (player.Talents.Count >= 5)
                    {
                        Console.WriteLine($" {player.Talents[4].TalentNameId}");
                    }
                    else
                    {
                        Console.WriteLine();
                    }

                    Console.Write($"{"Level 16:",10}");
                    if (player.Talents.Count >= 6)
                    {
                        Console.WriteLine($" {player.Talents[5].TalentNameId}");
                    }
                    else
                    {
                        Console.WriteLine();
                    }

                    Console.Write($"{"Level 20:",10}");
                    if (player.Talents.Count >= 7)
                    {
                        Console.WriteLine($" {player.Talents[6].TalentNameId}");
                    }
                    else
                    {
                        Console.WriteLine();
                    }

                    Console.WriteLine();
                }

                // stats
                if (_showPlayerStats)
                {
                    Console.WriteLine();
                    Console.WriteLine("Statistics");

                    if (player.ScoreResult != null)
                    {
                        Console.WriteLine("Combat");
                        Console.WriteLine($"{"Hero Kills:",_statisticsFieldWidth} {player.ScoreResult.SoloKills}");
                        Console.WriteLine($"{"Assists:",_statisticsFieldWidth} {player.ScoreResult.Assists}");
                        Console.WriteLine($"{"Takedowns:",_statisticsFieldWidth} {player.ScoreResult.Takedowns}");
                        Console.WriteLine($"{"Deaths:",_statisticsFieldWidth} {player.ScoreResult.Deaths}");

                        Console.WriteLine("Siege");
                        Console.WriteLine($"{"Minion Damage:",_statisticsFieldWidth} {player.ScoreResult.MinionDamage}");
                        Console.WriteLine($"{"Summon Damage:",_statisticsFieldWidth} {player.ScoreResult.SummonDamage}");
                        Console.WriteLine($"{"Structure Damage:",_statisticsFieldWidth} {player.ScoreResult.StructureDamage}");
                        Console.WriteLine($"{"Total Siege Damage:",_statisticsFieldWidth} {player.ScoreResult.SiegeDamage}");

                        Console.WriteLine("Hero");
                        Console.WriteLine($"{"Hero Damage:",_statisticsFieldWidth} {player.ScoreResult.HeroDamage}");

                        if (player.ScoreResult.DamageTaken > 0)
                        {
                            Console.WriteLine($"{"Damage Taken:",_statisticsFieldWidth} {player.ScoreResult.DamageTaken}");
                        }
                        else
                        {
                            Console.WriteLine($"{"Damage Taken:",_statisticsFieldWidth}");
                        }

                        if (player.ScoreResult.Healing > 0)
                        {
                            Console.WriteLine($"{"Healing/Shielding:",_statisticsFieldWidth} {player.ScoreResult.Healing}");
                        }
                        else
                        {
                            Console.WriteLine($"{"Healing/Shielding:",_statisticsFieldWidth}");
                        }

                        if (player.ScoreResult.SelfHealing > 0)
                        {
                            Console.WriteLine($"{"Self Healing:",_statisticsFieldWidth} {player.ScoreResult.SelfHealing}");
                        }
                        else
                        {
                            Console.WriteLine($"{"Self Healing:",_statisticsFieldWidth}");
                        }

                        Console.WriteLine($"{"Experience:",_statisticsFieldWidth} {player.ScoreResult.ExperienceContribution}");

                        Console.WriteLine("Time");
                        Console.WriteLine($"{"Spent Dead:",_statisticsFieldWidth} {player.ScoreResult.TimeSpentDead}");
                        Console.WriteLine($"{"Rooting Heroes:",_statisticsFieldWidth} {player.ScoreResult.TimeRootingEnemyHeroes}");
                        Console.WriteLine($"{"Silence Heroes:",_statisticsFieldWidth} {player.ScoreResult.TimeSilencingEnemyHeroes}");
                        Console.WriteLine($"{"Stun Heroes:",_statisticsFieldWidth} {player.ScoreResult.TimeStunningEnemyHeroes}");
                        Console.WriteLine($"{"CC Heroes:",_statisticsFieldWidth} {player.ScoreResult.TimeCCdEnemyHeroes}");
                        Console.WriteLine($"{"On Fire:",_statisticsFieldWidth} {player.ScoreResult.OnFireTimeonFire}");

                        Console.WriteLine("Other");
                        if (player.ScoreResult.SpellDamage.HasValue && player.ScoreResult.SpellDamage.Value > 0)
                        {
                            Console.WriteLine($"{"Spell Damage:",_statisticsFieldWidth} {player.ScoreResult.SpellDamage}");
                        }
                        else
                        {
                            Console.WriteLine($"{"Spell Damage:",_statisticsFieldWidth}");
                        }

                        if (player.ScoreResult.PhysicalDamage.HasValue && player.ScoreResult.PhysicalDamage.Value > 0)
                        {
                            Console.WriteLine($"{"Physical Damage:",_statisticsFieldWidth} {player.ScoreResult.PhysicalDamage}");
                        }
                        else
                        {
                            Console.WriteLine($"{"Physical Damage:",_statisticsFieldWidth}");
                        }

                        Console.WriteLine($"{"Merc Damage:",_statisticsFieldWidth} {player.ScoreResult.CreepDamage}");
                        Console.WriteLine($"{"Merc Camp Captures:",_statisticsFieldWidth} {player.ScoreResult.MercCampCaptures}");
                        Console.WriteLine($"{"Watch Tower Captures:",_statisticsFieldWidth} {player.ScoreResult.WatchTowerCaptures}");
                        Console.WriteLine($"{"Town Kills:",_statisticsFieldWidth} {player.ScoreResult.TownKills}");
                        Console.WriteLine($"{"Town Kills:",_statisticsFieldWidth} {player.ScoreResult.TownKills}");
                        Console.WriteLine($"{"Minion Kills:",_statisticsFieldWidth} {player.ScoreResult.MinionKills}");
                        Console.WriteLine($"{"Regen Globes:",_statisticsFieldWidth} {player.ScoreResult.RegenGlobes}");
                    }
                    else
                    {
                        Console.WriteLine("(NOT AVAILABLE)");
                    }

                    Console.WriteLine();
                }
            }
        }
Esempio n. 22
0
        public static void Parse(StormReplay replay, ReadOnlySpan <byte> source)
        {
            BitReader bitReader = new BitReader(source, EndianType.BigEndian);

            uint dependenciesLength = bitReader.ReadBits(6);

            for (int i = 0; i < dependenciesLength; i++)
            {
                bitReader.ReadBlobAsString(10);
            }

            // s2ma cache handles
            uint s2maCacheHandlesLength = bitReader.ReadBits(6);

            for (int i = 0; i < s2maCacheHandlesLength; i++)
            {
                if (bitReader.ReadStringFromBytes(4) != "s2ma")
                {
                    throw new StormParseException($"{_exceptionHeader}: s2ma cache");
                }

                bitReader.ReadAlignedBytes(36);
            }

            /*source.ReadAlignedBytes(94);
             * if (source.ReadStringFromBytes(4) != "Clsd")
             * throw new StormParseException($"{ExceptionHeader}: Clsd"); */

            // we're just going to skip all the way down to the s2mh

            bitReader.AlignToByte();

            /*for (; ;)
             * {
             *  if (source.ReadStringFromBytes(4) == "s2mh")
             *  {
             *      BitReader.Index -= 4;
             *      break;
             *  }
             *  else
             *  {
             *      BitReader.Index -= 3;
             *  }
             * }*/

            /*
             * for (; ; )
             * {
             *  if (source.ReadStringFromBytes(4) == "s2mv")
             *  {
             *      BitReader.Index -= 4;
             *      break;
             *  }
             *  else
             *  {
             *      BitReader.Index -= 3;
             *  }
             * }*/

            // first hit

            /*
             * BitReader.Index -= 1;
             *
             * uint s2mvCacheHandlesLength = source.ReadBits(8);
             *
             * for (int i = 0; i < s2mvCacheHandlesLength; i++)
             * {
             *  if (source.ReadStringFromBytes(4) != "s2mv")
             *      throw new StormParseException($"{ExceptionHeader}: s2mv cache");
             *
             *  source.ReadAlignedBytes(36);
             * }
             *
             * uint localeCount = source.ReadBits(5);
             *
             * for (int i = 0; i < localeCount; i++)
             * {
             *  source.ReadStringFromBits(32); // locale
             *
             *  uint s2mlCacheHandlesLength = source.ReadBits(6);
             *
             *  for (int j = 0; j < s2mlCacheHandlesLength; j++)
             *  {
             *      if (source.ReadStringFromBytes(4) != "s2ml")
             *          throw new StormParseException($"{ExceptionHeader}: s2ml cache");
             *
             *      source.ReadAlignedBytes(36);
             *  }
             * }
             *
             * source.ReadAlignedBytes(16);
             * uint sm2vCacheBlizzLength = source.ReadBits(8);
             *
             * for (int i = 0; i < sm2vCacheBlizzLength; i++)
             * {
             *
             * }
             */

            // second s2mv hit

            /*
             * for (; ; )
             * {
             *  if (source.ReadStringFromBytes(4) == "s2mv")
             *  {
             *      BitReader.Index -= 4;
             *      break;
             *  }
             *  else
             *  {
             *      BitReader.Index -= 3;
             *  }
             * }
             * for (int i = 0; i < 2; i++)
             * {
             *  if (source.ReadStringFromBytes(4) != "s2mv")
             *      throw new StormParseException($"{ExceptionHeader}: s2mv cache");
             *
             *  source.ReadAlignedBytes(36);
             * }
             *
             * source.ReadBits(1);
             *
             * uint region = source.ReadBits(8); // m_region
             * if (source.ReadStringFromBits(32) != "Hero") // m_programId
             *  throw new StormParseException($"{ExceptionHeader}: Not Hero");
             * source.ReadBits(32); // m_realm
             *
             * int blizzIdLength = (int)source.ReadBits(7);
             *
             * if (region >= 90)
             * {
             *  if (source.ReadStringFromBytes(2) != "T:")
             *      throw new StormParseException($"{ExceptionHeader}: Not blizz T:");
             *  source.ReadStringFromBytes(blizzIdLength);
             * }
             * else
             * {
             *  source.ReadStringFromBytes(blizzIdLength);
             *  source.ReadStringFromBytes(2);
             * }
             *
             * source.ReadBits(8); // m_region
             * if (source.ReadStringFromBits(32) != "Hero") // m_programId
             *  throw new StormParseException($"{ExceptionHeader}: Not Hero");
             * source.ReadBits(32); // m_realm
             * source.ReadLongBits(64); // m_id
             *
             *
             * int klj = (int)source.ReadBits(12);
             *
             * int sdfad = (int)source.ReadBits(12);
             *
             *
             * source.ReadBits(1); //temp
             *
             */

            for (; ;)
            {
                if (bitReader.ReadStringFromBytes(4) == "s2mh")
                {
                    bitReader.Index -= 4;
                    break;
                }
                else
                {
                    bitReader.Index -= 3;
                }
            }

            // source.ReadBits(???); // this depends on previous data (not byte aligned)

            // s2mh cache handles
            // uint s2mhCacheHandlesLength = source.ReadBits(6);
            // for (int i = 0; i < s2mhCacheHandlesLength; i++)
            for (int i = 0; i < s2maCacheHandlesLength; i++)
            {
                if (bitReader.ReadStringFromBytes(4) != "s2mh")
                {
                    throw new StormParseException($"{_exceptionHeader}: s2mh cache");
                }

                bitReader.ReadAlignedBytes(36);
            }

            // player collections

            uint collectionSize;

            // strings gone starting with build (ptr) 55929
            if (replay.ReplayBuild >= 48027)
            {
                collectionSize = bitReader.ReadBits(16);
            }
            else
            {
                collectionSize = bitReader.ReadBits(32);
            }

            for (uint i = 0; i < collectionSize; i++)
            {
                if (replay.ReplayBuild >= 55929)
                {
                    bitReader.ReadAlignedBytes(8); // most likey an identifier for the item; first six bytes are 0x00
                }
                else
                {
                    bitReader.ReadStringFromBytes(bitReader.ReadAlignedByte());
                }
            }

            // use to determine if the collection item is usable by the player (owns/free to play/internet cafe)
            if (bitReader.ReadBits(32) != collectionSize)
            {
                throw new StormParseException($"{_exceptionHeader}: collection difference");
            }

            for (int i = 0; i < collectionSize; i++)
            {
                // 16 is total player slots
                for (int j = 0; j < 16; j++)
                {
                    bitReader.ReadAlignedByte();
                    bitReader.ReadAlignedByte(); // more likely a boolean to get the value

                    if (replay.ReplayBuild < 55929)
                    {
                        // when the identifier is a string, set the value to the appropriate array index
                    }
                }
            }

            // Player info

            if (replay.ReplayBuild <= 47479 || replay.ReplayBuild == 47801 || replay.ReplayBuild == 47903)
            {
                // Builds that are not yet supported for detailed parsing
                // build 47801 is a ptr build that had new data in the battletag section, the data was changed in 47944 (patch for 47801)
                // GetBattleTags(replay, source);
                return;
            }

            replay.RandomValue = bitReader.ReadBits(32); // m_randomSeed

            bitReader.ReadAlignedBytes(4);

            uint playerListLength = bitReader.ReadBits(5);

            if (replay.PlayersWithObserversCount != playerListLength)
            {
                throw new StormParseException($"{_exceptionHeader}: mismatch on player list length - {playerListLength} to {replay.PlayersWithObserversCount}");
            }

            for (uint i = 0; i < playerListLength; i++)
            {
                bitReader.ReadBits(32);

                uint playerIndex = bitReader.ReadBits(5); // player index

                StormPlayer player = replay.ClientListByUserID[playerIndex];

                // toon handle
                uint playerRegion = bitReader.ReadBits(8); // m_region

                bitReader.EndianType = EndianType.LittleEndian;
                if (bitReader.ReadBits(32) != 1869768008) // m_programId
                {
                    throw new StormParseException($"{_exceptionHeader}: Not Hero");
                }
                bitReader.EndianType = EndianType.BigEndian;

                uint playerRealm = bitReader.ReadBits(32);     // m_realm
                long playerId    = bitReader.ReadLongBits(64); // m_id

                if (player.PlayerType == PlayerType.Human)
                {
                    if (player.ToonHandle !.Region != playerRegion)
                    {
                        throw new StormParseException($"{_exceptionHeader}: Mismatch on player region");
                    }
                    if (player.ToonHandle.Realm != playerRealm)
                    {
                        throw new StormParseException($"{_exceptionHeader}: Mismatch on player realm");
                    }
                    if (player.ToonHandle.Id != playerId)
                    {
                        throw new StormParseException($"{_exceptionHeader}: Mismatch on player id");
                    }
                }
                else if (player.PlayerType == PlayerType.Observer || player.PlayerType == PlayerType.Unknown)
                {
                    // observers don't have the information carried over to the details file and sometimes not the initdata file
                    player.ToonHandle ??= new ToonHandle();

                    player.ToonHandle.Region    = (int)playerRegion;
                    player.ToonHandle.ProgramId = 1869768008;
                    player.ToonHandle.Realm     = (int)playerRealm;
                    player.ToonHandle.Id        = (int)playerId;
                    player.PlayerType           = PlayerType.Observer;
                    player.Team = StormTeam.Observer;
                }

                // toon handle again but with T_ shortcut
                bitReader.ReadBits(8); // m_region

                bitReader.EndianType = EndianType.LittleEndian;
                if (bitReader.ReadBits(32) != 1869768008) // m_programId (Hero)
                {
                    throw new StormParseException($"{_exceptionHeader}: Not Hero");
                }
                bitReader.EndianType = EndianType.BigEndian;

                bitReader.ReadBits(32); // m_realm

                int idLength = (int)bitReader.ReadBits(7) + 2;

                player.ToonHandle ??= new ToonHandle();
                player.ToonHandle.ShortcutId = bitReader.ReadStringFromBytes(idLength);

                bitReader.ReadBits(6);

                if (replay.ReplayBuild <= 47479)
                {
                    // toon handle repeat again with T_ shortcut
                    bitReader.ReadBits(8);                          // m_region
                    if (bitReader.ReadStringFromBits(32) != "Hero") // m_programId
                    {
                        throw new StormParseException($"{_exceptionHeader}: Not Hero");
                    }
                    bitReader.ReadBits(32); // m_realm

                    idLength = (int)bitReader.ReadBits(7) + 2;
                    if (player.ToonHandle.ShortcutId != bitReader.ReadStringFromBytes(idLength))
                    {
                        throw new StormParseException($"{_exceptionHeader}: Duplicate shortcut id does not match");
                    }

                    bitReader.ReadBits(6);
                }

                bitReader.ReadBits(2);
                bitReader.ReadUnalignedBytes(25);
                bitReader.ReadBits(24);

                // ai games have 8 more bytes somewhere around here
                if (replay.GameMode == StormGameMode.Cooperative)
                {
                    bitReader.ReadUnalignedBytes(8);
                }

                bitReader.ReadBits(7);

                if (!bitReader.ReadBoolean())
                {
                    // repeat of the collection section above
                    if (replay.ReplayBuild > 51609 || replay.ReplayBuild == 47903 || replay.ReplayBuild == 47479)
                    {
                        bitReader.ReadBitArray(bitReader.ReadBits(12));
                    }
                    else if (replay.ReplayBuild > 47219)
                    {
                        // each byte has a max value of 0x7F (127)
                        bitReader.ReadUnalignedBytes((int)bitReader.ReadBits(15) * 2);
                    }
                    else
                    {
                        bitReader.ReadBitArray(bitReader.ReadBits(9));
                    }

                    bitReader.ReadBoolean();
                }

                bool isSilenced = bitReader.ReadBoolean(); // m_hasSilencePenalty
                if (player.PlayerType == PlayerType.Observer)
                {
                    player.IsSilenced = isSilenced;
                }

                if (replay.ReplayBuild >= 61718)
                {
                    bitReader.ReadBoolean();
                    bool isVoiceSilenced = bitReader.ReadBoolean(); // m_hasVoiceSilencePenalty
                    if (player.PlayerType == PlayerType.Observer)
                    {
                        player.IsVoiceSilenced = isVoiceSilenced;
                    }
                }

                if (replay.ReplayBuild >= 66977)
                {
                    bool isBlizzardStaff = bitReader.ReadBoolean(); // m_isBlizzardStaff
                    if (player.PlayerType == PlayerType.Observer)
                    {
                        player.IsBlizzardStaff = isBlizzardStaff;
                    }
                }

                if (bitReader.ReadBoolean())                        // is player in party
                {
                    player.PartyValue = bitReader.ReadLongBits(64); // players in same party will have the same exact 8 bytes of data
                }
                bitReader.ReadBoolean();

                string battleTagName = bitReader.ReadBlobAsString(7);
                int    poundIndex    = battleTagName.IndexOf('#');

                if (!string.IsNullOrEmpty(battleTagName) && poundIndex < 0)
                {
                    throw new StormParseException($"{_exceptionHeader}: Invalid battletag");
                }

                ReadOnlySpan <char> namePart = battleTagName.AsSpan().Slice(0, poundIndex);
                if (!namePart.SequenceEqual(player.Name))
                {
                    throw new StormParseException($"{_exceptionHeader}: Mismatch on battletag name with player name");
                }

                player.BattleTagName = battleTagName;

                if (replay.ReplayBuild >= 52860 || (replay.ReplayVersion.Major == 2 && replay.ReplayBuild >= 51978))
                {
                    player.AccountLevel = (int)bitReader.ReadBits(32);  // in custom games, this is a 0
                }
                if (replay.ReplayBuild >= 69947)
                {
                    bool hasActiveBoost = bitReader.ReadBoolean(); // m_hasActiveBoost
                    if (player.PlayerType == PlayerType.Observer)
                    {
                        player.HasActiveBoost = hasActiveBoost;
                    }
                }
            }
        }
        public static void Parse(StormReplay replay, ReadOnlySpan <byte> source)
        {
            BitReader bitReader = new BitReader(source, EndianType.LittleEndian);

            bitReader.ReadAlignedByte();
            bitReader.ReadUInt32Aligned();
            int count = bitReader.ReadInt32Aligned();

            // The 'PlayerID' in attributes does not seem to match any existing player array
            // It almost matches the 'Replay.Player' array, except for games with less than 10 players
            int replayPlayersWithOpenSlotsIndex = 1;

            ReplayAttributeEventType attribute;
            int         playerId;
            Span <char> value      = stackalloc char[4];
            Span <char> upperValue = stackalloc char[4];

            for (int i = 0; i < count; i++)
            {
                bitReader.ReadUInt32Aligned();                                       // namespace

                attribute = (ReplayAttributeEventType)bitReader.ReadUInt32Aligned(); // attrid
                playerId  = bitReader.ReadAlignedByte();
                Encoding.UTF8.GetChars(bitReader.ReadBytes(4), value);

                value.Reverse();

                for (int j = 0; j < value.Length; j++)
                {
                    upperValue[j] = char.ToUpperInvariant(value[j]);
                }

                switch (attribute)
                {
                case ReplayAttributeEventType.PlayerTypeAttribute:
                {
                    if (upperValue.SequenceEqual("COMP") || upperValue.SequenceEqual("HUMN"))
                    {
                        replay.PlayersWithOpenSlots[playerId - 1] = replay.Players[playerId - replayPlayersWithOpenSlotsIndex];
                    }

                    if (upperValue.SequenceEqual("COMP"))
                    {
                        replay.PlayersWithOpenSlots[playerId - 1].PlayerType = PlayerType.Computer;
                    }
                    else if (upperValue.SequenceEqual("HUMN"))
                    {
                        replay.PlayersWithOpenSlots[playerId - 1].PlayerType = PlayerType.Human;
                    }
                    else if (upperValue.SequenceEqual("OPEN"))
                    {
                        replayPlayersWithOpenSlotsIndex++;         // Less than 10 players in a Custom game
                    }
                    else
                    {
                        throw new StormParseException($"Unexpected value for PlayerTypeAttribute: {value.ToString()}");
                    }

                    break;
                }

                case ReplayAttributeEventType.TeamSizeAttribute:
                {
                    replay.TeamSize = value.Trim('\0').ToString();
                    break;
                }

                case ReplayAttributeEventType.DifficultyLevelAttribute:
                {
                    StormPlayer player = replay.PlayersWithOpenSlots[playerId - 1];

                    player.PlayerDifficulty = value.ToString() switch
                    {
                        "VyEy" => PlayerDifficulty.Beginner,
                        "Easy" => PlayerDifficulty.Recruit,
                        "Medi" => PlayerDifficulty.Adept,
                        "HdVH" => PlayerDifficulty.Veteran,
                        "VyHd" => PlayerDifficulty.Elite,
                        _ => PlayerDifficulty.Unknown,
                    };

                    break;
                }

                case ReplayAttributeEventType.GameSpeedAttribute:
                {
                    replay.GameSpeed = upperValue switch
                    {
                        Span <char> _ when upperValue.SequenceEqual("SLOR") => StormGameSpeed.Slower,
                        Span <char> _ when upperValue.SequenceEqual("SLOW") => StormGameSpeed.Slow,
                        Span <char> _ when upperValue.SequenceEqual("NORM") => StormGameSpeed.Normal,
                        Span <char> _ when upperValue.SequenceEqual("FAST") => StormGameSpeed.Fast,
                        Span <char> _ when upperValue.SequenceEqual("FASR") => StormGameSpeed.Faster,
                        _ => StormGameSpeed.Unknown,
                    };

                    break;
                }

                case ReplayAttributeEventType.GameModeAttribute:
                {
                    switch (upperValue)
                    {
                    case Span <char> _ when upperValue.SequenceEqual("PRIV"):
                        replay.GameMode = StormGameMode.Custom;

                        break;

                    case Span <char> _ when upperValue[1..4].SequenceEqual("AMM"):
                        if (replay.ReplayBuild < 33684)
                        {
                            replay.GameMode = StormGameMode.QuickMatch;
                        }
                        break;