Changeset - d036e3a47323
[Not reviewed]
Merge default
0 5 0
Tess Snider (Malkyne) - 3 years ago 2021-07-24 10:27:30
this@malkyne.org
Merge
5 files changed with 51 insertions and 13 deletions:
0 comments (0 inline, 0 general)
.hgtags
Show inline comments
 
f419334a476f33b0b93cbd02eba7a44f08f89733 R_T10_1.0
 
99f3d61e20d74cac591cb81f69afc7be0eae0a59 R_T10_1.1
 
d7b40cfbe0f3d20f25a52944cf8c0f1ecdc81471 R_T10_1.2
 
6919080271d55f3f138660bb37dccd116249d13e R_T10_1.3
 
e7cc8ddcbf589086578134ec292ad8b7c739237c R_T10_1.4
DesertPaintCodex.csproj
Show inline comments
 
<Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
 
    <OutputType>WinExe</OutputType>
 
    <TargetFramework>net5.0</TargetFramework>
 
    <Nullable>enable</Nullable>
 
    <ApplicationIcon>Assets\desert_paint_codex_icon.ico</ApplicationIcon>
 
    <StartupObject>DesertPaintCodex.Program</StartupObject>
 
    <Platforms>AnyCPU;x64</Platforms>
 
    <Version>1.4.0</Version>
 
  </PropertyGroup>
 
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
 
    <PlatformTarget>x64</PlatformTarget>
 
  </PropertyGroup>
 
  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
 
    <PlatformTarget>x64</PlatformTarget>
 
  </PropertyGroup>
 
  <ItemGroup>
 
    <Folder Include="Models\" />
 
    <AvaloniaResource Include="Assets\**" />
 
    <None Remove="CodexTheme.xaml" />
 
    <None Update="Data\renames.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
  </ItemGroup>
 
  <ItemGroup>
 
    <PackageReference Include="Avalonia" Version="0.10.999-cibuild0014036-beta" />
 
    <PackageReference Include="Avalonia.Desktop" Version="0.10.999-cibuild0014036-beta" />
 
    <PackageReference Include="Avalonia.Diagnostics" Version="0.10.999-cibuild0014036-beta" />
 
    <PackageReference Include="Avalonia.ReactiveUI" Version="0.10.999-cibuild0014036-beta" />
 
    <PackageReference Include="System.Reactive" Version="5.0.0" />
 
  </ItemGroup>
 
  <ItemGroup>
 
    <None Update="Data\ingredients.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
    <None Update="Data\colors.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
    <None Update="Data\template\clips.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
    <None Update="Data\template\dp_reactions.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
    <None Update="Data\template\ingredients.txt">
 
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
 
    </None>
 
  </ItemGroup>
 
</Project>
Models/PlayerProfile.cs
Show inline comments
 
using System;
 
using System.IO;
 
using System.IO.Compression;
 
using System.Collections.Generic;
 
using System.Diagnostics;
 
using System.Text.RegularExpressions;
 
using DesertPaintCodex.Util;
 
using DesertPaintCodex.Services;
 

	
 
namespace DesertPaintCodex.Models
 
{
 
    public class PlayerProfile
 
    {
 
        private const string PaintRecipeFile = "dp_recipes.txt";
 
        private const string RibbonRecipeFile = "dp_ribbons.txt";
 
        
 
        private readonly string _reactFile;
 
        private readonly string _settingsFile;
 
        private readonly string _clipFile;
 

	
 
        private static readonly Regex _recipeHeaderRegex     = new(@"^--- Recipe: (?<colorname>(\w*\s)*\w+)\s*");
 
        private static readonly Regex _recipeIngredientRegex = new(@"(?<ingredient>(\w+\s)?\w+)\s*\|\s*(?<quantity>\d+)\s*");
 
        
 
        private Settings ProfileSettings { get; } = new();
 

	
 
        public string Directory { get; }
 

	
 
        public string Name { get; private set; }
 
        
 
        public ReactionSet Reactions { get; } = new();
 

	
 
        public Dictionary<string, Dictionary<string, ClipType>> Clippers { get; } = new();
 

	
 
        public string ReagentFile { get; }
 

	
 
        public Dictionary<string, PaintRecipe> Recipes { get; } = new();
 

	
 
        public Dictionary<string, PaintRecipe> RibbonRecipes { get; } = new();
 

	
 
        public int RecipeCount
 
