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; } } }