diff --git a/.hgignore b/.hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -5,3 +5,4 @@ obj/
*.swp
*.zip
.DS_Store
+mac/build/DesertPaintLab.app
diff --git a/DesertPaintLab.csproj b/DesertPaintLab.csproj
--- a/DesertPaintLab.csproj
+++ b/DesertPaintLab.csproj
@@ -70,6 +70,11 @@
+
+
+
+
+
diff --git a/FileUtils.cs b/FileUtils.cs
new file mode 100644
--- /dev/null
+++ b/FileUtils.cs
@@ -0,0 +1,66 @@
+using System;
+
+namespace DesertPaintLab
+{
+ public class FileUtils
+ {
+ public FileUtils()
+ {
+ }
+
+ public static string FindApplicationResourceDirectory(string dirname)
+ {
+ string dirPath = System.IO.Path.Combine(
+ System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
+ dirname);
+ if (System.IO.Directory.Exists(dirPath))
+ {
+ return dirPath;
+ }
+ // try "Resources" in case this is a Mac app bundle
+ dirPath = System.IO.Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.Resources), dirname);
+ if (System.IO.Directory.Exists(dirPath))
+ {
+ return dirPath;
+ }
+ dirPath = System.IO.Path.Combine(
+ System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
+ "Resources",
+ dirname);
+ if (!System.IO.Directory.Exists(dirPath))
+ {
+ // not found
+ dirPath = null;
+ }
+ return dirPath;
+ }
+
+ public static string FindApplicationResourceFile(string filename)
+ {
+ string filePath = System.IO.Path.Combine(
+ System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
+ filename);
+ if (!System.IO.File.Exists(filePath))
+ {
+ // try "Resources" in case this is a Mac app bundle
+ filePath = System.IO.Path.Combine(
+ Environment.GetFolderPath(Environment.SpecialFolder.Resources), filename);
+ }
+ if (!System.IO.File.Exists(filePath))
+ {
+ filePath = System.IO.Path.Combine(
+ System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
+ "Resources",
+ filename);
+ }
+ if (!System.IO.File.Exists(filePath))
+ {
+ // not found
+ filePath = null;
+ }
+ return filePath;
+ }
+ }
+}
+
diff --git a/MainWindow.cs b/MainWindow.cs
--- a/MainWindow.cs
+++ b/MainWindow.cs
@@ -55,16 +55,14 @@ public partial class MainWindow : Gtk.Wi
Gdk.Window rootWindow = null;
Gdk.Pixbuf screenBuffer = null;
- Reagent reagent1 = null;
- Reagent reagent2 = null;
- Reagent reagent3 = null;
+ Reagent[] reagents = new Reagent[3];
+ PaintRecipe recipe = new PaintRecipe();
-
public bool ShouldShutDown
{
get
{
- return shouldShutDown;
+ return shouldShutDown;
}
}
@@ -90,9 +88,12 @@ public partial class MainWindow : Gtk.Wi
}
}
- Palette.Load(System.IO.Path.Combine(
- System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
- "colors.txt"));
+ reagents[0] = null;
+ reagents[1] = null;
+ reagents[2] = null;
+
+ string colorsPath = FileUtils.FindApplicationResourceFile("colors.txt");
+ Palette.Load(colorsPath);
Build();
@@ -385,29 +386,22 @@ public partial class MainWindow : Gtk.Wi
Reaction reaction1, reaction2;
TreeIter selectIter;
string reagentName;
- reagent1 = null;
- reagent2 = null;
- reagent3 = null;
-
- int expRedSum = 0;
- int expGreenSum = 0;
- int expBlueSum = 0;
-
- int reactRedSum = 0;
- int reactGreenSum = 0;
- int reactBlueSum = 0;
+ reagents[0] = null;
+ reagents[1] = null;
+ reagents[2] = null;
bool reactionKnown = true;
- int pigmentCount = 0;
-
saveButton.Sensitive = false;
+
+ recipe.Clear();
if (ingredient1ComboBox.GetActiveIter(out selectIter))
{
reagentName = (string)ingredient1ComboBox.Model.GetValue(selectIter, 0);
if ((reagentName == null) || (reagentName.Length == 0))
{
+ // Nothing selected as reagent 1
ingredient2ComboBox.Sensitive = false;
ingredient3ComboBox.Sensitive = false;
unmodifiedSwatch.Clear();
@@ -415,15 +409,9 @@ public partial class MainWindow : Gtk.Wi
}
else
{
- reagent1 = ReagentManager.GetReagent(reagentName);
+ recipe.AddReagent(reagentName);
+ reagents[0] = ReagentManager.GetReagent(reagentName);
ingredient2ComboBox.Sensitive = true;
- if (!reagent1.IsCatalyst)
- {
- expRedSum = reagent1.Color.Red;
- expGreenSum = reagent1.Color.Green;
- expBlueSum = reagent1.Color.Blue;
- pigmentCount = 1;
- }
if (ingredient2ComboBox.GetActiveIter(out selectIter))
{
reagentName = (string)ingredient2ComboBox.Model.GetValue(selectIter, 0);
@@ -435,25 +423,16 @@ public partial class MainWindow : Gtk.Wi
}
else
{
- reagent2 = ReagentManager.GetReagent(reagentName);
+ recipe.AddReagent(reagentName);
+ reagents[1] = ReagentManager.GetReagent(reagentName);
ingredient3ComboBox.Sensitive = true;
captureButton.Sensitive = true;
- if (!reagent2.IsCatalyst)
- {
- expRedSum += reagent2.Color.Red;
- expGreenSum += reagent2.Color.Green;
- expBlueSum += reagent2.Color.Blue;
- pigmentCount++;
- }
- reaction1 = profile.FindReaction(reagent1, reagent2);
+ reaction1 = profile.FindReaction(reagents[0], reagents[1]);
- if (reaction1 != null)
+ if ((reaction1 != null) || (reagents[0] == reagents[1]))
{
ingredient3ComboBox.Sensitive = true;
- reactRedSum = reaction1.Red;
- reactGreenSum = reaction1.Green;
- reactBlueSum = reaction1.Blue;;
}
else
{
@@ -466,7 +445,8 @@ public partial class MainWindow : Gtk.Wi
reagentName = (string)ingredient3ComboBox.Model.GetValue(selectIter, 0);
if ((reagentName != null) && (reagentName.Length != 0))
{
- reagent3 = ReagentManager.GetReagent(reagentName);
+ recipe.AddReagent(reagentName);
+ reagents[2] = ReagentManager.GetReagent(reagentName);
if (!reactionKnown)
{
@@ -482,16 +462,8 @@ public partial class MainWindow : Gtk.Wi
captureButton.Sensitive = false;
}
- if (!reagent3.IsCatalyst)
- {
- expRedSum += reagent3.Color.Red;
- expGreenSum += reagent3.Color.Green;
- expBlueSum += reagent3.Color.Blue;
- pigmentCount++;
- }
-
- reaction1 = profile.FindReaction(reagent1, reagent3);
- reaction2 = profile.FindReaction(reagent2, reagent3);
+ reaction1 = profile.FindReaction(reagents[0], reagents[2]);
+ reaction2 = profile.FindReaction(reagents[1], reagents[2]);
if (reactionKnown && (reaction1 == null) && (reaction2 == null))
{
@@ -508,24 +480,12 @@ public partial class MainWindow : Gtk.Wi
captureButton.Sensitive = false;
}
- if (reaction1 != null)
- {
- reactRedSum += reaction1.Red;
- reactGreenSum += reaction1.Green;
- reactBlueSum += reaction1.Blue;
- }
- else
+ if ((reaction1 == null) && (reagents[0] != reagents[2]))
{
reactionKnown = false;
}
- if (reaction2 != null)
- {
- reactRedSum += reaction2.Red;
- reactGreenSum += reaction2.Green;
- reactBlueSum += reaction2.Blue;
- }
- else
+ if ((reaction2 == null) && (reagents[1] != reagents[2]))
{
reactionKnown = false;
}
@@ -533,15 +493,13 @@ public partial class MainWindow : Gtk.Wi
}
}
}
- SetExpectedColor((byte)Math.Round((float)expRedSum / (float)pigmentCount),
- (byte)Math.Round((float)expGreenSum / (float)pigmentCount),
- (byte)Math.Round((float)expBlueSum / (float)pigmentCount));
+ recipe.ComputeBaseColor(ref expectedColor);
+ unmodifiedSwatch.Color = expectedColor;
+ //SetExpectedColor(recipeColor.Red, recipeColor.Green, recipeColor.Blue);
if (reactionKnown)
{
- reactedColor.Red = (byte)Math.Min(255, Math.Max(0, expectedColor.Red + reactRedSum));
- reactedColor.Green = (byte)Math.Min(255, Math.Max(0, expectedColor.Green + reactGreenSum));
- reactedColor.Blue = (byte)Math.Min(255, Math.Max(0, expectedColor.Blue + reactBlueSum));
+ recipe.ComputeReactedColor(profile, ref reactedColor);
reactionSwatch.Color = reactedColor;
}
else
@@ -574,9 +532,6 @@ public partial class MainWindow : Gtk.Wi
unsafe bool CaptureReactionColor()
{
// Take a screenshot.
- byte r, g, b;
- int pixelStart, otherPixelStart;
- bool colorMatch;
Gdk.Image rootImage = rootWindow.GetImage(0, 0, screenWidth, screenHeight);
screenBuffer.GetFromImage(rootImage, rootImage.Colormap, 0, 0, 0, 0, screenWidth, screenHeight);
//screenBuffer.GetFromDrawable(rootWindow,
@@ -584,117 +539,25 @@ public partial class MainWindow : Gtk.Wi
int stride = screenBuffer.Rowstride;
byte* pixBytes = (byte*)screenBuffer.Pixels;
- for (int x = 0; x < screenWidth - colorBarWidth; ++x)
- {
- for (int y = 0; y < (screenHeight - 53); ++y)
- {
- // Look for the color swatch.
- pixelStart = (y * stride) + (x * 3);
- r = pixBytes[pixelStart];
- g = pixBytes[pixelStart + 1];
- b = pixBytes[pixelStart + 2];
-
- // 1.) Check if this is a dark pixel.
- if ((r < 0x46) && (g < 0x46) && (b < 0x46))
- {
- // 2.) Check the pixel above it,
- // to see if it's from the papy texture.
- otherPixelStart = pixelStart - stride;
- if ((otherPixelStart >= 0) &&
- IsPapyTexture(pixBytes[otherPixelStart++],
- pixBytes[otherPixelStart++],
- pixBytes[otherPixelStart]))
- {
- // 3.) Check the pixel below where the swatch should be,
- // to see if it's also from the papy texture.
- otherPixelStart = pixelStart + (stride * swatchHeight);
- if (IsPapyTexture(pixBytes[otherPixelStart++],
- pixBytes[otherPixelStart++],
- pixBytes[otherPixelStart]))
- {
- // pixBytes[pixelStart] = 0xFF;
- // pixBytes[pixelStart + 1] = 0x00;
- // pixBytes[pixelStart + 2] = 0xFF;
-
- // 4.) Scan the left border of the potential swatch
- // location.
- colorMatch = true;
- 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))
- {
- colorMatch = false;
- break;
- }
- }
-
- if (colorMatch)
- {
- // WE FOUND THE SWATCH!
- // Now we know where the color bars are.
- otherPixelStart = pixelStart + (redBarSpacing * stride);
- int pixelCount = 0;
- while ((pixBytes[otherPixelStart] > 0x9F) &&
- (pixBytes[otherPixelStart + 1] < 0x62) &&
- (pixBytes[otherPixelStart + 2] < 0x62))
- {
- pixelCount++;
- // pixBytes[otherPixelStart] = 0x00;
- // pixBytes[otherPixelStart + 1] = 0xFF;
- // pixBytes[otherPixelStart + 2] = 0xFF;
- otherPixelStart += 3;
- }
-
- reactedColor.Red = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth);
- otherPixelStart = pixelStart + (greenBarSpacing * stride);
-
- pixelCount = 0;
- while ((pixBytes[otherPixelStart] < 0x62) &&
- (pixBytes[otherPixelStart + 1] > 0x9F) &&
- (pixBytes[otherPixelStart + 2] < 0x62))
- {
- pixelCount++;
- // pixBytes[otherPixelStart] = 0x00;
- // pixBytes[otherPixelStart + 1] = 0xFF;
- // pixBytes[otherPixelStart + 2] = 0xFF;
- otherPixelStart += 3;
- }
-
- reactedColor.Green = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth);
- otherPixelStart = pixelStart + (blueBarSpacing * stride);
-
- pixelCount = 0;
- while ((pixBytes[otherPixelStart] < 0x62) &&
- (pixBytes[otherPixelStart + 1] < 0x62) &&
- (pixBytes[otherPixelStart + 2] > 0x9F))
- {
- pixelCount++;
- // pixBytes[otherPixelStart] = 0x00;
- // pixBytes[otherPixelStart + 1] = 0xFF;
- // pixBytes[otherPixelStart + 2] = 0xFF;
- otherPixelStart += 3;
- }
-
- reactedColor.Blue = (byte)Math.Round((float)pixelCount * 255f / (float)colorBarWidth);
-
- // write out the screenshot
- //screenBuffer.Save("screenshot.png", "png");
- return true;
- }
- }
- }
- }
- }
- }
+ bool wasCaptured = ReactionRecorder.CaptureReaction(pixBytes, screenWidth, screenHeight, stride, ref reactedColor);
+ if (!wasCaptured && enableDebugMenu)
+ {
+ // 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));
+ screenBuffer.Save(filename, "png");
+ }
//screenBuffer.Save("screenshot.png", "png");
- return false;
-
+ return !wasCaptured;
}
-
+
protected virtual void OnDebugScreenshot(object sender, System.EventArgs e)
{
Gdk.Image rootImage = rootWindow.GetImage(0, 0, screenWidth, screenHeight);
@@ -774,47 +637,10 @@ public partial class MainWindow : Gtk.Wi
protected virtual void OnSaveButton(object sender, System.EventArgs e)
{
- int r, g, b;
- if (reagent3 != null)
- {
- // A 3-reagent reaction.
- Reaction reaction1 = profile.FindReaction(reagent1, reagent2);
- Reaction reaction2 = profile.FindReaction(reagent1, reagent3);
- Reaction reaction3 = profile.FindReaction(reagent2, reagent3);
-
- r = reactedColor.Red - expectedColor.Red;
- g = reactedColor.Green - expectedColor.Green;
- b = reactedColor.Blue - expectedColor.Blue;
-
- if (reaction2 == null)
- {
- r = r - reaction1.Red - reaction3.Red;
- g = g - reaction1.Green - reaction3.Green;
- b = b - reaction1.Blue - reaction3.Blue;
- profile.SetReaction(reagent1, reagent3, new Reaction(r, g, b));
- profile.Save();
- saveButton.Sensitive = false;
- }
- else if (reaction3 == null)
- {
- r = r - reaction1.Red - reaction2.Red;
- g = g - reaction1.Green - reaction2.Green;
- b = b - reaction1.Blue - reaction2.Blue;
- profile.SetReaction(reagent2, reagent3, new Reaction(r, g, b));
- profile.Save();
- saveButton.Sensitive = false;
- }
- }
- else if ((reagent1 != null) && (reagent2 != null))
- {
- // A 2-reagent reaction.
- r = reactedColor.Red - expectedColor.Red;
- g = reactedColor.Green - expectedColor.Green;
- b = reactedColor.Blue - expectedColor.Blue;
- profile.SetReaction(reagent1, reagent2, new Reaction(r, g, b));
- profile.Save();
- saveButton.Sensitive = false;
- }
+ if (ReactionRecorder.RecordReaction(profile, expectedColor, reactedColor, reagents))
+ {
+ saveButton.Sensitive = false;
+ }
}
protected virtual void OnChangedIngredient1(object sender, System.EventArgs e)
@@ -884,6 +710,12 @@ public partial class MainWindow : Gtk.Wi
protected virtual void OnAbout(object sender, System.EventArgs e)
{
AboutDialog aboutDialog = new AboutDialog();
+
+ aboutDialog.ProgramName = "Desert Paint Lab";
+ aboutDialog.Version = "0.0.1";
+ aboutDialog.Comments = "Desert Paint Lab paint reaction recorder for A Tale in the Desert";
+ aboutDialog.Authors = new string [] {"Tess Snider", "Jason Maltzen"};
+ //aboutDialog.Website = "http://www.google.com/";
aboutDialog.Run();
aboutDialog.Destroy();
}
@@ -919,8 +751,15 @@ public partial class MainWindow : Gtk.Wi
win.Show();
}
-
-
-
+ protected void OnOpenRecipeGenerator(object sender, EventArgs e)
+ {
+ throw new NotImplementedException();
+ }
+
+ protected void OnShowReactionStatus(object sender, EventArgs e)
+ {
+ ReactionStatusWindow win = new ReactionStatusWindow(profile);
+ win.Show();
+ }
}
diff --git a/PaintRecipe.cs b/PaintRecipe.cs
new file mode 100644
--- /dev/null
+++ b/PaintRecipe.cs
@@ -0,0 +1,126 @@
+using System;
+using System.Collections.Generic;
+
+namespace DesertPaintLab
+{
+ public class PaintRecipe
+ {
+ public struct RGB
+ {
+ public int r;
+ public int g;
+ public int b;
+ };
+
+ private List reagents = new List();
+
+ public PaintRecipe()
+ {
+ }
+
+ public void AddReagent(String reagentName)
+ {
+ reagents.Add(reagentName);
+ }
+
+ public void Clear()
+ {
+ reagents.Clear();
+ }
+
+ byte CalculateColor(int baseSum, int pigmentCount, int reactSum)
+ {
+ return (byte)Math.Max(Math.Min(Math.Round((((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)
+ {
+ RGB baseColor;
+ baseColor.r = 0;
+ baseColor.g = 0;
+ baseColor.b = 0;
+ RGB reactionColor;
+ reactionColor.r = 0;
+ reactionColor.g = 0;
+ reactionColor.b = 0;
+
+ int pigmentCount = 0;
+ string prevReagent = null;
+
+ // track visited reagents so the reaction is only applied once
+ SortedDictionary reagentSet = new SortedDictionary();
+ List prevReagents = new List();
+
+ foreach (string reagentName in reagents)
+ {
+ if (reagentName == null)
+ {
+ continue;
+ }
+
+ 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;
+ }
+ if (prevReagent == null || !prevReagent.Equals(reagentName))
+ {
+ if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
+ {
+ reagentSet[reagentName] = true;
+ // Run reactions.
+ foreach (Reagent otherReagent in prevReagents)
+ {
+ Reaction reaction = profile.FindReaction(otherReagent, reagent);
+ if (reaction != null)
+ {
+ reactionColor.r += reaction.Red;
+ reactionColor.g += reaction.Green;
+ reactionColor.b += reaction.Blue;
+ }
+ }
+ 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);
+ }
+
+ // Compute the base color without any reactions
+ public void ComputeBaseColor(ref PaintColor color)
+ {
+ RGB baseColor;
+ baseColor.r = 0;
+ baseColor.g = 0;
+ baseColor.b = 0;
+ int pigmentCount = 0;
+ foreach (string reagentName in reagents)
+ {
+ if (reagentName == null)
+ {
+ continue;
+ }
+
+ 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;
+ }
+ }
+ color.Red = CalculateColor(baseColor.r, pigmentCount, 0);
+ color.Green = CalculateColor(baseColor.g, pigmentCount, 0);
+ color.Blue = CalculateColor(baseColor.b, pigmentCount, 0);
+ }
+ }
+}
+
diff --git a/PlayerProfile.cs b/PlayerProfile.cs
--- a/PlayerProfile.cs
+++ b/PlayerProfile.cs
@@ -48,10 +48,12 @@ namespace DesertPaintLab
System.IO.Directory.CreateDirectory(directory);
// Copy template files into new directory.
- string templatePath = System.IO.Path.Combine(
- System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
- "template");
+ string templatePath = FileUtils.FindApplicationResourceDirectory("template");
+ if (!System.IO.Directory.Exists(templatePath))
+ {
+ }
+
DirectoryInfo di = new DirectoryInfo(templatePath);
FileInfo[] templateFiles = di.GetFiles();
diff --git a/ReactionRecorder.cs b/ReactionRecorder.cs
new file mode 100644
--- /dev/null
+++ b/ReactionRecorder.cs
@@ -0,0 +1,180 @@
+using System;
+
+namespace DesertPaintLab
+{
+ // ReactionRecorder - business logic for recording paint reactions
+ public class ReactionRecorder
+ {
+ const int colorTolerance = 2;
+
+ const int swatchHeight = 24;
+ const int colorBarWidth = 306;
+ const int redBarSpacing = 32;
+ const int greenBarSpacing = 42;
+ const int blueBarSpacing = 52;
+
+ private static bool IsPapyTexture(byte r, byte g, byte b)
+ {
+ return ((r > 0xD0) && (g > 0xC8) && (b > 0xA0)) &&
+ ((r < 0xF4) && (g < 0xE0) && (b < 0xC4));
+ }
+
+ unsafe public static bool CaptureReaction(byte* pixBytes, int screenshotWidth, int screenshotHeight, int stride, ref PaintColor reactedColor)
+ {
+ byte r, g, b;
+ int pixelStart, otherPixelStart;
+ bool colorMatch;
+ for (int x = 0; x < screenshotWidth - colorBarWidth; ++x)
+ {
+ for (int y = 0; y < (screenshotHeight - 53); ++y)
+ {
+ // Look for the color swatch.
+ pixelStart = (y * stride) + (x * 3);
+ r = pixBytes[pixelStart];
+ g = pixBytes[pixelStart + 1];
+ b = pixBytes[pixelStart + 2];
+
+ // 1.) Check if this is a dark pixel.
+ if ((r < 0x46) && (g < 0x46) && (b < 0x46))
+ {
+ // 2.) Check the pixel above it,
+ // to see if it's from the papy texture.
+ otherPixelStart = pixelStart - stride;
+ if ((otherPixelStart >= 0) &&
+ IsPapyTexture(pixBytes[otherPixelStart++],
+ pixBytes[otherPixelStart++],
+ pixBytes[otherPixelStart]))
+ {
+ // 3.) Check the pixel below where the swatch should be,
+ // to see if it's also from the papy texture.
+ otherPixelStart = pixelStart + (stride * swatchHeight);
+ if (IsPapyTexture(pixBytes[otherPixelStart++],
+ pixBytes[otherPixelStart++],
+ pixBytes[otherPixelStart]))
+ {
+ // pixBytes[pixelStart] = 0xFF;
+ // pixBytes[pixelStart + 1] = 0x00;
+ // pixBytes[pixelStart + 2] = 0xFF;
+
+ // 4.) Scan the left border of the potential swatch
+ // location.
+ colorMatch = true;
+ 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))
+ {
+ colorMatch = false;
+ break;
+ }
+ }
+
+ if (colorMatch)
+ {
+ // WE FOUND THE SWATCH!
+ // Now we know where the color bars are.
+ int redPixelStart = pixelStart + (redBarSpacing * stride);
+ int redPixelCount = 0;
+ while ((pixBytes[redPixelStart] > 0x9F) &&
+ (pixBytes[redPixelStart + 1] < 0x62) &&
+ (pixBytes[redPixelStart + 2] < 0x62))
+ {
+ redPixelCount++;
+ // pixBytes[redPixelStart] = 0x00;
+ // pixBytes[redPixelStart + 1] = 0xFF;
+ // pixBytes[redPixelStart + 2] = 0xFF;
+ redPixelStart += 3;
+ }
+
+ reactedColor.Red = (byte)Math.Round((float)redPixelCount * 255f / (float)colorBarWidth);
+ int greenPixelStart = pixelStart + (greenBarSpacing * stride);
+
+ int greenPixelCount = 0;
+ while ((pixBytes[greenPixelStart] < 0x62) &&
+ (pixBytes[greenPixelStart + 1] > 0x9F) &&
+ (pixBytes[greenPixelStart + 2] < 0x62))
+ {
+ greenPixelCount++;
+ // pixBytes[greenPixelStart] = 0x00;
+ // pixBytes[greenPixelStart + 1] = 0xFF;
+ // pixBytes[greenPixelStart + 2] = 0xFF;
+ greenPixelStart += 3;
+ }
+
+ reactedColor.Green = (byte)Math.Round((float)greenPixelCount * 255f / (float)colorBarWidth);
+ int bluePixelStart = pixelStart + (blueBarSpacing * stride);
+
+ int bluePixelCount = 0;
+ while ((pixBytes[bluePixelStart] < 0x62) &&
+ (pixBytes[bluePixelStart + 1] < 0x62) &&
+ (pixBytes[bluePixelStart + 2] > 0x9F))
+ {
+ bluePixelCount++;
+ // pixBytes[bluePixelStart] = 0x00;
+ // pixBytes[bluePixelStart + 1] = 0xFF;
+ // pixBytes[bluePixelStart + 2] = 0xFF;
+ bluePixelStart += 3;
+ }
+
+ reactedColor.Blue = (byte)Math.Round((float)bluePixelCount * 255f / (float)colorBarWidth);
+ return true;
+ }
+ }
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ public static bool RecordReaction(PlayerProfile profile, PaintColor expectedColor, PaintColor reactedColor, Reagent[] reagents)
+ {
+ bool saved = false;
+ int r, g, b;
+ if (reagents[2] != null)
+ {
+ // A 3-reagent reaction.
+ Reaction reaction1 = profile.FindReaction(reagents[0], reagents[1]);
+ Reaction reaction2 = profile.FindReaction(reagents[0], reagents[2]);
+ Reaction reaction3 = profile.FindReaction(reagents[1], reagents[2]);
+
+ r = reactedColor.Red - expectedColor.Red;
+ g = reactedColor.Green - expectedColor.Green;
+ b = reactedColor.Blue - expectedColor.Blue;
+
+ if (reaction2 == null)
+ {
+ r = r - reaction1.Red - reaction3.Red;
+ g = g - reaction1.Green - reaction3.Green;
+ b = b - reaction1.Blue - reaction3.Blue;
+ profile.SetReaction(reagents[0], reagents[2], new Reaction(r, g, b));
+ profile.Save();
+ saved = true;
+ }
+ else if (reaction3 == null)
+ {
+ r = r - reaction1.Red - reaction2.Red;
+ g = g - reaction1.Green - reaction2.Green;
+ b = b - reaction1.Blue - reaction2.Blue;
+ profile.SetReaction(reagents[1], reagents[2], new Reaction(r, g, b));
+ profile.Save();
+ saved = true;
+ }
+ }
+ else if ((reagents[0] != null) && (reagents[1] != null))
+ {
+ // A 2-reagent reaction.
+ r = reactedColor.Red - expectedColor.Red;
+ g = reactedColor.Green - expectedColor.Green;
+ b = reactedColor.Blue - expectedColor.Blue;
+ profile.SetReaction(reagents[0], reagents[1], new Reaction(r, g, b));
+ profile.Save();
+ saved = true;
+ }
+ return saved;
+ }
+ }
+}
+
diff --git a/ReactionStatusWindow.cs b/ReactionStatusWindow.cs
new file mode 100644
--- /dev/null
+++ b/ReactionStatusWindow.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace DesertPaintLab
+{
+ public partial class ReactionStatusWindow : Gtk.Window
+ {
+ //PlayerProfile profile;
+
+ public ReactionStatusWindow(PlayerProfile profile) : base(Gtk.WindowType.Toplevel)
+ {
+ //this.profile = profile;
+ this.Build();
+
+ //Gtk.CellRendererText reagentColumnCell = new Gtk.CellRendererText();
+
+ int count = 0;
+ foreach (string name1 in ReagentManager.Names)
+ {
+ Reagent reagent1 = ReagentManager.GetReagent(name1);
+ foreach (string name2 in ReagentManager.Names)
+ {
+ if (name1.Equals(name2))
+ {
+ continue;
+ }
+ Reagent reagent2 = ReagentManager.GetReagent(name2);
+ Reaction reaction = profile.FindReaction(reagent1, reagent2);
+ if (reaction == null)
+ {
+ if (count == 0)
+ {
+ Gtk.Label header = new Gtk.Label("Missing Reactions:");
+ resultbox.PackStart(header, false, false, 0);
+ Gtk.HSeparator sep = new Gtk.HSeparator();
+ resultbox.PackStart(sep, false, false, 0);
+ }
+ Gtk.Label label = new Gtk.Label(name1 + " + " + name2);
+ resultbox.PackStart(label, false, false, 0);
+
+ ++count;
+ }
+ }
+ }
+
+ if (count == 0)
+ {
+ Gtk.Label header = new Gtk.Label("All reactions recorded!");
+ resultbox.PackStart(header, false, false, 0);
+ }
+ ShowAll();
+ }
+ }
+}
+
diff --git a/Reagent.cs b/Reagent.cs
--- a/Reagent.cs
+++ b/Reagent.cs
@@ -6,6 +6,7 @@ namespace DesertPaintLab
{
string name;
bool isCatalyst = false;
+ int cost = 0;
PaintColor color;
public bool IsCatalyst
@@ -32,16 +33,18 @@ namespace DesertPaintLab
}
}
- public Reagent(string 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)
+ 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 override string ToString()
diff --git a/ReagentManager.cs b/ReagentManager.cs
--- a/ReagentManager.cs
+++ b/ReagentManager.cs
@@ -7,10 +7,11 @@ namespace DesertPaintLab
{
public class ReagentManager
{
- static Regex reagentRegex = new Regex(@"(?\w+)\s*\|\s*(?\d+),\s*(?\d+),\s*(?\d+).*");
- static Regex catalystRegex = new Regex(@"(?\w+)\s*\|\s*catalyst");
+ 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*\|.*");
static SortedDictionary reagents = new SortedDictionary();
+ static List names = new List();
static Gtk.ListStore nameStore = new Gtk.ListStore(typeof(string));
@@ -21,6 +22,14 @@ namespace DesertPaintLab
return nameStore;
}
}
+
+ static public List Names
+ {
+ get
+ {
+ return names;
+ }
+ }
public ReagentManager ()
{
@@ -44,8 +53,10 @@ namespace DesertPaintLab
new Reagent(name,
byte.Parse(match.Groups["red"].Value),
byte.Parse(match.Groups["green"].Value),
- byte.Parse(match.Groups["blue"].Value)));
+ byte.Parse(match.Groups["blue"].Value),
+ int.Parse(match.Groups["cost"].Value)));
nameStore.AppendValues(name);
+ names.Add(name);
}
else
{
@@ -53,8 +64,10 @@ namespace DesertPaintLab
if (match.Success)
{
string name = match.Groups["name"].Value;
- reagents.Add(name, new Reagent(name));
+ int cost = int.Parse(match.Groups["cost"].Value);
+ reagents.Add(name, new Reagent(name, cost));
nameStore.AppendValues(name);
+ names.Add(name);
}
}
}
@@ -88,9 +101,9 @@ namespace DesertPaintLab
comboBox.Model = store;
store.AppendValues("");
- foreach (KeyValuePair pair in reagents)
+ foreach (string name in names)
{
- store.AppendValues(pair.Key);
+ store.AppendValues(name);
}
}
diff --git a/SimulatorWindow.cs b/SimulatorWindow.cs
--- a/SimulatorWindow.cs
+++ b/SimulatorWindow.cs
@@ -29,6 +29,7 @@ namespace DesertPaintLab
{
PlayerProfile profile;
Gtk.ListStore recipeData = new Gtk.ListStore(typeof(string), typeof(int));
+ PaintRecipe paintRecipe = new PaintRecipe();
public SimulatorWindow(PlayerProfile profile) : base(Gtk.WindowType.Toplevel)
{
@@ -119,25 +120,11 @@ namespace DesertPaintLab
paintSwatch.Clear();
}
- Reaction reaction;
+ paintRecipe.Clear();
+
Gtk.TreeIter iter;
string reagentName;
int qty;
- PaintColor color = null;
- Reagent reagent = null;
-
- int baseRedSum = 0;
- int baseGreenSum = 0;
- int baseBlueSum = 0;
-
- int reactRedSum = 0;
- int reactGreenSum = 0;
- int reactBlueSum = 0;
-
- int pigmentCount = 0;
-
- SortedDictionary reagentSet = new SortedDictionary();
- List reagents = new List();
recipeData.GetIterFirst(out iter);
@@ -151,46 +138,18 @@ namespace DesertPaintLab
}
qty = (int)recipeData.GetValue(iter, 1);
- reagent = ReagentManager.GetReagent(reagentName);
- if (!reagent.IsCatalyst)
- {
- color = reagent.Color;
- baseRedSum += qty * color.Red;
- baseGreenSum += qty * color.Green;
- baseBlueSum += qty * color.Blue;
- pigmentCount += qty;
- }
- if (!reagentSet.ContainsKey(reagentName) && reagentSet.Count <= 4)
- {
- reagentSet[reagentName] = true;
- // Run reactions.
- foreach (Reagent otherReagent in reagents)
- {
- reaction = profile.FindReaction(otherReagent, reagent);
- if (reaction != null)
- {
- reactRedSum += reaction.Red;
- reactGreenSum += reaction.Green;
- reactBlueSum += reaction.Blue;
- }
- }
- reagents.Add(reagent);
- }
-
+ for (int i = 0; i < qty; ++i)
+ {
+ paintRecipe.AddReagent(reagentName);
+ }
}
while (recipeData.IterNext(ref iter));
- paintSwatch.Color = new PaintColor(
- CalculateColor(baseRedSum, pigmentCount, reactRedSum),
- CalculateColor(baseGreenSum, pigmentCount, reactGreenSum),
- CalculateColor(baseBlueSum, pigmentCount, reactBlueSum));
+ PaintColor resultColor = new PaintColor();
+ paintRecipe.ComputeReactedColor(profile, ref resultColor);
+ paintSwatch.Color = resultColor;
}
- byte CalculateColor(int baseSum, int pigmentCount, int reactSum)
- {
- return (byte)Math.Max(Math.Min(Math.Round((((float)baseSum / (float)pigmentCount) + (float)reactSum)), 255), 0);
- }
-
protected virtual void OnIncrementReagent (object sender, System.EventArgs e)
{
Gtk.TreeModel model;
diff --git a/gtk-gui/DesertPaintLab.ReactionStatusWindow.cs b/gtk-gui/DesertPaintLab.ReactionStatusWindow.cs
new file mode 100644
--- /dev/null
+++ b/gtk-gui/DesertPaintLab.ReactionStatusWindow.cs
@@ -0,0 +1,41 @@
+
+// This file has been generated by the GUI designer. Do not modify.
+namespace DesertPaintLab
+{
+ public partial class ReactionStatusWindow
+ {
+ private global::Gtk.ScrolledWindow scroller;
+
+ private global::Gtk.VBox resultbox;
+
+ protected virtual void Build ()
+ {
+ global::Stetic.Gui.Initialize (this);
+ // Widget DesertPaintLab.ReactionStatusWindow
+ this.Name = "DesertPaintLab.ReactionStatusWindow";
+ this.Title = "Reaction Status";
+ this.WindowPosition = ((global::Gtk.WindowPosition)(4));
+ // Container child DesertPaintLab.ReactionStatusWindow.Gtk.Container+ContainerChild
+ this.scroller = new global::Gtk.ScrolledWindow ();
+ this.scroller.CanFocus = true;
+ this.scroller.Name = "scroller";
+ this.scroller.ShadowType = ((global::Gtk.ShadowType)(1));
+ // Container child scroller.Gtk.Container+ContainerChild
+ global::Gtk.Viewport w1 = new global::Gtk.Viewport ();
+ w1.ShadowType = ((global::Gtk.ShadowType)(0));
+ // Container child GtkViewport.Gtk.Container+ContainerChild
+ this.resultbox = new global::Gtk.VBox ();
+ this.resultbox.Name = "resultbox";
+ this.resultbox.Spacing = 6;
+ w1.Add (this.resultbox);
+ this.scroller.Add (w1);
+ this.Add (this.scroller);
+ if ((this.Child != null)) {
+ this.Child.ShowAll ();
+ }
+ this.DefaultWidth = 400;
+ this.DefaultHeight = 300;
+ this.Show ();
+ }
+ }
+}
diff --git a/gtk-gui/MainWindow.cs b/gtk-gui/MainWindow.cs
--- a/gtk-gui/MainWindow.cs
+++ b/gtk-gui/MainWindow.cs
@@ -27,6 +27,10 @@ public partial class MainWindow
private global::Gtk.Action ScreenshotAction;
+ private global::Gtk.Action RecipeGeneratorAction;
+
+ private global::Gtk.Action ReactionStatusAction;
+
private global::Gtk.VBox vbox1;
private global::Gtk.MenuBar menubar1;
@@ -124,6 +128,12 @@ 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.ReactionStatusAction = new global::Gtk.Action ("ReactionStatusAction", "Reaction Status", null, null);
+ this.ReactionStatusAction.ShortLabel = "Reaction Status";
+ w1.Add (this.ReactionStatusAction, null);
this.UIManager.InsertActionGroup (w1, 0);
this.AddAccelGroup (this.UIManager.AccelGroup);
this.Name = "MainWindow";
@@ -133,7 +143,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);
@@ -364,6 +374,8 @@ 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.ReactionStatusAction.Activated += new global::System.EventHandler (this.OnShowReactionStatus);
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,6 +78,18 @@
Screenshot
+
+ Action
+ Recipe Generator
+ Recipe Generator
+
+
+
+ Action
+ Reaction Status
+ Reaction Status
+
+
Desert Paint Lab
@@ -99,9 +111,11 @@
+
+
@@ -1086,4 +1100,37 @@ You can either import an existing Practi
+
+
+ Reaction Status
+ CenterOnParent
+
+
+
+ True
+ In
+
+
+
+ None
+
+
+
+ 6
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mac/Info.plist b/mac/Info.plist
new file mode 100644
--- /dev/null
+++ b/mac/Info.plist
@@ -0,0 +1,28 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ launcher.sh
+ CFBundleIconFile
+ DesertPaintLab.icns
+ CFBundleIdentifier
+ org.malkyne.desert-paint-lab
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ Desert Paint Lab
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ 1.7.4
+ CFBundleSignature
+ xmmd
+ CFBundleVersion
+ 1.7.4
+ NSAppleScriptEnabled
+ NO
+
+
diff --git a/mac/build_mac_bundle.sh b/mac/build_mac_bundle.sh
new file mode 100644
--- /dev/null
+++ b/mac/build_mac_bundle.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+/bin/mv DesertPaintLab.app DesertPaintLab.app.`/bin/date +"%Y%m%d%H%M%S"`
+/bin/mkdir -p DesertPaintLab.app
+/bin/cp Info.plist DesertPaintLab.app/Info.plist
+/bin/mkdir -p DesertPaintLab.app/Contents/MacOS
+/bin/cp launcher.sh DesertPaintLab.app/Contents/MacOS
+/bin/chmod 755 DesertPaintLab.app/Contents/MacOS/launcher.sh
+/bin/cp ../bin/Release/DesertPaintLab.exe DesertPaintLab.app/Contents/MacOS/
+/bin/mkdir -p DesertPaintLab.app/Contents/Resources
+/bin/cp ../bin/Release/colors.txt DesertPaintLab.app/Contents/Resources/
+/bin/cp -r ../bin/Release/template DesertPaintLab.app/Contents/Resources/template
diff --git a/mac/launcher.sh b/mac/launcher.sh
new file mode 100755
--- /dev/null
+++ b/mac/launcher.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+APPNAME="DesertPaintLab"
+
+DIR=$(cd "$(dirname "$0")"; pwd)
+
+EXE_PATH="$DIR\DesertPaintLab.exe"
+PROCESS_NAME=desertpaintlab
+
+MONO_FRAMEWORK_PATH=/Library/Frameworks/Mono.framework/Versions/Current
+export DYLD_FALLBACK_LIBRARY_PATH="$DIR:$MONO_FRAMEWORK_PATH/lib:/lib:/usr/lib"
+export PATH="$MONO_FRAMEWORK_PATH/bin:$PATH"
+
+#mono version check
+
+REQUIRED_MAJOR=4
+REQUIRED_MINOR=0
+
+VERSION_TITLE="Cannot launch $APPNAME"
+VERSION_MSG="$APPNAME requires the Mono Framework version $REQUIRED_MAJOR.$REQUIRED_MINOR or later."
+DOWNLOAD_URL="http://www.go-mono.com/mono-downloads/download.html"
+
+MONO_VERSION="$(mono --version | grep 'Mono JIT compiler version ' | cut -f5 -d\ )"
+MONO_VERSION_MAJOR="$(echo $MONO_VERSION | cut -f1 -d.)"
+MONO_VERSION_MINOR="$(echo $MONO_VERSION | cut -f2 -d.)"
+if [ -z "$MONO_VERSION" ] \
+ || [ $MONO_VERSION_MAJOR -lt $REQUIRED_MAJOR ] \
+ || [ $MONO_VERSION_MAJOR -eq $REQUIRED_MAJOR -a $MONO_VERSION_MINOR -lt $REQUIRED_MINOR ]
+then
+ /usr/bin/osascript \
+ -e "set question to display dialog \"$VERSION_MSG\" with title \"$VERSION_TITLE\" buttons {\"Cancel\", \"Download...\"} default button 2" \
+ -e "if button returned of question is equal to \"Download...\" then open location \"$DOWNLOAD_URL\""
+ echo "$VERSION_TITLE"
+ echo "$VERSION_MSG"
+ exit 1
+fi
+
+OSX_VERSION=$(uname -r | cut -f1 -d.)
+if [ $OSX_VERSION -lt 9 ]; then # If OSX version is 10.4
+ MONO_EXEC="exec mono"
+else
+ MONO_EXEC="exec -a \"$PROCESS_NAME\" mono"
+fi
+
+LOG_FILE="$HOME/Library/Logs/$APPNAME/$APPNAME.log"
+/bin/mkdir -p "`dirname \"$LOG_FILE\"`"
+$MONO_EXEC $MONO_OPTIONS "$EXE_PATH" $* 2>&1 1> "$LOG_FILE"
+