        {
 
            get
 
            {
 
                int count = 0;
 
                foreach (PaintRecipe recipe in Recipes.Values)
 
                {
 
                    if (recipe.IsValidForConcentration(PaintRecipe.PaintRecipeMinConcentration))
 
                    {
 
                        ++count;
 
                    }
 
                }
 
                return count;
 
            }
 
        }
 

	
 
        public int RibbonCount
 
        {
 
            get
 
            {
 
                int count = 0;
 
                foreach (PaintRecipe recipe in RibbonRecipes.Values)
 
                {
 
                    if (recipe.IsValidForConcentration(PaintRecipe.RibbonRecipeMinConcentration))
 
                    {
 
                        ++count;
 
                    }
 
                }
 
                return count;
 
            }
 
        }
 
        
 
        public PlayerProfile(string name, string directory)
 
        {
 
            Name          = name;
 
            Directory     = directory;
 
            _reactFile    = Path.Combine(directory, "dp_reactions.txt");
 
            ReagentFile   = Path.Combine(directory, "ingredients.txt");
 
            _settingsFile = Path.Combine(directory, "settings");
 
            _clipFile     = Path.Combine(directory, "clips.txt");
 
            foreach (PaintColor color in PaletteService.Colors)
 
            {
 
                Recipes.Add(color.Name, new PaintRecipe());
 
            }
 
            foreach (PaintColor color in PaletteService.Colors)
 
            {
 
                RibbonRecipes.Add(color.Name, new PaintRecipe());
 
            }
 
        }
 

	
 
        public bool Initialize()
 
        {
 
            // Copy template files into new directory.
 
            string? templatePath = FileUtils.FindApplicationResourceDirectory("template");
 

	
 
            if (templatePath == null)
 
            {
 
                return false;
 
            }
 

	
 
            // Create new directory.
 
            System.IO.Directory.CreateDirectory(Directory);
 

	
 
            DirectoryInfo di = new(templatePath);
 
            FileInfo[] templateFiles = di.GetFiles();
 

	
 
            foreach (FileInfo file in templateFiles)
 
            {
 
                string destFile = Path.Combine(Directory, file.Name);
 
                File.Copy(file.FullName, destFile, true);
 
                if (!File.Exists(destFile)) return false;
 
            }
 
            return true;
 
        }
 

	
 
        private static void WriteReaction(TextWriter writer, string reagent1, string reagent2, string r, string g, string b)
 
        {
 
            writer.Write(reagent1);
 
            writer.Write(" ");
 
            writer.Write(reagent2);
 
            writer.Write(" ");
 
            writer.Write(r);
 
            writer.Write(" ");
 
            writer.Write(g);
 
            writer.Write(" ");
 
            writer.WriteLine(b);
 
        }
 

	
 
        public static void ConvertFromPP(string ppFile, string dpFile)
 
        {
 
            using StreamReader reader = new(ppFile);
 
            using StreamWriter writer = new(dpFile, false);
 
            string?            line;
 
            string? line;
 
            while ((line = reader.ReadLine()) != null)
 
            {
 
                string[] tokens = line.Split('|');
 
                //if ((tokens.Length > 0) && (tokens [0] != "//"))
 
                if ((tokens.Length == 5) && (tokens[0].Trim() != "//"))
 
                {
 
                    string reagent1  = tokens[0].Trim();
 
                    string reagent2  = tokens[1].Trim();
 
                    string reagent1Name  = tokens[0].Trim();
 
                    string reagent2Name  = tokens[1].Trim();
 
                    string colorCode = tokens[2].Trim();
 
                    string change1   = tokens[3].Trim();
 
                    string change2   = tokens[4].Trim();
 

	
 
                    Reagent reagent1 = ReagentService.GetReagent(reagent1Name);
 
                    Reagent reagent2 = ReagentService.GetReagent(reagent2Name);
 

	
 
                    if (reagent1 == null || reagent2 == null) continue;
 

	
 
                    bool hasChange1 = int.TryParse(change1, out _);
 
                    bool hasChange2 = int.TryParse(change2, out _);
 

	
 
                    // Write reaction.
 
                    switch (colorCode)
 
                    {
 
                        case "W":
 
                            WriteReaction(writer, reagent1, reagent2, change1, change1, change1);
 
                            WriteReaction(writer, reagent2, reagent1, change2, change2, change2);
 
                            if (hasChange1)
 
                            {
 
                                WriteReaction(writer, reagent1Name, reagent2Name, change1, change1, change1);
 
                            }
 
                            if (hasChange2)
 
                            {
 
                                WriteReaction(writer, reagent2Name, reagent1Name, change2, change2, change2);
 
                            }
 
                            break;
 
                        case "R":
 
                            WriteReaction(writer, reagent1, reagent2, change1, "0", "0");
 
                            WriteReaction(writer, reagent2, reagent1, change2, "0", "0");
 
                            if (hasChange1)
 
                            {
 
                                WriteReaction(writer, reagent1Name, reagent2Name, change1, "0", "0");
 
                            }
 
                            if (hasChange2)
 
                            {
 
                                WriteReaction(writer, reagent2Name, reagent1Name, change2, "0", "0");
 
                            }
 
                            break;
 
                        case "G":
 
                            WriteReaction(writer, reagent1, reagent2, "0", change1, "0");
 
                            WriteReaction(writer, reagent2, reagent1, "0", change2, "0");
 
                            if (hasChange1)
 
                            {
 
                                WriteReaction(writer, reagent1Name, reagent2Name, "0", change1, "0");
 
                            }
 
                            if (hasChange2)
 
                            {
 
                                WriteReaction(writer, reagent2Name, reagent1Name, "0", change2, "0");
 
                            }
 
                            break;
 
                        case "B":
 
                            WriteReaction(writer, reagent1, reagent2, "0", "0", change1);
 
                            WriteReaction(writer, reagent2, reagent1, "0", "0", change2);
 
                            if (hasChange1)
 
                            {
 
                                WriteReaction(writer, reagent1Name, reagent2Name, "0", "0", change1);
 
                            }
 
                            if (hasChange2)
 
                            {
 
                                WriteReaction(writer, reagent2Name, reagent1Name, "0", "0", change2);
 
                            }
 
                            break;
 
                    }
 
                }
 
            }
 
        }
 

	
 
