commit 446fe518436240856e3b90181234dc15a853c610 Author: Ben Date: Thu Jun 4 18:15:54 2015 +0100 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7964536 --- /dev/null +++ b/.gitignore @@ -0,0 +1,189 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +x64/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Roslyn cache directories +*.ide/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +#NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +## TODO: Comment the next line if you want to checkin your +## web deploy settings but do note that will include unencrypted +## passwords +#*.pubxml + +# NuGet Packages Directory +packages/* +## TODO: If the tool you use requires repositories.config +## uncomment the next line +#!packages/repositories.config + +# Enable "build/" folder in the NuGet Packages folder since +# NuGet packages use it for MSBuild targets. +# This line needs to be after the ignore of the build folder +# (and the packages folder if the line above has been uncommented) +!packages/build/ + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +sql/ +*.Cache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# LightSwitch generated files +GeneratedArtifacts/ +_Pvt_Extensions/ +ModelManifest.xml \ No newline at end of file diff --git a/Filtration.Tests/Filtration.Tests.csproj b/Filtration.Tests/Filtration.Tests.csproj new file mode 100644 index 0000000..8a100cd --- /dev/null +++ b/Filtration.Tests/Filtration.Tests.csproj @@ -0,0 +1,85 @@ + + + + + Debug + AnyCPU + {E0693972-72C5-4E05-A9C5-A5943E4015C6} + Library + Properties + Filtration.Tests + Filtration.Tests + v4.5.1 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Moq.4.2.1502.0911\lib\net40\Moq.dll + + + ..\packages\NUnit.2.6.4\lib\nunit.framework.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + {55e0a34c-e039-43d7-a024-a4045401cdda} + Filtration + + + + + PreserveNewest + + + PreserveNewest + + + + + \ No newline at end of file diff --git a/Filtration.Tests/Models/TestLootFilterBlock.cs b/Filtration.Tests/Models/TestLootFilterBlock.cs new file mode 100644 index 0000000..5a06e88 --- /dev/null +++ b/Filtration.Tests/Models/TestLootFilterBlock.cs @@ -0,0 +1,55 @@ +using Filtration.Models; +using Filtration.Models.BlockItemTypes; +using NUnit.Framework; + +namespace Filtration.Tests.Models +{ + [TestFixture] + public class TestLootFilterBlock + { + [Test] + public void LootFilterBlock_BlockCount_ReturnsCorrectNumber() + { + // Arrange + var block = new LootFilterBlock(); + block.BlockItems.Add(new ItemLevelBlockItem()); + block.BlockItems.Add(new ItemLevelBlockItem()); + block.BlockItems.Add(new ItemLevelBlockItem()); + block.BlockItems.Add(new ItemLevelBlockItem()); + + // Act + var count = block.BlockCount(typeof (ItemLevelBlockItem)); + + // Assert + Assert.AreEqual(4, count); + } + + [Test] + public void LootFilterBlock_AddBlockItemAllowed_LessThanMaximum_ReturnsTrue() + { + // Arrange + var block = new LootFilterBlock(); + block.BlockItems.Add(new ItemLevelBlockItem()); + + // Act + bool result = block.AddBlockItemAllowed(typeof (ItemLevelBlockItem)); + + // Assert + Assert.IsTrue(result); + } + + [Test] + public void LootFilterBlock_AddBlockItemAllowed_MoreThanMaximum_ReturnsFalse() + { + // Arrange + var block = new LootFilterBlock(); + block.BlockItems.Add(new SoundBlockItem()); + + // Act + bool result = block.AddBlockItemAllowed(typeof (SoundBlockItem)); + + // Assert + Assert.IsFalse(result); + } + } +} diff --git a/Filtration.Tests/Models/TestLootFilterScript.cs b/Filtration.Tests/Models/TestLootFilterScript.cs new file mode 100644 index 0000000..a19eb3c --- /dev/null +++ b/Filtration.Tests/Models/TestLootFilterScript.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Filtration.Models; +using NUnit.Framework; + +namespace Filtration.Tests.Models +{ + [TestFixture] + public class TestLootFilterScript + { + [Test] + public void Validate_AtLeastOneBlock_Fail_ReturnsListWithCorrectError() + { + // Arrange + + var script = new LootFilterScript(); + + // Act + var result = script.Validate(); + + // Assert + Assert.AreEqual(1, result.Count(r => r.Contains("A script must have at least one block"))); + } + + [Test] + public void Validate_AtLeastOneBlock_Pass_ReturnsListWithoutError() + { + // Arrange + + var script = new LootFilterScript + { + LootFilterBlocks = new ObservableCollection {new LootFilterBlock()} + }; + + // Act + var result = script.Validate(); + + // Assert + Assert.AreEqual(0, result.Count(r => r.Contains("A script must have at least one block"))); + } + } +} diff --git a/Filtration.Tests/Properties/AssemblyInfo.cs b/Filtration.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0613ed4 --- /dev/null +++ b/Filtration.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Filtration.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Filtration.Tests")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("307a4c95-f595-485b-8a04-8c4d461c5325")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Filtration.Tests/Resources/ThioleLootFilter.txt b/Filtration.Tests/Resources/ThioleLootFilter.txt new file mode 100644 index 0000000..88df3cc --- /dev/null +++ b/Filtration.Tests/Resources/ThioleLootFilter.txt @@ -0,0 +1,914 @@ +###BETA VERSION 1.0.0.1-8 +#Please test and let me know via pm on reddit /u/brute_force or @Thiole in game if i am online + +#------------------------------------------------------------------ +#Fishing Rods First OFC +Show + Class Fishing Rods + SetTextColor 0 0 0 + SetBorderColor 0 0 0 + SetBackgroundColor 255 255 255 + +Show + Class "Quest Items" + +#####Shows better currency differently from lower currency for efficiency +##### skill gems +Show + Class "Active Skill Gems" "Support Skill Gems" + Quality >= 1 + SetFontSize 28 + SetTextColor 0 200 200 + SetBorderColor 0 200 200 + +Show + Class "Active Skill Gems" "Support Skill Gems" + SetFontSize 30 + SetTextColor 0 200 200 + +# Section: Chance bases + +Show + BaseType "Lapis Amulet" "Amber Amulet" + SetFontSize 25 + SetBorderColor 255 0 255 + +Show + SocketGroup RGB BGR + SetFontSize 30 + SetBorderColor 0 255 0 + +#Chance Bases +Show + BaseType "Prophecy Wand" "Leather Belt" "Occultist's Vestment" "Agate Amulet" "Gavel" + SetFontSize 30 + Rarity Normal + SetBorderColor 255 0 255 + + +################currency highlighting +Show + BaseType "Mirror of Kalandra" "Exalted Orb" "Divine Orb" "Orb of Regret" "Eternal Orb" "Regal Orb" + SetTextColor 200 200 130 + SetBorderColor 200 200 130 + SetBackgroundColor 0 0 0 + SetFontSize 32 + +Show + BaseType "Gemcutter's Prism" "Chaos Orb" "Orb of Fusing" "Orb of Alchemy" + SetBorderColor 200 200 130 + SetTextColor 200 200 130 + SetFontSize 30 + +Show + BaseType "Chromatic Orb" + SetBorderColor 0 255 0 + SetTextColor 200 200 130 + +Show + BaseType "Cartographer's Chisel" + SetBorderColor 0 0 255 + SetTextColor 200 200 130 +Show + Class Currency + SetTextColor 200 200 130 + SetFontSize 30 + SetBackgroundColor 0 0 0 + ############################# + +#unique Weapon slot start + +#staves / wands + + +Show + BaseType "Judgement Staff" "Long Staff" "Vile Staff" "Imperial Staff" "Prophecy Wand" "Spiraled Wand" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +Show + Class "Staves" + Rarity Unique + DropLevel = 48 + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +#Claws / Daggers + +Show + BaseType "Slaughter Knife" "Imperial Skean" "Thresher Claw" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +#Maces / Sceptres / Mauls / Axes + +Show + BaseType "Void Sceptre" "Siege Axe" "Gavel" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +#Swords + + + +#Bows +Show + BaseType "Spine Bow" "Imperial Bow" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +#Body Armor +Show + BaseType "Simple Robe" "Full Wyrmscale" "Varnished Coat" "Occultist's Vestment" "Glorious Plate" "Sacrificial Garb" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +#boots +Show + BaseType "Conjurer Boots" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 +#gloves +Show + BaseType "Vaal Gauntlets" "Strapped Mitts" "Conjurer Gloves" "Deerskin Gloves" "Murder Mitts" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + + +#helm +Show + BaseType "Sinner Tricorne" "Ursine Pelt" "Ezomyte Burgonet" "Great Crown" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +#######shields + +Show + BaseType "Champion Kite Shield" "Archon Kite Shield" "Titanium Spirit Shield" "Mosaic Kite Shield" "Branded Kite Shield" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +#######Amulet / Ring/ Belt + + +Show + BaseType "Amber Amulet" "Onyx Amulet" "Agate Amulet" "Chain Belt" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +Show + BaseType "Gold Ring" "Sapphire Ring" "Prismatic Ring" "Amethyst Ring" "Paua Ring" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +#Utility +Show + Class Jewel "Utility Flask" + Rarity Unique + SetTextColor 175 100 40 + SetBorderColor 175 100 40 + SetBackgroundColor 0 0 0 + +Show + Class Jewel + SetBorderColor 255 0 255 + +################### LEVEL BASED UNIQUE HIGHLIGHTING ############################# +Show + BaseType "Steelscale Gauntlets" "Prophet Crown" "Leather Cap" "Pine Buckler" + Rarity Unique + ItemLevel < 70 + SetTextColor 175 100 40 + SetBorderColor 0 0 0 + SetBackgroundColor 175 100 40 + + + #### + Show + Rarity Unique + SetFontSize 30 + SetTextColor 175 100 40 + SetBackgroundColor 0 0 0 + + + + +###########RARES##### + +#Global Rules + +Show + Class Amulets Rings Belt + Rarity Rare + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 60 0 200 + +Show + BaseType "Sai" "Moon Staff" "Maraketh Bow" "Tiger Hook" "Morning Star" "Tornado Wand" "Prophecy Wand" "Imbued Wand" "Infernal Sword" "Void Axe" "Jewelled Foil" "Harpy Rapier" "Judgement Staff" "Void Sceptre" "Midnight Blade" "Eternal Sword" "Harbinger Bow" "Imperial Claw" "Ambusher" "Platinum Kris" "Terror Claw" "Imperial Skean" "Demon Dagger" "Infernal Axe" + Rarity Rare + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + + + +##Level Scaling Rules +##low 60 bases############################### + +Show + BaseType "Terror Maul" "Vaal Rapier" "Imperial Staff" "Carnal Sceptre" "Imperial Bow" "Vaal Claw" "Thicket Bow" "Vaal Hatchet" "Royal Axe" "Nightmare Mace" "Vaal Blade" "Vaal Sceptre" + Rarity Rare + ItemLevel <= 70 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + +Show + BaseType "Terror Maul" "Vaal Rapier" "Imperial Staff" "Carnal Sceptre" "Imperial Bow" "Vaal Claw" "Thicket Bow" "Vaal Hatchet" "Royal Axe" "Nightmare Mace" "Vaal Blade" "Vaal Sceptre" + Rarity Rare + ItemLevel <= 75 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 +##### +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 75 + DropLevel >= 67 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + DropLevel >= 68 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + + +#######65-68 bases +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 73 + DropLevel > 65 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + + Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + DropLevel > 65 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + + ########## 60-65 bases +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 68 + DropLevel > 60 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + + Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 73 + DropLevel > 60 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + ##### 55-60 bases + + Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 64 + DropLevel > 55 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + + Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 70 + DropLevel > 55 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + ## 50-55 bases +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 58 + DropLevel > 50 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 65 + DropLevel > 50 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + +#####46-50 bases +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 54 + DropLevel > 46 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 60 + DropLevel > 46 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + +####42 - 48 bases + + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 48 + DropLevel > 42 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 51 + DropLevel > 42 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + ###38-42 + + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 42 + DropLevel > 38 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 48 + DropLevel > 38 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + ####33-38 +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 39 + DropLevel > 33 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 42 + DropLevel > 33 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + #### 27 - 32 +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 32 + DropLevel > 27 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 38 + DropLevel > 27 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + ##### 22- 27 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 27 + DropLevel > 22 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 31 + DropLevel > 22 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + +########18-22 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 22 + DropLevel > 18 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 27 + DropLevel > 18 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + ##12-18 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 18 + DropLevel > 12 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 24 + DropLevel > 12 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 +###### 8-12 +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 12 + DropLevel > 8 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 16 + DropLevel > 8 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 +#####1-8 +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 8 + DropLevel >= 0 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 100 0 200 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + Rarity Rare + ItemLevel <= 12 + DropLevel >= 0 + SetFontSize 30 + SetTextColor 255 255 0 + SetBorderColor 255 255 0 + SetBackgroundColor 0 0 0 + + + ###Rare highlighting + + Show + Rarity Rare + SetFontSize 27 + SetTextColor 255 255 0 170 + SetBorderColor 255 255 0 + SetBackgroundColor 100 40 40 200 + +#### Flask Highlighting + +Show + Class Flask + Quality > 13 + SetFontSize 25 + SetBorderColor 255 255 255 + +Show + Class Flask + Quality > 5 + SetFontSize 25 + +Show + Class "Utility Flask" + SetFontSize 30 + +###### RGB Highlighting + +Show + SocketGroup RGB BGR + SetFontSize 30 + SetTextColor 0 255 0 + SetBorderColor 0 255 0 + +########Chisel Recipe Highlighting + +Show + BaseType "Gavel" "Rock Breaker" "Stone Hammer" + Rarity Normal + SetFontSize 25 + SetBorderColor 0 0 255 +Show + BaseType "Gavel" "Rock Breaker" "Stone Hammer" + Rarity Magic + Quality > 15 + SetFontSize 25 + SetBorderColor 0 0 255 + +####### 6S/5L + +Show + LinkedSockets >= 5 + SetBorderColor 255 0 0 + SetFontSize 30 +Show + Sockets 6 + SetBorderColor 255 0 0 + SetFontSize 30 + +Show + LinkedSockets >= 3 + ItemLevel < 20 + + ########################### Normal Show Rules +Show + BaseType "Sai" "Moon Staff" "Maraketh Bow" "Tiger Hook" "Morning Star" "Tornado Wand" "Prophecy Wand" "Imbued Wand" "Infernal Sword" "Void Axe" "Jewelled Foil" "Harpy Rapier" "Judgement Staff" "Void Sceptre" "Midnight Blade" "Eternal Sword" "Harbinger Bow" "Imperial Claw" "Ambusher" "Platinum Kris" "Terror Claw" "Imperial Skean" "Demon Dagger" "Infernal Axe" + SetFontSize 30 + +### Show these whites up to 70 +Show + BaseType "Terror Maul" "Vaal Rapier" "Imperial Staff" "Carnal Sceptre" "Imperial Bow" "Vaal Claw" "Thicket Bow" "Vaal Hatchet" "Royal Axe" "Nightmare Mace" "Vaal Blade" "Vaal Sceptre" + Rarity Normal + SetFontSize 30 + ItemLevel <= 70 + +###### T1 Bases up to forever +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + DropLevel >= 68 + Rarity Normal + SetFontSize 30 + + +### T2 Bases up to 70 +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 70 + DropLevel > 64 + Rarity Normal + SetFontSize 30 + +###### T3 Bases up to 65 +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 65 + DropLevel > 59 + Rarity Normal + SetFontSize 30 + +#### t4 Bases up to 60 +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 60 + DropLevel > 53 + Rarity Normal + SetFontSize 30 + +###### T5 up to 56 + +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 56 + DropLevel > 49 + Rarity Normal + SetFontSize 30 + +##### t6 up to 50 + +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 50 + DropLevel > 44 + Rarity Normal + SetFontSize 30 + +##### t7 up to 45 +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 45 + DropLevel > 39 + Rarity Normal + SetFontSize 30 + +##### t8 up to 39 +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 39 + DropLevel > 33 + Rarity Normal + SetFontSize 30 + +##### t9 up to 34 + +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 34 + DropLevel > 28 + Rarity Normal + SetFontSize 30 + +#### t10 up to 30 +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 30 + DropLevel > 27 + Rarity Normal + SetFontSize 30 + +#t11 up to 29 + +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 29 + DropLevel > 21 + Rarity Normal + SetFontSize 30 + +#t12 bases up to 27 + +Show + Class "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 27 + DropLevel > 20 + Rarity Normal + SetFontSize 30 + +#T13 Bases up to 21 +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 21 + DropLevel > 15 + Rarity Normal + SetFontSize 29 + +#### t14 up to 17 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 17 + DropLevel > 10 + Rarity Normal + SetFontSize 30 + +####t15 up to 15 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 14 + DropLevel > 7 + Rarity Normal + SetFontSize 29 + +###t16 up to 10 +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 10 + DropLevel > 5 + Rarity Normal + SetFontSize 29 + +####t17 up to 7 + +Show + Class "Body Armour" "Gloves" "Boots" "Belt" "Helm" "Shield" "Quiver" "Bow" "Staves" "Two Hand Axes" "Claw" "One Hand Axe" "Wands" "One Hand Swords" "Thrusting One Hand Swords" "One Hand Mace" "Two Hand Mace" "Two Hand Sword" "Dagger" "Sceptre" + ItemLevel <= 7 + DropLevel >= 1 + Rarity Normal + SetFontSize 29 + + ##################### WHITES RULES DONE + + + #### Show rings at all levels for progression +Show +Class Ring Amulet +ItemLevel < 70 + +Show +Class Boots Gloves Helmet Shield Belt +Rarity Magic +SetFontSize 25 +ItemLevel < 20 + +#### Cant get map based level highlighting to work, DropLevel does not work for maps + +Show +Class "Map Fragments" "Maps" + + +##### Flask based level visibility + +### Small Flask to ~ End of A1N, REQ level 0 +Show +BaseType "Small Life Flask" "Small Mana Flask" +ItemLevel < 12 + SetFontSize 30 + SetBorderColor 255 255 255 + + +#### Medium Ends midway through a2, medium starts dropping around the same time as large, REQ LEVEL 4 + +Show + BaseType "Medium Life Flask" "Medium Mana Flask" + ItemLevel < 19 + SetFontSize 30 + SetBorderColor 255 255 255 + +#### Large Ends around the same time, these make mediums basically obsolete, i only keep mediums on because of 3x vendor recipe for another large REQ LEVEL 14 + + Show + BaseType "Large Life Flask" "Large Mana Flask" + ItemLevel < 20 + SetFontSize 30 + SetBorderColor 255 255 255 + + +#### Greater ends near the end of a2/beginning of a3 REQ LEVEL 18 + Show + BaseType "Greater Life Flask" "Greater Mana Flask" + ItemLevel < 24 + SetFontSize 30 + SetBorderColor 255 255 255 + + + #### Hides Midway through A3N REQ LEVEL 26 + Show + BaseType "Grand Life Flask" "Grand Mana Flask" + ItemLevel < 34 + SetFontSize 30 + SetBorderColor 255 255 255 + + ####Hides Near the end of Normal difficulty REQ LEVEL 34 + +Show + BaseType "Giant Life Flask" "Giant Mana Flask" + ItemLevel < 38 + SetFontSize 30 + SetBorderColor 255 255 255 + + ####Hides somewhere at the start of cruel REQ LEVEL 37 +Show + BaseType "Colossal Life Flask" "Colossal Mana Flask" + ItemLevel < 45 + SetFontSize 30 + SetBorderColor 255 255 255 + + ####### Hides Midway through cruel REQ LEVEL 45 + +Show + BaseType "Sacred Life Flask" "Sacred Mana Flask" + ItemLevel < 49 + SetFontSize 30 + SetBorderColor 255 255 255 + +### REQ LEVEL 53 + +Show + BaseType "Hallowed Life Flask" "Hallowed Mana Flask" + ItemLevel < 58 + SetFontSize 30 + SetBorderColor 255 255 255 + +##### REQ LEVEL 60 + +Show +BaseType "Sanctified Life Flask" "Sanctified Mana Flask" + ItemLevel < 70 + SetFontSize 30 + SetBorderColor 255 255 255 + +### REQ LEVEL 65 and 70 Respectively 2 best bases in game, always show + +Show + BaseType "Divine Life Flask" "Eternal Life Flask" "Divine Mana Flask" "Eternal Mana Flask" + SetFontSize 30 + SetBorderColor 255 255 255 + +Hide + SetFontSize 20 + SetBackgroundColor 0 0 0 50 diff --git a/Filtration.Tests/Resources/testscript.txt b/Filtration.Tests/Resources/testscript.txt new file mode 100644 index 0000000..4265427 --- /dev/null +++ b/Filtration.Tests/Resources/testscript.txt @@ -0,0 +1,39 @@ +# Loot Filter Script created by Filtration v0.1 - www.github.com/XVar/filtration +# Begin Script Description +# This is a test script +# +# End Script Description + +# First test condition booyah +Show + ItemLevel = 32 + DropLevel >= 85 + Quality >= 15 + Sockets < 4 + LinkedSockets >= 3 + SetFontSize 12 + +# Section: Gud stuff u shud pick up m9 + +# Second test condition +Show + ItemLevel > 50 + Quality >= 15 + Rarity > Magic + Class "Test Class 1" "Test Class 2" "Test Class 3" "Test Class 4" "Test Class 5" "Test Class 6" "Test Class 7" "Test Class 8" "Test Class 9" "Test Class 10" + BaseType "Test Base Type 1" "Test Base Type 2" + Sockets <= 5 + LinkedSockets = 2 + SetFontSize 10 + +Show + ItemLevel > 50 + Quality >= 15 + Rarity > Magic + Class "Test Class 1" "Test Class 2" "Test Class 3" "Test Class 4" "Test Class 5" "Test Class 6" "Test Class 7" "Test Class 8" "Test Class 9" "Test Class 10" + BaseType "Test Base Type 1" "Test Base Type 2" + Sockets <= 5 + LinkedSockets = 2 + SetFontSize 10 + +Hide \ No newline at end of file diff --git a/Filtration.Tests/Services/TestLootFilterPersistenceService.cs b/Filtration.Tests/Services/TestLootFilterPersistenceService.cs new file mode 100644 index 0000000..f059ddf --- /dev/null +++ b/Filtration.Tests/Services/TestLootFilterPersistenceService.cs @@ -0,0 +1,83 @@ +using Filtration.Models; +using Filtration.Services; +using Filtration.Translators; +using Moq; +using NUnit.Framework; + +namespace Filtration.Tests.Services +{ + [TestFixture] + public class TestLootFilterPersistenceService + { + [Test] + public void LoadLootFilterScript_CallsTranslatorAndFileSystemService() + { + // Arrange + const string TestInputPath = "C:\\Test Path\\Script.Filter"; + const string TestScriptString = "This is a test loot filter script"; + var testLootFilterScript = new LootFilterScript(); + + var mockFileSystemService = new Mock(); + mockFileSystemService.Setup(s => s.ReadFileAsString(TestInputPath)).Returns(TestScriptString).Verifiable(); + + var mockLootFilterScriptTranslator = new Mock(); + mockLootFilterScriptTranslator.Setup(t => t.TranslateStringToLootFilterScript(TestScriptString)).Returns(testLootFilterScript).Verifiable(); + + var service = new LootFilterPersistenceService(mockFileSystemService.Object, mockLootFilterScriptTranslator.Object); + + // Act + var script = service.LoadLootFilterScript(TestInputPath); + + // Assert + mockFileSystemService.Verify(); + mockLootFilterScriptTranslator.Verify(); + Assert.AreEqual(testLootFilterScript, script); + } + + [Test] + public void SaveLootFilterScript_CallsTranslatorAndFileSystemService() + { + // Arrange + var testFilePath = "C:\\Test\\File.txt"; + var testScript = new LootFilterScript {FilePath = testFilePath}; + var testTranslatedScript = "Test translated script"; + + var mockFileSystemService = new Mock(); + mockFileSystemService.Setup(s => s.WriteFileFromString(testFilePath, testTranslatedScript)).Verifiable(); + + var mockLootFilterScriptTranslator = new Mock(); + mockLootFilterScriptTranslator.Setup(t => t.TranslateLootFilterScriptToString(testScript)).Returns(testTranslatedScript).Verifiable(); + + var service = new LootFilterPersistenceService(mockFileSystemService.Object, mockLootFilterScriptTranslator.Object); + + // Act + service.SaveLootFilterScript(testScript); + + // Assert + mockFileSystemService.Verify(); + mockLootFilterScriptTranslator.Verify(); + } + + [Test] + public void DefaultPathOfExileDirectoryExists_CallsFileSystemServiceWithCorrectString() + { + // Arrange + const string TestUserProfilePath = "C:\\Users\\TestUser"; + + + var mockFileSystemService = new Mock(); + mockFileSystemService.Setup(f => f.GetUserProfilePath()).Returns(TestUserProfilePath).Verifiable(); + mockFileSystemService.Setup(f => f.DirectoryExists(TestUserProfilePath + "\\Documents\\My Games\\Path of Exile")).Returns(true).Verifiable(); + + var mockLootFilterScriptTranslator = new Mock(); + + var service = new LootFilterPersistenceService(mockFileSystemService.Object, mockLootFilterScriptTranslator.Object); + + // Act + var result = service.DefaultPathOfExileDirectory(); + + // Assert + mockFileSystemService.Verify(); + } + } +} diff --git a/Filtration.Tests/Services/TestStaticDataService.cs b/Filtration.Tests/Services/TestStaticDataService.cs new file mode 100644 index 0000000..94f9943 --- /dev/null +++ b/Filtration.Tests/Services/TestStaticDataService.cs @@ -0,0 +1,43 @@ +using Filtration.Services; +using Moq; +using NUnit.Framework; + +namespace Filtration.Tests.Services +{ + [TestFixture] + public class TestStaticDataService + { + [Test] + public void Constructor_CallsFileSystemService() + { + // Arrange + + var mockFileSystemService = new Mock(); + mockFileSystemService.Setup(f => f.ReadFileAsString(It.IsAny())).Verifiable(); + + var service = new StaticDataService(mockFileSystemService.Object); + + // Act + + // Assert + mockFileSystemService.Verify(); + } + + [Ignore("Integration Test")] + [Test] + public void Constructor_ReadsFromFileCorrectly() + { + // Arrange + + var fileSystemService = new FileSystemService(); + + var service = new StaticDataService(fileSystemService); + + // Act + + // Assert + + } + + } +} diff --git a/Filtration.Tests/Translators/TestLootFilterBlockTranslator.cs b/Filtration.Tests/Translators/TestLootFilterBlockTranslator.cs new file mode 100644 index 0000000..8d83214 --- /dev/null +++ b/Filtration.Tests/Translators/TestLootFilterBlockTranslator.cs @@ -0,0 +1,1169 @@ +using System; +using System.Linq; +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models; +using Filtration.Models.BlockItemBaseTypes; +using Filtration.Models.BlockItemTypes; +using Filtration.Translators; +using NUnit.Framework; + +namespace Filtration.Tests.Translators +{ + [TestFixture] + public class TestLootFilterBlockTranslator + { + private LootFilterBlockTranslatorTestUtility _testUtility; + + [SetUp] + public void LootFilterBlockTranslatorTestSetUp() + { + _testUtility = new LootFilterBlockTranslatorTestUtility(); + } + + [Test] + public void TranslateStringToLootFilterBlock_NoDescriptionComment_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " ItemLevel >= 55"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is ItemLevelBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(55, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.GreaterThanOrEqual, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_Hide_ReturnsCorrectObject() + { + // Arrange + var inputString = "Hide" + Environment.NewLine; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is ActionBlockItem)); + Assert.AreEqual(BlockAction.Hide, result.Action); + } + + [Test] + public void TranslateStringToLootFilterBlock_DescriptionComment_ReturnsCorrectObject() + { + // Arrange + var inputString = "# This is a test Block" + Environment.NewLine + + "Show" + Environment.NewLine + + " ItemLevel >= 55"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual("This is a test Block", result.Description); + Assert.AreEqual(1, result.BlockItems.Count(b => b is ItemLevelBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(55, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.GreaterThanOrEqual, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_MultiLineDescriptionComment_OnlyAddsLastCommentLineToDescription() + { + // Arrange + var inputString = "#First line" + Environment.NewLine + + "#Second Line" + Environment.NewLine + + "Show" + Environment.NewLine + + " ItemLevel >= 55"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual("Second Line", result.Description); + Assert.AreEqual(1, result.BlockItems.Count(b => b is ItemLevelBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(55, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.GreaterThanOrEqual, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_DropLevel_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " DropLevel = 40"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + + Assert.AreEqual(1, result.BlockItems.Count(b => b is DropLevelBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(40, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.Equal, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_Quality_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " Quality < 18"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + + Assert.AreEqual(1, result.BlockItems.Count(b => b is QualityBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(18, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.LessThan, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_Rarity_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " Rarity > Normal"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + + Assert.AreEqual(1, result.BlockItems.Count(b => b is RarityBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual((int)ItemRarity.Normal, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.GreaterThan, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_Rarity_WorksWithoutPredicateOperator() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " Rarity Normal"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is RarityBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual((int)ItemRarity.Normal, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.Equal, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_Class_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + @" Class ""Test Class 1"" ""TestOneWordClassInQuotes"" TestOneWordClassNotInQuotes ""Test Class 2"""; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is ClassBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.Contains("Test Class 1", blockItem.Items); + Assert.Contains("TestOneWordClassInQuotes", blockItem.Items); + Assert.Contains("TestOneWordClassNotInQuotes", blockItem.Items); + Assert.Contains("Test Class 2", blockItem.Items); + } + + [Test] + public void TranslateStringToLootFilterBlock_BaseType_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + @" BaseType ""Test Base Type 1"" ""TestOneWordBaseTypeInQuotes"" TestOneWordBaseTypeNotInQuotes ""Test BaseType 2"""; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is BaseTypeBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.Contains("Test Base Type 1", blockItem.Items); + Assert.Contains("TestOneWordBaseTypeInQuotes", blockItem.Items); + Assert.Contains("TestOneWordBaseTypeNotInQuotes", blockItem.Items); + Assert.Contains("Test BaseType 2", blockItem.Items); + } + + [Test] + public void TranslateStringToLootFilterBlock_Sockets_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " Sockets > 2"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + + Assert.AreEqual(1, result.BlockItems.Count(b => b is SocketsBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(2, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.GreaterThan, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_LinkedSockets_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " LinkedSockets != 1"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + + Assert.AreEqual(1, result.BlockItems.Count(b => b is LinkedSocketsBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(1, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.NotEqual, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_Width_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " Width != 1"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + + Assert.AreEqual(1, result.BlockItems.Count(b => b is WidthBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(1, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.NotEqual, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_Height_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " Height <= 3"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + + Assert.AreEqual(1, result.BlockItems.Count(b => b is HeightBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(3, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.LessThanOrEqual, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_Height_PredicatesWorkWithoutSpaceBetweenOperatorAndOperand() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " Height <=3"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is HeightBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(3, blockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.LessThanOrEqual, blockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_SocketGroup_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " SocketGroup RRGB"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is SocketGroupBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(1, blockItem.Items.Count); + var firstSocketColorGroup = blockItem.Items.First(); + Assert.AreEqual("RRGB", firstSocketColorGroup); + } + + [Test] + public void TranslateStringToLootFilterBlock_SetTextColor_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " SetTextColor 255 20 100"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is TextColorBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(255, blockItem.Color.R); + Assert.AreEqual(20, blockItem.Color.G); + Assert.AreEqual(100, blockItem.Color.B); + } + + [Test] + public void TranslateStringToLootFilterBlock_SetTextColorWithAlpha_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " SetTextColor 65 0 255 12"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is TextColorBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(12, blockItem.Color.A); + Assert.AreEqual(65, blockItem.Color.R); + Assert.AreEqual(0, blockItem.Color.G); + Assert.AreEqual(255, blockItem.Color.B); + } + + [Test] + public void TranslateStringToLootFilterBlock_SetBackgroundColor_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " SetBackgroundColor 255 20 100"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is BackgroundColorBlockItem)); + var blockItem = + (ColorBlockItem)result.BlockItems.OfType().First(); + Assert.AreEqual(255, blockItem.Color.R); + Assert.AreEqual(20, blockItem.Color.G); + Assert.AreEqual(100, blockItem.Color.B); + } + + [Test] + public void TranslateStringToLootFilterBlock_SetBorderColor_ReturnsCorrectObject() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " SetBorderColor 255 20 100"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is BorderColorBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(255, blockItem.Color.R); + Assert.AreEqual(20, blockItem.Color.G); + Assert.AreEqual(100, blockItem.Color.B); + } + + [Test] + public void TranslateStringToLootFilterBlock_SetFontSize_ReturnsCorrectObject() + { + // Arrange + + var inputString = "Show" + Environment.NewLine + + " SetFontSize 15"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is FontSizeBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(15, blockItem.Value); + } + + [Test] + public void TranslateStringToLootFilterBlock_PlayAlertSoundWithoutVolume_ReturnsCorrectObject() + { + // Arrange + + var inputString = "Show" + Environment.NewLine + + " PlayAlertSound 4"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is SoundBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(4, blockItem.Value); + Assert.AreEqual(79, blockItem.SecondValue); + } + + [Test] + public void TranslateStringToLootFilterBlock_PlayAlertSoundWithVolume_ReturnsCorrectObject() + { + // Arrange + + var inputString = "Show" + Environment.NewLine + + " PlayAlertSound 2 95"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is SoundBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(2, blockItem.Value); + Assert.AreEqual(95, blockItem.SecondValue); + } + + [Test] + public void TranslateStringToLootFilterBlock_SectionComment_ReturnsLootFilterSectionObjectWithCorrectDescription() + { + // Arrange + const string TestInputSectionDescription = "Wonderful items that you definitely won't want to miss!"; + var inputString = "# Section: " + TestInputSectionDescription; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.IsInstanceOf(result); + Assert.AreEqual(TestInputSectionDescription, result.Description); + } + + [Test] + public void TranslateStringToLootFilterBlock_Everything_ReturnsCorrectObject() + { + // Arrange + + var inputString = "#Comment which should be ignored" + Environment.NewLine + + "#Test filter with everything" + Environment.NewLine + + "Show" + Environment.NewLine + + " ItemLevel >= 50" + Environment.NewLine + + " DropLevel < 70" + Environment.NewLine + + " Quality = 15" + Environment.NewLine + + " Rarity <= Unique" + Environment.NewLine + + @" Class ""My Item Class"" AnotherClass ""AndAnotherClass""" + Environment.NewLine + + @" BaseType MyBaseType ""Another BaseType""" + Environment.NewLine + + " JunkLine Let's ignore this one!" + Environment.NewLine + + " #Quality Commented out quality line" + Environment.NewLine + + " Sockets >= 3" + Environment.NewLine + + " LinkedSockets != 2" + Environment.NewLine + + " SocketGroup RGBB RGBWW" + Environment.NewLine + + " SetTextColor 50 100 3 200" + Environment.NewLine + + " SetBackgroundColor 255 100 5" + Environment.NewLine + + " SetBorderColor 0 0 0" + Environment.NewLine + + " SetFontSize 50" + Environment.NewLine + + " PlayAlertSound 3"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual("Test filter with everything", result.Description); + var itemLevelblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(FilterPredicateOperator.GreaterThanOrEqual, itemLevelblockItem.FilterPredicate.PredicateOperator); + Assert.AreEqual(50, itemLevelblockItem.FilterPredicate.PredicateOperand); + + var dropLevelblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(FilterPredicateOperator.LessThan, dropLevelblockItem.FilterPredicate.PredicateOperator); + Assert.AreEqual(70, dropLevelblockItem.FilterPredicate.PredicateOperand); + + var qualityblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(FilterPredicateOperator.Equal, qualityblockItem.FilterPredicate.PredicateOperator); + Assert.AreEqual(15, qualityblockItem.FilterPredicate.PredicateOperand); + + var itemRarityblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(FilterPredicateOperator.LessThanOrEqual, itemRarityblockItem.FilterPredicate.PredicateOperator); + Assert.AreEqual((int)ItemRarity.Unique, itemRarityblockItem.FilterPredicate.PredicateOperand); + + var classblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(3, classblockItem.Items.Count); + Assert.Contains("My Item Class", classblockItem.Items); + Assert.Contains("AnotherClass", classblockItem.Items); + Assert.Contains("AndAnotherClass", classblockItem.Items); + + var baseTypeblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(2, baseTypeblockItem.Items.Count); + Assert.Contains("MyBaseType", baseTypeblockItem.Items); + Assert.Contains("Another BaseType", baseTypeblockItem.Items); + + var socketsblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(FilterPredicateOperator.GreaterThanOrEqual, socketsblockItem.FilterPredicate.PredicateOperator); + Assert.AreEqual(3, socketsblockItem.FilterPredicate.PredicateOperand); + + var linkedSocketsblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(FilterPredicateOperator.NotEqual, linkedSocketsblockItem.FilterPredicate.PredicateOperator); + Assert.AreEqual(2, linkedSocketsblockItem.FilterPredicate.PredicateOperand); + + var socketGroupblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(2, socketGroupblockItem.Items.Count); + var firstSocketGroup = socketGroupblockItem.Items.First(); + Assert.AreEqual("RGBB", firstSocketGroup); + var secondSocketGroup = socketGroupblockItem.Items.Skip(1).First(); + Assert.AreEqual("RGBWW", secondSocketGroup); + + var textColorblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(200, textColorblockItem.Color.A); + Assert.AreEqual(50, textColorblockItem.Color.R); + Assert.AreEqual(100, textColorblockItem.Color.G); + Assert.AreEqual(3, textColorblockItem.Color.B); + + var backgroundColorblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(255, backgroundColorblockItem.Color.R); + Assert.AreEqual(100, backgroundColorblockItem.Color.G); + Assert.AreEqual(5, backgroundColorblockItem.Color.B); + + var borderColorblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(0, borderColorblockItem.Color.R); + Assert.AreEqual(0, borderColorblockItem.Color.G); + Assert.AreEqual(0, borderColorblockItem.Color.B); + + var fontSizeblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(50, fontSizeblockItem.Value); + + var soundblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(3, soundblockItem.Value); + Assert.AreEqual(79, soundblockItem.SecondValue); + } + + [Test] + public void TranslateStringToLootFilterBlock_MultipleValues_ReturnsCorrectObject() + { + // Arrange + + var inputString = "Show" + Environment.NewLine + + " ItemLevel >= 70" + Environment.NewLine + + " ItemLevel <= 80" + Environment.NewLine + + " Quality = 15" + Environment.NewLine + + " Quality < 17"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(2, result.BlockItems.Count(b => b is ItemLevelBlockItem)); + var firstItemLevelblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(70, firstItemLevelblockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.GreaterThanOrEqual, firstItemLevelblockItem.FilterPredicate.PredicateOperator); + var secondItemLevelblockItem = result.BlockItems.OfType().Skip(1).First(); + Assert.AreEqual(80, secondItemLevelblockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.LessThanOrEqual, secondItemLevelblockItem.FilterPredicate.PredicateOperator); + + Assert.AreEqual(2, result.BlockItems.Count(b => b is QualityBlockItem)); + var firstQualityblockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(15, firstQualityblockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.Equal, firstQualityblockItem.FilterPredicate.PredicateOperator); + var secondQualityblockItem = result.BlockItems.OfType().Skip(1).First(); + Assert.AreEqual(17, secondQualityblockItem.FilterPredicate.PredicateOperand); + Assert.AreEqual(FilterPredicateOperator.LessThan, secondQualityblockItem.FilterPredicate.PredicateOperator); + } + + [Test] + public void TranslateStringToLootFilterBlock_MultipleTextColorItems_OnlyLastOneUsed() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " SetTextColor 25 1 50" + Environment.NewLine + + " SetTextColor 255 20 100"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is TextColorBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(255, blockItem.Color.R); + Assert.AreEqual(20, blockItem.Color.G); + Assert.AreEqual(100, blockItem.Color.B); + } + + [Test] + public void TranslateStringToLootFilterBlock_MultipleFontSizeItems_OnlyLastOneUsed() + { + // Arrange + + var inputString = "Show" + Environment.NewLine + + " SetFontSize 12" + Environment.NewLine + + " SetFontSize 38" + Environment.NewLine + + " SetFontSize 27" + Environment.NewLine; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is FontSizeBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(27, blockItem.Value); + } + + [Test] + public void TranslateStringToLootFilterBlock_MultipleSoundItems_OnlyLastOneUsed() + { + // Arrange + + var inputString = "Show" + Environment.NewLine + + " PlayAlertSound 7" + Environment.NewLine + + " PlayAlertSound 5 100 38" + Environment.NewLine + + " PlayAlertSound 2" + Environment.NewLine; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is SoundBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(2, blockItem.Value); + Assert.AreEqual(79, blockItem.SecondValue); + } + + [Test] + public void TranslateStringToLootFilterBlock_MultipleBackgroundColorItems_OnlyLastOneUsed() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " SetBackgroundColor 25 1 50" + Environment.NewLine + + " SetBackgroundColor 255 20 100"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is BackgroundColorBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(255, blockItem.Color.R); + Assert.AreEqual(20, blockItem.Color.G); + Assert.AreEqual(100, blockItem.Color.B); + } + + [Test] + public void TranslateStringToLootFilterBlock_MultipleBorderColorItems_OnlyLastOneUsed() + { + // Arrange + var inputString = "Show" + Environment.NewLine + + " SetBorderColor 25 1 50" + Environment.NewLine + + " SetBorderColor 255 20 100"; + + // Act + var result = _testUtility.Translator.TranslateStringToLootFilterBlock(inputString); + + // Assert + Assert.AreEqual(1, result.BlockItems.Count(b => b is BorderColorBlockItem)); + var blockItem = result.BlockItems.OfType().First(); + Assert.AreEqual(255, blockItem.Color.R); + Assert.AreEqual(20, blockItem.Color.G); + Assert.AreEqual(100, blockItem.Color.B); + } + + [Test] + public void TranslateLootFilterBlockToString_NothingPopulated_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show"; + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_FilterTypeHide_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Hide"; + + _testUtility.TestBlock.Action = BlockAction.Hide; + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_FilterDescription_ReturnsCorrectString() + { + // Arrange + var expectedResult = "# Test Block Description" + Environment.NewLine + + "Show"; + + _testUtility.TestBlock.Description = "Test Block Description"; + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_FilterDescriptionWithApostraphes_DoesNotDuplicateApostraphes() + { + // Arrange + var expectedResult = "# Test Block Descr'iption" + Environment.NewLine + + "Show"; + + _testUtility.TestBlock.Description = "Test Block Descr'iption"; + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_ItemLevel_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " ItemLevel >= 56"; + + _testUtility.TestBlock.BlockItems.Add(new ItemLevelBlockItem(FilterPredicateOperator.GreaterThanOrEqual, 56)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_DropLevel_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " DropLevel = 23"; + + _testUtility.TestBlock.BlockItems.Add(new DropLevelBlockItem(FilterPredicateOperator.Equal, 23)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Quality_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " Quality < 10"; + + _testUtility.TestBlock.BlockItems.Add(new QualityBlockItem(FilterPredicateOperator.LessThan, 10)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Rarity_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " Rarity < Rare"; + + _testUtility.TestBlock.BlockItems.Add(new RarityBlockItem(FilterPredicateOperator.LessThan, + (int) ItemRarity.Rare)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Classes_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " Class \"Test Class\" \"Another Class\" \"Yet Another Class\""; + + var classBlockItem = new ClassBlockItem(); + classBlockItem.Items.Add("Test Class"); + classBlockItem.Items.Add("Another Class"); + classBlockItem.Items.Add("Yet Another Class"); + _testUtility.TestBlock.BlockItems.Add(classBlockItem); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Classes_DoesNotDuplicateApostraphes() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " Class \"Test Cl'ass\""; + + var classBlockItem = new ClassBlockItem(); + classBlockItem.Items.Add("Test Cl'ass"); + _testUtility.TestBlock.BlockItems.Add(classBlockItem); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_BaseTypes_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " BaseType \"Test BaseType\" \"Another BaseType\" \"Yet Another BaseType\""; + + var baseTypeBlockItem = new BaseTypeBlockItem(); + baseTypeBlockItem.Items.Add("Test BaseType"); + baseTypeBlockItem.Items.Add("Another BaseType"); + baseTypeBlockItem.Items.Add("Yet Another BaseType"); + _testUtility.TestBlock.BlockItems.Add(baseTypeBlockItem); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Sockets_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " Sockets >= 5"; + + _testUtility.TestBlock.BlockItems.Add(new SocketsBlockItem(FilterPredicateOperator.GreaterThanOrEqual, 5)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_SocketGroup_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " SocketGroup \"RRG\" \"GBB\""; + + var socketGroupBlockItem = new SocketGroupBlockItem(); + socketGroupBlockItem.Items.Add("RRG"); + socketGroupBlockItem.Items.Add("GBB"); + _testUtility.TestBlock.BlockItems.Add(socketGroupBlockItem); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_LinkedSockets_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " LinkedSockets != 3"; + + _testUtility.TestBlock.BlockItems.Add(new LinkedSocketsBlockItem(FilterPredicateOperator.NotEqual, 3)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Width_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " Width = 4"; + + _testUtility.TestBlock.BlockItems.Add(new WidthBlockItem(FilterPredicateOperator.Equal, 4)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Height_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " Height > 1"; + + _testUtility.TestBlock.BlockItems.Add(new HeightBlockItem(FilterPredicateOperator.GreaterThan, 1)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_TextColorMaxAlpha_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " SetTextColor 54 102 255"; + + _testUtility.TestBlock.BlockItems.Add(new TextColorBlockItem(new Color {A = 255, R = 54, G = 102, B = 255})); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_TextColorNotMaxAlpha_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " SetTextColor 54 102 255 66"; + + _testUtility.TestBlock.BlockItems.Add(new TextColorBlockItem(new Color {A = 66, R = 54, G = 102, B = 255})); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_BackgroundColorNotMaxAlpha_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " SetBackgroundColor 12 0 212 69"; + + _testUtility.TestBlock.BlockItems.Add( + new BackgroundColorBlockItem(new Color {A = 69, R = 12, G = 0, B = 212})); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_BorderColorNotMaxAlpha_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " SetBorderColor 128 32 0 1"; + + _testUtility.TestBlock.BlockItems.Add(new BorderColorBlockItem(new Color {A = 1, R = 128, G = 32, B = 0})); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_FontSize_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " SetFontSize 15"; + + _testUtility.TestBlock.BlockItems.Add(new FontSizeBlockItem(15)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Sound_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " PlayAlertSound 2 50"; + + _testUtility.TestBlock.BlockItems.Add(new SoundBlockItem(2, 50)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_MultipleItemLevel_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " ItemLevel > 56" + Environment.NewLine + + " ItemLevel >= 45" + Environment.NewLine + + " ItemLevel < 100"; + + _testUtility.TestBlock.BlockItems.Add(new ItemLevelBlockItem(FilterPredicateOperator.GreaterThan, 56)); + _testUtility.TestBlock.BlockItems.Add(new ItemLevelBlockItem(FilterPredicateOperator.GreaterThanOrEqual, 45)); + _testUtility.TestBlock.BlockItems.Add(new ItemLevelBlockItem(FilterPredicateOperator.LessThan, 100)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_MultipleFontSize_UsesFirstFontSize() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " SetFontSize 1"; + + _testUtility.TestBlock.BlockItems.Add(new FontSizeBlockItem(1)); + _testUtility.TestBlock.BlockItems.Add(new FontSizeBlockItem(2)); + _testUtility.TestBlock.BlockItems.Add(new FontSizeBlockItem(3)); + _testUtility.TestBlock.BlockItems.Add(new FontSizeBlockItem(4)); + _testUtility.TestBlock.BlockItems.Add(new FontSizeBlockItem(15)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Section_ReturnsCorrectString() + { + // Arrange + var TestInputSectionText = "Ermagerd it's a section!"; + var expectedResult = "# Section: " + TestInputSectionText; + + _testUtility.TestBlock = new LootFilterSection { Description = TestInputSectionText }; + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + [Test] + public void TranslateLootFilterBlockToString_Everything_ReturnsCorrectString() + { + // Arrange + var expectedResult = "Show" + Environment.NewLine + + " ItemLevel > 70" + Environment.NewLine + + " ItemLevel <= 85" + Environment.NewLine + + " DropLevel != 56" + Environment.NewLine + + " Quality > 2" + Environment.NewLine + + " Rarity = Unique" + Environment.NewLine + + " Class \"Body Armour\" \"Gloves\" \"Belt\" \"Two Hand Axes\"" + Environment.NewLine + + " BaseType \"Greater Life Flask\" \"Simple Robe\" \"Full Wyrmscale\"" + + Environment.NewLine + + " Sockets <= 6" + Environment.NewLine + + " LinkedSockets >= 4" + Environment.NewLine + + " Width = 3" + Environment.NewLine + + " Height <= 6" + Environment.NewLine + + " Height >= 2" + Environment.NewLine + + " SetTextColor 255 89 0 56" + Environment.NewLine + + " SetBackgroundColor 0 0 0" + Environment.NewLine + + " SetBorderColor 255 1 254" + Environment.NewLine + + " SetFontSize 50" + Environment.NewLine + + " PlayAlertSound 6 90"; + + _testUtility.TestBlock.BlockItems.Add(new ActionBlockItem(BlockAction.Show)); + _testUtility.TestBlock.BlockItems.Add(new ItemLevelBlockItem(FilterPredicateOperator.GreaterThan, 70)); + _testUtility.TestBlock.BlockItems.Add(new ItemLevelBlockItem(FilterPredicateOperator.LessThanOrEqual, 85)); + _testUtility.TestBlock.BlockItems.Add(new DropLevelBlockItem(FilterPredicateOperator.NotEqual, 56)); + _testUtility.TestBlock.BlockItems.Add(new QualityBlockItem(FilterPredicateOperator.GreaterThan, 2)); + _testUtility.TestBlock.BlockItems.Add(new RarityBlockItem(FilterPredicateOperator.Equal, (int)ItemRarity.Unique)); + var classItemblockItem = new ClassBlockItem(); + classItemblockItem.Items.Add("Body Armour"); + classItemblockItem.Items.Add("Gloves"); + classItemblockItem.Items.Add("Belt"); + classItemblockItem.Items.Add("Two Hand Axes"); + _testUtility.TestBlock.BlockItems.Add(classItemblockItem); + var baseTypeItemblockItem = new BaseTypeBlockItem(); + baseTypeItemblockItem.Items.Add("Greater Life Flask"); + baseTypeItemblockItem.Items.Add("Simple Robe"); + baseTypeItemblockItem.Items.Add("Full Wyrmscale"); + _testUtility.TestBlock.BlockItems.Add(baseTypeItemblockItem); + _testUtility.TestBlock.BlockItems.Add(new SocketsBlockItem(FilterPredicateOperator.LessThanOrEqual, 6)); + _testUtility.TestBlock.BlockItems.Add(new LinkedSocketsBlockItem(FilterPredicateOperator.GreaterThanOrEqual, 4)); + _testUtility.TestBlock.BlockItems.Add(new WidthBlockItem(FilterPredicateOperator.Equal, 3)); + _testUtility.TestBlock.BlockItems.Add(new HeightBlockItem(FilterPredicateOperator.LessThanOrEqual, 6)); + _testUtility.TestBlock.BlockItems.Add(new HeightBlockItem(FilterPredicateOperator.GreaterThanOrEqual, 2)); + _testUtility.TestBlock.BlockItems.Add(new TextColorBlockItem(new Color {A = 56, R = 255, G = 89, B = 0})); + _testUtility.TestBlock.BlockItems.Add(new BackgroundColorBlockItem(new Color { A = 255, R = 0, G = 0, B = 0 })); + _testUtility.TestBlock.BlockItems.Add(new BorderColorBlockItem(new Color { A = 255, R = 255, G = 1, B = 254 })); + _testUtility.TestBlock.BlockItems.Add(new FontSizeBlockItem(50)); + _testUtility.TestBlock.BlockItems.Add(new SoundBlockItem(6, 90)); + + // Act + var result = _testUtility.Translator.TranslateLootFilterBlockToString(_testUtility.TestBlock); + + // Assert + Assert.AreEqual(expectedResult, result); + } + + private class LootFilterBlockTranslatorTestUtility + { + public LootFilterBlockTranslatorTestUtility() + { + // Test Data + TestBlock = new LootFilterBlock(); + + // Class under test instantiation + Translator = new LootFilterBlockTranslator(); + } + + public LootFilterBlock TestBlock { get; set; } + public LootFilterBlockTranslator Translator { get; private set; } + } + } +} diff --git a/Filtration.Tests/Translators/TestLootFilterScriptTranslator.cs b/Filtration.Tests/Translators/TestLootFilterScriptTranslator.cs new file mode 100644 index 0000000..d8bf7dd --- /dev/null +++ b/Filtration.Tests/Translators/TestLootFilterScriptTranslator.cs @@ -0,0 +1,173 @@ +using System; +using System.IO; +using System.Linq; +using Filtration.Enums; +using Filtration.Models; +using Filtration.Models.BlockItemBaseTypes; +using Filtration.Models.BlockItemTypes; +using Filtration.Translators; +using Moq; +using NUnit.Framework; + +namespace Filtration.Tests.Translators +{ + [TestFixture] + public class TestLootFilterScriptTranslator + { + [Test] + public void TranslateStringToLootFilterScript_ReturnsScriptWithCorrectNumberOfBlocks() + { + // Arrange + var testInput = File.ReadAllText(@"Resources/testscript.txt"); + + var mockLootFilterBlockTranslator = new Mock(); + mockLootFilterBlockTranslator.Setup(t => t.TranslateStringToLootFilterBlock(It.IsAny())).Verifiable(); + + var translator = new LootFilterScriptTranslator(mockLootFilterBlockTranslator.Object); + + // Act + var script = translator.TranslateStringToLootFilterScript(testInput); + + // Assert + Assert.AreEqual(5, script.LootFilterBlocks.Count); + mockLootFilterBlockTranslator.Verify(); + } + + [Test] + public void TranslateStringToLootFilterScript_ReturnsScriptWithDescriptionCorrectlySet() + { + // Arrange + var testInput = File.ReadAllText(@"Resources/testscript.txt"); + var expectedDescription = "Loot Filter Script created by Filtration v0.1 - www.github.com/XVar/filtration" + Environment.NewLine + + "Begin Script Description" + Environment.NewLine + + "This is a test script" + Environment.NewLine + + Environment.NewLine + + "End Script Description"; + + var mockLootFilterBlockTranslator = new Mock(); + mockLootFilterBlockTranslator.Setup(t => t.TranslateStringToLootFilterBlock(It.IsAny())).Verifiable(); + + var translator = new LootFilterScriptTranslator(mockLootFilterBlockTranslator.Object); + + // Act + var script = translator.TranslateStringToLootFilterScript(testInput); + + // Assert + Assert.AreEqual(expectedDescription, script.Description); + } + + [Ignore("Integration Test")] + [Test] + public void TranslateStringToLootFilterScript_ThioleLootFilterTest() + { + // Arrange + var testInput = File.ReadAllText(@"Resources/ThioleLootFilter.txt"); + + + var BlockTranslator = new LootFilterBlockTranslator(); + var translator = new LootFilterScriptTranslator(BlockTranslator); + + // Act + var script = translator.TranslateStringToLootFilterScript(testInput); + // Assert + // Not crashing out when loading a huge script means this integration test has passed! + } + + [Test] + public void TranslateLootFilterScriptToString_OneBlock_CallsTranslator() + { + // Arrange + var testScript = new LootFilterScript(); + + var testBlock = new LootFilterBlock(); + testBlock.BlockItems.Add(new ItemLevelBlockItem(FilterPredicateOperator.Equal, 5)); + + var BlockOutput = "Test Script Output"; + var expectedOutput = "Test Script Output" + Environment.NewLine + Environment.NewLine; + + testScript.LootFilterBlocks.Add(testBlock); + + var mockLootFilterBlockTranslator = new Mock(); + mockLootFilterBlockTranslator.Setup(t => t.TranslateLootFilterBlockToString(testBlock)).Returns(BlockOutput).Verifiable(); + + var translator = new LootFilterScriptTranslator(mockLootFilterBlockTranslator.Object); + + // Act + var result = translator.TranslateLootFilterScriptToString(testScript); + + // Assert + Assert.AreEqual(expectedOutput, result); + mockLootFilterBlockTranslator.Verify(); + } + + [Test] + public void TranslateLootFilterScriptToString_FullScript_ReturnsCorrectOutput() + { + var script = new LootFilterScript + { + Description = "Test script description" + Environment.NewLine + + "This is a really great script!" + Environment.NewLine + + "Multiple line script descriptions are fun!" + }; + var block1 = new LootFilterBlock {Description = "Test Filter 1"}; + block1.BlockItems.Add(new ItemLevelBlockItem(FilterPredicateOperator.GreaterThan, 5)); + + var block2 = new LootFilterBlock(); + block2.BlockItems.Add(new QualityBlockItem(FilterPredicateOperator.LessThan, 15)); + block2.BlockItems.Add(new FontSizeBlockItem(7)); + block2.BlockItems.Add(new WidthBlockItem(FilterPredicateOperator.Equal, 3)); + + script.LootFilterBlocks.Add(block1); + script.LootFilterBlocks.Add(block2); + + var expectedOutput = "# Test script description" + Environment.NewLine + + "# This is a really great script!" + Environment.NewLine + + "# Multiple line script descriptions are fun!" + Environment.NewLine + + Environment.NewLine + + "# Test Filter 1" + Environment.NewLine + + "Show" + Environment.NewLine + + " ItemLevel > 5" + Environment.NewLine + + Environment.NewLine + + "Show" + Environment.NewLine + + " Quality < 15" + Environment.NewLine + + " Width = 3" + Environment.NewLine + + " SetFontSize 7" + Environment.NewLine + Environment.NewLine; + + var blockTranslator = new LootFilterBlockTranslator(); + var translator = new LootFilterScriptTranslator(blockTranslator); + + // Act + var result = translator.TranslateLootFilterScriptToString(script); + + // Assert + Assert.AreEqual(expectedOutput, result); + } + + [Test] + public void TranslateStringToLootFilterScript_SectionDirectlyBeforeBlockWithoutDescription_ReturnsCorrectObject() + { + // Arrange + var testInputScript = "# My Script" + Environment.NewLine + + Environment.NewLine + + "# Section: Chance Bases" + Environment.NewLine + + "Show" + Environment.NewLine + + " BaseType \"Lapis Amulet\" \"Amber Amulet\"" + Environment.NewLine + + " SetBorderColor 255 0 255" + Environment.NewLine + + " SetFontSize 25"; + + var blockTranslator = new LootFilterBlockTranslator(); + var translator = new LootFilterScriptTranslator(blockTranslator); + + // Act + var result = translator.TranslateStringToLootFilterScript(testInputScript); + + // Assert + Assert.AreEqual(2, result.LootFilterBlocks.Count); + var block = result.LootFilterBlocks.First(l => l.GetType() != typeof(LootFilterSection)); + Assert.AreEqual(4, block.BlockItems.Count); + var baseTypeItem = block.BlockItems.OfType().First(); + Assert.AreEqual(2, baseTypeItem.Items.Count); + + } + } +} diff --git a/Filtration.Tests/packages.config b/Filtration.Tests/packages.config new file mode 100644 index 0000000..064b0de --- /dev/null +++ b/Filtration.Tests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/Filtration.sln b/Filtration.sln new file mode 100644 index 0000000..cf51ce8 --- /dev/null +++ b/Filtration.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Filtration", "Filtration\Filtration.csproj", "{55E0A34C-E039-43D7-A024-A4045401CDDA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Filtration.Tests", "Filtration.Tests\Filtration.Tests.csproj", "{E0693972-72C5-4E05-A9C5-A5943E4015C6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {55E0A34C-E039-43D7-A024-A4045401CDDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {55E0A34C-E039-43D7-A024-A4045401CDDA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {55E0A34C-E039-43D7-A024-A4045401CDDA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {55E0A34C-E039-43D7-A024-A4045401CDDA}.Release|Any CPU.Build.0 = Release|Any CPU + {E0693972-72C5-4E05-A9C5-A5943E4015C6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0693972-72C5-4E05-A9C5-A5943E4015C6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0693972-72C5-4E05-A9C5-A5943E4015C6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0693972-72C5-4E05-A9C5-A5943E4015C6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection +EndGlobal diff --git a/Filtration.sln.DotSettings b/Filtration.sln.DotSettings new file mode 100644 index 0000000..2d2bbe3 --- /dev/null +++ b/Filtration.sln.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/Filtration/App.config b/Filtration/App.config new file mode 100644 index 0000000..9c05822 --- /dev/null +++ b/Filtration/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Filtration/App.xaml b/Filtration/App.xaml new file mode 100644 index 0000000..606edc7 --- /dev/null +++ b/Filtration/App.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Filtration/App.xaml.cs b/Filtration/App.xaml.cs new file mode 100644 index 0000000..fcc31ba --- /dev/null +++ b/Filtration/App.xaml.cs @@ -0,0 +1,37 @@ +using System.Linq; +using System.Windows; +using Castle.MicroKernel.ModelBuilder.Inspectors; +using Castle.Windsor; +using Castle.Windsor.Installer; +using Filtration.Views; + +namespace Filtration +{ + public partial class App + { + private IWindsorContainer _container; + + private void Application_Startup(object sender, StartupEventArgs e) + { + _container = new WindsorContainer(); + + var propInjector = _container.Kernel.ComponentModelBuilder + .Contributors + .OfType() + .Single(); + + _container.Kernel.ComponentModelBuilder.RemoveContributor(propInjector); + + _container.Install(FromAssembly.This()); + + var mainWindow = _container.Resolve(); + mainWindow.Show(); + } + + protected override void OnExit(ExitEventArgs e) + { + _container.Dispose(); + base.OnExit(e); + } + } +} diff --git a/Filtration/Converters/BlockItemTypeToStringConverter.cs b/Filtration/Converters/BlockItemTypeToStringConverter.cs new file mode 100644 index 0000000..55d37d6 --- /dev/null +++ b/Filtration/Converters/BlockItemTypeToStringConverter.cs @@ -0,0 +1,21 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Filtration.Models; + +namespace Filtration.Converters +{ + internal class BlockItemTypeToStringConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var test = (ILootFilterBlockItem)Activator.CreateInstance((Type) value); + return test.DisplayHeading; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Filtration/Converters/BooleanInverterConverter.cs b/Filtration/Converters/BooleanInverterConverter.cs new file mode 100644 index 0000000..2794068 --- /dev/null +++ b/Filtration/Converters/BooleanInverterConverter.cs @@ -0,0 +1,29 @@ +using System; +using System.Globalization; +using System.Windows.Data; + +namespace Filtration.Converters +{ + internal class BoolInverterConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, + CultureInfo culture) + { + if (value is bool) + { + return !(bool)value; + } + return value; + } + + public object ConvertBack(object value, Type targetType, object parameter, + CultureInfo culture) + { + if (value is bool) + { + return !(bool)value; + } + return value; + } + } +} diff --git a/Filtration/Converters/BooleanToBlockActionConverter.cs b/Filtration/Converters/BooleanToBlockActionConverter.cs new file mode 100644 index 0000000..c51456b --- /dev/null +++ b/Filtration/Converters/BooleanToBlockActionConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Filtration.Enums; + +namespace Filtration.Converters +{ + internal class BooleanToBlockActionConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return (BlockAction)value == BlockAction.Show; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return (bool)value ? BlockAction.Show : BlockAction.Hide; + } + } +} diff --git a/Filtration/Converters/BooleanToBlockActionInverseConverter.cs b/Filtration/Converters/BooleanToBlockActionInverseConverter.cs new file mode 100644 index 0000000..8689892 --- /dev/null +++ b/Filtration/Converters/BooleanToBlockActionInverseConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Filtration.Enums; + +namespace Filtration.Converters +{ + internal class BooleanToBlockActionInverseConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return (BlockAction)value == BlockAction.Hide; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return (bool)value ? BlockAction.Hide : BlockAction.Show; + } + } +} diff --git a/Filtration/Converters/BooleanVisibilityConverter.cs b/Filtration/Converters/BooleanVisibilityConverter.cs new file mode 100644 index 0000000..ba451d8 --- /dev/null +++ b/Filtration/Converters/BooleanVisibilityConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Filtration.Converters +{ + internal class BooleanVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return (bool)value ? Visibility.Visible : Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Filtration/Converters/ColorToSolidColorBrushConverter.cs b/Filtration/Converters/ColorToSolidColorBrushConverter.cs new file mode 100644 index 0000000..2a1a324 --- /dev/null +++ b/Filtration/Converters/ColorToSolidColorBrushConverter.cs @@ -0,0 +1,22 @@ +using System; +using System.Windows.Data; +using System.Windows.Media; + +namespace Filtration.Converters +{ + public class ColorToSolidColorBrushConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return value != null ? new SolidColorBrush((Color)value) : null; + } + + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + if (value != null) + return ((SolidColorBrush)value).Color; + + return null; + } + } +} diff --git a/Filtration/Converters/ItemRarityConverter.cs b/Filtration/Converters/ItemRarityConverter.cs new file mode 100644 index 0000000..51bbab6 --- /dev/null +++ b/Filtration/Converters/ItemRarityConverter.cs @@ -0,0 +1,20 @@ +using System; +using System.Globalization; +using System.Windows.Data; +using Filtration.Enums; + +namespace Filtration.Converters +{ + public class IntToItemRarityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return (ItemRarity) ((int) value); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return (int) ((ItemRarity) value); + } + } +} diff --git a/Filtration/Converters/StringToVisibilityConverter.cs b/Filtration/Converters/StringToVisibilityConverter.cs new file mode 100644 index 0000000..48683fb --- /dev/null +++ b/Filtration/Converters/StringToVisibilityConverter.cs @@ -0,0 +1,27 @@ +using System; +using System.Globalization; +using System.Windows; +using System.Windows.Data; + +namespace Filtration.Converters +{ + internal class StringToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (String.IsNullOrEmpty((string)value)) + { + return Visibility.Collapsed; + } + else + { + return Visibility.Visible; + } + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/Filtration/Enums/BlockAction.cs b/Filtration/Enums/BlockAction.cs new file mode 100644 index 0000000..e915e01 --- /dev/null +++ b/Filtration/Enums/BlockAction.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace Filtration.Enums +{ + internal enum BlockAction + { + [Description("Show")] + Show, + [Description("Hide")] + Hide + } +} diff --git a/Filtration/Enums/BlockItemType.cs b/Filtration/Enums/BlockItemType.cs new file mode 100644 index 0000000..cc09263 --- /dev/null +++ b/Filtration/Enums/BlockItemType.cs @@ -0,0 +1,22 @@ +namespace Filtration.Enums +{ + internal enum BlockItemType + { + ItemLevel, + DropLevel, + Quality, + Rarity, + Class, + BaseType, + Sockets, + LinkedSockets, + SocketGroup, + Width, + Height, + TextColor, + BackgroundColor, + BorderColor, + Sound, + FontSize + } +} diff --git a/Filtration/Enums/FilterPredicateOperator.cs b/Filtration/Enums/FilterPredicateOperator.cs new file mode 100644 index 0000000..f13baa9 --- /dev/null +++ b/Filtration/Enums/FilterPredicateOperator.cs @@ -0,0 +1,20 @@ +using System.ComponentModel; + +namespace Filtration.Enums +{ + internal enum FilterPredicateOperator + { + [Description("=")] + Equal, + [Description("!=")] + NotEqual, + [Description("<")] + LessThan, + [Description("<=")] + LessThanOrEqual, + [Description(">")] + GreaterThan, + [Description(">=")] + GreaterThanOrEqual + } +} diff --git a/Filtration/Enums/FilterType.cs b/Filtration/Enums/FilterType.cs new file mode 100644 index 0000000..4d30d2d --- /dev/null +++ b/Filtration/Enums/FilterType.cs @@ -0,0 +1,12 @@ +using System.ComponentModel; + +namespace Filtration.Enums +{ + internal enum FilterType + { + [Description("Show")] + Show, + [Description("Hide")] + Hide + } +} diff --git a/Filtration/Enums/ItemRarity.cs b/Filtration/Enums/ItemRarity.cs new file mode 100644 index 0000000..04b0daf --- /dev/null +++ b/Filtration/Enums/ItemRarity.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; + +namespace Filtration.Enums +{ + internal enum ItemRarity + { + [Description("Normal")] + Normal, + [Description("Magic")] + Magic, + [Description("Rare")] + Rare, + [Description("Unique")] + Unique + } +} diff --git a/Filtration/Enums/SocketColor.cs b/Filtration/Enums/SocketColor.cs new file mode 100644 index 0000000..d495103 --- /dev/null +++ b/Filtration/Enums/SocketColor.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; + +namespace Filtration.Enums +{ + internal enum SocketColor + { + [Description("R")] + Red, + [Description("G")] + Green, + [Description("B")] + Blue, + [Description("W")] + White + } +} diff --git a/Filtration/Extensions/EnumHelper.cs b/Filtration/Extensions/EnumHelper.cs new file mode 100644 index 0000000..12b8f96 --- /dev/null +++ b/Filtration/Extensions/EnumHelper.cs @@ -0,0 +1,38 @@ +using System; +using System.ComponentModel; + +namespace Filtration.Extensions +{ + internal static class EnumHelper + { + public static T GetAttributeOfType(this Enum enumVal) where T : Attribute + { + var type = enumVal.GetType(); + var memInfo = type.GetMember(enumVal.ToString()); + var attributes = memInfo[0].GetCustomAttributes(typeof(T), false); + return (attributes.Length > 0) ? (T)attributes[0] : null; + } + + public static string GetAttributeDescription(this Enum enumValue) + { + var attribute = enumValue.GetAttributeOfType(); + + return attribute == null ? String.Empty : attribute.Description; + } + + public static T GetEnumValueFromDescription(string description) + { + var fis = typeof(T).GetFields(); + + foreach (var fi in fis) + { + DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); + + if (attributes.Length > 0 && attributes[0].Description == description) + return (T)Enum.Parse(typeof(T), fi.Name); + } + + throw new Exception("Not found"); + } + } +} diff --git a/Filtration/Extensions/EnumerationExtension.cs b/Filtration/Extensions/EnumerationExtension.cs new file mode 100644 index 0000000..98d2c57 --- /dev/null +++ b/Filtration/Extensions/EnumerationExtension.cs @@ -0,0 +1,65 @@ +using System; +using System.ComponentModel; +using System.Linq; +using System.Windows.Markup; + +namespace Filtration.Extensions +{ + internal class EnumerationExtension : MarkupExtension + { + private Type _enumType; + + + public EnumerationExtension(Type enumType) + { + if (enumType == null) throw new ArgumentNullException("enumType"); + + EnumType = enumType; + } + + public Type EnumType + { + get + { + return _enumType; + } + private set + { + if (_enumType == value) return; + + var enumType = Nullable.GetUnderlyingType(value) ?? value; + + if (enumType.IsEnum == false) throw new ArgumentException("Type must be an Enum."); + + _enumType = value; + } + } + + public override object ProvideValue(IServiceProvider serviceProvider) + { + var enumValues = Enum.GetValues(EnumType); + + return (from object enumValue in enumValues + select new EnumerationMember { Value = enumValue, Description = GetDescription(enumValue) }).ToArray + (); + } + + private string GetDescription(object enumValue) + { + var descriptionAttribute = + EnumType.GetField(enumValue.ToString()) + .GetCustomAttributes(typeof(DescriptionAttribute), false) + .FirstOrDefault() as DescriptionAttribute; + + + return descriptionAttribute != null ? descriptionAttribute.Description : enumValue.ToString(); + } + + public class EnumerationMember + { + public string Description { get; set; } + + public object Value { get; set; } + } + } +} \ No newline at end of file diff --git a/Filtration/Filtration.csproj b/Filtration/Filtration.csproj new file mode 100644 index 0000000..f204f13 --- /dev/null +++ b/Filtration/Filtration.csproj @@ -0,0 +1,321 @@ + + + + + Debug + AnyCPU + {55E0A34C-E039-43D7-A024-A4045401CDDA} + WinExe + Properties + Filtration + Filtration + v4.5.1 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Castle.Core.3.3.0\lib\net45\Castle.Core.dll + + + ..\packages\Castle.Windsor.3.3.0\lib\net45\Castle.Windsor.dll + + + ..\..\DragAndDrop(Complete)\DragAndDropLib\bin\Debug\DragAndDropLib.dll + + + False + ..\packages\FontAwesome.WPF.4.3.0.2\lib\FontAwesome.WPF.dll + + + False + ..\packages\MvvmLightLibs.5.1.1.0\lib\net45\GalaSoft.MvvmLight.dll + + + False + ..\packages\MvvmLightLibs.5.1.1.0\lib\net45\GalaSoft.MvvmLight.Platform.dll + + + ..\packages\MahApps.Metro.1.1.2.0\lib\net45\MahApps.Metro.dll + + + + + ..\packages\WPFToolkit.3.5.50211.1\lib\System.Windows.Controls.Input.Toolkit.dll + + + ..\packages\WPFToolkit.3.5.50211.1\lib\System.Windows.Controls.Layout.Toolkit.dll + + + + + + 4.0 + + + + + + ..\packages\WPFToolkit.3.5.50211.1\lib\WPFToolkit.dll + + + False + ..\packages\Extended.Wpf.Toolkit.2.4\lib\net40\Xceed.Wpf.Toolkit.dll + + + + + MSBuild:Compile + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + EditableListBoxControl.xaml + + + + + + + + + + + + LootFilterBlockDisplaySettingsView.xaml + + + LootFilterScriptView.xaml + + + LootFilterBlockView.xaml + + + NumericFilterPredicateControl.xaml + + + LootFilterSectionView.xaml + + + + + + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + Designer + MSBuild:Compile + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + + + MainWindow.xaml + Code + + + Designer + MSBuild:Compile + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + + + + + + PreserveNewest + + + Always + + + Always + + + + + PreserveNewest + + + + + PreserveNewest + + + + + PreserveNewest + + + + + PreserveNewest + + + + + PreserveNewest + + + + + PreserveNewest + + + + + PreserveNewest + + + + + PreserveNewest + + + + + + \ No newline at end of file diff --git a/Filtration/Models/BlockItemBaseTypes/ActionBlockItem.cs b/Filtration/Models/BlockItemBaseTypes/ActionBlockItem.cs new file mode 100644 index 0000000..c033520 --- /dev/null +++ b/Filtration/Models/BlockItemBaseTypes/ActionBlockItem.cs @@ -0,0 +1,84 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows.Media; +using Filtration.Annotations; +using Filtration.Enums; + +namespace Filtration.Models.BlockItemBaseTypes +{ + internal class ActionBlockItem : ILootFilterBlockItem + { + private BlockAction _action; + + public ActionBlockItem(BlockAction action) + { + Action = action; + } + + public BlockAction Action + { + get { return _action; } + set + { + _action = value; + OnPropertyChanged(); + OnPropertyChanged("SummaryText"); + OnPropertyChanged("SummaryBackgroundColor"); + OnPropertyChanged("SummaryTextColor"); + } + } + + public string PrefixText + { + get { return string.Empty; } + } + + public int MaximumAllowed + { + get { return 1; } + } + + public string DisplayHeading + { + get + { + return "Action"; + } + } + + public string SummaryText + { + get + { + return Action == BlockAction.Show ? "Show" : "Hide"; + } + } + + public Color SummaryBackgroundColor + { + get + { + return Action == BlockAction.Show ? Colors.LimeGreen : Colors.OrangeRed; + } + } + + public Color SummaryTextColor + { + get + { + return Action == BlockAction.Show ? Colors.Black : Colors.White; + } + } + + public int SortOrder { get { return 0; } } + + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/Models/BlockItemBaseTypes/ColorBlockItem.cs b/Filtration/Models/BlockItemBaseTypes/ColorBlockItem.cs new file mode 100644 index 0000000..e99a20c --- /dev/null +++ b/Filtration/Models/BlockItemBaseTypes/ColorBlockItem.cs @@ -0,0 +1,53 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows.Media; +using Filtration.Annotations; + +namespace Filtration.Models.BlockItemBaseTypes +{ + internal abstract class ColorBlockItem : ILootFilterBlockItem, IAudioVisualBlockItem + { + private Color _color; + + protected ColorBlockItem() + { + } + + protected ColorBlockItem(Color color) + { + Color = color; + } + + public abstract string PrefixText { get; } + public abstract int MaximumAllowed { get; } + + public abstract string DisplayHeading { get; } + + public string SummaryText + { + get { return string.Empty; } + } + + public Color SummaryBackgroundColor { get { return Colors.Transparent; } } + public Color SummaryTextColor { get { return Colors.Transparent; } } + public abstract int SortOrder { get; } + + public Color Color + { + get { return _color; } + set + { + _color = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/Models/BlockItemBaseTypes/DualIntegerBlockItem.cs b/Filtration/Models/BlockItemBaseTypes/DualIntegerBlockItem.cs new file mode 100644 index 0000000..1776a00 --- /dev/null +++ b/Filtration/Models/BlockItemBaseTypes/DualIntegerBlockItem.cs @@ -0,0 +1,60 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows.Media; +using Filtration.Annotations; + +namespace Filtration.Models.BlockItemBaseTypes +{ + internal abstract class DualIntegerBlockItem : ILootFilterBlockItem, IAudioVisualBlockItem + { + private int _value; + private int _secondValue; + + protected DualIntegerBlockItem() + { + } + + protected DualIntegerBlockItem(int value, int secondValue) + { + Value = value; + SecondValue = secondValue; + } + + public abstract string PrefixText { get; } + public abstract int MaximumAllowed { get; } + public abstract string DisplayHeading { get; } + + public string SummaryText { get { return string.Empty; } } + public Color SummaryBackgroundColor { get { return Colors.Transparent; } } + public Color SummaryTextColor { get { return Colors.Transparent; } } + public abstract int SortOrder { get; } + + public int Value + { + get { return _value; } + set + { + _value = value; + OnPropertyChanged(); + } + } + + public int SecondValue + { + get { return _secondValue; } + set + { + _secondValue = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/Models/BlockItemBaseTypes/IntegerBlockItem.cs b/Filtration/Models/BlockItemBaseTypes/IntegerBlockItem.cs new file mode 100644 index 0000000..6ee87de --- /dev/null +++ b/Filtration/Models/BlockItemBaseTypes/IntegerBlockItem.cs @@ -0,0 +1,52 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows.Media; +using Filtration.Annotations; + +namespace Filtration.Models.BlockItemBaseTypes +{ + internal abstract class IntegerBlockItem : ILootFilterBlockItem, IAudioVisualBlockItem + { + private int _value; + + protected IntegerBlockItem() + { + } + + protected IntegerBlockItem(int value) + { + Value = value; + } + + public abstract string PrefixText { get; } + public abstract int MaximumAllowed { get; } + + public abstract string DisplayHeading { get; } + + public string SummaryText { get { return string.Empty; } } + public Color SummaryBackgroundColor { get { return Colors.Transparent; } } + public Color SummaryTextColor { get { return Colors.Transparent; } } + public abstract int SortOrder { get; } + + public abstract int Minimum { get; } + public abstract int Maximum { get; } + + public int Value + { + get { return _value; } + set + { + _value = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/Models/BlockItemBaseTypes/NumericFilterPredicateBlockItem.cs b/Filtration/Models/BlockItemBaseTypes/NumericFilterPredicateBlockItem.cs new file mode 100644 index 0000000..c0b6919 --- /dev/null +++ b/Filtration/Models/BlockItemBaseTypes/NumericFilterPredicateBlockItem.cs @@ -0,0 +1,61 @@ +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows.Media; +using Filtration.Annotations; +using Filtration.Enums; + +namespace Filtration.Models.BlockItemBaseTypes +{ + internal abstract class NumericFilterPredicateBlockItem : ILootFilterBlockItem + { + private NumericFilterPredicate _filterPredicate; + + protected NumericFilterPredicateBlockItem() + { + FilterPredicate = new NumericFilterPredicate(); + FilterPredicate.PropertyChanged += OnFilterPredicateChanged; + } + + protected NumericFilterPredicateBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) + { + FilterPredicate = new NumericFilterPredicate(predicateOperator, predicateOperand); + FilterPredicate.PropertyChanged += OnFilterPredicateChanged; + } + + public abstract string PrefixText { get; } + public abstract int MaximumAllowed { get; } + public abstract string DisplayHeading { get; } + public abstract string SummaryText { get; } + public abstract Color SummaryBackgroundColor { get; } + public abstract Color SummaryTextColor { get; } + public abstract int SortOrder { get; } + + public abstract int Minimum { get; } + public abstract int Maximum { get; } + + public NumericFilterPredicate FilterPredicate + { + get { return _filterPredicate; } + protected set + { + _filterPredicate = value; + OnPropertyChanged(); + } + } + + private void OnFilterPredicateChanged(object sender, EventArgs e) + { + OnPropertyChanged("FilterPredicate"); + OnPropertyChanged("SummaryText"); + } + + public event PropertyChangedEventHandler PropertyChanged; + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/Models/BlockItemBaseTypes/SocketGroupBlockItemBase.cs b/Filtration/Models/BlockItemBaseTypes/SocketGroupBlockItemBase.cs new file mode 100644 index 0000000..eb43d1c --- /dev/null +++ b/Filtration/Models/BlockItemBaseTypes/SocketGroupBlockItemBase.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows.Media; +using Filtration.Annotations; +using Filtration.Enums; + +namespace Filtration.Models.BlockItemBaseTypes +{ + internal abstract class SocketGroupBlockItemBase : ILootFilterBlockItem + { + protected SocketGroupBlockItemBase() + { + SocketColorGroups = new ObservableCollection>(); + SocketColorGroups.CollectionChanged += OnSocketColorGroupsCollectionChanged; + } + + public abstract string PrefixText { get; } + public abstract int MaximumAllowed { get; } + public abstract string DisplayHeading { get; } + + public abstract string SummaryText { get; } + public abstract Color SummaryBackgroundColor { get; } + public abstract Color SummaryTextColor { get; } + public abstract int SortOrder { get; } + public ObservableCollection> SocketColorGroups { get; private set; } + + private void OnSocketColorGroupsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + OnPropertyChanged("SocketColorGroups"); + } + + public event PropertyChangedEventHandler PropertyChanged; + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + + } +} diff --git a/Filtration/Models/BlockItemBaseTypes/StringListBlockItem.cs b/Filtration/Models/BlockItemBaseTypes/StringListBlockItem.cs new file mode 100644 index 0000000..08681a9 --- /dev/null +++ b/Filtration/Models/BlockItemBaseTypes/StringListBlockItem.cs @@ -0,0 +1,42 @@ +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows.Media; +using Filtration.Annotations; + +namespace Filtration.Models.BlockItemBaseTypes +{ + internal abstract class StringListBlockItem : ILootFilterBlockItem + { + protected StringListBlockItem() + { + Items = new ObservableCollection(); + Items.CollectionChanged += OnItemsCollectionChanged; + } + + public abstract string PrefixText { get; } + public abstract int MaximumAllowed { get; } + public abstract string DisplayHeading { get; } + + public abstract string SummaryText { get; } + public abstract Color SummaryBackgroundColor { get; } + public abstract Color SummaryTextColor { get; } + public abstract int SortOrder { get; } + public ObservableCollection Items { get; protected set; } + + private void OnItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + OnPropertyChanged("Items"); + OnPropertyChanged("SummaryText"); + } + + public event PropertyChangedEventHandler PropertyChanged; + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/Models/BlockItemTypes/BackgroundColorBlockItem.cs b/Filtration/Models/BlockItemTypes/BackgroundColorBlockItem.cs new file mode 100644 index 0000000..cf1fddb --- /dev/null +++ b/Filtration/Models/BlockItemTypes/BackgroundColorBlockItem.cs @@ -0,0 +1,39 @@ +using System.Windows.Media; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class BackgroundColorBlockItem : ColorBlockItem + { + public BackgroundColorBlockItem() + { + } + + public BackgroundColorBlockItem(Color color) : base(color) + { + } + + public override string PrefixText + { + get { return "SetBackgroundColor"; } + } + + public override int MaximumAllowed + { + get { return 1; } + } + + public override string DisplayHeading + { + get + { + return "Background Color"; + } + } + + public override int SortOrder + { + get { return 13; } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/BaseTypeBlockItem.cs b/Filtration/Models/BlockItemTypes/BaseTypeBlockItem.cs new file mode 100644 index 0000000..6fb2133 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/BaseTypeBlockItem.cs @@ -0,0 +1,60 @@ +using System.Linq; +using System.Windows.Media; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class BaseTypeBlockItem : StringListBlockItem + { + public override string PrefixText { get { return "BaseType"; } } + + public override int MaximumAllowed + { + get { return 1; } + } + + public override string DisplayHeading + { + get + { + return "Base Type"; + } + } + + public override string SummaryText + { + get + { + if (Items.Count > 0 && Items.Count < 4) + { + return "Item Base Types: " + + Items.Aggregate(string.Empty, (current, i) => current + i + ", ").TrimEnd(' ').TrimEnd(','); + } + if (Items.Count >= 4) + { + var remaining = Items.Count - 3; + return "Item Base Types: " + Items.Take(3) + .Aggregate(string.Empty, (current, i) => current + i + ", ") + .TrimEnd(' ') + .TrimEnd(',') + " (+" + remaining + " more)"; + } + return "Item Base Types: (none)"; + } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.MediumTurquoise; } + } + + public override Color SummaryTextColor + { + get { return Colors.Black; } + } + + public override int SortOrder + { + get { return 11; } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/BorderColorBlockItem.cs b/Filtration/Models/BlockItemTypes/BorderColorBlockItem.cs new file mode 100644 index 0000000..59a025c --- /dev/null +++ b/Filtration/Models/BlockItemTypes/BorderColorBlockItem.cs @@ -0,0 +1,39 @@ +using System.Windows.Media; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class BorderColorBlockItem : ColorBlockItem + { + public BorderColorBlockItem() + { + } + + public BorderColorBlockItem(Color color) : base(color) + { + } + + public override string PrefixText + { + get { return "SetBorderColor"; } + } + + public override int MaximumAllowed + { + get { return 1; } + } + + public override string DisplayHeading + { + get + { + return "Border Color"; + } + } + + public override int SortOrder + { + get { return 14; } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/ClassBlockItem.cs b/Filtration/Models/BlockItemTypes/ClassBlockItem.cs new file mode 100644 index 0000000..2dfc577 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/ClassBlockItem.cs @@ -0,0 +1,54 @@ +using System.Linq; +using System.Windows.Media; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class ClassBlockItem : StringListBlockItem + { + public override string PrefixText { get { return "Class"; } } + + public override int MaximumAllowed + { + get { return 1; } + } + + public override string DisplayHeading { get { return "Class"; } } + + public override string SummaryText + { + get + { + if (Items.Count > 0 && Items.Count < 4) + { + return "Item Classes: " + + Items.Aggregate(string.Empty, (current, i) => current + i + ", ").TrimEnd(' ').TrimEnd(','); + } + if (Items.Count >= 4) + { + var remaining = Items.Count - 3; + return "Item Classes: " + Items.Take(3) + .Aggregate(string.Empty, (current, i) => current + i + ", ") + .TrimEnd(' ') + .TrimEnd(',') + " (+" + remaining + " more)"; + } + return "Item Classes: (none)"; + } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.MediumSeaGreen; } + } + + public override Color SummaryTextColor + { + get { return Colors.White; } + } + + public override int SortOrder + { + get { return 10; } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/DropLevelBlockItem.cs b/Filtration/Models/BlockItemTypes/DropLevelBlockItem.cs new file mode 100644 index 0000000..1e686b1 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/DropLevelBlockItem.cs @@ -0,0 +1,72 @@ +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class DropLevelBlockItem : NumericFilterPredicateBlockItem + { + public DropLevelBlockItem() + { + } + + public DropLevelBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) + : base(predicateOperator, predicateOperand) + { + } + + public override string PrefixText + { + get { return "DropLevel"; } + } + + public override int MaximumAllowed + { + get { return 2; } + } + + public override string DisplayHeading + { + get + { + return "Drop Level"; + } + } + + public override string SummaryText + { + get { return "Drop Level " + FilterPredicate; } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.DodgerBlue; } + } + + public override Color SummaryTextColor + { + get { return Colors.White; } + } + + public override int SortOrder + { + get { return 2; } + } + + public override int Minimum + { + get + { + return 0; + } + } + + public override int Maximum + { + get + { + return 100; + } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/FontSizeBlockItem.cs b/Filtration/Models/BlockItemTypes/FontSizeBlockItem.cs new file mode 100644 index 0000000..d9eb107 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/FontSizeBlockItem.cs @@ -0,0 +1,54 @@ +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class FontSizeBlockItem : IntegerBlockItem + { + public FontSizeBlockItem() + { + } + + public FontSizeBlockItem(int value) : base(value) + { + } + + public override string PrefixText + { + get { return "SetFontSize"; } + } + + public override int MaximumAllowed + { + get { return 1; } + } + + public override string DisplayHeading + { + get + { + return "Font Size"; + } + } + + public override int SortOrder + { + get { return 15; } + } + + public override int Minimum + { + get + { + return 11; + } + } + + public override int Maximum + { + get + { + return 45; + } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/HeightBlockItem.cs b/Filtration/Models/BlockItemTypes/HeightBlockItem.cs new file mode 100644 index 0000000..f21a13b --- /dev/null +++ b/Filtration/Models/BlockItemTypes/HeightBlockItem.cs @@ -0,0 +1,69 @@ +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class HeightBlockItem : NumericFilterPredicateBlockItem + { + public HeightBlockItem() + { + } + + public HeightBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) + : base(predicateOperator, predicateOperand) + { + } + + public override string PrefixText + { + get { return "Height"; } + } + + public override int MaximumAllowed + { + get { return 2; } + } + + public override string DisplayHeading + { + get { return "Height"; } + } + + public override string SummaryText + { + get { return "Height " + FilterPredicate; } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.Plum; } + } + + public override Color SummaryTextColor + { + get { return Colors.White; } + } + + public override int SortOrder + { + get { return 8; } + } + + public override int Minimum + { + get + { + return 0; + } + } + + public override int Maximum + { + get + { + return 6; + } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/ItemLevelBlockItem.cs b/Filtration/Models/BlockItemTypes/ItemLevelBlockItem.cs new file mode 100644 index 0000000..ca1b31f --- /dev/null +++ b/Filtration/Models/BlockItemTypes/ItemLevelBlockItem.cs @@ -0,0 +1,71 @@ +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class ItemLevelBlockItem : NumericFilterPredicateBlockItem + { + public ItemLevelBlockItem() + { + } + + public ItemLevelBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) : base (predicateOperator, predicateOperand) + { + } + + public override string PrefixText + { + get { return "ItemLevel"; } + } + + public override int MaximumAllowed + { + get { return 2; } + } + + public override string DisplayHeading + { + get + { + return "Item Level"; + } + } + + public override string SummaryText + { + get { return "Item Level " + FilterPredicate; } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.DarkSlateGray; } + } + + public override Color SummaryTextColor + { + get { return Colors.White; } + } + + public override int SortOrder + { + get { return 1; } + } + + public override int Minimum + { + get + { + return 0; + } + } + + public override int Maximum + { + get + { + return 100; + } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/LinkedSocketsBlockItem.cs b/Filtration/Models/BlockItemTypes/LinkedSocketsBlockItem.cs new file mode 100644 index 0000000..8046890 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/LinkedSocketsBlockItem.cs @@ -0,0 +1,72 @@ +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class LinkedSocketsBlockItem : NumericFilterPredicateBlockItem + { + public LinkedSocketsBlockItem() + { + } + + public LinkedSocketsBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) + : base(predicateOperator, predicateOperand) + { + } + + public override string PrefixText + { + get { return "LinkedSockets"; } + } + + public override int MaximumAllowed + { + get { return 2; } + } + + public override string DisplayHeading + { + get + { + return "Linked Sockets"; + } + } + + public override string SummaryText + { + get { return "Linked Sockets " + FilterPredicate; } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.Gold; } + } + + public override Color SummaryTextColor + { + get { return Colors.Black; } + } + + public override int SortOrder + { + get { return 6; } + } + + public override int Minimum + { + get + { + return 0; + } + } + + public override int Maximum + { + get + { + return 6; + } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/QualityBlockItem.cs b/Filtration/Models/BlockItemTypes/QualityBlockItem.cs new file mode 100644 index 0000000..2c9f062 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/QualityBlockItem.cs @@ -0,0 +1,72 @@ +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class QualityBlockItem : NumericFilterPredicateBlockItem + { + public QualityBlockItem() + { + } + + public QualityBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) + : base(predicateOperator, predicateOperand) + { + } + + public override string PrefixText + { + get { return "Quality"; } + } + + public override int MaximumAllowed + { + get { return 2; } + } + + public override string DisplayHeading + { + get + { + return "Quality"; + } + } + + public override string SummaryText + { + get { return "Quality " + FilterPredicate; } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.DarkOrange; } + } + + public override Color SummaryTextColor + { + get { return Colors.White; } + } + + public override int SortOrder + { + get { return 3; } + } + + public override int Minimum + { + get + { + return 0; + } + } + + public override int Maximum + { + get + { + return 20; + } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/RarityBlockItem.cs b/Filtration/Models/BlockItemTypes/RarityBlockItem.cs new file mode 100644 index 0000000..60ed7f6 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/RarityBlockItem.cs @@ -0,0 +1,77 @@ +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Extensions; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class RarityBlockItem : NumericFilterPredicateBlockItem + { + public RarityBlockItem() + { + } + + public RarityBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) + : base(predicateOperator, predicateOperand) + { + } + + public override string PrefixText + { + get { return "Rarity"; } + } + + public override int MaximumAllowed + { + get { return 2; } + } + + public override string DisplayHeading + { + get + { + return "Item Rarity"; + } + } + + public override string SummaryText + { + get + { + return "Rarity " + FilterPredicate.PredicateOperator.GetAttributeDescription() + " " + + ((ItemRarity) FilterPredicate.PredicateOperand).GetAttributeDescription(); + } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.LightCoral; } + } + + public override Color SummaryTextColor + { + get { return Colors.White; } + } + + public override int SortOrder + { + get { return 4; } + } + + public override int Minimum + { + get + { + return 0; + } + } + + public override int Maximum + { + get + { + return (int)ItemRarity.Unique; + } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/SocketGroupBlockItem.cs b/Filtration/Models/BlockItemTypes/SocketGroupBlockItem.cs new file mode 100644 index 0000000..72e01e6 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/SocketGroupBlockItem.cs @@ -0,0 +1,51 @@ +using System.Linq; +using System.Windows.Media; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class SocketGroupBlockItem : StringListBlockItem + { + public override string PrefixText + { + get { return "SocketGroup"; } + } + + public override int MaximumAllowed + { + get { return 1; } + } + + public override string DisplayHeading + { + get + { + return "Socket Group"; + } + } + + public override string SummaryText + { + get + { + var summaryItemText = " " + Items.Aggregate(string.Empty, (current, i) => current + " " + i); + return "Socket Group " + summaryItemText.TrimStart(' '); + } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.GhostWhite; } + } + + public override Color SummaryTextColor + { + get { return Colors.Black; } + } + + public override int SortOrder + { + get { return 9; } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/SocketsBlockItem.cs b/Filtration/Models/BlockItemTypes/SocketsBlockItem.cs new file mode 100644 index 0000000..a91a8d3 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/SocketsBlockItem.cs @@ -0,0 +1,72 @@ +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class SocketsBlockItem : NumericFilterPredicateBlockItem + { + public SocketsBlockItem() + { + } + + public SocketsBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) + : base(predicateOperator, predicateOperand) + { + } + + public override string PrefixText + { + get { return "Sockets"; } + } + + public override int MaximumAllowed + { + get { return 2; } + } + + public override string DisplayHeading + { + get + { + return "Sockets"; + } + } + + public override string SummaryText + { + get { return "Sockets " + FilterPredicate; } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.LightGray; } + } + + public override Color SummaryTextColor + { + get { return Colors.Black; } + } + + public override int SortOrder + { + get { return 5; } + } + + public override int Minimum + { + get + { + return 0; + } + } + + public override int Maximum + { + get + { + return 6; + } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/SoundBlockItem.cs b/Filtration/Models/BlockItemTypes/SoundBlockItem.cs new file mode 100644 index 0000000..481a9d2 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/SoundBlockItem.cs @@ -0,0 +1,40 @@ +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class SoundBlockItem : DualIntegerBlockItem + { + public SoundBlockItem() + { + Value = 1; + SecondValue = 79; + } + + public SoundBlockItem(int value, int secondValue) : base(value, secondValue) + { + } + + public override string PrefixText + { + get { return "PlayAlertSound"; } + } + + public override int MaximumAllowed + { + get { return 1; } + } + + public override string DisplayHeading + { + get + { + return "Play Alert Sound"; + } + } + + public override int SortOrder + { + get { return 16; } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/TextColorBlockItem.cs b/Filtration/Models/BlockItemTypes/TextColorBlockItem.cs new file mode 100644 index 0000000..02c3883 --- /dev/null +++ b/Filtration/Models/BlockItemTypes/TextColorBlockItem.cs @@ -0,0 +1,39 @@ +using System.Windows.Media; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class TextColorBlockItem : ColorBlockItem + { + public TextColorBlockItem() + { + } + + public TextColorBlockItem(Color color) : base(color) + { + } + + public override string PrefixText + { + get { return "SetTextColor"; } + } + + public override int MaximumAllowed + { + get { return 1; } + } + + public override string DisplayHeading + { + get + { + return "Text Color"; + } + } + + public override int SortOrder + { + get { return 12; } + } + } +} diff --git a/Filtration/Models/BlockItemTypes/WidthBlockItem.cs b/Filtration/Models/BlockItemTypes/WidthBlockItem.cs new file mode 100644 index 0000000..189632b --- /dev/null +++ b/Filtration/Models/BlockItemTypes/WidthBlockItem.cs @@ -0,0 +1,72 @@ +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models.BlockItemTypes +{ + internal class WidthBlockItem : NumericFilterPredicateBlockItem + { + public WidthBlockItem() + { + } + + public WidthBlockItem(FilterPredicateOperator predicateOperator, int predicateOperand) + : base(predicateOperator, predicateOperand) + { + } + + public override string PrefixText + { + get { return "Width"; } + } + + public override int MaximumAllowed + { + get { return 2; } + } + + public override string DisplayHeading + { + get + { + return "Width"; + } + } + + public override string SummaryText + { + get { return "Width " + FilterPredicate; } + } + + public override Color SummaryBackgroundColor + { + get { return Colors.Tan; } + } + + public override Color SummaryTextColor + { + get { return Colors.White; } + } + + public override int SortOrder + { + get { return 7; } + } + + public override int Minimum + { + get + { + return 0; + } + } + + public override int Maximum + { + get + { + return 2; + } + } + } +} diff --git a/Filtration/Models/IAudioVisualBlockItem.cs b/Filtration/Models/IAudioVisualBlockItem.cs new file mode 100644 index 0000000..a808331 --- /dev/null +++ b/Filtration/Models/IAudioVisualBlockItem.cs @@ -0,0 +1,9 @@ +using System.ComponentModel; + +namespace Filtration.Models +{ + internal interface IAudioVisualBlockItem + { + event PropertyChangedEventHandler PropertyChanged; + } +} diff --git a/Filtration/Models/ILootFilterBlockItem.cs b/Filtration/Models/ILootFilterBlockItem.cs new file mode 100644 index 0000000..4bccfd9 --- /dev/null +++ b/Filtration/Models/ILootFilterBlockItem.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; +using System.Windows.Media; + +namespace Filtration.Models +{ + internal interface ILootFilterBlockItem : INotifyPropertyChanged + { + string PrefixText { get; } + int MaximumAllowed { get; } + string DisplayHeading { get; } + string SummaryText { get; } + Color SummaryBackgroundColor { get; } + Color SummaryTextColor { get; } + int SortOrder { get; } + } +} diff --git a/Filtration/Models/LootFilterBlock.cs b/Filtration/Models/LootFilterBlock.cs new file mode 100644 index 0000000..a58b41b --- /dev/null +++ b/Filtration/Models/LootFilterBlock.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.ObjectModel; +using System.Linq; +using Filtration.Enums; +using Filtration.Models.BlockItemBaseTypes; + +namespace Filtration.Models +{ + internal class LootFilterBlock + { + public LootFilterBlock() + { + BlockItems = new ObservableCollection {new ActionBlockItem(BlockAction.Show)}; + } + + public string Description { get; set; } + + public BlockAction Action + { + get + { + var actionBlock = BlockItems.OfType().First(); + return actionBlock.Action; + } + set + { + var actionBlock = BlockItems.OfType().First(); + actionBlock.Action = value; + } + } + + public ObservableCollection BlockItems { get; private set; } + + public int BlockCount(Type type) + { + return BlockItems != null ? BlockItems.Count(b => b.GetType() == type) : 0; + } + + public bool AddBlockItemAllowed(Type type) + { + var blockItem = (ILootFilterBlockItem)Activator.CreateInstance(type); + return BlockCount(type) < blockItem.MaximumAllowed; + } + } +} diff --git a/Filtration/Models/LootFilterScript.cs b/Filtration/Models/LootFilterScript.cs new file mode 100644 index 0000000..17ee0da --- /dev/null +++ b/Filtration/Models/LootFilterScript.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Filtration.Models +{ + internal class LootFilterScript + { + public LootFilterScript() + { + LootFilterBlocks = new ObservableCollection(); + } + + public ObservableCollection LootFilterBlocks { get; set; } + public string FilePath { get; set; } + public string Description { get; set; } + public DateTime DateModified { get; set; } + + public List Validate() + { + var validationErrors = new List(); + + if (LootFilterBlocks.Count == 0) + { + validationErrors.Add("A script must have at least one block"); + } + + return validationErrors; + } + } +} diff --git a/Filtration/Models/LootFilterSection.cs b/Filtration/Models/LootFilterSection.cs new file mode 100644 index 0000000..19c9de2 --- /dev/null +++ b/Filtration/Models/LootFilterSection.cs @@ -0,0 +1,6 @@ +namespace Filtration.Models +{ + internal class LootFilterSection : LootFilterBlock + { + } +} diff --git a/Filtration/Models/NumericFilterPredicate.cs b/Filtration/Models/NumericFilterPredicate.cs new file mode 100644 index 0000000..e6d3188 --- /dev/null +++ b/Filtration/Models/NumericFilterPredicate.cs @@ -0,0 +1,59 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using Filtration.Annotations; +using Filtration.Enums; +using Filtration.Extensions; + +namespace Filtration.Models +{ + internal class NumericFilterPredicate : INotifyPropertyChanged + { + private FilterPredicateOperator _predicateOperator; + private int _predicateOperand; + + public NumericFilterPredicate(FilterPredicateOperator predicateOperator, int predicateOperand) + { + PredicateOperator = predicateOperator; + PredicateOperand = predicateOperand; + } + + public NumericFilterPredicate() + { + + } + + public FilterPredicateOperator PredicateOperator + { + get { return _predicateOperator; } + set + { + _predicateOperator = value; + OnPropertyChanged(); + } + } + + public int PredicateOperand + { + get { return _predicateOperand; } + set + { + _predicateOperand = value; + OnPropertyChanged(); + } + } + + public override string ToString() + { + return PredicateOperator.GetAttributeDescription() + " " + PredicateOperand; + } + + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/Properties/Annotations.cs b/Filtration/Properties/Annotations.cs new file mode 100644 index 0000000..444902e --- /dev/null +++ b/Filtration/Properties/Annotations.cs @@ -0,0 +1,937 @@ +using System; + +#pragma warning disable 1591 +// ReSharper disable UnusedMember.Global +// ReSharper disable MemberCanBePrivate.Global +// ReSharper disable UnusedAutoPropertyAccessor.Global +// ReSharper disable IntroduceOptionalParameters.Global +// ReSharper disable MemberCanBeProtected.Global +// ReSharper disable InconsistentNaming +// ReSharper disable CheckNamespace + +namespace Filtration.Annotations +{ + /// + /// Indicates that the value of the marked element could be null sometimes, + /// so the check for null is necessary before its usage. + /// + /// + /// [CanBeNull] public object Test() { return null; } + /// public void UseTest() { + /// var p = Test(); + /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)] + public sealed class CanBeNullAttribute : Attribute { } + + /// + /// Indicates that the value of the marked element could never be null. + /// + /// + /// [NotNull] public object Foo() { + /// return null; // Warning: Possible 'null' assignment + /// } + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event)] + public sealed class NotNullAttribute : Attribute { } + + /// + /// Indicates that collection or enumerable value does not contain null elements. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemNotNullAttribute : Attribute { } + + /// + /// Indicates that collection or enumerable value can contain null elements. + /// + [AttributeUsage( + AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | + AttributeTargets.Delegate | AttributeTargets.Field)] + public sealed class ItemCanBeNullAttribute : Attribute { } + + /// + /// Indicates that the marked method builds string by format pattern and (optional) arguments. + /// Parameter, which contains format string, should be given in constructor. The format string + /// should be in -like form. + /// + /// + /// [StringFormatMethod("message")] + /// public void ShowError(string message, params object[] args) { /* do something */ } + /// public void Foo() { + /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string + /// } + /// + [AttributeUsage( + AttributeTargets.Constructor | AttributeTargets.Method | + AttributeTargets.Property | AttributeTargets.Delegate)] + public sealed class StringFormatMethodAttribute : Attribute + { + /// + /// Specifies which parameter of an annotated method should be treated as format-string + /// + public StringFormatMethodAttribute(string formatParameterName) + { + FormatParameterName = formatParameterName; + } + + public string FormatParameterName { get; private set; } + } + + /// + /// For a parameter that is expected to be one of the limited set of values. + /// Specify fields of which type should be used as values for this parameter. + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class ValueProviderAttribute : Attribute + { + public ValueProviderAttribute(string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Indicates that the function argument should be string literal and match one + /// of the parameters of the caller function. For example, ReSharper annotates + /// the parameter of . + /// + /// + /// public void Foo(string param) { + /// if (param == null) + /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol + /// } + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InvokerParameterNameAttribute : Attribute { } + + /// + /// Indicates that the method is contained in a type that implements + /// System.ComponentModel.INotifyPropertyChanged interface and this method + /// is used to notify that some property value changed. + /// + /// + /// The method should be non-static and conform to one of the supported signatures: + /// + /// NotifyChanged(string) + /// NotifyChanged(params string[]) + /// NotifyChanged{T}(Expression{Func{T}}) + /// NotifyChanged{T,U}(Expression{Func{T,U}}) + /// SetProperty{T}(ref T, T, string) + /// + /// + /// + /// public class Foo : INotifyPropertyChanged { + /// public event PropertyChangedEventHandler PropertyChanged; + /// [NotifyPropertyChangedInvocator] + /// protected virtual void NotifyChanged(string propertyName) { ... } + /// + /// private string _name; + /// public string Name { + /// get { return _name; } + /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } + /// } + /// } + /// + /// Examples of generated notifications: + /// + /// NotifyChanged("Property") + /// NotifyChanged(() => Property) + /// NotifyChanged((VM x) => x.Property) + /// SetProperty(ref myField, value, "Property") + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class NotifyPropertyChangedInvocatorAttribute : Attribute + { + public NotifyPropertyChangedInvocatorAttribute() { } + public NotifyPropertyChangedInvocatorAttribute(string parameterName) + { + ParameterName = parameterName; + } + + public string ParameterName { get; private set; } + } + + /// + /// Describes dependency between method input and output. + /// + /// + ///

