Files @ f28757bb21cb
Branch filter:

Location: ATITD-Tools/Desert-Paint-Lab/ReactionRecorder.cs - annotation

jmaltzen
Refactor recipe / reaction computation into a common class. Add some file utilities for supporting Mac bundles. Add some scripts for building Mac app bundles. Add a help window that shows the missing reactions.
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
f28757bb21cb
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;
        }
    }
}