        public bool SaveToPP(string ppFile)
 
        {
 
            Reaction? reaction1, reaction2;
 
            using (StreamWriter writer = new(ppFile))
 
            {
 
                foreach (string reagentName1 in ReagentService.Names)
 
                {
 
                    // TODO: could be more efficient by only iterating over the names after reagent1
 
                    foreach (string reagentName2 in ReagentService.Names)
 
                    {
 
                        if (reagentName1.Equals(reagentName2)) continue;
 

	
 
                        Reagent reagent1 = ReagentService.GetReagent(reagentName1);
 
                        Reagent reagent2 = ReagentService.GetReagent(reagentName2);
 
                        reaction1 = Reactions.Find(reagent1, reagent2);
 
                        
 
                        if (reaction1 is not {Exported: false}) continue;
 
                        
 
                        reaction2 = Reactions.Find(reagent2, reagent1);
 
                        
 
                        if (reaction2 == null) continue;
 
                        
 
                        writer.Write(reagent1.PracticalPaintName + " | " + reagent2.PracticalPaintName + " | ");
 
                        if ((Math.Abs(reaction1.Red) > Math.Abs(reaction1.Green)) ||
 
                            (Math.Abs(reaction2.Red) > Math.Abs(reaction2.Green)))
 
                        {
 
                            writer.WriteLine("R | " + reaction1.Red + " | " + reaction2.Red);
 
                        }
 
                        else if ((Math.Abs(reaction1.Green) > Math.Abs(reaction1.Red)) ||
 
                            (Math.Abs(reaction2.Green) > Math.Abs(reaction2.Red)))
 
                        {
 
                            writer.WriteLine("G | " + reaction1.Green + " | " + reaction2.Green);
 
                        }
 
                        else if ((Math.Abs(reaction1.Blue) > Math.Abs(reaction1.Red)) ||
 
                            (Math.Abs(reaction2.Blue) > Math.Abs(reaction2.Red)))
 
                        {
 
                            writer.WriteLine("B | " + reaction1.Blue + " | " + reaction2.Blue);
 
                        }
 
                        else
 
                        {
 
                            writer.WriteLine("W | " + reaction1.Red + " | " + reaction2.Red);
 
                        }
 
                        reaction1.Exported = true;
 
                        reaction2.Exported = true;
 
                    }
 
                }
 
            }
 

	
 
            // Clear Exported flags.
 
            foreach (string reagentName1 in ReagentService.Names)
 
            {
 
                // TODO: could be more efficient by only iterating over the names after reagent1
 
                foreach (string reagentName2 in ReagentService.Names)
 
                {
 
                    if (reagentName1.Equals(reagentName2))
 
                    {
 
                        continue;
 
                    }
 
                    Reagent reagent1 = ReagentService.GetReagent(reagentName1);
 
                    Reagent reagent2 = ReagentService.GetReagent(reagentName2);
 
                    reaction1 = Reactions.Find(reagent1, reagent2);
 
                    if (reaction1 != null)
 
                    {
 
                        reaction1.Exported = false;
 
                    }
 
                    reaction2 = Reactions.Find(reagent2, reagent1);
 
                    if (reaction2 != null)
 
                    {
 
                        reaction2.Exported = false;
 
                    }
 
                }
 
            }
 
            return true;
 
        }
 

	
 