Function Definition Table syntax:

+ /// + /// FDT ::= FDTRow [;FDTRow]* + /// FDTRow ::= Input => Output | Output <= Input + /// Input ::= ParameterName: Value [, Input]* + /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} + /// Value ::= true | false | null | notnull | canbenull + /// + /// If method has single input parameter, it's name could be omitted.
+ /// Using halt (or void/nothing, which is the same) + /// for method output means that the methos doesn't return normally.
+ /// canbenull annotation is only applicable for output parameters.
+ /// You can use multiple [ContractAnnotation] for each FDT row, + /// or use single attribute with rows separated by semicolon.
+ ///
+ /// + /// + /// [ContractAnnotation("=> halt")] + /// public void TerminationMethod() + /// + /// + /// [ContractAnnotation("halt <= condition: false")] + /// public void Assert(bool condition, string text) // regular assertion method + /// + /// + /// [ContractAnnotation("s:null => true")] + /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() + /// + /// + /// // A method that returns null if the parameter is null, + /// // and not null if the parameter is not null + /// [ContractAnnotation("null => null; notnull => notnull")] + /// public object Transform(object data) + /// + /// + /// [ContractAnnotation("s:null=>false; =>true,result:notnull; =>false, result:null")] + /// public bool TryParse(string s, out Person result) + /// + /// + [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] + public sealed class ContractAnnotationAttribute : Attribute + { + public ContractAnnotationAttribute([NotNull] string contract) + : this(contract, false) { } + + public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) + { + Contract = contract; + ForceFullStates = forceFullStates; + } + + public string Contract { get; private set; } + public bool ForceFullStates { get; private set; } + } + + /// + /// Indicates that marked element should be localized or not. + /// + /// + /// [LocalizationRequiredAttribute(true)] + /// public class Foo { + /// private string str = "my string"; // Warning: Localizable string + /// } + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class LocalizationRequiredAttribute : Attribute + { + public LocalizationRequiredAttribute() : this(true) { } + public LocalizationRequiredAttribute(bool required) + { + Required = required; + } + + public bool Required { get; private set; } + } + + /// + /// Indicates that the value of the marked type (or its derivatives) + /// cannot be compared using '==' or '!=' operators and Equals() + /// should be used instead. However, using '==' or '!=' for comparison + /// with null is always permitted. + /// + /// + /// [CannotApplyEqualityOperator] + /// class NoEquality { } + /// class UsesNoEquality { + /// public void Test() { + /// var ca1 = new NoEquality(); + /// var ca2 = new NoEquality(); + /// if (ca1 != null) { // OK + /// bool condition = ca1 == ca2; // Warning + /// } + /// } + /// } + /// + [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] + public sealed class CannotApplyEqualityOperatorAttribute : Attribute { } + + /// + /// When applied to a target attribute, specifies a requirement for any type marked + /// with the target attribute to implement or inherit specific type or types. + /// + /// + /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement + /// public class ComponentAttribute : Attribute { } + /// [Component] // ComponentAttribute requires implementing IComponent interface + /// public class MyComponent : IComponent { } + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + [BaseTypeRequired(typeof(Attribute))] + public sealed class BaseTypeRequiredAttribute : Attribute + { + public BaseTypeRequiredAttribute([NotNull] Type baseType) + { + BaseType = baseType; + } + + [NotNull] public Type BaseType { get; private set; } + } + + /// + /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), + /// so this symbol will not be marked as unused (as well as by other usage inspections). + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class UsedImplicitlyAttribute : Attribute + { + public UsedImplicitlyAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + public ImplicitUseKindFlags UseKindFlags { get; private set; } + public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + /// + /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes + /// as unused (as well as by other usage inspections) + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] + public sealed class MeansImplicitUseAttribute : Attribute + { + public MeansImplicitUseAttribute() + : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) + : this(useKindFlags, ImplicitUseTargetFlags.Default) { } + + public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) + : this(ImplicitUseKindFlags.Default, targetFlags) { } + + public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) + { + UseKindFlags = useKindFlags; + TargetFlags = targetFlags; + } + + [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } + [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } + } + + [Flags] + public enum ImplicitUseKindFlags + { + Default = Access | Assign | InstantiatedWithFixedConstructorSignature, + /// Only entity marked with attribute considered used. + Access = 1, + /// Indicates implicit assignment to a member. + Assign = 2, + /// + /// Indicates implicit instantiation of a type with fixed constructor signature. + /// That means any unused constructor parameters won't be reported as such. + /// + InstantiatedWithFixedConstructorSignature = 4, + /// Indicates implicit instantiation of a type. + InstantiatedNoFixedConstructorSignature = 8, + } + + /// + /// Specify what is considered used implicitly when marked + /// with or . + /// + [Flags] + public enum ImplicitUseTargetFlags + { + Default = Itself, + Itself = 1, + /// Members of entity marked with attribute are considered used. + Members = 2, + /// Entity marked with attribute and all its members considered used. + WithMembers = Itself | Members + } + + /// + /// This attribute is intended to mark publicly available API + /// which should not be removed and so is treated as used. + /// + [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] + public sealed class PublicAPIAttribute : Attribute + { + public PublicAPIAttribute() { } + public PublicAPIAttribute([NotNull] string comment) + { + Comment = comment; + } + + public string Comment { get; private set; } + } + + /// + /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. + /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. + /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class InstantHandleAttribute : Attribute { } + + /// + /// Indicates that a method does not make any observable state changes. + /// The same as System.Diagnostics.Contracts.PureAttribute. + /// + /// + /// [Pure] private int Multiply(int x, int y) { return x * y; } + /// public void Foo() { + /// const int a = 2, b = 2; + /// Multiply(a, b); // Waring: Return value of pure method is not used + /// } + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class PureAttribute : Attribute { } + + /// + /// Indicates that a parameter is a path to a file or a folder within a web project. + /// Path can be relative or absolute, starting from web root (~). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class PathReferenceAttribute : Attribute + { + public PathReferenceAttribute() { } + public PathReferenceAttribute([PathReference] string basePath) + { + BasePath = basePath; + } + + public string BasePath { get; private set; } + } + + /// + /// An extension method marked with this attribute is processed by ReSharper code completion + /// as a 'Source Template'. When extension method is completed over some expression, it's source code + /// is automatically expanded like a template at call site. + /// + /// + /// Template method body can contain valid source code and/or special comments starting with '$'. + /// Text inside these comments is added as source code when the template is applied. Template parameters + /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. + /// Use the attribute to specify macros for parameters. + /// + /// + /// In this example, the 'forEach' method is a source template available over all values + /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: + /// + /// [SourceTemplate] + /// public static void forEach<T>(this IEnumerable<T> xs) { + /// foreach (var x in xs) { + /// //$ $END$ + /// } + /// } + /// + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class SourceTemplateAttribute : Attribute { } + + /// + /// Allows specifying a macro for a parameter of a source template. + /// + /// + /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression + /// is defined in the property. When applied on a method, the target + /// template parameter is defined in the property. To apply the macro silently + /// for the parameter, set the property value = -1. + /// + /// + /// Applying the attribute on a source template method: + /// + /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] + /// public static void forEach<T>(this IEnumerable<T> collection) { + /// foreach (var item in collection) { + /// //$ $END$ + /// } + /// } + /// + /// Applying the attribute on a template method parameter: + /// + /// [SourceTemplate] + /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { + /// /*$ var $x$Id = "$newguid$" + x.ToString(); + /// x.DoSomething($x$Id); */ + /// } + /// + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] + public sealed class MacroAttribute : Attribute + { + /// + /// Allows specifying a macro that will be executed for a source template + /// parameter when the template is expanded. + /// + public string Expression { get; set; } + + /// + /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. + /// + /// + /// If the target parameter is used several times in the template, only one occurrence becomes editable; + /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, + /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. + /// > + public int Editable { get; set; } + + /// + /// Identifies the target parameter of a source template if the + /// is applied on a template method. + /// + public string Target { get; set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute + { + public AspMvcAreaMasterLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute + { + public AspMvcAreaPartialViewLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcAreaViewLocationFormatAttribute : Attribute + { + public AspMvcAreaViewLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcMasterLocationFormatAttribute : Attribute + { + public AspMvcMasterLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcPartialViewLocationFormatAttribute : Attribute + { + public AspMvcPartialViewLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class AspMvcViewLocationFormatAttribute : Attribute + { + public AspMvcViewLocationFormatAttribute(string format) + { + Format = format; + } + + public string Format { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC action. If applied to a method, the MVC action name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcActionAttribute : Attribute + { + public AspMvcActionAttribute() { } + public AspMvcActionAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcAreaAttribute : Attribute + { + public AspMvcAreaAttribute() { } + public AspMvcAreaAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is + /// an MVC controller. If applied to a method, the MVC controller name is calculated + /// implicitly from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcControllerAttribute : Attribute + { + public AspMvcControllerAttribute() { } + public AspMvcControllerAttribute(string anonymousProperty) + { + AnonymousProperty = anonymousProperty; + } + + public string AnonymousProperty { get; private set; } + } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcMasterAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute + /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcModelTypeAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC + /// partial view. If applied to a method, the MVC partial view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcPartialViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] + public sealed class AspMvcSupressViewErrorAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcDisplayTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. + /// Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcEditorTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. + /// Use this attribute for custom wrappers similar to + /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AspMvcTemplateAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter + /// is an MVC view. If applied to a method, the MVC view name is calculated implicitly + /// from the context. Use this attribute for custom wrappers similar to + /// System.Web.Mvc.Controller.View(Object). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class AspMvcViewAttribute : Attribute { } + + /// + /// ASP.NET MVC attribute. When applied to a parameter of an attribute, + /// indicates that this parameter is an MVC action name. + /// + /// + /// [ActionName("Foo")] + /// public ActionResult Login(string returnUrl) { + /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK + /// return RedirectToAction("Bar"); // Error: Cannot resolve action + /// } + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] + public sealed class AspMvcActionSelectorAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class HtmlElementAttributesAttribute : Attribute + { + public HtmlElementAttributesAttribute() { } + public HtmlElementAttributesAttribute(string name) + { + Name = name; + } + + public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] + public sealed class HtmlAttributeValueAttribute : Attribute + { + public HtmlAttributeValueAttribute([NotNull] string name) + { + Name = name; + } + + [NotNull] public string Name { get; private set; } + } + + /// + /// Razor attribute. Indicates that a parameter or a method is a Razor section. + /// Use this attribute for custom wrappers similar to + /// System.Web.WebPages.WebPageBase.RenderSection(String). + /// + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] + public sealed class RazorSectionAttribute : Attribute { } + + /// + /// Indicates how method invocation affects content of the collection. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class CollectionAccessAttribute : Attribute + { + public CollectionAccessAttribute(CollectionAccessType collectionAccessType) + { + CollectionAccessType = collectionAccessType; + } + + public CollectionAccessType CollectionAccessType { get; private set; } + } + + [Flags] + public enum CollectionAccessType + { + /// Method does not use or modify content of the collection. + None = 0, + /// Method only reads content of the collection but does not modify it. + Read = 1, + /// Method can change content of the collection but does not add new elements. + ModifyExistingContent = 2, + /// Method can add new elements to the collection. + UpdatedContent = ModifyExistingContent | 4 + } + + /// + /// Indicates that the marked method is assertion method, i.e. it halts control flow if + /// one of the conditions is satisfied. To set the condition, mark one of the parameters with + /// attribute. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class AssertionMethodAttribute : Attribute { } + + /// + /// Indicates the condition parameter of the assertion method. The method itself should be + /// marked by attribute. The mandatory argument of + /// the attribute is the assertion type. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class AssertionConditionAttribute : Attribute + { + public AssertionConditionAttribute(AssertionConditionType conditionType) + { + ConditionType = conditionType; + } + + public AssertionConditionType ConditionType { get; private set; } + } + + /// + /// Specifies assertion type. If the assertion method argument satisfies the condition, + /// then the execution continues. Otherwise, execution is assumed to be halted. + /// + public enum AssertionConditionType + { + /// Marked parameter should be evaluated to true. + IS_TRUE = 0, + /// Marked parameter should be evaluated to false. + IS_FALSE = 1, + /// Marked parameter should be evaluated to null value. + IS_NULL = 2, + /// Marked parameter should be evaluated to not null value. + IS_NOT_NULL = 3, + } + + /// + /// Indicates that the marked method unconditionally terminates control flow execution. + /// For example, it could unconditionally throw exception. + /// + [Obsolete("Use [ContractAnnotation('=> halt')] instead")] + [AttributeUsage(AttributeTargets.Method)] + public sealed class TerminatesProgramAttribute : Attribute { } + + /// + /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, + /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters + /// of delegate type by analyzing LINQ method chains. + /// + [AttributeUsage(AttributeTargets.Method)] + public sealed class LinqTunnelAttribute : Attribute { } + + /// + /// Indicates that IEnumerable, passed as parameter, is not enumerated. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class NoEnumerationAttribute : Attribute { } + + /// + /// Indicates that parameter is regular expression pattern. + /// + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RegexPatternAttribute : Attribute { } + + /// + /// XAML attribute. Indicates the type that has ItemsSource property and should be treated + /// as ItemsControl-derived type, to enable inner items DataContext type resolve. + /// + [AttributeUsage(AttributeTargets.Class)] + public sealed class XamlItemsControlAttribute : Attribute { } + + /// + /// XAML attibute. Indicates the property of some BindingBase-derived type, that + /// is used to bind some item of ItemsControl-derived type. This annotation will + /// enable the DataContext type resolve for XAML bindings for such properties. + /// + /// + /// Property should have the tree ancestor of the ItemsControl type or + /// marked with the attribute. + /// + [AttributeUsage(AttributeTargets.Property)] + public sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspChildControlTypeAttribute : Attribute + { + public AspChildControlTypeAttribute(string tagName, Type controlType) + { + TagName = tagName; + ControlType = controlType; + } + + public string TagName { get; private set; } + public Type ControlType { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] + public sealed class AspDataFieldsAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspMethodPropertyAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] + public sealed class AspRequiredAttributeAttribute : Attribute + { + public AspRequiredAttributeAttribute([NotNull] string attribute) + { + Attribute = attribute; + } + + public string Attribute { get; private set; } + } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class AspTypePropertyAttribute : Attribute + { + public bool CreateConstructorReferences { get; private set; } + + public AspTypePropertyAttribute(bool createConstructorReferences) + { + CreateConstructorReferences = createConstructorReferences; + } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorImportNamespaceAttribute : Attribute + { + public RazorImportNamespaceAttribute(string name) + { + Name = name; + } + + public string Name { get; private set; } + } + + [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] + public sealed class RazorInjectionAttribute : Attribute + { + public RazorInjectionAttribute(string type, string fieldName) + { + Type = type; + FieldName = fieldName; + } + + public string Type { get; private set; } + public string FieldName { get; private set; } + } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorHelperCommonAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Property)] + public sealed class RazorLayoutAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteLiteralMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Method)] + public sealed class RazorWriteMethodAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.Parameter)] + public sealed class RazorWriteMethodParameterAttribute : Attribute { } + + /// + /// Prevents the Member Reordering feature from tossing members of the marked class. + /// + /// + /// The attribute must be mentioned in your member reordering patterns + /// + [AttributeUsage(AttributeTargets.All)] + public sealed class NoReorder : Attribute { } +} \ No newline at end of file diff --git a/Filtration/Properties/AssemblyInfo.cs b/Filtration/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..b96ae8e --- /dev/null +++ b/Filtration/Properties/AssemblyInfo.cs @@ -0,0 +1,57 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Filtration")] +[assembly: AssemblyDescription("A loot filter script manager for Path of Exile")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("XVar Industries Inc.")] +[assembly: AssemblyProduct("Filtration")] +[assembly: AssemblyCopyright("Copyright © Ben Wallis 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +//In order to begin building localizable applications, set +//CultureYouAreCodingWith in your .csproj file +//inside a . For example, if you are using US english +//in your source files, set the to en-US. Then uncomment +//the NeutralResourceLanguage attribute below. Update the "en-US" in +//the line below to match the UICulture setting in the project file. + +//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: InternalsVisibleTo("Filtration.Tests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file diff --git a/Filtration/Properties/Resources.Designer.cs b/Filtration/Properties/Resources.Designer.cs new file mode 100644 index 0000000..63336cf --- /dev/null +++ b/Filtration/Properties/Resources.Designer.cs @@ -0,0 +1,164 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Filtration.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Filtration.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound1 { + get { + return ResourceManager.GetStream("AlertSound1", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound2 { + get { + return ResourceManager.GetStream("AlertSound2", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound3 { + get { + return ResourceManager.GetStream("AlertSound3", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound4 { + get { + return ResourceManager.GetStream("AlertSound4", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound5 { + get { + return ResourceManager.GetStream("AlertSound5", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound6 { + get { + return ResourceManager.GetStream("AlertSound6", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound7 { + get { + return ResourceManager.GetStream("AlertSound7", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound8 { + get { + return ResourceManager.GetStream("AlertSound8", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.IO.UnmanagedMemoryStream similar to System.IO.MemoryStream. + /// + internal static System.IO.UnmanagedMemoryStream AlertSound9 { + get { + return ResourceManager.GetStream("AlertSound9", resourceCulture); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] Fontin_SmallCaps { + get { + object obj = ResourceManager.GetObject("Fontin_SmallCaps", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap groundtile { + get { + object obj = ResourceManager.GetObject("groundtile", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Filtration/Properties/Resources.resx b/Filtration/Properties/Resources.resx new file mode 100644 index 0000000..9308e95 --- /dev/null +++ b/Filtration/Properties/Resources.resx @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\AlertSound1.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\AlertSound2.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\AlertSound3.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\AlertSound4.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\AlertSound5.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\AlertSound6.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\AlertSound7.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\AlertSound8.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\AlertSound9.wav;System.IO.MemoryStream, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\Fontin-SmallCaps.ttf;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\groundtile.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/Filtration/Properties/Settings.Designer.cs b/Filtration/Properties/Settings.Designer.cs new file mode 100644 index 0000000..60cbe77 --- /dev/null +++ b/Filtration/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34209 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Filtration.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Filtration/Properties/Settings.settings b/Filtration/Properties/Settings.settings new file mode 100644 index 0000000..033d7a5 --- /dev/null +++ b/Filtration/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Filtration/Resources/AlertSound1.wav b/Filtration/Resources/AlertSound1.wav new file mode 100644 index 0000000..4ae9006 Binary files /dev/null and b/Filtration/Resources/AlertSound1.wav differ diff --git a/Filtration/Resources/AlertSound2.wav b/Filtration/Resources/AlertSound2.wav new file mode 100644 index 0000000..0bd42cd Binary files /dev/null and b/Filtration/Resources/AlertSound2.wav differ diff --git a/Filtration/Resources/AlertSound3.wav b/Filtration/Resources/AlertSound3.wav new file mode 100644 index 0000000..81997cf Binary files /dev/null and b/Filtration/Resources/AlertSound3.wav differ diff --git a/Filtration/Resources/AlertSound4.wav b/Filtration/Resources/AlertSound4.wav new file mode 100644 index 0000000..94eb71c Binary files /dev/null and b/Filtration/Resources/AlertSound4.wav differ diff --git a/Filtration/Resources/AlertSound5.wav b/Filtration/Resources/AlertSound5.wav new file mode 100644 index 0000000..5c393e8 Binary files /dev/null and b/Filtration/Resources/AlertSound5.wav differ diff --git a/Filtration/Resources/AlertSound6.wav b/Filtration/Resources/AlertSound6.wav new file mode 100644 index 0000000..1c24dbc Binary files /dev/null and b/Filtration/Resources/AlertSound6.wav differ diff --git a/Filtration/Resources/AlertSound7.wav b/Filtration/Resources/AlertSound7.wav new file mode 100644 index 0000000..296957e Binary files /dev/null and b/Filtration/Resources/AlertSound7.wav differ diff --git a/Filtration/Resources/AlertSound8.wav b/Filtration/Resources/AlertSound8.wav new file mode 100644 index 0000000..b106182 Binary files /dev/null and b/Filtration/Resources/AlertSound8.wav differ diff --git a/Filtration/Resources/AlertSound9.wav b/Filtration/Resources/AlertSound9.wav new file mode 100644 index 0000000..b1858e2 Binary files /dev/null and b/Filtration/Resources/AlertSound9.wav differ diff --git a/Filtration/Resources/Fontin-SmallCaps.ttf b/Filtration/Resources/Fontin-SmallCaps.ttf new file mode 100644 index 0000000..25efca5 Binary files /dev/null and b/Filtration/Resources/Fontin-SmallCaps.ttf differ diff --git a/Filtration/Resources/ItemBaseTypes.txt b/Filtration/Resources/ItemBaseTypes.txt new file mode 100644 index 0000000..f9e7323 --- /dev/null +++ b/Filtration/Resources/ItemBaseTypes.txt @@ -0,0 +1,1070 @@ +Anger +Animate Guardian +Animate Weapon +Arc +Arctic Armour +Arctic Breath +Assassin's Mark +Ball Lightning +Barrage +Bear Trap +Blink Arrow +Blood Rage +Bone Offering +Burning Arrow +Clarity +Cleave +Cold Snap +Conductivity +Conversion Trap +Convocation +Cyclone +Decoy Totem +Desecrate +Determination +Detonate Dead +Devouring Totem +Discharge +Discipline +Dominating Blow +Double Strike +Dual Strike +Elemental Hit +Elemental Weakness +Enduring Cry +Enfeeble +Ethereal Knives +Explosive Arrow +Fire Trap +Fireball +Firestorm +Flame Surge +Flame Totem +Flameblast +Flammability +Flesh Offering +Flicker Strike +Freeze Mine +Freezing Pulse +Frenzy +Frost Wall +Frostbite +Glacial Cascade +Glacial Hammer +Grace +Ground Slam +Haste +Hatred +Heavy Strike +Herald of Ash +Herald of Ice +Herald of Thunder +Ice Nova +Ice Shot +Ice Spear +Ignite +Immortal Call +Incinerate +Infernal Blow +Kinetic Blast +Leap Slam +Lightning Arrow +Lightning Strike +Lightning Tendrils +Lightning Trap +Lightning Warp +Mirror Arrow +Molten Shell +Molten Strike +Phase Run troll +Poacher's Mark +Poison Arrow +Portal +Power Siphon +Projectile Weakness +Puncture +Punishment +Purity of Elements +Purity of Fire +Purity of Ice +Purity of Lightning +Rain of Arrows +Raise Spectre +Raise Zombie +Reave +Reckoning +Rejuvenation Totem +Righteous Fire +Riposte +Searing Bond +Shield Charge +Shock Nova +Shockwave Totem +Smoke Mine +Spark +Spectral Throw +Split Arrow +Static Strike +Storm Call +Summon Raging Spirit +Summon Skeletons +Sweep +Tempest Shield +Temporal Chains +Tornado Shot +Touch of God +Vaal Arc +Vaal Burning Arrow +Vaal Clarity +Vaal Cold Snap +Vaal Cyclone +Vaal Detonate Dead +Vaal Discipline +Vaal Double Strike +Vaal Fireball +Vaal Flameblast +Vaal Glacial Hammer +Vaal Grace +Vaal Ground Slam +Vaal Haste +Vaal Heavy Strike +Vaal Ice Nova +Vaal Immortal Call +Vaal Lightning Strike +Vaal Lightning Warp +Vaal Molten Shell +Vaal Power Siphon +Vaal Rain of Arrows +Vaal Reave +Vaal Righteous Fire +Vaal Spark +Vaal Spectral Throw +Vaal Storm Call +Vaal Summon Skeletons +Vaal Sweep +Vengeance +Viper Strike +Vitality +Vulnerability +Warlord's Mark +Whirling Blades +Wrath +Agate Amulet +Amber Amulet +Citrine Amulet +Coral Amulet +Gold Amulet +Jade Amulet +Jet Amulet +Lapis Amulet +Onyx Amulet +Paua Amulet +Turquoise Amulet +Chain Belt +Cloth Belt +Golden Obi +Heavy Belt +Leather Belt +Rustic Sash +Studded Belt +Arena Plate +Assassin's Garb +Astral Plate +Battle Lamellar +Battle Plate +Blood Raiment +Bone Armour +Bronze Plate +Buckskin Tunic +Cabalist Regalia +Carnal Armour +Chain Hauberk +Chainmail Doublet +Chainmail Tunic +Chainmail Vest +Chestplate +Colosseum Plate +Commander's Brigandine +Conjurer's Vestment +Conquest Chainmail +Copper Plate +Coronal Leather +Crimson Raiment +Crusader Chainmail +Crusader Plate +Crypt Armour +Cutthroat's Garb +Desert Brigandine +Destiny Leather +Destroyer Regalia +Devout Chainmail +Dragonscale Doublet +Eelskin Tunic +Elegant Ringmail +Exquisite Leather +Field Lamellar +Frontier Leather +Full Chainmail +Full Dragonscale +Full Leather +Full Plate +Full Ringmail +Full Scale Armour +Full Wyrmscale +General's Brigandine +Gladiator Plate +Glorious Leather +Glorious Plate +Golden Plate +Holy Chainmail +Hussar Brigandine +Infantry Brigandine +Kaom's Plate +Lacquered Garb +Latticed Ringmail +Light Brigandine +Lordly Plate +Loricated Ringmail +Mage's Vestment +Majestic Plate +Necromancer Silks +Occultist's Vestment +Oiled Coat +Oiled Vest +Ornate Ringmail +Padded Jacket +Padded Vest +Plate Vest +Quilted Jacket +Ringmail Coat +Sacrificial Garb +Sadist Garb +Sage's Robe +Saintly Chainmail +Saint's Hauberk +Savant's Robe +Scale Doublet +Scale Vest +Scarlet Raiment +Scholar's Robe +Sentinel Jacket +Shabby Jerkin +Sharkskin Tunic +Silk Robe +Silken Garb +Silken Vest +Silken Wrap +Simple Robe +Sleek Coat +Soldier's Brigandine +Spidersilk Robe +Strapped Leather +Sun Leather +Sun Plate +Thief's Garb +Triumphant Lamellar +Vaal Regalia +Varnished Coat +War Plate +Waxed Garb +Widowsilk Robe +Wild Leather +Wyrmscale Doublet +Zodiac Leather +Ambush Boots +Ancient Greaves +Antique Greaves +Arcanist Slippers +Assassin's Boots +Avian Slippers +Bronzescale Boots +Carnal Boots +Chain Boots +Clasped Boots +Conjurer Boots +Crusader Boots +Deerskin Boots +Dragonscale Boots +Eelskin Boots +Goathide Boots +Golden Caligae +Goliath Greaves +Hydrascale Boots +Iron Greaves +Ironscale Boots +Kaom's Greaves +Leatherscale Boots +Legion Boots +Mesh Boots +Murder Boots +Nubuck Boots +Plated Greaves +Rawhide Boots +Reinforced Greaves +Ringmail Boots +Riveted Boots +Samite Slippers +Satin Slippers +Scholar Boots +Serpentscale Boots +Shackled Boots +Shagreen Boots +Sharkskin Boots +Silk Slippers +Slink Boots +Soldier Boots +Sorcerer Boots +Stealth Boots +Steel Greaves +Steelscale Boots +Strapped Boots +Titan Greaves +Trapper Boots +Vaal Greaves +Velvet Slippers +Wool Shoes +Wrapped Boots +Wyrmscale Boots +Zealot Boots +Bone Bow +Citadel Bow +Composite Bow +Compound Bow +Crude Bow +Death Bow +Decimation Bow +Decurve Bow +Grove Bow +Harbinger Bow +Highborn Bow +Imperial Bow +Ivory Bow +Long Bow +Maraketh Bow +Ranger Bow +Recurve Bow +Royal Bow +Short Bow +Sniper Bow +Spine Bow +Thicket Bow +Awl +Blinder +Cat's Paw +Eagle Claw +Eye Gouger +Fright Claw +Gouger +Great White Claw +Gut Ripper +Hellion's Paw +Imperial Claw +Nailed Fist +Noble Claw +Prehistoric Claw +Sharktooth Claw +Sparkling Claw +Terror Claw +Thresher Claw +Throat Stabber +Tiger's Paw +Timeworn Claw +Vaal Claw +Diamond Flask +Ambusher +Boot Blade +Boot Knife +Butcher Knife +Carving Knife +Copper Kris +Demon Dagger +Ezomyte Dagger +Fiend Dagger +Flaying Knife +Glass Shank +Golden Kris +Gutting Knife +Imp Dagger +Imperial Skean +Platinum Kris +Poignard +Royal Skean +Skean +Skinning Knife +Slaughter Knife +Stiletto +Fishing Rod +Ambush Mitts +Ancient Gauntlets +Antique Gauntlets +Arcanist Gloves +Assassin's Mitts +Bronze Gauntlets +Bronzescale Gauntlets +Carnal Mitts +Chain Gloves +Clasped Mitts +Conjurer Gloves +Crusader Gloves +Deerskin Gloves +Dragonscale Gauntlets +Eelskin Gloves +Embroidered Gloves +Fishscale Gauntlets +Goathide Gloves +Golden Bracers +Goliath Gauntlets +Hydrascale Gauntlets +Iron Gauntlets +Ironscale Gauntlets +Legion Gloves +Mesh Gloves +Murder Mitts +Nubuck Gloves +Plated Gauntlets +Rawhide Gloves +Ringmail Gloves +Riveted Gloves +Samite Gloves +Satin Gloves +Serpentscale Gauntlets +Shagreen Gloves +Sharkskin Gloves +Silk Gloves +Slink Gloves +Soldier Gloves +Sorcerer Gloves +Stealth Gloves +Steel Gauntlets +Steelscale Gauntlets +Strapped Mitts +Titan Gauntlets +Trapper Mitts +Vaal Gauntlets +Velvet Gloves +Wool Gloves +Wrapped Mitts +Wyrmscale Gauntlets +Zealot Gloves +Aventail Helmet +Barbute Helmet +Battered Helm +Bone Circlet +Callous Mask +Close Helmet +Cone Helmet +Crusader Helmet +Deicide Mask +Eternal Burgonet +Ezomyte Burgonet +Fencer Helm +Festival Mask +Fluted Bascinet +Gilded Sallet +Gladiator Helmet +Golden Mask +Golden Wreath +Great Crown +Great Helmet +Harlequin Mask +Hubris Circlet +Hunter Hood +Iron Circlet +Iron Hat +Iron Mask +Lacquered Helmet +Leather Cap +Leather Hood +Lion Pelt +Lunaris Circlet +Magistrate Crown +Mind Cage +Necromancer Circlet +Nightmare Bascinet +Noble Tricorne +Pig-Faced Bascinet +Plague Mask +Praetor Crown +Prophet Crown +Raven Mask +Reaver Helmet +Regicide Mask +Royal Burgonet +Rusted Coif +Sallet +Samite Helmet +Scare Mask +Secutor Helm +Siege Helmet +Silken Hood +Sinner Tricorne +Solaris Circlet +Soldier Helmet +Steel Circlet +Torture Cage +Tribal Circlet +Tricorne +Ursine Pelt +Vaal Mask +Vine Circlet +Visored Sallet +Wolf Pelt +Zealot Helmet +Colossal Hybrid Flask +Hallowed Hybrid Flask +Large Hybrid Flask +Medium Hybrid Flask +Sacred Hybrid Flask +Small Hybrid Flask +Colossal Life Flask +Divine Life Flask +Eternal Life Flask +Giant Life Flask +Grand Life Flask +Greater Life Flask +Hallowed Life Flask +Large Life Flask +Medium Life Flask +Sacred Life Flask +Sanctified Life Flask +Small Life Flask +Colossal Mana Flask +Divine Mana Flask +Eternal Mana Flask +Giant Mana Flask +Grand Mana Flask +Greater Mana Flask +Hallowed Mana Flask +Large Mana Flask +Medium Mana Flask +Sacred Mana Flask +Sanctified Mana Flask +Small Mana Flask +Mortal Grief +Mortal Hope +Mortal Ignorance +Mortal Rage +Sacrifice at Dawn +Sacrifice at Dusk +Sacrifice at Midnight +Sacrifice at Noon +Academy Map +Arachnid Nest Map +Arcade Map +Arsenal Map +Bazaar Map +Bog Map +Canyon Map +Catacomb Map +Cells Map +Cemetery Map +Colonnade Map +Courtyard Map +Coves Map +Crematorium Map +Crypt Map +Dark Forest Map +Dried Lake Map +Dry Peninsula Map +Dry Woods Map +Dunes Map +Dungeon Map +Ghetto Map +Gorge Map +Graveyard Map +Grotto Map +Jungle Valley Map +Labyrinth Map +Maze Map +Mine Map +Mountain Ledge Map +Mud Geyser Map +Museum Map +Necropolis Map +Orchard Map +Overgrown Ruin Map +Overgrown Shrine Map +Palace Map +Pier Map +Plateau Map +Precinct Map +Promenade Map +Reef Map +Residence Map +Sewer Map +Shipyard Map +Shore Map +Shrine Map +Spider Forest Map +Spider Lair Map +Springs Map +Strand Map +Temple Map +Thicket Map +Torture Chamber Map +Tropical Island Map +Tunnel Map +Underground River Map +Underground Sea Map +Vaal Pyramid Map +Vaal Temple Map +Villa Map +Waste Pool Map +Wharf Map +Arming Axe +Boarding Axe +Broad Axe +Butcher Axe +Ceremonial Axe +Chest Splitter +Cleaver +Decorative Axe +Infernal Axe +Jade Hatchet +Jasper Axe +Karui Axe +Reaver Axe +Royal Axe +Rusted Hatchet +Siege Axe +Spectral Axe +Tomahawk +Vaal Hatchet +War Axe +Wraith Axe +Wrist Chopper +Ancestral Club +Auric Mace +Barbed Club +Battle Hammer +Bladed Mace +Ceremonial Mace +Dream Mace +Driftwood Club +Flanged Mace +Gavel +Legion Hammer +Nightmare Mace +Ornate Mace +Pernarch +Petrified Club +Phantom Mace +Rock Breaker +Spiked Club +Stone Hammer +Tenderizer +Tribal Club +War Hammer +Ancient Sword +Baselard +Battle Sword +Broad Sword +Copper Sword +Corsair Sword +Cutlass +Dusk Blade +Elder Sword +Elegant Sword +Eternal Sword +Gemstone Sword +Gladius +Graceful Sword +Legion Sword +Midnight Blade +Rusted Sword +Sabre +Twilight Blade +Vaal Blade +Variscite Blade +War Sword +Alira's Amulet +Allflame +Ammonite Glyph +Baleful Gem +Book of Reform +Book of Regrets +Book of Skill +Bust of Gaius Sentari +Bust of Hector Titucius +Bust of Marceus Lioneye +Chitus' Plum +Decanter Spiritus +Golden Hand +Golden Page +Haliotis Glyph +Infernal Talc +Kraityn's Amulet +Maligaro's Spike +Medicine Chest +Oak's Amulet +Ribbon Spool +Roseus Glyph +Sewer Keys +Thaumetic Emblem +Thaumetic Sulphite +The Apex +Tolman's Bracelet +Tower Key +Blunt Arrow Quiver +Broadhead Arrow Quiver +Conductive Quiver +Cured Quiver +Fire Arrow Quiver +Heavy Quiver +Light Quiver +Penetrating Arrow Quiver +Rugged Quiver +Serrated Arrow Quiver +Sharktooth Arrow Quiver +Spike-Point Arrow Quiver +Two-Point Arrow Quiver +Amethyst Ring +Coral Ring +Diamond Ring +Gold Ring +Golden Hoop +Iron Ring +Moonstone Ring +Paua Ring +Prismatic Ring +Ruby Ring +Sapphire Ring +Topaz Ring +Two-Stone Ring +Unset Ring +Abyssal Sceptre +Blood Sceptre +Bronze Sceptre +Carnal Sceptre +Crystal Sceptre +Darkwood Sceptre +Driftwood Sceptre +Grinning Fetish +Iron Sceptre +Karui Sceptre +Lead Sceptre +Ochre Sceptre +Opal Sceptre +Platinum Sceptre +Quartz Sceptre +Ritual Sceptre +Royal Sceptre +Sekhem +Shadow Sceptre +Tyrant's Sekhem +Vaal Sceptre +Void Sceptre +Alder Spike Shield +Alloyed Spike Shield +Ancient Spirit Shield +Angelic Kite Shield +Archon Kite Shield +Baroque Round Shield +Battle Buckler +Bone Spirit Shield +Branded Kite Shield +Brass Spirit Shield +Bronze Tower Shield +Buckskin Tower Shield +Burnished Spike Shield +Cardinal Round Shield +Cedar Tower Shield +Ceremonial Kite Shield +Champion Kite Shield +Chiming Spirit Shield +Colossal Tower Shield +Compound Spiked Shield +Copper Tower Shield +Corroded Tower Shield +Corrugated Buckler +Crested Tower Shield +Crimson Round Shield +Crusader Buckler +Driftwood Spiked Shield +Ebony Tower Shield +Elegant Round Shield +Enameled Buckler +Etched Kite Shield +Ezomyte Spiked Shield +Ezomyte Tower Shield +Fir Round Shield +Fossilized Spirit Shield +Gilded Buckler +Girded Tower Shield +Goathide Buckler +Golden Buckler +Hammered Buckler +Harmonic Spirit Shield +Imperial Buckler +Ironwood Buckler +Ivory Spirit Shield +Jingling Spirit Shield +Lacewood Spirit Shield +Lacquered Buckler +Laminated Kite Shield +Layered Kite Shield +Linden Kite Shield +Mahogany Tower Shield +Maple Round Shield +Mirrored Spiked Shield +Mosaic Kite Shield +Oak Buckler +Ornate Spiked Shield +Painted Buckler +Painted Tower Shield +Pine Buckler +Pinnacle Tower Shield +Plank Kite Shield +Polished Spiked Shield +Rawhide Tower Shield +Redwood Spiked Shield +Reinforced Kite Shield +Reinforced Tower Shield +Rotted Round Shield +Scarlet Round Shield +Shagreen Tower Shield +Sovereign Spiked Shield +Spiked Bundle +Spiked Round Shield +Spiny Round Shield +Splendid Round Shield +Splintered Tower Shield +Steel Kite Shield +Studded Round Shield +Supreme Spiked Shield +Tarnished Spirit Shield +Teak Round Shield +Thorium Spirit Shield +Titanium Spirit Shield +Twig Spirit Shield +Vaal Buckler +Vaal Spirit Shield +Walnut Spirit Shield +War Buckler +Yew Spirit Shield +Albino Rhoa Feather +Alchemy Shard +Alteration Shard +Armourer's Scrap +Blacksmith's Whetstone +Blessed Orb +Cartographer's Chisel +Chaos Orb +Chromatic Orb +Divine Orb +Eternal Orb +Exalted Orb +Gemcutter's Prism +Glassblower's Bauble +Imprint +Jeweller's Orb +Mirror of Kalandra +Orb of Alchemy +Orb of Alteration +Orb of Augmentation +Orb of Chance +Orb of Fusing +Orb of Regret +Orb of Scouring +Orb of Transmutation +Portal Scroll +Regal Orb +Scroll Fragment +Scroll of Wisdom +Transmutation Shard +Vaal Orb +Coiled Staff +Ezomyte Staff +Foul Staff +Gnarled Branch +Highborn Staff +Imperial Staff +Iron Staff +Judgement Staff +Lathi +Long Staff +Maelström Staff +Military Staff +Primitive Staff +Primordial Staff +Quarterstaff +Royal Staff +Serpentine Staff +Vile Staff +Woodful Staff +Added Chaos Damage +Added Cold Damage +Added Fire Damage +Added Lightning Damage +Additional Accuracy +Blind +Block Chance Reduction +Blood Magic +Cast On Critical Strike +Cast on Death +Cast on Melee Kill +Cast when Damage Taken +Cast when Stunned +Chain +Chance to Flee +Chance to Ignite +Cold Penetration +Cold to Fire +Concentrated Effect +Culling Strike +Curse On Hit +Elemental Proliferation +Empower +Endurance Charge on Melee Stun +Enhance +Enlighten +Faster Attacks +Faster Casting +Faster Projectiles +Fire Penetration +Fork +Generosity +Greater Multiple Projectiles +Increased Area of Effect +Increased Burning Damage +Increased Critical Damage +Increased Critical Strikes +Increased Duration +Iron Grip +Iron Will +Item Quantity +Item Rarity +Knockback +Lesser Multiple Projectiles +Life Gain on Hit +Life Leech +Lightning Penetration +Mana Leech +Melee Damage on Full Life +Melee Physical Damage +Melee Splash +Minion and Totem Elemental Resistance +Minion Damage +Minion Life +Minion Speed +Multiple Traps +Multistrike +Physical Projectile Attack Damage +Physical to Lightning +Pierce +Point Blank +Power Charge On Critical +Ranged Attack Totem +Reduced Duration +Reduced Mana +Remote Mine +Slower Projectiles +Spell Echo +Spell Totem +Stun +Trap +Weapon Elemental Damage +Antique Rapier +Apex Rapier +Basket Rapier +Battered Foil +Burnished Foil +Dragonbone Rapier +Elegant Foil +Estoc +Fancy Foil +Harpy Rapier +Jagged Foil +Jewelled Foil +Pecoraro +Primeval Rapier +Rusted Spike +Serrated Foil +Spiraled Foil +Tempered Foil +Thorn Rapier +Vaal Rapier +Whalebone Rapier +Wyrmbone Rapier +Abyssal Axe +Despot Axe +Double Axe +Ezomyte Axe +Gilded Axe +Headsman Axe +Jade Chopper +Jasper Chopper +Karui Chopper +Labrys +Noble Axe +Poleaxe +Shadow Axe +Stone Axe +Sundering Axe +Timber Axe +Vaal Axe +Void Axe +Woodsplitter +Brass Maul +Colossus Mallet +Dread Maul +Driftwood Maul +Fright Maul +Great Mallet +Imperial Maul +Karui Maul +Mallet +Meatgrinder +Piledriver +Plated Maul +Sledgehammer +Spiked Maul +Spiny Maul +Steelhead +Terror Maul +Totemic Maul +Tribal Maul +Bastard Sword +Butcher Sword +Corroded Blade +Engraved Greatsword +Etched Greatsword +Ezomyte Blade +Footman Sword +Headman's Sword +Highland Blade +Infernal Sword +Keyblade troll +Lion Sword +Longsword +Ornate Sword +Reaver Sword +Spectral Sword +Tiger Sword +Two-Handed Sword +Vaal Greatsword +Wraith Sword +Amethyst Flask +Granite Flask +Jade Flask +Quartz Flask +Quicksilver Flask +Ruby Flask +Sapphire Flask +Topaz Flask +Carved Wand +Crystal Wand +Demon's Horn +Driftwood Wand +Engraved Wand +Faun's Horn +Goat's Horn +Imbued Wand +Omen Wand +Opal Wand +Prophecy Wand +Quartz Wand +Sage Wand +Serpent Wand +Spiraled Wand +Tornado Wand \ No newline at end of file diff --git a/Filtration/Resources/ItemClasses.txt b/Filtration/Resources/ItemClasses.txt new file mode 100644 index 0000000..ec52b69 --- /dev/null +++ b/Filtration/Resources/ItemClasses.txt @@ -0,0 +1,36 @@ +Life Flasks +Mana Flasks +Hybrid Flasks +Currency +Amulets +Rings +Claws +Daggers +Wands +One Hand Swords +Thrusting One Hand Swords +One Hand Axes +One Hand Maces +Bows +Staves +Two Hand Swords +Two Hand Axes +Two Hand Maces +Active Skill Gems +Support Skill Gems +Quivers +Belts +Gloves +Boots +Body Armours +Helmets +Shields +Stackable Currency +Quest Items +Sceptres +Utility Flasks +Maps +Fishing Rods +Map Fragments +Hideout Doodads +Microtransactions \ No newline at end of file diff --git a/Filtration/Resources/groundtile.png b/Filtration/Resources/groundtile.png new file mode 100644 index 0000000..01880e4 Binary files /dev/null and b/Filtration/Resources/groundtile.png differ diff --git a/Filtration/Services/FileSystemService.cs b/Filtration/Services/FileSystemService.cs new file mode 100644 index 0000000..31ba58a --- /dev/null +++ b/Filtration/Services/FileSystemService.cs @@ -0,0 +1,36 @@ +using System; +using System.IO; + +namespace Filtration.Services +{ + internal interface IFileSystemService + { + string ReadFileAsString(string filePath); + void WriteFileFromString(string filePath, string inputString); + bool DirectoryExists(string directoryPath); + string GetUserProfilePath(); + } + + internal class FileSystemService : IFileSystemService + { + public string ReadFileAsString(string filePath) + { + return File.ReadAllText(filePath); + } + + public void WriteFileFromString(string filePath, string inputString) + { + File.WriteAllText(filePath, inputString); + } + + public bool DirectoryExists(string directoryPath) + { + return Directory.Exists(directoryPath); + } + + public string GetUserProfilePath() + { + return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + } + } +} diff --git a/Filtration/Services/LootFilterPersistenceService.cs b/Filtration/Services/LootFilterPersistenceService.cs new file mode 100644 index 0000000..254df27 --- /dev/null +++ b/Filtration/Services/LootFilterPersistenceService.cs @@ -0,0 +1,51 @@ +using Filtration.Models; +using Filtration.Translators; + +namespace Filtration.Services +{ + internal interface ILootFilterPersistenceService + { + string LootFilterScriptDirectory { get; set; } + LootFilterScript LoadLootFilterScript(string filePath); + void SaveLootFilterScript(LootFilterScript script); + string DefaultPathOfExileDirectory(); + } + + internal class LootFilterPersistenceService : ILootFilterPersistenceService + { + private readonly IFileSystemService _fileSystemService; + private readonly ILootFilterScriptTranslator _lootFilterScriptTranslator; + + public LootFilterPersistenceService(IFileSystemService fileSystemService, ILootFilterScriptTranslator lootFilterScriptTranslator) + { + _fileSystemService = fileSystemService; + _lootFilterScriptTranslator = lootFilterScriptTranslator; + } + + public string LootFilterScriptDirectory { get; set; } + + public LootFilterScript LoadLootFilterScript(string filePath) + { + var script = + _lootFilterScriptTranslator.TranslateStringToLootFilterScript( + _fileSystemService.ReadFileAsString(filePath)); + + script.FilePath = filePath; + return script; + } + + public void SaveLootFilterScript(LootFilterScript script) + { + _fileSystemService.WriteFileFromString(script.FilePath, + _lootFilterScriptTranslator.TranslateLootFilterScriptToString(script)); + } + + public string DefaultPathOfExileDirectory() + { + var defaultDir = _fileSystemService.GetUserProfilePath() + "\\Documents\\My Games\\Path of Exile"; + var defaultDirExists = _fileSystemService.DirectoryExists(defaultDir); + + return defaultDirExists ? defaultDir : string.Empty; + } + } +} diff --git a/Filtration/Services/StaticDataService.cs b/Filtration/Services/StaticDataService.cs new file mode 100644 index 0000000..a6bc954 --- /dev/null +++ b/Filtration/Services/StaticDataService.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Filtration.Utilities; + +namespace Filtration.Services +{ + public interface IStaticDataService + { + IEnumerable ItemBaseTypes { get; } + IEnumerable ItemClasses { get; } + } + + internal class StaticDataService : IStaticDataService + { + private readonly IFileSystemService _fileSystemService; + + public StaticDataService(IFileSystemService fileSystemService) + { + _fileSystemService = fileSystemService; + PopulateStaticData(); + } + + public IEnumerable ItemBaseTypes { get; private set; } + + public IEnumerable ItemClasses { get; private set; } + + private void PopulateStaticData() + { + var itemBaseTypes = _fileSystemService.ReadFileAsString("Resources\\ItemBaseTypes.txt"); + ItemBaseTypes = new LineReader(() => new StringReader(itemBaseTypes)).ToList(); + + var itemClasses = _fileSystemService.ReadFileAsString("Resources\\ItemClasses.txt"); + ItemClasses = new LineReader(() => new StringReader(itemClasses)).ToList(); + } + } +} diff --git a/Filtration/Translators/LootFilterBlockTranslator.cs b/Filtration/Translators/LootFilterBlockTranslator.cs new file mode 100644 index 0000000..377ff56 --- /dev/null +++ b/Filtration/Translators/LootFilterBlockTranslator.cs @@ -0,0 +1,436 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Extensions; +using Filtration.Models; +using Filtration.Models.BlockItemBaseTypes; +using Filtration.Models.BlockItemTypes; +using Filtration.Utilities; + +namespace Filtration.Translators +{ + internal interface ILootFilterBlockTranslator + { + LootFilterBlock TranslateStringToLootFilterBlock(string inputString); + string TranslateLootFilterBlockToString(LootFilterBlock block); + } + + internal class LootFilterBlockTranslator : ILootFilterBlockTranslator + { + private const string Indent = " "; + private readonly string _newLine = Environment.NewLine + Indent; + + // This method converts a string into a LootFilterBlock. This is used for pasting LootFilterBlocks + // and reading LootFilterScripts from a file. + public LootFilterBlock TranslateStringToLootFilterBlock(string inputString) + { + var block = new LootFilterBlock(); + var showHideFound = false; + foreach (var line in new LineReader(() => new StringReader(inputString))) + { + if (line.StartsWith(@"# Section:")) + { + var section = new LootFilterSection + { + Description = line.Substring(line.IndexOf(":", StringComparison.Ordinal) + 1).Trim() + }; + return section; + } + + if (line.StartsWith(@"#") && !showHideFound) + { + block.Description = line.TrimStart('#').TrimStart(' '); + continue; + } + + var trimmedLine = line.TrimStart(' '); + var spaceOrEndOfLinePos = trimmedLine.IndexOf(" ", StringComparison.Ordinal) > 0 ? trimmedLine.IndexOf(" ", StringComparison.Ordinal) : trimmedLine.Length; + + var lineOption = trimmedLine.Substring(0, spaceOrEndOfLinePos); + switch (lineOption) + { + case "Show": + showHideFound = true; + block.Action = BlockAction.Show; + break; + case "Hide": + showHideFound = true; + block.Action = BlockAction.Hide; + break; + case "ItemLevel": + { + AddNumericFilterPredicateItemToBlockItems(block, trimmedLine); + break; + } + case "DropLevel": + { + AddNumericFilterPredicateItemToBlockItems(block, trimmedLine); + break; + } + case "Quality": + { + AddNumericFilterPredicateItemToBlockItems(block,trimmedLine); + break; + } + case "Rarity": + { + var blockItemValue = new RarityBlockItem(); + var result = Regex.Match(trimmedLine, @"^\w+\s+([>(string.IsNullOrEmpty(result.Groups[1].Value) ? "=" : result.Groups[1].Value); + blockItemValue.FilterPredicate.PredicateOperand = + (int)(EnumHelper.GetEnumValueFromDescription(result.Groups[2].Value)); + } + block.BlockItems.Add(blockItemValue); + break; + } + case "Class": + { + AddStringListItemToBlockItems(block, trimmedLine); + break; + } + case "BaseType": + { + AddStringListItemToBlockItems(block, trimmedLine); + break; + } + case "Sockets": + { + AddNumericFilterPredicateItemToBlockItems(block, trimmedLine); + break; + } + case "LinkedSockets": + { + AddNumericFilterPredicateItemToBlockItems(block,trimmedLine); + break; + } + case "Width": + { + AddNumericFilterPredicateItemToBlockItems(block, trimmedLine); + break; + } + case "Height": + { + AddNumericFilterPredicateItemToBlockItems(block, trimmedLine); + break; + } + case "SocketGroup": + { + //var blockItem = new SocketGroupBlockItem(); + + //var socketGroups = Regex.Matches(trimmedLine, @"\s+([RGBW]{1,6})"); + + //foreach (Match socketGroupMatch in socketGroups) + //{ + + // var socketGroupCharArray = socketGroupMatch.Groups[1].Value.Trim(' ').ToCharArray(); + // var socketColorList = socketGroupCharArray.Select(c => (EnumHelper.GetEnumValueFromDescription(c.ToString()))).ToList(); + + // blockItem.SocketColorGroups.Add(socketColorList); + //} + + //block.FilterBlockItems.Add(blockItem); + AddStringListItemToBlockItems(block, trimmedLine); + break; + } + case "SetTextColor": + { + // Only ever use the last SetTextColor item encountered as multiples aren't valid. + RemoveExistingBlockItemsOfType(block); + + AddColorItemToBlockItems(block, trimmedLine); + break; + } + case "SetBackgroundColor": + { + // Only ever use the last SetBackgroundColor item encountered as multiples aren't valid. + RemoveExistingBlockItemsOfType(block); + + AddColorItemToBlockItems(block, trimmedLine); + break; + } + case "SetBorderColor": + { + // Only ever use the last SetBorderColor item encountered as multiples aren't valid. + RemoveExistingBlockItemsOfType(block); + + AddColorItemToBlockItems(block, trimmedLine); + break; + } + case "SetFontSize": + { + // Only ever use the last SetFontSize item encountered as multiples aren't valid. + RemoveExistingBlockItemsOfType(block); + + var match = Regex.Match(trimmedLine, @"\s+(\d+)"); + if (match.Success) + { + var blockItemValue = new FontSizeBlockItem(Convert.ToInt16(match.Value)); + block.BlockItems.Add(blockItemValue); + } + break; + } + case "PlayAlertSound": + { + // Only ever use the last PlayAlertSound item encountered as multiples aren't valid. + RemoveExistingBlockItemsOfType(block); + + var matches = Regex.Matches(trimmedLine, @"\s+(\d+)"); + switch (matches.Count) + { + case 1: + if (matches[0].Success) + { + var blockItemValue = new SoundBlockItem + { + Value = Convert.ToInt16(matches[0].Value), + SecondValue = 79 + }; + block.BlockItems.Add(blockItemValue); + } + break; + case 2: + if (matches[0].Success && matches[1].Success) + { + var blockItemValue = new SoundBlockItem + { + Value = Convert.ToInt16(matches[0].Value), + SecondValue = Convert.ToInt16(matches[1].Value) + }; + block.BlockItems.Add(blockItemValue); + } + break; + } + break; + } + } + } + + return block; + } + + private static void RemoveExistingBlockItemsOfType(LootFilterBlock block) + { + var existingBlockItemCount = block.BlockItems.Count(b => b.GetType() == typeof(T)); + if (existingBlockItemCount > 0) + { + var existingBlockItem = block.BlockItems.First(b => b.GetType() == typeof(T)); + block.BlockItems.Remove(existingBlockItem); + } + } + + private static void AddNumericFilterPredicateItemToBlockItems(LootFilterBlock block, string inputString) where T : NumericFilterPredicateBlockItem + { + var blockItem = Activator.CreateInstance(); + + SetNumericFilterPredicateFromString(blockItem.FilterPredicate, inputString); + block.BlockItems.Add(blockItem); + } + + private static void SetNumericFilterPredicateFromString(NumericFilterPredicate predicate, string inputString) + { + var result = Regex.Match(inputString, @"^\w+\s+([>(string.IsNullOrEmpty(result.Groups[1].Value) ? "=" : result.Groups[1].Value); + predicate.PredicateOperand = Convert.ToInt16(result.Groups[2].Value); + } + + private static void AddStringListItemToBlockItems(LootFilterBlock block, string inputString) where T : StringListBlockItem + { + var blockItem = Activator.CreateInstance(); + PopulateListFromString(blockItem.Items, inputString.Substring(inputString.IndexOf(" ", StringComparison.Ordinal) + 1).Trim()); + block.BlockItems.Add(blockItem); + } + + private static void PopulateListFromString(ICollection list, string inputString) + { + var result = Regex.Matches(inputString, @"[^\s""]+|""([^""]*)"""); + foreach (Match match in result) + { + list.Add(match.Groups[1].Success + ? match.Groups[1].Value + : match.Groups[0].Value); + } + } + + private static void AddColorItemToBlockItems(LootFilterBlock block, string inputString) where T : ColorBlockItem + { + var blockItem = Activator.CreateInstance(); + blockItem.Color = GetColorFromString(inputString); + block.BlockItems.Add(blockItem); + } + + private static Color GetColorFromString(string inputString) + { + var argbValues = Regex.Matches(inputString, @"\s+(\d+)"); + + switch (argbValues.Count) + { + case 3: + return new Color + { + A = byte.MaxValue, + R = Convert.ToByte(argbValues[0].Value), + G = Convert.ToByte(argbValues[1].Value), + B = Convert.ToByte(argbValues[2].Value) + }; + case 4: + return new Color + { + R = Convert.ToByte(argbValues[0].Value), + G = Convert.ToByte(argbValues[1].Value), + B = Convert.ToByte(argbValues[2].Value), + A = Convert.ToByte(argbValues[3].Value) + }; + } + return new Color(); + } + + // This method converts a LootFilterBlock object into a string. This is used for copying a LootFilterBlock + // to the clipboard, and when saving a LootFilterScript. + public string TranslateLootFilterBlockToString(LootFilterBlock block) + { + if (block.GetType() == typeof (LootFilterSection)) + { + return "# Section: " + block.Description; + } + + var outputString = string.Empty; + + if (!string.IsNullOrEmpty(block.Description)) + { + outputString += "# " + block.Description + Environment.NewLine; + } + + outputString += block.Action.GetAttributeDescription(); + + // This could be refactored to use the upcasted NumericFilterPredicateBlockItem (or even ILootFilterBlockItem) instead + // of the specific downcasts. Leaving it like this currently to preserve sorting since the different + // downcasts have no defined sort order (yet). + foreach (var blockItem in block.BlockItems.OfType()) + { + AddNumericFilterPredicateBlockItemToString(ref outputString, blockItem); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddNumericFilterPredicateBlockItemToString(ref outputString, blockItem); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddNumericFilterPredicateBlockItemToString(ref outputString, blockItem); + } + + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var blockItem in block.BlockItems.OfType()) + { + outputString += _newLine + "Rarity " + + blockItem.FilterPredicate.PredicateOperator + .GetAttributeDescription() + + " " + + ((ItemRarity) blockItem.FilterPredicate.PredicateOperand) + .GetAttributeDescription(); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddStringListBlockItemToString(ref outputString, blockItem); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddStringListBlockItemToString(ref outputString, blockItem); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddNumericFilterPredicateBlockItemToString(ref outputString, blockItem); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddNumericFilterPredicateBlockItemToString(ref outputString, blockItem); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddNumericFilterPredicateBlockItemToString(ref outputString, blockItem); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddNumericFilterPredicateBlockItemToString(ref outputString, blockItem); + } + + foreach (var blockItem in block.BlockItems.OfType()) + { + AddStringListBlockItemToString(ref outputString, blockItem); + } + + if (block.BlockItems.Count(b => b is TextColorBlockItem) > 0) + { + // Only add the first TextColorBlockItem type (not that we should ever have more than one). + AddColorBlockItemToString(ref outputString, block.BlockItems.OfType().First()); + } + + if (block.BlockItems.Count(b => b.GetType() == typeof(BackgroundColorBlockItem)) > 0) + { + // Only add the first BackgroundColorBlockItem type (not that we should ever have more than one). + AddColorBlockItemToString(ref outputString, block.BlockItems.OfType().First()); + } + + if (block.BlockItems.Count(b => b.GetType() == typeof(BorderColorBlockItem)) > 0) + { + // Only add the first BorderColorBlockItem (not that we should ever have more than one). + AddColorBlockItemToString(ref outputString, block.BlockItems.OfType().First()); + } + + if (block.BlockItems.Count(b => b.GetType() == typeof(FontSizeBlockItem)) > 0) + { + outputString += _newLine + "SetFontSize " + + block.BlockItems.OfType().First().Value; + } + + if (block.BlockItems.Count(b => b.GetType() == typeof(SoundBlockItem)) > 0) + { + var blockItemValue = block.BlockItems.OfType().First(); + outputString += _newLine + "PlayAlertSound " + blockItemValue.Value + " " + blockItemValue.SecondValue; + } + + return outputString; + } + + private void AddNumericFilterPredicateBlockItemToString(ref string targetString, NumericFilterPredicateBlockItem blockItem) + { + targetString += _newLine + blockItem.PrefixText + " " + + blockItem.FilterPredicate.PredicateOperator.GetAttributeDescription() + + " " + blockItem.FilterPredicate.PredicateOperand; + } + + private void AddStringListBlockItemToString(ref string targetString, StringListBlockItem blockItem) + { + var enumerable = blockItem.Items as IList ?? blockItem.Items.ToList(); + if (enumerable.Count > 0) + { + targetString += _newLine + blockItem.PrefixText + " " + + string.Format("\"{0}\"", + string.Join("\" \"", enumerable.ToArray())); + } + } + + private void AddColorBlockItemToString(ref string targetString, ColorBlockItem blockItem) + { + targetString += _newLine + blockItem.PrefixText + " " + blockItem.Color.R + " " + blockItem.Color.G + " " + + blockItem.Color.B + (blockItem.Color.A < 255 ? " " + blockItem.Color.A : string.Empty); + } + } +} diff --git a/Filtration/Translators/LootFilterScriptTranslator.cs b/Filtration/Translators/LootFilterScriptTranslator.cs new file mode 100644 index 0000000..fe03e91 --- /dev/null +++ b/Filtration/Translators/LootFilterScriptTranslator.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Text.RegularExpressions; +using Filtration.Models; +using Filtration.Utilities; + +namespace Filtration.Translators +{ + internal interface ILootFilterScriptTranslator + { + LootFilterScript TranslateStringToLootFilterScript(string inputString); + string TranslateLootFilterScriptToString(LootFilterScript script); + } + + internal class LootFilterScriptTranslator : ILootFilterScriptTranslator + { + private readonly ILootFilterBlockTranslator _blockTranslator; + + public LootFilterScriptTranslator(ILootFilterBlockTranslator blockTranslator) + { + _blockTranslator = blockTranslator; + } + + public LootFilterScript TranslateStringToLootFilterScript(string inputString) + { + var script = new LootFilterScript(); + inputString = inputString.Replace("\t", ""); + var conditionBoundaries = IdentifyBlockBoundaries(inputString); + + var lines = Regex.Split(inputString, "\r\n|\r|\n"); + + // Process the script header + for (var i = 0; i < conditionBoundaries.First.Value; i++) + { + if (lines[i].StartsWith("#")) + { + script.Description += lines[i].Substring(1).Trim(' ') + Environment.NewLine; + } + } + script.Description = script.Description.TrimEnd('\n').TrimEnd('\r'); + + // Extract each block from between boundaries and translate it into a LootFilterBlock object + // and add that object to the LootFilterBlocks list + for (var boundary = conditionBoundaries.First; boundary != null; boundary = boundary.Next) + { + var begin = boundary.Value; + var end = boundary.Next != null ? boundary.Next.Value : lines.Length; + var block = new string[end - begin]; + Array.Copy(lines, begin, block, 0, end - begin); + var blockString = string.Join("\r\n", block); + script.LootFilterBlocks.Add(_blockTranslator.TranslateStringToLootFilterBlock(blockString)); + } + + return script; + } + + private static LinkedList IdentifyBlockBoundaries(string inputString) + { + var blockBoundaries = new LinkedList(); + var previousLine = string.Empty; + var currentLine = 0; + + foreach (var line in new LineReader(() => new StringReader(inputString))) + { + currentLine++; + var trimmedLine = line.TrimStart(' ').TrimEnd(' '); + if (trimmedLine.StartsWith("Show") || trimmedLine.StartsWith("Hide") || + trimmedLine.StartsWith("# Section:")) + { + // If the line previous to the Show or Hide line is a comment then we should include that in the block + // as it represents the block description. + blockBoundaries.AddLast(previousLine.StartsWith("#") && !previousLine.StartsWith("# Section:") ? currentLine - 2 : currentLine - 1); + } + previousLine = line; + } + + return blockBoundaries; + } + + public string TranslateLootFilterScriptToString(LootFilterScript script) + { + var outputString = string.Empty; + + if (!string.IsNullOrEmpty(script.Description)) + { + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var line in new LineReader(() => new StringReader(script.Description))) + { + outputString += "# " + line + Environment.NewLine; + } + outputString += Environment.NewLine; + } + + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (var block in script.LootFilterBlocks) + { + outputString += _blockTranslator.TranslateLootFilterBlockToString(block) + Environment.NewLine + Environment.NewLine; + } + + return outputString; + } + } +} diff --git a/Filtration/UserControls/AutoScrollingListBox.cs b/Filtration/UserControls/AutoScrollingListBox.cs new file mode 100644 index 0000000..1864a50 --- /dev/null +++ b/Filtration/UserControls/AutoScrollingListBox.cs @@ -0,0 +1,20 @@ +using System.Collections.Specialized; +using System.Windows.Controls; + +namespace Filtration.UserControls +{ + public class AutoScrollingListBox : ListBox + { + protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e) + { + if (e.NewItems == null) return; + + var newItemCount = e.NewItems.Count; + + if (newItemCount > 0) + ScrollIntoView(e.NewItems[newItemCount - 1]); + + base.OnItemsChanged(e); + } + } +} diff --git a/Filtration/UserControls/CrossButton.cs b/Filtration/UserControls/CrossButton.cs new file mode 100644 index 0000000..255d4b7 --- /dev/null +++ b/Filtration/UserControls/CrossButton.cs @@ -0,0 +1,15 @@ +using System.Windows; +using System.Windows.Controls; + +namespace Filtration.UserControls +{ + public class CrossButton : Button + { + static CrossButton() + { + // Set the style key, so that our control template is used. + DefaultStyleKeyProperty.OverrideMetadata(typeof(CrossButton), + new FrameworkPropertyMetadata(typeof(CrossButton))); + } + } +} diff --git a/Filtration/UserControls/EditableListBoxControl.xaml b/Filtration/UserControls/EditableListBoxControl.xaml new file mode 100644 index 0000000..c2accdc --- /dev/null +++ b/Filtration/UserControls/EditableListBoxControl.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Filtration/UserControls/EditableListBoxControl.xaml.cs b/Filtration/UserControls/EditableListBoxControl.xaml.cs new file mode 100644 index 0000000..2754d72 --- /dev/null +++ b/Filtration/UserControls/EditableListBoxControl.xaml.cs @@ -0,0 +1,111 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows; +using Filtration.Annotations; +using GalaSoft.MvvmLight.CommandWpf; + +namespace Filtration.UserControls +{ + public partial class EditableListBoxControl : INotifyPropertyChanged + { + private string _addItemText; + + public EditableListBoxControl() + { + InitializeComponent(); + + // ReSharper disable once PossibleNullReferenceException + (Content as FrameworkElement).DataContext = this; + AddItemCommand = new RelayCommand(OnAddItemCommand, () => !string.IsNullOrEmpty(AddItemText)); + DeleteItemCommand = new RelayCommand(OnDeleteItemCommand); + } + + public RelayCommand AddItemCommand { get; private set; } + public RelayCommand DeleteItemCommand { get; private set; } + + public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register( + "ItemsSource", + typeof (ICollection), + typeof (EditableListBoxControl), + new FrameworkPropertyMetadata(OnItemsSourcePropertyChanged) + ); + + public static readonly DependencyProperty LabelProperty = DependencyProperty.Register( + "Label", + typeof (string), + typeof (EditableListBoxControl), + new FrameworkPropertyMetadata() + ); + + public static readonly DependencyProperty AutoCompleteItemsSourceProperty = DependencyProperty.Register( + "AutoCompleteItemsSource", + typeof (IEnumerable), + typeof (EditableListBoxControl), + new FrameworkPropertyMetadata() + ); + + public IEnumerable AutoCompleteItemsSource + { + get { return (IEnumerable) GetValue(AutoCompleteItemsSourceProperty); } + set { SetValue(AutoCompleteItemsSourceProperty, value); } + } + + public ICollection ItemsSource + { + get { return (ICollection)GetValue(ItemsSourceProperty); } + set { SetValue(ItemsSourceProperty, value); } + } + + public string Label + { + get { return (string)GetValue(LabelProperty); } + set { SetValue(LabelProperty, value); } + } + + public string AddItemText + { + get { return _addItemText; } + set + { + _addItemText = value; + OnPropertyChanged(); + AddItemCommand.RaiseCanExecuteChanged(); + } + } + + private void OnAddItemCommand() + { + if (string.IsNullOrEmpty(AddItemText)) return; + + ItemsSource.Add(AddItemText); + AddItemText = string.Empty; + } + + private void OnDeleteItemCommand(string itemToDelete) + { + if (!string.IsNullOrEmpty(itemToDelete)) + { + ItemsSource.Remove(itemToDelete); + } + } + + private static void OnItemsSourcePropertyChanged(DependencyObject source, + DependencyPropertyChangedEventArgs e) + { + var control = source as EditableListBoxControl; + if (control == null) return; + + control.OnPropertyChanged("ItemsSource"); + } + + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/UserControls/NumericFilterPredicateControl.xaml b/Filtration/UserControls/NumericFilterPredicateControl.xaml new file mode 100644 index 0000000..ccb79a2 --- /dev/null +++ b/Filtration/UserControls/NumericFilterPredicateControl.xaml @@ -0,0 +1,25 @@ + + + + + + + + + + + diff --git a/Filtration/UserControls/NumericFilterPredicateControl.xaml.cs b/Filtration/UserControls/NumericFilterPredicateControl.xaml.cs new file mode 100644 index 0000000..5a804fd --- /dev/null +++ b/Filtration/UserControls/NumericFilterPredicateControl.xaml.cs @@ -0,0 +1,133 @@ +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Windows; +using Filtration.Annotations; +using Filtration.Enums; +using Filtration.Models; + +namespace Filtration.UserControls +{ + internal partial class NumericFilterPredicateControl : INotifyPropertyChanged + { + public NumericFilterPredicateControl() + { + InitializeComponent(); + // ReSharper disable once PossibleNullReferenceException + (Content as FrameworkElement).DataContext = this; + } + + public static readonly DependencyProperty NumericFilterPredicateProperty = DependencyProperty.Register( + "NumericFilterPredicate", + typeof (NumericFilterPredicate), + typeof (NumericFilterPredicateControl), + new FrameworkPropertyMetadata(OnNumericFilterPredicatePropertyChanged)); + + public static readonly DependencyProperty TextProperty = DependencyProperty.Register( + "Text", + typeof (string), + typeof (NumericFilterPredicateControl), + new FrameworkPropertyMetadata {BindsTwoWayByDefault = true}); + + public static readonly DependencyProperty MinimumProperty = DependencyProperty.Register( + "Minimum", + typeof (int), + typeof (NumericFilterPredicateControl), + new FrameworkPropertyMetadata {BindsTwoWayByDefault = true}); + + public static readonly DependencyProperty MaximumProperty = DependencyProperty.Register( + "Maximum", + typeof (int), + typeof (NumericFilterPredicateControl), + new FrameworkPropertyMetadata {BindsTwoWayByDefault = true}); + + public NumericFilterPredicate NumericFilterPredicate + { + get + { + return (NumericFilterPredicate)GetValue(NumericFilterPredicateProperty); + } + set + { + SetValue(NumericFilterPredicateProperty, value); + OnPropertyChanged(); + OnPropertyChanged("FilterPredicateOperator"); + OnPropertyChanged("FilterPredicateOperand"); + } + } + + public FilterPredicateOperator FilterPredicateOperator + { + get { return NumericFilterPredicate.PredicateOperator; } + set + { + NumericFilterPredicate.PredicateOperator = value; + OnPropertyChanged(); + } + } + + public int FilterPredicateOperand + { + get { return NumericFilterPredicate.PredicateOperand; } + set + { + NumericFilterPredicate.PredicateOperand = value; + OnPropertyChanged(); + } + } + + public string Text + { + get + { + return (string)GetValue(TextProperty); + } + set + { + SetValue(TextProperty, value); + } + } + + public int Minimum + { + get + { + return (int)GetValue(MinimumProperty); + } + set + { + SetValue(MinimumProperty, value); + } + } + + public int Maximum + { + get + { + return (int)GetValue(MaximumProperty); + } + set + { + SetValue(MaximumProperty, value); + } + } + + private static void OnNumericFilterPredicatePropertyChanged(DependencyObject source, + DependencyPropertyChangedEventArgs e) + { + var control = source as NumericFilterPredicateControl; + if (control == null) return; + + control.OnPropertyChanged("FilterPredicateOperator"); + control.OnPropertyChanged("FilterPredicateOperand"); + } + + public event PropertyChangedEventHandler PropertyChanged; + + [NotifyPropertyChangedInvocator] + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + var handler = PropertyChanged; + if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); + } + } +} diff --git a/Filtration/Utilities/LineReader.cs b/Filtration/Utilities/LineReader.cs new file mode 100644 index 0000000..cb68df3 --- /dev/null +++ b/Filtration/Utilities/LineReader.cs @@ -0,0 +1,97 @@ +// Taken from JonSkeet's StackOverflow answer here: http://stackoverflow.com/questions/286533/filestream-streamreader-problem-in-c-sharp/286598#286598 +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace Filtration.Utilities +{ + + public sealed class LineReader : IEnumerable + { + /// + /// Means of creating a TextReader to read from. + /// + private readonly Func _dataSource; + + /// + /// Creates a LineReader from a stream source. The delegate is only + /// called when the enumerator is fetched. UTF-8 is used to decode + /// the stream into text. + /// + /// Data source + public LineReader(Func streamSource) + : this(streamSource, Encoding.UTF8) + { + } + + /// + /// Creates a LineReader from a stream source. The delegate is only + /// called when the enumerator is fetched. + /// + /// Data source + /// Encoding to use to decode the stream + /// into text + public LineReader(Func streamSource, Encoding encoding) + : this(() => new StreamReader(streamSource(), encoding)) + { + } + + /// + /// Creates a LineReader from a filename. The file is only opened + /// (or even checked for existence) when the enumerator is fetched. + /// UTF8 is used to decode the file into text. + /// + /// File to read from + public LineReader(string filename) + : this(filename, Encoding.UTF8) + { + } + + /// + /// Creates a LineReader from a filename. The file is only opened + /// (or even checked for existence) when the enumerator is fetched. + /// + /// File to read from + /// Encoding to use to decode the file + /// into text + public LineReader(string filename, Encoding encoding) + : this(() => new StreamReader(filename, encoding)) + { + } + + /// + /// Creates a LineReader from a TextReader source. The delegate + /// is only called when the enumerator is fetched + /// + /// Data source + public LineReader(Func dataSource) + { + _dataSource = dataSource; + } + + /// + /// Enumerates the data source line by line. + /// + public IEnumerator GetEnumerator() + { + using (TextReader reader = _dataSource()) + { + string line; + while ((line = reader.ReadLine()) != null) + { + yield return line; + } + } + } + + /// + /// Enumerates the data source line by line. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} diff --git a/Filtration/ViewModels/FiltrationViewModelBase.cs b/Filtration/ViewModels/FiltrationViewModelBase.cs new file mode 100644 index 0000000..18fa736 --- /dev/null +++ b/Filtration/ViewModels/FiltrationViewModelBase.cs @@ -0,0 +1,18 @@ +using System.Runtime.CompilerServices; +using Filtration.Annotations; +using GalaSoft.MvvmLight; + +namespace Filtration.ViewModels +{ + internal class FiltrationViewModelBase : ViewModelBase + { + /// This gives us the ReSharper option to transform an autoproperty into a property with change notification + /// Also leverages .net 4.5 callermembername attribute + [NotifyPropertyChangedInvocator] + + protected override void RaisePropertyChanged([CallerMemberName]string property = "") + { + base.RaisePropertyChanged(property); + } + } +} diff --git a/Filtration/ViewModels/ILootFilterBlockViewModel.cs b/Filtration/ViewModels/ILootFilterBlockViewModel.cs new file mode 100644 index 0000000..4a055c0 --- /dev/null +++ b/Filtration/ViewModels/ILootFilterBlockViewModel.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; +using Filtration.Models; +using GalaSoft.MvvmLight.CommandWpf; + +namespace Filtration.ViewModels +{ + internal interface ILootFilterBlockViewModel + { + void Initialise(LootFilterBlock lootFilterBlock); + bool IsDirty { get; set; } + LootFilterBlock Block { get; } + RelayCommand CopyBlockCommand { get; } + string BlockDescription { get; set; } + event PropertyChangedEventHandler PropertyChanged; + } +} \ No newline at end of file diff --git a/Filtration/ViewModels/ILootFilterBlockViewModelFactory.cs b/Filtration/ViewModels/ILootFilterBlockViewModelFactory.cs new file mode 100644 index 0000000..1d41df2 --- /dev/null +++ b/Filtration/ViewModels/ILootFilterBlockViewModelFactory.cs @@ -0,0 +1,8 @@ +namespace Filtration.ViewModels +{ + internal interface ILootFilterBlockViewModelFactory + { + ILootFilterBlockViewModel Create(); + void Release(ILootFilterBlockViewModel lootFilterBlockViewModel); + } +} diff --git a/Filtration/ViewModels/ILootFilterScriptViewModel.cs b/Filtration/ViewModels/ILootFilterScriptViewModel.cs new file mode 100644 index 0000000..6ecb2ca --- /dev/null +++ b/Filtration/ViewModels/ILootFilterScriptViewModel.cs @@ -0,0 +1,20 @@ +using System.Collections.ObjectModel; +using System.ComponentModel; +using Filtration.Models; + +namespace Filtration.ViewModels +{ + internal interface ILootFilterScriptViewModel + { + ObservableCollection LootFilterBlockViewModels { get; } + LootFilterScript Script { get; } + bool IsDirty { get; } + string Description { get; set; } + string Filename { get; } + string DisplayName { get; } + string Filepath { get; } + void Initialise(LootFilterScript lootFilterScript); + void RemoveDirtyFlag(); + event PropertyChangedEventHandler PropertyChanged; + } +} \ No newline at end of file diff --git a/Filtration/ViewModels/ILootFilterScriptViewModelFactory.cs b/Filtration/ViewModels/ILootFilterScriptViewModelFactory.cs new file mode 100644 index 0000000..69f8226 --- /dev/null +++ b/Filtration/ViewModels/ILootFilterScriptViewModelFactory.cs @@ -0,0 +1,8 @@ +namespace Filtration.ViewModels +{ + internal interface ILootFilterScriptViewModelFactory + { + ILootFilterScriptViewModel Create(); + void Release(ILootFilterScriptViewModel lootFilterScriptViewModel); + } +} diff --git a/Filtration/ViewModels/IMainWindowViewModel.cs b/Filtration/ViewModels/IMainWindowViewModel.cs new file mode 100644 index 0000000..9bf09b1 --- /dev/null +++ b/Filtration/ViewModels/IMainWindowViewModel.cs @@ -0,0 +1,6 @@ +namespace Filtration.ViewModels +{ + internal interface IMainWindowViewModel + { + } +} \ No newline at end of file diff --git a/Filtration/ViewModels/LootFilterBlockViewModel.cs b/Filtration/ViewModels/LootFilterBlockViewModel.cs new file mode 100644 index 0000000..dd5f1d0 --- /dev/null +++ b/Filtration/ViewModels/LootFilterBlockViewModel.cs @@ -0,0 +1,288 @@ +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Linq; +using System.Windows; +using System.Windows.Media; +using Filtration.Models; +using Filtration.Models.BlockItemTypes; +using Filtration.Services; +using Filtration.Translators; +using GalaSoft.MvvmLight.CommandWpf; + +namespace Filtration.ViewModels +{ + internal class LootFilterBlockViewModel : FiltrationViewModelBase, ILootFilterBlockViewModel + { + private readonly ILootFilterBlockTranslator _translator; + private readonly IStaticDataService _staticDataService; + private readonly MediaPlayer _mediaPlayer = new MediaPlayer(); + + private bool _displaySettingsPopupOpen; + + public LootFilterBlockViewModel(ILootFilterBlockTranslator translator, IStaticDataService staticDataService) + { + _translator = translator; + _staticDataService = staticDataService; + CopyBlockCommand = new RelayCommand(OnCopyConditionCommand); + + AddFilterBlockItemCommand = new RelayCommand(OnAddFilterBlockItemCommand); + AddAudioVisualBlockItemCommand = new RelayCommand(OnAddAudioVisualBlockItemCommand); + RemoveFilterBlockItemCommand = new RelayCommand(OnRemoveFilterBlockItemCommand); + RemoveAudioVisualBlockItemCommand = new RelayCommand(OnRemoveAudioVisualBlockItemCommand); + PlaySoundCommand = new RelayCommand(OnPlaySoundCommand, () => HasSound); + } + + public void Initialise(LootFilterBlock lootFilterBlock) + { + if (lootFilterBlock == null) + { + throw new ArgumentNullException("lootFilterBlock"); + } + + Block = lootFilterBlock; + lootFilterBlock.BlockItems.CollectionChanged += OnBlockItemsCollectionChanged; + + foreach (var blockItem in lootFilterBlock.BlockItems.OfType()) + { + blockItem.PropertyChanged += OnAudioVisualBlockItemChanged; + } + } + + public RelayCommand CopyBlockCommand { get; private set; } + public RelayCommand AddFilterBlockItemCommand { get; private set; } + public RelayCommand AddAudioVisualBlockItemCommand { get; private set; } + public RelayCommand RemoveFilterBlockItemCommand { get; private set; } + public RelayCommand RemoveAudioVisualBlockItemCommand { get; private set; } + public RelayCommand PlaySoundCommand { get; private set; } + + public LootFilterBlock Block { get; private set; } + public bool IsDirty { get; set; } + + public ObservableCollection FilterBlockItems + { + get { return Block.BlockItems; } + } + + public IEnumerable SummaryBlockItems + { + get { return Block.BlockItems.Where(b => !(b is IAudioVisualBlockItem)); } + } + + public IEnumerable AudioVisualBlockItems + { + get { return Block.BlockItems.Where(b => b is IAudioVisualBlockItem); } + } + + public bool DisplaySettingsPopupOpen + { + get { return _displaySettingsPopupOpen; } + set + { + _displaySettingsPopupOpen = value; + RaisePropertyChanged(); + } + } + + public IEnumerable AutoCompleteItemClasses + { + get { return _staticDataService.ItemClasses; } + } + + public IEnumerable AutoCompleteItemBaseTypes + { + get { return _staticDataService.ItemBaseTypes; } + } + + public List SoundsAvailable + { + get + { + return new List + { + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + }; + } + } + + public List BlockItemTypesAvailable + { + get + { + return new List + { + typeof (ItemLevelBlockItem), + typeof (DropLevelBlockItem), + typeof (QualityBlockItem), + typeof (RarityBlockItem), + typeof (SocketsBlockItem), + typeof (LinkedSocketsBlockItem), + typeof (WidthBlockItem), + typeof (HeightBlockItem), + typeof (SocketGroupBlockItem), + typeof (ClassBlockItem), + typeof (BaseTypeBlockItem) + }; + } + } + + public List AudioVisualBlockItemTypesAvailable + { + get + { + return new List + { + typeof (TextColorBlockItem), + typeof (BackgroundColorBlockItem), + typeof (BorderColorBlockItem), + typeof (FontSizeBlockItem), + typeof (SoundBlockItem) + }; + } + } + + public string BlockDescription + { + get + { + return Block.Description; + } + set + { + Block.Description = value; + RaisePropertyChanged(); + } + } + + public bool HasTextColor + { + get { return FilterBlockItems.Count(b => b is TextColorBlockItem) > 0; } + } + + public Color DisplayTextColor + { + get + { + return HasTextColor + ? FilterBlockItems.OfType().First().Color + : new Color { A = 255, R = 255, G = 255, B = 255 }; + } + } + + public bool HasBackgroundColor + { + get { return FilterBlockItems.Count(b => b is BackgroundColorBlockItem) > 0; } + } + + public Color DisplayBackgroundColor + { + get + { + return HasBackgroundColor + ? FilterBlockItems.OfType().First().Color + : new Color { A = 255, R = 0, G = 0, B = 0 }; + } + } + + public bool HasBorderColor + { + get { return FilterBlockItems.Count(b => b is BorderColorBlockItem) > 0; } + } + + public Color DisplayBorderColor + { + get + { + return HasBorderColor + ? FilterBlockItems.OfType().First().Color + : new Color { A = 255, R = 0, G = 0, B = 0 }; + } + } + + public bool HasFontSize + { + get { return FilterBlockItems.Count(b => b is FontSizeBlockItem) > 0; } + } + + public bool HasSound + { + get { return FilterBlockItems.Count(b => b is SoundBlockItem) > 0; } + } + + private void OnCopyConditionCommand() + { + Clipboard.SetText(_translator.TranslateLootFilterBlockToString(Block)); + } + + private void OnAddFilterBlockItemCommand(Type blockItemType) + { + if (!AddBlockItemAllowed(blockItemType)) return; + var newBlockItem = (ILootFilterBlockItem) Activator.CreateInstance(blockItemType); + + FilterBlockItems.Add(newBlockItem); + IsDirty = true; + } + + private void OnRemoveFilterBlockItemCommand(ILootFilterBlockItem blockItem) + { + FilterBlockItems.Remove(blockItem); + IsDirty = true; + } + + private void OnAddAudioVisualBlockItemCommand(Type blockItemType) + { + if (!AddBlockItemAllowed(blockItemType)) return; + var newBlockItem = (ILootFilterBlockItem) Activator.CreateInstance(blockItemType); + + newBlockItem.PropertyChanged += OnAudioVisualBlockItemChanged; + FilterBlockItems.Add(newBlockItem); + OnAudioVisualBlockItemChanged(null, null); + IsDirty = true; + } + + private void OnRemoveAudioVisualBlockItemCommand(ILootFilterBlockItem blockItem) + { + blockItem.PropertyChanged -= OnAudioVisualBlockItemChanged; + FilterBlockItems.Remove(blockItem); + OnAudioVisualBlockItemChanged(null, null); + IsDirty = true; + } + + private bool AddBlockItemAllowed(Type type) + { + var blockItem = (ILootFilterBlockItem)Activator.CreateInstance(type); + var blockCount = FilterBlockItems.Count(b => b.GetType() == type); + return blockCount < blockItem.MaximumAllowed; + } + + private void OnPlaySoundCommand() + { + var soundUri = "Resources/AlertSound" + FilterBlockItems.OfType().First().Value + ".wav"; + _mediaPlayer.Open(new Uri(soundUri, UriKind.Relative)); + _mediaPlayer.Play(); + } + + private void OnAudioVisualBlockItemChanged(object sender, EventArgs e) + { + RaisePropertyChanged("DisplayTextColor"); + RaisePropertyChanged("DisplayBackgroundColor"); + RaisePropertyChanged("DisplayBorderColor"); + RaisePropertyChanged("HasSound"); + } + + private void OnBlockItemsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) + { + RaisePropertyChanged("SummaryBlockItems"); + RaisePropertyChanged("AudioVisualBlockItems"); + } + } +} diff --git a/Filtration/ViewModels/LootFilterScriptViewModel - Copy.cs b/Filtration/ViewModels/LootFilterScriptViewModel - Copy.cs new file mode 100644 index 0000000..7af7c25 --- /dev/null +++ b/Filtration/ViewModels/LootFilterScriptViewModel - Copy.cs @@ -0,0 +1,100 @@ +using System.Collections.Generic; +using System.Windows.Media; +using Filtration.Enums; +using Filtration.Models; + +namespace Filtration.ViewModels +{ + internal class LootFilterScriptViewModela : ILootFilterScriptViewModel + { + private readonly ILootFilterConditionViewModelFactory _lootFilterConditionViewModelFactory; + + public LootFilterScriptViewModela(ILootFilterConditionViewModelFactory lootFilterConditionViewModelFactory) + { + _lootFilterConditionViewModelFactory = lootFilterConditionViewModelFactory; + LootFilterConditionViewModels = new List(); + + var testCondition = new LootFilterCondition + { + BackgroundColor = Colors.DarkCyan, + TextColor = Colors.White, + BorderColor = Colors.Red, + Sockets = + new NumericFilterPredicate(FilterPredicateOperator.LessThanOrEqual, 5), + LinkedSockets = + new NumericFilterPredicate(FilterPredicateOperator.Equal, 2), + ItemRarity = new NumericFilterPredicate(FilterPredicateOperator.GreaterThan, (int) ItemRarity.Magic), + FontSize = 10, + Quality = + new NumericFilterPredicate(FilterPredicateOperator.GreaterThanOrEqual, 15), + ItemLevel = + new NumericFilterPredicate(FilterPredicateOperator.GreaterThan, 50), + DropLevel = new NumericFilterPredicate(), + FilterDescription = "My Wicked Filter" + }; + + var testCondition2 = new LootFilterCondition + { + BackgroundColor = Colors.Beige, + TextColor = Colors.Blue, + BorderColor = Colors.Black, + Sockets = + new NumericFilterPredicate(FilterPredicateOperator.LessThan, 4), + LinkedSockets = + new NumericFilterPredicate(FilterPredicateOperator.GreaterThanOrEqual, 3), + FontSize = 12, + Quality = + new NumericFilterPredicate(FilterPredicateOperator.GreaterThanOrEqual, 15), + ItemLevel = + new NumericFilterPredicate(FilterPredicateOperator.Equal, 32), + DropLevel = new NumericFilterPredicate(FilterPredicateOperator.GreaterThanOrEqual, 85), + FilterDescription = "This is a test filter" + }; + + + + var testCondition3 = new LootFilterCondition + { + BackgroundColor = Colors.Beige, + TextColor = new Color { A = 128, R = 0, G = 0, B = 255}, + BorderColor = Colors.Black, + Sockets = + new NumericFilterPredicate(FilterPredicateOperator.LessThan, 4), + LinkedSockets = + new NumericFilterPredicate(FilterPredicateOperator.GreaterThanOrEqual, 3), + FontSize = 12, + Quality = + new NumericFilterPredicate(FilterPredicateOperator.GreaterThanOrEqual, 15), + ItemLevel = + new NumericFilterPredicate(FilterPredicateOperator.Equal, 32), + DropLevel = new NumericFilterPredicate(FilterPredicateOperator.GreaterThanOrEqual, 85), + FilterDescription = "This is a test filter" + }; + + var testConditionFilter = _lootFilterConditionViewModelFactory.Create(); + var testConditionFilter2 = _lootFilterConditionViewModelFactory.Create(); + var testConditionFilter3 = _lootFilterConditionViewModelFactory.Create(); + testConditionFilter.Initialise(testCondition); + testConditionFilter2.Initialise(testCondition2); + testConditionFilter3.Initialise(testCondition3); + + testConditionFilter.Classes.Add("Test Class 1"); + testConditionFilter.Classes.Add("Test Class 2"); + testConditionFilter.Classes.Add("Test Class 3"); + testConditionFilter.Classes.Add("Test Class 4"); + testConditionFilter.Classes.Add("Test Class 5"); + testConditionFilter.Classes.Add("Test Class 6"); + testConditionFilter.Classes.Add("Test Class 7"); + testConditionFilter.Classes.Add("Test Class 8"); + testConditionFilter.Classes.Add("Test Class 9"); + testConditionFilter.Classes.Add("Test Class 10"); + testConditionFilter.BaseTypes.Add("Test Base Type 1"); + testConditionFilter.BaseTypes.Add("Test Base Type 2"); + LootFilterConditionViewModels.Add(testConditionFilter); + LootFilterConditionViewModels.Add(testConditionFilter2); + LootFilterConditionViewModels.Add(testConditionFilter3); + } + + public List LootFilterConditionViewModels { get; private set; } + } +} diff --git a/Filtration/ViewModels/LootFilterScriptViewModel.cs b/Filtration/ViewModels/LootFilterScriptViewModel.cs new file mode 100644 index 0000000..a944f6c --- /dev/null +++ b/Filtration/ViewModels/LootFilterScriptViewModel.cs @@ -0,0 +1,225 @@ +using System.Collections.ObjectModel; +using System.IO; +using System.Linq; +using System.Windows; +using Filtration.Models; +using GalaSoft.MvvmLight.CommandWpf; + +namespace Filtration.ViewModels +{ + internal class LootFilterScriptViewModel : FiltrationViewModelBase, ILootFilterScriptViewModel + { + private readonly ILootFilterBlockViewModelFactory _lootFilterBlockViewModelFactory; + private bool _isDirty; + + public LootFilterScriptViewModel(ILootFilterBlockViewModelFactory lootFilterBlockViewModelFactory ) + { + DeleteBlockCommand = new RelayCommand(OnDeleteBlock, () => SelectedBlockViewModel != null); + MoveBlockToTopCommand = new RelayCommand(OnMoveBlockToTopCommand, () => SelectedBlockViewModel != null); + MoveBlockUpCommand = new RelayCommand(OnMoveBlockUpCommand, () => SelectedBlockViewModel != null); + MoveBlockDownCommand = new RelayCommand(OnMoveBlockDownCommand, () => SelectedBlockViewModel != null); + MoveBlockToBottomCommand = new RelayCommand(OnMoveBlockToBottomCommand, () => SelectedBlockViewModel != null); + AddBlockAboveCommand = new RelayCommand(OnAddBlockAboveCommand, () => SelectedBlockViewModel != null || LootFilterBlockViewModels.Count == 0); + AddBlockBelowCommand = new RelayCommand(OnAddBlockBelowCommand, () => SelectedBlockViewModel != null); + + AddSectionAboveCommand = new RelayCommand(OnAddSectionAboveCommand, () => SelectedBlockViewModel != null); + + _lootFilterBlockViewModelFactory = lootFilterBlockViewModelFactory; + LootFilterBlockViewModels = new ObservableCollection(); + + } + + public RelayCommand DeleteBlockCommand { get; private set; } + public RelayCommand MoveBlockToTopCommand { get; private set; } + public RelayCommand MoveBlockUpCommand { get; private set; } + public RelayCommand MoveBlockDownCommand { get; private set; } + public RelayCommand MoveBlockToBottomCommand { get; private set; } + public RelayCommand AddBlockAboveCommand { get; private set; } + public RelayCommand AddBlockBelowCommand { get; private set; } + public RelayCommand AddSectionAboveCommand { get; private set; } + + public ObservableCollection LootFilterBlockViewModels { get; private set; } + + public string Description + { + get { return Script.Description; } + set + { + Script.Description = value; + _isDirty = true; + RaisePropertyChanged(); + } + } + + public LootFilterBlockViewModel SelectedBlockViewModel { get; set; } + + public LootFilterScript Script { get; private set; } + + public bool IsDirty + { + get { return _isDirty || HasDirtyChildren; } + set { _isDirty = value; } + } + + private bool HasDirtyChildren + { + get { return LootFilterBlockViewModels.Any(vm => vm.IsDirty); } + } + + private void CleanChildren() + { + foreach (var vm in LootFilterBlockViewModels) + { + vm.IsDirty = false; + } + } + + public void RemoveDirtyFlag() + { + CleanChildren(); + IsDirty = false; + } + + public string DisplayName + { + get { return !string.IsNullOrEmpty(Filename) ? Filename : Description; } + } + + public string Filename + { + get { return Path.GetFileName(Script.FilePath); } + } + + public string Filepath + { + get { return Script.FilePath; } + } + + public void Initialise(LootFilterScript lootFilterScript) + { + LootFilterBlockViewModels.Clear(); + + Script = lootFilterScript; + foreach (var block in Script.LootFilterBlocks) + { + var vm = _lootFilterBlockViewModelFactory.Create(); + vm.Initialise(block); + LootFilterBlockViewModels.Add(vm); + } + } + + private void OnMoveBlockToTopCommand() + { + var currentIndex = LootFilterBlockViewModels.IndexOf(SelectedBlockViewModel); + + if (currentIndex > 0) + { + var block = SelectedBlockViewModel.Block; + Script.LootFilterBlocks.Remove(block); + Script.LootFilterBlocks.Insert(0, block); + LootFilterBlockViewModels.Move(currentIndex, 0); + _isDirty = true; + } + } + + private void OnMoveBlockUpCommand() + { + var currentIndex = LootFilterBlockViewModels.IndexOf(SelectedBlockViewModel); + + if (currentIndex > 0) + { + var block = SelectedBlockViewModel.Block; + var blockPos = Script.LootFilterBlocks.IndexOf(block); + Script.LootFilterBlocks.RemoveAt(blockPos); + Script.LootFilterBlocks.Insert(blockPos - 1, block); + LootFilterBlockViewModels.Move(currentIndex, currentIndex - 1); + _isDirty = true; + } + } + + private void OnMoveBlockDownCommand() + { + var currentIndex = LootFilterBlockViewModels.IndexOf(SelectedBlockViewModel); + + if (currentIndex < LootFilterBlockViewModels.Count - 1) + { + var block = SelectedBlockViewModel.Block; + var blockPos = Script.LootFilterBlocks.IndexOf(block); + Script.LootFilterBlocks.RemoveAt(blockPos); + Script.LootFilterBlocks.Insert(blockPos + 1, block); + LootFilterBlockViewModels.Move(currentIndex, currentIndex + 1); + _isDirty = true; + } + } + + private void OnMoveBlockToBottomCommand() + { + var currentIndex = LootFilterBlockViewModels.IndexOf(SelectedBlockViewModel); + + if (currentIndex < LootFilterBlockViewModels.Count - 1) + { + var block = SelectedBlockViewModel.Block; + Script.LootFilterBlocks.Remove(block); + Script.LootFilterBlocks.Add(block); + LootFilterBlockViewModels.Move(currentIndex, LootFilterBlockViewModels.Count - 1); + _isDirty = true; + } + } + + private void OnAddBlockAboveCommand() + { + var vm = _lootFilterBlockViewModelFactory.Create(); + var newBlock = new LootFilterBlock(); + vm.Initialise(newBlock); + + if (LootFilterBlockViewModels.Count > 0) + { + Script.LootFilterBlocks.Insert(Script.LootFilterBlocks.IndexOf(SelectedBlockViewModel.Block), newBlock); + LootFilterBlockViewModels.Insert(LootFilterBlockViewModels.IndexOf(SelectedBlockViewModel), vm); + } + else + { + Script.LootFilterBlocks.Add(newBlock); + LootFilterBlockViewModels.Add(vm); + } + + _isDirty = true; + } + + private void OnAddBlockBelowCommand() + { + var vm = _lootFilterBlockViewModelFactory.Create(); + var newBlock = new LootFilterBlock(); + vm.Initialise(newBlock); + + Script.LootFilterBlocks.Insert(Script.LootFilterBlocks.IndexOf(SelectedBlockViewModel.Block) + 1, newBlock); + LootFilterBlockViewModels.Insert(LootFilterBlockViewModels.IndexOf(SelectedBlockViewModel) + 1, vm); + _isDirty = true; + } + + private void OnAddSectionAboveCommand() + { + var vm = _lootFilterBlockViewModelFactory.Create(); + var newSection = new LootFilterSection { Description = "New Section" }; + vm.Initialise(newSection); + + Script.LootFilterBlocks.Insert(Script.LootFilterBlocks.IndexOf(SelectedBlockViewModel.Block), newSection); + LootFilterBlockViewModels.Insert(LootFilterBlockViewModels.IndexOf(SelectedBlockViewModel), vm); + _isDirty = true; + } + + private void OnDeleteBlock() + { + var result = MessageBox.Show("Are you sure you wish to delete this block?", "Delete Confirmation", + MessageBoxButton.YesNo, MessageBoxImage.Question); + + if (result == MessageBoxResult.Yes) + { + Script.LootFilterBlocks.Remove(SelectedBlockViewModel.Block); + LootFilterBlockViewModels.Remove(SelectedBlockViewModel); + _isDirty = true; + } + SelectedBlockViewModel = null; + } + } +} diff --git a/Filtration/ViewModels/MainWindowViewModel.cs b/Filtration/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..127b7bf --- /dev/null +++ b/Filtration/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections.ObjectModel; +using System.Windows.Forms; +using Castle.Core; +using Filtration.Models; +using Filtration.Services; +using Filtration.Translators; +using GalaSoft.MvvmLight.CommandWpf; +using Clipboard = System.Windows.Clipboard; +using OpenFileDialog = Microsoft.Win32.OpenFileDialog; + +namespace Filtration.ViewModels +{ + internal class MainWindowViewModel : FiltrationViewModelBase, IMainWindowViewModel + { + private LootFilterScript _loadedScript; + + private readonly ILootFilterScriptViewModelFactory _lootFilterScriptViewModelFactory; + private readonly ILootFilterPersistenceService _persistenceService; + private readonly ILootFilterScriptTranslator _lootFilterScriptTranslator; + private ILootFilterScriptViewModel _currentScriptViewModel; + private readonly ObservableCollection _scriptViewModels; + + public MainWindowViewModel(ILootFilterScriptViewModelFactory lootFilterScriptViewModelFactory, + ILootFilterPersistenceService persistenceService, ILootFilterScriptTranslator lootFilterScriptTranslator) + { + _lootFilterScriptViewModelFactory = lootFilterScriptViewModelFactory; + _persistenceService = persistenceService; + _lootFilterScriptTranslator = lootFilterScriptTranslator; + + _scriptViewModels = new ObservableCollection(); + + OpenScriptCommand = new RelayCommand(OnOpenScriptCommand); + SaveScriptCommand = new RelayCommand(OnSaveScriptCommand, () => CurrentScriptViewModel != null); + SaveScriptAsCommand = new RelayCommand(OnSaveScriptAsCommand, () => CurrentScriptViewModel != null); + CopyScriptCommand = new RelayCommand(OnCopyScriptCommand, () => CurrentScriptViewModel != null); + NewScriptCommand = new RelayCommand(OnNewScriptCommand); + CloseScriptCommand = new RelayCommand(OnCloseScriptCommand, v => CurrentScriptViewModel != null); + + LoadScriptFromFile("C:\\ThioleLootFilter.txt"); + + SetLootFilterScriptDirectory(); + } + + public RelayCommand OpenScriptCommand { get; private set; } + public RelayCommand SaveScriptCommand { get; private set; } + public RelayCommand SaveScriptAsCommand { get; private set; } + public RelayCommand CopyScriptCommand { get; private set; } + public RelayCommand NewScriptCommand { get; private set; } + public RelayCommand CloseScriptCommand { get; private set; } + + public ObservableCollection ScriptViewModels + { + get { return _scriptViewModels; } + } + + [DoNotWire] + public ILootFilterScriptViewModel CurrentScriptViewModel + { + get { return _currentScriptViewModel; } + set + { + _currentScriptViewModel = value; + RaisePropertyChanged(); + SaveScriptCommand.RaiseCanExecuteChanged(); + SaveScriptAsCommand.RaiseCanExecuteChanged(); + } + } + + private void OnOpenScriptCommand() + { + var openFileDialog = new OpenFileDialog + { + Filter = "Filter Files (*.filter)|*.filter|All Files (*.*)|*.*", + InitialDirectory = _persistenceService.LootFilterScriptDirectory + }; + + if (openFileDialog.ShowDialog() != true) return; + + LoadScriptFromFile(openFileDialog.FileName); + } + + private void LoadScriptFromFile(string path) + { + try + { + _loadedScript = _persistenceService.LoadLootFilterScript(path); + } + catch (Exception e) + { + MessageBox.Show(@"Error loading filter script - " + e.Message, @"Script Load Error", MessageBoxButtons.OK, + MessageBoxIcon.Error); + return; + } + + var newViewModel = _lootFilterScriptViewModelFactory.Create(); + newViewModel.Initialise(_loadedScript); + ScriptViewModels.Add(newViewModel); + CurrentScriptViewModel = newViewModel; + } + + private void SetLootFilterScriptDirectory() + { + var defaultDir = _persistenceService.DefaultPathOfExileDirectory(); + if (!string.IsNullOrEmpty(defaultDir)) + { + _persistenceService.LootFilterScriptDirectory = defaultDir; + } + else + { + var dlg = new FolderBrowserDialog + { + Description = @"Select your Path of Exile data directory, usually in Documents\My Games", + ShowNewFolderButton = false + }; + var result = dlg.ShowDialog(); + + if (result == DialogResult.OK) + { + _persistenceService.LootFilterScriptDirectory = dlg.SelectedPath; + } + } + } + + private void OnSaveScriptCommand() + { + if (!ValidateScript()) return; + + if (string.IsNullOrEmpty(CurrentScriptViewModel.Script.FilePath)) + { + OnSaveScriptAsCommand(); + return; + } + + try + { + _persistenceService.SaveLootFilterScript(CurrentScriptViewModel.Script); + CurrentScriptViewModel.RemoveDirtyFlag(); + } + catch (Exception e) + { + MessageBox.Show(@"Error saving filter file - " + e.Message, @"Save Error", MessageBoxButtons.OK, + MessageBoxIcon.Error); + } + + } + + private void OnSaveScriptAsCommand() + { + if (!ValidateScript()) return; + + var saveDialog = new SaveFileDialog + { + DefaultExt = ".filter", + Filter = @"Filter Files (*.filter)|*.filter|All Files (*.*)|*.*", + InitialDirectory = _persistenceService.LootFilterScriptDirectory + }; + + var result = saveDialog.ShowDialog(); + + if (result != DialogResult.OK) return; + + var previousFilePath = CurrentScriptViewModel.Script.FilePath; + try + { + CurrentScriptViewModel.Script.FilePath = saveDialog.FileName; + _persistenceService.SaveLootFilterScript(CurrentScriptViewModel.Script); + CurrentScriptViewModel.RemoveDirtyFlag(); + } + catch (Exception e) + { + MessageBox.Show(@"Error saving filter file - " + e.Message, @"Save Error", MessageBoxButtons.OK, + MessageBoxIcon.Error); + CurrentScriptViewModel.Script.FilePath = previousFilePath; + } + } + + private bool ValidateScript() + { + var result = CurrentScriptViewModel.Script.Validate(); + + if (result.Count == 0) return true; + + var failures = string.Empty; + + // ReSharper disable once LoopCanBeConvertedToQuery + foreach (string failure in result) + { + failures += failure + Environment.NewLine; + } + + MessageBox.Show(@"The following script validation errors occurred:" + Environment.NewLine + failures, + @"Script Validation Failure", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + return false; + } + + private void OnCopyScriptCommand() + { + Clipboard.SetText(_lootFilterScriptTranslator.TranslateLootFilterScriptToString(_loadedScript)); + } + + private void OnNewScriptCommand() + { + var newScript = new LootFilterScript(); + var newViewModel = _lootFilterScriptViewModelFactory.Create(); + newViewModel.Initialise(newScript); + newViewModel.Description = "New Script"; + ScriptViewModels.Add(newViewModel); + CurrentScriptViewModel = newViewModel; + } + + private void OnCloseScriptCommand(ILootFilterScriptViewModel scriptViewModel) + { + CurrentScriptViewModel = scriptViewModel; + if (!CurrentScriptViewModel.IsDirty) + { + ScriptViewModels.Remove(CurrentScriptViewModel); + } + else + { + var result = MessageBox.Show(@"Want to save your changes to this script?", + @"Filtration", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); + switch (result) + { + case DialogResult.Yes: + { + OnSaveScriptCommand(); + ScriptViewModels.Remove(CurrentScriptViewModel); + break; + } + case DialogResult.No: + { + ScriptViewModels.Remove(CurrentScriptViewModel); + break; + } + case DialogResult.Cancel: + { + break; + } + } + } + } + } +} diff --git a/Filtration/Views/BlockTemplateSelector.cs b/Filtration/Views/BlockTemplateSelector.cs new file mode 100644 index 0000000..8c12197 --- /dev/null +++ b/Filtration/Views/BlockTemplateSelector.cs @@ -0,0 +1,26 @@ +using System.Windows; +using System.Windows.Controls; +using Filtration.Models; +using Filtration.ViewModels; + +namespace Filtration.Views +{ + public class BlockTemplateSelector : DataTemplateSelector + { + public override DataTemplate SelectTemplate(object item, DependencyObject container) + { + var viewModel = item as LootFilterBlockViewModel; + var element = container as FrameworkElement; + + if (viewModel == null || element == null) + return null; + + if (viewModel.Block is LootFilterSection) + { + return element.FindResource("LootFilterSectionTemplate") as DataTemplate; + } + + return element.FindResource("LootFilterBlockTemplate") as DataTemplate; + } + } +} diff --git a/Filtration/Views/CrossButton.xaml b/Filtration/Views/CrossButton.xaml new file mode 100644 index 0000000..1fcf259 --- /dev/null +++ b/Filtration/Views/CrossButton.xaml @@ -0,0 +1,67 @@ + + + \ No newline at end of file diff --git a/Filtration/Views/ExpanderStyle.xaml b/Filtration/Views/ExpanderStyle.xaml new file mode 100644 index 0000000..d4653d3 --- /dev/null +++ b/Filtration/Views/ExpanderStyle.xaml @@ -0,0 +1,230 @@ + + + + + + + + \ No newline at end of file diff --git a/Filtration/Views/IMainWindow.cs b/Filtration/Views/IMainWindow.cs new file mode 100644 index 0000000..d8a0e2f --- /dev/null +++ b/Filtration/Views/IMainWindow.cs @@ -0,0 +1,7 @@ +namespace Filtration.Views +{ + public interface IMainWindow + { + void Show(); + } +} diff --git a/Filtration/Views/LootFilterBlockDisplaySettingsView.xaml b/Filtration/Views/LootFilterBlockDisplaySettingsView.xaml new file mode 100644 index 0000000..d48be38 --- /dev/null +++ b/Filtration/Views/LootFilterBlockDisplaySettingsView.xaml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Filtration/Views/LootFilterBlockDisplaySettingsView.xaml.cs b/Filtration/Views/LootFilterBlockDisplaySettingsView.xaml.cs new file mode 100644 index 0000000..7cae0f0 --- /dev/null +++ b/Filtration/Views/LootFilterBlockDisplaySettingsView.xaml.cs @@ -0,0 +1,10 @@ +namespace Filtration.Views +{ + public partial class LootFilterBlockDisplaySettingsView + { + public LootFilterBlockDisplaySettingsView() + { + InitializeComponent(); + } + } +} diff --git a/Filtration/Views/LootFilterBlockView.xaml b/Filtration/Views/LootFilterBlockView.xaml new file mode 100644 index 0000000..ddda5d2 --- /dev/null +++ b/Filtration/Views/LootFilterBlockView.xaml @@ -0,0 +1,284 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Show + Hide + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Filtration/Views/LootFilterBlockView.xaml.cs b/Filtration/Views/LootFilterBlockView.xaml.cs new file mode 100644 index 0000000..39972fc --- /dev/null +++ b/Filtration/Views/LootFilterBlockView.xaml.cs @@ -0,0 +1,10 @@ +namespace Filtration.Views +{ + public partial class LootFilterBlockView + { + public LootFilterBlockView() + { + InitializeComponent(); + } + } +} diff --git a/Filtration/Views/LootFilterBlockViewDictionary.xaml b/Filtration/Views/LootFilterBlockViewDictionary.xaml new file mode 100644 index 0000000..1198f40 --- /dev/null +++ b/Filtration/Views/LootFilterBlockViewDictionary.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Filtration/Views/LootFilterScriptView.xaml b/Filtration/Views/LootFilterScriptView.xaml new file mode 100644 index 0000000..adf084e --- /dev/null +++ b/Filtration/Views/LootFilterScriptView.xaml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Filtration/Views/LootFilterScriptView.xaml.cs b/Filtration/Views/LootFilterScriptView.xaml.cs new file mode 100644 index 0000000..fb362d1 --- /dev/null +++ b/Filtration/Views/LootFilterScriptView.xaml.cs @@ -0,0 +1,10 @@ +namespace Filtration.Views +{ + public partial class LootFilterScriptView + { + public LootFilterScriptView() + { + InitializeComponent(); + } + } +} diff --git a/Filtration/Views/LootFilterSectionView.xaml b/Filtration/Views/LootFilterSectionView.xaml new file mode 100644 index 0000000..021cc7d --- /dev/null +++ b/Filtration/Views/LootFilterSectionView.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/Filtration/Views/LootFilterSectionView.xaml.cs b/Filtration/Views/LootFilterSectionView.xaml.cs new file mode 100644 index 0000000..2770291 --- /dev/null +++ b/Filtration/Views/LootFilterSectionView.xaml.cs @@ -0,0 +1,10 @@ +namespace Filtration.Views +{ + public partial class LootFilterSectionView + { + public LootFilterSectionView() + { + InitializeComponent(); + } + } +} diff --git a/Filtration/Views/MainWindow.xaml b/Filtration/Views/MainWindow.xaml new file mode 100644 index 0000000..db255c1 --- /dev/null +++ b/Filtration/Views/MainWindow.xaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Filtration/Views/MainWindow.xaml.cs b/Filtration/Views/MainWindow.xaml.cs new file mode 100644 index 0000000..99c8e87 --- /dev/null +++ b/Filtration/Views/MainWindow.xaml.cs @@ -0,0 +1,13 @@ +using Filtration.ViewModels; + +namespace Filtration.Views +{ + internal partial class MainWindow : IMainWindow + { + public MainWindow(IMainWindowViewModel mainWindowViewModel) + { + InitializeComponent(); + DataContext = mainWindowViewModel; + } + } +} diff --git a/Filtration/WindsorInstallers/ModelsInstaller.cs b/Filtration/WindsorInstallers/ModelsInstaller.cs new file mode 100644 index 0000000..768d433 --- /dev/null +++ b/Filtration/WindsorInstallers/ModelsInstaller.cs @@ -0,0 +1,14 @@ +using Castle.MicroKernel.Registration; +using Castle.MicroKernel.SubSystems.Configuration; +using Castle.Windsor; + +namespace Filtration.WindsorInstallers +{ + public class ModelsInstaller : IWindsorInstaller + { + public void Install(IWindsorContainer container, IConfigurationStore store) + { + + } + } +} diff --git a/Filtration/WindsorInstallers/ServicesInstaller.cs b/Filtration/WindsorInstallers/ServicesInstaller.cs new file mode 100644 index 0000000..0f09b79 --- /dev/null +++ b/Filtration/WindsorInstallers/ServicesInstaller.cs @@ -0,0 +1,28 @@ +using Castle.MicroKernel.Registration; +using Castle.MicroKernel.SubSystems.Configuration; +using Castle.Windsor; +using Filtration.Services; + +namespace Filtration.WindsorInstallers +{ + public class ServicesInstaller :IWindsorInstaller + { + public void Install(IWindsorContainer container, IConfigurationStore store) + { + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Singleton); + + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Singleton); + + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Singleton); + } + } +} diff --git a/Filtration/WindsorInstallers/TranslatorsInstaller.cs b/Filtration/WindsorInstallers/TranslatorsInstaller.cs new file mode 100644 index 0000000..48bd3a6 --- /dev/null +++ b/Filtration/WindsorInstallers/TranslatorsInstaller.cs @@ -0,0 +1,23 @@ +using Castle.MicroKernel.Registration; +using Castle.MicroKernel.SubSystems.Configuration; +using Castle.Windsor; +using Filtration.Translators; + +namespace Filtration.WindsorInstallers +{ + public class TranslatorsInstaller : IWindsorInstaller + { + public void Install(IWindsorContainer container, IConfigurationStore store) + { + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Singleton); + + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Singleton); + } + } +} diff --git a/Filtration/WindsorInstallers/ViewModelsInstaller.cs b/Filtration/WindsorInstallers/ViewModelsInstaller.cs new file mode 100644 index 0000000..c178199 --- /dev/null +++ b/Filtration/WindsorInstallers/ViewModelsInstaller.cs @@ -0,0 +1,36 @@ +using Castle.Facilities.TypedFactory; +using Castle.MicroKernel.Registration; +using Castle.MicroKernel.SubSystems.Configuration; +using Castle.Windsor; +using Filtration.ViewModels; + +namespace Filtration.WindsorInstallers +{ + public class ViewModelsInstaller : IWindsorInstaller + { + public void Install(IWindsorContainer container, IConfigurationStore store) + { + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Transient); + + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Transient); + + container.Register( + Component.For() + .ImplementedBy() + .LifeStyle.Transient); + + container.AddFacility(); + container.Register( + Component.For().AsFactory()); + + container.Register( + Component.For().AsFactory()); + } + } +} diff --git a/Filtration/WindsorInstallers/ViewsInstaller.cs b/Filtration/WindsorInstallers/ViewsInstaller.cs new file mode 100644 index 0000000..4d03f10 --- /dev/null +++ b/Filtration/WindsorInstallers/ViewsInstaller.cs @@ -0,0 +1,18 @@ +using Castle.MicroKernel.Registration; +using Castle.MicroKernel.SubSystems.Configuration; +using Castle.Windsor; +using Filtration.Views; + +namespace Filtration.WindsorInstallers +{ + public class ViewsInstaller : IWindsorInstaller + { + public void Install(IWindsorContainer container, IConfigurationStore store) + { + container.Register(Component + .For() + .ImplementedBy() + .LifeStyle.Transient); + } + } +} diff --git a/Filtration/packages.config b/Filtration/packages.config new file mode 100644 index 0000000..defa11d --- /dev/null +++ b/Filtration/packages.config @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file