diff --git a/DesertPaintLab.csproj b/DesertPaintLab.csproj
--- a/DesertPaintLab.csproj
+++ b/DesertPaintLab.csproj
@@ -75,6 +75,12 @@
+
+
+
+
+
+
diff --git a/MainWindow.cs b/MainWindow.cs
--- a/MainWindow.cs
+++ b/MainWindow.cs
@@ -29,15 +29,6 @@ using DesertPaintLab;
public partial class MainWindow : Gtk.Window
{
- const int colorTolerance = 2;
-
- int swatchHeight = 24;
- int colorBarWidth = 306;
- int redBarSpacing = 32;
- int greenBarSpacing = 42;
- int blueBarSpacing = 52;
-
-
bool unsavedData = false;
bool shouldShutDown = false;
string appDataPath;
@@ -94,6 +85,9 @@ public partial class MainWindow : Gtk.Wi
string colorsPath = FileUtils.FindApplicationResourceFile("colors.txt");
Palette.Load(colorsPath);
+
+ string ingredientsPath = FileUtils.FindApplicationResourceFile("ingredients.txt");
+ ReagentManager.Load(ingredientsPath);
Build();
@@ -134,11 +128,7 @@ public partial class MainWindow : Gtk.Wi
screenBuffer = new Gdk.Pixbuf(Gdk.Colorspace.Rgb, false, 8, screenWidth, screenHeight);
- swatchHeight *= pixelMultiplier;
- colorBarWidth *= pixelMultiplier;
- redBarSpacing *= pixelMultiplier;
- greenBarSpacing *= pixelMultiplier;
- blueBarSpacing *= pixelMultiplier;
+ ReactionRecorder.SetPixelMultiplier(pixelMultiplier);
if (!OpenProfile())
{
@@ -345,6 +335,7 @@ public partial class MainWindow : Gtk.Wi
{
profile.Load();
PopulateDropDowns();
+ recipe.Reactions = profile.Reactions;
}
return profileSelected;
@@ -493,13 +484,13 @@ public partial class MainWindow : Gtk.Wi
}
}
}
- recipe.ComputeBaseColor(ref expectedColor);
+ expectedColor.Set(recipe.BaseColor);
unmodifiedSwatch.Color = expectedColor;
//SetExpectedColor(recipeColor.Red, recipeColor.Green, recipeColor.Blue);
if (reactionKnown)
{
- recipe.ComputeReactedColor(profile, ref reactedColor);
+ reactedColor.Set(recipe.ReactedColor);
reactionSwatch.Color = reactedColor;
}
else
@@ -538,8 +529,9 @@ public partial class MainWindow : Gtk.Wi
// rootWindow.Colormap, 0, 0, 0, 0, screenWidth, screenHeight);
int stride = screenBuffer.Rowstride;
byte* pixBytes = (byte*)screenBuffer.Pixels;
+ int redPixelStart = -1;
- bool wasCaptured = ReactionRecorder.CaptureReaction(pixBytes, screenWidth, screenHeight, stride, ref reactedColor);
+ bool wasCaptured = ReactionRecorder.CaptureReaction(pixBytes, screenWidth, screenHeight, stride, ref reactedColor, ref redPixelStart);
if (!wasCaptured && enableDebugMenu)
{
// write out the screenshot
@@ -553,9 +545,29 @@ public partial class MainWindow : Gtk.Wi
} while (System.IO.File.Exists(filename));
screenBuffer.Save(filename, "png");
}
+ else
+ {
+ // convert to pixel offset instead of byte
+ int redPixelStartX = (redPixelStart % stride) / 3;
+ int redPixelStartY = (redPixelStart / stride);
+ // write out the screenshot
+ string screenshotDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
+ string filename;
+ int i = 0;
+ do
+ {
+ ++i;
+ filename = System.IO.Path.Combine(screenshotDir, String.Format("DesertPaintLab_Colormatch{0}.png", i));
+ } while (System.IO.File.Exists(filename));
+ int captureAreaWidth = Math.Min(64, screenWidth - redPixelStartX + 64);
+ int captureAreaHeight = Math.Min(64, screenWidth - redPixelStartY + 64);
+ Gdk.Pixbuf outPixBuf = new Gdk.Pixbuf(screenBuffer, Math.Max(0, redPixelStartX - 16), Math.Max(0, redPixelStartY - 16), captureAreaWidth, captureAreaHeight);
+ //screenBuffer.Save(filename, "png");
+ outPixBuf.Save(filename, "png");
+ }
//screenBuffer.Save("screenshot.png", "png");
- return !wasCaptured;
+ return wasCaptured;
}
protected virtual void OnDebugScreenshot(object sender, System.EventArgs e)
@@ -628,6 +640,7 @@ public partial class MainWindow : Gtk.Wi
MessageType.Error, ButtonsType.Ok,
"Pigment Lab dialog box NOT FOUND. Please ensure " +
"that there is an unobstructed view of the dialog " +
+ "and that your interface size is set to 'small' " +
"when you press the Capture button.");
md.Run();
@@ -681,6 +694,7 @@ public partial class MainWindow : Gtk.Wi
{
profile.Load();
PopulateDropDowns();
+ recipe.Reactions = profile.Reactions;
}
}
@@ -753,7 +767,16 @@ public partial class MainWindow : Gtk.Wi
protected void OnOpenRecipeGenerator(object sender, EventArgs e)
{
- throw new NotImplementedException();
+ RecipeGeneratorWindow win = new RecipeGeneratorWindow(profile);
+ win.Show();
+ //RecipeGenerator gen = new RecipeGenerator();
+ //gen.BeginRecipeGeneration(profile.Reactions, 15, 7);
+ //MessageDialog md = new MessageDialog(this,
+ // DialogFlags.DestroyWithParent,
+ // MessageType.Info, ButtonsType.Close,
+ // "Coming Soon!");
+ //md.Run();
+ //md.Destroy();
}
protected void OnShowReactionStatus(object sender, EventArgs e)
@@ -761,5 +784,11 @@ public partial class MainWindow : Gtk.Wi
ReactionStatusWindow win = new ReactionStatusWindow(profile);
win.Show();
}
+
+ protected void OnShowIngredients(object sender, EventArgs e)
+ {
+ ReagentWindow win = new ReagentWindow(profile);
+ win.Show();
+ }
}
diff --git a/PaintColor.cs b/PaintColor.cs
--- a/PaintColor.cs
+++ b/PaintColor.cs
@@ -83,6 +83,14 @@ namespace DesertPaintLab
this.green = green;
this.blue = blue;
}
+
+ public PaintColor(PaintColor other)
+ {
+ name = other.name;
+ red = other.red;
+ green = other.green;
+ blue = other.blue;
+ }
public int GetDistanceSquared(PaintColor otherColor)
{
@@ -97,6 +105,14 @@ namespace DesertPaintLab
green = 0;
blue = 0;
}
+
+ public void Set(PaintColor other)
+ {
+ this.red = other.red;
+ this.green = other.green;
+ this.blue = other.blue;
+ this.name = other.name;
+ }
public override string ToString()
{
diff --git a/PaintRecipe.cs b/PaintRecipe.cs
--- a/PaintRecipe.cs
+++ b/PaintRecipe.cs
@@ -1,4 +1,26 @@
-using System;
+/*
+ * Copyright (c) 2015, Jason Maltzen
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+using System;
using System.Collections.Generic;
namespace DesertPaintLab
@@ -12,48 +34,147 @@ namespace DesertPaintLab
public int b;
};
+ public class RecipeIngredient
+ {
+ public string name;
+ public uint quantity;
+
+ public RecipeIngredient(string name, uint quantity)
+ {
+ this.name = name;
+ this.quantity = quantity;
+ }
+ };
+
+ private List recipe = new List();
private List reagents = new List();
+ private bool dirty = false;
+ private PaintColor reactedColor = new PaintColor();
+ private PaintColor baseColor = new PaintColor();
+ private ReactionSet reactions;
+
public PaintRecipe()
{
}
+ public List Ingredients
+ {
+ get {
+ return recipe;
+ }
+ }
+
+ public ReactionSet Reactions
+ {
+ get
+ {
+ return reactions;
+ }
+ set
+ {
+ dirty = true;
+ reactions = value;
+ }
+ }
+
+ public PaintColor ReactedColor
+ {
+ get
+ {
+ if (dirty)
+ {
+ ComputeBaseColor();
+ ComputeReactedColor();
+ dirty = false;
+ }
+ return reactedColor;
+ }
+ }
+
+ public PaintColor BaseColor
+ {
+ get
+ {
+ if (dirty)
+ {
+ ComputeBaseColor();
+ ComputeReactedColor();
+ dirty = false;
+ }
+ return baseColor;
+ }
+ }
+
public void AddReagent(String reagentName)
{
+ AddReagent(reagentName, 1);
+ }
+
+ public void AddReagent(String reagentName, uint quantity)
+ {
+ if (quantity == 0)
+ {
+ return;
+ }
+ Reagent reagent = ReagentManager.GetReagent(reagentName);
+ if (reagent == null)
+ {
+ Console.WriteLine("ERROR: invalid reagent {0}", reagentName);
+ return;
+ }
+
+ RecipeIngredient ingredient = recipe.Find(x => x.name.Equals(reagentName));
+ if (ingredient != null)
+ {
+ if (!reagent.IsCatalyst)
+ {
+ ingredient.quantity += quantity;
+ dirty = true;
+ }
+ }
+ else
+ {
+ RecipeIngredient newIngredient = new RecipeIngredient(reagentName, reagent.IsCatalyst ? 1 : quantity);
+ recipe.Add(newIngredient);
+ dirty = true;
+ }
reagents.Add(reagentName);
}
public void Clear()
{
reagents.Clear();
+ recipe.Clear();
}
- byte CalculateColor(int baseSum, int pigmentCount, int reactSum)
+ byte CalculateColor(int baseSum, uint pigmentCount, int reactSum)
{
- return (byte)Math.Max(Math.Min(Math.Round((((float)baseSum / (float)pigmentCount) + (float)reactSum)), 255), 0);
+ // Changed to Math.Floor from Math.Round, since Round appears to be incorrect.
+ return (byte)Math.Max(Math.Min(Math.Floor((((float)baseSum / (float)pigmentCount) + (float)reactSum)), 255), 0);
}
// Compute the color including reactions based on the player's profile
- public void ComputeReactedColor(PlayerProfile profile, ref PaintColor paintColor)
+ private void ComputeReactedColor()
{
- RGB baseColor;
- baseColor.r = 0;
- baseColor.g = 0;
- baseColor.b = 0;
+ RGB baseRGB;
+ baseRGB.r = 0;
+ baseRGB.g = 0;
+ baseRGB.b = 0;
RGB reactionColor;
reactionColor.r = 0;
reactionColor.g = 0;
reactionColor.b = 0;
- int pigmentCount = 0;
- string prevReagent = null;
+ uint pigmentCount = 0;
// track visited reagents so the reaction is only applied once
SortedDictionary reagentSet = new SortedDictionary();
List prevReagents = new List();
- foreach (string reagentName in reagents)
+ foreach (RecipeIngredient ingredient in recipe)
{
+ string reagentName = ingredient.name;
if (reagentName == null)
{
continue;
@@ -62,47 +183,45 @@ namespace DesertPaintLab
Reagent reagent = ReagentManager.GetReagent(reagentName);
if (!reagent.IsCatalyst)
{
- baseColor.r += reagent.Color.Red;
- baseColor.g += reagent.Color.Green;
- baseColor.b += reagent.Color.Blue;
- pigmentCount += 1;
+ baseRGB.r += (reagent.Color.Red * (int)ingredient.quantity);
+ baseRGB.g += (reagent.Color.Green * (int)ingredient.quantity);
+ baseRGB.b += (reagent.Color.Blue * (int)ingredient.quantity);
+ pigmentCount += ingredient.quantity;
}
- if (prevReagent == null || !prevReagent.Equals(reagentName))
+
+ if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
{
- if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
+ reagentSet[reagentName] = true;
+ // Run reactions.
+ foreach (Reagent otherReagent in prevReagents)
{
- reagentSet[reagentName] = true;
- // Run reactions.
- foreach (Reagent otherReagent in prevReagents)
+ Reaction reaction = reactions.Find(otherReagent, reagent);
+ if (reaction != null)
{
- Reaction reaction = profile.FindReaction(otherReagent, reagent);
- if (reaction != null)
- {
- reactionColor.r += reaction.Red;
- reactionColor.g += reaction.Green;
- reactionColor.b += reaction.Blue;
- }
+ reactionColor.r += reaction.Red;
+ reactionColor.g += reaction.Green;
+ reactionColor.b += reaction.Blue;
}
- prevReagents.Add(reagent);
}
+ prevReagents.Add(reagent);
}
- prevReagent = reagentName;
}
- paintColor.Red = CalculateColor(baseColor.r, pigmentCount, reactionColor.r);
- paintColor.Green = CalculateColor(baseColor.g, pigmentCount, reactionColor.g);
- paintColor.Blue = CalculateColor(baseColor.b, pigmentCount, reactionColor.b);
+ reactedColor.Red = CalculateColor(baseRGB.r, pigmentCount, reactionColor.r);
+ reactedColor.Green = CalculateColor(baseRGB.g, pigmentCount, reactionColor.g);
+ reactedColor.Blue = CalculateColor(baseRGB.b, pigmentCount, reactionColor.b);
}
// Compute the base color without any reactions
- public void ComputeBaseColor(ref PaintColor color)
+ private void ComputeBaseColor()
{
- RGB baseColor;
- baseColor.r = 0;
- baseColor.g = 0;
- baseColor.b = 0;
- int pigmentCount = 0;
- foreach (string reagentName in reagents)
+ RGB baseRGB;
+ baseRGB.r = 0;
+ baseRGB.g = 0;
+ baseRGB.b = 0;
+ uint pigmentCount = 0;
+ foreach (RecipeIngredient ingredient in recipe)
{
+ string reagentName = ingredient.name;
if (reagentName == null)
{
continue;
@@ -111,15 +230,110 @@ namespace DesertPaintLab
Reagent reagent = ReagentManager.GetReagent(reagentName);
if (!reagent.IsCatalyst)
{
- baseColor.r += reagent.Color.Red;
- baseColor.g += reagent.Color.Green;
- baseColor.b += reagent.Color.Blue;
- pigmentCount += 1;
+ baseRGB.r += (reagent.Color.Red * (int)ingredient.quantity);
+ baseRGB.g += (reagent.Color.Green * (int)ingredient.quantity);
+ baseRGB.b += (reagent.Color.Blue * (int)ingredient.quantity);
+ pigmentCount += ingredient.quantity;
}
}
- color.Red = CalculateColor(baseColor.r, pigmentCount, 0);
- color.Green = CalculateColor(baseColor.g, pigmentCount, 0);
- color.Blue = CalculateColor(baseColor.b, pigmentCount, 0);
+ baseColor.Red = CalculateColor(baseRGB.r, pigmentCount, 0);
+ baseColor.Green = CalculateColor(baseRGB.g, pigmentCount, 0);
+ baseColor.Blue = CalculateColor(baseRGB.b, pigmentCount, 0);
+ }
+
+ // Compute the base color without any reactions
+ public uint Cost
+ {
+ get {
+ uint cost = 0;
+ foreach (RecipeIngredient ingredient in recipe)
+ {
+ string reagentName = ingredient.name;
+ if (reagentName == null)
+ {
+ continue;
+ }
+
+ Reagent reagent = ReagentManager.GetReagent(reagentName);
+ cost += (reagent.Cost * ingredient.quantity);
+ }
+ return cost;
+ }
+ }
+
+ public uint GetBulkCost(uint quantity)
+ {
+ uint cost = 0;
+ foreach (RecipeIngredient ingredient in recipe)
+ {
+ string reagentName = ingredient.name;
+ if (reagentName == null)
+ {
+ continue;
+ }
+
+ Reagent reagent = ReagentManager.GetReagent(reagentName);
+ cost += (reagent.Cost * ingredient.quantity);
+ }
+ uint batchCount = (uint)Math.Ceiling((double)quantity / (double)cost); // number of batches require to make quantity
+ return batchCount * cost;
+ }
+
+ public bool IsValid
+ {
+ get
+ {
+ uint weight = 0;
+ foreach (RecipeIngredient ingredient in recipe)
+ {
+ string reagentName = ingredient.name;
+ if (reagentName == null)
+ {
+ continue;
+ }
+
+ Reagent reagent = ReagentManager.GetReagent(reagentName);
+ if (!reagent.IsCatalyst)
+ {
+ weight += ingredient.quantity;
+ }
+ }
+ return (weight >= 10);
+ }
+ }
+
+ public bool CheckMissingReactions(ref List> missing)
+ {
+ missing.Clear();
+
+ SortedDictionary reagentSet = new SortedDictionary();
+ List prevReagents = new List();
+
+ foreach (RecipeIngredient ingredient in recipe)
+ {
+ string reagentName = ingredient.name;
+ if (reagentName == null)
+ {
+ continue;
+ }
+
+ Reagent reagent = ReagentManager.GetReagent(reagentName);
+ if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
+ {
+ reagentSet[reagentName] = true;
+ // Run reactions.
+ foreach (Reagent otherReagent in prevReagents)
+ {
+ Reaction reaction = reactions.Find(otherReagent, reagent);
+ if (reaction == null)
+ {
+ missing.Add(new KeyValuePair(otherReagent.Name, reagent.Name));
+ }
+ }
+ prevReagents.Add(reagent);
+ }
+ }
+ return (missing.Count > 0);
}
}
}
diff --git a/Palette.cs b/Palette.cs
--- a/Palette.cs
+++ b/Palette.cs
@@ -1,3 +1,25 @@
+/*
+ * Copyright (c) 2010, Tess Snider
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
using System;
using System.IO;
using System.Collections.Generic;
@@ -10,6 +32,12 @@ namespace DesertPaintLab
static List colors = new List();
static Regex colorEntry = new Regex(@"\#(?\w\w)(?\w\w)(?\w\w)\s*(?\w+)");
+ public static List Colors
+ {
+ get {
+ return colors;
+ }
+ }
public Palette ()
{
@@ -35,6 +63,14 @@ namespace DesertPaintLab
}
}
}
+
+ public static int Count
+ {
+ get
+ {
+ return colors.Count;
+ }
+ }
public static string FindNearest(PaintColor color)
{
diff --git a/PlayerProfile.cs b/PlayerProfile.cs
--- a/PlayerProfile.cs
+++ b/PlayerProfile.cs
@@ -23,6 +23,7 @@
using System;
using System.IO;
using System.Collections.Generic;
+using System.Text.RegularExpressions;
namespace DesertPaintLab
{
@@ -31,16 +32,61 @@ namespace DesertPaintLab
string name;
string directory;
string reactFile;
+ string reagentFile;
- SortedDictionary> reactions =
- new SortedDictionary>();
+ static Regex recipeHeaderRegex = new Regex(@"^--- Recipe: (?(\w*\s)*\w+)\s*");
+ static Regex recipeIngredientRegex = new Regex(@"(?(\w+\s)?\w+)\s*\|\s*(?\d+)\s*");
+
+ ReactionSet reactions = new ReactionSet();
+ // ingredient -> [ingredient, reaction]
+ //SortedDictionary> reactions =
+ // new SortedDictionary>();
+ SortedDictionary recipes;
+
+ static PlayerProfile current = null;
+
+ static PlayerProfile Current
+ {
+ get {
+ return current;
+ }
+ }
public PlayerProfile(string name, string directory)
{
this.name = name;
this.directory = directory;
this.reactFile = System.IO.Path.Combine(directory, "dp_reactions.txt");
+ this.reagentFile = System.IO.Path.Combine(directory, "ingredients.txt");
}
+
+ public string Directory
+ {
+ get {
+ return this.directory;
+ }
+ }
+
+ public ReactionSet Reactions
+ {
+ get {
+ return this.reactions;
+ }
+ }
+
+ public string ReagentFile
+ {
+ get {
+ return this.reagentFile;
+ }
+ }
+
+ public SortedDictionary Recipes
+ {
+ get {
+ return this.recipes;
+ }
+ }
public void Initialize()
{
@@ -120,25 +166,26 @@ namespace DesertPaintLab
public bool SaveToPP(string ppFile)
{
Reaction reaction1, reaction2;
- SortedDictionary secondReagentDict;
using (StreamWriter writer = new StreamWriter(ppFile))
{
- foreach (KeyValuePair> firstPair in reactions)
- {
- foreach (KeyValuePair secondPair in firstPair.Value)
- {
- reaction1 = secondPair.Value;
- if ((reaction1 != null) && !reaction1.Exported)
- {
- reaction2 = null;
- reactions.TryGetValue(secondPair.Key, out secondReagentDict);
- if (secondReagentDict != null)
- {
- secondReagentDict.TryGetValue(firstPair.Key, out reaction2);
- }
- if (reaction2 != null)
- {
- writer.Write(firstPair.Key + " | " + secondPair.Key + " | ");
+ foreach (string reagentName1 in ReagentManager.Names)
+ {
+ // TODO: could be more efficient by only iterating over the names after reagent1
+ foreach (string reagentName2 in ReagentManager.Names)
+ {
+ if (reagentName1.Equals(reagentName2))
+ {
+ continue;
+ }
+ Reagent reagent1 = ReagentManager.GetReagent(reagentName1);
+ Reagent reagent2 = ReagentManager.GetReagent(reagentName2);
+ reaction1 = reactions.Find(reagent1, reagent2);
+ if (reaction1 != null && !reaction1.Exported)
+ {
+ reaction2 = reactions.Find(reagent2, reagent1);
+ if (reaction2 != null)
+ {
+ writer.Write(reagent1.PracticalPaintName + " | " + reagent2.PracticalPaintName + " | ");
if ((Math.Abs(reaction1.Red) > Math.Abs(reaction1.Green)) ||
(Math.Abs(reaction2.Red) > Math.Abs(reaction2.Green)))
{
@@ -167,14 +214,27 @@ namespace DesertPaintLab
}
// Clear Exported flags.
- foreach (KeyValuePair> firstPair in reactions)
- {
- foreach (KeyValuePair secondPair in firstPair.Value)
- {
- if (secondPair.Value != null)
- {
- secondPair.Value.Exported = false;
- }
+ foreach (string reagentName1 in ReagentManager.Names)
+ {
+ // TODO: could be more efficient by only iterating over the names after reagent1
+ foreach (string reagentName2 in ReagentManager.Names)
+ {
+ if (reagentName1.Equals(reagentName2))
+ {
+ continue;
+ }
+ Reagent reagent1 = ReagentManager.GetReagent(reagentName1);
+ Reagent reagent2 = ReagentManager.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;
@@ -208,17 +268,17 @@ namespace DesertPaintLab
public void Load()
{
string line;
- SortedDictionary dict;
reactions.Clear();
- ReagentManager.Load(System.IO.Path.Combine(directory, "ingredients.txt"));
+ ReagentManager.LoadProfileReagents(reagentFile);
ReagentManager.InitializeReactions(ref reactions);
using (StreamReader reader = new StreamReader(reactFile))
{
while ((line = reader.ReadLine()) != null)
{
string[] tokens = line.Split(null);
- reactions.TryGetValue(tokens[0], out dict);
- dict[tokens[1]] = new Reaction(int.Parse(tokens[2]), int.Parse(tokens[3]), int.Parse(tokens[4]));
+ Reagent reagent1 = ReagentManager.GetReagent(tokens[0]);
+ Reagent reagent2 = ReagentManager.GetReagent(tokens[1]);
+ reactions.Set(reagent1, reagent2, new Reaction(int.Parse(tokens[2]), int.Parse(tokens[3]), int.Parse(tokens[4])));
}
}
}
@@ -228,43 +288,144 @@ namespace DesertPaintLab
Reaction reaction;
using (StreamWriter writer = new StreamWriter(reactFile, false))
{
- foreach (KeyValuePair> firstPair in reactions)
- {
- foreach (KeyValuePair secondPair in firstPair.Value)
- {
- reaction = secondPair.Value;
-
- if (reaction != null)
- {
- writer.WriteLine(firstPair.Key + " " + secondPair.Key + " " +
- reaction.Red + " " + reaction.Green + " " + reaction.Blue);
- }
- }
- }
- }
+ foreach (string reagentName1 in ReagentManager.Names)
+ {
+ // TODO: could be more efficient by only iterating over the names after reagent1
+ foreach (string reagentName2 in ReagentManager.Names)
+ {
+ if (reagentName1.Equals(reagentName2))
+ {
+ continue;
+ }
+ Reagent reagent1 = ReagentManager.GetReagent(reagentName1);
+ Reagent reagent2 = ReagentManager.GetReagent(reagentName2);
+ reaction = reactions.Find(reagent1, reagent2);
+ if (reaction != null)
+ {
+ writer.WriteLine(reagent1.PracticalPaintName + " " + reagent2.PracticalPaintName + " " +
+ reaction.Red + " " + reaction.Green + " " + reaction.Blue);
+ }
+ }
+ }
+ }
}
+
+ public void LoadRecipes()
+ {
+ this.recipes = new SortedDictionary();
+ string recipeFile = System.IO.Path.Combine(directory, "dp_recipes.txt");
+ string line;
+ Match match;
+ bool inRecipe = false;
+ PaintRecipe recipe = null;
+ string currentRecipeColor = null;
+ if (File.Exists(recipeFile))
+ {
+ using (StreamReader reader = new StreamReader(recipeFile))
+ {
+ while ((line = reader.ReadLine()) != null)
+ {
+ match = recipeHeaderRegex.Match(line);
+ if (match.Success)
+ {
+ if (recipe != null && currentRecipeColor != null)
+ {
+ recipes.Add(currentRecipeColor, recipe);
+ }
+ recipe = new PaintRecipe();
+ recipe.Reactions = reactions;
+ currentRecipeColor = match.Groups["colorname"].Value;
+ inRecipe = true;
+ }
+ else if (inRecipe)
+ {
+ match = recipeIngredientRegex.Match(line);
+ if (match.Success)
+ {
+ string ingredient = match.Groups["ingredient"].Value;
+ uint quantity = uint.Parse(match.Groups["quantity"].Value);
+ recipe.AddReagent(ingredient, quantity);
+ }
+ }
+ }
+ if (inRecipe)
+ {
+ recipes.Add(currentRecipeColor, recipe);
+ }
+ }
+ }
+ }
+
+ public void SaveRecipes()
+ {
+ if (recipes != null)
+ {
+ string recipeFile = System.IO.Path.Combine(directory, "dp_recipes.txt");
+ using (StreamWriter writer = new StreamWriter(recipeFile, false))
+ {
+ foreach (KeyValuePair pair in recipes)
+ {
+ writer.WriteLine("--- Recipe: {0}", pair.Key);
+ foreach (PaintRecipe.RecipeIngredient ingredient in pair.Value.Ingredients)
+ {
+ writer.WriteLine("{0,-14} | {1}", ingredient.name, ingredient.quantity);
+ }
+ }
+ }
+ }
+ }
+
+ public void ExportWikiRecipes(string file)
+ {
+ PaintRecipe recipe;
+ using (StreamWriter writer = new StreamWriter(file))
+ {
+ writer.WriteLine("{| class='wikitable sortable' border=\"1\" style=\"background-color:#DEB887;\"");
+ writer.WriteLine("! Color !! Recipe !! Verified");
+ foreach (PaintColor color in Palette.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.Red < 128 && color.Green < 128 && color.Blue < 128)
+ {
+ // dark color gets light text
+ colorLine += " color: #FFFFFF;";
+ }
+ colorLine += "\" | " + color.Name + " || ";
+ if (recipes.TryGetValue(color.Name, out recipe))
+ {
+ foreach (PaintRecipe.RecipeIngredient ingredient in recipe.Ingredients)
+ {
+ colorLine += " " + ingredient.name + " " + ingredient.quantity.ToString();
+ }
+ }
+ else
+ {
+ // no recipe
+ }
+ colorLine += " || ";
+ writer.WriteLine(colorLine);
+ }
+ writer.WriteLine("|}");
+ }
+ }
public Reaction FindReaction(Reagent reagent1, Reagent reagent2)
- {
- SortedDictionary secondReagentDict = null;
- Reaction reaction = null;
- reactions.TryGetValue(reagent1.Name, out secondReagentDict);
- if (secondReagentDict != null)
- {
- secondReagentDict.TryGetValue(reagent2.Name, out reaction);
- }
- return reaction;
+ {
+ return reactions.Find(reagent1, reagent2);
}
public void SetReaction(Reagent reagent1, Reagent reagent2, Reaction reaction)
{
- SortedDictionary secondReagentDict = null;
- reactions.TryGetValue(reagent1.Name, out secondReagentDict);
- if (secondReagentDict != null)
- {
- secondReagentDict[reagent2.Name] = reaction;
- }
+ reactions.Set(reagent1, reagent2, reaction);
}
+
+ public void SetRecipe(PaintRecipe recipe)
+ {
+ string colorName = Palette.FindNearest(recipe.ReactedColor);
+ recipes[colorName] = recipe;
+ }
}
}
diff --git a/Reaction.cs b/Reaction.cs
--- a/Reaction.cs
+++ b/Reaction.cs
@@ -1,3 +1,25 @@
+/*
+ * Copyright (c) 2015, Tess Snider
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
using System;
namespace DesertPaintLab
{
diff --git a/ReactionRecorder.cs b/ReactionRecorder.cs
--- a/ReactionRecorder.cs
+++ b/ReactionRecorder.cs
@@ -1,4 +1,26 @@
-using System;
+/*
+ * Copyright (c) 2015, Jason Maltzen
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+using System;
namespace DesertPaintLab
{
@@ -7,11 +29,17 @@ namespace DesertPaintLab
{
const int colorTolerance = 2;
- const int swatchHeight = 24;
- const int colorBarWidth = 306;
- const int redBarSpacing = 32;
- const int greenBarSpacing = 42;
- const int blueBarSpacing = 52;
+ const int DEFAULT_SWATCH_HEIGHT = 24;
+ const int DEFAULT_COLOR_BAR_WIDTH = 306;
+ const int DEFAULT_RED_BAR_SPACING = 32;
+ const int DEFAULT_GREEN_BAR_SPACING = 42;
+ const int DEFAULT_BLUE_BAR_SPACING = 52;
+
+ static int swatchHeight = DEFAULT_SWATCH_HEIGHT;
+ static int colorBarWidth = DEFAULT_COLOR_BAR_WIDTH;
+ static int redBarSpacing = DEFAULT_RED_BAR_SPACING;
+ static int greenBarSpacing = DEFAULT_GREEN_BAR_SPACING;
+ static int blueBarSpacing = DEFAULT_BLUE_BAR_SPACING;
private static bool IsPapyTexture(byte r, byte g, byte b)
{
@@ -19,9 +47,18 @@ namespace DesertPaintLab
((r < 0xF4) && (g < 0xE0) && (b < 0xC4));
}
- unsafe public static bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor)
+ public static void SetPixelMultiplier(int pixelMultiplier)
{
- byte r, g, b;
+ swatchHeight = DEFAULT_SWATCH_HEIGHT * pixelMultiplier;
+ colorBarWidth = DEFAULT_COLOR_BAR_WIDTH * pixelMultiplier;
+ redBarSpacing = DEFAULT_RED_BAR_SPACING * pixelMultiplier;
+ greenBarSpacing = DEFAULT_GREEN_BAR_SPACING * pixelMultiplier;
+ blueBarSpacing = DEFAULT_BLUE_BAR_SPACING * pixelMultiplier;
+ }
+
+ unsafe public static bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor, ref int redPixelStart)
+ {
+ byte pixel_r, pixel_g, pixel_b;
int pixelStart, otherPixelStart;
bool colorMatch;
for (int x = 0; x < screenshotWidth - colorBarWidth; ++x)
@@ -30,12 +67,12 @@ namespace DesertPaintLab
{
// Look for the color swatch.
pixelStart = (y * stride) + (x * 3);
- r = pixBytes[pixelStart];
- g = pixBytes[pixelStart + 1];
- b = pixBytes[pixelStart + 2];
+ pixel_r = pixBytes[pixelStart];
+ pixel_g = pixBytes[pixelStart + 1];
+ pixel_b = pixBytes[pixelStart + 2];
// 1.) Check if this is a dark pixel.
- if ((r < 0x46) && (g < 0x46) && (b < 0x46))
+ if ((pixel_r < 0x46) && (pixel_g < 0x46) && (pixel_b < 0x46))
{
// 2.) Check the pixel above it,
// to see if it's from the papy texture.
@@ -62,9 +99,9 @@ namespace DesertPaintLab
for (int i = 1; i < swatchHeight - 2; ++i)
{
otherPixelStart = pixelStart + (stride * i);
- if ((Math.Abs(r - pixBytes[otherPixelStart++]) > colorTolerance) ||
- (Math.Abs(g - pixBytes[otherPixelStart++]) > colorTolerance) ||
- (Math.Abs(b - pixBytes[otherPixelStart]) > colorTolerance))
+ if ((Math.Abs(pixel_r - pixBytes[otherPixelStart++]) > colorTolerance) ||
+ (Math.Abs(pixel_g - pixBytes[otherPixelStart++]) > colorTolerance) ||
+ (Math.Abs(pixel_b - pixBytes[otherPixelStart]) > colorTolerance))
{
colorMatch = false;
break;
@@ -75,7 +112,7 @@ namespace DesertPaintLab
{
// WE FOUND THE SWATCH!
// Now we know where the color bars are.
- int redPixelStart = pixelStart + (redBarSpacing * stride);
+ redPixelStart = pixelStart + (redBarSpacing * stride);
int redPixelCount = 0;
while ((pixBytes[redPixelStart] > 0x9F) &&
(pixBytes[redPixelStart + 1] < 0x62) &&
diff --git a/ReactionSet.cs b/ReactionSet.cs
new file mode 100644
--- /dev/null
+++ b/ReactionSet.cs
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015, Jason Maltzen
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+using System;
+using System.Collections.Generic;
+
+namespace DesertPaintLab
+{
+ public class ReactionSet
+ {
+ // ingredient -> [ingredient, reaction]
+ SortedDictionary> reactions =
+ new SortedDictionary>();
+
+ public ReactionSet()
+ {
+ }
+
+ public Reaction Find(Reagent reagent1, Reagent reagent2)
+ {
+ SortedDictionary secondReagentDict = null;
+ Reaction reaction = null;
+ reactions.TryGetValue(reagent1.PracticalPaintName, out secondReagentDict);
+ if (secondReagentDict != null)
+ {
+ secondReagentDict.TryGetValue(reagent2.PracticalPaintName, out reaction);
+ }
+ return reaction;
+ }
+
+ public void Set(Reagent reagent1, Reagent reagent2, Reaction reaction)
+ {
+ SortedDictionary secondReagentDict = null;
+ reactions.TryGetValue(reagent1.PracticalPaintName, out secondReagentDict);
+ if (secondReagentDict == null)
+ {
+ secondReagentDict = new SortedDictionary();
+ reactions.Add(reagent1.PracticalPaintName, secondReagentDict);
+ }
+ secondReagentDict[reagent2.PracticalPaintName] = reaction;
+ }
+
+ public void Clear()
+ {
+ reactions.Clear();
+ }
+ }
+}
+
diff --git a/Reagent.cs b/Reagent.cs
--- a/Reagent.cs
+++ b/Reagent.cs
@@ -1,3 +1,25 @@
+/*
+ * Copyright (c) 2015, Jason Maltzen
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
using System;
namespace DesertPaintLab
@@ -5,8 +27,11 @@ namespace DesertPaintLab
public class Reagent
{
string name;
+ string ppName;
bool isCatalyst = false;
- int cost = 0;
+ uint cost = 0;
+ bool enabled = true;
+ uint recipeMax = 10;
PaintColor color;
public bool IsCatalyst
@@ -32,21 +57,76 @@ namespace DesertPaintLab
return name;
}
}
-
- public Reagent(string name, int cost)
- {
- this.name = name;
- this.cost = cost;
- isCatalyst = true;
- }
-
- public Reagent(string name, byte red, byte green, byte blue, int cost)
- {
- color = new PaintColor(red, green, blue);
- this.name = name;
- this.cost = cost;
- }
-
+
+ public string PracticalPaintName
+ {
+ get
+ {
+ return ppName;
+ }
+ }
+
+ public bool Enabled
+ {
+ get
+ {
+ return enabled;
+ }
+ set
+ {
+ enabled = value;
+ }
+ }
+
+ public uint Cost
+ {
+ get
+ {
+ return cost;
+ }
+ set
+ {
+ cost = Math.Max(1, value);
+ }
+ }
+
+ public uint RecipeMax
+ {
+ get
+ {
+ return recipeMax;
+ }
+ set
+ {
+ if (!isCatalyst)
+ {
+ recipeMax = Math.Max(0, value);
+ }
+ }
+ }
+
+ // catalyst
+ public Reagent(string name, string ppName)
+ {
+ this.name = name;
+ this.ppName = ppName;
+ this.cost = 2;
+ this.enabled = true;
+ this.recipeMax = 1;
+ this.isCatalyst = true;
+ }
+
+ public Reagent(string name, string ppName, byte red, byte green, byte blue)
+ {
+ this.color = new PaintColor(red, green, blue);
+ this.name = name;
+ this.ppName = ppName;
+ this.cost = 1;
+ this.recipeMax = 10;
+ this.enabled = true;
+ this.isCatalyst = false;
+ }
+
public override string ToString()
{
if (isCatalyst)
diff --git a/ReagentManager.cs b/ReagentManager.cs
--- a/ReagentManager.cs
+++ b/ReagentManager.cs
@@ -1,3 +1,24 @@
+/*
+ * Copyright (c) 2010, Tess Snider
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
using System;
using System.IO;
using System.Collections.Generic;
@@ -7,11 +28,15 @@ namespace DesertPaintLab
{
public class ReagentManager
{
- static Regex reagentRegex = new Regex(@"(?\w+)\s*\|\s*(?\d+),\s*(?\d+),\s*(?\d+)\s*\|\s*(?\d+)\s*\|.*");
- static Regex catalystRegex = new Regex(@"(?\w+)\s*\|\s*catalyst\s*\|\s*(?\d+)\s*\|.*");
+ // PP format
+ static Regex reagentRegex = new Regex(@"(?\w+)\s*\|\s*(?\d+),\s*(?\d+),\s*(?\d+)\s*\|\s*(?\d+)\s*\|\s*(?[YN])\s*\|\s*(?(bulk|normal))\s*\|\s*(?\d+).*");
+ static Regex catalystRegex = new Regex(@"(?\w+)\s*\|\s*catalyst\s*\|\s*(?\d+)\s*\|\s*(?[YN])\s*\|\s*(?(bulk|normal)).*");
+ static Regex internalReagentRegex = new Regex(@"(?(\w*\s)*\w+)\s*\|\s*(?\w+)\s*\|\s*(?\d+),\s*(?\d+),\s*(?\d+).*");
+ static Regex internalCatalystRegex = new Regex(@"(?(\w+\s)*\w+)\s*\|\s*(?\w+)\s*\|\s*catalyst.*");
static SortedDictionary reagents = new SortedDictionary();
static List names = new List();
+ static Dictionary nameLookup = new Dictionary(); // pp name to our name
static Gtk.ListStore nameStore = new Gtk.ListStore(typeof(string));
@@ -35,12 +60,52 @@ namespace DesertPaintLab
{
}
+
+ // Loads reagent name/colors
+ public static void Load(string file)
+ {
+ Match match;
+ string line;
+ reagents.Clear();
+ using (StreamReader reader = new StreamReader(file))
+ {
+ while ((line = reader.ReadLine()) != null)
+ {
+ match = internalReagentRegex.Match(line);
+ if (match.Success)
+ {
+ string name = match.Groups["name"].Value;
+ string ppname = match.Groups["ppname"].Value;
+ reagents.Add(name,
+ new Reagent(name, ppname,
+ byte.Parse(match.Groups["red"].Value),
+ byte.Parse(match.Groups["green"].Value),
+ byte.Parse(match.Groups["blue"].Value)));
+ nameStore.AppendValues(name);
+ names.Add(name);
+ nameLookup.Add(ppname, name);
+ }
+ else
+ {
+ match = internalCatalystRegex.Match(line);
+ if (match.Success)
+ {
+ string name = match.Groups["name"].Value;
+ string ppname = match.Groups["ppname"].Value;
+ reagents.Add(name, new Reagent(ppname, ppname));
+ nameStore.AppendValues(name);
+ names.Add(name);
+ nameLookup.Add(ppname, name);
+ }
+ }
+ }
+ }
+ }
- public static void Load(string file)
+ public static void LoadProfileReagents(string file)
{
Match match;
string line;
- reagents.Clear();
using (StreamReader reader = new StreamReader(file))
{
while ((line = reader.ReadLine()) != null)
@@ -48,45 +113,88 @@ namespace DesertPaintLab
match = reagentRegex.Match(line);
if (match.Success)
{
- string name = match.Groups["name"].Value;
- reagents.Add(name,
- new Reagent(name,
- byte.Parse(match.Groups["red"].Value),
- byte.Parse(match.Groups["green"].Value),
- byte.Parse(match.Groups["blue"].Value),
- int.Parse(match.Groups["cost"].Value)));
- nameStore.AppendValues(name);
- names.Add(name);
+ string ppname = match.Groups["name"].Value;
+ string name = null;
+ nameLookup.TryGetValue(ppname, out name);
+ Reagent reagent = GetReagent(name);
+ if (reagent != null && !reagent.IsCatalyst)
+ {
+ reagent.Enabled = match.Groups["enabled"].Value.Equals("Y");
+ reagent.Cost = uint.Parse(match.Groups["cost"].Value);
+ reagent.RecipeMax = uint.Parse(match.Groups["max"].Value);
+ }
}
else
{
match = catalystRegex.Match(line);
if (match.Success)
{
- string name = match.Groups["name"].Value;
- int cost = int.Parse(match.Groups["cost"].Value);
- reagents.Add(name, new Reagent(name, cost));
- nameStore.AppendValues(name);
- names.Add(name);
+ string ppname = match.Groups["name"].Value;
+ string name = null;
+ nameLookup.TryGetValue(ppname, out name);
+ Reagent reagent = GetReagent(name);
+ if (reagent != null && reagent.IsCatalyst)
+ {
+ reagent.Enabled = match.Groups["enabled"].Value.Equals("Y");
+ reagent.Cost = uint.Parse(match.Groups["cost"].Value);
+ }
}
}
}
}
}
+
+ public static void SaveProfileReagents(string file)
+ {
+ using (StreamWriter writer = new StreamWriter(file))
+ {
+ writer.WriteLine("// Ingredients are in the form:");
+ writer.WriteLine("// Name | RGB values | cost | enabled (Y/N) | bulk/normal | max items per paint (1-20)");
+ writer.WriteLine("//");
+ writer.WriteLine("// It is recommended to only change the cost value");
+ writer.WriteLine("// It is not recommended to set many of the ingredients above 10 per paint");
+
+ List sortedReagents = new List(reagents.Count);
+ foreach (KeyValuePair pair in reagents)
+ {
+ sortedReagents.Add(pair.Value);
+ }
+ sortedReagents.Sort( (x,y) => ((x.IsCatalyst && !y.IsCatalyst) ? 1 : ((y.IsCatalyst && !x.IsCatalyst) ? -1 : x.PracticalPaintName.CompareTo(y.PracticalPaintName))) );
+ foreach (Reagent reagent in sortedReagents)
+ {
+ if (!reagent.IsCatalyst)
+ {
+ writer.WriteLine("{0,-10} | {1,3}, {2,3}, {3,3} | {4,7} | {5} | {6} | {7}",
+ reagent.PracticalPaintName,
+ reagent.Color.Red, reagent.Color.Blue, reagent.Color.Green,
+ reagent.Cost,
+ reagent.Enabled ? "Y" : "N",
+ reagent.RecipeMax >= 10 ? " bulk" : "normal",
+ reagent.RecipeMax);
+ }
+ else
+ {
+ writer.WriteLine("{0,-10} | catalyst | {1,7} | {2} | normal | 1",
+ reagent.PracticalPaintName,
+ reagent.Cost,
+ reagent.Enabled ? "Y" : "N");
+ }
+ }
+ }
+ }
+
- public static void InitializeReactions(ref SortedDictionary> reactions)
+ public static void InitializeReactions(ref ReactionSet reactions)
{
foreach (KeyValuePair pair1 in reagents)
{
- SortedDictionary dict = new SortedDictionary();
foreach (KeyValuePair pair2 in reagents)
{
if (pair1.Key != pair2.Key)
{
- dict.Add(pair2.Key, null);
+ reactions.Set(pair1.Value, pair2.Value, null);
}
}
- reactions.Add(pair1.Key, dict);
}
}
@@ -133,6 +241,16 @@ namespace DesertPaintLab
{
Reagent returnVal;
reagents.TryGetValue(reagentName, out returnVal);
+ if (returnVal == null)
+ {
+ // convert pp name to our internal name
+ string otherName = null;
+ nameLookup.TryGetValue(reagentName, out otherName);
+ if (otherName != null)
+ {
+ reagents.TryGetValue(otherName, out returnVal);
+ }
+ }
return returnVal;
}
}
diff --git a/ReagentWindow.cs b/ReagentWindow.cs
new file mode 100644
--- /dev/null
+++ b/ReagentWindow.cs
@@ -0,0 +1,180 @@
+using System;
+using System.Collections.Generic;
+
+namespace DesertPaintLab
+{
+ public partial class ReagentWindow : Gtk.Window
+ {
+ private class ReagentCheckButton : Gtk.CheckButton
+ {
+ private Reagent reagent;
+ public Reagent Reagent
+ {
+ get {
+ return reagent;
+ }
+ set {
+ reagent = value;
+ }
+ }
+ }
+ private class ReagentEntry : Gtk.Entry
+ {
+ private Reagent reagent;
+ public Reagent Reagent
+ {
+ get {
+ return reagent;
+ }
+ set {
+ reagent = value;
+ }
+ }
+
+ // public ReagentEntry(int size) : base(size)
+ //{
+ //}
+
+ override protected void OnTextInserted(string text, ref int position)
+ {
+ uint val;
+ if (uint.TryParse(text, out val))
+ {
+ base.OnTextInserted(text, ref position);
+ }
+ }
+ }
+
+ bool dirty = false;
+ PlayerProfile profile;
+
+ SortedDictionary ingredientCheckButtons = new SortedDictionary();
+ SortedDictionary ingredientCostEntries = new SortedDictionary();
+ SortedDictionary ingredientQuantityEntries = new SortedDictionary();
+ int numReagents = 14;
+ public ReagentWindow(PlayerProfile profile)
+ : base(Gtk.WindowType.Toplevel)
+ {
+ this.profile = profile;
+ this.Build();
+
+ numReagents = ReagentManager.Names.Count;
+ ingredientTable.NRows = (uint)numReagents;
+ ingredientTable.NColumns = 4;
+ uint row = 0;
+ foreach (string reagentName in ReagentManager.Names)
+ {
+ Reagent reagent = ReagentManager.GetReagent(reagentName);
+ ReagentCheckButton checkButton = new ReagentCheckButton();
+ checkButton.Active = reagent.Enabled;
+ checkButton.Reagent = reagent;
+ checkButton.Toggled += OnEnableToggled;
+ ingredientCheckButtons.Add(reagentName, checkButton);
+ ingredientTable.Attach(checkButton, 0, 1, row, row+1); // checkbox for enabled
+ ingredientTable.Attach(new Gtk.Label(reagentName), 1, 2, row, row+1); // name label
+ ReagentEntry costEntry = new ReagentEntry();
+ costEntry.MaxLength = 6;
+ costEntry.WidthChars = 6;
+ costEntry.Text = reagent.Cost.ToString();
+ // TODO: set up validator
+ // TODO: set up event handler for changed
+ costEntry.Reagent = reagent;
+ costEntry.TextDeleted += OnCostChanged;
+ costEntry.TextInserted += OnCostChanged;
+ ingredientCostEntries.Add(reagentName, costEntry);
+ ingredientTable.Attach(costEntry, 2, 3, row, row+1); // cost input
+ ReagentEntry maxQuantityEntry = new ReagentEntry();
+ maxQuantityEntry.Text = reagent.RecipeMax.ToString();
+ maxQuantityEntry.MaxLength = 4;
+ maxQuantityEntry.WidthChars = 4;
+ // TODO: set up validator
+ // TODO: set up event handler for changed
+ maxQuantityEntry.Reagent = reagent;
+ if (reagent.IsCatalyst)
+ {
+ maxQuantityEntry.Sensitive = false;
+ }
+ else
+ {
+ maxQuantityEntry.TextDeleted += OnQuantityChanged;
+ maxQuantityEntry.TextInserted += OnQuantityChanged;
+ }
+ ingredientQuantityEntries.Add(reagentName, maxQuantityEntry);
+ ingredientTable.Attach(maxQuantityEntry, 3, 4, row, row+1); // maximum quantity input
+ ++row;
+ }
+
+ okButton.Clicked += OnOK;
+ cancelButton.Clicked += OnCancel;
+ ShowAll();
+ }
+
+ private void OnCostChanged(object o, EventArgs args)
+ {
+ ReagentEntry costEntry = (ReagentEntry)o;
+ uint newCost;
+ if (uint.TryParse(costEntry.Text, out newCost))
+ {
+ if (costEntry.Reagent.Cost != newCost)
+ {
+ dirty = true;
+ }
+ }
+ }
+
+ private void OnQuantityChanged(object o, EventArgs args)
+ {
+ ReagentEntry qtyEntry = (ReagentEntry)o;
+ uint newCost;
+ if (uint.TryParse(qtyEntry.Text, out newCost))
+ {
+ if (qtyEntry.Reagent.Cost != newCost)
+ {
+ dirty = true;
+ }
+ }
+ }
+
+ private void OnEnableToggled(object o, EventArgs args)
+ {
+ ReagentCheckButton btn = (ReagentCheckButton)o;
+ if (btn.Active != btn.Reagent.Enabled)
+ {
+ dirty = true;
+ }
+ }
+
+ private void OnOK(object obj, EventArgs args)
+ {
+ if (dirty)
+ {
+ // save out state
+ foreach (string reagentName in ReagentManager.Names)
+ {
+ ReagentCheckButton checkButton = ingredientCheckButtons[reagentName];
+ ReagentEntry costEntry = ingredientCostEntries[reagentName];
+ ReagentEntry qtyEntry = ingredientQuantityEntries[reagentName];
+ checkButton.Reagent.Enabled = checkButton.Active;
+ uint val;
+ if (uint.TryParse(costEntry.Text, out val))
+ {
+ costEntry.Reagent.Cost = val;
+ }
+ if (uint.TryParse(qtyEntry.Text, out val))
+ {
+ qtyEntry.Reagent.RecipeMax = val;
+ }
+ }
+
+ ReagentManager.SaveProfileReagents(profile.ReagentFile);
+ }
+ this.Destroy();
+ }
+
+ private void OnCancel(object obj, EventArgs args)
+ {
+ this.Destroy();
+ }
+ }
+}
+
diff --git a/RecipeGenerator.cs b/RecipeGenerator.cs
new file mode 100644
--- /dev/null
+++ b/RecipeGenerator.cs
@@ -0,0 +1,722 @@
+/*
+ * Copyright (c) 2015, Jason Maltzen
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Collections.Concurrent;
+using System.Threading;
+
+namespace DesertPaintLab
+{
+ public class NewRecipeEventArgs : EventArgs
+ {
+ string color;
+ PaintRecipe recipe;
+
+ public NewRecipeEventArgs(string color, PaintRecipe recipe)
+ {
+ this.color = color;
+ this.recipe = recipe;
+ }
+
+ public string Color
+ {
+ get {
+ return color;
+ }
+ }
+
+ public PaintRecipe Recipe
+ {
+ get {
+ return recipe;
+ }
+ }
+ }
+
+ public class RecipeGenerator
+ {
+ private class SearchNode
+ {
+ //int initialReagentCount;
+ List reagents;
+ HashSet reagentInUse = new HashSet();
+ List costSortedReagents;
+ PaintRecipe testRecipe = null;
+ public PaintRecipe TestRecipe
+ {
+ get
+ {
+ return testRecipe;
+ }
+ set
+ {
+ testRecipe = value;
+ }
+ }
+ public uint CurrentTargetQuantity { get; set; }
+ public uint MaxQuantity { get; set; }
+ uint maxReagents;
+ public uint MaxReagents
+ {
+ get
+ {
+ return maxReagents;
+ }
+ set
+ {
+ maxReagents = value;
+ currentWeights = new uint[maxReagents];
+ }
+ }
+ public uint UsedQuantity { get; private set; }
+
+ public uint CatalystCount { get; set; }
+
+ uint[] currentWeights;
+ public uint[] CurrentWeights
+ {
+ get
+ {
+ return currentWeights;
+ }
+ }
+
+ public SearchNode(List costSortedReagents, List reagents)
+ {
+ this.costSortedReagents = new List(costSortedReagents);
+ this.reagents = new List(reagents);
+ foreach (uint reagentIdx in reagents)
+ {
+ reagentInUse.Add(reagentIdx);
+ }
+ InitialCount = this.reagents.Count;
+ MaxReagents = (uint)this.reagents.Count; // better set this later!
+ UsedQuantity = 0;
+ }
+
+ // top-level search
+ public SearchNode(List costSortedReagents, uint startReagent)
+ {
+ this.costSortedReagents = new List(costSortedReagents);
+ this.reagents = new List();
+ this.reagents.Add(NextFreeReagent(startReagent));
+ InitialCount = 1; // don't iterate up beyond the start reagent
+ MaxReagents = 1;
+ UsedQuantity = 0;
+ }
+
+ public int InitialCount { get; private set; }
+ public List Reagents
+ {
+ get
+ {
+ return reagents;
+ }
+ }
+
+ public Reagent Reagent(int idx)
+ {
+ return costSortedReagents[(int)reagents[idx]];
+ }
+
+ public uint LastReagent
+ {
+ get
+ {
+ return reagents[reagents.Count - 1];
+ }
+ }
+
+ public void RemoveLastReagent()
+ {
+ uint reagentIdx = reagents[reagents.Count-1];
+ ReleaseReagent(reagentIdx);
+ if (costSortedReagents[(int)reagentIdx].IsCatalyst)
+ {
+ --CatalystCount;
+ }
+ reagents.RemoveAt(reagents.Count-1);
+ }
+
+ public void ReplaceLastReagent(uint reagentIdx)
+ {
+ uint oldReagentIdx = reagents[reagents.Count-1];
+ ReleaseReagent(oldReagentIdx);
+ reagents[reagents.Count-1] = reagentIdx;
+ if (costSortedReagents[(int)oldReagentIdx].IsCatalyst)
+ {
+ --CatalystCount;
+ }
+ if (costSortedReagents[(int)reagentIdx].IsCatalyst)
+ {
+ ++CatalystCount;
+ }
+ }
+
+ public uint NextFreeReagent(uint startIdx)
+ {
+ uint idx = startIdx;
+ for (; idx < costSortedReagents.Count; ++idx)
+ {
+ bool inUse = reagentInUse.Contains(idx);
+ if (inUse == false)
+ {
+ //Console.WriteLine("Found free reagent idx {0}", idx);
+ reagentInUse.Add(idx);
+ return idx;
+ }
+ }
+ //Console.WriteLine("Failed to find free reagent.");
+ return (uint)costSortedReagents.Count;
+ }
+
+ private void ReleaseReagent(uint reagentIdx)
+ {
+ reagentInUse.Remove(reagentIdx);
+ }
+
+ public bool AddNextReagent()
+ {
+ bool ok = (reagents.Count < MaxReagents);
+ if (ok)
+ {
+ uint nextReagent = NextFreeReagent(0);
+ reagents.Add(nextReagent);
+ if (costSortedReagents[(int)nextReagent].IsCatalyst)
+ {
+ ++CatalystCount;
+ }
+ InitForQuantity(CurrentTargetQuantity);
+ }
+ return ok;
+ }
+
+ public void InitForQuantity(uint quantity)
+ {
+ //System.Console.WriteLine("Target quantity: {0}, reagent count: {1}", quantity, reagents.Count);
+ CurrentTargetQuantity = quantity;
+ if (CurrentTargetQuantity < (10 + CatalystCount))
+ {
+ return;
+ }
+ UsedQuantity = 0;
+ uint remainingReagents = ((uint)reagents.Count - CatalystCount);
+ uint remainingWeight = CurrentTargetQuantity - CatalystCount;
+ for (int i = 0; i < reagents.Count; ++i)
+ {
+ Reagent reagent = Reagent(i);
+
+ if (reagent.IsCatalyst)
+ {
+ currentWeights[i] = 1;
+ ++UsedQuantity;
+ }
+ else
+ {
+ uint weight = (uint)Math.Min(remainingWeight - (remainingReagents-1), reagent.RecipeMax);
+ remainingWeight -= weight;
+ currentWeights[i] = weight;
+ UsedQuantity += weight;
+ }
+ --remainingReagents;
+ }
+ }
+
+ public void SetWeight(int idx, uint quantity)
+ {
+ UsedQuantity -= currentWeights[idx];
+ currentWeights[idx] = quantity;
+ UsedQuantity += quantity;
+ }
+ }
+
+ const uint DEFAULT_MAX_QUANTITY = 14; // minimum recipe: 10 base + 4 catalysts
+ const uint DEFAULT_MAX_REAGENTS = 5;
+
+ //uint maxQuantity; // maximum number of total ingredients
+ uint maxReagents; // maximum number of reagents to use in the recipe
+ uint fullQuantityDepth; // at or equal this number of reagents, ignore ingredient settings for max quantity
+ uint fullQuantity; // The max number of a reagent to use at full quantity
+
+ ReactionSet reactions;
+ bool running = false;
+
+ SortedDictionary recipeCosts = new SortedDictionary();
+ SortedDictionary recipes = new SortedDictionary();
+
+ uint totalReagents;
+
+ List costSortedReagents = new List();
+
+ ConcurrentQueue searchQueue = new ConcurrentQueue();
+
+ int recipeCount = 0;
+
+ List generatorThreads = new List();
+ Object workerLock = new Object();
+
+ bool requestCancel = false;
+
+ // events
+ public event EventHandler Finished;
+ public event EventHandler Progress;
+ public event EventHandler NewRecipe;
+
+ public RecipeGenerator()
+ {
+ }
+
+ public SortedDictionary Recipes
+ {
+ get
+ {
+ return recipes;
+ }
+ }
+
+ public int RecipeCount
+ {
+ get
+ {
+ return recipeCount;
+ }
+ }
+
+ private class ReagentCostSort : IComparer
+ {
+ public int Compare(Reagent reagent1, Reagent reagent2)
+ {
+ return (int)reagent1.Cost - (int)reagent2.Cost;
+ }
+ }
+
+ public void InitRecipes(SortedDictionary recipes, ReactionSet reactions)
+ {
+ if (running)
+ {
+ return;
+ }
+ this.reactions = reactions;
+ foreach (PaintRecipe recipe in recipes.Values)
+ {
+ // TODO: copy?
+ AddCheapestRecipe(recipe);
+ }
+ }
+
+ public void BeginRecipeGeneration(ReactionSet reactions, uint maxQuantity, uint maxReagents, uint fullQuantityDepth, uint fullQuantity)
+ {
+ if (running)
+ {
+ // Already running - don't start again
+ return;
+ }
+ this.running = true;
+
+ this.reactions = reactions;
+ //this.maxQuantity = maxQuantity;
+ this.maxReagents = maxReagents;
+ this.fullQuantity = fullQuantity;
+ this.fullQuantityDepth = fullQuantityDepth;
+
+ // first, sort reagents by cost.
+ costSortedReagents.Clear();
+ foreach (string name in ReagentManager.Names)
+ {
+ Reagent reagent = ReagentManager.GetReagent(name);
+ if (reagent.Enabled)
+ {
+ costSortedReagents.Add(reagent);
+ }
+ }
+ costSortedReagents.Sort(new ReagentCostSort());
+ this.maxReagents = (uint)Math.Min(costSortedReagents.Count, this.maxReagents);
+
+ totalReagents = (uint)costSortedReagents.Count;
+
+ // Pre-populate recipes list with:
+ // 1) 1-ingredient recipes @ 10db for all enabled ingredients with a count >= 10
+ // 2) any previously-generated recipes
+ foreach (Reagent reagent in costSortedReagents)
+ {
+ if (!reagent.IsCatalyst && reagent.RecipeMax >= 10)
+ {
+ PaintRecipe recipe = new PaintRecipe();
+ recipe.Reactions = reactions;
+ recipe.AddReagent(reagent.Name, 10);
+ AddCheapestRecipe(recipe);
+ }
+ }
+
+ for (uint reagentIdx = 0; reagentIdx < costSortedReagents.Count; ++reagentIdx)
+ {
+ SearchNode initialNode = new SearchNode(costSortedReagents, reagentIdx);
+ initialNode.MaxQuantity = maxQuantity;
+ initialNode.MaxReagents = maxReagents;
+ searchQueue.Enqueue(initialNode);
+ }
+
+ // start worker threads to do the actual work
+
+ requestCancel = false;
+ running = true;
+ // Start the workers thread
+ for (int i = 0; i < costSortedReagents.Count; ++i)
+ {
+ Thread thr = new Thread(new ThreadStart(this.Generate));
+ generatorThreads.Add(thr);
+ }
+ foreach (Thread thr in generatorThreads)
+ {
+ thr.Start();
+ }
+
+ }
+
+ public void ResumeRecipeGeneration()
+ {
+ if (running)
+ {
+ // Already running - don't start again
+ return;
+ }
+ this.running = true;
+ requestCancel = false;
+
+ for (int i = 0; i < costSortedReagents.Count; ++i)
+ {
+ Thread thr = new Thread(new ThreadStart(this.Generate));
+ generatorThreads.Add(thr);
+ }
+ foreach (Thread thr in generatorThreads)
+ {
+ thr.Start();
+ }
+ }
+
+ private void Generate()
+ {
+ SearchNode node;
+
+ bool ok = true;
+ do
+ {
+ lock (workerLock)
+ {
+ ok = searchQueue.TryDequeue(out node);
+ }
+ if (ok)
+ {
+ uint targetQuantity = node.MaxQuantity + 1;
+ do {
+ --targetQuantity;
+ node.InitForQuantity(targetQuantity);
+ } while (targetQuantity > 10 && (node.CurrentTargetQuantity != node.UsedQuantity));
+
+ while (ok = Iterate(node) && !requestCancel)
+ {
+ if (Progress != null)
+ {
+ Progress(this, null);
+ }
+ }
+ if (ok)
+ {
+ // stopped because cancel was requested - requeue the node in its current state for resume
+ searchQueue.Enqueue(node);
+ }
+ }
+ } while (!requestCancel && ok);
+
+ bool done = false;
+ lock(workerLock)
+ {
+ generatorThreads.Remove(Thread.CurrentThread);
+
+ done = (generatorThreads.Count == 0);
+ }
+ if (done)
+ {
+ if (Finished != null)
+ {
+ Finished(this, null);
+ }
+ running = false;
+ }
+ }
+
+ // Add the cheapest recipe to the recipe list
+ // returns the discarded recipe from the pair (or null if no original recipe to replace)
+ private PaintRecipe AddCheapestRecipe(PaintRecipe recipe)
+ {
+ PaintRecipe discarded = recipe;
+ if (recipe.IsValid)
+ {
+ recipe.Reactions = reactions;
+
+ string colorName = Palette.FindNearest(recipe.ReactedColor);
+ //System.Console.WriteLine("Recipe: {0} {1}:", colorName, recipe.Cost);
+ //foreach (PaintRecipe.RecipeIngredient ingr in recipe.Ingredients)
+ //{
+ // System.Console.WriteLine(" -> {0} {1}", ingr.quantity, ingr.name);
+ //}
+ uint cost;
+ lock (workerLock)
+ {
+ if (recipeCosts.TryGetValue(colorName, out cost))
+ {
+ if (cost > recipe.Cost)
+ {
+ discarded = recipes[colorName];
+ recipeCosts[colorName] = recipe.Cost;
+ recipes[colorName] = recipe;
+ if (NewRecipe != null)
+ {
+ NewRecipeEventArgs args = new NewRecipeEventArgs(colorName, recipe);
+ NewRecipe(this, args);
+ }
+ }
+ }
+ else
+ {
+ discarded = null;
+ recipeCosts.Add(colorName, recipe.Cost);
+ recipes.Add(colorName, recipe);
+ if (NewRecipe != null)
+ {
+ NewRecipeEventArgs args = new NewRecipeEventArgs(colorName, recipe);
+ NewRecipe(this, args);
+ }
+ }
+ }
+ }
+ else
+ {
+ string msg = String.Format("Recipe is invalid ({0} ingredients)\n", recipe.Ingredients.Count);
+ foreach (PaintRecipe.RecipeIngredient ingr in recipe.Ingredients)
+ {
+ msg += String.Format(" -> {0} {1}", ingr.quantity, ingr.name);
+ }
+ lock (workerLock) {
+ Console.WriteLine(msg);
+ }
+ }
+
+ return discarded;
+ }
+
+ private bool Iterate(SearchNode node)
+ {
+ // pick recipe quantities at current recipe ingredients/size
+ if (NextRecipe(node))
+ {
+ lock(workerLock)
+ {
+ ++recipeCount;
+ }
+ //System.Console.WriteLine("Found next recipe at size {0} qty {1}", node.Reagents.Count, node.CurrentTargetQuantity);
+ return true;
+ }
+
+ if (NextRecipeSize(node))
+ {
+ //System.Console.WriteLine("Found next recipee size {0}", node.CurrentTargetQuantity);
+ return true;
+ }
+
+ // Search for next ingredient combo - all quantity combos for previous were searched
+ //System.Console.WriteLine("Finding next ingredient combo");
+ do
+ {
+ if (!node.AddNextReagent())
+ {
+ while ((node.Reagents.Count > node.InitialCount) && (node.LastReagent == (totalReagents-1)))
+ {
+ node.RemoveLastReagent();
+ }
+ if (node.Reagents.Count == node.InitialCount)
+ {
+ // done
+ return false;
+ }
+ uint nextReagent = node.NextFreeReagent(node.LastReagent);
+ while ((node.Reagents.Count > node.InitialCount) && (nextReagent >= totalReagents))
+ {
+ // No more reagents to try at this level
+ node.RemoveLastReagent();
+ if (node.Reagents.Count > node.InitialCount)
+ {
+ nextReagent = node.NextFreeReagent(node.LastReagent);
+ }
+ }
+ if (node.Reagents.Count == node.InitialCount)
+ {
+ // done
+ return false;
+ }
+ node.ReplaceLastReagent(nextReagent);
+ }
+ } while (node.MaxQuantity < (10 + node.CatalystCount));
+ node.InitForQuantity(node.MaxQuantity);
+
+ //string outStr = "{0} : {1} : ";
+ //for (int i = 0; i < currentReagents.Count; ++i)
+ //{
+ // Reagent reagent = costSortedReagents[(int)currentReagents[i]];
+ // if (i > 0)
+ // {
+ // outStr += ", ";
+ // }
+ // outStr += reagent.Name + " (" + reagent.Cost + ")";
+ //}
+ //Console.WriteLine(outStr, currentReagents.Count, recipeCount);
+ return true;
+ }
+
+ private bool NextRecipe(SearchNode node)
+ {
+ // First, run the current recipe
+ if (node.TestRecipe == null)
+ {
+ node.TestRecipe = new PaintRecipe();
+ node.TestRecipe.Reactions = reactions;
+ }
+ node.TestRecipe.Clear();
+ for (int i = 0; i < node.Reagents.Count; ++i)
+ {
+ node.TestRecipe.AddReagent(node.Reagent(i).Name, node.CurrentWeights[i]);
+ }
+ PaintRecipe replacement = AddCheapestRecipe(node.TestRecipe);
+ if (replacement == null)
+ {
+ node.TestRecipe = new PaintRecipe();
+ node.TestRecipe.Reactions = reactions;
+ }
+ else
+ {
+ node.TestRecipe = replacement;
+ }
+
+ // check for the next recipe
+ uint remainingWeight = node.CurrentTargetQuantity - node.CatalystCount;
+ if (remainingWeight < 10)
+ {
+ // not possible to make a valid recipe
+ return false;
+ }
+ //uint remainingReagents = (uint)node.Reagents.Count - node.CatalystCount;
+
+ uint depth = (uint)node.Reagents.Count;
+ uint weightToConsume = 0;
+ uint spaceBelow = 0;
+ int reagentsBelow = 0;
+ for (int i = (int)depth-1 ; i >= 0; --i)
+ {
+ uint currentWeight = node.CurrentWeights[i];
+
+ if ((spaceBelow >= (weightToConsume+1)) && (currentWeight > 1))
+ {
+ // reduce this node by 1, allocate remaining weight to reagents below it
+ node.SetWeight(i, currentWeight-1);
+ weightToConsume += 1;
+ for (int j = i+1; j < depth; ++j)
+ {
+ --reagentsBelow;
+ Reagent reagent = node.Reagent(j);
+ uint allocated = (uint)Math.Min(reagent.IsCatalyst ? 1 : (depth <= fullQuantityDepth ? fullQuantity : reagent.RecipeMax), weightToConsume - reagentsBelow);
+ if (allocated > 100)
+ {
+ Console.WriteLine("ACK: allocated = {0}", allocated);
+ }
+ node.SetWeight(j, allocated);
+ weightToConsume -= allocated;
+ }
+ break;
+ }
+ else
+ {
+ Reagent reagent = node.Reagent(i);
+ spaceBelow += (reagent.IsCatalyst ? 1 : (depth <= fullQuantityDepth ? fullQuantity : reagent.RecipeMax));
+ weightToConsume += currentWeight;
+ ++reagentsBelow;
+ }
+ }
+
+ //int recipeWeight = 0;
+ //foreach (int weight in node.CurrentWeights)
+ //{
+ // recipeWeight += weight;
+ //}
+ //if ((weightToConsume != 0) || (recipeWeight != node.CurrentTargetQuantity))
+ //{
+ // Console.WriteLine("Failed recipe with leftover weight {0} ({1}/{2}):", weightToConsume, recipeWeight, node.CurrentTargetQuantity);
+ // for (int i = 0; i < node.Reagents.Count; ++i)
+ // {
+ // Console.WriteLine(" > {0} {1}", node.Reagent(i).Name, node.CurrentWeights[i]);
+ // }
+ //}
+
+ return (weightToConsume == 0);
+ }
+
+ private bool NextRecipeSize(SearchNode node)
+ {
+ uint newQuantity = node.CurrentTargetQuantity - 1;
+ if (newQuantity < (10 + node.CatalystCount))
+ {
+ return false;
+ }
+
+ node.InitForQuantity(newQuantity);
+ if (node.CurrentTargetQuantity > node.UsedQuantity)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ public void Wait()
+ {
+ if (running)
+ {
+ foreach (Thread thr in generatorThreads)
+ {
+ thr.Join();
+ }
+ }
+ }
+
+ public void Stop()
+ {
+ this.requestCancel = true;
+ }
+
+ public void Reset()
+ {
+ recipes.Clear();
+ recipeCosts.Clear();
+ }
+ }
+}
+
diff --git a/RecipeGeneratorWindow.cs b/RecipeGeneratorWindow.cs
new file mode 100644
--- /dev/null
+++ b/RecipeGeneratorWindow.cs
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2015, Jason Maltzen
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+using System;
+using System.Collections.Generic;
+
+namespace DesertPaintLab
+{
+ public partial class RecipeGeneratorWindow : Gtk.Window
+ {
+ RecipeGenerator generator;
+ PlayerProfile profile;
+ bool canceling = false;
+ bool running = false;
+
+ static Gtk.ListStore colorStore = new Gtk.ListStore(typeof(string));
+
+ List> missingReactions = new List>();
+
+ long lastProgressUpdate;
+ long lastStatusUpdate;
+
+ static public Gtk.ListStore RecipeModel
+ {
+ get
+ {
+ return colorStore;
+ }
+ }
+
+ public RecipeGeneratorWindow(PlayerProfile profile) : base(Gtk.WindowType.Toplevel)
+ {
+ this.profile = profile;
+ this.Build();
+ maxIngredientsSpinButton.Value = 5; // TODO: read/save profile info
+ maxRecipeSpinButton.Value = 20; // TODO: read/save profile info
+ fullQuantitySpinButton.Value = 20; // TODO: read/save profile info
+ fullQuantityDepthSpinButton.Value = 4; // TODO: read/save profile info
+
+ fullQuantityDepthSpinButton.SetRange(0, ReagentManager.Names.Count);
+ maxIngredientsSpinButton.SetRange(0, ReagentManager.Names.Count);
+
+ Gtk.TreeViewColumn recipeColorColumn = new Gtk.TreeViewColumn();
+ Gtk.CellRendererText recipeColumnCell = new Gtk.CellRendererText();
+ recipeColorColumn.PackStart(recipeColumnCell, true);
+ recipeColorColumn.Title = "Color";
+
+ recipeList.AppendColumn(recipeColorColumn);
+ recipeColorColumn.AddAttribute(recipeColumnCell, "text", 0);
+
+ colorStore.SetSortColumnId(0, Gtk.SortType.Ascending);
+ recipeList.Model = RecipeModel;
+
+ recipeList.Selection.Changed += OnColorSelected;
+
+ profile.LoadRecipes();
+
+ // init UI
+ foreach (string key in profile.Recipes.Keys)
+ {
+ colorStore.AppendValues(key);
+ }
+
+ countLabel.Text = String.Format("{0} / {1}", profile.Recipes.Count, Palette.Count);
+ canceling = false;
+ running = false;
+ }
+
+ protected void OnMaxIngredientsChanged(object sender, EventArgs e)
+ {
+ // TODO: save profile setting
+ // TODO: no longer permit resume
+ }
+
+ protected void OnMaxRecipeChanged(object sender, EventArgs e)
+ {
+ // TODO: save profile setting
+ // TODO: no longer permit resume
+ }
+
+ protected void OnFullQuantityDepthChanged(object sender, EventArgs e)
+ {
+ // TODO: save profile setting
+ // TODO: no longer permit resume
+ }
+
+ protected void OnFullQuantityChanged(object sender, EventArgs e)
+ {
+ // TODO: save profile setting
+ // TODO: no longer permit resume
+ }
+
+ protected void OnBegin(object sender, EventArgs e)
+ {
+ maxIngredientsSpinButton.Sensitive = false;
+ ExportAction.Sensitive = false;
+ SettingsAction.Sensitive = false;
+ maxRecipeSpinButton.Sensitive = false;
+ beginButton.Sensitive = false; // TODO: change to "pause"?
+ stopResumeButton.Sensitive = true;
+ fullQuantitySpinButton.Sensitive = false;
+ fullQuantityDepthSpinButton.Sensitive = false;
+
+ generator = new RecipeGenerator();
+
+ generator.InitRecipes(profile.Recipes, profile.Reactions);
+ countLabel.Text = String.Format("{0} / {1}", generator.Recipes.Count, Palette.Count);
+
+ generator.Progress += OnProgress;
+ generator.Finished += OnFinished;
+ generator.NewRecipe += OnNewRecipe;
+ // TODO: hook up event notifications
+ // - progress
+ // - complete
+ // - new recipe / recipe update
+
+ // Total recipe search count
+ //int current = ReagentManager.Names.Count;
+ //long recipePermutations = 1;
+ //for (int i = 0; i < maxIngredientsSpinButton.ValueAsInt; ++i)
+ //{
+ // recipePermutations *= current;
+ // --current;
+ //}
+ //System.Console.WriteLine("Will search {0} reagent permutations.", recipePermutations);
+
+ lastProgressUpdate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+ lastStatusUpdate = lastProgressUpdate;
+
+ running = true;
+ stopResumeButton.Label = "Stop";
+
+ generator.BeginRecipeGeneration(profile.Reactions, (uint)maxRecipeSpinButton.ValueAsInt, (uint)maxIngredientsSpinButton.ValueAsInt, (uint)fullQuantityDepthSpinButton.ValueAsInt, (uint)fullQuantitySpinButton.ValueAsInt);
+ }
+
+ protected void OnStopResume(object sender, EventArgs e)
+ {
+ if (generator != null)
+ {
+ if (running)
+ {
+ canceling = true;
+ generator.Stop();
+ }
+ else
+ {
+ // this must be a resume
+ lastProgressUpdate = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+ lastStatusUpdate = lastProgressUpdate;
+
+ canceling = false;
+ running = true;
+
+ stopResumeButton.Label = "Stop";
+ generator.BeginRecipeGeneration(profile.Reactions, (uint)maxRecipeSpinButton.ValueAsInt, (uint)maxIngredientsSpinButton.ValueAsInt, (uint)fullQuantityDepthSpinButton.ValueAsInt, (uint)fullQuantitySpinButton.ValueAsInt);
+ }
+ }
+ }
+
+ protected void OnFinished(object sender, EventArgs args)
+ {
+ Gtk.Application.Invoke(delegate {
+ running = false;
+ beginButton.Sensitive = true;
+ ExportAction.Sensitive = true;
+ SettingsAction.Sensitive = true;
+ stopResumeButton.Sensitive = false;
+ maxIngredientsSpinButton.Sensitive = true;
+ maxRecipeSpinButton.Sensitive = true;
+ fullQuantitySpinButton.Sensitive = true;
+ fullQuantityDepthSpinButton.Sensitive = true;
+ //generator = null; // don't. Hang on to generator for resume.
+ profile.SaveRecipes();
+ if (canceling)
+ {
+ stopResumeButton.Label = "Resume";
+ stopResumeButton.Sensitive = true;
+ }
+ });
+ }
+
+ protected void OnNewRecipe(object sender, NewRecipeEventArgs args)
+ {
+ Gtk.Application.Invoke(delegate
+ {
+ progressBar.Pulse();
+ // TODO: Add item to recipe list only if not already listed
+ bool exists = false;
+ Gtk.TreeIter iter;
+ if (colorStore.GetIterFirst(out iter))
+ {
+ do
+ {
+ string color = (string)colorStore.GetValue(iter, 0);
+ if (color.Equals(args.Color))
+ {
+ exists = true;
+ break;
+ }
+ } while (colorStore.IterNext(ref iter));
+ }
+ if (!exists)
+ {
+ Console.WriteLine("Add new recipe for {0}", args.Color);
+ // bool isMissingReactions = args.Recipe.CheckMissingReactions(ref missingReactions);
+ // string missingReactionLabel = isMissingReactions ? "X" : "";
+ colorStore.AppendValues(args.Color); // , missingReactionLabel);
+ countLabel.Text = String.Format("{0} / {1}", generator.Recipes.Count, Palette.Count);
+ }
+ profile.SetRecipe(args.Recipe);
+ });
+ }
+
+ protected void OnProgress(object sender, EventArgs args)
+ {
+ Gtk.Application.Invoke(delegate
+ {
+ // TODO: based on time rather than count
+ long progressTime = DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond;
+ long delta = progressTime - lastProgressUpdate;
+ if (delta > 30)
+ {
+ progressBar.Pulse();
+ lastProgressUpdate = progressTime;
+ }
+ delta = progressTime - lastStatusUpdate;
+ if (delta > 500)
+ {
+ // Update recipe count label as well
+ statusLabel.Text = String.Format("Recipes searched: {0:N00}", generator.RecipeCount);
+ lastStatusUpdate = progressTime;
+ }
+ //progressBar.Fraction = (double)((generator.RecipeCount / 10000) % 100) / 100.0;
+ });
+ }
+
+ protected void OnColorSelected(object o, EventArgs args)
+ {
+ Gtk.TreeModel model;
+ Gtk.TreeIter iter;
+ Gtk.TreeSelection selection = recipeList.Selection;
+ if ((selection != null) && selection.GetSelected(out model, out iter))
+ {
+ string colorName = (string)colorStore.GetValue(iter, 0);
+ PaintRecipe recipe;
+ if (profile.Recipes.TryGetValue(colorName, out recipe))
+ {
+ foreach (Gtk.Widget child in recipeListBox.AllChildren)
+ {
+ recipeListBox.Remove(child);
+ }
+ if (recipe.CheckMissingReactions(ref missingReactions))
+ {
+ statusLabel.Text = "WARNING: This recipe includes reactions that have not yet been recorded.";
+ }
+ foreach (PaintRecipe.RecipeIngredient ingredient in recipe.Ingredients)
+ {
+ Gtk.Label label = new Gtk.Label(ingredient.quantity.ToString() + " " + ingredient.name);
+ recipeListBox.PackStart(label);
+ label.Show();
+ }
+ }
+ paintSwatch.Color = recipe.ReactedColor;
+ }
+ }
+
+ protected void OnExportToWiki(object sender, EventArgs e)
+ {
+ Gtk.FileChooserDialog fileDialog =
+ new Gtk.FileChooserDialog("Select destination file.",
+ this, Gtk.FileChooserAction.Save,
+ Gtk.Stock.Cancel, Gtk.ResponseType.Cancel,
+ Gtk.Stock.Save, Gtk.ResponseType.Accept);
+ Gtk.ResponseType resp = (Gtk.ResponseType)fileDialog.Run();
+ if (resp == Gtk.ResponseType.Accept)
+ {
+ string fileName = fileDialog.Filename;
+ string directory = fileDialog.CurrentFolder;
+ profile.ExportWikiRecipes(System.IO.Path.Combine(directory, fileName));
+ }
+ fileDialog.Destroy();
+ }
+
+ protected void OnShowIngredients(object sender, EventArgs e)
+ {
+ ReagentWindow win = new ReagentWindow(profile);
+ win.Show();
+ }
+ }
+}
+
diff --git a/SimulatorWindow.cs b/SimulatorWindow.cs
--- a/SimulatorWindow.cs
+++ b/SimulatorWindow.cs
@@ -27,14 +27,30 @@ namespace DesertPaintLab
{
public partial class SimulatorWindow : Gtk.Window
{
- PlayerProfile profile;
Gtk.ListStore recipeData = new Gtk.ListStore(typeof(string), typeof(int));
- PaintRecipe paintRecipe = new PaintRecipe();
+ PaintRecipe paintRecipe;
+
+ List missingWarned = new List();
+ List> newMissing = new List>();
+
+ private class IngredientPair
+ {
+ public string first;
+ public string second;
+
+ public IngredientPair(string first, string second)
+ {
+ this.first = first;
+ this.second = second;
+ }
+ }
public SimulatorWindow(PlayerProfile profile) : base(Gtk.WindowType.Toplevel)
{
- this.profile = profile;
this.Build ();
+
+ paintRecipe = new PaintRecipe();
+ paintRecipe.Reactions = profile.Reactions;
Gtk.TreeViewColumn reagentColumn = new Gtk.TreeViewColumn();
Gtk.CellRendererText reagentColumnCell = new Gtk.CellRendererText();
@@ -145,9 +161,35 @@ namespace DesertPaintLab
}
while (recipeData.IterNext(ref iter));
- PaintColor resultColor = new PaintColor();
- paintRecipe.ComputeReactedColor(profile, ref resultColor);
- paintSwatch.Color = resultColor;
+ PaintColor resultColor = new PaintColor(paintRecipe.ReactedColor);
+ paintSwatch.Color = resultColor;
+ if (paintRecipe.CheckMissingReactions(ref newMissing))
+ {
+ string warningMsg = "";
+
+ foreach (KeyValuePair newEntry in newMissing)
+ {
+ IngredientPair match = missingWarned.Find(x => (x.first.Equals(newEntry.Key) && x.second.Equals(newEntry.Value)));
+ if (match == null)
+ {
+ match = new IngredientPair(newEntry.Key, newEntry.Value);
+ missingWarned.Add(match);
+ warningMsg += newEntry.Key + " + " + newEntry.Value + "\n";
+ }
+ }
+ if (warningMsg.Length > 0)
+ {
+ Gtk.MessageDialog md = new Gtk.MessageDialog(this,
+ Gtk.DialogFlags.DestroyWithParent,
+ Gtk.MessageType.Warning,
+ Gtk.ButtonsType.Ok,
+ "These combinations have not yet had reactions recorded:\n\n" +
+ warningMsg);
+ md.Run();
+ md.Destroy();
+ }
+ }
+
}
protected virtual void OnIncrementReagent (object sender, System.EventArgs e)
diff --git a/bin/Debug/DesertPaintLab/template/ingredients.txt b/bin/Debug/DesertPaintLab/template/ingredients.txt
--- a/bin/Debug/DesertPaintLab/template/ingredients.txt
+++ b/bin/Debug/DesertPaintLab/template/ingredients.txt
@@ -5,17 +5,17 @@
// It is not recommended to set many of the ingredients above 10 per paint
Cabbage | 128, 64, 144 | 8 | Y | bulk | 10
-Clay | 128, 96, 32 | 4 | Y | bulk | 20
-Carrot | 224, 112, 32 | 10 | Y | bulk | 10
-Copper | 64, 192, 192 | 30 | Y | normal | 8
-Iron | 96, 48, 32 | 30 | Y | normal | 8
+Carrot | 224, 112, 32 | 8 | Y | bulk | 10
+Clay | 128, 96, 32 | 10 | Y | bulk | 20
+DeadTongue | 112, 64, 64 | 500 | Y | normal | 4
+ToadSkin | 48, 96, 48 | 500 | Y | normal | 4
+EarthLight | 128, 240, 224 | 10000 | Y | normal | 4
+RedSand | 144, 16, 24 | 4 | Y | bulk | 20
Lead | 80, 80, 96 | 50 | Y | normal | 6
-RedSand | 144, 16, 24 | 10 | Y | bulk | 20
-Silver | 16, 16, 32 | 50 | N | normal | 6
-ToadSkin | 48, 96, 48 | 500 | N | normal | 4
-DeadTongue | 112, 64, 64 | 500 | N | normal | 4
-EarthLight | 128, 240, 224 | 10000 | N | normal | 4
+Silver | 16, 16, 32 | 50 | Y | normal | 6
+Iron | 96, 48, 32 | 30 | Y | normal | 8
+Copper | 64, 192, 192 | 30 | Y | normal | 8
+Sulfur | catalyst | 15 | Y | normal | 1
+Potash | catalyst | 50 | Y | normal | 1
Lime | catalyst | 20 | Y | normal | 1
-Sulfur | catalyst | 10 | Y | normal | 1
-Potash | catalyst | 50 | Y | normal | 1
-Saltpeter | catalyst | 10 | Y | normal | 1
\ No newline at end of file
+Saltpeter | catalyst | 10 | Y | normal | 1
diff --git a/bin/Debug/ingredients.txt b/bin/Debug/ingredients.txt
new file mode 100644
--- /dev/null
+++ b/bin/Debug/ingredients.txt
@@ -0,0 +1,19 @@
+// Desert Paint Lab Ingredients
+// Name | Practical Paint Name | RGB values
+// These should be kept in the order they show up on the paint bench
+
+Cabbage Juice | Cabbage | 128, 64, 144
+Carrot | Carrot | 224, 112, 32
+Clay | Clay | 128, 96, 32
+Dead Tongue | DeadTongue | 112, 64, 64
+Toad Skin | ToadSkin | 48, 96, 48
+Earth Light | EarthLight | 128, 240, 224
+Red Sand | RedSand | 144, 16, 24
+Lead | Lead | 80, 80, 96
+Silver Powder | Silver | 16, 16, 32
+Iron | Iron | 96, 48, 32
+Copper | Copper | 64, 192, 192
+Sulfur | Sulfur | catalyst
+Potash | Potash | catalyst
+Lime | Lime | catalyst
+Saltpeter | Saltpeter | catalyst
diff --git a/bin/Debug/template/ingredients.txt b/bin/Debug/template/ingredients.txt
--- a/bin/Debug/template/ingredients.txt
+++ b/bin/Debug/template/ingredients.txt
@@ -5,17 +5,17 @@
// It is not recommended to set many of the ingredients above 10 per paint
Cabbage | 128, 64, 144 | 8 | Y | bulk | 10
-Clay | 128, 96, 32 | 4 | Y | bulk | 20
-Carrot | 224, 112, 32 | 10 | Y | bulk | 10
-Copper | 64, 192, 192 | 30 | Y | normal | 8
-Iron | 96, 48, 32 | 30 | Y | normal | 8
+Carrot | 224, 112, 32 | 8 | Y | bulk | 10
+Clay | 128, 96, 32 | 10 | Y | bulk | 20
+DeadTongue | 112, 64, 64 | 500 | Y | normal | 4
+ToadSkin | 48, 96, 48 | 500 | Y | normal | 4
+EarthLight | 128, 240, 224 | 10000 | Y | normal | 4
+RedSand | 144, 16, 24 | 4 | Y | bulk | 20
Lead | 80, 80, 96 | 50 | Y | normal | 6
-RedSand | 144, 16, 24 | 10 | Y | bulk | 20
-Silver | 16, 16, 32 | 50 | N | normal | 6
-ToadSkin | 48, 96, 48 | 500 | N | normal | 4
-DeadTongue | 112, 64, 64 | 500 | N | normal | 4
-EarthLight | 128, 240, 224 | 10000 | N | normal | 4
+Silver | 16, 16, 32 | 50 | Y | normal | 6
+Iron | 96, 48, 32 | 30 | Y | normal | 8
+Copper | 64, 192, 192 | 30 | Y | normal | 8
+Sulfur | catalyst | 15 | Y | normal | 1
+Potash | catalyst | 50 | Y | normal | 1
Lime | catalyst | 20 | Y | normal | 1
-Sulfur | catalyst | 10 | Y | normal | 1
-Potash | catalyst | 50 | Y | normal | 1
-Saltpeter | catalyst | 10 | Y | normal | 1
\ No newline at end of file
+Saltpeter | catalyst | 10 | Y | normal | 1
diff --git a/bin/Release/ingredients.txt b/bin/Release/ingredients.txt
new file mode 100644
--- /dev/null
+++ b/bin/Release/ingredients.txt
@@ -0,0 +1,19 @@
+// Desert Paint Lab Ingredients
+// Name | Practical Paint Name | RGB values
+// These should be kept in the order they show up on the paint bench
+
+Cabbage Juice | Cabbage | 128, 64, 144
+Carrot | Carrot | 224, 112, 32
+Clay | Clay | 128, 96, 32
+Dead Tongue | DeadTongue | 112, 64, 64
+Toad Skin | ToadSkin | 48, 96, 48
+Earth Light | EarthLight | 128, 240, 224
+Red Sand | RedSand | 144, 16, 24
+Lead | Lead | 80, 80, 96
+Silver Powder | Silver | 16, 16, 32
+Iron | Iron | 96, 48, 32
+Copper | Copper | 64, 192, 192
+Sulfur | Sulfur | catalyst
+Potash | Potash | catalyst
+Lime | Lime | catalyst
+Saltpeter | Saltpeter | catalyst
diff --git a/bin/Release/template/ingredients.txt b/bin/Release/template/ingredients.txt
--- a/bin/Release/template/ingredients.txt
+++ b/bin/Release/template/ingredients.txt
@@ -5,17 +5,17 @@
// It is not recommended to set many of the ingredients above 10 per paint
Cabbage | 128, 64, 144 | 8 | Y | bulk | 10
-Clay | 128, 96, 32 | 4 | Y | bulk | 20
-Carrot | 224, 112, 32 | 10 | Y | bulk | 10
-Copper | 64, 192, 192 | 30 | Y | normal | 8
-Iron | 96, 48, 32 | 30 | Y | normal | 8
+Carrot | 224, 112, 32 | 8 | Y | bulk | 10
+Clay | 128, 96, 32 | 10 | Y | bulk | 20
+DeadTongue | 112, 64, 64 | 500 | Y | normal | 4
+ToadSkin | 48, 96, 48 | 500 | Y | normal | 4
+EarthLight | 128, 240, 224 | 10000 | Y | normal | 4
+RedSand | 144, 16, 24 | 4 | Y | bulk | 20
Lead | 80, 80, 96 | 50 | Y | normal | 6
-RedSand | 144, 16, 24 | 10 | Y | bulk | 20
-Silver | 16, 16, 32 | 50 | N | normal | 6
-ToadSkin | 48, 96, 48 | 500 | N | normal | 4
-DeadTongue | 112, 64, 64 | 500 | N | normal | 4
-EarthLight | 128, 240, 224 | 10000 | N | normal | 4
+Silver | 16, 16, 32 | 50 | Y | normal | 6
+Iron | 96, 48, 32 | 30 | Y | normal | 8
+Copper | 64, 192, 192 | 30 | Y | normal | 8
+Sulfur | catalyst | 15 | Y | normal | 1
+Potash | catalyst | 50 | Y | normal | 1
Lime | catalyst | 20 | Y | normal | 1
-Sulfur | catalyst | 10 | Y | normal | 1
-Potash | catalyst | 50 | Y | normal | 1
-Saltpeter | catalyst | 10 | Y | normal | 1
\ No newline at end of file
+Saltpeter | catalyst | 10 | Y | normal | 1
diff --git a/gtk-gui/DesertPaintLab.ReagentWindow.cs b/gtk-gui/DesertPaintLab.ReagentWindow.cs
new file mode 100644
--- /dev/null
+++ b/gtk-gui/DesertPaintLab.ReagentWindow.cs
@@ -0,0 +1,101 @@
+
+// This file has been generated by the GUI designer. Do not modify.
+namespace DesertPaintLab
+{
+ public partial class ReagentWindow
+ {
+ private global::Gtk.VBox vbox2;
+
+ private global::Gtk.Frame frame3;
+
+ private global::Gtk.Alignment GtkAlignment;
+
+ private global::Gtk.Table ingredientTable;
+
+ private global::Gtk.Label GtkLabel;
+
+ private global::Gtk.HButtonBox hbuttonbox3;
+
+ private global::Gtk.Button okButton;
+
+ private global::Gtk.Button cancelButton;
+
+ protected virtual void Build ()
+ {
+ global::Stetic.Gui.Initialize (this);
+ // Widget DesertPaintLab.ReagentWindow
+ this.Name = "DesertPaintLab.ReagentWindow";
+ this.Title = "ReagentWindow";
+ this.WindowPosition = ((global::Gtk.WindowPosition)(4));
+ // Container child DesertPaintLab.ReagentWindow.Gtk.Container+ContainerChild
+ this.vbox2 = new global::Gtk.VBox ();
+ this.vbox2.Name = "vbox2";
+ this.vbox2.Spacing = 6;
+ this.vbox2.BorderWidth = ((uint)(8));
+ // Container child vbox2.Gtk.Box+BoxChild
+ this.frame3 = new global::Gtk.Frame ();
+ this.frame3.Name = "frame3";
+ this.frame3.ShadowType = ((global::Gtk.ShadowType)(0));
+ // Container child frame3.Gtk.Container+ContainerChild
+ this.GtkAlignment = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
+ this.GtkAlignment.Name = "GtkAlignment";
+ this.GtkAlignment.LeftPadding = ((uint)(12));
+ // Container child GtkAlignment.Gtk.Container+ContainerChild
+ this.ingredientTable = new global::Gtk.Table (((uint)(3)), ((uint)(3)), false);
+ this.ingredientTable.Name = "ingredientTable";
+ this.ingredientTable.RowSpacing = ((uint)(6));
+ this.ingredientTable.ColumnSpacing = ((uint)(6));
+ this.GtkAlignment.Add (this.ingredientTable);
+ this.frame3.Add (this.GtkAlignment);
+ this.GtkLabel = new global::Gtk.Label ();
+ this.GtkLabel.Name = "GtkLabel";
+ this.GtkLabel.LabelProp = "Ingredients";
+ this.GtkLabel.UseMarkup = true;
+ this.frame3.LabelWidget = this.GtkLabel;
+ this.vbox2.Add (this.frame3);
+ global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.frame3]));
+ w3.Position = 0;
+ // Container child vbox2.Gtk.Box+BoxChild
+ this.hbuttonbox3 = new global::Gtk.HButtonBox ();
+ this.hbuttonbox3.Name = "hbuttonbox3";
+ // Container child hbuttonbox3.Gtk.ButtonBox+ButtonBoxChild
+ this.okButton = new global::Gtk.Button ();
+ this.okButton.WidthRequest = 80;
+ this.okButton.CanFocus = true;
+ this.okButton.Name = "okButton";
+ this.okButton.UseUnderline = true;
+ this.okButton.Label = "Ok";
+ this.hbuttonbox3.Add (this.okButton);
+ global::Gtk.ButtonBox.ButtonBoxChild w4 = ((global::Gtk.ButtonBox.ButtonBoxChild)(this.hbuttonbox3 [this.okButton]));
+ w4.Expand = false;
+ w4.Fill = false;
+ // Container child hbuttonbox3.Gtk.ButtonBox+ButtonBoxChild
+ this.cancelButton = new global::Gtk.Button ();
+ this.cancelButton.WidthRequest = 80;
+ this.cancelButton.CanFocus = true;
+ this.cancelButton.Name = "cancelButton";
+ this.cancelButton.UseUnderline = true;
+ this.cancelButton.Label = "Cancel";
+ this.hbuttonbox3.Add (this.cancelButton);
+ global::Gtk.ButtonBox.ButtonBoxChild w5 = ((global::Gtk.ButtonBox.ButtonBoxChild)(this.hbuttonbox3 [this.cancelButton]));
+ w5.Position = 1;
+ w5.Expand = false;
+ w5.Fill = false;
+ this.vbox2.Add (this.hbuttonbox3);
+ global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbuttonbox3]));
+ w6.PackType = ((global::Gtk.PackType)(1));
+ w6.Position = 1;
+ w6.Expand = false;
+ w6.Fill = false;
+ this.Add (this.vbox2);
+ if ((this.Child != null)) {
+ this.Child.ShowAll ();
+ }
+ this.DefaultWidth = 400;
+ this.DefaultHeight = 357;
+ this.Show ();
+ this.okButton.Clicked += new global::System.EventHandler (this.OnOK);
+ this.cancelButton.Clicked += new global::System.EventHandler (this.OnCancel);
+ }
+ }
+}
diff --git a/gtk-gui/DesertPaintLab.RecipeGeneratorWindow.cs b/gtk-gui/DesertPaintLab.RecipeGeneratorWindow.cs
new file mode 100644
--- /dev/null
+++ b/gtk-gui/DesertPaintLab.RecipeGeneratorWindow.cs
@@ -0,0 +1,392 @@
+
+// This file has been generated by the GUI designer. Do not modify.
+namespace DesertPaintLab
+{
+ public partial class RecipeGeneratorWindow
+ {
+ private global::Gtk.UIManager UIManager;
+
+ private global::Gtk.Action ExportAction;
+
+ private global::Gtk.Action ExportToWikiAction;
+
+ private global::Gtk.Action SettingsAction;
+
+ private global::Gtk.Action IngredientsAction;
+
+ private global::Gtk.VBox vbox2;
+
+ private global::Gtk.MenuBar menubar1;
+
+ private global::Gtk.HBox hbox1;
+
+ private global::Gtk.VBox vbox8;
+
+ private global::Gtk.Label label3;
+
+ private global::Gtk.SpinButton maxIngredientsSpinButton;
+
+ private global::Gtk.HSeparator hseparator3;
+
+ private global::Gtk.Label label4;
+
+ private global::Gtk.SpinButton maxRecipeSpinButton;
+
+ private global::Gtk.HSeparator hseparator4;
+
+ private global::Gtk.Label label8;
+
+ private global::Gtk.SpinButton fullQuantityDepthSpinButton;
+
+ private global::Gtk.Label label9;
+
+ private global::Gtk.SpinButton fullQuantitySpinButton;
+
+ private global::Gtk.ScrolledWindow GtkScrolledWindow1;
+
+ private global::Gtk.TreeView recipeList;
+
+ private global::Gtk.VBox vbox3;
+
+ private global::Gtk.Frame frame2;
+
+ private global::Gtk.Alignment GtkAlignment;
+
+ private global::Gtk.ScrolledWindow scrolledwindow1;
+
+ private global::Gtk.VBox recipeListBox;
+
+ private global::Gtk.Label recipeLabel;
+
+ private global::DesertPaintLab.PaintSwatch paintSwatch;
+
+ private global::Gtk.HBox hbox3;
+
+ private global::Gtk.HSeparator hseparator2;
+
+ private global::Gtk.Button stopResumeButton;
+
+ private global::Gtk.Label countLabel;
+
+ private global::Gtk.Button beginButton;
+
+ private global::Gtk.HSeparator hseparator1;
+
+ private global::Gtk.Label statusLabel;
+
+ private global::Gtk.ProgressBar progressBar;
+
+ protected virtual void Build ()
+ {
+ global::Stetic.Gui.Initialize (this);
+ // Widget DesertPaintLab.RecipeGeneratorWindow
+ this.UIManager = new global::Gtk.UIManager ();
+ global::Gtk.ActionGroup w1 = new global::Gtk.ActionGroup ("Default");
+ this.ExportAction = new global::Gtk.Action ("ExportAction", "Export", null, null);
+ this.ExportAction.ShortLabel = "Export";
+ w1.Add (this.ExportAction, null);
+ this.ExportToWikiAction = new global::Gtk.Action ("ExportToWikiAction", "Export to Wiki", null, null);
+ this.ExportToWikiAction.ShortLabel = "Export to Wiki";
+ w1.Add (this.ExportToWikiAction, null);
+ this.SettingsAction = new global::Gtk.Action ("SettingsAction", "Settings", null, null);
+ this.SettingsAction.ShortLabel = "Tools";
+ w1.Add (this.SettingsAction, null);
+ this.IngredientsAction = new global::Gtk.Action ("IngredientsAction", "Ingredients", null, null);
+ this.IngredientsAction.ShortLabel = "Ingredients";
+ w1.Add (this.IngredientsAction, null);
+ this.UIManager.InsertActionGroup (w1, 0);
+ this.AddAccelGroup (this.UIManager.AccelGroup);
+ this.Name = "DesertPaintLab.RecipeGeneratorWindow";
+ this.Title = "Recipe Generator";
+ this.WindowPosition = ((global::Gtk.WindowPosition)(4));
+ // Container child DesertPaintLab.RecipeGeneratorWindow.Gtk.Container+ContainerChild
+ this.vbox2 = new global::Gtk.VBox ();
+ this.vbox2.Name = "vbox2";
+ this.vbox2.Spacing = 6;
+ this.vbox2.BorderWidth = ((uint)(8));
+ // Container child vbox2.Gtk.Box+BoxChild
+ this.UIManager.AddUiFromString ("");
+ this.menubar1 = ((global::Gtk.MenuBar)(this.UIManager.GetWidget ("/menubar1")));
+ this.menubar1.Name = "menubar1";
+ this.vbox2.Add (this.menubar1);
+ global::Gtk.Box.BoxChild w2 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.menubar1]));
+ w2.Position = 0;
+ w2.Expand = false;
+ w2.Fill = false;
+ // Container child vbox2.Gtk.Box+BoxChild
+ this.hbox1 = new global::Gtk.HBox ();
+ this.hbox1.Name = "hbox1";
+ this.hbox1.Spacing = 6;
+ // Container child hbox1.Gtk.Box+BoxChild
+ this.vbox8 = new global::Gtk.VBox ();
+ this.vbox8.Name = "vbox8";
+ this.vbox8.Spacing = 6;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.label3 = new global::Gtk.Label ();
+ this.label3.Name = "label3";
+ this.label3.LabelProp = "Maximum Ingredients";
+ this.vbox8.Add (this.label3);
+ global::Gtk.Box.BoxChild w3 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label3]));
+ w3.Position = 0;
+ w3.Expand = false;
+ w3.Fill = false;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.maxIngredientsSpinButton = new global::Gtk.SpinButton (0, 14, 1);
+ this.maxIngredientsSpinButton.CanFocus = true;
+ this.maxIngredientsSpinButton.Name = "maxIngredientsSpinButton";
+ this.maxIngredientsSpinButton.Adjustment.PageIncrement = 10;
+ this.maxIngredientsSpinButton.ClimbRate = 1;
+ this.maxIngredientsSpinButton.Numeric = true;
+ this.vbox8.Add (this.maxIngredientsSpinButton);
+ global::Gtk.Box.BoxChild w4 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.maxIngredientsSpinButton]));
+ w4.Position = 1;
+ w4.Expand = false;
+ w4.Fill = false;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.hseparator3 = new global::Gtk.HSeparator ();
+ this.hseparator3.Name = "hseparator3";
+ this.vbox8.Add (this.hseparator3);
+ global::Gtk.Box.BoxChild w5 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator3]));
+ w5.Position = 2;
+ w5.Expand = false;
+ w5.Fill = false;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.label4 = new global::Gtk.Label ();
+ this.label4.Name = "label4";
+ this.label4.LabelProp = "Max Total Quantity";
+ this.label4.UseMarkup = true;
+ this.label4.Wrap = true;
+ this.vbox8.Add (this.label4);
+ global::Gtk.Box.BoxChild w6 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label4]));
+ w6.Position = 3;
+ w6.Expand = false;
+ w6.Fill = false;
+ w6.Padding = ((uint)(8));
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.maxRecipeSpinButton = new global::Gtk.SpinButton (0, 100, 1);
+ this.maxRecipeSpinButton.CanFocus = true;
+ this.maxRecipeSpinButton.Name = "maxRecipeSpinButton";
+ this.maxRecipeSpinButton.Adjustment.PageIncrement = 10;
+ this.maxRecipeSpinButton.ClimbRate = 1;
+ this.maxRecipeSpinButton.Numeric = true;
+ this.vbox8.Add (this.maxRecipeSpinButton);
+ global::Gtk.Box.BoxChild w7 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.maxRecipeSpinButton]));
+ w7.Position = 4;
+ w7.Expand = false;
+ w7.Fill = false;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.hseparator4 = new global::Gtk.HSeparator ();
+ this.hseparator4.Name = "hseparator4";
+ this.vbox8.Add (this.hseparator4);
+ global::Gtk.Box.BoxChild w8 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.hseparator4]));
+ w8.Position = 5;
+ w8.Expand = false;
+ w8.Fill = false;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.label8 = new global::Gtk.Label ();
+ this.label8.Name = "label8";
+ this.label8.LabelProp = "Full Quantity Depth";
+ this.vbox8.Add (this.label8);
+ global::Gtk.Box.BoxChild w9 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label8]));
+ w9.Position = 6;
+ w9.Expand = false;
+ w9.Fill = false;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.fullQuantityDepthSpinButton = new global::Gtk.SpinButton (0, 15, 1);
+ this.fullQuantityDepthSpinButton.CanFocus = true;
+ this.fullQuantityDepthSpinButton.Name = "fullQuantityDepthSpinButton";
+ this.fullQuantityDepthSpinButton.Adjustment.PageIncrement = 10;
+ this.fullQuantityDepthSpinButton.ClimbRate = 1;
+ this.fullQuantityDepthSpinButton.Numeric = true;
+ this.vbox8.Add (this.fullQuantityDepthSpinButton);
+ global::Gtk.Box.BoxChild w10 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.fullQuantityDepthSpinButton]));
+ w10.Position = 7;
+ w10.Expand = false;
+ w10.Fill = false;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.label9 = new global::Gtk.Label ();
+ this.label9.Name = "label9";
+ this.label9.LabelProp = "FullQuantity";
+ this.vbox8.Add (this.label9);
+ global::Gtk.Box.BoxChild w11 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.label9]));
+ w11.Position = 8;
+ w11.Expand = false;
+ w11.Fill = false;
+ // Container child vbox8.Gtk.Box+BoxChild
+ this.fullQuantitySpinButton = new global::Gtk.SpinButton (0, 30, 1);
+ this.fullQuantitySpinButton.CanFocus = true;
+ this.fullQuantitySpinButton.Name = "fullQuantitySpinButton";
+ this.fullQuantitySpinButton.Adjustment.PageIncrement = 10;
+ this.fullQuantitySpinButton.ClimbRate = 1;
+ this.fullQuantitySpinButton.Numeric = true;
+ this.vbox8.Add (this.fullQuantitySpinButton);
+ global::Gtk.Box.BoxChild w12 = ((global::Gtk.Box.BoxChild)(this.vbox8 [this.fullQuantitySpinButton]));
+ w12.Position = 9;
+ w12.Expand = false;
+ w12.Fill = false;
+ this.hbox1.Add (this.vbox8);
+ global::Gtk.Box.BoxChild w13 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox8]));
+ w13.Position = 0;
+ w13.Expand = false;
+ w13.Fill = false;
+ // Container child hbox1.Gtk.Box+BoxChild
+ this.GtkScrolledWindow1 = new global::Gtk.ScrolledWindow ();
+ this.GtkScrolledWindow1.Name = "GtkScrolledWindow1";
+ this.GtkScrolledWindow1.ShadowType = ((global::Gtk.ShadowType)(1));
+ // Container child GtkScrolledWindow1.Gtk.Container+ContainerChild
+ this.recipeList = new global::Gtk.TreeView ();
+ this.recipeList.WidthRequest = 300;
+ this.recipeList.CanFocus = true;
+ this.recipeList.Name = "recipeList";
+ this.GtkScrolledWindow1.Add (this.recipeList);
+ this.hbox1.Add (this.GtkScrolledWindow1);
+ global::Gtk.Box.BoxChild w15 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.GtkScrolledWindow1]));
+ w15.Position = 1;
+ // Container child hbox1.Gtk.Box+BoxChild
+ this.vbox3 = new global::Gtk.VBox ();
+ this.vbox3.Name = "vbox3";
+ this.vbox3.Spacing = 6;
+ // Container child vbox3.Gtk.Box+BoxChild
+ this.frame2 = new global::Gtk.Frame ();
+ this.frame2.WidthRequest = 200;
+ this.frame2.HeightRequest = 200;
+ this.frame2.Name = "frame2";
+ this.frame2.ShadowType = ((global::Gtk.ShadowType)(0));
+ // Container child frame2.Gtk.Container+ContainerChild
+ this.GtkAlignment = new global::Gtk.Alignment (0F, 0F, 1F, 1F);
+ this.GtkAlignment.Name = "GtkAlignment";
+ this.GtkAlignment.LeftPadding = ((uint)(12));
+ // Container child GtkAlignment.Gtk.Container+ContainerChild
+ this.scrolledwindow1 = new global::Gtk.ScrolledWindow ();
+ this.scrolledwindow1.CanFocus = true;
+ this.scrolledwindow1.Name = "scrolledwindow1";
+ this.scrolledwindow1.ShadowType = ((global::Gtk.ShadowType)(1));
+ // Container child scrolledwindow1.Gtk.Container+ContainerChild
+ global::Gtk.Viewport w16 = new global::Gtk.Viewport ();
+ w16.ShadowType = ((global::Gtk.ShadowType)(0));
+ // Container child GtkViewport.Gtk.Container+ContainerChild
+ this.recipeListBox = new global::Gtk.VBox ();
+ this.recipeListBox.Name = "recipeListBox";
+ this.recipeListBox.Homogeneous = true;
+ this.recipeListBox.Spacing = 6;
+ this.recipeListBox.BorderWidth = ((uint)(1));
+ w16.Add (this.recipeListBox);
+ this.scrolledwindow1.Add (w16);
+ this.GtkAlignment.Add (this.scrolledwindow1);
+ this.frame2.Add (this.GtkAlignment);
+ this.recipeLabel = new global::Gtk.Label ();
+ this.recipeLabel.Name = "recipeLabel";
+ this.recipeLabel.LabelProp = "Recipe";
+ this.recipeLabel.UseMarkup = true;
+ this.frame2.LabelWidget = this.recipeLabel;
+ this.vbox3.Add (this.frame2);
+ global::Gtk.Box.BoxChild w21 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.frame2]));
+ w21.Position = 0;
+ // Container child vbox3.Gtk.Box+BoxChild
+ this.paintSwatch = new global::DesertPaintLab.PaintSwatch ();
+ this.paintSwatch.HeightRequest = 200;
+ this.paintSwatch.Events = ((global::Gdk.EventMask)(256));
+ this.paintSwatch.Name = "paintSwatch";
+ this.vbox3.Add (this.paintSwatch);
+ global::Gtk.Box.BoxChild w22 = ((global::Gtk.Box.BoxChild)(this.vbox3 [this.paintSwatch]));
+ w22.Position = 1;
+ this.hbox1.Add (this.vbox3);
+ global::Gtk.Box.BoxChild w23 = ((global::Gtk.Box.BoxChild)(this.hbox1 [this.vbox3]));
+ w23.Position = 2;
+ w23.Expand = false;
+ w23.Fill = false;
+ this.vbox2.Add (this.hbox1);
+ global::Gtk.Box.BoxChild w24 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox1]));
+ w24.Position = 1;
+ // Container child vbox2.Gtk.Box+BoxChild
+ this.hbox3 = new global::Gtk.HBox ();
+ this.hbox3.Name = "hbox3";
+ this.hbox3.Spacing = 6;
+ // Container child hbox3.Gtk.Box+BoxChild
+ this.hseparator2 = new global::Gtk.HSeparator ();
+ this.hseparator2.Name = "hseparator2";
+ this.hbox3.Add (this.hseparator2);
+ global::Gtk.Box.BoxChild w25 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.hseparator2]));
+ w25.Position = 0;
+ // Container child hbox3.Gtk.Box+BoxChild
+ this.stopResumeButton = new global::Gtk.Button ();
+ this.stopResumeButton.Sensitive = false;
+ this.stopResumeButton.CanFocus = true;
+ this.stopResumeButton.Name = "stopResumeButton";
+ this.stopResumeButton.UseUnderline = true;
+ this.stopResumeButton.Label = "Stop";
+ this.hbox3.Add (this.stopResumeButton);
+ global::Gtk.Box.BoxChild w26 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.stopResumeButton]));
+ w26.Position = 1;
+ w26.Expand = false;
+ w26.Fill = false;
+ w26.Padding = ((uint)(20));
+ // Container child hbox3.Gtk.Box+BoxChild
+ this.countLabel = new global::Gtk.Label ();
+ this.countLabel.Name = "countLabel";
+ this.countLabel.LabelProp = "0/192";
+ this.hbox3.Add (this.countLabel);
+ global::Gtk.Box.BoxChild w27 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.countLabel]));
+ w27.Position = 2;
+ w27.Expand = false;
+ w27.Fill = false;
+ w27.Padding = ((uint)(40));
+ // Container child hbox3.Gtk.Box+BoxChild
+ this.beginButton = new global::Gtk.Button ();
+ this.beginButton.CanFocus = true;
+ this.beginButton.Name = "beginButton";
+ this.beginButton.UseUnderline = true;
+ this.beginButton.Label = "Begin";
+ this.hbox3.Add (this.beginButton);
+ global::Gtk.Box.BoxChild w28 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.beginButton]));
+ w28.Position = 3;
+ w28.Expand = false;
+ w28.Fill = false;
+ w28.Padding = ((uint)(20));
+ // Container child hbox3.Gtk.Box+BoxChild
+ this.hseparator1 = new global::Gtk.HSeparator ();
+ this.hseparator1.Name = "hseparator1";
+ this.hbox3.Add (this.hseparator1);
+ global::Gtk.Box.BoxChild w29 = ((global::Gtk.Box.BoxChild)(this.hbox3 [this.hseparator1]));
+ w29.Position = 4;
+ this.vbox2.Add (this.hbox3);
+ global::Gtk.Box.BoxChild w30 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.hbox3]));
+ w30.Position = 2;
+ w30.Expand = false;
+ w30.Fill = false;
+ // Container child vbox2.Gtk.Box+BoxChild
+ this.statusLabel = new global::Gtk.Label ();
+ this.statusLabel.Name = "statusLabel";
+ this.vbox2.Add (this.statusLabel);
+ global::Gtk.Box.BoxChild w31 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.statusLabel]));
+ w31.PackType = ((global::Gtk.PackType)(1));
+ w31.Position = 3;
+ w31.Expand = false;
+ w31.Fill = false;
+ // Container child vbox2.Gtk.Box+BoxChild
+ this.progressBar = new global::Gtk.ProgressBar ();
+ this.progressBar.Name = "progressBar";
+ this.vbox2.Add (this.progressBar);
+ global::Gtk.Box.BoxChild w32 = ((global::Gtk.Box.BoxChild)(this.vbox2 [this.progressBar]));
+ w32.PackType = ((global::Gtk.PackType)(1));
+ w32.Position = 4;
+ w32.Expand = false;
+ w32.Fill = false;
+ this.Add (this.vbox2);
+ if ((this.Child != null)) {
+ this.Child.ShowAll ();
+ }
+ this.DefaultWidth = 887;
+ this.DefaultHeight = 570;
+ this.Show ();
+ this.ExportToWikiAction.Activated += new global::System.EventHandler (this.OnExportToWiki);
+ this.IngredientsAction.Activated += new global::System.EventHandler (this.OnShowIngredients);
+ this.maxIngredientsSpinButton.ValueChanged += new global::System.EventHandler (this.OnMaxIngredientsChanged);
+ this.maxRecipeSpinButton.ValueChanged += new global::System.EventHandler (this.OnMaxRecipeChanged);
+ this.fullQuantityDepthSpinButton.ValueChanged += new global::System.EventHandler (this.OnFullQuantityDepthChanged);
+ this.fullQuantitySpinButton.Input += new global::Gtk.InputHandler (this.OnFullQuantityChanged);
+ this.stopResumeButton.Clicked += new global::System.EventHandler (this.OnStopResume);
+ this.beginButton.Clicked += new global::System.EventHandler (this.OnBegin);
+ }
+ }
+}
diff --git a/gtk-gui/MainWindow.cs b/gtk-gui/MainWindow.cs
--- a/gtk-gui/MainWindow.cs
+++ b/gtk-gui/MainWindow.cs
@@ -27,10 +27,12 @@ public partial class MainWindow
private global::Gtk.Action ScreenshotAction;
- private global::Gtk.Action RecipeGeneratorAction;
+ private global::Gtk.Action RecipesAction;
private global::Gtk.Action ReactionStatusAction;
+ private global::Gtk.Action IngredientsAction;
+
private global::Gtk.VBox vbox1;
private global::Gtk.MenuBar menubar1;
@@ -128,12 +130,15 @@ public partial class MainWindow
this.ScreenshotAction = new global::Gtk.Action ("ScreenshotAction", "Screenshot", null, null);
this.ScreenshotAction.ShortLabel = "Screenshot";
w1.Add (this.ScreenshotAction, null);
- this.RecipeGeneratorAction = new global::Gtk.Action ("RecipeGeneratorAction", "Recipe Generator", null, null);
- this.RecipeGeneratorAction.ShortLabel = "Recipe Generator";
- w1.Add (this.RecipeGeneratorAction, null);
+ this.RecipesAction = new global::Gtk.Action ("RecipesAction", "Recipes", null, null);
+ this.RecipesAction.ShortLabel = "Recipe Generator";
+ w1.Add (this.RecipesAction, null);
this.ReactionStatusAction = new global::Gtk.Action ("ReactionStatusAction", "Reaction Status", null, null);
this.ReactionStatusAction.ShortLabel = "Reaction Status";
w1.Add (this.ReactionStatusAction, null);
+ this.IngredientsAction = new global::Gtk.Action ("IngredientsAction", "Ingredients", null, null);
+ this.IngredientsAction.ShortLabel = "Ingredients";
+ w1.Add (this.IngredientsAction, null);
this.UIManager.InsertActionGroup (w1, 0);
this.AddAccelGroup (this.UIManager.AccelGroup);
this.Name = "MainWindow";
@@ -143,7 +148,7 @@ public partial class MainWindow
this.vbox1 = new global::Gtk.VBox ();
this.vbox1.Name = "vbox1";
// Container child vbox1.Gtk.Box+BoxChild
- this.UIManager.AddUiFromString ("");
+ this.UIManager.AddUiFromString ("");
this.menubar1 = ((global::Gtk.MenuBar)(this.UIManager.GetWidget ("/menubar1")));
this.menubar1.Name = "menubar1";
this.vbox1.Add (this.menubar1);
@@ -374,8 +379,9 @@ public partial class MainWindow
this.ExportForPracticalPaintAction.Activated += new global::System.EventHandler (this.OnExport);
this.RunSimulatorAction.Activated += new global::System.EventHandler (this.RunSimulator);
this.ScreenshotAction.Activated += new global::System.EventHandler (this.OnDebugScreenshot);
- this.RecipeGeneratorAction.Activated += new global::System.EventHandler (this.OnOpenRecipeGenerator);
+ this.RecipesAction.Activated += new global::System.EventHandler (this.OnOpenRecipeGenerator);
this.ReactionStatusAction.Activated += new global::System.EventHandler (this.OnShowReactionStatus);
+ this.IngredientsAction.Activated += new global::System.EventHandler (this.OnShowIngredients);
this.ingredient1ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient1);
this.ingredient2ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient2);
this.ingredient3ComboBox.Changed += new global::System.EventHandler (this.OnChangedIngredient3);
diff --git a/gtk-gui/gui.stetic b/gtk-gui/gui.stetic
--- a/gtk-gui/gui.stetic
+++ b/gtk-gui/gui.stetic
@@ -78,9 +78,9 @@
Screenshot
-
+
Action
- Recipe Generator
+ Recipes
Recipe Generator
@@ -90,6 +90,12 @@
Reaction Status
+
+ Action
+ Ingredients
+ Ingredients
+
+
Desert Paint Lab
@@ -111,7 +117,8 @@
-
+
+
@@ -1133,4 +1140,549 @@ You can either import an existing Practi
+
+
+
+ Action
+ Export
+ Export
+
+
+ Action
+ Export to Wiki
+ Export to Wiki
+
+
+
+ Action
+ Settings
+ Tools
+
+
+ Action
+ Ingredients
+ Ingredients
+
+
+
+
+ Recipe Generator
+ CenterOnParent
+
+
+
+ 6
+ 8
+
+
+
+ 0
+ True
+ False
+ False
+
+
+
+
+
+ 6
+
+
+
+ 6
+
+
+
+ Maximum Ingredients
+
+
+ 0
+ True
+ False
+ False
+
+
+
+
+
+ True
+ 14
+ 10
+ 1
+ 1
+ True
+
+
+
+ 1
+ True
+ False
+ False
+
+
+
+
+
+
+
+ 2
+ True
+ False
+ False
+
+
+
+
+
+ Max Total Quantity
+ True
+ True
+
+
+ 3
+ True
+ False
+ False
+ 8
+
+
+
+
+
+ True
+ 100
+ 10
+ 1
+ 1
+ True
+
+
+
+ 4
+ True
+ False
+ False
+
+
+
+
+
+
+
+ 5
+ True
+ False
+ False
+
+
+
+
+
+ Full Quantity Depth
+
+
+ 6
+ True
+ False
+ False
+
+
+
+
+
+ True
+ 15
+ 10
+ 1
+ 1
+ True
+
+
+
+ 7
+ True
+ False
+ False
+
+
+
+
+
+ FullQuantity
+
+
+ 8
+ True
+ False
+ False
+
+
+
+
+
+ True
+ 30
+ 10
+ 1
+ 1
+ True
+
+
+
+ 9
+ True
+ False
+ False
+
+
+
+
+ 0
+ True
+ False
+ False
+
+
+
+
+
+ In
+
+
+
+ 300
+ True
+ True
+
+
+
+
+ 1
+ True
+
+
+
+
+
+ 6
+
+
+
+ 200
+ 200
+ None
+
+
+
+ 0
+ 0
+ 12
+
+
+
+ True
+ In
+
+
+
+ None
+
+
+
+ True
+ 6
+ 1
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <b>Recipe</b>
+ True
+
+
+ label_item
+
+
+
+
+ 0
+ True
+
+
+
+
+
+ 200
+ ButtonPressMask
+
+
+ 1
+ True
+
+
+
+
+ 2
+ True
+ False
+ False
+
+
+
+
+ 1
+ False
+
+
+
+
+
+ 6
+
+
+
+
+
+ 0
+ True
+
+
+
+
+
+ False
+ True
+ TextOnly
+ Stop
+ True
+
+
+
+ 1
+ True
+ False
+ False
+ 20
+
+
+
+
+
+ 0/192
+
+
+ 2
+ True
+ False
+ False
+ 40
+
+
+
+
+
+ True
+ TextOnly
+ Begin
+ True
+
+
+
+ 3
+ True
+ False
+ False
+ 20
+
+
+
+
+
+
+
+ 4
+ True
+
+
+
+
+ 2
+ True
+ False
+ False
+
+
+
+
+
+
+
+ End
+ 3
+ True
+ False
+ False
+
+
+
+
+
+
+
+ End
+ 4
+ True
+ False
+ False
+
+
+
+
+
+
+
+ ReagentWindow
+ CenterOnParent
+
+
+
+ 6
+ 8
+
+
+
+ None
+
+
+
+ 0
+ 0
+ 12
+
+
+
+ 3
+ 3
+ 6
+ 6
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <b>Ingredients</b>
+ True
+
+
+ label_item
+
+
+
+
+ 0
+ True
+
+
+
+
+
+ 2
+
+
+
+ 80
+ True
+ TextOnly
+ Ok
+ True
+
+
+
+ False
+ False
+
+
+
+
+
+ 80
+ True
+ TextOnly
+ Cancel
+ True
+
+
+
+ 1
+ False
+ False
+
+
+
+
+ End
+ 1
+ True
+ False
+ False
+
+
+
+
+
\ No newline at end of file