        public void ImportFromPP(string reactionsFile)
 
        {
 
            // Convert old file.
 
            ConvertFromPP(reactionsFile, _reactFile);
 
            try
 
            {
 
                // If there is an ingredients file, move it in.
 
                string importDir = Path.GetDirectoryName(reactionsFile) ?? "";
 
                File.Copy(
 
                    Path.Combine(importDir, "ingredients.txt"),
 
                    Path.Combine(Directory, "ingredients.txt"),
 
                    true);
 
            }
 
            catch (Exception)
 
            {
 
                // If there is no ingredients file, we don't really care.	
 
            }
 
        }
 

	
 
        public void Import(string file)
 
        {
 
            if (!File.Exists(file))
 
            {
 
                Debug.WriteLine("Import file does not exist: " + file);
 
                // TODO: Show message dialog.
 
            }
 
            ZipFile.ExtractToDirectory(file, Directory, true);
 
        }
 

	
 
        public void Export(string file)
 
        {
 
            ZipFile.CreateFromDirectory(Directory, file);
 
        }
 

	
 
        public bool Load()
 
        {
 
            string? line;
 
            ProfileSettings.Reset();
 
            ProfileSettings.Load(_settingsFile);
 
            Reactions.Clear();
 
            if (File.Exists(ReagentFile))
 
            {
 
                ReagentService.LoadProfileReagents(ReagentFile);
 
            }
 
            else
 
            {
 
                return false;
 
            }
 
            ReagentService.InitializeReactions(Reactions);
 
            if (!File.Exists(_reactFile))
 
            {
 
                return false;
 
            }
 
            using (StreamReader reader = new(_reactFile))
 
            {
 
                while ((line = reader.ReadLine()) != null)
 
                {
 
                    string[] tokens = line.Split(' ');
 
                    if (tokens.Length == 5)
 
                    {
 
                        Reagent reagent1 = ReagentService.GetReagent(tokens[0].Trim());
 
                        Reagent reagent2 = ReagentService.GetReagent(tokens[1].Trim());
 
                        Reaction reaction = new(
 
                            int.Parse(tokens[2].Trim()),
 
                            int.Parse(tokens[3].Trim()),
 
                            int.Parse(tokens[4].Trim())
 
                            );
 
                        Reactions.Set(reagent1, reagent2, reaction);
 
                    }
 
                }
 
            }
 

	
 
            if (!File.Exists(_clipFile)) return true;
 
            {
 
                using StreamReader reader = new(_clipFile);
 
                while ((line = reader.ReadLine()) != null)
 
                {
 
                    string[] tokens = line.Split(' ');
 
                    
 
                    if (tokens.Length != 3) continue;
 
                    
 
                    string reagent1 = tokens[0].Trim();
 
                    if (!Clippers.ContainsKey(reagent1))
 
                    {
 
                        Clippers.Add(reagent1, new Dictionary<string, ClipType>());
 
                    }
 
                    Clippers[reagent1][tokens[1].Trim()] = (ClipType)int.Parse(tokens[2].Trim());
 
                }
 
            }
 

	
 
            return true;
 
        }
 

	
 
        public void Save()
 
        {
 
            ProfileSettings.Save(_settingsFile);
 
            Reaction? reaction;
 
            using (StreamWriter writer = new(_reactFile, false))
 
            {
 
                foreach (string reagentName1 in ReagentService.Names)
 
                {
 
                    // TODO: could be more efficient by only iterating over the names after reagent1
 
                    foreach (string reagentName2 in ReagentService.Names)
 
                    {
 
                        if (reagentName1.Equals(reagentName2))
 
                        {
 
                            continue;
 
                        }
 
                        Reagent reagent1 = ReagentService.GetReagent(reagentName1);
 
                        Reagent reagent2 = ReagentService.GetReagent(reagentName2);
 
                        reaction = Reactions.Find(reagent1, reagent2);
 
                        if (reaction != null)
 
                        {
 
                            writer.WriteLine(reagent1.PracticalPaintName + " " + reagent2.PracticalPaintName + " " +
 
                            reaction.Red + " " + reaction.Green + " " + reaction.Blue);
 
                        }
 
                    }
 
                }
 
            }
 
            using (StreamWriter writer = new(_clipFile, false))
 
            {
 
                foreach (var item1 in Clippers)
 
                {
 
                    foreach (var item2 in item1.Value)
 
                    {
 
                        if (item2.Value == ClipType.None) continue;
 
                        writer.WriteLine(item1.Key + " " + item2.Key + " " + (int)item2.Value);
 
                    }
 
                }
 
            }
 
        }
 

	
 
