public void TestLists() { var db = new ObjectDatabase(Array.Empty <int>(), null); const string TestFilePath = @"..\..\..\TestData\TestLists.w3o"; using var fileStream = File.OpenRead(TestFilePath); using var binaryReader = new BinaryReader(fileStream); var objectData = binaryReader.ReadObjectData(false); var errorLines = new List <string>(); // Verify unit data var unitData = objectData.UnitData; // Empty lists test var peasant = new Unit(UnitType.Peasant, db); peasant.TechtreeStructuresBuilt = Array.Empty <Unit>(); var keep = new Unit(UnitType.Keep, db); keep.ArtRequiredAnimationNames = Array.Empty <string>(); keep.PathingPlacementRequires = Array.Empty <PathingType>(); keep.TechtreeResearchesAvailable = Array.Empty <Upgrade>(); keep.StatsUnitClassification = Array.Empty <UnitClassification>(); var cannonTower = new Unit(UnitType.Cannontower, db); cannonTower.TechtreeRequirements = Array.Empty <Tech>(); var arcaneVault = new Unit(UnitType.Arcanevault, db); arcaneVault.TechtreeItemsMade = Array.Empty <Item>(); var paladin = new Unit(UnitType.Paladin, db); paladin.ArtSpecial = Array.Empty <string>(); paladin.AbilitiesNormal = Array.Empty <Ability>(); paladin.AbilitiesHero = Array.Empty <Ability>(); paladin.EditorTilesets = Array.Empty <Tileset>(); paladin.CombatTargetedAs = Array.Empty <Target>(); var ziggurat = new Unit(UnitType.Ziggurat, db); ziggurat.PathingPlacementPreventedBy = Array.Empty <PathingType>(); // Non-empty lists test var bloodmage = new Unit(UnitType.Bloodmage, db); bloodmage.ArtRequiredAnimationNames = new[] { "upgrade", "second" }; bloodmage.TechtreeStructuresBuilt = new[] { keep, cannonTower, arcaneVault }; bloodmage.TechtreeItemsSold = new[] { new Item(ItemType.ClawsOfAttack15), new Item(ItemType.CrownOfKings5) }; bloodmage.TechtreeRequirements = new[] { new Tech(peasant), new Tech(new Upgrade(UpgradeType.HumanAnimalBreeding)), new Tech(TechEquivalent.AnyTier1Hall) }; bloodmage.TechtreeRequirementsLevels = new[] { 1, 2, 0 }; bloodmage.ArtSpecial = new[] { @"buildings\other\ElvenFarm\ElvenFarm.mdl", string.Empty }; bloodmage.AbilitiesNormal = new Ability[] { new Abilities.Alarm(), new Abilities.Inventory() }; bloodmage.AbilitiesHero = new Ability[] { new Abilities.BloodMageFlameStrike(), new Abilities.BloodMageBanish() }; bloodmage.EditorTilesets = new[] { Tileset.Ashenvale, Tileset.All }; bloodmage.StatsUnitClassification = new[] { UnitClassification.Ancient, UnitClassification.Giant }; bloodmage.CombatTargetedAs = new[] { Target.Alive, Target.Hero }; var workshop = new Unit(UnitType.Workshop, db); workshop.TechtreeResearchesAvailable = new[] { new Upgrade(UpgradeType.HumanFlare), new Upgrade(UpgradeType.HumanFragShards) }; workshop.PathingPlacementRequires = new[] { PathingType.Unfloat, PathingType.Blighted }; workshop.PathingPlacementPreventedBy = new[] { PathingType.Unbuildable, PathingType.Unwalkable }; var expectUnit = new Dictionary <string, Type>() { // Bloodmage { "uani", bloodmage.ArtRequiredAnimationNamesRaw.GetType() }, { "ubui", bloodmage.TechtreeStructuresBuiltRaw.GetType() }, { "usei", bloodmage.TechtreeItemsSoldRaw.GetType() }, { "ureq", bloodmage.TechtreeRequirementsRaw.GetType() }, { "urqa", bloodmage.TechtreeRequirementsLevelsRaw.GetType() }, { "uspa", bloodmage.ArtSpecialRaw.GetType() }, { "uabi", bloodmage.AbilitiesNormalRaw.GetType() }, { "uhab", bloodmage.AbilitiesHeroRaw.GetType() }, { "util", bloodmage.EditorTilesetsRaw.GetType() }, { "utyp", bloodmage.StatsUnitClassificationRaw.GetType() }, { "utar", bloodmage.CombatTargetedAsRaw.GetType() }, // Workshop { "ures", workshop.TechtreeResearchesAvailableRaw.GetType() }, { "upap", workshop.PathingPlacementRequiresRaw.GetType() }, { "upar", workshop.PathingPlacementPreventedByRaw.GetType() }, // Arcane vault { "umki", arcaneVault.TechtreeItemsMadeRaw.GetType() }, }; foreach (var unit in objectData.GetAllUnits()) { var actualUnit = db.GetUnit(unit.OldId); foreach (var mod in unit.Modifications) { if (expectUnit.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != ObjectDataType.String) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = actualUnit.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } // Verify ability data var abilityData = objectData.AbilityData; // Empty lists test var aerialShackles = new Abilities.AerialShackles(db); aerialShackles.StatsBuffs[1] = Array.Empty <Buff>(); aerialShackles.ArtLightningEffects = Array.Empty <LightningEffect>(); var reveal = new Abilities.RevealArcaneTower(db); reveal.StatsEffects[1] = Array.Empty <Buff>(); // Non-empty lists test var stormHammers = new Abilities.StormHammers(db); stormHammers.StatsBuffs[1] = new[] { new Buff(BuffType.AcidBomb), new Buff(BuffType.AntiMagicShell) }; stormHammers.StatsEffects[1] = new[] { new Buff(BuffType.Blizzard_XHbz), new Buff(BuffType.CloudOfFog_Xclf) }; stormHammers.ArtLightningEffects = new[] { LightningEffect.AFOD, LightningEffect.SPLK }; var expectAbility = new Dictionary <string, Type>() { { "abuf", stormHammers.StatsBuffsRaw[1].GetType() }, { "aeff", stormHammers.StatsEffectsRaw[1].GetType() }, { "alig", stormHammers.ArtLightningEffectsRaw.GetType() }, }; foreach (var ability in objectData.GetAllAbilities()) { var actualAbility = db.GetAbility(ability.OldId) ?? throw new NullReferenceException(ability.OldId.ToRawcode()); foreach (var mod in ability.Modifications) { if (expectAbility.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != ObjectDataType.String) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = mod.Level > 0 ? actualAbility.Modifications[mod.Id, mod.Level] : actualAbility.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } Assert.AreEqual(0, errorLines.Count, string.Join("\r\n ", errorLines.Prepend("\r\nFound one or more errors:"))); }
public void TestEnums() { var db = new ObjectDatabase(Array.Empty <int>(), null); const string TestFilePath = @"..\..\..\TestData\TestEnums.w3o"; using var fileStream = File.OpenRead(TestFilePath); using var binaryReader = new BinaryReader(fileStream); var objectData = binaryReader.ReadObjectData(false); var errorLines = new List <string>(); var castType = new Dictionary <Type, ObjectDataType> { { typeof(int), ObjectDataType.Int }, { typeof(string), ObjectDataType.String }, }; // Verify unit data var unitData = objectData.UnitData; var peasant = new Unit(UnitType.Peasant, db); peasant.ArtShadowImageUnit = ShadowImage.ShadowFlyer; peasant.CombatArmorType = ArmorType.Stone; peasant.CombatAttack1AttackType = AttackType.Hero; peasant.CombatAttacksEnabled = AttackBits.Attack2Only; peasant.PathingAIPlacementType = AiBuffer.Resource; peasant.ArtTeamColor = TeamColor.Player1Red; var paladin = new Unit(UnitType.Paladin, db); paladin.CombatAttack2WeaponSound = CombatSound.MetalLightChop; paladin.CombatDeathType = DeathType.CanRaiseDoesDecay; paladin.CombatDefenseType = DefenseType.Normal; paladin.MovementType = MoveType.Amph; paladin.StatsPrimaryAttribute = AttributeType.AGI; var altar = new Unit(UnitType.Altarofkings, db); altar.ArtModelFileExtraVersions = VersionFlags.ReignOfChaos | VersionFlags.TheFrozenThrone; altar.CombatAttack1WeaponType = WeaponType.Mline; altar.PathingPlacementPreventedBy = new[] { PathingType.Unbuildable, PathingType.Blighted }; altar.PathingPlacementRequires = Array.Empty <PathingType>(); altar.StatsHitPointsRegenerationType = RegenType.Night; altar.StatsRace = UnitRace.Other; altar.StatsUnitClassification = new[] { UnitClassification.Ancient, UnitClassification.Mechanical, UnitClassification.Peon }; var expectUnit = new Dictionary <string, ObjectDataType>() { // Peasant { "ushu", castType[peasant.ArtShadowImageUnitRaw.GetType()] }, { "uarm", castType[peasant.CombatArmorTypeRaw.GetType()] }, { "ua1t", castType[peasant.CombatAttack1AttackTypeRaw.GetType()] }, { "uaen", castType[peasant.CombatAttacksEnabledRaw.GetType()] }, { "uabt", castType[peasant.PathingAIPlacementTypeRaw.GetType()] }, { "utco", castType[peasant.ArtTeamColorRaw.GetType()] }, // Paladin { "ucs2", castType[paladin.CombatAttack2WeaponSoundRaw.GetType()] }, { "udea", castType[paladin.CombatDeathTypeRaw.GetType()] }, { "udty", castType[paladin.CombatDefenseTypeRaw.GetType()] }, { "umvt", castType[paladin.MovementTypeRaw.GetType()] }, { "upra", castType[paladin.StatsPrimaryAttributeRaw.GetType()] }, // Altar { "uver", castType[altar.ArtModelFileExtraVersionsRaw.GetType()] }, { "ua1w", castType[altar.CombatAttack1WeaponTypeRaw.GetType()] }, { "upar", castType[altar.PathingPlacementPreventedByRaw.GetType()] }, { "upap", castType[altar.PathingPlacementRequiresRaw.GetType()] }, { "uhrt", castType[altar.StatsHitPointsRegenerationTypeRaw.GetType()] }, { "urac", castType[altar.StatsRaceRaw.GetType()] }, { "utyp", castType[altar.StatsUnitClassificationRaw.GetType()] }, }; foreach (var unit in objectData.GetAllUnits()) { var actualUnit = db.GetUnit(unit.OldId); foreach (var mod in unit.Modifications) { if (expectUnit.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != type) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = actualUnit.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } // Verify item data var itemData = objectData.ItemData; var gauntlets = new Item(ItemType.GauntletsOfOgreStrength3, db); gauntlets.StatsClassification = ItemClass.Miscellaneous; var expectItem = new Dictionary <string, ObjectDataType>() { // Gauntlets { "icla", castType[gauntlets.StatsClassificationRaw.GetType()] }, }; foreach (var item in objectData.GetAllItems()) { var actualItem = db.GetItem(item.OldId); foreach (var mod in item.Modifications) { if (expectItem.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != type) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = actualItem.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } // Verify destructable data var destructableData = objectData.DestructableData; var stoneDoor = new Destructable(DestructableType.RollingStoneDoor_ZTd6, db); stoneDoor.EditorCategory = DestructableCategory.BridgesRamps; stoneDoor.EditorTilesets = new[] { Tileset.LordaeronFall, Tileset.LordaeronSummer, Tileset.SunkenRuins }; var expectDestructable = new Dictionary <string, ObjectDataType>() { // Stone door { "bcat", castType[stoneDoor.EditorCategoryRaw.GetType()] }, { "btil", castType[stoneDoor.EditorTilesetsRaw.GetType()] }, }; foreach (var destructable in objectData.GetAllDestructables()) { var actualDestructable = db.GetDestructable(destructable.OldId); foreach (var mod in destructable.Modifications) { if (expectDestructable.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != type) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = actualDestructable.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } // Verify doodad data var doodadData = objectData.DoodadData; var trough = new Doodad(DoodadType.Trough, db); trough.EditorCategory = DoodadCategory.Cinematic; var expectDoodad = new Dictionary <string, ObjectDataType>() { // Trough { "dcat", castType[trough.EditorCategoryRaw.GetType()] }, }; foreach (var doodad in objectData.GetAllDoodads()) { var actualDoodad = db.GetDoodad(doodad.OldId); foreach (var mod in doodad.Modifications) { if (expectDoodad.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != type) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = actualDoodad.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } // Verify ability data var abilityData = objectData.AbilityData; var darkRangerSilence = new Abilities.DarkRangerSilence(db); darkRangerSilence.DataAttacksPrevented[1] = SilenceFlags.Melee | SilenceFlags.Ranged | SilenceFlags.Special | SilenceFlags.Spells; var demonHunterMetamorphosis = new Abilities.DemonHunterMetamorphosis(db); demonHunterMetamorphosis.DataMorphingFlags[1] = MorphFlags.RequiresPayment; var farseerFarSight = new Abilities.FarseerFarSight(db); farseerFarSight.DataDetectionType[1] = DetectionType.Invisible; var seaWitchFrostArrows = new Abilities.SeaWitchFrostArrows(db); seaWitchFrostArrows.DataStackFlags[1] = 0; var illidanChannel = new Abilities.IllidanChannel(db); illidanChannel.DataTargetType[1] = ChannelType.UnitOrPointTarget; illidanChannel.DataOptions[1] = ChannelFlags.UniversalSpell | ChannelFlags.UniqueCast; var alliedBuilding = new Abilities.AlliedBuilding(db); alliedBuilding.DataInteractionType[1] = InteractionFlags.AnyUnitWInventory | InteractionFlags.AnyNonBuilding; var rejuvination = new Abilities.Rejuvination(db); rejuvination.DataAllowWhenFull[1] = FullFlags.ManaOnly; var rootAncientProtector = new Abilities.RootAncientProtector(db); rootAncientProtector.DataUprootedDefenseType[1] = DefenseTypeInt.Normal; var preservation = new Abilities.Preservation(db); preservation.DataBuildingTypesAllowed[1] = PickFlags.General; var expectAbility = new Dictionary <string, ObjectDataType>() { // Dark ranger silence { "Nsi1", castType[darkRangerSilence.DataAttacksPreventedRaw[1].GetType()] }, // Demon hunter metamorphosis { "Eme2", castType[demonHunterMetamorphosis.DataMorphingFlagsRaw[1].GetType()] }, // Farseer far sight { "Ofs1", castType[farseerFarSight.DataDetectionTypeRaw[1].GetType()] }, // Sea witch frost arrows { "Hca4", castType[seaWitchFrostArrows.DataStackFlagsRaw[1].GetType()] }, // Illidan channel { "Ncl2", castType[illidanChannel.DataTargetTypeRaw[1].GetType()] }, { "Ncl3", castType[illidanChannel.DataOptionsRaw[1].GetType()] }, // Allied building { "Neu2", castType[alliedBuilding.DataInteractionTypeRaw[1].GetType()] }, // Rejuvination { "Rej3", castType[rejuvination.DataAllowWhenFullRaw[1].GetType()] }, // Root ancient protector { "Roo4", castType[rootAncientProtector.DataUprootedDefenseTypeRaw[1].GetType()] }, // Preservation { "Npr1", castType[preservation.DataBuildingTypesAllowedRaw[1].GetType()] }, }; foreach (var ability in objectData.GetAllAbilities()) { var actualAbility = db.GetAbility(ability.OldId) ?? throw new NullReferenceException(ability.OldId.ToRawcode()); foreach (var mod in ability.Modifications) { if (expectAbility.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != type) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = mod.Level > 0 ? actualAbility.Modifications[mod.Id, mod.Level] : actualAbility.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } // Verify buff data var buffData = objectData.BuffData; var avatar = new Buff(BuffType.Avatar, db); avatar.ArtLightning = LightningEffect.DRAB; avatar.ArtRequiredSpellDetail = SpellDetail.Medium; var expectBuff = new Dictionary <string, ObjectDataType>() { // Avatar { "flig", castType[avatar.ArtLightningRaw.GetType()] }, { "fspd", castType[avatar.ArtRequiredSpellDetailRaw.GetType()] }, }; foreach (var buff in objectData.GetAllBuffs()) { var actualBuff = db.GetBuff(buff.OldId); foreach (var mod in buff.Modifications) { if (expectBuff.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != type) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = actualBuff.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } // Verify upgrade data var upgradeData = objectData.UpgradeData; var humanAnimalBreeding = new Upgrade(UpgradeType.HumanAnimalBreeding, db); humanAnimalBreeding.StatsClass = UpgradeClass.Caster; humanAnimalBreeding.DataEffect2 = UpgradeEffect.Radl; var expectUpgrade = new Dictionary <string, ObjectDataType>() { // Human animal breeding { "gcls", castType[humanAnimalBreeding.StatsClassRaw.GetType()] }, { "gef2", castType[humanAnimalBreeding.DataEffect2Raw.GetType()] }, }; foreach (var upgrade in objectData.GetAllUpgrades()) { var actualUpgrade = db.GetUpgrade(upgrade.OldId); foreach (var mod in upgrade.Modifications) { if (expectUpgrade.TryGetValue(mod.Id.ToRawcode(), out var type)) { if (mod.Type != type) { errorLines.Add($"'{mod.Id.ToRawcode()}' TYPE: Expected {mod.Type}, actual {type}."); } var actualMod = actualUpgrade.Modifications[mod.Id]; var value = actualMod.Value; if (!object.Equals(mod.Value, value)) { errorLines.Add($"'{mod.Id.ToRawcode()}' VALUE: Expected {{{mod.Value}}}, actual {{{value}}}."); Assert.AreNotEqual(mod.Value.ToString(), value.ToString()); } } else { Assert.Fail($"Key '{mod.Id.ToRawcode()}' not found."); } } } Assert.AreEqual(0, errorLines.Count, string.Join("\r\n ", errorLines.Prepend("\r\nFound one or more errors:"))); }