Files @ f28757bb21cb
Branch filter:

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

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