        public ClipType PairClipStatus(Reagent reagent1, Reagent reagent2)
 
        {
 
            if (Clippers.TryGetValue(reagent1.PracticalPaintName, out var item1))
 
            {
 
                if (item1.TryGetValue(reagent2.PracticalPaintName, out var clipType))
 
                {
 
                    return clipType;
 
                }
 
            }
 
            return ClipType.None;
 
        }
 

	
 
        public void SetPairClipStatus(Reagent reagent1, Reagent reagent2, ClipType clip)
 
        {
 
            if (Clippers.TryGetValue(reagent1.PracticalPaintName, out var item1))
 
            {
 
                if (item1.TryGetValue(reagent2.PracticalPaintName, out var clipType))
 
                {
 
                    if (clipType == clip) return;
 
                }
 
            }
 
            else
 
            {
 
                item1 = new Dictionary<string, ClipType>();
 
                Clippers.Add(reagent1.PracticalPaintName, item1);
 
            }
 

	
 
            item1[reagent2.PracticalPaintName] = clip;
 
            Save();
 
        }
 

	
 
        private void LoadRecipes(Dictionary<string, PaintRecipe> recipeDict, string filename, uint concentration)
 
        {
 
            foreach (PaintRecipe recipe in recipeDict.Values)
 
            {
 
                recipe.Clear();
 
            }
 
            string      recipeFile         = Path.Combine(Directory, filename);
 
            bool        inRecipe           = false;
 
            PaintRecipe testRecipe         = new();
 
            string?     currentRecipeColor = null;
 
            
 
            if (!File.Exists(recipeFile)) return;
 
            
 
            using StreamReader reader = new(recipeFile);
 
            
 
            string? line;
 
            while ((line = reader.ReadLine()) != null)
 
            {
 
                Match match = _recipeHeaderRegex.Match(line);
 
                if (match.Success)
 
                {
 
                    // Store previous recipe.
 
                    if ((currentRecipeColor != null) && testRecipe.IsValidForConcentration(concentration))
 
                    {
 
                        SetRecipe(currentRecipeColor, testRecipe);
 
                    }
 
                    testRecipe.Clear();
 
                    currentRecipeColor = match.Groups["colorname"].Value;
 
                    inRecipe  = true;
 
                }
 
                else if (inRecipe)
 
                {
 
                    match = _recipeIngredientRegex.Match(line);
 
                    
 
                    if (!match.Success) continue;
 
                    
 
                    string ingredient = match.Groups["ingredient"].Value;
 
                    uint   quantity   = uint.Parse(match.Groups["quantity"].Value);
 
                    
 
                    testRecipe.AddReagent(ingredient, quantity);
 
                }
 
            }
 

	
 
            if (!inRecipe || (currentRecipeColor == null)) return;
 
            
 
            // Store final recipe.
 
            if (testRecipe.IsValidForConcentration(concentration))
 
            {
 
                SetRecipe(currentRecipeColor, testRecipe);
 
            }
 
        }
 

	
 
        private void SaveRecipes(Dictionary<string, PaintRecipe> recipeDict, string filename)
 
        {
 
            string recipeFile = Path.Combine(Directory, filename);
 
            
 
            using StreamWriter writer = new(recipeFile, false);
 
            
 
            foreach (KeyValuePair<string, PaintRecipe> pair in recipeDict)
 
            {
 
                writer.WriteLine("--- Recipe: {0}", pair.Key);
 
                foreach (PaintRecipe.ReagentQuantity ingredient in pair.Value.Reagents)
 
                {
 
                    writer.WriteLine("{0,-14} | {1}", ingredient.Name, ingredient.Quantity);
 
                }
 
            }
 
        }
 

	
 
        private void DeleteRecipes(Dictionary<string, PaintRecipe> recipeDict, string filename)
 
        {
 
            string recipeFile = Path.Combine(Directory, filename);
 
            
 
            File.Delete(recipeFile);
 
            recipeDict.Clear();
 
        }
 

	
 
        public void LoadRecipes()
 
        {
 
            LoadRecipes(Recipes, PaintRecipeFile, PaintRecipe.PaintRecipeMinConcentration);
 
            LoadRecipes(RibbonRecipes, RibbonRecipeFile, PaintRecipe.RibbonRecipeMinConcentration);
 
        }
 

	
 
        public void SaveRecipes()
 
        {
 
            SaveRecipes(Recipes, PaintRecipeFile);
 
            SaveRecipes(RibbonRecipes, RibbonRecipeFile);
 
        }
 

	
 
        public void ClearRecipes()
 
        {
 
            DeleteRecipes(Recipes, PaintRecipeFile);
 
            DeleteRecipes(RibbonRecipes, RibbonRecipeFile);
 
        }
 

	
 
        public void ClearPaintRecipes()
 
        {
 
            DeleteRecipes(Recipes, PaintRecipeFile);
 
        }
 

	
 
        public void ClearRibbonRecipes()
 
        {
 
            DeleteRecipes(RibbonRecipes, RibbonRecipeFile);
 
        }
 

	
 
        public void ExportWikiRecipes(string file)
 
        {
 
            StreamWriter writer = new(file);
 
            ExportWikiFormat(writer, Recipes);
 
        }
 

	
 
        public void ExportWikiRibbons(string file)
 
        {
 
            StreamWriter writer = new StreamWriter(file);
 
            ExportWikiFormat(writer, this.RibbonRecipes);
 
        }
 

	
 
        public void ExportWikiRecipes(TextWriter writer)
 
        {
 
            ExportWikiFormat(writer, this.Recipes);
 
        }
 

	
 
        public void ExportWikiRibbons(TextWriter writer)
 
        {
 
            ExportWikiFormat(writer, this.RibbonRecipes);
 
        }
 
        
 
        public static void ExportWikiFormat(TextWriter writer, Dictionary<string, PaintRecipe> recipeDict)
 
        {
 
            using (writer)
 
            {
 
                writer.WriteLine("{| class='wikitable sortable' border=\"1\" style=\"background-color:#DEB887;\"");
 
                writer.WriteLine("! Color !! Recipe !! Missing Reactions? || Verified");
 
                foreach (PaintColor color in PaletteService.Colors)
 
                {
 
                    writer.WriteLine("|-");
 
                    string colorLine = "| ";
 
                    colorLine += "style=\"font-weight: bold; background-color: #" + color.Red.ToString("X2") + color.Green.ToString("X2") + color.Blue.ToString("X2") + ";";
 
                    
 
                    if (color.UseWhiteText)
 
                    {
 
                        // dark color gets light text
 
                        colorLine += " color: #FFFFFF;";
 
                    }
 
                    else
 
                    {
 
                        colorLine += "color: #000000;";
 
                    }
 
                    colorLine += "\" | " + color.Name + " || ";
 
                    if (recipeDict.TryGetValue(color.Name, out PaintRecipe? recipe))
 
                    {
 
                        foreach (PaintRecipe.ReagentQuantity ingredient in recipe.Reagents)
 
                        {
 
                            colorLine += " " + ingredient;
 
                        }
 
                    }
 
                    else
 
                    {
 
                        // no recipe
 
                    }
 
                    colorLine += " || ";
 

	
 
                    if (recipe == null)
 
                    {
 
                        colorLine += "?";
 
                    }
 
                    else if (recipe.HasMissingReactions())
 
                    {
 
                        colorLine += "Y";
 
                    }
 
                    else
 
                    {
 
                        colorLine += "N";
 
                    }
 

	
 
                    colorLine += " || N";
 
                    writer.WriteLine(colorLine);
 
                }
 
                writer.WriteLine("|}");
 
            }
 
        }
 

	
 
        public Reaction? FindReaction(Reagent? reagent1, Reagent? reagent2)
 
        {
 
            if ((reagent1 == null) || (reagent2 == null)) return null;
 
            return Reactions.Find(reagent1, reagent2);
 
        }
 

	
 
        public void SetReaction(Reagent reagent1, Reagent reagent2, Reaction reaction)
 
        {
 
            Reactions.Set(reagent1, reagent2, reaction);
 
        }
 

	
 
        public void ClearReaction(Reagent reagent1, Reagent reagent2)
 
        {
 
            Reactions.Remove(reagent1, reagent2);
 
        }
 

	
 
        public void SetRecipe(PaintRecipe recipe)
 
        {
 
            SetRecipe(PaletteService.FindNearest(recipe.ReactedColor), recipe);
 
        }
 

	
 
        public void SetRecipe(string colorName, PaintRecipe recipe)
 
        {
 
            if (Recipes.TryGetValue(colorName, out PaintRecipe? profileRecipe))
 
            {
 
                profileRecipe.CopyFrom(recipe);
 
            }
 
            else
 
            {
 
                Recipes.Add(colorName, new PaintRecipe(recipe));
 
            }
 
        }
 

	
 
        public void SetRibbonRecipe(PaintRecipe recipe)
 
        {
 
            SetRibbonRecipe(PaletteService.FindNearest(recipe.ReactedColor), recipe);
 
        }
 

	
 
        public void SetRibbonRecipe(string colorName, PaintRecipe recipe)
 
        {
 
            if (RibbonRecipes.TryGetValue(colorName, out PaintRecipe? profileRecipe))
 
            {
 
                profileRecipe.CopyFrom(recipe);
 
            }
 
            else
 
            {
 
                RibbonRecipes.Add(colorName, new PaintRecipe(recipe));
 
            }
 
        }
 
    }
 
}
Properties/PublishProfiles/Win64 Self-Contained.pubxml
Show inline comments
 
<?xml version="1.0" encoding="utf-8"?>
 
<!--
 
https://go.microsoft.com/fwlink/?LinkID=208121. 
 
-->
 
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 
  <PropertyGroup>
 
    <Configuration>Release</Configuration>
 
    <Platform>x64</Platform>
 
    <PublishDir>publish\win-x64</PublishDir>
 
    <PublishDir>publish\win-x64\DesertPaintCodex</PublishDir>
 
    <PublishProtocol>FileSystem</PublishProtocol>
 
    <TargetFramework>net5.0</TargetFramework>
 
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
 
    <SelfContained>true</SelfContained>
 
    <PublishSingleFile>True</PublishSingleFile>
 
    <PublishReadyToRun>False</PublishReadyToRun>
 
    <PublishTrimmed>False</PublishTrimmed>
 
  </PropertyGroup>
 
</Project>
...
 
\ No newline at end of file
ViewModels/ExperimentLogViewModel.cs
Show inline comments
 
using System;
 
using System.Collections.Generic;
 
using System.Collections.ObjectModel;
 
using System.Diagnostics;
 
using ReactiveUI;
 
using DesertPaintCodex.Services;
 
using DesertPaintCodex.Models;
 
using DesertPaintCodex.Util;
 
using DynamicData.Binding;
 

	
 
namespace DesertPaintCodex.ViewModels
 
{
 
    public class ExperimentLogViewModel : ViewModelBase
 
    {
 
        public ObservableCollection<ReactionTest> RemainingTests { get; } = new();
 
        public ObservableCollection<ReactionTest> CompletedTests { get; } = new();
 

	
 
        private int _selectedList = 0;
 
        public int SelectedList { get => _selectedList; private set => this.RaiseAndSetIfChanged(ref _selectedList, value); }
 

	
 
        private int _selectedRemainingTest = 0;
 
        public int SelectedRemainingTest { get => _selectedRemainingTest; private set => this.RaiseAndSetIfChanged(ref _selectedRemainingTest, value); }
 
        
 
        private int _selectedCompletedTest = 0;
 
        public int SelectedCompletedTest { get => _selectedCompletedTest; private set => this.RaiseAndSetIfChanged(ref _selectedCompletedTest, value); }
 

	
 
        private ReactionTestViewModel _testView;
 
        public ReactionTestViewModel TestView { get => _testView; private set => this.RaiseAndSetIfChanged(ref _testView, value); }
 
        
 
        private readonly List<ObservableCollection<ReactionTest>> _testLists = new();
 

	
 
        public ExperimentLogViewModel()
 
        {
 
            PlayerProfile? profile = ProfileManager.CurrentProfile;
 
            Debug.Assert(profile != null);
 
            
 
            _testLists.Add(RemainingTests);
 
            _testLists.Add(CompletedTests);
 
            
 
            ReactionTestService.PopulateRemainingTests(RemainingTests);
 
            ReactionTestService.PopulateCompletedTests(CompletedTests);
 
            
 
            // If we have no remaining tests, switch to the completed list.
 
            _testView = new ReactionTestViewModel();
 
            if (RemainingTests.Count > 0)
 
            {
 
                SelectedList = 0;
 
                _testView.ReactionTest = RemainingTests[0];
 
            }
 
            else
 
            {
 
                SelectedList = 1;
 
                _testView.ReactionTest = CompletedTests[0];
 
            }
 

	
 
            this.WhenAnyPropertyChanged(nameof(SelectedList), nameof(SelectedRemainingTest), nameof(SelectedCompletedTest))
 
                .Subscribe(_ => _testView.ReactionTest = GetSelectedReactionTest());
 

	
 
            _testView.ReactionTest = GetSelectedReactionTest();
 

	
 
            TestView.SaveReaction.Subscribe(_ => OnSaveReaction());
 
            TestView.ClearReaction.Subscribe(_ => OnClearReaction());
 
            TestView.FinalizeTestResults.Subscribe(_ => OnReactionResults());
 
        }
 

	
 
        private ReactionTest GetSelectedReactionTest()
 
        {
 
            int itemIndex = (SelectedList == 0) ? SelectedRemainingTest : SelectedCompletedTest;
 
            if (itemIndex < 0) return Constants.StubReactionTest;
 
            var list = _testLists[SelectedList];
 
            return (itemIndex >= list.Count) ? Constants.StubReactionTest : list[itemIndex];
 
        }
 
        
 
        #region Command Handlers
 

	
 
        private void OnSaveReaction()
 
        {
 
            ReactionTest test = TestView.ReactionTest;
 
            int oldPos = RemainingTests.IndexOf(test);
 
            
 
            // Move test to Completed Tests.
 
            InsertTestIntoList(test, CompletedTests);
 
            RemainingTests.RemoveAt(oldPos);
 
            
 
            // Select next Remaining Test.
 
            if (RemainingTests.Count == 0) return;
 
            SelectedRemainingTest = (oldPos == RemainingTests.Count) ? RemainingTests.Count - 1 : oldPos;
 

	
 
            // If we have just inserted our first completed test, select it.
 
            if (CompletedTests.Count == 1)
 
            {
 
                 SelectedCompletedTest = 0;
 
            }
 
        }
 

	
 
        private void OnClearReaction()
 
        {
 
            int oldPos = 0;
 
            ReactionTest test = TestView.ReactionTest;
 
            if (RemainingTests.Contains(test))
 
            {
 
                SelectedRemainingTest = ResortTestInList(test, RemainingTests);
 
                return;
 
            }
 

	
 
            oldPos = CompletedTests.IndexOf(test);
 
            
 
            // Move test to Remaining Tests.
 
            InsertTestIntoList(test, RemainingTests);
 
            CompletedTests.RemoveAt(oldPos);
 
            
 
            // Select next Completed Test.
 
            if (CompletedTests.Count == 0) return;
 
            SelectedCompletedTest = (oldPos == CompletedTests.Count) ? CompletedTests.Count - 1 : oldPos;
 

	
 
            // If we have just inserted our first remaining test, select it.
 
            if (RemainingTests.Count == 1)
 
            {
 
                SelectedRemainingTest = 0;
 
            }
 
        }
 

	
 
        private void OnReactionResults()
 
        {
 
            int newPos = ResortTestInList(TestView.ReactionTest, RemainingTests);
 
            if (newPos != SelectedRemainingTest)
 
            {
 
                SelectedRemainingTest = newPos;
 
            }
 
        }
 
        
 
        #endregion
 

	
 
        private static int ResortTestInList(ReactionTest test, IList<ReactionTest> list)
 
        {
 
            int oldPos = list.IndexOf(test);
 

	
 
            if (((oldPos > 0) || (test.CompareTo(list[oldPos - 1]) > 0))
 
            // If the item is first in the list or the previous item is valued < the test
 
            // And either the item is last in the list or the next item in the list is valued > than the item
 
            // Don't move it
 
            if (((oldPos == 0) || (test.CompareTo(list[oldPos - 1]) > 0))
 
                && ((oldPos == list.Count - 1) || (test.CompareTo(list[oldPos + 1]) < 0))) return oldPos; // No need to move.
 

	
 
            list.RemoveAt(oldPos);
 
            return InsertTestIntoList(test, list);
 
        }
 
        
 
        private static int InsertTestIntoList(ReactionTest test, IList<ReactionTest> list)
 
        {
 
            int i;
 
            for (i = 0; i < list.Count; i++)
 
            {
 
                if (test.CompareTo(list[i]) < 0) break;
 
            }
 
            list.Insert(i, test);
 
            return i;
 
        }
 
    }
 
}
0 comments (0 inline, 0 general)