shithub: candycrisis

Download patch

ref: b09f9d2dd1242bbe3298d6b2d5b2e7749c21dd32
parent: 6e467c170e62f4a5fe7c9ea677cecb540c043a0d
author: Iliyas Jorio <[email protected]>
date: Thu Feb 9 14:53:07 EST 2023

Most of the game is really just pure C

--- /dev/null
+++ b/src/MTypes.c
@@ -1,0 +1,37 @@
+///
+///  MTypes.c
+///
+///  Generic replacements for very basic Mac types.
+///
+///  John Stiles, 2002/10/14
+///
+
+
+#include "MTypes.h"
+
+
+void UnionMRect( const MRect* a, const MRect* b, MRect* u )
+{
+	u->top    = MinShort( a->top, b->top );
+	u->left   = MinShort( a->left, b->left );
+	u->bottom = MaxShort( a->bottom, b->bottom );
+	u->right  = MaxShort( a->right, b->right );
+}
+
+
+void OffsetMRect( MRect* r, int x, int y )
+{
+	r->top += y;
+	r->left += x;
+	r->bottom += y;
+	r->right += x;
+}
+
+
+unsigned char MPointInMRect( MPoint p, const MRect* r )
+{
+	return (p.h >= r->left) &&
+	       (p.h <  r->right) &&
+	       (p.v >= r->top) &&
+	       (p.v <  r->bottom);
+}
--- a/src/MTypes.cpp
+++ /dev/null
@@ -1,41 +1,0 @@
-///
-///  MTypes.c
-///
-///  Generic replacements for very basic Mac types.
-///
-///  John Stiles, 2002/10/14
-///
-
-
-#include "MTypes.h"
-#include <algorithm>
-
-using std::min;
-using std::max;
-
-
-void UnionMRect( const MRect* a, const MRect* b, MRect* u )
-{
-	u->top    = min( a->top, b->top );
-	u->left   = min( a->left, b->left );
-	u->bottom = max( a->bottom, b->bottom );
-	u->right  = max( a->right, b->right );
-}
-
-
-void OffsetMRect( MRect* r, int x, int y )
-{
-	r->top += y;
-	r->left += x;
-	r->bottom += y;
-	r->right += x;
-}
-
-
-unsigned char MPointInMRect( MPoint p, const MRect* r )
-{
-	return (p.h >= r->left) &&
-	       (p.h <  r->right) &&
-	       (p.v >= r->top) &&
-	       (p.v <  r->bottom);
-}
--- a/src/MTypes.h
+++ b/src/MTypes.h
@@ -8,7 +8,8 @@
 
 #pragma once
 
-#include <cstdint>
+#include <stdint.h>
+#include <stdbool.h>
 
 typedef signed char MBoolean;
 
@@ -15,28 +16,28 @@
 typedef uint32_t MTicks;
 
 
-struct MRGBColor
+typedef struct MRGBColor
 {
 	unsigned short red;
 	unsigned short green;
 	unsigned short blue;
-};
+} MRGBColor;
 
 
-struct MRect
+typedef struct MRect
 {
 	short top;
 	short left;
 	short bottom;
-	short right;	
-};
+	short right;
+} MRect;
 
 
-struct MPoint
+typedef struct MPoint
 {
 	short v;
 	short h;
-};
+} MPoint;
 
 
 void UnionMRect( const MRect* a, const MRect* b, MRect* u );
@@ -43,3 +44,8 @@
 void OffsetMRect( MRect* r, int x, int y );
 unsigned char MPointInMRect( MPoint p, const MRect* r );
 
+
+static inline short MinShort(short a, short b) { return a < b ? a : b; }
+static inline short MaxShort(short a, short b) { return a < b ? b : a; }
+static inline int MinInt(int a, int b) { return a < b ? a : b; }
+static inline int MaxInt(int a, int b) { return a < b ? b : a; }
--- /dev/null
+++ b/src/SDLU.c
@@ -1,0 +1,580 @@
+///
+///  SDLU.c
+///
+///  SDL utilities.
+///
+///  John Stiles, 2002/10/12
+///
+
+#include "SDLU.h"
+#include "gameticks.h"
+#include "music.h"
+#include "main.h" // for Error
+
+// for acquiresurface
+#define k_acquireMax 10
+static int          s_acquireHead = -1;
+static SDL_Surface* s_acquireList[k_acquireMax];
+
+// for initsurface
+static SDL_Palette* s_grayscalePalette;
+
+// for button and getmouse
+static int          s_mouseButton;
+static MPoint       s_mousePosition;
+
+// system mouse cursors
+static SDL_Cursor*  s_standardCursor = NULL;
+static SDL_Cursor*  s_handCursor = NULL;
+
+// for event loop
+static MBoolean     s_isForeground = true;
+
+// for fade out / fade in
+static float        s_fadeGamma = 1;
+ 
+// for checktyping
+typedef struct BufferedKey
+{
+    bool isASCII;
+    
+    union
+    {
+        char         ascii;
+        SDL_Keycode  keycode;
+    } value;
+} BufferedKey;
+
+#define k_maxBufferedKeys 256
+
+static MBoolean                s_interestedInTyping = false;
+static BufferedKey             s_keyBuffer[k_maxBufferedKeys];
+static int                     s_keyBufferSize = 0;
+
+int SDLUi_EventFilter(void* junk, SDL_Event *event)
+{
+	(void) junk;
+	
+    switch (event->type)
+    {
+        case SDL_TEXTINPUT:
+        {
+            // Put text input into a buffer.
+            if (s_interestedInTyping)
+            {
+                for (char* asciiPtr = event->text.text; *asciiPtr; ++asciiPtr)
+                {
+                    BufferedKey key;
+                    key.isASCII = true;
+                    key.value.ascii = *asciiPtr;
+                    s_keyBuffer[s_keyBufferSize] = key;
+                    s_keyBufferSize = MinInt(k_maxBufferedKeys, s_keyBufferSize + 1);
+                }
+            }
+            break;
+        }
+    
+        case SDL_KEYDOWN:
+        {
+            // Put keydowns in a buffer
+            if (s_interestedInTyping)
+            {
+                BufferedKey key;
+                key.isASCII = false;
+                key.value.keycode = event->key.keysym.sym;
+                s_keyBuffer[s_keyBufferSize] = key;
+                s_keyBufferSize = MinInt(k_maxBufferedKeys, s_keyBufferSize + 1);
+            }
+            break;
+        }
+    
+        // Get mouse state
+        case SDL_MOUSEBUTTONDOWN:
+        {
+            if( event->button.button == SDL_BUTTON_LEFT )
+                s_mouseButton = true;
+            
+            s_mousePosition.v = event->button.y;
+            s_mousePosition.h = event->button.x;
+            break;
+        }
+    
+        case SDL_MOUSEBUTTONUP:
+        {
+            if( event->button.button == SDL_BUTTON_LEFT )
+                s_mouseButton = false;
+            
+            s_mousePosition.v = event->button.y;
+            s_mousePosition.h = event->button.x;
+            break;
+        }
+    
+        case SDL_MOUSEMOTION:
+        {
+            s_mousePosition.v = event->motion.y;
+            s_mousePosition.h = event->motion.x;
+            s_mouseButton = event->motion.state & SDL_BUTTON(1);
+            break;
+        }
+    
+        case SDL_QUIT:
+        {
+            finished = true;
+            break;
+        }
+            
+        case SDL_WINDOWEVENT:
+        {
+            if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST && s_isForeground)
+            {
+                FreezeGameTickCount();
+                //EnableMusic(false);
+                s_isForeground = false;
+            }
+            else if (event->window.event == SDL_WINDOWEVENT_FOCUS_GAINED && !s_isForeground)
+            {
+                UnfreezeGameTickCount();
+                //EnableMusic(musicOn);
+                s_isForeground = true;
+                
+                DoFullRepaint();
+            }
+            else if (event->window.event == SDL_WINDOWEVENT_RESIZED)
+            {
+                SDLU_CreateRendererTexture();
+            }
+            break;
+        }
+    }
+    
+    return 1;
+}
+
+
+void SDLU_CreateRendererTexture()
+{
+    if (!g_renderer)
+        return;
+
+    if (!crispUpscaling)
+    {
+        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
+        SDL_RenderSetIntegerScale(g_renderer, SDL_FALSE);
+    }
+    else
+    {
+        int minWidth = 640;
+        int minHeight = widescreen ? 360 : 480;
+
+        int currentWidth = 0;
+        int currentHeight = 0;
+#if SDL_VERSION_ATLEAST(2,26,0)
+        SDL_GetWindowSizeInPixels(g_window, &currentWidth, &currentHeight);
+#else
+        SDL_GetWindowSize(g_window, &currentWidth, &currentHeight);
+#endif
+
+        if (currentWidth < minWidth || currentHeight < minHeight)
+        {
+            SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
+            SDL_RenderSetIntegerScale(g_renderer, SDL_FALSE);
+        }
+        else
+        {
+            SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
+            SDL_RenderSetIntegerScale(g_renderer, SDL_TRUE);
+        }
+    }
+
+    if (g_windowTexture)
+        SDL_DestroyTexture(g_windowTexture);
+
+    g_windowTexture = SDL_CreateTexture(g_renderer,
+        SDL_PIXELFORMAT_RGB888,
+        SDL_TEXTUREACCESS_STREAMING,
+        640, 480);
+
+    SDL_RenderSetLogicalSize(g_renderer, 640, widescreen ? 360: 480);
+}
+
+
+void SDLU_Init()
+{
+    SDL_SetEventFilter(SDLUi_EventFilter, NULL);
+
+    // Initialize eight bit grayscale ramp palette.
+    SDL_Color  grayscaleColors[256];
+    for (int index=0; index<256; index++)
+    {
+        grayscaleColors[index].r =
+        grayscaleColors[index].g =
+        grayscaleColors[index].b = 255 - index;
+        grayscaleColors[index].a = 255;
+    }
+    
+    s_grayscalePalette = SDL_AllocPalette(256);
+    SDL_SetPaletteColors(s_grayscalePalette, grayscaleColors, 0, arrsize(grayscaleColors));
+
+    s_standardCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
+    s_handCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
+}
+
+
+SDL_Rect* SDLU_MRectToSDLRect( const MRect* in, SDL_Rect* out )
+{
+	int t = in->top, l = in->left, b = in->bottom, r = in->right;
+	
+	out->x = l;
+	out->y = t;
+	out->w = r - l;
+	out->h = b - t; 
+	
+	return out;
+}
+
+
+MRect* SDLU_SDLRectToMRect( const SDL_Rect* in, MRect* out )
+{
+	int x = in->x, y = in->y, w = in->w, h = in->h;
+	
+	out->top    = y;
+	out->left   = x;
+	out->bottom = y + h;
+	out->right  = x + w;
+	
+	return out; 
+}
+
+
+int SDLU_BlitSurface( SDL_Surface* src, SDL_Rect* srcrect,
+			          SDL_Surface* dst, SDL_Rect* dstrect  )
+{
+	// Let SDL handle this.
+	return SDL_BlitSurface( src, srcrect,
+	                        dst, dstrect  );
+}
+
+
+void SDLU_GetPixel(	SDL_Surface* surface, int x, int y, SDL_Color* pixel )
+{
+	unsigned int   px;
+	unsigned char* ptr;
+	
+	switch( surface->format->BytesPerPixel )
+	{
+		case 1:
+			ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x);
+			px = *(unsigned char*) ptr;
+			break;
+		
+		case 2:
+			ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x * 2);
+			px = *(unsigned short*) ptr;
+			break;
+
+		case 4:
+			ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x * 4);
+			px = *(unsigned int *) ptr;
+			break;
+        
+        default:
+            Error("SDLU_GetPixel: unrecognized surface format");
+            return;
+	}
+	
+	return SDL_GetRGB( px, surface->format, &pixel->r, &pixel->g, &pixel->b );
+}
+
+
+void SDLU_ChangeSurfaceDepth( SDL_Surface** surface, int depth )
+{
+	SDL_Surface* newSurface;
+
+	newSurface = SDLU_InitSurface( &surface[0]->clip_rect, depth );
+	
+	SDLU_BlitSurface( *surface,    &surface[0]->clip_rect,
+	                   newSurface, &newSurface->clip_rect  );
+			
+	SDL_FreeSurface( *surface );
+	
+	*surface = newSurface;
+}
+
+
+SDL_Surface* SDLU_InitSurface( SDL_Rect* rect, int depth )
+{
+	SDL_Surface*            surface = NULL;
+	
+	switch( depth )
+	{
+        case 32:
+            surface = SDL_CreateRGBSurface(
+                            SDL_SWSURFACE,
+                            rect->w,
+                            rect->h, 
+                            32,
+                            RED_MASK, GREEN_MASK, BLUE_MASK, 0);
+            break;
+          
+		case 8:
+			surface = SDL_CreateRGBSurface( 
+							SDL_SWSURFACE, 
+							rect->w, 
+							rect->h, 
+							8, 
+							0, 0, 0, 0 );
+
+            SDL_SetSurfacePalette(surface, s_grayscalePalette);
+			break;
+            
+        default:
+            Error("SDLU_InitSurface: invalid depth");
+            return NULL;
+	}					
+	
+	if( surface == NULL )
+	{
+		Error( "SDLU_InitSurface: SDL_CreateRGBSurface" );
+		return NULL;
+	}
+	
+	// SDL_FillRect only works on 8-bit or higher surfaces.
+	if( depth >= 8 )
+		SDL_FillRect( surface, rect, SDL_MapRGB( surface->format, 0xFF, 0xFF, 0xFF ) );
+	
+	return surface;
+}
+
+
+void SDLU_BlitFrontSurface( SDL_Surface* source, SDL_Rect* sourceSDLRect, SDL_Rect* destSDLRect )
+{
+	SDLU_BlitSurface( source,       sourceSDLRect,
+	                  g_frontSurface, destSDLRect );
+}
+
+
+void SDLU_SetBrightness( float b )
+{
+    s_fadeGamma = b;
+}
+
+void SDLU_Yield()
+{
+    SDL_Delay( 2 );
+    SDL_PumpEvents();
+}
+
+void SDLU_PumpEvents()
+{
+	static unsigned int  lastPump = 0;
+	unsigned int  time = MTickCount();
+	
+	if( lastPump != time )
+	{
+        SDL_Event evt;
+        while( SDL_PollEvent( &evt ) ) { }
+		lastPump = time;
+	}
+}
+
+
+MBoolean SDLU_IsForeground()
+{
+    return s_isForeground;
+}
+
+
+void SDLU_StartWatchingTyping()
+{
+	s_interestedInTyping = true;
+    s_keyBufferSize = 0;  // clear keybuffer
+}
+
+
+void SDLU_StopWatchingTyping()
+{
+	s_interestedInTyping = false;
+}
+
+
+MBoolean SDLU_CheckASCIITyping(char* ascii)
+{
+    if (s_keyBufferSize > 0 && s_keyBuffer[0].isASCII)
+	{
+        *ascii = s_keyBuffer[0].value.ascii;
+
+        s_keyBufferSize--;
+        SDL_memcpy(&s_keyBuffer[0], &s_keyBuffer[1], (s_keyBufferSize) * sizeof(BufferedKey));
+
+		return true;
+	}
+
+	*ascii = '\0';
+	return false;
+}
+
+
+MBoolean SDLU_CheckSDLTyping(SDL_Keycode* sdlKey)
+{
+    if (s_keyBufferSize > 0 && !s_keyBuffer[0].isASCII)
+    {
+        *sdlKey = s_keyBuffer[0].value.keycode;
+
+        s_keyBufferSize--;
+        SDL_memcpy(&s_keyBuffer[0], &s_keyBuffer[1], (s_keyBufferSize) * sizeof(BufferedKey));
+
+        return true;
+    }
+    
+    *sdlKey = SDLK_UNKNOWN;
+    return false;
+}
+
+
+static MPoint SDLUi_TranslatePointFromWindowToFrontSurface(MPoint pt)
+{
+	// On macOS, the mouse position is relative to the window's "point size" on Retina screens.
+	int windowPointW = 1;
+	int windowPointH = 1;
+	int windowPixelW = 1;
+	int windowPixelH = 1;
+
+	SDL_GetWindowSize(g_window, &windowPointW, &windowPointH);
+#if SDL_VERSION_ATLEAST(2,26,0)
+	SDL_GetWindowSizeInPixels(g_window, &windowPixelW, &windowPixelH);
+#else
+	// Backwards compat with old versions of SDL
+	windowPixelW = windowPointW;
+	windowPixelH = windowPointH;
+#endif
+
+	if (windowPointW != windowPixelW || windowPointH != windowPixelH)
+	{
+		float dpiScaleX = (float) windowPixelW / (float) windowPointW;		// gGameWindowWidth is in actual pixels
+		float dpiScaleY = (float) windowPixelH / (float) windowPointH;		// gGameWindowHeight is in actual pixels
+		pt.h *= dpiScaleX;
+		pt.v *= dpiScaleY;
+	}
+
+    SDL_Rect viewport;
+    float scaleX, scaleY;
+    SDL_RenderGetViewport(g_renderer, &viewport);
+    SDL_RenderGetScale(g_renderer, &scaleX, &scaleY);
+
+    pt.h = pt.h / scaleX - viewport.x;
+    pt.v = pt.v / scaleY - viewport.y;
+
+    if (widescreen)
+    {
+        pt.h += g_widescreenCrop.x;
+        pt.v += g_widescreenCrop.y;
+    }
+
+    return pt;
+}
+
+
+void SDLU_GetMouse( MPoint* pt )
+{
+	SDLU_PumpEvents();
+	*pt = SDLUi_TranslatePointFromWindowToFrontSurface(s_mousePosition);
+}
+
+
+int SDLU_Button()
+{
+	SDLU_PumpEvents();
+	return s_mouseButton;
+}
+
+
+void SDLU_AcquireSurface( SDL_Surface* surface )
+{
+	if (s_acquireHead >= arrsize(s_acquireList) - 1)
+		Error("SDLU_AcquireSurface: overflow");
+
+	s_acquireList[++s_acquireHead] = surface;
+}
+
+
+SDL_Surface* SDLU_GetCurrentSurface()
+{	
+	return s_acquireList[s_acquireHead];
+}
+
+
+void SDLU_ReleaseSurface( SDL_Surface* surface )
+{
+    if (s_acquireHead < 0)
+        Error( "SDLU_ReleaseSurface: underflow" );
+		
+    if( s_acquireList[s_acquireHead] != surface )
+		Error( "SDLU_ReleaseSurface: out of order" );
+		
+	s_acquireHead--;
+}
+
+
+void SDLU_Present()
+{
+    SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
+
+    SDL_UpdateTexture(g_windowTexture, NULL, g_frontSurface->pixels, g_frontSurface->pitch);
+    SDL_RenderClear(g_renderer);
+    
+    SDL_RenderCopy(g_renderer, g_windowTexture, widescreen ? &g_widescreenCrop : NULL, NULL);
+
+    if (s_fadeGamma < 1.0)
+    {
+        SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND);
+        SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, (Uint8)((1.0f - s_fadeGamma) * 255.0f));
+        SDL_RenderFillRect(g_renderer, NULL);
+    }
+
+    SDL_RenderPresent(g_renderer);
+
+#if 0
+    static int         s_fpsAccumulator = 0;
+    static int         s_fpsSampleStart = 0;
+    const int          k_fpsSampleInterval = 500;
+
+    s_fpsAccumulator++;
+    int now = SDL_GetTicks();
+    int elapsed = now - s_fpsSampleStart;
+    if (elapsed > k_fpsSampleInterval)
+    {
+        float fps = s_fpsAccumulator / (elapsed / 1000.0f);
+#if _DEBUG
+        printf("FPS: %.1f\n", fps);
+#endif
+        s_fpsAccumulator = 0;
+        s_fpsSampleStart = now;
+    }
+#endif
+}
+
+
+void SDLU_SetSystemCursor(int which)
+{
+#if USE_CURSOR_SPRITE
+    SDL_ShowCursor(SDL_DISABLE);
+#else
+    switch (which)
+    {
+        case SYSTEM_CURSOR_OFF:
+            SDL_ShowCursor(SDL_DISABLE);
+            SDL_SetCursor(s_standardCursor);
+            break;
+
+        case SYSTEM_CURSOR_ARROW:
+            SDL_SetCursor(s_standardCursor);
+            SDL_ShowCursor(SDL_ENABLE);
+            break;
+
+        case SYSTEM_CURSOR_HAND:
+            SDL_SetCursor(s_handCursor);
+            SDL_ShowCursor(SDL_ENABLE);
+            break;
+    }
+#endif
+}
--- a/src/SDLU.cpp
+++ /dev/null
@@ -1,572 +1,0 @@
-///
-///  SDLU.c
-///
-///  SDL utilities.
-///
-///  John Stiles, 2002/10/12
-///
-
-#include "SDLU.h"
-#include "gameticks.h"
-#include "music.h"
-
-#include "main.h" // for Error
-#include <deque>
-
-using std::deque;
-
-// for acquiresurface
-const int           k_acquireMax = 10;
-static int          s_acquireHead = -1;
-static SDL_Surface* s_acquireList[k_acquireMax];
-
-// for initsurface
-static SDL_Palette* s_grayscalePalette;
-
-// for button and getmouse
-static int          s_mouseButton;
-static MPoint       s_mousePosition;
-
-// system mouse cursors
-static SDL_Cursor*  s_standardCursor = NULL;
-static SDL_Cursor*  s_handCursor = NULL;
-
-// for event loop
-static MBoolean     s_isForeground = true;
-
-// for fade out / fade in
-static float        s_fadeGamma = 1;
- 
-// for checktyping
-struct BufferedKey
-{
-    bool isASCII;
-    
-    union
-    {
-        char         ascii;
-        SDL_Keycode  keycode;
-    } value;
-};
-
-
-static MBoolean                s_interestedInTyping = false;
-static std::deque<BufferedKey> s_keyBuffer;
-
-int SDLUi_EventFilter(void*, SDL_Event *event)
-{
-    switch (event->type)
-    {
-        case SDL_TEXTINPUT:
-        {
-            // Put text input into a buffer.
-            if (s_interestedInTyping)
-            {
-                for (char* asciiPtr = event->text.text; *asciiPtr; ++asciiPtr)
-                {
-                    BufferedKey key;
-                    key.isASCII = true;
-                    key.value.ascii = *asciiPtr;
-                    s_keyBuffer.push_back(key);
-                }
-            }
-            break;
-        }
-    
-        case SDL_KEYDOWN:
-        {
-            // Put keydowns in a buffer
-            if (s_interestedInTyping)
-            {
-                BufferedKey key;
-                key.isASCII = false;
-                key.value.keycode = event->key.keysym.sym;
-                s_keyBuffer.push_back(key);
-            }
-            break;
-        }
-    
-        // Get mouse state
-        case SDL_MOUSEBUTTONDOWN:
-        {
-            if( event->button.button == SDL_BUTTON_LEFT )
-                s_mouseButton = true;
-            
-            s_mousePosition.v = event->button.y;
-            s_mousePosition.h = event->button.x;
-            break;
-        }
-    
-        case SDL_MOUSEBUTTONUP:
-        {
-            if( event->button.button == SDL_BUTTON_LEFT )
-                s_mouseButton = false;
-            
-            s_mousePosition.v = event->button.y;
-            s_mousePosition.h = event->button.x;
-            break;
-        }
-    
-        case SDL_MOUSEMOTION:
-        {
-            s_mousePosition.v = event->motion.y;
-            s_mousePosition.h = event->motion.x;
-            s_mouseButton = event->motion.state & SDL_BUTTON(1);
-            break;
-        }
-    
-        case SDL_QUIT:
-        {
-            finished = true;
-            break;
-        }
-            
-        case SDL_WINDOWEVENT:
-        {
-            if (event->window.event == SDL_WINDOWEVENT_FOCUS_LOST && s_isForeground)
-            {
-                FreezeGameTickCount();
-                //EnableMusic(false);
-                s_isForeground = false;
-            }
-            else if (event->window.event == SDL_WINDOWEVENT_FOCUS_GAINED && !s_isForeground)
-            {
-                UnfreezeGameTickCount();
-                //EnableMusic(musicOn);
-                s_isForeground = true;
-                
-                DoFullRepaint();
-            }
-            else if (event->window.event == SDL_WINDOWEVENT_RESIZED)
-            {
-                SDLU_CreateRendererTexture();
-            }
-            break;
-        }
-    }
-    
-    return 1;
-}
-
-
-void SDLU_CreateRendererTexture()
-{
-    if (!g_renderer)
-        return;
-
-    if (!crispUpscaling)
-    {
-        SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
-        SDL_RenderSetIntegerScale(g_renderer, SDL_FALSE);
-    }
-    else
-    {
-        int minWidth = 640;
-        int minHeight = widescreen ? 360 : 480;
-
-        int currentWidth = 0;
-        int currentHeight = 0;
-#if SDL_VERSION_ATLEAST(2,26,0)
-        SDL_GetWindowSizeInPixels(g_window, &currentWidth, &currentHeight);
-#else
-        SDL_GetWindowSize(g_window, &currentWidth, &currentHeight);
-#endif
-
-        if (currentWidth < minWidth || currentHeight < minHeight)
-        {
-            SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
-            SDL_RenderSetIntegerScale(g_renderer, SDL_FALSE);
-        }
-        else
-        {
-            SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "0");
-            SDL_RenderSetIntegerScale(g_renderer, SDL_TRUE);
-        }
-    }
-
-    if (g_windowTexture)
-        SDL_DestroyTexture(g_windowTexture);
-
-    g_windowTexture = SDL_CreateTexture(g_renderer,
-        SDL_PIXELFORMAT_RGB888,
-        SDL_TEXTUREACCESS_STREAMING,
-        640, 480);
-
-    SDL_RenderSetLogicalSize(g_renderer, 640, widescreen ? 360: 480);
-}
-
-
-void SDLU_Init()
-{
-    SDL_SetEventFilter(SDLUi_EventFilter, NULL);
-
-    // Initialize eight bit grayscale ramp palette.
-    SDL_Color  grayscaleColors[256];
-    for (int index=0; index<256; index++)
-    {
-        grayscaleColors[index].r =
-        grayscaleColors[index].g =
-        grayscaleColors[index].b = 255 - index;
-        grayscaleColors[index].a = 255;
-    }
-    
-    s_grayscalePalette = SDL_AllocPalette(256);
-    SDL_SetPaletteColors(s_grayscalePalette, grayscaleColors, 0, arrsize(grayscaleColors));
-
-    s_standardCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
-    s_handCursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
-}
-
-
-SDL_Rect* SDLU_MRectToSDLRect( const MRect* in, SDL_Rect* out )
-{
-	int t = in->top, l = in->left, b = in->bottom, r = in->right;
-	
-	out->x = l;
-	out->y = t;
-	out->w = r - l;
-	out->h = b - t; 
-	
-	return out;
-}
-
-
-MRect* SDLU_SDLRectToMRect( const SDL_Rect* in, MRect* out )
-{
-	int x = in->x, y = in->y, w = in->w, h = in->h;
-	
-	out->top    = y;
-	out->left   = x;
-	out->bottom = y + h;
-	out->right  = x + w;
-	
-	return out; 
-}
-
-
-int SDLU_BlitSurface( SDL_Surface* src, SDL_Rect* srcrect,
-			          SDL_Surface* dst, SDL_Rect* dstrect  )
-{
-	// Let SDL handle this.
-	return SDL_BlitSurface( src, srcrect,
-	                        dst, dstrect  );
-}
-
-
-void SDLU_GetPixel(	SDL_Surface* surface, int x, int y, SDL_Color* pixel )
-{
-	unsigned int   px;
-	unsigned char* ptr;
-	
-	switch( surface->format->BytesPerPixel )
-	{
-		case 1:
-			ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x);
-			px = *(unsigned char*) ptr;
-			break;
-		
-		case 2:
-			ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x * 2);
-			px = *(unsigned short*) ptr;
-			break;
-
-		case 4:
-			ptr = (unsigned char*)surface->pixels + (y * surface->pitch) + (x * 4);
-			px = *(unsigned int *) ptr;
-			break;
-        
-        default:
-            Error("SDLU_GetPixel: unrecognized surface format");
-            return;
-	}
-	
-	return SDL_GetRGB( px, surface->format, &pixel->r, &pixel->g, &pixel->b );
-}
-
-
-void SDLU_ChangeSurfaceDepth( SDL_Surface** surface, int depth )
-{
-	SDL_Surface* newSurface;
-
-	newSurface = SDLU_InitSurface( &surface[0]->clip_rect, depth );
-	
-	SDLU_BlitSurface( *surface,    &surface[0]->clip_rect,
-	                   newSurface, &newSurface->clip_rect  );
-			
-	SDL_FreeSurface( *surface );
-	
-	*surface = newSurface;
-}
-
-
-SDL_Surface* SDLU_InitSurface( SDL_Rect* rect, int depth )
-{
-	SDL_Surface*            surface = NULL;
-	
-	switch( depth )
-	{
-        case 32:
-            surface = SDL_CreateRGBSurface(
-                            SDL_SWSURFACE,
-                            rect->w,
-                            rect->h, 
-                            32,
-                            RED_MASK, GREEN_MASK, BLUE_MASK, 0);
-            break;
-          
-		case 8:
-			surface = SDL_CreateRGBSurface( 
-							SDL_SWSURFACE, 
-							rect->w, 
-							rect->h, 
-							8, 
-							0, 0, 0, 0 );
-
-            SDL_SetSurfacePalette(surface, s_grayscalePalette);
-			break;
-            
-        default:
-            Error("SDLU_InitSurface: invalid depth");
-            return NULL;
-	}					
-	
-	if( surface == NULL )
-	{
-		Error( "SDLU_InitSurface: SDL_CreateRGBSurface" );
-		return NULL;
-	}
-	
-	// SDL_FillRect only works on 8-bit or higher surfaces.
-	if( depth >= 8 )
-		SDL_FillRect( surface, rect, SDL_MapRGB( surface->format, 0xFF, 0xFF, 0xFF ) );
-	
-	return surface;
-}
-
-
-void SDLU_BlitFrontSurface( SDL_Surface* source, SDL_Rect* sourceSDLRect, SDL_Rect* destSDLRect )
-{
-	SDLU_BlitSurface( source,       sourceSDLRect,
-	                  g_frontSurface, destSDLRect );
-}
-
-
-void SDLU_SetBrightness( float b )
-{
-    s_fadeGamma = b;
-}
-
-void SDLU_Yield()
-{
-    SDL_Delay( 2 );
-    SDL_PumpEvents();
-}
-
-void SDLU_PumpEvents()
-{
-	static unsigned int  lastPump = 0;
-	unsigned int  time = MTickCount();
-	
-	if( lastPump != time )
-	{
-        SDL_Event evt;
-        while( SDL_PollEvent( &evt ) ) { }
-		lastPump = time;
-	}
-}
-
-
-MBoolean SDLU_IsForeground()
-{
-    return s_isForeground;
-}
-
-
-void SDLU_StartWatchingTyping()
-{
-	s_interestedInTyping = true;
-    s_keyBuffer.clear();
-}
-
-
-void SDLU_StopWatchingTyping()
-{
-	s_interestedInTyping = false;
-}
-
-
-MBoolean SDLU_CheckASCIITyping(char* ascii)
-{
-    if (!s_keyBuffer.empty() && s_keyBuffer.front().isASCII)
-	{
-        *ascii = s_keyBuffer.front().value.ascii;
-        s_keyBuffer.pop_front();
-		return true;
-	}
-
-	*ascii = '\0';
-	return false;
-}
-
-
-MBoolean SDLU_CheckSDLTyping(SDL_Keycode* sdlKey)
-{
-    if (!s_keyBuffer.empty() && !s_keyBuffer.front().isASCII)
-    {
-        *sdlKey = s_keyBuffer.front().value.keycode;
-        s_keyBuffer.pop_front();
-        return true;
-    }
-    
-    *sdlKey = SDLK_UNKNOWN;
-    return false;
-}
-
-
-static MPoint SDLUi_TranslatePointFromWindowToFrontSurface(MPoint pt)
-{
-	// On macOS, the mouse position is relative to the window's "point size" on Retina screens.
-	int windowPointW = 1;
-	int windowPointH = 1;
-	int windowPixelW = 1;
-	int windowPixelH = 1;
-
-	SDL_GetWindowSize(g_window, &windowPointW, &windowPointH);
-#if SDL_VERSION_ATLEAST(2,26,0)
-	SDL_GetWindowSizeInPixels(g_window, &windowPixelW, &windowPixelH);
-#else
-	// Backwards compat with old versions of SDL
-	windowPixelW = windowPointW;
-	windowPixelH = windowPointH;
-#endif
-
-	if (windowPointW != windowPixelW || windowPointH != windowPixelH)
-	{
-		float dpiScaleX = (float) windowPixelW / (float) windowPointW;		// gGameWindowWidth is in actual pixels
-		float dpiScaleY = (float) windowPixelH / (float) windowPointH;		// gGameWindowHeight is in actual pixels
-		pt.h *= dpiScaleX;
-		pt.v *= dpiScaleY;
-	}
-
-    SDL_Rect viewport;
-    float scaleX, scaleY;
-    SDL_RenderGetViewport(g_renderer, &viewport);
-    SDL_RenderGetScale(g_renderer, &scaleX, &scaleY);
-
-    pt.h = pt.h / scaleX - viewport.x;
-    pt.v = pt.v / scaleY - viewport.y;
-
-    if (widescreen)
-    {
-        pt.h += g_widescreenCrop.x;
-        pt.v += g_widescreenCrop.y;
-    }
-
-    return pt;
-}
-
-
-void SDLU_GetMouse( MPoint* pt )
-{
-	SDLU_PumpEvents();
-	*pt = SDLUi_TranslatePointFromWindowToFrontSurface(s_mousePosition);
-}
-
-
-int SDLU_Button()
-{
-	SDLU_PumpEvents();
-	return s_mouseButton;
-}
-
-
-void SDLU_AcquireSurface( SDL_Surface* surface )
-{
-	if (s_acquireHead >= arrsize(s_acquireList) - 1)
-		Error("SDLU_AcquireSurface: overflow");
-
-	s_acquireList[++s_acquireHead] = surface;
-}
-
-
-SDL_Surface* SDLU_GetCurrentSurface()
-{	
-	return s_acquireList[s_acquireHead];
-}
-
-
-void SDLU_ReleaseSurface( SDL_Surface* surface )
-{
-    if (s_acquireHead < 0)
-        Error( "SDLU_ReleaseSurface: underflow" );
-		
-    if( s_acquireList[s_acquireHead] != surface )
-		Error( "SDLU_ReleaseSurface: out of order" );
-		
-	s_acquireHead--;
-}
-
-
-void SDLU_Present()
-{
-    SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, 255);
-
-    SDL_UpdateTexture(g_windowTexture, NULL, g_frontSurface->pixels, g_frontSurface->pitch);
-    SDL_RenderClear(g_renderer);
-    
-    SDL_RenderCopy(g_renderer, g_windowTexture, widescreen ? &g_widescreenCrop : NULL, NULL);
-
-    if (s_fadeGamma < 1.0)
-    {
-        SDL_SetRenderDrawBlendMode(g_renderer, SDL_BLENDMODE_BLEND);
-        SDL_SetRenderDrawColor(g_renderer, 0, 0, 0, (Uint8)((1.0f - s_fadeGamma) * 255.0f));
-        SDL_RenderFillRect(g_renderer, NULL);
-    }
-
-    SDL_RenderPresent(g_renderer);
-
-#if 0
-    static int         s_fpsAccumulator = 0;
-    static int         s_fpsSampleStart = 0;
-    const int          k_fpsSampleInterval = 500;
-
-    s_fpsAccumulator++;
-    int now = SDL_GetTicks();
-    int elapsed = now - s_fpsSampleStart;
-    if (elapsed > k_fpsSampleInterval)
-    {
-        float fps = s_fpsAccumulator / (elapsed / 1000.0f);
-#if _DEBUG
-        printf("FPS: %.1f\n", fps);
-#endif
-        s_fpsAccumulator = 0;
-        s_fpsSampleStart = now;
-    }
-#endif
-}
-
-
-void SDLU_SetSystemCursor(int which)
-{
-#if USE_CURSOR_SPRITE
-    SDL_ShowCursor(SDL_DISABLE);
-#else
-    switch (which)
-    {
-        case SYSTEM_CURSOR_OFF:
-            SDL_ShowCursor(SDL_DISABLE);
-            SDL_SetCursor(s_standardCursor);
-            break;
-
-        case SYSTEM_CURSOR_ARROW:
-            SDL_SetCursor(s_standardCursor);
-            SDL_ShowCursor(SDL_ENABLE);
-            break;
-
-        case SYSTEM_CURSOR_HAND:
-            SDL_SetCursor(s_handCursor);
-            SDL_ShowCursor(SDL_ENABLE);
-            break;
-    }
-#endif
-}
--- /dev/null
+++ b/src/blitter.c
@@ -1,0 +1,870 @@
+// blitter.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "gworld.h"
+#include "blitter.h"
+#include "font.h"
+#include "level.h"
+#include "graphics.h"
+
+MBoolean update[2][kGridAcross][kGridDown];
+MBoolean refresh[2];
+
+void InitBlitter( void )
+{
+	int player, x, y;
+
+	for( player=0; player<=1; player++ )
+	{
+		refresh[player] = false;
+		
+		for( x=0; x<kGridAcross; x++ )
+		{
+			for( y=0; y<kGridDown; y++ )
+			{
+				update[player][x][y] = false;
+			}
+		}
+	}
+}
+
+void UpdatePlayerWindow( int player )
+{
+	SDL_Rect fullSDLRect, offsetSDLRect;
+	int      x, y;
+	
+	if( control[player] == kNobodyControl ) return;
+	
+	if( playerWindowVisible[player] && refresh[player] )
+	{
+		MRect updateRect = {0, 0, 0, 0}, fullRect, offsetRect;
+		MBoolean first = true;
+		
+		for( x=0; x<kGridAcross; x++ )
+		{
+			for( y=1; y<kGridDown; y++ )
+			{
+				if( update[player][x][y] )
+				{
+					updateRect.top  = y * kBlobVertSize;
+					updateRect.left = x * kBlobHorizSize;
+					updateRect.bottom = updateRect.top + kBlobVertSize;
+					updateRect.right = updateRect.left + kBlobHorizSize;
+					if( first )
+					{
+						fullRect = updateRect;
+						first = false;
+					}
+					else
+					{
+						UnionMRect( &fullRect, &updateRect, &fullRect );
+					}
+
+					update[player][x][y] = false;
+				}
+			}
+		}
+
+		if( !first )
+		{
+			offsetRect = fullRect;
+			OffsetMRect( &offsetRect, playerWindowRect[player].left, playerWindowRect[player].top - kBlobVertSize );
+			
+			SDLU_BlitFrontSurface( playerSpriteSurface[player], 
+								   SDLU_MRectToSDLRect( &fullRect, &fullSDLRect ),
+								   SDLU_MRectToSDLRect( &offsetRect, &offsetSDLRect ) );
+		}
+	}
+}
+
+void SetUpdateRect( int player, MRect *where )
+{
+	int x,y;
+	int xMin, xMax, yMin, yMax;
+	
+	xMin = where->left / kBlobHorizSize;
+	xMax = ( where->right + kBlobHorizSize - 1 ) / kBlobHorizSize;
+	
+	if( xMin < 0 ) xMin = 0;
+	if( xMin > (kGridAcross-1) ) xMin = kGridAcross-1;
+	if( xMax < 0 ) xMax = 0;
+	if( xMax > kGridAcross ) xMax = kGridAcross;
+	
+	yMin = where->top / kBlobVertSize;
+	yMax = ( where->bottom + kBlobVertSize - 1 ) / kBlobVertSize;
+
+	if( yMin < 0 ) yMin = 0;
+	if( yMin > (kGridDown-1) ) yMin = kGridDown-1;
+	if( yMax < 0 ) yMax = 0;
+	if( yMax > kGridDown ) yMax = kGridDown;
+	
+	for( x=xMin; x<xMax; x++ )
+	{
+		for( y=yMin; y<yMax; y++ )
+		{
+			update[player][x][y] = true;
+		}
+	}
+	
+	refresh[player] = true;
+}
+
+
+void SurfaceBlitMask( SDL_Surface* object,     SDL_Surface* mask,     SDL_Surface* dest,
+                      const MRect*  objectRect, const MRect*  maskRect, const MRect*  destRect )
+{
+	int            startX = 0, startY = 0, endX, endY, x, y, srcRowBytes, mskRowBytes, dstRowBytes;
+	unsigned char *src, *msk, *dst;
+	MRect          destBounds;
+
+	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
+	
+	endX = objectRect->right - objectRect->left;
+	endY = objectRect->bottom - objectRect->top;
+
+	if( destRect->left   > destBounds.right  ||			// completely clipped?
+		destRect->right  < destBounds.left   ||
+		destRect->top    > destBounds.bottom ||
+		destRect->bottom < destBounds.top )
+	{
+		return; 												// do nothing
+	}
+	
+	src         = (unsigned char*) object->pixels;
+	msk         = (unsigned char*) mask->pixels;
+	dst         = (unsigned char*) dest->pixels;
+	srcRowBytes = object->pitch;
+	mskRowBytes = mask->pitch;
+	dstRowBytes = dest->pitch;
+	
+	src += (objectRect->top * srcRowBytes) + (objectRect->left * BYTES_PER_PIXEL);
+	msk += (maskRect->top   * mskRowBytes) + (maskRect->left   * BYTES_PER_MASK_PIXEL);
+	dst += (destRect->top   * dstRowBytes) + (destRect->left   * BYTES_PER_PIXEL);
+	
+	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
+	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
+	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
+	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
+	
+	msk += (mskRowBytes * startY) + (startX * BYTES_PER_MASK_PIXEL);
+	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+
+	for( y=startY; y<endY; y++ )
+	{
+		unsigned char *tSrc = src, *tDst = dst, *tMsk = msk;
+		
+		for( x=startX; x<endX; x++ )
+		{
+			if( *msk )
+			{
+				*(COLOR_T*)dst = *(COLOR_T*)src;
+			}
+			
+			src += BYTES_PER_PIXEL;
+			dst += BYTES_PER_PIXEL;
+			msk += BYTES_PER_MASK_PIXEL;
+		}
+		
+		src = tSrc + srcRowBytes;
+		dst = tDst + dstRowBytes;
+		msk = tMsk + mskRowBytes;
+	}
+}
+
+
+void SurfaceBlitBlob( const MRect* blobRect, const MRect* destRect )
+{
+	SurfaceBlitMask( blobSurface, maskSurface, SDLU_GetCurrentSurface(),
+	                 blobRect,    blobRect,    destRect                  );
+}
+
+
+void SurfaceBlitColor( SDL_Surface* mask,     SDL_Surface* dest,
+                       const MRect* maskRect, const MRect* destRect, 
+                       int r, int g, int b, int weight )
+{
+	int            startX = 0, startY = 0, endX, endY, x, y, mskRowBytes, dstRowBytes;
+	unsigned char *msk, *dst;
+	MRect          destBounds;
+	
+	endX = maskRect->right - maskRect->left;
+	endY = maskRect->bottom - maskRect->top;
+	
+	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
+
+	if( destRect->left   > destBounds.right  ||			// completely clipped?
+		destRect->right  < destBounds.left   ||
+		destRect->top    > destBounds.bottom ||
+		destRect->bottom < destBounds.top )
+	{
+		return; 												// do nothing
+	}
+	
+	msk         = (unsigned char*) mask->pixels;
+	dst         = (unsigned char*) dest->pixels;
+	mskRowBytes = mask->pitch;
+	dstRowBytes = dest->pitch;
+	
+	msk += (maskRect->top * mskRowBytes) + (maskRect->left * BYTES_PER_MASK_PIXEL);
+	dst += (destRect->top * dstRowBytes) + (destRect->left * BYTES_PER_PIXEL);
+	
+	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
+	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
+	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
+	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
+	
+	msk += (mskRowBytes * startY) + (startX * BYTES_PER_MASK_PIXEL);
+	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+
+	r *= weight;
+	g *= weight;
+	b *= weight;
+	weight = FULL_WEIGHT - weight;
+	
+	for( y=startY; y<endY; y++ )
+	{
+		unsigned char *tMsk = msk, *tDst = dst;
+		int work, workB, workG, workR;
+		
+		for( x=startX; x<endX; x++ )
+		{
+			if( *msk )
+			{
+				work = *(COLOR_T*)dst;
+                workB = ((((work                    ) & CHANNEL_MASK)*weight) + b) >> BITS_PER_1CHANNEL;
+                workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK)*weight) + g)                     ;
+                workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK)*weight) + r) << BITS_PER_1CHANNEL;
+
+                *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+			}
+			
+			dst += BYTES_PER_PIXEL;
+			msk += BYTES_PER_MASK_PIXEL;
+		}
+		
+		msk = tMsk + mskRowBytes;
+		dst = tDst + dstRowBytes;
+	}
+}
+
+
+void SurfaceBlitAlpha( SDL_Surface* back,     SDL_Surface* source,     SDL_Surface* alpha,     SDL_Surface* dest,
+                       const MRect* backRect, const MRect* sourceRect, const MRect* alphaRect, const MRect* destRect )
+{
+	int startX = 0, startY = 0, endX, endY, x, y, srcRowBytes, alfRowBytes, dstRowBytes, bckRowBytes;
+	unsigned char *bck, *src, *alf, *dst;
+	MRect destBounds;
+	
+	endX = sourceRect->right - sourceRect->left;
+	endY = sourceRect->bottom - sourceRect->top;
+	
+	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
+
+	if( destRect->left   > destBounds.right  ||			// completely clipped?
+		destRect->right  < destBounds.left   ||
+		destRect->top    > destBounds.bottom ||
+		destRect->bottom < destBounds.top )
+	{
+		return; 												// do nothing
+	}
+	
+	bck         = (unsigned char*) back->pixels;
+	src         = (unsigned char*) source->pixels; 
+	alf         = (unsigned char*) alpha->pixels;
+	dst         = (unsigned char*) dest->pixels; 
+	bckRowBytes = back->pitch; 
+	srcRowBytes = source->pitch;
+	alfRowBytes = alpha->pitch;
+	dstRowBytes = dest->pitch;
+	
+	bck += (backRect->top   * bckRowBytes) + (backRect->left   * BYTES_PER_PIXEL);
+	src += (sourceRect->top * srcRowBytes) + (sourceRect->left * BYTES_PER_PIXEL);
+	alf += (alphaRect->top  * alfRowBytes) + (alphaRect->left  * BYTES_PER_PIXEL);
+	dst += (destRect->top   * dstRowBytes) + (destRect->left   * BYTES_PER_PIXEL);
+	
+	if( destRect->left   < destBounds.left   ) startX -= destRect->left   - destBounds.left;
+	if( destRect->right  > destBounds.right  ) endX   -= destRect->right  - destBounds.right;
+	if( destRect->top    < destBounds.top    ) startY -= destRect->top    - destBounds.top;
+	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
+	
+	bck += (bckRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	alf += (alfRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+
+	for( y=startY; y<endY; y++ )
+	{
+		unsigned char *tSrc = src, *tAlf = alf, *tDst = dst, *tBck = bck;
+		int pixS, pixB, weightS, weightB, workB, workG, workR;
+		
+		for( x=startX; x<endX; x++ )
+		{
+			weightB = *(COLOR_T*)alf;
+			
+			if( (weightB & (RED_MASK | GREEN_MASK | BLUE_MASK)) == (RED_MASK | GREEN_MASK | BLUE_MASK))
+			{
+				*(COLOR_T*)dst = *(COLOR_T*)bck;
+			}
+			else if( (weightB & (RED_MASK | GREEN_MASK | BLUE_MASK)) == 0)
+			{
+				*(COLOR_T*)dst = *(COLOR_T*)src;
+			}
+			else
+			{
+				weightS = ~weightB;
+
+				pixS = *(COLOR_T*)src;
+				pixB = *(COLOR_T*)bck;
+								
+				workB = ((((pixS                      ) & CHANNEL_MASK) * ((weightS                      ) & CHANNEL_MASK)) + (((pixB                      ) & CHANNEL_MASK) * ((weightB                      ) & CHANNEL_MASK))) >> BITS_PER_1CHANNEL;
+				workG = ((((pixS >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * ((weightS >> BITS_PER_1CHANNEL ) & CHANNEL_MASK)) + (((pixB >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * ((weightB >> BITS_PER_1CHANNEL ) & CHANNEL_MASK)));
+				workR = ((((pixS >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * ((weightS >> BITS_PER_2CHANNELS) & CHANNEL_MASK)) + (((pixB >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * ((weightB >> BITS_PER_2CHANNELS) & CHANNEL_MASK))) << BITS_PER_1CHANNEL;
+
+				*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+			}
+
+			src += BYTES_PER_PIXEL;
+			alf += BYTES_PER_PIXEL;
+			bck += BYTES_PER_PIXEL;
+            dst += BYTES_PER_PIXEL;
+		}
+		
+		bck = tBck + bckRowBytes;
+		src = tSrc + srcRowBytes;
+		alf = tAlf + alfRowBytes;
+		dst = tDst + dstRowBytes;
+	}
+}
+
+
+void SurfaceBlitWeightedDualAlpha(SDL_Surface* back,     SDL_Surface* source,     SDL_Surface* mask,     SDL_Surface* alpha,     SDL_Surface* dest,
+                                   const MRect* backRect, const MRect* sourceRect, const MRect* maskRect, const MRect* alphaRect, const MRect* destRect,
+                                   int inWeight )
+{
+	int startX = 0, startY = 0, endX, endY, x, y,
+	    srcRowBytes, alfRowBytes, mskRowBytes, dstRowBytes, bckRowBytes;
+	unsigned char *bck, *src, *alf, *msk, *dst;
+	MRect destBounds;
+
+	endX = sourceRect->right - sourceRect->left;
+	endY = sourceRect->bottom - sourceRect->top;
+	
+	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
+	
+	if( destRect->left   > destBounds.right  ||			// completely clipped?
+		destRect->right  < destBounds.left   ||
+		destRect->top    > destBounds.bottom ||
+		destRect->bottom < destBounds.top )
+	{
+		return; 												// do nothing
+	}
+	
+	bck = (unsigned char*) back->pixels;
+	src = (unsigned char*) source->pixels;
+	msk = (unsigned char*) mask->pixels;
+	alf = (unsigned char*) alpha->pixels;
+	dst = (unsigned char*) dest->pixels;
+	
+	bckRowBytes = back->pitch;
+	srcRowBytes = source->pitch;
+	mskRowBytes = mask->pitch;
+	alfRowBytes = alpha->pitch;
+	dstRowBytes = dest->pitch;
+	
+	bck += (backRect->top   * bckRowBytes) + (backRect->left   * BYTES_PER_PIXEL);
+	src += (sourceRect->top * srcRowBytes) + (sourceRect->left * BYTES_PER_PIXEL);
+	alf += (alphaRect->top  * alfRowBytes) + (alphaRect->left  * BYTES_PER_PIXEL);
+	dst += (destRect->top   * dstRowBytes) + (destRect->left   * BYTES_PER_PIXEL);
+	msk += (maskRect->top   * mskRowBytes) + (maskRect->left   * BYTES_PER_MASK_PIXEL);
+	
+	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
+	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
+	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
+	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
+	
+	bck += (bckRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	alf += (alfRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	msk += (mskRowBytes * startY) + (startX * BYTES_PER_MASK_PIXEL);
+	
+	for( y=startY; y<endY; y++ )
+	{
+		unsigned char *tSrc = src, *tAlf = alf, *tDst = dst, *tBck = bck, *tMsk = msk;
+		int pixS, pixB, weightS, weightB, work, workB, workG, workR;
+		
+		for( x=startX; x<endX; x++ )
+		{
+			if( *msk )
+			{
+				work = *(COLOR_T*)alf;
+				workB = ((((work                    ) & CHANNEL_MASK)*inWeight) ) >> BITS_PER_1CHANNEL;
+				workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK)*inWeight) );
+				workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK)*inWeight) ) << BITS_PER_1CHANNEL;
+
+				weightB = ~((workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK));
+				
+                if( (weightB & (RED_MASK | GREEN_MASK | BLUE_MASK)) == (RED_MASK | GREEN_MASK | BLUE_MASK))
+				{
+					*(COLOR_T*)dst = *(COLOR_T*)bck;
+				}
+                else if( (weightB & (RED_MASK | GREEN_MASK | BLUE_MASK)) == 0)
+				{
+					*(COLOR_T*)dst = *(COLOR_T*)src;
+				}
+				else
+				{
+					weightS = ~weightB;
+
+					pixS = *(COLOR_T*)src;
+					pixB = *(COLOR_T*)bck;
+									
+					workB = ((((pixS                      ) & CHANNEL_MASK) * ((weightS                      ) & CHANNEL_MASK)) + (((pixB                      ) & CHANNEL_MASK) * ((weightB                      ) & CHANNEL_MASK))) >> BITS_PER_1CHANNEL;
+					workG = ((((pixS >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * ((weightS >> BITS_PER_1CHANNEL ) & CHANNEL_MASK)) + (((pixB >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * ((weightB >> BITS_PER_1CHANNEL ) & CHANNEL_MASK)));
+					workR = ((((pixS >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * ((weightS >> BITS_PER_2CHANNELS) & CHANNEL_MASK)) + (((pixB >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * ((weightB >> BITS_PER_2CHANNELS) & CHANNEL_MASK))) << BITS_PER_1CHANNEL;
+
+					*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+				}
+			}
+
+			src += BYTES_PER_PIXEL;
+			alf += BYTES_PER_PIXEL;
+			bck += BYTES_PER_PIXEL;
+			dst += BYTES_PER_PIXEL;
+			msk += BYTES_PER_MASK_PIXEL;
+		}
+		
+		bck = tBck + bckRowBytes;
+		src = tSrc + srcRowBytes;
+		alf = tAlf + alfRowBytes;
+		dst = tDst + dstRowBytes;
+		msk = tMsk + mskRowBytes;
+	}
+}
+
+void SurfaceBlitWeightedCharacter( SkittlesFontPtr font, unsigned char text, MPoint *dPoint, int r, int g, int b, int alpha )
+{
+	if (alpha == FULL_WEIGHT)
+	{
+		SurfaceBlitCharacter( font, text, dPoint, r, g, b, 0 );
+        return;
+	}
+    
+	if (alpha == 0)
+	{
+		return;
+	}
+
+    SDL_Surface*   destSurface;
+    unsigned char* src;
+    unsigned char* dst;
+    int            srcRowBytes;
+    int            dstRowBytes;
+    int            index;
+    MRect          destBounds;
+    
+    int height = font->surface->h;
+    int width  = font->width[text];
+    int across = font->across[text];
+    
+    destSurface = SDLU_GetCurrentSurface();
+    SDLU_SDLRectToMRect( &destSurface->clip_rect, &destBounds );
+    
+    if( (dPoint->h + width)  > destBounds.right           ||      // clipped?
+        (dPoint->v + height) > destBounds.bottom          || 
+         dPoint->h           < destBounds.left            ||
+         dPoint->v           < destBounds.top                )
+    {
+        dPoint->h += width;
+        return;                                               // do nothing
+    }
+    
+    srcRowBytes = font->surface->pitch;
+    dstRowBytes = destSurface->pitch;
+    
+    src = (unsigned char*) font->surface->pixels + across;
+    dst = (unsigned char*) destSurface->pixels + (dPoint->h * BYTES_PER_PIXEL) + (dPoint->v * dstRowBytes);
+    
+    while( height-- )
+    {
+        unsigned char *tSrc = src, *tDst = dst;
+        
+        for( index=0; index<width; index++ )
+        {
+            int workR, workG, workB, work, weightS, weightD;
+            weightS = *src & CHANNEL_MASK;
+            
+            weightS = (weightS * alpha) >> BITS_PER_1CHANNEL;
+            
+            if( weightS )
+            {
+                weightD = FULL_WEIGHT - weightS;
+
+                work = *(COLOR_T*)dst;
+                workB = ((((work                    ) & CHANNEL_MASK) * weightD) + (b * weightS)) >> BITS_PER_1CHANNEL;
+                workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weightD) + (g * weightS));
+                workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK) * weightD) + (r * weightS)) << BITS_PER_1CHANNEL;
+
+                *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+            }
+            
+            src++;
+            dst+=BYTES_PER_PIXEL;
+        }
+        
+        src = tSrc + srcRowBytes;
+        dst = tDst + dstRowBytes;
+    }
+        
+    dPoint->h += width;
+}
+
+void SurfaceBlitCharacter( SkittlesFontPtr font, unsigned char text, MPoint *dPoint, int r, int g, int b, int dropShadow )
+{
+    SDL_Surface*    destSurface;
+	unsigned char*  src;
+	unsigned char*  dst;
+	int             srcRowBytes;
+	int             dstRowBytes;
+	int             index;
+	int             rgbPremixed;
+	MRect           destBounds;
+	
+	int height = font->surface->h;
+	int width  = font->width[text];
+	int across = font->across[text];
+	
+	destSurface = SDLU_GetCurrentSurface();
+	SDLU_SDLRectToMRect( &destSurface->clip_rect, &destBounds );	
+	
+	if( (dPoint->h + width)  > destBounds.right           ||      // clipped?
+	    (dPoint->v + height) > destBounds.bottom          || 
+	     dPoint->h           < destBounds.left            ||
+	     dPoint->v           < destBounds.top                )
+	{
+		dPoint->h += width;
+		return;                                               // do nothing
+	}
+	
+	srcRowBytes = font->surface->pitch;
+	dstRowBytes = destSurface->pitch;
+	
+	src = (unsigned char*) font->surface->pixels + across;
+	dst = (unsigned char*) destSurface->pixels + (dPoint->h * BYTES_PER_PIXEL) + (dPoint->v * dstRowBytes);
+	rgbPremixed = (r << BITS_PER_2CHANNELS) | (g << BITS_PER_1CHANNEL) | b;
+	
+	switch( dropShadow )
+	{
+		case 0:
+			while( height-- )
+			{
+				unsigned char *tSrc = src, *tDst = dst;
+				
+				for( index=0; index<width; index++ )
+				{
+					int workR, workG, workB, work, weightS, weightD;
+					weightS = *src & CHANNEL_MASK;
+					
+					if( weightS == CHANNEL_MASK )
+					{
+						*(COLOR_T*)dst = rgbPremixed;
+					}
+					else if( weightS > 0 )
+					{
+						weightD = FULL_WEIGHT - weightS;
+
+						work = *(COLOR_T*)dst;
+						workB = ((((work                      ) & CHANNEL_MASK) * weightD) + (b * weightS)) >> BITS_PER_1CHANNEL;
+						workG = ((((work >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weightD) + (g * weightS));
+						workR = ((((work >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * weightD) + (r * weightS)) << BITS_PER_1CHANNEL;
+
+						*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+					}
+					
+					src++;
+					dst+=BYTES_PER_PIXEL;
+				}
+				
+				src = tSrc + srcRowBytes;
+				dst = tDst + dstRowBytes;
+			}
+			break;
+		
+		default:
+			{
+				unsigned char *drp = dst + ((dstRowBytes + BYTES_PER_PIXEL) * dropShadow);
+				
+				while( height-- )
+				{
+					unsigned char *tSrc = src, *tDst = dst, *tDrp = drp;
+					
+					for( index=0; index<width; index++ )
+					{
+						int workR, workG, workB, work, weightS, weightD;
+						weightS = *src & CHANNEL_MASK;
+						
+						if( weightS == CHANNEL_MASK )
+						{
+							*(COLOR_T*)drp = 0;
+							*(COLOR_T*)dst = rgbPremixed;
+						}
+						else if( weightS > 0 )
+						{
+							weightD = FULL_WEIGHT - weightS;
+
+							work = *(COLOR_T*)drp;
+							workB = ((((work                    ) & CHANNEL_MASK) * weightD)) >> BITS_PER_1CHANNEL;
+							workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weightD));
+							workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK) * weightD)) << BITS_PER_1CHANNEL;
+							*(COLOR_T*)drp = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+
+							work = *(COLOR_T*)dst;
+							workB = ((((work                    ) & CHANNEL_MASK) * weightD) + (b * weightS)) >> BITS_PER_1CHANNEL;
+							workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weightD) + (g * weightS));
+							workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK) * weightD) + (r * weightS)) << BITS_PER_1CHANNEL;
+							*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+						}
+						
+						src++;
+						dst+=BYTES_PER_PIXEL;
+						drp+=BYTES_PER_PIXEL;
+					}
+					
+					src = tSrc + srcRowBytes;
+					dst = tDst + dstRowBytes;
+					drp = tDrp + dstRowBytes;
+				}
+				break;
+			}
+	}	
+	dPoint->h += width;
+}
+
+void SurfaceBlitColorOver( SDL_Surface* source,     SDL_Surface* dest,
+                           const MRect* sourceRect, const MRect* destRect,
+                           int r, int g, int b, int weight )
+{
+    int            startX = 0, startY = 0, endX, endY, x, y, dstRowBytes, srcRowBytes;
+	unsigned char* src;
+	unsigned char* dst;
+	MRect          destBounds;
+	
+	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
+	
+	endX = destRect->right - destRect->left;
+	endY = destRect->bottom - destRect->top;
+	
+	if( destRect->left   > destBounds.right  ||			// completely clipped?
+		destRect->right  < destBounds.left   ||
+		destRect->top    > destBounds.bottom ||
+		destRect->bottom < destBounds.top )
+	{
+		return; 												// do nothing
+	}
+	
+	src         = (unsigned char*) source->pixels;
+	dst         = (unsigned char*) dest->pixels;
+	srcRowBytes = source->pitch;
+	dstRowBytes = dest->pitch;
+	
+	src += (sourceRect->top * srcRowBytes) + (sourceRect->left * BYTES_PER_PIXEL);
+	dst += (destRect->top * dstRowBytes) + (destRect->left * BYTES_PER_PIXEL);
+	
+	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
+	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
+	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
+	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
+	
+	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+
+	r *= weight;
+	g *= weight;
+	b *= weight;
+	weight = FULL_WEIGHT - weight;
+	
+	for( y=startY; y<endY; y++ )
+	{
+		unsigned char *tSrc = src, *tDst = dst;
+		int work, workB, workG, workR;
+		
+		for( x=startX; x<endX; x++ )
+		{
+			work = *(COLOR_T*)src;
+			workB = ((((work                    ) & CHANNEL_MASK)*weight) + b) >> BITS_PER_1CHANNEL;
+			workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK)*weight) + g);
+			workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK)*weight) + r) << BITS_PER_1CHANNEL;
+
+			*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+			
+			src += BYTES_PER_PIXEL;
+			dst += BYTES_PER_PIXEL;
+		}
+		
+		src = tSrc + srcRowBytes;
+		dst = tDst + dstRowBytes;
+	}
+}
+
+// NOTE: due to rounding error, there should never be a case of transitioning all the way from 0 to 255.
+// You'll overflow and get weird glitches on the edges.
+
+void SurfaceBlitBlendOver(SDL_Surface* source,     SDL_Surface* dest,
+                           const MRect* sourceRect, const MRect* destRect, 
+                           int r1, int g1, int b1, 
+                           int r2, int g2, int b2, 
+                           int r3, int g3, int b3, 
+                           int r4, int g4, int b4, 
+                           int weight )
+{
+	int startX = 0, startY = 0, endX, endY, x, y;
+	int rA, gA, bA;
+	int rB, gB, bB;
+	int rI, gI, bI;
+	int vrLI, vgLI, vbLI;
+	int vrRI, vgRI, vbRI;
+	int weight12, ditherDiff;
+	int width, height, dstRowBytes, srcRowBytes;
+	unsigned char *src, *dst;
+	MRect destBounds;
+	MBoolean oddX = false;
+
+	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
+	
+	if( destRect->left   > destBounds.right  ||			// completely clipped?
+		destRect->right  < destBounds.left   ||
+		destRect->top    > destBounds.bottom ||
+		destRect->bottom < destBounds.top )
+	{
+		return; 												// do nothing
+	}
+
+	endX = destRect->right - destRect->left;
+	endY = destRect->bottom - destRect->top;
+	
+	src         = (unsigned char*) source->pixels; 
+	dst         = (unsigned char*) dest->pixels;
+	srcRowBytes = source->pitch;
+	dstRowBytes = dest->pitch;
+	
+	src += (sourceRect->top * srcRowBytes) + (sourceRect->left * BYTES_PER_PIXEL);
+	dst += (destRect->top * dstRowBytes)   + (destRect->left * BYTES_PER_PIXEL);
+	
+	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
+	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
+	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
+	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
+	
+	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
+
+	height = endY - startY;
+	width  = endX - startX;
+	
+	weight12 = weight << 12;
+	
+	r1 *= weight12;		g1 *= weight12;		b1 *= weight12;
+	r2 *= weight12;		g2 *= weight12;		b2 *= weight12;
+	r3 *= weight12;		g3 *= weight12;		b3 *= weight12;
+	r4 *= weight12;		g4 *= weight12;		b4 *= weight12;
+	ditherDiff = weight12 >> 1;
+	
+	vrLI = (r3 - r1) / height;
+	vgLI = (g3 - g1) / height;
+	vbLI = (b3 - b1) / height;
+
+	vrRI = (r4 - r2) / height;
+	vgRI = (g4 - g2) / height;
+	vbRI = (b4 - b2) / height;
+
+	weight = FULL_WEIGHT - weight;
+    weight12 = weight << 12;
+
+    if( (endX - startX) & 1 )
+	{
+		endX--;
+		oddX = true;
+	}
+	
+	for( y=startY; y<endY; y++ )
+	{
+		unsigned char *tSrc = src, *tDst = dst;
+		int work, workB, workG, workR;
+		
+		if( y & 1 )
+		{
+			rA =  r1;
+			gA =  g1;
+			bA =  b1;
+			rB =  r1 - ditherDiff; if( rB < 0 ) rB = 0;
+			gB =  g1 - ditherDiff; if( gB < 0 ) gB = 0;
+			bB =  b1 - ditherDiff; if( bB < 0 ) bB = 0;
+		}
+		else
+		{
+			rA =  r1 - ditherDiff; if( rA < 0 ) rA = 0;
+			gA =  g1 - ditherDiff; if( gA < 0 ) gA = 0;
+			bA =  b1 - ditherDiff; if( bA < 0 ) bA = 0;
+			rB =  r1;
+			gB =  g1;
+			bB =  b1;
+		}
+		
+		rI = (2 * (r2 - r1)) / width;
+		gI = (2 * (g2 - g1)) / width;
+		bI = (2 * (b2 - b1)) / width;
+					
+		for( x=startX; x<endX; x+=2 )
+		{
+			work = *(COLOR_T*)src;
+
+            workB = ((((work                      ) & CHANNEL_MASK) * weight12) + bA) >> (12 + BITS_PER_1CHANNEL);
+            workG = ((((work >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weight12) + gA) >> (12                    );
+            workR = ((((work >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * weight12) + rA) >> (12 - BITS_PER_1CHANNEL);
+
+            *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+			
+			src+= BYTES_PER_PIXEL;
+			dst+= BYTES_PER_PIXEL;
+			rA += rI; 
+			gA += gI;
+			bA += bI;
+
+			work = *(COLOR_T*)src;
+
+            workB = ((((work                      ) & CHANNEL_MASK) * weight12) + bB) >> (12 + BITS_PER_1CHANNEL);
+            workG = ((((work >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weight12) + gB) >> (12                    );
+            workR = ((((work >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * weight12) + rB) >> (12 - BITS_PER_1CHANNEL);
+
+            *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+			
+            src+= BYTES_PER_PIXEL;
+            dst+= BYTES_PER_PIXEL;
+			rB += rI;
+			gB += gI;
+			bB += bI;
+		}
+		
+		if( oddX )
+		{
+			work = *(COLOR_T*)src;
+            
+            workB = ((((work                      ) & CHANNEL_MASK) * weight12) + bA) >> (12 + BITS_PER_1CHANNEL);
+            workG = ((((work >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weight12) + gA) >> (12                    );
+            workR = ((((work >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * weight12) + rA) >> (12 - BITS_PER_1CHANNEL);
+
+            *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
+		}
+		
+		src = tSrc + srcRowBytes;
+		dst = tDst + dstRowBytes;
+
+		r1 += vrLI; r2 += vrRI;
+		g1 += vgLI; g2 += vgRI;
+		b1 += vbLI; b2 += vbRI;
+	}
+}
+
--- a/src/blitter.cpp
+++ /dev/null
@@ -1,870 +1,0 @@
-// blitter.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "gworld.h"
-#include "blitter.h"
-#include "font.h"
-#include "level.h"
-#include "graphics.h"
-
-MBoolean update[2][kGridAcross][kGridDown];
-MBoolean refresh[2];
-
-void InitBlitter( void )
-{
-	int player, x, y;
-
-	for( player=0; player<=1; player++ )
-	{
-		refresh[player] = false;
-		
-		for( x=0; x<kGridAcross; x++ )
-		{
-			for( y=0; y<kGridDown; y++ )
-			{
-				update[player][x][y] = false;
-			}
-		}
-	}
-}
-
-void UpdatePlayerWindow( int player )
-{
-	SDL_Rect fullSDLRect, offsetSDLRect;
-	int      x, y;
-	
-	if( control[player] == kNobodyControl ) return;
-	
-	if( playerWindowVisible[player] && refresh[player] )
-	{
-		MRect updateRect = {0, 0, 0, 0}, fullRect, offsetRect;
-		MBoolean first = true;
-		
-		for( x=0; x<kGridAcross; x++ )
-		{
-			for( y=1; y<kGridDown; y++ )
-			{
-				if( update[player][x][y] )
-				{
-					updateRect.top  = y * kBlobVertSize;
-					updateRect.left = x * kBlobHorizSize;
-					updateRect.bottom = updateRect.top + kBlobVertSize;
-					updateRect.right = updateRect.left + kBlobHorizSize;
-					if( first )
-					{
-						fullRect = updateRect;
-						first = false;
-					}
-					else
-					{
-						UnionMRect( &fullRect, &updateRect, &fullRect );
-					}
-
-					update[player][x][y] = false;
-				}
-			}
-		}
-
-		if( !first )
-		{
-			offsetRect = fullRect;
-			OffsetMRect( &offsetRect, playerWindowRect[player].left, playerWindowRect[player].top - kBlobVertSize );
-			
-			SDLU_BlitFrontSurface( playerSpriteSurface[player], 
-								   SDLU_MRectToSDLRect( &fullRect, &fullSDLRect ),
-								   SDLU_MRectToSDLRect( &offsetRect, &offsetSDLRect ) );
-		}
-	}
-}
-
-void SetUpdateRect( int player, MRect *where )
-{
-	int x,y;
-	int xMin, xMax, yMin, yMax;
-	
-	xMin = where->left / kBlobHorizSize;
-	xMax = ( where->right + kBlobHorizSize - 1 ) / kBlobHorizSize;
-	
-	if( xMin < 0 ) xMin = 0;
-	if( xMin > (kGridAcross-1) ) xMin = kGridAcross-1;
-	if( xMax < 0 ) xMax = 0;
-	if( xMax > kGridAcross ) xMax = kGridAcross;
-	
-	yMin = where->top / kBlobVertSize;
-	yMax = ( where->bottom + kBlobVertSize - 1 ) / kBlobVertSize;
-
-	if( yMin < 0 ) yMin = 0;
-	if( yMin > (kGridDown-1) ) yMin = kGridDown-1;
-	if( yMax < 0 ) yMax = 0;
-	if( yMax > kGridDown ) yMax = kGridDown;
-	
-	for( x=xMin; x<xMax; x++ )
-	{
-		for( y=yMin; y<yMax; y++ )
-		{
-			update[player][x][y] = true;
-		}
-	}
-	
-	refresh[player] = true;
-}
-
-
-void SurfaceBlitMask( SDL_Surface* object,     SDL_Surface* mask,     SDL_Surface* dest,
-                      const MRect*  objectRect, const MRect*  maskRect, const MRect*  destRect )
-{
-	int            startX = 0, startY = 0, endX, endY, x, y, srcRowBytes, mskRowBytes, dstRowBytes;
-	unsigned char *src, *msk, *dst;
-	MRect          destBounds;
-
-	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
-	
-	endX = objectRect->right - objectRect->left;
-	endY = objectRect->bottom - objectRect->top;
-
-	if( destRect->left   > destBounds.right  ||			// completely clipped?
-		destRect->right  < destBounds.left   ||
-		destRect->top    > destBounds.bottom ||
-		destRect->bottom < destBounds.top )
-	{
-		return; 												// do nothing
-	}
-	
-	src         = (unsigned char*) object->pixels;
-	msk         = (unsigned char*) mask->pixels;
-	dst         = (unsigned char*) dest->pixels;
-	srcRowBytes = object->pitch;
-	mskRowBytes = mask->pitch;
-	dstRowBytes = dest->pitch;
-	
-	src += (objectRect->top * srcRowBytes) + (objectRect->left * BYTES_PER_PIXEL);
-	msk += (maskRect->top   * mskRowBytes) + (maskRect->left   * BYTES_PER_MASK_PIXEL);
-	dst += (destRect->top   * dstRowBytes) + (destRect->left   * BYTES_PER_PIXEL);
-	
-	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
-	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
-	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
-	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
-	
-	msk += (mskRowBytes * startY) + (startX * BYTES_PER_MASK_PIXEL);
-	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-
-	for( y=startY; y<endY; y++ )
-	{
-		unsigned char *tSrc = src, *tDst = dst, *tMsk = msk;
-		
-		for( x=startX; x<endX; x++ )
-		{
-			if( *msk )
-			{
-				*(COLOR_T*)dst = *(COLOR_T*)src;
-			}
-			
-			src += BYTES_PER_PIXEL;
-			dst += BYTES_PER_PIXEL;
-			msk += BYTES_PER_MASK_PIXEL;
-		}
-		
-		src = tSrc + srcRowBytes;
-		dst = tDst + dstRowBytes;
-		msk = tMsk + mskRowBytes;
-	}
-}
-
-
-void SurfaceBlitBlob( const MRect* blobRect, const MRect* destRect )
-{
-	SurfaceBlitMask( blobSurface, maskSurface, SDLU_GetCurrentSurface(),
-	                 blobRect,    blobRect,    destRect                  );
-}
-
-
-void SurfaceBlitColor( SDL_Surface* mask,     SDL_Surface* dest,
-                       const MRect* maskRect, const MRect* destRect, 
-                       int r, int g, int b, int weight )
-{
-	int            startX = 0, startY = 0, endX, endY, x, y, mskRowBytes, dstRowBytes;
-	unsigned char *msk, *dst;
-	MRect          destBounds;
-	
-	endX = maskRect->right - maskRect->left;
-	endY = maskRect->bottom - maskRect->top;
-	
-	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
-
-	if( destRect->left   > destBounds.right  ||			// completely clipped?
-		destRect->right  < destBounds.left   ||
-		destRect->top    > destBounds.bottom ||
-		destRect->bottom < destBounds.top )
-	{
-		return; 												// do nothing
-	}
-	
-	msk         = (unsigned char*) mask->pixels;
-	dst         = (unsigned char*) dest->pixels;
-	mskRowBytes = mask->pitch;
-	dstRowBytes = dest->pitch;
-	
-	msk += (maskRect->top * mskRowBytes) + (maskRect->left * BYTES_PER_MASK_PIXEL);
-	dst += (destRect->top * dstRowBytes) + (destRect->left * BYTES_PER_PIXEL);
-	
-	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
-	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
-	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
-	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
-	
-	msk += (mskRowBytes * startY) + (startX * BYTES_PER_MASK_PIXEL);
-	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-
-	r *= weight;
-	g *= weight;
-	b *= weight;
-	weight = FULL_WEIGHT - weight;
-	
-	for( y=startY; y<endY; y++ )
-	{
-		unsigned char *tMsk = msk, *tDst = dst;
-		int work, workB, workG, workR;
-		
-		for( x=startX; x<endX; x++ )
-		{
-			if( *msk )
-			{
-				work = *(COLOR_T*)dst;
-                workB = ((((work                    ) & CHANNEL_MASK)*weight) + b) >> BITS_PER_1CHANNEL;
-                workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK)*weight) + g)                     ;
-                workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK)*weight) + r) << BITS_PER_1CHANNEL;
-
-                *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-			}
-			
-			dst += BYTES_PER_PIXEL;
-			msk += BYTES_PER_MASK_PIXEL;
-		}
-		
-		msk = tMsk + mskRowBytes;
-		dst = tDst + dstRowBytes;
-	}
-}
-
-
-void SurfaceBlitAlpha( SDL_Surface* back,     SDL_Surface* source,     SDL_Surface* alpha,     SDL_Surface* dest,
-                       const MRect* backRect, const MRect* sourceRect, const MRect* alphaRect, const MRect* destRect )
-{
-	int startX = 0, startY = 0, endX, endY, x, y, srcRowBytes, alfRowBytes, dstRowBytes, bckRowBytes;
-	unsigned char *bck, *src, *alf, *dst;
-	MRect destBounds;
-	
-	endX = sourceRect->right - sourceRect->left;
-	endY = sourceRect->bottom - sourceRect->top;
-	
-	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
-
-	if( destRect->left   > destBounds.right  ||			// completely clipped?
-		destRect->right  < destBounds.left   ||
-		destRect->top    > destBounds.bottom ||
-		destRect->bottom < destBounds.top )
-	{
-		return; 												// do nothing
-	}
-	
-	bck         = (unsigned char*) back->pixels;
-	src         = (unsigned char*) source->pixels; 
-	alf         = (unsigned char*) alpha->pixels;
-	dst         = (unsigned char*) dest->pixels; 
-	bckRowBytes = back->pitch; 
-	srcRowBytes = source->pitch;
-	alfRowBytes = alpha->pitch;
-	dstRowBytes = dest->pitch;
-	
-	bck += (backRect->top   * bckRowBytes) + (backRect->left   * BYTES_PER_PIXEL);
-	src += (sourceRect->top * srcRowBytes) + (sourceRect->left * BYTES_PER_PIXEL);
-	alf += (alphaRect->top  * alfRowBytes) + (alphaRect->left  * BYTES_PER_PIXEL);
-	dst += (destRect->top   * dstRowBytes) + (destRect->left   * BYTES_PER_PIXEL);
-	
-	if( destRect->left   < destBounds.left   ) startX -= destRect->left   - destBounds.left;
-	if( destRect->right  > destBounds.right  ) endX   -= destRect->right  - destBounds.right;
-	if( destRect->top    < destBounds.top    ) startY -= destRect->top    - destBounds.top;
-	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
-	
-	bck += (bckRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	alf += (alfRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-
-	for( y=startY; y<endY; y++ )
-	{
-		unsigned char *tSrc = src, *tAlf = alf, *tDst = dst, *tBck = bck;
-		int pixS, pixB, weightS, weightB, workB, workG, workR;
-		
-		for( x=startX; x<endX; x++ )
-		{
-			weightB = *(COLOR_T*)alf;
-			
-			if( (weightB & (RED_MASK | GREEN_MASK | BLUE_MASK)) == (RED_MASK | GREEN_MASK | BLUE_MASK))
-			{
-				*(COLOR_T*)dst = *(COLOR_T*)bck;
-			}
-			else if( (weightB & (RED_MASK | GREEN_MASK | BLUE_MASK)) == 0)
-			{
-				*(COLOR_T*)dst = *(COLOR_T*)src;
-			}
-			else
-			{
-				weightS = ~weightB;
-
-				pixS = *(COLOR_T*)src;
-				pixB = *(COLOR_T*)bck;
-								
-				workB = ((((pixS                      ) & CHANNEL_MASK) * ((weightS                      ) & CHANNEL_MASK)) + (((pixB                      ) & CHANNEL_MASK) * ((weightB                      ) & CHANNEL_MASK))) >> BITS_PER_1CHANNEL;
-				workG = ((((pixS >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * ((weightS >> BITS_PER_1CHANNEL ) & CHANNEL_MASK)) + (((pixB >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * ((weightB >> BITS_PER_1CHANNEL ) & CHANNEL_MASK)));
-				workR = ((((pixS >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * ((weightS >> BITS_PER_2CHANNELS) & CHANNEL_MASK)) + (((pixB >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * ((weightB >> BITS_PER_2CHANNELS) & CHANNEL_MASK))) << BITS_PER_1CHANNEL;
-
-				*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-			}
-
-			src += BYTES_PER_PIXEL;
-			alf += BYTES_PER_PIXEL;
-			bck += BYTES_PER_PIXEL;
-            dst += BYTES_PER_PIXEL;
-		}
-		
-		bck = tBck + bckRowBytes;
-		src = tSrc + srcRowBytes;
-		alf = tAlf + alfRowBytes;
-		dst = tDst + dstRowBytes;
-	}
-}
-
-
-void SurfaceBlitWeightedDualAlpha(SDL_Surface* back,     SDL_Surface* source,     SDL_Surface* mask,     SDL_Surface* alpha,     SDL_Surface* dest,
-                                   const MRect* backRect, const MRect* sourceRect, const MRect* maskRect, const MRect* alphaRect, const MRect* destRect,
-                                   int inWeight )
-{
-	int startX = 0, startY = 0, endX, endY, x, y,
-	    srcRowBytes, alfRowBytes, mskRowBytes, dstRowBytes, bckRowBytes;
-	unsigned char *bck, *src, *alf, *msk, *dst;
-	MRect destBounds;
-
-	endX = sourceRect->right - sourceRect->left;
-	endY = sourceRect->bottom - sourceRect->top;
-	
-	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
-	
-	if( destRect->left   > destBounds.right  ||			// completely clipped?
-		destRect->right  < destBounds.left   ||
-		destRect->top    > destBounds.bottom ||
-		destRect->bottom < destBounds.top )
-	{
-		return; 												// do nothing
-	}
-	
-	bck = (unsigned char*) back->pixels;
-	src = (unsigned char*) source->pixels;
-	msk = (unsigned char*) mask->pixels;
-	alf = (unsigned char*) alpha->pixels;
-	dst = (unsigned char*) dest->pixels;
-	
-	bckRowBytes = back->pitch;
-	srcRowBytes = source->pitch;
-	mskRowBytes = mask->pitch;
-	alfRowBytes = alpha->pitch;
-	dstRowBytes = dest->pitch;
-	
-	bck += (backRect->top   * bckRowBytes) + (backRect->left   * BYTES_PER_PIXEL);
-	src += (sourceRect->top * srcRowBytes) + (sourceRect->left * BYTES_PER_PIXEL);
-	alf += (alphaRect->top  * alfRowBytes) + (alphaRect->left  * BYTES_PER_PIXEL);
-	dst += (destRect->top   * dstRowBytes) + (destRect->left   * BYTES_PER_PIXEL);
-	msk += (maskRect->top   * mskRowBytes) + (maskRect->left   * BYTES_PER_MASK_PIXEL);
-	
-	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
-	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
-	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
-	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
-	
-	bck += (bckRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	alf += (alfRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	msk += (mskRowBytes * startY) + (startX * BYTES_PER_MASK_PIXEL);
-	
-	for( y=startY; y<endY; y++ )
-	{
-		unsigned char *tSrc = src, *tAlf = alf, *tDst = dst, *tBck = bck, *tMsk = msk;
-		int pixS, pixB, weightS, weightB, work, workB, workG, workR;
-		
-		for( x=startX; x<endX; x++ )
-		{
-			if( *msk )
-			{
-				work = *(COLOR_T*)alf;
-				workB = ((((work                    ) & CHANNEL_MASK)*inWeight) ) >> BITS_PER_1CHANNEL;
-				workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK)*inWeight) );
-				workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK)*inWeight) ) << BITS_PER_1CHANNEL;
-
-				weightB = ~((workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK));
-				
-                if( (weightB & (RED_MASK | GREEN_MASK | BLUE_MASK)) == (RED_MASK | GREEN_MASK | BLUE_MASK))
-				{
-					*(COLOR_T*)dst = *(COLOR_T*)bck;
-				}
-                else if( (weightB & (RED_MASK | GREEN_MASK | BLUE_MASK)) == 0)
-				{
-					*(COLOR_T*)dst = *(COLOR_T*)src;
-				}
-				else
-				{
-					weightS = ~weightB;
-
-					pixS = *(COLOR_T*)src;
-					pixB = *(COLOR_T*)bck;
-									
-					workB = ((((pixS                      ) & CHANNEL_MASK) * ((weightS                      ) & CHANNEL_MASK)) + (((pixB                      ) & CHANNEL_MASK) * ((weightB                      ) & CHANNEL_MASK))) >> BITS_PER_1CHANNEL;
-					workG = ((((pixS >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * ((weightS >> BITS_PER_1CHANNEL ) & CHANNEL_MASK)) + (((pixB >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * ((weightB >> BITS_PER_1CHANNEL ) & CHANNEL_MASK)));
-					workR = ((((pixS >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * ((weightS >> BITS_PER_2CHANNELS) & CHANNEL_MASK)) + (((pixB >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * ((weightB >> BITS_PER_2CHANNELS) & CHANNEL_MASK))) << BITS_PER_1CHANNEL;
-
-					*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-				}
-			}
-
-			src += BYTES_PER_PIXEL;
-			alf += BYTES_PER_PIXEL;
-			bck += BYTES_PER_PIXEL;
-			dst += BYTES_PER_PIXEL;
-			msk += BYTES_PER_MASK_PIXEL;
-		}
-		
-		bck = tBck + bckRowBytes;
-		src = tSrc + srcRowBytes;
-		alf = tAlf + alfRowBytes;
-		dst = tDst + dstRowBytes;
-		msk = tMsk + mskRowBytes;
-	}
-}
-
-void SurfaceBlitWeightedCharacter( SkittlesFontPtr font, unsigned char text, MPoint *dPoint, int r, int g, int b, int alpha )
-{
-	if (alpha == FULL_WEIGHT)
-	{
-		SurfaceBlitCharacter( font, text, dPoint, r, g, b, 0 );
-        return;
-	}
-    
-	if (alpha == 0)
-	{
-		return;
-	}
-
-    SDL_Surface*   destSurface;
-    unsigned char* src;
-    unsigned char* dst;
-    int            srcRowBytes;
-    int            dstRowBytes;
-    int            index;
-    MRect          destBounds;
-    
-    int height = font->surface->h;
-    int width  = font->width[text];
-    int across = font->across[text];
-    
-    destSurface = SDLU_GetCurrentSurface();
-    SDLU_SDLRectToMRect( &destSurface->clip_rect, &destBounds );
-    
-    if( (dPoint->h + width)  > destBounds.right           ||      // clipped?
-        (dPoint->v + height) > destBounds.bottom          || 
-         dPoint->h           < destBounds.left            ||
-         dPoint->v           < destBounds.top                )
-    {
-        dPoint->h += width;
-        return;                                               // do nothing
-    }
-    
-    srcRowBytes = font->surface->pitch;
-    dstRowBytes = destSurface->pitch;
-    
-    src = (unsigned char*) font->surface->pixels + across;
-    dst = (unsigned char*) destSurface->pixels + (dPoint->h * BYTES_PER_PIXEL) + (dPoint->v * dstRowBytes);
-    
-    while( height-- )
-    {
-        unsigned char *tSrc = src, *tDst = dst;
-        
-        for( index=0; index<width; index++ )
-        {
-            int workR, workG, workB, work, weightS, weightD;
-            weightS = *src & CHANNEL_MASK;
-            
-            weightS = (weightS * alpha) >> BITS_PER_1CHANNEL;
-            
-            if( weightS )
-            {
-                weightD = FULL_WEIGHT - weightS;
-
-                work = *(COLOR_T*)dst;
-                workB = ((((work                    ) & CHANNEL_MASK) * weightD) + (b * weightS)) >> BITS_PER_1CHANNEL;
-                workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weightD) + (g * weightS));
-                workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK) * weightD) + (r * weightS)) << BITS_PER_1CHANNEL;
-
-                *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-            }
-            
-            src++;
-            dst+=BYTES_PER_PIXEL;
-        }
-        
-        src = tSrc + srcRowBytes;
-        dst = tDst + dstRowBytes;
-    }
-        
-    dPoint->h += width;
-}
-
-void SurfaceBlitCharacter( SkittlesFontPtr font, unsigned char text, MPoint *dPoint, int r, int g, int b, int dropShadow )
-{
-    SDL_Surface*    destSurface;
-	unsigned char*  src;
-	unsigned char*  dst;
-	int             srcRowBytes;
-	int             dstRowBytes;
-	int             index;
-	int             rgbPremixed;
-	MRect           destBounds;
-	
-	int height = font->surface->h;
-	int width  = font->width[text];
-	int across = font->across[text];
-	
-	destSurface = SDLU_GetCurrentSurface();
-	SDLU_SDLRectToMRect( &destSurface->clip_rect, &destBounds );	
-	
-	if( (dPoint->h + width)  > destBounds.right           ||      // clipped?
-	    (dPoint->v + height) > destBounds.bottom          || 
-	     dPoint->h           < destBounds.left            ||
-	     dPoint->v           < destBounds.top                )
-	{
-		dPoint->h += width;
-		return;                                               // do nothing
-	}
-	
-	srcRowBytes = font->surface->pitch;
-	dstRowBytes = destSurface->pitch;
-	
-	src = (unsigned char*) font->surface->pixels + across;
-	dst = (unsigned char*) destSurface->pixels + (dPoint->h * BYTES_PER_PIXEL) + (dPoint->v * dstRowBytes);
-	rgbPremixed = (r << BITS_PER_2CHANNELS) | (g << BITS_PER_1CHANNEL) | b;
-	
-	switch( dropShadow )
-	{
-		case 0:
-			while( height-- )
-			{
-				unsigned char *tSrc = src, *tDst = dst;
-				
-				for( index=0; index<width; index++ )
-				{
-					int workR, workG, workB, work, weightS, weightD;
-					weightS = *src & CHANNEL_MASK;
-					
-					if( weightS == CHANNEL_MASK )
-					{
-						*(COLOR_T*)dst = rgbPremixed;
-					}
-					else if( weightS > 0 )
-					{
-						weightD = FULL_WEIGHT - weightS;
-
-						work = *(COLOR_T*)dst;
-						workB = ((((work                      ) & CHANNEL_MASK) * weightD) + (b * weightS)) >> BITS_PER_1CHANNEL;
-						workG = ((((work >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weightD) + (g * weightS));
-						workR = ((((work >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * weightD) + (r * weightS)) << BITS_PER_1CHANNEL;
-
-						*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-					}
-					
-					src++;
-					dst+=BYTES_PER_PIXEL;
-				}
-				
-				src = tSrc + srcRowBytes;
-				dst = tDst + dstRowBytes;
-			}
-			break;
-		
-		default:
-			{
-				unsigned char *drp = dst + ((dstRowBytes + BYTES_PER_PIXEL) * dropShadow);
-				
-				while( height-- )
-				{
-					unsigned char *tSrc = src, *tDst = dst, *tDrp = drp;
-					
-					for( index=0; index<width; index++ )
-					{
-						int workR, workG, workB, work, weightS, weightD;
-						weightS = *src & CHANNEL_MASK;
-						
-						if( weightS == CHANNEL_MASK )
-						{
-							*(COLOR_T*)drp = 0;
-							*(COLOR_T*)dst = rgbPremixed;
-						}
-						else if( weightS > 0 )
-						{
-							weightD = FULL_WEIGHT - weightS;
-
-							work = *(COLOR_T*)drp;
-							workB = ((((work                    ) & CHANNEL_MASK) * weightD)) >> BITS_PER_1CHANNEL;
-							workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weightD));
-							workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK) * weightD)) << BITS_PER_1CHANNEL;
-							*(COLOR_T*)drp = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-
-							work = *(COLOR_T*)dst;
-							workB = ((((work                    ) & CHANNEL_MASK) * weightD) + (b * weightS)) >> BITS_PER_1CHANNEL;
-							workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weightD) + (g * weightS));
-							workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK) * weightD) + (r * weightS)) << BITS_PER_1CHANNEL;
-							*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-						}
-						
-						src++;
-						dst+=BYTES_PER_PIXEL;
-						drp+=BYTES_PER_PIXEL;
-					}
-					
-					src = tSrc + srcRowBytes;
-					dst = tDst + dstRowBytes;
-					drp = tDrp + dstRowBytes;
-				}
-				break;
-			}
-	}	
-	dPoint->h += width;
-}
-
-void SurfaceBlitColorOver( SDL_Surface* source,     SDL_Surface* dest,
-                           const MRect* sourceRect, const MRect* destRect,
-                           int r, int g, int b, int weight )
-{
-    int            startX = 0, startY = 0, endX, endY, x, y, dstRowBytes, srcRowBytes;
-	unsigned char* src;
-	unsigned char* dst;
-	MRect          destBounds;
-	
-	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
-	
-	endX = destRect->right - destRect->left;
-	endY = destRect->bottom - destRect->top;
-	
-	if( destRect->left   > destBounds.right  ||			// completely clipped?
-		destRect->right  < destBounds.left   ||
-		destRect->top    > destBounds.bottom ||
-		destRect->bottom < destBounds.top )
-	{
-		return; 												// do nothing
-	}
-	
-	src         = (unsigned char*) source->pixels;
-	dst         = (unsigned char*) dest->pixels;
-	srcRowBytes = source->pitch;
-	dstRowBytes = dest->pitch;
-	
-	src += (sourceRect->top * srcRowBytes) + (sourceRect->left * BYTES_PER_PIXEL);
-	dst += (destRect->top * dstRowBytes) + (destRect->left * BYTES_PER_PIXEL);
-	
-	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
-	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
-	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
-	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
-	
-	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-
-	r *= weight;
-	g *= weight;
-	b *= weight;
-	weight = FULL_WEIGHT - weight;
-	
-	for( y=startY; y<endY; y++ )
-	{
-		unsigned char *tSrc = src, *tDst = dst;
-		int work, workB, workG, workR;
-		
-		for( x=startX; x<endX; x++ )
-		{
-			work = *(COLOR_T*)src;
-			workB = ((((work                    ) & CHANNEL_MASK)*weight) + b) >> BITS_PER_1CHANNEL;
-			workG = ((((work>>BITS_PER_1CHANNEL ) & CHANNEL_MASK)*weight) + g);
-			workR = ((((work>>BITS_PER_2CHANNELS) & CHANNEL_MASK)*weight) + r) << BITS_PER_1CHANNEL;
-
-			*(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-			
-			src += BYTES_PER_PIXEL;
-			dst += BYTES_PER_PIXEL;
-		}
-		
-		src = tSrc + srcRowBytes;
-		dst = tDst + dstRowBytes;
-	}
-}
-
-// NOTE: due to rounding error, there should never be a case of transitioning all the way from 0 to 255.
-// You'll overflow and get weird glitches on the edges.
-
-void SurfaceBlitBlendOver(SDL_Surface* source,     SDL_Surface* dest,
-                           const MRect* sourceRect, const MRect* destRect, 
-                           int r1, int g1, int b1, 
-                           int r2, int g2, int b2, 
-                           int r3, int g3, int b3, 
-                           int r4, int g4, int b4, 
-                           int weight )
-{
-	int startX = 0, startY = 0, endX, endY, x, y;
-	int rA, gA, bA;
-	int rB, gB, bB;
-	int rI, gI, bI;
-	int vrLI, vgLI, vbLI;
-	int vrRI, vgRI, vbRI;
-	int weight12, ditherDiff;
-	int width, height, dstRowBytes, srcRowBytes;
-	unsigned char *src, *dst;
-	MRect destBounds;
-	MBoolean oddX = false;
-
-	SDLU_SDLRectToMRect( &dest->clip_rect, &destBounds );
-	
-	if( destRect->left   > destBounds.right  ||			// completely clipped?
-		destRect->right  < destBounds.left   ||
-		destRect->top    > destBounds.bottom ||
-		destRect->bottom < destBounds.top )
-	{
-		return; 												// do nothing
-	}
-
-	endX = destRect->right - destRect->left;
-	endY = destRect->bottom - destRect->top;
-	
-	src         = (unsigned char*) source->pixels; 
-	dst         = (unsigned char*) dest->pixels;
-	srcRowBytes = source->pitch;
-	dstRowBytes = dest->pitch;
-	
-	src += (sourceRect->top * srcRowBytes) + (sourceRect->left * BYTES_PER_PIXEL);
-	dst += (destRect->top * dstRowBytes)   + (destRect->left * BYTES_PER_PIXEL);
-	
-	if( destRect->left   < destBounds.left   ) startX -= destRect->left - destBounds.left;
-	if( destRect->right  > destBounds.right  ) endX   -= destRect->right - destBounds.right;
-	if( destRect->top    < destBounds.top    ) startY -= destRect->top - destBounds.top;
-	if( destRect->bottom > destBounds.bottom ) endY   -= destRect->bottom - destBounds.bottom;
-	
-	src += (srcRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-	dst += (dstRowBytes * startY) + (startX * BYTES_PER_PIXEL);
-
-	height = endY - startY;
-	width  = endX - startX;
-	
-	weight12 = weight << 12;
-	
-	r1 *= weight12;		g1 *= weight12;		b1 *= weight12;
-	r2 *= weight12;		g2 *= weight12;		b2 *= weight12;
-	r3 *= weight12;		g3 *= weight12;		b3 *= weight12;
-	r4 *= weight12;		g4 *= weight12;		b4 *= weight12;
-	ditherDiff = weight12 >> 1;
-	
-	vrLI = (r3 - r1) / height;
-	vgLI = (g3 - g1) / height;
-	vbLI = (b3 - b1) / height;
-
-	vrRI = (r4 - r2) / height;
-	vgRI = (g4 - g2) / height;
-	vbRI = (b4 - b2) / height;
-
-	weight = FULL_WEIGHT - weight;
-    weight12 = weight << 12;
-
-    if( (endX - startX) & 1 )
-	{
-		endX--;
-		oddX = true;
-	}
-	
-	for( y=startY; y<endY; y++ )
-	{
-		unsigned char *tSrc = src, *tDst = dst;
-		int work, workB, workG, workR;
-		
-		if( y & 1 )
-		{
-			rA =  r1;
-			gA =  g1;
-			bA =  b1;
-			rB =  r1 - ditherDiff; if( rB < 0 ) rB = 0;
-			gB =  g1 - ditherDiff; if( gB < 0 ) gB = 0;
-			bB =  b1 - ditherDiff; if( bB < 0 ) bB = 0;
-		}
-		else
-		{
-			rA =  r1 - ditherDiff; if( rA < 0 ) rA = 0;
-			gA =  g1 - ditherDiff; if( gA < 0 ) gA = 0;
-			bA =  b1 - ditherDiff; if( bA < 0 ) bA = 0;
-			rB =  r1;
-			gB =  g1;
-			bB =  b1;
-		}
-		
-		rI = (2 * (r2 - r1)) / width;
-		gI = (2 * (g2 - g1)) / width;
-		bI = (2 * (b2 - b1)) / width;
-					
-		for( x=startX; x<endX; x+=2 )
-		{
-			work = *(COLOR_T*)src;
-
-            workB = ((((work                      ) & CHANNEL_MASK) * weight12) + bA) >> (12 + BITS_PER_1CHANNEL);
-            workG = ((((work >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weight12) + gA) >> (12                    );
-            workR = ((((work >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * weight12) + rA) >> (12 - BITS_PER_1CHANNEL);
-
-            *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-			
-			src+= BYTES_PER_PIXEL;
-			dst+= BYTES_PER_PIXEL;
-			rA += rI; 
-			gA += gI;
-			bA += bI;
-
-			work = *(COLOR_T*)src;
-
-            workB = ((((work                      ) & CHANNEL_MASK) * weight12) + bB) >> (12 + BITS_PER_1CHANNEL);
-            workG = ((((work >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weight12) + gB) >> (12                    );
-            workR = ((((work >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * weight12) + rB) >> (12 - BITS_PER_1CHANNEL);
-
-            *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-			
-            src+= BYTES_PER_PIXEL;
-            dst+= BYTES_PER_PIXEL;
-			rB += rI;
-			gB += gI;
-			bB += bI;
-		}
-		
-		if( oddX )
-		{
-			work = *(COLOR_T*)src;
-            
-            workB = ((((work                      ) & CHANNEL_MASK) * weight12) + bA) >> (12 + BITS_PER_1CHANNEL);
-            workG = ((((work >> BITS_PER_1CHANNEL ) & CHANNEL_MASK) * weight12) + gA) >> (12                    );
-            workR = ((((work >> BITS_PER_2CHANNELS) & CHANNEL_MASK) * weight12) + rA) >> (12 - BITS_PER_1CHANNEL);
-
-            *(COLOR_T*)dst = (workR & RED_MASK) | (workG & GREEN_MASK) | (workB & BLUE_MASK);
-		}
-		
-		src = tSrc + srcRowBytes;
-		dst = tDst + dstRowBytes;
-
-		r1 += vrLI; r2 += vrRI;
-		g1 += vgLI; g2 += vgRI;
-		b1 += vbLI; b2 += vbRI;
-	}
-}
-
--- /dev/null
+++ b/src/control.c
@@ -1,0 +1,629 @@
+// control.c
+
+#include "main.h"
+#include "control.h"
+#include "moving.h"
+#include "players.h"
+#include "random.h"
+#include "grays.h"
+#include "zap.h"
+#include "gameticks.h"
+#include "level.h"
+#include "tutorial.h"
+#include <stdlib.h>
+
+int destinationX[2], destinationR[2];
+signed char tempGrid[kGridAcross][kGridDown];
+MTicks timeAI[2], timeMove[2];
+MBoolean moveQuick[2];
+AutoPatternPtr autoPattern = NULL;
+
+void AutoControl( int player )
+{
+	if( autoPattern ) 
+	{				
+		switch( autoPattern->command )
+		{
+			case kMessage:
+				StartBalloon( autoPattern->message );
+				autoPattern++;
+				break;
+				
+			case kIdleTicks:
+				if( !tutorialTime )
+				{
+					tutorialTime = GameTickCount() + autoPattern->d1;
+				}
+				else
+				{
+					if( GameTickCount() >= tutorialTime )
+					{
+						tutorialTime = 0;
+						autoPattern++;
+					}
+				}
+				break;
+			
+			case kRetrieve:
+				if( role[player] == kWaitForRetrieval )
+				{
+					nextA[player] = abs( autoPattern->d1 );
+					nextB[player] = abs( autoPattern->d2 );
+					nextM[player] = (autoPattern->d1 < 0);
+					nextG[player] = (autoPattern->d1 == kBombBottom) && (autoPattern->d2 == kBombTop);
+					
+					if( !nextG[player] )
+					{
+						nextA[player] = pieceMap[ nextA[player] ];
+						nextB[player] = pieceMap[ nextB[player] ];
+					}
+										
+					role[player]  = kRetrieveBlobs;
+					autoPattern++;
+				}
+				break;
+			
+			case kPosition:
+				if( (role[player] != kFalling) || (blobX[player] == autoPattern->d1) )
+				{
+					autoPattern++;
+				}
+				else if( GameTickCount() >= timeMove[player] )
+				{
+					timeMove[player] = GameTickCount() + 12;
+					
+					if( blobX[player] > autoPattern->d1 )
+					{
+						if( CanGoLeft( player ) )
+							GoLeft( player );
+					}
+					else
+					{
+						if( CanGoRight( player ) )
+							GoRight( player );
+					}
+				}
+				break;
+			
+			case kSpin:
+				if( role[player] != kFalling )
+				{
+					autoPattern++;
+				}
+				else if( CanRotate( player ) )
+				{
+					DoRotate( player );
+					autoPattern++;
+				}
+				break;
+
+			case kBlockUntilLand:
+				if( role[player] != kFalling ) 
+				{
+					autoPattern++;
+				}			
+				break;
+			
+			case kBlockUntilDrop:
+				if( !dropping[player] ) DoDrop( player );
+
+				if( role[player] != kFalling ) 
+				{
+					autoPattern++;
+				}			
+				break;
+
+			case kPunish:
+				if( role[player] == kWaitForRetrieval )
+				{
+					lockGrays[player] = autoPattern->d1;
+					SetupGrays( player );
+					blobTime[player] = GameTickCount( );
+					role[player] = kDropGrays;
+
+					autoPattern++;
+				}
+				break;
+		
+			case kComplete:
+				EndTutorial( );
+				break;
+			
+			case kBlockUntilComplete:
+				if( role[player] == kWaitForRetrieval ) 
+				{
+					autoPattern++;
+				}			
+				break;
+		}
+	}
+}
+
+void PlayerControl( int player )
+{
+	int a = player, b = player;
+	MBoolean moved = false;
+	
+	if( players == 1 )
+	{
+		a = 0;
+		b = 1;
+	}
+
+	if( hitKey[a].left || hitKey[b].left )
+	{
+		if( GameTickCount() >= timeMove[player] )
+		{
+			timeMove[player] += 12;
+			if( CanGoLeft( player ) )
+				GoLeft( player );
+		}
+		
+		moved = true;
+	}
+	
+	if( hitKey[a].right || hitKey[b].right )
+	{	
+		if( GameTickCount() >= timeMove[player] )
+		{
+			timeMove[player] += 12;
+			if( CanGoRight( player ) )
+				GoRight( player );
+		}
+		
+		moved = true;
+	}
+	
+	if( !moved ) timeMove[player] = GameTickCount( );
+	
+	if( hitKey[a].rotate == 1 || hitKey[b].rotate == 1 )
+	{
+		if( CanRotate( player ) )
+			DoRotate( player );
+	}
+	
+	if( hitKey[a].drop == 1 || hitKey[b].drop == 1 )
+	{
+		DoDrop( player );
+	}
+	
+	if( hitKey[a].drop == 0 && hitKey[b].drop == 0 )
+	{
+		StopDrop( player );
+	}
+}
+
+void AIControl( int player )
+{
+	if( timeAI[player] > GameTickCount() )
+		return;
+	
+	timeAI[player] += moveQuick[player]? character[player].speedRush:
+									     character[player].speedNormal;
+	
+	switch( RandomBefore( 2 ) )
+	{
+		case 0:
+			if( destinationR[player] != blobR[player] )
+			{
+				if( CanRotate(player) )
+				{
+					DoRotate( player );
+				}
+			}
+			break;
+		
+		case 1:
+			if( destinationX[player] != blobX[player] ) 
+			{
+				if( destinationX[player] > blobX[player] )
+				{
+					if( CanGoRight( player ) )
+						GoRight( player );
+				}
+				else
+				{
+					if( CanGoLeft( player ) )
+						GoLeft( player );
+				}
+			}
+			break;		
+	}
+	
+	if( destinationX[player] == blobX[player] &&
+		destinationR[player] == blobR[player] &&
+		RandomBefore( 100 ) < character[player].intellect )
+	{
+		DoDrop( player );
+	}
+}
+
+void ChooseAIDestination( int player )
+{
+	int testX, testR, testX2, testR2, value, bestValue = -9999999;
+	int x, y;
+	int bestX[kGridAcross*4], bestR[kGridAcross*4], currentBest = -1;
+	int rowDifference, totalTries, temp;
+	MBoolean shouldTry[kGridAcross][4];
+	
+	timeAI[player] = GameTickCount( ) + 1;
+	moveQuick[player] = true;
+	
+	if( grenade[player] )
+	{
+		bestValue = 0;
+		currentBest = 2;
+		
+		for( testX = 0; testX < kGridAcross; testX++ )
+		{
+			rowDifference = GetRowHeight( player, testX );
+			
+			if( (rowDifference < kGridDown - 1) &&
+					(grid[player][testX][rowDifference+1] >= kFirstBlob) &&
+					(grid[player][testX][rowDifference+1] <= kLastBlob)     )
+			{
+				value = 0;
+
+				for( x=0; x<kGridAcross; x++ )
+				{
+					for( y=0; y<kGridDown; y++ )
+					{
+						if( grid[player][x][y] == grid[player][testX][rowDifference+1] ) value++;
+					}
+				}
+			
+				if( value > bestValue )
+				{
+					bestValue = value;
+					currentBest = testX;
+				}
+			}
+		}
+		
+		destinationR[player] = upRotate;
+		destinationX[player] = currentBest;
+		return;
+	}
+	
+	if( (GameTickCount() - startTime) <= 3600 )
+	{
+		for( testX = 0; testX < kGridAcross; testX++ )
+		{
+			rowDifference =  GetRowHeight( player, testX ) - character[player].autoSetup[testX];
+			
+			if( rowDifference >= 2 )
+			{
+				destinationR[player] = downRotate;
+				destinationX[player] = testX;
+				return;
+			}
+			
+			if( rowDifference == 1 )
+			{
+				destinationX[player] = testX;
+				
+				if( testX > 0 )
+				{
+					if( GetRowHeight( player, testX-1 ) > character[player].autoSetup[testX-1] )
+					{
+						destinationR[player] = leftRotate;
+						return;
+					}
+				}
+				
+				if( testX < (kGridAcross-1) )
+				{
+					if( GetRowHeight( player, testX+1 ) > character[player].autoSetup[testX+1] )
+					{
+						destinationR[player] = rightRotate;
+						return;
+					}
+				}
+				
+				destinationR[player] = upRotate;
+				return;
+			}
+		}
+	}
+	
+	moveQuick[player] = (emotions[player] == kEmotionSad) || (emotions[player] == kEmotionPanic);
+	
+	totalTries = character[player].intellect;
+	for( testX = 0; testX < kGridAcross; testX++ )
+	{
+		for( testR = 0; testR < 4; testR++ )
+		{
+			shouldTry[testX][testR] = --totalTries >= 0;
+		}
+	}
+	
+	for( testX = 0; testX < kGridAcross; testX++ )
+	{
+		for( testR = 0; testR < 4; testR++ )
+		{
+			testX2 = RandomBefore( kGridAcross );
+			testR2 = RandomBefore( 4 );
+		
+			temp = shouldTry[testX][testR];
+			shouldTry[testX][testR] = shouldTry[testX2][testR2];
+			shouldTry[testX2][testR2] = temp;
+		}
+	}
+	
+	shouldTry[0][leftRotate]			  = false;
+	shouldTry[kGridAcross-1][rightRotate] = false;
+		
+	for( testX = 0; testX < kGridAcross; testX++ )
+	{
+		for( testR = 0; testR<=3; testR++ )
+		{
+			if( shouldTry[testX][testR] )
+			{
+				value = TestAIDestination( player, testX, testR );
+				
+				if( value > bestValue )
+				{
+					bestValue = value;
+					currentBest = -1;
+				}
+				
+				if( value == bestValue )
+				{
+					currentBest++;
+					bestX[currentBest] = testX;
+					bestR[currentBest] = testR;
+				}
+			}
+		}
+	}
+	
+	currentBest = RandomBefore( currentBest + 1 );
+	destinationX[player] = bestX[currentBest];
+	destinationR[player] = bestR[currentBest];
+}
+
+int TestAIDestination( int player, int testX, int testR )
+{
+	int x, y, height, chains, rensa = 50, result = 0;
+
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=0; y<kGridDown; y++ )
+		{
+			tempGrid[x][y] = grid[player][x][y];
+		}
+	}
+		
+	height = GetRowHeight(player, testX);
+	switch( testR ) 
+	{
+		case upRotate:
+			tempGrid[testX][height--] = colorA[player];
+			if( height >= 0 ) tempGrid[testX][height]   = colorB[player];
+			break;
+		
+		case downRotate:
+			tempGrid[testX][height--] = colorB[player];
+			if( height >= 0 ) tempGrid[testX][height]   = colorA[player];
+			break;
+		
+		case leftRotate:
+			tempGrid[testX][height]                         = colorA[player];
+			tempGrid[testX-1][GetRowHeight(player,testX-1)] = colorB[player];
+			break;
+		
+		case rightRotate:
+			tempGrid[testX][height]                         = colorA[player];
+			tempGrid[testX+1][GetRowHeight(player,testX+1)] = colorB[player];
+			break;		
+	}
+	
+	chains = TestTemporaryGrid( );
+	
+	result = ScoreTemporaryGrid( );
+		
+	if( (chains < 2) && (character[player].intellect > (24 * 2/3)) )
+	{
+		rensa = 0;
+	}
+	else
+	{
+		while( chains-- ) rensa *= 10;
+	}
+	
+	result += rensa;
+	
+	return result;
+}
+
+int ScoreTemporaryGrid( void )
+{
+	int x, y, change, result = 0;
+	int deductions[kGridAcross][kGridDown] = 
+		{ { 400, 350, 350, 200, 120, 60, 20, 5, 0, 0, 0, 0 },
+		  { 600, 500, 300, 150, 100, 50, 20, 0, 0, 0, 0, 0 },
+		  { 9999, 800, 200, 100,  50, 40, 20, 0, 0, 0, 0, 0 },
+		  { 9999, 800, 200, 100,  50, 40, 20, 0, 0, 0, 0, 0 },
+		  { 9999, 500, 300, 150, 100, 50, 20, 0, 0, 0, 0, 0 },
+		  { 400, 350, 350, 200, 120, 60, 20, 5, 0, 0, 0, 0 } };
+
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=0; y<kGridDown; y++ )
+		{
+			if( tempGrid[x][y] == kEmpty )
+			{
+				result += deductions[x][y];
+			}
+			else if( tempGrid[x][y] == kGray )
+			{
+				result -= deductions[x][y];
+			}
+			else
+			{
+				change = 0;
+
+				if( y < (kGridDown-1)   && (tempGrid[x][y] == tempGrid[x][y+1])   ) change += 50;
+				if( y > 0               && (tempGrid[x][y] == tempGrid[x][y-1])   ) change += 50;
+				if( x < (kGridAcross-1) && (tempGrid[x][y] == tempGrid[x+1][y])   ) change += 40;
+				if( x > 0               && (tempGrid[x][y] == tempGrid[x-1][y])   ) change += 40;
+				if( x > 0               &&  
+				    y > 0               && (tempGrid[x][y] == tempGrid[x-1][y-1]) ) change += 20;
+				if( x < (kGridAcross-1) &&  
+				    y > 0               && (tempGrid[x][y] == tempGrid[x+1][y-1]) ) change += 20;
+				if( x > 0               &&  
+				    y < (kGridDown-1)   && (tempGrid[x][y] == tempGrid[x-1][y+1]) ) change += 10;
+				if( x < (kGridAcross-1) &&  
+				    y < (kGridDown-1)   && (tempGrid[x][y] == tempGrid[x+1][y+1]) ) change += 10;
+				if( y < (kGridDown-2)   && (tempGrid[x][y] == tempGrid[x][y+2])   ) change += 10;
+				if( y > 1               && (tempGrid[x][y] == tempGrid[x][y-2])   ) change += 10;
+				
+				if( (x > 0               && tempGrid[x-1][y] == kEmpty) ||
+				    (x < (kGridAcross-1) && tempGrid[x+1][y] == kEmpty) ||
+				    (y < 4               || tempGrid[x][y-4] == kEmpty)    ) change *= 4;
+				    
+				result += change / 4;
+			}
+		}
+	}
+	
+	return result;
+}
+
+int TestTemporaryGrid( void )
+{
+	MBoolean busy;
+	int x, y, stackSize, chains = 0;
+	
+	do
+	{
+		busy = false;
+		
+		for( x=0; x<kGridAcross; x++ )
+		{
+			for( y=0; y<kGridDown; y++ )
+			{
+				if( tempGrid[x][y] >= kFirstBlob && tempGrid[x][y] <= kLastBlob )
+				{
+					if( SizeUp( tempGrid, x, y, tempGrid[x][y] ) >= kBlobClusterSize )
+					{
+						QuickRemove( tempGrid, x, y, tempGrid[x][y] );
+						busy = true;
+					}
+				}
+			}
+		}
+		
+		if( busy )
+		{
+			chains++;
+			
+			for( x=0; x<kGridAcross; x++ )
+			{
+				stackSize = kGridDown-1;
+				
+				for( y=kGridDown-1; y>=0; y-- )
+				{
+					if( tempGrid[x][y] != kEmpty )
+					{
+						tempGrid[x][stackSize] = tempGrid[x][y];
+						if( y < stackSize ) tempGrid[x][y] = kEmpty;
+						stackSize--;
+					}
+				}
+			}
+		}
+	}
+	while( busy );
+	
+	return chains;
+}
+
+void QuickRemove( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color )
+{
+	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) )
+		return;
+	
+	if( myGrid[x][y] == kGray ) myGrid[x][y] = kEmpty;	
+	if( myGrid[x][y] == color )
+	{
+		myGrid[x][y] = kEmpty;
+		QuickRemove( myGrid, x-1, y,   color );
+		QuickRemove( myGrid, x+1, y,   color );
+		QuickRemove( myGrid, x,   y-1, color );
+		QuickRemove( myGrid, x,   y+1, color );
+	}
+}
+
+int BestColor( int player, int blobX, int blobY )
+{
+	int x, y, color, bestColor = kFirstBlob, bestResult = -9999999, rensa, chains, result;
+//	char C[] = {' ', '*', '@', '.', '=', '+', 'o', 'J'};	
+
+
+	for( color = kFirstBlob; color <= kLastBlob; color++ )
+	{		
+		for( y=0; y<kGridDown; y++ )
+		{
+			for( x=0; x<kGridAcross; x++ )
+			{
+				tempGrid[x][y] = grid[player][x][y];
+			}
+		}
+	
+		tempGrid[blobX][blobY] = color;
+		
+		chains = TestTemporaryGrid( );
+		result = ScoreTemporaryGrid( );
+
+		rensa = 1000;
+		while( chains-- ) rensa *= 10;
+		
+		result += rensa;
+		
+		if( result > bestResult )
+		{
+			bestColor = color;
+			bestResult = result;
+		}
+	}
+		
+	return bestColor;
+}
+
+// Returns the first empty row.
+int GetRowHeight( int player, int row )
+{
+	int height;
+	
+	for( height = (kGridDown-1); height > 0; height-- )
+	{
+		if( grid[player][row][height] == kEmpty ) break;
+	}
+	
+	return height;
+}
+
+int DetermineEmotion( int player )
+{
+	int us = 0, them = 0, aboveWater = 1;
+	int x,y;
+	
+	if( role[player] == kLosing )  return kEmotionPanic;
+	if( role[player] == kWinning ) return kEmotionHappy;
+	
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=0; y<kGridDown; y++ )
+		{
+			if( aboveWater && (y<3) && (grid[player][x][y] != kEmpty) ) aboveWater = 0;
+			if( grid[player][x][y]   != kEmpty ) us++;
+			if( grid[1-player][x][y] != kEmpty ) them++;
+		}
+	}
+	
+	if( us > 48 && !aboveWater ) return kEmotionPanic;
+	else if( abs(us-them) < 12 ) return kEmotionNeutral;
+	else if( us > them ) return kEmotionSad;
+	else return kEmotionHappy;
+}
--- a/src/control.cpp
+++ /dev/null
@@ -1,629 +1,0 @@
-// control.c
-
-#include "main.h"
-#include "control.h"
-#include "moving.h"
-#include "players.h"
-#include "random.h"
-#include "grays.h"
-#include "zap.h"
-#include "gameticks.h"
-#include "level.h"
-#include "tutorial.h"
-#include <stdlib.h>
-
-int destinationX[2], destinationR[2];
-signed char tempGrid[kGridAcross][kGridDown];
-MTicks timeAI[2], timeMove[2];
-MBoolean moveQuick[2];
-AutoPatternPtr autoPattern = NULL;
-
-void AutoControl( int player )
-{
-	if( autoPattern ) 
-	{				
-		switch( autoPattern->command )
-		{
-			case kMessage:
-				StartBalloon( autoPattern->message );
-				autoPattern++;
-				break;
-				
-			case kIdleTicks:
-				if( !tutorialTime )
-				{
-					tutorialTime = GameTickCount() + autoPattern->d1;
-				}
-				else
-				{
-					if( GameTickCount() >= tutorialTime )
-					{
-						tutorialTime = 0;
-						autoPattern++;
-					}
-				}
-				break;
-			
-			case kRetrieve:
-				if( role[player] == kWaitForRetrieval )
-				{
-					nextA[player] = abs( autoPattern->d1 );
-					nextB[player] = abs( autoPattern->d2 );
-					nextM[player] = (autoPattern->d1 < 0);
-					nextG[player] = (autoPattern->d1 == kBombBottom) && (autoPattern->d2 == kBombTop);
-					
-					if( !nextG[player] )
-					{
-						nextA[player] = pieceMap[ nextA[player] ];
-						nextB[player] = pieceMap[ nextB[player] ];
-					}
-										
-					role[player]  = kRetrieveBlobs;
-					autoPattern++;
-				}
-				break;
-			
-			case kPosition:
-				if( (role[player] != kFalling) || (blobX[player] == autoPattern->d1) )
-				{
-					autoPattern++;
-				}
-				else if( GameTickCount() >= timeMove[player] )
-				{
-					timeMove[player] = GameTickCount() + 12;
-					
-					if( blobX[player] > autoPattern->d1 )
-					{
-						if( CanGoLeft( player ) )
-							GoLeft( player );
-					}
-					else
-					{
-						if( CanGoRight( player ) )
-							GoRight( player );
-					}
-				}
-				break;
-			
-			case kSpin:
-				if( role[player] != kFalling )
-				{
-					autoPattern++;
-				}
-				else if( CanRotate( player ) )
-				{
-					DoRotate( player );
-					autoPattern++;
-				}
-				break;
-
-			case kBlockUntilLand:
-				if( role[player] != kFalling ) 
-				{
-					autoPattern++;
-				}			
-				break;
-			
-			case kBlockUntilDrop:
-				if( !dropping[player] ) DoDrop( player );
-
-				if( role[player] != kFalling ) 
-				{
-					autoPattern++;
-				}			
-				break;
-
-			case kPunish:
-				if( role[player] == kWaitForRetrieval )
-				{
-					lockGrays[player] = autoPattern->d1;
-					SetupGrays( player );
-					blobTime[player] = GameTickCount( );
-					role[player] = kDropGrays;
-
-					autoPattern++;
-				}
-				break;
-		
-			case kComplete:
-				EndTutorial( );
-				break;
-			
-			case kBlockUntilComplete:
-				if( role[player] == kWaitForRetrieval ) 
-				{
-					autoPattern++;
-				}			
-				break;
-		}
-	}
-}
-
-void PlayerControl( int player )
-{
-	int a = player, b = player;
-	MBoolean moved = false;
-	
-	if( players == 1 )
-	{
-		a = 0;
-		b = 1;
-	}
-
-	if( hitKey[a].left || hitKey[b].left )
-	{
-		if( GameTickCount() >= timeMove[player] )
-		{
-			timeMove[player] += 12;
-			if( CanGoLeft( player ) )
-				GoLeft( player );
-		}
-		
-		moved = true;
-	}
-	
-	if( hitKey[a].right || hitKey[b].right )
-	{	
-		if( GameTickCount() >= timeMove[player] )
-		{
-			timeMove[player] += 12;
-			if( CanGoRight( player ) )
-				GoRight( player );
-		}
-		
-		moved = true;
-	}
-	
-	if( !moved ) timeMove[player] = GameTickCount( );
-	
-	if( hitKey[a].rotate == 1 || hitKey[b].rotate == 1 )
-	{
-		if( CanRotate( player ) )
-			DoRotate( player );
-	}
-	
-	if( hitKey[a].drop == 1 || hitKey[b].drop == 1 )
-	{
-		DoDrop( player );
-	}
-	
-	if( hitKey[a].drop == 0 && hitKey[b].drop == 0 )
-	{
-		StopDrop( player );
-	}
-}
-
-void AIControl( int player )
-{
-	if( timeAI[player] > GameTickCount() )
-		return;
-	
-	timeAI[player] += moveQuick[player]? character[player].speedRush:
-									     character[player].speedNormal;
-	
-	switch( RandomBefore( 2 ) )
-	{
-		case 0:
-			if( destinationR[player] != blobR[player] )
-			{
-				if( CanRotate(player) )
-				{
-					DoRotate( player );
-				}
-			}
-			break;
-		
-		case 1:
-			if( destinationX[player] != blobX[player] ) 
-			{
-				if( destinationX[player] > blobX[player] )
-				{
-					if( CanGoRight( player ) )
-						GoRight( player );
-				}
-				else
-				{
-					if( CanGoLeft( player ) )
-						GoLeft( player );
-				}
-			}
-			break;		
-	}
-	
-	if( destinationX[player] == blobX[player] &&
-		destinationR[player] == blobR[player] &&
-		RandomBefore( 100 ) < character[player].intellect )
-	{
-		DoDrop( player );
-	}
-}
-
-void ChooseAIDestination( int player )
-{
-	int testX, testR, testX2, testR2, value, bestValue = -9999999;
-	int x, y;
-	int bestX[kGridAcross*4], bestR[kGridAcross*4], currentBest = -1;
-	int rowDifference, totalTries, temp;
-	MBoolean shouldTry[kGridAcross][4];
-	
-	timeAI[player] = GameTickCount( ) + 1;
-	moveQuick[player] = true;
-	
-	if( grenade[player] )
-	{
-		bestValue = 0;
-		currentBest = 2;
-		
-		for( testX = 0; testX < kGridAcross; testX++ )
-		{
-			rowDifference = GetRowHeight( player, testX );
-			
-			if( (rowDifference < kGridDown - 1) &&
-					(grid[player][testX][rowDifference+1] >= kFirstBlob) &&
-					(grid[player][testX][rowDifference+1] <= kLastBlob)     )
-			{
-				value = 0;
-
-				for( x=0; x<kGridAcross; x++ )
-				{
-					for( y=0; y<kGridDown; y++ )
-					{
-						if( grid[player][x][y] == grid[player][testX][rowDifference+1] ) value++;
-					}
-				}
-			
-				if( value > bestValue )
-				{
-					bestValue = value;
-					currentBest = testX;
-				}
-			}
-		}
-		
-		destinationR[player] = upRotate;
-		destinationX[player] = currentBest;
-		return;
-	}
-	
-	if( (GameTickCount() - startTime) <= 3600 )
-	{
-		for( testX = 0; testX < kGridAcross; testX++ )
-		{
-			rowDifference =  GetRowHeight( player, testX ) - character[player].autoSetup[testX];
-			
-			if( rowDifference >= 2 )
-			{
-				destinationR[player] = downRotate;
-				destinationX[player] = testX;
-				return;
-			}
-			
-			if( rowDifference == 1 )
-			{
-				destinationX[player] = testX;
-				
-				if( testX > 0 )
-				{
-					if( GetRowHeight( player, testX-1 ) > character[player].autoSetup[testX-1] )
-					{
-						destinationR[player] = leftRotate;
-						return;
-					}
-				}
-				
-				if( testX < (kGridAcross-1) )
-				{
-					if( GetRowHeight( player, testX+1 ) > character[player].autoSetup[testX+1] )
-					{
-						destinationR[player] = rightRotate;
-						return;
-					}
-				}
-				
-				destinationR[player] = upRotate;
-				return;
-			}
-		}
-	}
-	
-	moveQuick[player] = (emotions[player] == kEmotionSad) || (emotions[player] == kEmotionPanic);
-	
-	totalTries = character[player].intellect;
-	for( testX = 0; testX < kGridAcross; testX++ )
-	{
-		for( testR = 0; testR < 4; testR++ )
-		{
-			shouldTry[testX][testR] = --totalTries >= 0;
-		}
-	}
-	
-	for( testX = 0; testX < kGridAcross; testX++ )
-	{
-		for( testR = 0; testR < 4; testR++ )
-		{
-			testX2 = RandomBefore( kGridAcross );
-			testR2 = RandomBefore( 4 );
-		
-			temp = shouldTry[testX][testR];
-			shouldTry[testX][testR] = shouldTry[testX2][testR2];
-			shouldTry[testX2][testR2] = temp;
-		}
-	}
-	
-	shouldTry[0][leftRotate]			  = false;
-	shouldTry[kGridAcross-1][rightRotate] = false;
-		
-	for( testX = 0; testX < kGridAcross; testX++ )
-	{
-		for( testR = 0; testR<=3; testR++ )
-		{
-			if( shouldTry[testX][testR] )
-			{
-				value = TestAIDestination( player, testX, testR );
-				
-				if( value > bestValue )
-				{
-					bestValue = value;
-					currentBest = -1;
-				}
-				
-				if( value == bestValue )
-				{
-					currentBest++;
-					bestX[currentBest] = testX;
-					bestR[currentBest] = testR;
-				}
-			}
-		}
-	}
-	
-	currentBest = RandomBefore( currentBest + 1 );
-	destinationX[player] = bestX[currentBest];
-	destinationR[player] = bestR[currentBest];
-}
-
-int TestAIDestination( int player, int testX, int testR )
-{
-	int x, y, height, chains, rensa = 50, result = 0;
-
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=0; y<kGridDown; y++ )
-		{
-			tempGrid[x][y] = grid[player][x][y];
-		}
-	}
-		
-	height = GetRowHeight(player, testX);
-	switch( testR ) 
-	{
-		case upRotate:
-			tempGrid[testX][height--] = colorA[player];
-			if( height >= 0 ) tempGrid[testX][height]   = colorB[player];
-			break;
-		
-		case downRotate:
-			tempGrid[testX][height--] = colorB[player];
-			if( height >= 0 ) tempGrid[testX][height]   = colorA[player];
-			break;
-		
-		case leftRotate:
-			tempGrid[testX][height]                         = colorA[player];
-			tempGrid[testX-1][GetRowHeight(player,testX-1)] = colorB[player];
-			break;
-		
-		case rightRotate:
-			tempGrid[testX][height]                         = colorA[player];
-			tempGrid[testX+1][GetRowHeight(player,testX+1)] = colorB[player];
-			break;		
-	}
-	
-	chains = TestTemporaryGrid( );
-	
-	result = ScoreTemporaryGrid( );
-		
-	if( (chains < 2) && (character[player].intellect > (24 * 2/3)) )
-	{
-		rensa = 0;
-	}
-	else
-	{
-		while( chains-- ) rensa *= 10;
-	}
-	
-	result += rensa;
-	
-	return result;
-}
-
-int ScoreTemporaryGrid( void )
-{
-	int x, y, change, result = 0;
-	int deductions[kGridAcross][kGridDown] = 
-		{ { 400, 350, 350, 200, 120, 60, 20, 5, 0, 0, 0, 0 },
-		  { 600, 500, 300, 150, 100, 50, 20, 0, 0, 0, 0, 0 },
-		  { 9999, 800, 200, 100,  50, 40, 20, 0, 0, 0, 0, 0 },
-		  { 9999, 800, 200, 100,  50, 40, 20, 0, 0, 0, 0, 0 },
-		  { 9999, 500, 300, 150, 100, 50, 20, 0, 0, 0, 0, 0 },
-		  { 400, 350, 350, 200, 120, 60, 20, 5, 0, 0, 0, 0 } };
-
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=0; y<kGridDown; y++ )
-		{
-			if( tempGrid[x][y] == kEmpty )
-			{
-				result += deductions[x][y];
-			}
-			else if( tempGrid[x][y] == kGray )
-			{
-				result -= deductions[x][y];
-			}
-			else
-			{
-				change = 0;
-
-				if( y < (kGridDown-1)   && (tempGrid[x][y] == tempGrid[x][y+1])   ) change += 50;
-				if( y > 0               && (tempGrid[x][y] == tempGrid[x][y-1])   ) change += 50;
-				if( x < (kGridAcross-1) && (tempGrid[x][y] == tempGrid[x+1][y])   ) change += 40;
-				if( x > 0               && (tempGrid[x][y] == tempGrid[x-1][y])   ) change += 40;
-				if( x > 0               &&  
-				    y > 0               && (tempGrid[x][y] == tempGrid[x-1][y-1]) ) change += 20;
-				if( x < (kGridAcross-1) &&  
-				    y > 0               && (tempGrid[x][y] == tempGrid[x+1][y-1]) ) change += 20;
-				if( x > 0               &&  
-				    y < (kGridDown-1)   && (tempGrid[x][y] == tempGrid[x-1][y+1]) ) change += 10;
-				if( x < (kGridAcross-1) &&  
-				    y < (kGridDown-1)   && (tempGrid[x][y] == tempGrid[x+1][y+1]) ) change += 10;
-				if( y < (kGridDown-2)   && (tempGrid[x][y] == tempGrid[x][y+2])   ) change += 10;
-				if( y > 1               && (tempGrid[x][y] == tempGrid[x][y-2])   ) change += 10;
-				
-				if( (x > 0               && tempGrid[x-1][y] == kEmpty) ||
-				    (x < (kGridAcross-1) && tempGrid[x+1][y] == kEmpty) ||
-				    (y < 4               || tempGrid[x][y-4] == kEmpty)    ) change *= 4;
-				    
-				result += change / 4;
-			}
-		}
-	}
-	
-	return result;
-}
-
-int TestTemporaryGrid( void )
-{
-	MBoolean busy;
-	int x, y, stackSize, chains = 0;
-	
-	do
-	{
-		busy = false;
-		
-		for( x=0; x<kGridAcross; x++ )
-		{
-			for( y=0; y<kGridDown; y++ )
-			{
-				if( tempGrid[x][y] >= kFirstBlob && tempGrid[x][y] <= kLastBlob )
-				{
-					if( SizeUp( tempGrid, x, y, tempGrid[x][y] ) >= kBlobClusterSize )
-					{
-						QuickRemove( tempGrid, x, y, tempGrid[x][y] );
-						busy = true;
-					}
-				}
-			}
-		}
-		
-		if( busy )
-		{
-			chains++;
-			
-			for( x=0; x<kGridAcross; x++ )
-			{
-				stackSize = kGridDown-1;
-				
-				for( y=kGridDown-1; y>=0; y-- )
-				{
-					if( tempGrid[x][y] != kEmpty )
-					{
-						tempGrid[x][stackSize] = tempGrid[x][y];
-						if( y < stackSize ) tempGrid[x][y] = kEmpty;
-						stackSize--;
-					}
-				}
-			}
-		}
-	}
-	while( busy );
-	
-	return chains;
-}
-
-void QuickRemove( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color )
-{
-	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) )
-		return;
-	
-	if( myGrid[x][y] == kGray ) myGrid[x][y] = kEmpty;	
-	if( myGrid[x][y] == color )
-	{
-		myGrid[x][y] = kEmpty;
-		QuickRemove( myGrid, x-1, y,   color );
-		QuickRemove( myGrid, x+1, y,   color );
-		QuickRemove( myGrid, x,   y-1, color );
-		QuickRemove( myGrid, x,   y+1, color );
-	}
-}
-
-int BestColor( int player, int blobX, int blobY )
-{
-	int x, y, color, bestColor = kFirstBlob, bestResult = -9999999, rensa, chains, result;
-//	char C[] = {' ', '*', '@', '.', '=', '+', 'o', 'J'};	
-
-
-	for( color = kFirstBlob; color <= kLastBlob; color++ )
-	{		
-		for( y=0; y<kGridDown; y++ )
-		{
-			for( x=0; x<kGridAcross; x++ )
-			{
-				tempGrid[x][y] = grid[player][x][y];
-			}
-		}
-	
-		tempGrid[blobX][blobY] = color;
-		
-		chains = TestTemporaryGrid( );
-		result = ScoreTemporaryGrid( );
-
-		rensa = 1000;
-		while( chains-- ) rensa *= 10;
-		
-		result += rensa;
-		
-		if( result > bestResult )
-		{
-			bestColor = color;
-			bestResult = result;
-		}
-	}
-		
-	return bestColor;
-}
-
-// Returns the first empty row.
-int GetRowHeight( int player, int row )
-{
-	int height;
-	
-	for( height = (kGridDown-1); height > 0; height-- )
-	{
-		if( grid[player][row][height] == kEmpty ) break;
-	}
-	
-	return height;
-}
-
-int DetermineEmotion( int player )
-{
-	int us = 0, them = 0, aboveWater = 1;
-	int x,y;
-	
-	if( role[player] == kLosing )  return kEmotionPanic;
-	if( role[player] == kWinning ) return kEmotionHappy;
-	
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=0; y<kGridDown; y++ )
-		{
-			if( aboveWater && (y<3) && (grid[player][x][y] != kEmpty) ) aboveWater = 0;
-			if( grid[player][x][y]   != kEmpty ) us++;
-			if( grid[1-player][x][y] != kEmpty ) them++;
-		}
-	}
-	
-	if( us > 48 && !aboveWater ) return kEmotionPanic;
-	else if( abs(us-them) < 12 ) return kEmotionNeutral;
-	else if( us > them ) return kEmotionSad;
-	else return kEmotionHappy;
-}
--- /dev/null
+++ b/src/font.c
@@ -1,0 +1,119 @@
+// font.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "font.h"
+#include "gworld.h"
+
+
+#define kNumFonts (picBatsuFont-picFont+1)
+
+static SkittlesFont s_font[kNumFonts];
+
+
+static SkittlesFontPtr LoadFont( SkittlesFontPtr font, int pictID, unsigned char *letterMap )
+{
+	unsigned char* lastLine;
+	unsigned char  white;
+	MBoolean       success = false;
+	int            start, across, skip;
+	SDL_Surface*   temporarySurface;
+	SDL_Rect       sdlRect;
+	
+	temporarySurface = LoadPICTAsSurface( pictID, 8 );
+	
+	if( temporarySurface )
+	{
+		sdlRect.x = 0;
+		sdlRect.y = 0;
+		sdlRect.h = temporarySurface->h;
+		sdlRect.w = temporarySurface->w;
+		
+		font->surface = SDLU_InitSurface( &sdlRect, 8 );
+		
+		SDLU_BlitSurface(  temporarySurface, &temporarySurface->clip_rect,
+		                   font->surface,    &temporarySurface->clip_rect  );
+		
+		SDL_FreeSurface( temporarySurface );
+		
+		white    = SDL_MapRGB( font->surface->format, 0xFF, 0xFF, 0xFF );
+		lastLine = (unsigned char*) font->surface->pixels + (font->surface->pitch * (font->surface->h - 1));
+		across   = 0;
+		
+		// Measure empty space between character breaks
+		while( lastLine[across] == white ) across++;
+		skip = across;
+		
+		success = true;
+
+		// Measure character starts and widths
+		while( *letterMap )
+		{
+			while( lastLine[across] != white ) across++;
+			if( across > font->surface->pitch ) 
+			{
+				success = false;
+				break;
+			}
+			
+			start = across;
+			font->across[*letterMap] = across + (skip/2);
+
+			while( lastLine[across] == white ) across++;		
+			font->width [*letterMap] = across - start - skip;
+
+			letterMap++;
+		}
+	}
+	
+	if( success )
+	{
+		return font;
+	}
+	else
+	{
+		Error( "LoadFont: files are missing or corrupt" );
+		return NULL;
+	}
+}
+
+
+void InitFont( void ) 
+{
+	LoadFont( &s_font[0],  picFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!:,.()*?0123456789'|-\x01\x02\x03\x04\x05 " );
+	LoadFont( &s_font[1],  picHiScoreFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*().,/-=_+<>?|'\":; " );
+	LoadFont( &s_font[2],  picContinueFont, (unsigned char*) "0123456789" );
+	LoadFont( &s_font[3],  picBalloonFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=_+;:,./<>? \x01\x02'\"" );
+	LoadFont( &s_font[4],  picZapFont, (unsigned char*) "0123456789*PS" );
+	LoadFont( &s_font[5],  picZapOutlineFont, (unsigned char*) "0123456789*" );
+	LoadFont( &s_font[6],  picVictoryFont, (unsigned char*) "AB" );
+	LoadFont( &s_font[7],  picBubbleFont, (unsigned char*) "*" );
+	LoadFont( &s_font[8],  picTinyFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.!`-=[];'/~_+{}:\"\\ " );
+	LoadFont( &s_font[9],  picDashedLineFont, (unsigned char*) "." );
+	LoadFont( &s_font[10], picBatsuFont, (unsigned char*) "X" );
+}
+
+
+SkittlesFontPtr GetFont( int pictID )
+{
+	int fontID = pictID - picFont;
+	
+	if( (fontID < 0) || (fontID >= kNumFonts) )
+		Error( "GetFont: fontID" );
+			
+	return &s_font[fontID];
+} 
+
+
+int GetTextWidth( SkittlesFontPtr font, const char *text )
+{
+	int width = 0;
+	while( *text )
+	{
+		width += font->width[(uint8_t) *text++];
+	}
+	
+	return width;
+}
+
--- a/src/font.cpp
+++ /dev/null
@@ -1,119 +1,0 @@
-// font.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "font.h"
-#include "gworld.h"
-
-
-const int kNumFonts = (picBatsuFont-picFont+1);
-
-static SkittlesFont s_font[kNumFonts] = {};
-
-
-static SkittlesFontPtr LoadFont( SkittlesFontPtr font, int pictID, unsigned char *letterMap )
-{
-	unsigned char* lastLine;
-	unsigned char  white;
-	MBoolean       success = false;
-	int            start, across, skip;
-	SDL_Surface*   temporarySurface;
-	SDL_Rect       sdlRect;
-	
-	temporarySurface = LoadPICTAsSurface( pictID, 8 );
-	
-	if( temporarySurface )
-	{
-		sdlRect.x = 0;
-		sdlRect.y = 0;
-		sdlRect.h = temporarySurface->h;
-		sdlRect.w = temporarySurface->w;
-		
-		font->surface = SDLU_InitSurface( &sdlRect, 8 );
-		
-		SDLU_BlitSurface(  temporarySurface, &temporarySurface->clip_rect,
-		                   font->surface,    &temporarySurface->clip_rect  );
-		
-		SDL_FreeSurface( temporarySurface );
-		
-		white    = SDL_MapRGB( font->surface->format, 0xFF, 0xFF, 0xFF );
-		lastLine = (unsigned char*) font->surface->pixels + (font->surface->pitch * (font->surface->h - 1));
-		across   = 0;
-		
-		// Measure empty space between character breaks
-		while( lastLine[across] == white ) across++;
-		skip = across;
-		
-		success = true;
-
-		// Measure character starts and widths
-		while( *letterMap )
-		{
-			while( lastLine[across] != white ) across++;
-			if( across > font->surface->pitch ) 
-			{
-				success = false;
-				break;
-			}
-			
-			start = across;
-			font->across[*letterMap] = across + (skip/2);
-
-			while( lastLine[across] == white ) across++;		
-			font->width [*letterMap] = across - start - skip;
-
-			letterMap++;
-		}
-	}
-	
-	if( success )
-	{
-		return font;
-	}
-	else
-	{
-		Error( "LoadFont: files are missing or corrupt" );
-		return NULL;
-	}
-}
-
-
-void InitFont( void ) 
-{
-	LoadFont( &s_font[0],  picFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!:,.()*?0123456789'|-\x01\x02\x03\x04\x05 " );
-	LoadFont( &s_font[1],  picHiScoreFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*().,/-=_+<>?|'\":; " );
-	LoadFont( &s_font[2],  picContinueFont, (unsigned char*) "0123456789" );
-	LoadFont( &s_font[3],  picBalloonFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=_+;:,./<>? \x01\x02'\"" );
-	LoadFont( &s_font[4],  picZapFont, (unsigned char*) "0123456789*PS" );
-	LoadFont( &s_font[5],  picZapOutlineFont, (unsigned char*) "0123456789*" );
-	LoadFont( &s_font[6],  picVictoryFont, (unsigned char*) "AB" );
-	LoadFont( &s_font[7],  picBubbleFont, (unsigned char*) "*" );
-	LoadFont( &s_font[8],  picTinyFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789,.!`-=[];'/~_+{}:\"\\ " );
-	LoadFont( &s_font[9],  picDashedLineFont, (unsigned char*) "." );
-	LoadFont( &s_font[10], picBatsuFont, (unsigned char*) "X" );
-}
-
-
-SkittlesFontPtr GetFont( int pictID )
-{
-	int fontID = pictID - picFont;
-	
-	if( (fontID < 0) || (fontID >= kNumFonts) )
-		Error( "GetFont: fontID" );
-			
-	return &s_font[fontID];
-} 
-
-
-int GetTextWidth( SkittlesFontPtr font, const char *text )
-{
-	int width = 0;
-	while( *text )
-	{
-		width += font->width[(uint8_t) *text++];
-	}
-	
-	return width;
-}
-
--- /dev/null
+++ b/src/gameticks.c
@@ -1,0 +1,45 @@
+// gameticks.c
+
+#include <SDL_timer.h>
+#include "gameticks.h"
+
+MTicks baseTickCount, freezeTickCount;
+int freezeLevel;
+
+MTicks MTickCount()
+{
+	return (unsigned int ) ((float)SDL_GetTicks() * 0.06f);
+}
+
+void InitGameTickCount( void )
+{
+	baseTickCount = freezeTickCount = 0;
+	freezeLevel = 0;
+}
+
+void FreezeGameTickCount( void )
+{
+	if( freezeLevel	== 0 ) 
+    {
+        freezeTickCount = MTickCount( );
+    }
+	freezeLevel--;
+}
+
+void UnfreezeGameTickCount( void )
+{
+	freezeLevel++;
+	if( freezeLevel >= 0 )
+	{
+		freezeLevel = 0;
+		baseTickCount += MTickCount( ) - freezeTickCount;
+	}
+}
+
+unsigned int  GameTickCount( void )
+{
+	if( freezeLevel < 0 )
+		return freezeTickCount - baseTickCount;
+
+	return MTickCount( ) - baseTickCount;
+}
--- a/src/gameticks.cpp
+++ /dev/null
@@ -1,45 +1,0 @@
-// gameticks.c
-
-#include <SDL_timer.h>
-#include "gameticks.h"
-
-MTicks baseTickCount, freezeTickCount;
-int freezeLevel;
-
-MTicks MTickCount()
-{
-	return (unsigned int ) ((float)SDL_GetTicks() * 0.06f);
-}
-
-void InitGameTickCount( void )
-{
-	baseTickCount = freezeTickCount = 0;
-	freezeLevel = 0;
-}
-
-void FreezeGameTickCount( void )
-{
-	if( freezeLevel	== 0 ) 
-    {
-        freezeTickCount = MTickCount( );
-    }
-	freezeLevel--;
-}
-
-void UnfreezeGameTickCount( void )
-{
-	freezeLevel++;
-	if( freezeLevel >= 0 )
-	{
-		freezeLevel = 0;
-		baseTickCount += MTickCount( ) - freezeTickCount;
-	}
-}
-
-unsigned int  GameTickCount( void )
-{
-	if( freezeLevel < 0 )
-		return freezeTickCount - baseTickCount;
-
-	return MTickCount( ) - baseTickCount;
-}
--- /dev/null
+++ b/src/graphics.c
@@ -1,0 +1,199 @@
+// graphics.c
+
+#include <stdlib.h>
+#include "version.h"
+#include "SDLU.h"
+#include "main.h"
+#include "players.h"
+#include "graphics.h"
+#include "gworld.h"
+#include "moving.h"
+#include "tweak.h"
+#include "gameticks.h"
+#include "blitter.h"
+#include "victory.h"
+#include "grays.h"
+#include "level.h"
+#include "keyselect.h"
+
+
+SDL_Surface* backdropSurface = NULL;
+
+
+void DrawSpriteBlobs( int player, int type )
+{
+	MRect firstRect, secondRect, thirdRect;
+
+#define repeat 0xFF
+#define forever 0xFE
+	
+	static const unsigned char blobAnimation[6][2][25] = 
+	{ 
+	    { { kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
+	        kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
+	        kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,       
+		    kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob,
+		    kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob,
+		    kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, repeat }, 
+		  { kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
+		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
+		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
+		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
+		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
+		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction, repeat } },
+		{ { kNoSuction,       kSquish,          kNoSuction,       kSquash,        
+		    kNoSuction,       kSquish,          kNoSuction,       kSquash,
+		    kNoSuction,       forever },
+		  { kNoSuction,       kSquish,          kNoSuction,       kSquash,        
+		    kNoSuction,       kSquish,          kNoSuction,       kSquash,
+		    kNoSuction,       forever } },
+		{ { kSobBlob,         kSobBlob,         kSobBlob,         kSobBlob,         
+			kSob2Blob,        kSob2Blob,        kSob2Blob,        kSob2Blob,        
+			repeat },
+		  { kSobBlob,         kSobBlob,         kSobBlob,         kSobBlob,         
+			kSob2Blob,        kSob2Blob,        kSob2Blob,        kSob2Blob,        
+			repeat } },
+		{ { kBombFuse1,       kBombFuse2,       kBombFuse3,       repeat }, 
+		  { kBombFuse1,       kBombFuse2,       kBombFuse3,       repeat } }, 
+		{ { kBlinkBomb1,      kBombFuse2,       kBlinkBomb3,      kBombFuse1,        
+		    kBlinkBomb2,      kBombFuse3,       repeat },
+		  { kBlinkBomb1,      kBombFuse2,       kBlinkBomb3,      kBombFuse1,        
+		    kBlinkBomb2,      kBombFuse3,       repeat } }
+	};
+	
+	if( grenade[player] ) type += 3;
+	
+	SDLU_AcquireSurface( playerSpriteSurface[player] );
+	
+	if( blobAnimation[type][0][anim[player]] == forever ) anim[player]--;
+	if( blobAnimation[type][0][anim[player]] == repeat  ) anim[player] = 0;
+	
+	CalcBlobRect( blobX[player], blobY[player], &firstRect );
+	if( halfway[player] ) OffsetMRect( &firstRect, 0, kBlobVertSize / 2 );
+	
+	TweakFirstBlob ( player, &firstRect );
+	secondRect = firstRect;
+	TweakSecondBlob( player, &secondRect );
+		
+	thirdRect = firstRect;
+	thirdRect.top    -= kBlobShadowError;
+	thirdRect.left   -= kBlobShadowError;
+	thirdRect.right  += kBlobShadowDepth + kBlobShadowError;
+	thirdRect.bottom += kBlobShadowDepth + kBlobShadowError;
+	CleanSpriteArea( player, &thirdRect );
+							
+	thirdRect = secondRect;
+	thirdRect.top    -= kBlobShadowError;
+	thirdRect.left   -= kBlobShadowError;
+	thirdRect.right  += kBlobShadowDepth + kBlobShadowError;
+	thirdRect.bottom += kBlobShadowDepth + kBlobShadowError;
+	CleanSpriteArea( player, &thirdRect );
+	
+	thirdRect = firstRect;
+	OffsetMRect( &thirdRect, shadowDepth[player], shadowDepth[player] );
+	SurfaceDrawShadow( &thirdRect,  colorA[player], blobAnimation[type][0][anim[player]] );
+
+	thirdRect = secondRect;
+	OffsetMRect( &thirdRect, shadowDepth[player], shadowDepth[player] );
+	SurfaceDrawShadow( &thirdRect, colorB[player], blobAnimation[type][1][anim[player]] );
+
+	SurfaceDrawSprite( &firstRect,  colorA[player], blobAnimation[type][0][anim[player]] );
+	
+	SurfaceDrawSprite( &secondRect, colorB[player], blobAnimation[type][1][anim[player]] );
+	
+	SDLU_ReleaseSurface( playerSpriteSurface[player] );
+
+#undef repeat
+#undef forever
+}
+
+void CleanSpriteArea( int player, MRect *myRect )
+{
+	SDL_Rect sdlRect;
+
+	SDLU_MRectToSDLRect( myRect, &sdlRect );
+	
+	SDLU_BlitSurface( playerSurface[player],       &sdlRect,
+	                  playerSpriteSurface[player], &sdlRect  );
+		
+	SetUpdateRect( player, myRect );
+}
+
+void EraseSpriteBlobs( int player )
+{
+	MRect myRect, secondRect;
+	
+	CalcBlobRect( blobX[player], blobY[player], &myRect );
+	if( halfway[player] ) OffsetMRect( &myRect, 0, kBlobVertSize / 2 );
+
+	TweakFirstBlob( player, &myRect );
+	secondRect = myRect;
+	secondRect.top    -= kBlobShadowError;
+	secondRect.left   -= kBlobShadowError;
+	secondRect.right  += kBlobShadowDepth + kBlobShadowError;
+	secondRect.bottom += kBlobShadowDepth + kBlobShadowError;
+	CleanSpriteArea( player, &secondRect );
+
+	TweakSecondBlob( player, &myRect );
+	myRect.top    -= kBlobShadowError;
+	myRect.left   -= kBlobShadowError;
+	myRect.right  += kBlobShadowDepth + kBlobShadowError;
+	myRect.bottom += kBlobShadowDepth + kBlobShadowError;
+	CleanSpriteArea( player, &myRect );
+}
+
+void CalcBlobRect( int x, int y, MRect *myRect )
+{
+	myRect->top = y * kBlobVertSize;
+	myRect->left = x * kBlobHorizSize;
+	myRect->bottom = myRect->top + kBlobVertSize;
+	myRect->right = myRect->left + kBlobHorizSize;
+}
+
+void InitBackdrop( void )
+{
+	backdropSurface = LoadPICTAsSurface( picBackdrop, 32 );
+}
+
+void DrawBackdrop( void )
+{
+	SDL_Rect backdropRect = { 0, 0, 640, 480 };
+	
+	SDLU_BlitFrontSurface( backdropSurface, &backdropRect, &backdropRect );
+}
+
+void ShowTitle( void )
+{
+    SDL_FillRect( g_frontSurface, &g_frontSurface->clip_rect, SDL_MapRGB( g_frontSurface->format, 0, 0, 0 ) );
+    SDLU_Present();
+
+    RetrieveResources( );
+
+    MTicks time = MTickCount() + 120;
+	SDLU_AcquireSurface( g_frontSurface );
+    DrawPICTInSurface( g_frontSurface, picTitle );
+
+	SkittlesFontPtr font = GetFont(picTinyFont);
+	MPoint dPoint;
+	dPoint.v = (widescreen ? 420 : 480) - 16;
+	dPoint.h = 4;
+	for (const char* scan = "Source port v" PROJECT_VERSION; *scan; scan++)
+	{
+		SurfaceBlitCharacter(font, *scan, &dPoint, 50, 50, 50, 1);
+	}
+	SDLU_ReleaseSurface( g_frontSurface );
+
+	while( time > MTickCount() && !SDLU_Button() )
+	{
+        SDLU_Present();
+		SDLU_Yield();
+	}
+	
+	WaitForRelease();
+		
+	QuickFadeOut( NULL );
+	
+	SDL_FillRect( g_frontSurface, &g_frontSurface->clip_rect, SDL_MapRGB( g_frontSurface->format, 0, 0, 0 ) );
+    SDLU_Present();
+}
+
--- a/src/graphics.cpp
+++ /dev/null
@@ -1,194 +1,0 @@
-// graphics.c
-
-#include <stdlib.h>
-#include "version.h"
-#include "SDLU.h"
-#include "main.h"
-#include "players.h"
-#include "graphics.h"
-#include "gworld.h"
-#include "moving.h"
-#include "tweak.h"
-#include "gameticks.h"
-#include "blitter.h"
-#include "victory.h"
-#include "grays.h"
-#include "level.h"
-#include "keyselect.h"
-
-
-SDL_Surface* backdropSurface = NULL;
-
-
-void DrawSpriteBlobs( int player, int type )
-{
-	MRect firstRect, secondRect, thirdRect;
-	const int repeat = 0xFF, forever = 0xFE;
-	
-	static const unsigned char blobAnimation[6][2][25] = 
-	{ 
-	    { { kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
-	        kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
-	        kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,       
-		    kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob,
-		    kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob,
-		    kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, kFlashBrightBlob, repeat }, 
-		  { kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
-		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
-		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
-		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
-		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction,
-		    kNoSuction,       kNoSuction,       kNoSuction,       kNoSuction, repeat } },
-		{ { kNoSuction,       kSquish,          kNoSuction,       kSquash,        
-		    kNoSuction,       kSquish,          kNoSuction,       kSquash,
-		    kNoSuction,       forever },
-		  { kNoSuction,       kSquish,          kNoSuction,       kSquash,        
-		    kNoSuction,       kSquish,          kNoSuction,       kSquash,
-		    kNoSuction,       forever } },
-		{ { kSobBlob,         kSobBlob,         kSobBlob,         kSobBlob,         
-			kSob2Blob,        kSob2Blob,        kSob2Blob,        kSob2Blob,        
-			repeat },
-		  { kSobBlob,         kSobBlob,         kSobBlob,         kSobBlob,         
-			kSob2Blob,        kSob2Blob,        kSob2Blob,        kSob2Blob,        
-			repeat } },
-		{ { kBombFuse1,       kBombFuse2,       kBombFuse3,       repeat }, 
-		  { kBombFuse1,       kBombFuse2,       kBombFuse3,       repeat } }, 
-		{ { kBlinkBomb1,      kBombFuse2,       kBlinkBomb3,      kBombFuse1,        
-		    kBlinkBomb2,      kBombFuse3,       repeat },
-		  { kBlinkBomb1,      kBombFuse2,       kBlinkBomb3,      kBombFuse1,        
-		    kBlinkBomb2,      kBombFuse3,       repeat } }
-	};
-	
-	if( grenade[player] ) type += 3;
-	
-	SDLU_AcquireSurface( playerSpriteSurface[player] );
-	
-	if( blobAnimation[type][0][anim[player]] == forever ) anim[player]--;
-	if( blobAnimation[type][0][anim[player]] == repeat  ) anim[player] = 0;
-	
-	CalcBlobRect( blobX[player], blobY[player], &firstRect );
-	if( halfway[player] ) OffsetMRect( &firstRect, 0, kBlobVertSize / 2 );
-	
-	TweakFirstBlob ( player, &firstRect );
-	secondRect = firstRect;
-	TweakSecondBlob( player, &secondRect );
-		
-	thirdRect = firstRect;
-	thirdRect.top    -= kBlobShadowError;
-	thirdRect.left   -= kBlobShadowError;
-	thirdRect.right  += kBlobShadowDepth + kBlobShadowError;
-	thirdRect.bottom += kBlobShadowDepth + kBlobShadowError;
-	CleanSpriteArea( player, &thirdRect );
-							
-	thirdRect = secondRect;
-	thirdRect.top    -= kBlobShadowError;
-	thirdRect.left   -= kBlobShadowError;
-	thirdRect.right  += kBlobShadowDepth + kBlobShadowError;
-	thirdRect.bottom += kBlobShadowDepth + kBlobShadowError;
-	CleanSpriteArea( player, &thirdRect );
-	
-	thirdRect = firstRect;
-	OffsetMRect( &thirdRect, shadowDepth[player], shadowDepth[player] );
-	SurfaceDrawShadow( &thirdRect,  colorA[player], blobAnimation[type][0][anim[player]] );
-
-	thirdRect = secondRect;
-	OffsetMRect( &thirdRect, shadowDepth[player], shadowDepth[player] );
-	SurfaceDrawShadow( &thirdRect, colorB[player], blobAnimation[type][1][anim[player]] );
-
-	SurfaceDrawSprite( &firstRect,  colorA[player], blobAnimation[type][0][anim[player]] );
-	
-	SurfaceDrawSprite( &secondRect, colorB[player], blobAnimation[type][1][anim[player]] );
-	
-	SDLU_ReleaseSurface( playerSpriteSurface[player] );
-}
-
-void CleanSpriteArea( int player, MRect *myRect )
-{
-	SDL_Rect sdlRect;
-
-	SDLU_MRectToSDLRect( myRect, &sdlRect );
-	
-	SDLU_BlitSurface( playerSurface[player],       &sdlRect,
-	                  playerSpriteSurface[player], &sdlRect  );
-		
-	SetUpdateRect( player, myRect );
-}
-
-void EraseSpriteBlobs( int player )
-{
-	MRect myRect, secondRect;
-	
-	CalcBlobRect( blobX[player], blobY[player], &myRect );
-	if( halfway[player] ) OffsetMRect( &myRect, 0, kBlobVertSize / 2 );
-
-	TweakFirstBlob( player, &myRect );
-	secondRect = myRect;
-	secondRect.top    -= kBlobShadowError;
-	secondRect.left   -= kBlobShadowError;
-	secondRect.right  += kBlobShadowDepth + kBlobShadowError;
-	secondRect.bottom += kBlobShadowDepth + kBlobShadowError;
-	CleanSpriteArea( player, &secondRect );
-
-	TweakSecondBlob( player, &myRect );
-	myRect.top    -= kBlobShadowError;
-	myRect.left   -= kBlobShadowError;
-	myRect.right  += kBlobShadowDepth + kBlobShadowError;
-	myRect.bottom += kBlobShadowDepth + kBlobShadowError;
-	CleanSpriteArea( player, &myRect );
-}
-
-void CalcBlobRect( int x, int y, MRect *myRect )
-{
-	myRect->top = y * kBlobVertSize;
-	myRect->left = x * kBlobHorizSize;
-	myRect->bottom = myRect->top + kBlobVertSize;
-	myRect->right = myRect->left + kBlobHorizSize;
-}
-
-void InitBackdrop( void )
-{
-	backdropSurface = LoadPICTAsSurface( picBackdrop, 32 );
-}
-
-void DrawBackdrop( void )
-{
-	SDL_Rect backdropRect = { 0, 0, 640, 480 };
-	
-	SDLU_BlitFrontSurface( backdropSurface, &backdropRect, &backdropRect );
-}
-
-void ShowTitle( void )
-{
-    SDL_FillRect( g_frontSurface, &g_frontSurface->clip_rect, SDL_MapRGB( g_frontSurface->format, 0, 0, 0 ) );
-    SDLU_Present();
-
-    RetrieveResources( );
-
-    MTicks time = MTickCount() + 120;
-	SDLU_AcquireSurface( g_frontSurface );
-    DrawPICTInSurface( g_frontSurface, picTitle );
-
-	SkittlesFontPtr font = GetFont(picTinyFont);
-	MPoint dPoint;
-	dPoint.v = (widescreen ? 420 : 480) - 16;
-	dPoint.h = 4;
-	for (const char* scan = "Source port v" PROJECT_VERSION; *scan; scan++)
-	{
-		SurfaceBlitCharacter(font, *scan, &dPoint, 50, 50, 50, 1);
-	}
-	SDLU_ReleaseSurface( g_frontSurface );
-
-	while( time > MTickCount() && !SDLU_Button() )
-	{
-        SDLU_Present();
-		SDLU_Yield();
-	}
-	
-	WaitForRelease();
-		
-	QuickFadeOut( NULL );
-	
-	SDL_FillRect( g_frontSurface, &g_frontSurface->clip_rect, SDL_MapRGB( g_frontSurface->format, 0, 0, 0 ) );
-    SDLU_Present();
-}
-
--- /dev/null
+++ b/src/graymonitor.c
@@ -1,0 +1,90 @@
+// graymonitor.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "graymonitor.h"
+#include "grays.h"
+#include "score.h"
+#include "gworld.h"
+#include "graphics.h"
+#include "level.h"
+
+
+static SDL_Surface* smallGrayDrawSurface;
+
+
+MRect grayMonitorZRect, grayMonitorRect[2];
+MBoolean grayMonitorVisible[2] = {true, true};
+
+void InitGrayMonitors( void )
+{
+	const double windowLoc[ ] = { 0.16, 0.84 };
+	SDL_Rect     sdlRect;
+			 
+	grayMonitorZRect.top = grayMonitorZRect.left = 0;
+	grayMonitorZRect.bottom = 32; grayMonitorZRect.right = 144;
+	
+	grayMonitorRect[0] = grayMonitorRect[1] = grayMonitorZRect;
+	CenterRectOnScreen( &grayMonitorRect[0], windowLoc[0], 0.11 );
+	CenterRectOnScreen( &grayMonitorRect[1], windowLoc[1], 0.11 );
+
+	smallGrayDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &grayMonitorZRect, &sdlRect ), 32 );
+	DrawPICTInSurface( smallGrayDrawSurface, picBoard );
+}
+
+void ShowGrayMonitor( short player )
+{
+	SDL_Rect   sourceSDLRect, destSDLRect;
+	short      monitor;
+	MRect      myRect = { 4, 4, kBlobVertSize+4, 4 };
+	MRect      srcRect;
+	const int  smallGrayList[] = { 0, kSmallGray1, kSmallGray2, kSmallGray3, kSmallGray4, kSmallGray5 };
+	
+	if( !grayMonitorVisible[player] ) return;
+	
+	if( control[player] != kNobodyControl )
+	{
+		SDLU_AcquireSurface( smallGrayDrawSurface );
+		
+		SDLU_BlitSurface( boardSurface[player], &smallGrayDrawSurface->clip_rect,
+						  smallGrayDrawSurface, &smallGrayDrawSurface->clip_rect  );
+	 				
+		monitor = unallocatedGrays[player];
+		
+		CalcBlobRect( kSobBlob, 3, &srcRect );
+		while( monitor >= (6*4) )
+		{
+			myRect.right += kBlobHorizSize;
+			SurfaceDrawSprite( &myRect, 4, kSobBlob );
+			myRect.left = myRect.right;
+			
+			monitor -= (6*4);
+		}
+		
+		CalcBlobRect( kNoSuction, kGray-1, &srcRect );
+		while( monitor >= 6 )
+		{
+			myRect.right += kBlobHorizSize;
+			SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
+			myRect.left = myRect.right;
+			
+			monitor -= 6;
+		}
+		
+		if( monitor > 0 )
+		{
+			myRect.right += kBlobHorizSize;
+			SurfaceDrawAlpha( &myRect, kGray, kLight, smallGrayList[monitor] );
+			myRect.left = myRect.right;
+			myRect.right += kBlobHorizSize;
+			SurfaceDrawAlpha( &myRect, kGray, kLight, smallGrayList[monitor]+1 );
+		}
+		
+		SDLU_ReleaseSurface( smallGrayDrawSurface );
+
+		SDLU_BlitFrontSurface( smallGrayDrawSurface, 
+		                       SDLU_MRectToSDLRect( &grayMonitorZRect, &sourceSDLRect ),
+		                       SDLU_MRectToSDLRect( &grayMonitorRect[player], &destSDLRect ) );
+	}
+}
--- a/src/graymonitor.cpp
+++ /dev/null
@@ -1,90 +1,0 @@
-// graymonitor.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "graymonitor.h"
-#include "grays.h"
-#include "score.h"
-#include "gworld.h"
-#include "graphics.h"
-#include "level.h"
-
-
-static SDL_Surface* smallGrayDrawSurface;
-
-
-MRect grayMonitorZRect, grayMonitorRect[2];
-MBoolean grayMonitorVisible[2] = {true, true};
-
-void InitGrayMonitors( void )
-{
-	const double windowLoc[ ] = { 0.16, 0.84 };
-	SDL_Rect     sdlRect;
-			 
-	grayMonitorZRect.top = grayMonitorZRect.left = 0;
-	grayMonitorZRect.bottom = 32; grayMonitorZRect.right = 144;
-	
-	grayMonitorRect[0] = grayMonitorRect[1] = grayMonitorZRect;
-	CenterRectOnScreen( &grayMonitorRect[0], windowLoc[0], 0.11 );
-	CenterRectOnScreen( &grayMonitorRect[1], windowLoc[1], 0.11 );
-
-	smallGrayDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &grayMonitorZRect, &sdlRect ), 32 );
-	DrawPICTInSurface( smallGrayDrawSurface, picBoard );
-}
-
-void ShowGrayMonitor( short player )
-{
-	SDL_Rect   sourceSDLRect, destSDLRect;
-	short      monitor;
-	MRect      myRect = { 4, 4, kBlobVertSize+4, 4 };
-	MRect      srcRect;
-	const int  smallGrayList[] = { 0, kSmallGray1, kSmallGray2, kSmallGray3, kSmallGray4, kSmallGray5 };
-	
-	if( !grayMonitorVisible[player] ) return;
-	
-	if( control[player] != kNobodyControl )
-	{
-		SDLU_AcquireSurface( smallGrayDrawSurface );
-		
-		SDLU_BlitSurface( boardSurface[player], &smallGrayDrawSurface->clip_rect,
-						  smallGrayDrawSurface, &smallGrayDrawSurface->clip_rect  );
-	 				
-		monitor = unallocatedGrays[player];
-		
-		CalcBlobRect( kSobBlob, 3, &srcRect );
-		while( monitor >= (6*4) )
-		{
-			myRect.right += kBlobHorizSize;
-			SurfaceDrawSprite( &myRect, 4, kSobBlob );
-			myRect.left = myRect.right;
-			
-			monitor -= (6*4);
-		}
-		
-		CalcBlobRect( kNoSuction, kGray-1, &srcRect );
-		while( monitor >= 6 )
-		{
-			myRect.right += kBlobHorizSize;
-			SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
-			myRect.left = myRect.right;
-			
-			monitor -= 6;
-		}
-		
-		if( monitor > 0 )
-		{
-			myRect.right += kBlobHorizSize;
-			SurfaceDrawAlpha( &myRect, kGray, kLight, smallGrayList[monitor] );
-			myRect.left = myRect.right;
-			myRect.right += kBlobHorizSize;
-			SurfaceDrawAlpha( &myRect, kGray, kLight, smallGrayList[monitor]+1 );
-		}
-		
-		SDLU_ReleaseSurface( smallGrayDrawSurface );
-
-		SDLU_BlitFrontSurface( smallGrayDrawSurface, 
-		                       SDLU_MRectToSDLRect( &grayMonitorZRect, &sourceSDLRect ),
-		                       SDLU_MRectToSDLRect( &grayMonitorRect[player], &destSDLRect ) );
-	}
-}
--- /dev/null
+++ b/src/grays.c
@@ -1,0 +1,372 @@
+// grays.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "grays.h"
+#include "players.h"
+#include "graphics.h"
+#include "gworld.h"
+#include "control.h"
+#include "soundfx.h"
+#include "score.h"
+#include "random.h"
+#include "graymonitor.h"
+#include "gameticks.h"
+#include "blitter.h"
+#include "zap.h"
+#include "level.h"
+#include "opponent.h"
+
+int grays[2][kGridAcross], grayAir[2][kGridAcross];
+int unallocatedGrays[2], lockGrays[2], rowBounce[2][kGridAcross], splat[2][kGridAcross];
+MTicks blinkTime[2];
+int blinkStage[2];
+const int blinkList[] = { kGrayNoBlink, kGrayBlink1,  kGrayBlink2, 
+  						  kGrayBlink3,  kGrayBlink2,  kGrayBlink1   };
+
+void InitGrays( void )
+{
+	int player, gridX;
+	
+	blinkTime[0] = GameTickCount( );
+	blinkTime[1] = GameTickCount( ) + 60;
+	
+	for( player=0; player<=1; player++ )
+	{
+		blinkStage[player] = 0;
+		unallocatedGrays[player] = 0;
+		
+		for( gridX=0; gridX<kGridAcross; gridX++ )
+		{
+			grays[player][gridX] = 0;
+			rowBounce[player][gridX] = -1;
+		}
+	}	
+}
+
+void BlinkGrays( int player )
+{
+	int x, y, currentBlinkGraphic;
+	MRect myRect;
+	
+	if( blinkTime[player] > GameTickCount( ) )
+		return;
+	
+	blinkTime[player] += 2;
+	blinkStage[player]++;
+	
+	if( blinkStage[player] > 5 )
+	{
+		blinkStage[player] = 0;
+		blinkTime[player] = GameTickCount( ) + kTimeBetweenBlinks;
+	}
+	
+    currentBlinkGraphic = blinkList[ blinkStage[player] ];
+
+    SDLU_AcquireSurface( playerSurface[player] );
+    
+    for( x=0; x<kGridAcross; x++ )
+    {
+        if( rowBounce[player][x] == -1 )
+        {
+            for( y=kGridDown-1; y>=0; y-- )
+            {			
+                if( (grid[player][x][y] == kGray) &&
+                    (suction[player][x][y] == kGrayNoBlink) )
+                {
+                    CalcBlobRect( x, y, &myRect );
+                    SurfaceDrawBoard( player, &myRect );
+                    SurfaceDrawAlpha( &myRect, kGray, kLight, currentBlinkGraphic );
+                    CleanSpriteArea( player, &myRect );
+                }					
+            }
+        }
+    }
+    
+    SDLU_ReleaseSurface( playerSurface[player] );
+}
+
+void CalculateGrays( int player, int blobsToDrop )
+{
+	if( blobsToDrop < unallocatedGrays[1-player] )
+	{
+		unallocatedGrays[1-player] -= blobsToDrop;
+		lockGrays[1-player] -= blobsToDrop;
+		if( lockGrays[1-player] < 0 ) lockGrays[1-player] = 0;
+		
+		blobsToDrop = 0;
+		ShowGrayMonitor( 1-player );
+	}
+	else
+	{
+		blobsToDrop -= unallocatedGrays[1-player];
+		unallocatedGrays[1-player] = 0;
+		lockGrays[1-player] = 0;
+		ShowGrayMonitor( 1-player );
+		
+		unallocatedGrays[player] += blobsToDrop;
+		ShowGrayMonitor( player );
+	}
+}
+
+void LockGrays( int player )
+{
+	lockGrays[player] = unallocatedGrays[player];
+}
+
+void SetupGrays( int player )
+{
+	int grayX, change;
+	MBoolean onlyOnce[kGridAcross];
+	int rowFree[kGridAcross];
+	
+	if( role[player] == kDropGrays ) return; // next time around
+	
+	for( grayX=0; grayX < kGridAcross; grayX++ )
+	{
+		grayAir[player][grayX] = -RandomBefore(kBlobVertSize*3/2);
+		rowFree[grayX] = -1;
+		
+		while( grid[player][grayX][rowFree[grayX]+1] == kEmpty && 
+			   rowFree[grayX] < (kGridDown-1) )
+			rowFree[grayX]++;
+	}
+	
+	while( lockGrays[player] >= kGridAcross )
+	{
+		change = 0;
+		
+		for( grayX=0; grayX < kGridAcross; grayX++ )
+		{
+			if( rowFree[grayX] >= 0 )
+			{
+				grays[player][grayX]++;
+				//grayAir[player][grayX] -= kBlobVertSize;
+				rowFree[grayX]--;
+				change++;
+			}
+		}
+		
+		lockGrays[player] -= change;
+		unallocatedGrays[player] -= change;
+		
+		if( change == 0 ) break;
+	}
+	
+	if( lockGrays[player] > 0 )
+	{
+		for( grayX = 0; grayX < kGridAcross; grayX++ )
+		{
+			onlyOnce[grayX] = rowFree[grayX] > 0;
+		}
+		
+		while( (onlyOnce[0] || onlyOnce[1] || onlyOnce[2] ||
+			    onlyOnce[3] || onlyOnce[4] || onlyOnce[5]) &&
+			   (lockGrays[player] > 0) )
+		{
+			grayX = RandomBefore(kGridAcross);
+			
+			if( onlyOnce[grayX] )
+			{
+				grays[player][grayX]++;
+				//grayAir[player][grayX] -= kBlobVertSize;
+				lockGrays[player]--;
+				unallocatedGrays[player]--;
+				onlyOnce[grayX] = false;
+			}
+		}
+	}
+}
+
+void DropGrays( int player )
+{
+	MRect myRect;
+	int count, grayX;
+	
+	if( blobTime[player] > GameTickCount( ) )
+		return;
+		
+	blobTime[player]++;
+	
+	for( grayX = 0; grayX < kGridAcross; grayX++ )
+	{
+		if( grays[player][grayX] > 0 )
+		{
+			myRect.bottom = grayAir[player][grayX];
+			myRect.left = kBlobHorizSize * grayX;
+			myRect.top = myRect.bottom - (kBlobVertSize * grays[player][grayX]);
+			myRect.right = myRect.left + kBlobHorizSize;
+			CleanSpriteArea( player, &myRect );
+			
+			grayAir[player][grayX] += 4;
+			if( grayAir[player][grayX] > 0 ) grayAir[player][grayX] += grayAir[player][grayX] / 12;
+			
+			if( ( grayAir[player][grayX] / kBlobVertSize ) >= GetRowHeight( player, grayX ) )
+			{
+				PlaceGrayRow( player, grayX );
+				ShowGrayMonitor( player );
+			}
+			else
+			{
+				myRect.top = grayAir[player][grayX];
+				myRect.bottom = myRect.top + kBlobVertSize;
+				
+				SDLU_AcquireSurface( playerSpriteSurface[player] );
+
+				for( count=0; count<grays[player][grayX]; count++ )
+				{
+					OffsetMRect( &myRect, 0, -kBlobVertSize );
+					CleanSpriteArea( player, &myRect );
+					SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );					
+				}
+
+				SDLU_ReleaseSurface( playerSpriteSurface[player] );
+			}
+		}
+	}
+	
+	Bounce( player );
+		
+	if( !BusyDroppingGrays( player ) )
+	{
+		role[player] = kWaitForRetrieval;
+	}
+}
+
+MBoolean BusyDroppingGrays( int player )
+{
+	int grayX;
+	
+	for( grayX = 0; grayX<kGridAcross; grayX++ )
+	{
+		if( rowBounce[player][grayX] >= 0 ||
+			grays[player][grayX] > 0 ) 
+		{
+			return true;
+		} 
+	}
+
+	return false;
+}
+
+void PlaceGrayRow( int player, int grayX )
+{
+	int grayY;
+	MRect myRect;
+
+	if( player == 1 ) OpponentPissed( );
+	
+	rowBounce[player][grayX] = 0;
+	splat[player][grayX] = GetRowHeight( player, grayX )+1;
+	
+	SDLU_AcquireSurface( playerSurface[player] );
+	
+	while( grays[player][grayX] > 0 )
+	{
+		grayY = GetRowHeight( player, grayX );
+				
+		grid[player][grayX][grayY] = kGray;
+		suction[player][grayX][grayY] = kGrayNoBlink;
+		
+		CalcBlobRect( grayX, grayY, &myRect );
+		SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
+		CleanSpriteArea( player, &myRect );
+		
+		grays[player][grayX]--;
+	}
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+	
+	PlayStereoFrequency( player, kBounce, player );
+}
+
+void Bounce( int player )
+{
+	int x, y, bounce, suck, blob, rows, currentBlinkGraphic;
+	MRect blobRect;
+	double blobTop, compress;
+	const double compressList[kZapFrames+1] = { 
+							 -kBlobVertSize + 0.83,
+							 -kBlobVertSize + 1.58,
+							 -kBlobVertSize + 2.22,
+							 -kBlobVertSize + 2.76,
+							 -kBlobVertSize + 3.20,
+							 -kBlobVertSize + 3.55, 
+							 -kBlobVertSize + 3.80,
+							 -kBlobVertSize + 3.95,  
+							 -kBlobVertSize + 4.00,
+							 -kBlobVertSize + 3.95,  
+							 -kBlobVertSize + 3.80,
+							 -kBlobVertSize + 3.55, 
+							 -kBlobVertSize + 3.20,
+							 -kBlobVertSize + 2.76,
+							 -kBlobVertSize + 2.22,
+							 -kBlobVertSize + 1.58,
+							 -kBlobVertSize + 0.83,
+							 -kBlobVertSize,
+							 -kBlobVertSize,
+							 -kBlobVertSize,
+							 -kBlobVertSize
+						   };
+	
+    currentBlinkGraphic = blinkList[ blinkStage[player] ];
+    
+    SDLU_AcquireSurface( playerSpriteSurface[player] );
+    
+    for( x=0; x<kGridAcross; x++ )
+    {
+        if( rowBounce[player][x] >= 0 ) CleanSplat( player, x, splat[player][x], rowBounce[player][x] );
+    }
+    
+    for( x=0; x<kGridAcross; x++ )
+    {
+        bounce = rowBounce[player][x];
+        if( bounce >= 0 )
+        {
+            compress = compressList[bounce];
+            rows = GetRowHeight( player, x );
+            
+            CalcBlobRect( x, rows, &blobRect );
+            blobRect.bottom = kBlobVertSize * kGridDown;
+            SurfaceDrawBoard( player, &blobRect );
+            SetUpdateRect( player, &blobRect );
+            
+            blobRect.top = kBlobVertSize * (kGridDown-1);
+            blobTop = kBlobVertSize * (kGridDown-1);
+            
+            for( y=kGridDown-1; y>=rows; y-- )
+            {
+                suck = suction[player][x][y];
+                blob = grid[player][x][y];
+                if( suck == kNoSuction && blob >= kFirstBlob &&
+                    blob <= kLastBlob && compress > (-kBlobVertSize + 3) )
+                {
+                    suck = kSquish;
+                }
+                
+                if( blob == kGray )	SurfaceDrawAlpha( &blobRect, kGray, kLight, currentBlinkGraphic );
+                else SurfaceDrawBlob( player, &blobRect, blob, suck, charred[player][x][y] );
+                
+                blobTop += compress;
+                blobRect.top = (short) blobTop;
+                blobRect.bottom = blobRect.top + kBlobVertSize;
+            }
+        }
+    }
+
+    for( x=0; x<kGridAcross; x++ )
+    {
+        if( rowBounce[player][x] >= 0 ) DrawSplat( player, x, splat[player][x], rowBounce[player][x] );
+    }
+    
+    SDLU_ReleaseSurface( playerSpriteSurface[player] );
+	
+	for( x=0; x<kGridAcross; x++ )
+	{
+		if( rowBounce[player][x] >= 0 && rowBounce[player][x] < (kZapFrames) )
+			rowBounce[player][x]++;
+		else
+			rowBounce[player][x] = -1;
+	}
+}
--- a/src/grays.cpp
+++ /dev/null
@@ -1,372 +1,0 @@
-// grays.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "grays.h"
-#include "players.h"
-#include "graphics.h"
-#include "gworld.h"
-#include "control.h"
-#include "soundfx.h"
-#include "score.h"
-#include "random.h"
-#include "graymonitor.h"
-#include "gameticks.h"
-#include "blitter.h"
-#include "zap.h"
-#include "level.h"
-#include "opponent.h"
-
-int grays[2][kGridAcross], grayAir[2][kGridAcross];
-int unallocatedGrays[2], lockGrays[2], rowBounce[2][kGridAcross], splat[2][kGridAcross];
-MTicks blinkTime[2];
-int blinkStage[2];
-const int blinkList[] = { kGrayNoBlink, kGrayBlink1,  kGrayBlink2, 
-  						  kGrayBlink3,  kGrayBlink2,  kGrayBlink1   };
-
-void InitGrays( void )
-{
-	int player, gridX;
-	
-	blinkTime[0] = GameTickCount( );
-	blinkTime[1] = GameTickCount( ) + 60;
-	
-	for( player=0; player<=1; player++ )
-	{
-		blinkStage[player] = 0;
-		unallocatedGrays[player] = 0;
-		
-		for( gridX=0; gridX<kGridAcross; gridX++ )
-		{
-			grays[player][gridX] = 0;
-			rowBounce[player][gridX] = -1;
-		}
-	}	
-}
-
-void BlinkGrays( int player )
-{
-	int x, y, currentBlinkGraphic;
-	MRect myRect;
-	
-	if( blinkTime[player] > GameTickCount( ) )
-		return;
-	
-	blinkTime[player] += 2;
-	blinkStage[player]++;
-	
-	if( blinkStage[player] > 5 )
-	{
-		blinkStage[player] = 0;
-		blinkTime[player] = GameTickCount( ) + kTimeBetweenBlinks;
-	}
-	
-    currentBlinkGraphic = blinkList[ blinkStage[player] ];
-
-    SDLU_AcquireSurface( playerSurface[player] );
-    
-    for( x=0; x<kGridAcross; x++ )
-    {
-        if( rowBounce[player][x] == -1 )
-        {
-            for( y=kGridDown-1; y>=0; y-- )
-            {			
-                if( (grid[player][x][y] == kGray) &&
-                    (suction[player][x][y] == kGrayNoBlink) )
-                {
-                    CalcBlobRect( x, y, &myRect );
-                    SurfaceDrawBoard( player, &myRect );
-                    SurfaceDrawAlpha( &myRect, kGray, kLight, currentBlinkGraphic );
-                    CleanSpriteArea( player, &myRect );
-                }					
-            }
-        }
-    }
-    
-    SDLU_ReleaseSurface( playerSurface[player] );
-}
-
-void CalculateGrays( int player, int blobsToDrop )
-{
-	if( blobsToDrop < unallocatedGrays[1-player] )
-	{
-		unallocatedGrays[1-player] -= blobsToDrop;
-		lockGrays[1-player] -= blobsToDrop;
-		if( lockGrays[1-player] < 0 ) lockGrays[1-player] = 0;
-		
-		blobsToDrop = 0;
-		ShowGrayMonitor( 1-player );
-	}
-	else
-	{
-		blobsToDrop -= unallocatedGrays[1-player];
-		unallocatedGrays[1-player] = 0;
-		lockGrays[1-player] = 0;
-		ShowGrayMonitor( 1-player );
-		
-		unallocatedGrays[player] += blobsToDrop;
-		ShowGrayMonitor( player );
-	}
-}
-
-void LockGrays( int player )
-{
-	lockGrays[player] = unallocatedGrays[player];
-}
-
-void SetupGrays( int player )
-{
-	int grayX, change;
-	MBoolean onlyOnce[kGridAcross];
-	int rowFree[kGridAcross];
-	
-	if( role[player] == kDropGrays ) return; // next time around
-	
-	for( grayX=0; grayX < kGridAcross; grayX++ )
-	{
-		grayAir[player][grayX] = -RandomBefore(kBlobVertSize*3/2);
-		rowFree[grayX] = -1;
-		
-		while( grid[player][grayX][rowFree[grayX]+1] == kEmpty && 
-			   rowFree[grayX] < (kGridDown-1) )
-			rowFree[grayX]++;
-	}
-	
-	while( lockGrays[player] >= kGridAcross )
-	{
-		change = 0;
-		
-		for( grayX=0; grayX < kGridAcross; grayX++ )
-		{
-			if( rowFree[grayX] >= 0 )
-			{
-				grays[player][grayX]++;
-				//grayAir[player][grayX] -= kBlobVertSize;
-				rowFree[grayX]--;
-				change++;
-			}
-		}
-		
-		lockGrays[player] -= change;
-		unallocatedGrays[player] -= change;
-		
-		if( change == 0 ) break;
-	}
-	
-	if( lockGrays[player] > 0 )
-	{
-		for( grayX = 0; grayX < kGridAcross; grayX++ )
-		{
-			onlyOnce[grayX] = rowFree[grayX] > 0;
-		}
-		
-		while( (onlyOnce[0] || onlyOnce[1] || onlyOnce[2] ||
-			    onlyOnce[3] || onlyOnce[4] || onlyOnce[5]) &&
-			   (lockGrays[player] > 0) )
-		{
-			grayX = RandomBefore(kGridAcross);
-			
-			if( onlyOnce[grayX] )
-			{
-				grays[player][grayX]++;
-				//grayAir[player][grayX] -= kBlobVertSize;
-				lockGrays[player]--;
-				unallocatedGrays[player]--;
-				onlyOnce[grayX] = false;
-			}
-		}
-	}
-}
-
-void DropGrays( int player )
-{
-	MRect myRect;
-	int count, grayX;
-	
-	if( blobTime[player] > GameTickCount( ) )
-		return;
-		
-	blobTime[player]++;
-	
-	for( grayX = 0; grayX < kGridAcross; grayX++ )
-	{
-		if( grays[player][grayX] > 0 )
-		{
-			myRect.bottom = grayAir[player][grayX];
-			myRect.left = kBlobHorizSize * grayX;
-			myRect.top = myRect.bottom - (kBlobVertSize * grays[player][grayX]);
-			myRect.right = myRect.left + kBlobHorizSize;
-			CleanSpriteArea( player, &myRect );
-			
-			grayAir[player][grayX] += 4;
-			if( grayAir[player][grayX] > 0 ) grayAir[player][grayX] += grayAir[player][grayX] / 12;
-			
-			if( ( grayAir[player][grayX] / kBlobVertSize ) >= GetRowHeight( player, grayX ) )
-			{
-				PlaceGrayRow( player, grayX );
-				ShowGrayMonitor( player );
-			}
-			else
-			{
-				myRect.top = grayAir[player][grayX];
-				myRect.bottom = myRect.top + kBlobVertSize;
-				
-				SDLU_AcquireSurface( playerSpriteSurface[player] );
-
-				for( count=0; count<grays[player][grayX]; count++ )
-				{
-					OffsetMRect( &myRect, 0, -kBlobVertSize );
-					CleanSpriteArea( player, &myRect );
-					SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );					
-				}
-
-				SDLU_ReleaseSurface( playerSpriteSurface[player] );
-			}
-		}
-	}
-	
-	Bounce( player );
-		
-	if( !BusyDroppingGrays( player ) )
-	{
-		role[player] = kWaitForRetrieval;
-	}
-}
-
-MBoolean BusyDroppingGrays( int player )
-{
-	int grayX;
-	
-	for( grayX = 0; grayX<kGridAcross; grayX++ )
-	{
-		if( rowBounce[player][grayX] >= 0 ||
-			grays[player][grayX] > 0 ) 
-		{
-			return true;
-		} 
-	}
-
-	return false;
-}
-
-void PlaceGrayRow( int player, int grayX )
-{
-	int grayY;
-	MRect myRect;
-
-	if( player == 1 ) OpponentPissed( );
-	
-	rowBounce[player][grayX] = 0;
-	splat[player][grayX] = GetRowHeight( player, grayX )+1;
-	
-	SDLU_AcquireSurface( playerSurface[player] );
-	
-	while( grays[player][grayX] > 0 )
-	{
-		grayY = GetRowHeight( player, grayX );
-				
-		grid[player][grayX][grayY] = kGray;
-		suction[player][grayX][grayY] = kGrayNoBlink;
-		
-		CalcBlobRect( grayX, grayY, &myRect );
-		SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
-		CleanSpriteArea( player, &myRect );
-		
-		grays[player][grayX]--;
-	}
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-	
-	PlayStereoFrequency( player, kBounce, player );
-}
-
-void Bounce( int player )
-{
-	int x, y, bounce, suck, blob, rows, currentBlinkGraphic;
-	MRect blobRect;
-	double blobTop, compress;
-	const double compressList[kZapFrames+1] = { 
-							 -kBlobVertSize + 0.83,
-							 -kBlobVertSize + 1.58,
-							 -kBlobVertSize + 2.22,
-							 -kBlobVertSize + 2.76,
-							 -kBlobVertSize + 3.20,
-							 -kBlobVertSize + 3.55, 
-							 -kBlobVertSize + 3.80,
-							 -kBlobVertSize + 3.95,  
-							 -kBlobVertSize + 4.00,
-							 -kBlobVertSize + 3.95,  
-							 -kBlobVertSize + 3.80,
-							 -kBlobVertSize + 3.55, 
-							 -kBlobVertSize + 3.20,
-							 -kBlobVertSize + 2.76,
-							 -kBlobVertSize + 2.22,
-							 -kBlobVertSize + 1.58,
-							 -kBlobVertSize + 0.83,
-							 -kBlobVertSize,
-							 -kBlobVertSize,
-							 -kBlobVertSize,
-							 -kBlobVertSize
-						   };
-	
-    currentBlinkGraphic = blinkList[ blinkStage[player] ];
-    
-    SDLU_AcquireSurface( playerSpriteSurface[player] );
-    
-    for( x=0; x<kGridAcross; x++ )
-    {
-        if( rowBounce[player][x] >= 0 ) CleanSplat( player, x, splat[player][x], rowBounce[player][x] );
-    }
-    
-    for( x=0; x<kGridAcross; x++ )
-    {
-        bounce = rowBounce[player][x];
-        if( bounce >= 0 )
-        {
-            compress = compressList[bounce];
-            rows = GetRowHeight( player, x );
-            
-            CalcBlobRect( x, rows, &blobRect );
-            blobRect.bottom = kBlobVertSize * kGridDown;
-            SurfaceDrawBoard( player, &blobRect );
-            SetUpdateRect( player, &blobRect );
-            
-            blobRect.top = kBlobVertSize * (kGridDown-1);
-            blobTop = kBlobVertSize * (kGridDown-1);
-            
-            for( y=kGridDown-1; y>=rows; y-- )
-            {
-                suck = suction[player][x][y];
-                blob = grid[player][x][y];
-                if( suck == kNoSuction && blob >= kFirstBlob &&
-                    blob <= kLastBlob && compress > (-kBlobVertSize + 3) )
-                {
-                    suck = kSquish;
-                }
-                
-                if( blob == kGray )	SurfaceDrawAlpha( &blobRect, kGray, kLight, currentBlinkGraphic );
-                else SurfaceDrawBlob( player, &blobRect, blob, suck, charred[player][x][y] );
-                
-                blobTop += compress;
-                blobRect.top = (short) blobTop;
-                blobRect.bottom = blobRect.top + kBlobVertSize;
-            }
-        }
-    }
-
-    for( x=0; x<kGridAcross; x++ )
-    {
-        if( rowBounce[player][x] >= 0 ) DrawSplat( player, x, splat[player][x], rowBounce[player][x] );
-    }
-    
-    SDLU_ReleaseSurface( playerSpriteSurface[player] );
-	
-	for( x=0; x<kGridAcross; x++ )
-	{
-		if( rowBounce[player][x] >= 0 && rowBounce[player][x] < (kZapFrames) )
-			rowBounce[player][x]++;
-		else
-			rowBounce[player][x] = -1;
-	}
-}
--- /dev/null
+++ b/src/gworld.c
@@ -1,0 +1,243 @@
+// gworld.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "gworld.h"
+#include "blitter.h"
+#include "graphics.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define STBI_ONLY_JPEG
+#define STBI_ONLY_PNG
+#define STB_IMAGE_IMPLEMENTATION
+#include "support/stb_image.h"
+
+SDL_Surface* blobSurface;
+SDL_Surface* maskSurface;
+SDL_Surface* charMaskSurface;
+SDL_Surface* boardSurface[2];
+SDL_Surface* blastSurface;
+SDL_Surface* blastMaskSurface;
+SDL_Surface* playerSurface[2];
+SDL_Surface* playerSpriteSurface[2];
+
+
+void GetBlobGraphics()
+{
+	MRect myRect;
+	
+	// Get board
+	
+	myRect.top = myRect.left = 0;
+	myRect.right = kBlobHorizSize * kGridAcross;
+	myRect.bottom = kBlobVertSize * (kGridDown-1);
+	
+	boardSurface[0] = LoadPICTAsSurface( picBoard, 32 );
+	
+	boardSurface[1] = LoadPICTAsSurface( picBoardRight, 32 );
+	if( boardSurface[1] == NULL )
+		boardSurface[1] = LoadPICTAsSurface( picBoard, 32 );
+	
+	// Get blob worlds
+	blobSurface = LoadPICTAsSurface( picBlob, 32 );
+	maskSurface = LoadPICTAsSurface( picBlobMask, MASK_DEPTH );
+	charMaskSurface = LoadPICTAsSurface( picCharMask, MASK_DEPTH );
+
+	// Get blast worlds
+	
+	blastSurface = LoadPICTAsSurface( picBlast, 32 );
+	blastMaskSurface = LoadPICTAsSurface( picBlastMask, 32 );
+}
+
+
+void InitPlayerWorlds()
+{
+	MRect     myRect;
+	SDL_Rect  sdlRect;
+	int       count;
+	
+	myRect.top = myRect.left = 0;
+	myRect.right = kGridAcross * kBlobHorizSize;
+	myRect.bottom = kGridDown * kBlobVertSize;
+	
+	SDLU_MRectToSDLRect( &myRect, &sdlRect );
+	
+	for( count=0; count<=1; count++ )
+	{
+		playerSurface[count]       = SDLU_InitSurface( &sdlRect, 32 );
+		playerSpriteSurface[count] = SDLU_InitSurface( &sdlRect, 32 );
+	}
+}
+
+
+void SurfaceDrawBoard( int player, const MRect *myRect )
+{
+	MRect    srcRect, offsetRect;
+	SDL_Rect srcSDLRect, offsetSDLRect;
+	
+	srcRect = *myRect;
+	if( srcRect.bottom <= kBlobVertSize ) return;
+	if( srcRect.top < kBlobVertSize ) srcRect.top = kBlobVertSize;
+
+	offsetRect = srcRect;
+	OffsetMRect( &offsetRect, 0, -kBlobVertSize );
+
+	SDLU_BlitSurface( boardSurface[player],     SDLU_MRectToSDLRect( &offsetRect, &offsetSDLRect ),
+	                  SDLU_GetCurrentSurface(), SDLU_MRectToSDLRect( &srcRect, &srcSDLRect )         );
+}
+
+
+void SurfaceDrawBlob( int player, const MRect *myRect, int blob, int state, int charred )
+{
+	SurfaceDrawBoard( player, myRect );
+	SurfaceDrawSprite( myRect, blob, state );
+
+	if( charred & 0x0F )
+	{
+		MRect blobRect, charRect, alphaRect;
+		
+		CalcBlobRect( (charred & 0x0F), kBombTop-1, &charRect );
+		CalcBlobRect( (charred & 0x0F), kBombBottom-1, &alphaRect );
+		CalcBlobRect( state, blob-1, &blobRect );
+
+		SurfaceBlitWeightedDualAlpha( SDLU_GetCurrentSurface(),  blobSurface,  charMaskSurface,  blobSurface,  SDLU_GetCurrentSurface(),
+                                       myRect,                   &charRect,    &blobRect,        &alphaRect,    myRect, 
+                                      (charred & 0xF0) );
+	}
+}
+
+void SurfaceDrawShadow( const MRect *myRect, int blob, int state )
+{
+	int x;
+	const MPoint offset[4] = { {-2, 0}, {0, -2}, {2, 0}, {0, 2} };
+	
+	if( blob > kEmpty )
+	{
+		MRect blobRect, destRect;
+
+		for( x=0; x<4; x++ )
+		{
+			destRect = *myRect;
+			OffsetMRect( &destRect, offset[x].h, offset[x].v );
+			
+			CalcBlobRect( state, blob-1, &blobRect );
+			SurfaceBlitColor( maskSurface,  SDLU_GetCurrentSurface(),
+			                  &blobRect,    &destRect, 
+			                   0, 0, 0, _5TO8(3) );
+		}
+	}
+}
+
+void SurfaceDrawColor( const MRect *myRect, int blob, int state, int r, int g, int b, int w )
+{
+	MRect blobRect;
+	if( blob > kEmpty )
+	{
+		CalcBlobRect( state, blob-1, &blobRect );
+		SurfaceBlitColor( charMaskSurface,  SDLU_GetCurrentSurface(),
+						  &blobRect,         myRect, 
+						   r, g, b, w );
+	}
+}
+
+void SurfaceDrawAlpha( const MRect *myRect, int blob, int mask, int state )
+{
+	if( blob > kEmpty )
+	{
+		MRect blobRect, alphaRect;
+
+		CalcBlobRect( state, blob-1, &blobRect );
+		CalcBlobRect( state, mask-1, &alphaRect );
+		
+		SurfaceBlitAlpha( SDLU_GetCurrentSurface(),  blobSurface,  blobSurface, SDLU_GetCurrentSurface(),
+		                  myRect,                   &blobRect,    &alphaRect,   myRect );
+	}
+}
+
+void SurfaceDrawSprite( const MRect *myRect, int blob, int state )
+{
+	MRect blobRect;
+	if( blob > kEmpty )
+	{
+		CalcBlobRect( state, blob-1, &blobRect );
+		SurfaceBlitBlob( &blobRect, myRect );
+	}
+}
+
+
+MBoolean PICTExists( int pictID )
+{
+	if( FileExists( QuickResourceName( "PICT", pictID, ".jpg" ) ) )
+		return true;
+
+	if( FileExists( QuickResourceName( "PICT", pictID, ".png" ) ) )
+		return true;
+	
+	return false;
+}
+
+
+SDL_Surface* LoadPICTAsSurface( int pictID, int depth )
+{
+	const char*  filename;
+	SDL_Surface* surface;
+    SDL_Rect     rect = {0};
+    uint8_t*     pixels = NULL;
+
+	filename = QuickResourceName( "PICT", pictID, ".jpg" );
+	if( !FileExists( filename ) )
+	{
+		filename = QuickResourceName( "PICT", pictID, ".png" );
+	}
+    if( !FileExists( filename ) )
+    {
+        return NULL;
+    }
+
+    pixels = stbi_load(filename, &rect.w, &rect.h, NULL, 3);
+
+    surface = SDLU_InitSurface(&rect, 32);
+    SDL_LockSurface(surface);
+
+    uint8_t* srcPixels = pixels;
+    uint8_t* destPixels = (uint8_t*) surface->pixels;
+    for (int i = 0; i < rect.w*rect.h; i++)
+    {
+        destPixels[0] = srcPixels[2];
+        destPixels[1] = srcPixels[1];
+        destPixels[2] = srcPixels[0];
+        destPixels[3] = 0xFF;
+        destPixels += 4;
+        srcPixels += 3;
+    }
+
+    SDL_UnlockSurface(surface);
+
+    free(pixels);
+    pixels = NULL;
+
+	if( depth != 0 )
+	{
+		SDLU_ChangeSurfaceDepth( &surface, depth );
+	}
+	
+	return surface;
+}
+
+void DrawPICTInSurface( SDL_Surface* surface, int pictID )
+{
+	SDL_Surface* image;
+	
+	image = LoadPICTAsSurface( pictID, 0 );
+	if( image != NULL )
+	{
+		SDLU_BlitSurface( image,    &image->clip_rect,
+		                  surface,  &surface->clip_rect );
+		                 
+		SDL_FreeSurface( image );
+	}
+}
--- a/src/gworld.cpp
+++ /dev/null
@@ -1,243 +1,0 @@
-// gworld.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "gworld.h"
-#include "blitter.h"
-#include "graphics.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#define STBI_ONLY_JPEG
-#define STBI_ONLY_PNG
-#define STB_IMAGE_IMPLEMENTATION
-#include "support/stb_image.h"
-
-SDL_Surface* blobSurface;
-SDL_Surface* maskSurface;
-SDL_Surface* charMaskSurface;
-SDL_Surface* boardSurface[2];
-SDL_Surface* blastSurface;
-SDL_Surface* blastMaskSurface;
-SDL_Surface* playerSurface[2];
-SDL_Surface* playerSpriteSurface[2];
-
-
-void GetBlobGraphics()
-{
-	MRect myRect;
-	
-	// Get board
-	
-	myRect.top = myRect.left = 0;
-	myRect.right = kBlobHorizSize * kGridAcross;
-	myRect.bottom = kBlobVertSize * (kGridDown-1);
-	
-	boardSurface[0] = LoadPICTAsSurface( picBoard, 32 );
-	
-	boardSurface[1] = LoadPICTAsSurface( picBoardRight, 32 );
-	if( boardSurface[1] == NULL )
-		boardSurface[1] = LoadPICTAsSurface( picBoard, 32 );
-	
-	// Get blob worlds
-	blobSurface = LoadPICTAsSurface( picBlob, 32 );
-	maskSurface = LoadPICTAsSurface( picBlobMask, MASK_DEPTH );
-	charMaskSurface = LoadPICTAsSurface( picCharMask, MASK_DEPTH );
-
-	// Get blast worlds
-	
-	blastSurface = LoadPICTAsSurface( picBlast, 32 );
-	blastMaskSurface = LoadPICTAsSurface( picBlastMask, 32 );
-}
-
-
-void InitPlayerWorlds()
-{
-	MRect     myRect;
-	SDL_Rect  sdlRect;
-	int       count;
-	
-	myRect.top = myRect.left = 0;
-	myRect.right = kGridAcross * kBlobHorizSize;
-	myRect.bottom = kGridDown * kBlobVertSize;
-	
-	SDLU_MRectToSDLRect( &myRect, &sdlRect );
-	
-	for( count=0; count<=1; count++ )
-	{
-		playerSurface[count]       = SDLU_InitSurface( &sdlRect, 32 );
-		playerSpriteSurface[count] = SDLU_InitSurface( &sdlRect, 32 );
-	}
-}
-
-
-void SurfaceDrawBoard( int player, const MRect *myRect )
-{
-	MRect    srcRect, offsetRect;
-	SDL_Rect srcSDLRect, offsetSDLRect;
-	
-	srcRect = *myRect;
-	if( srcRect.bottom <= kBlobVertSize ) return;
-	if( srcRect.top < kBlobVertSize ) srcRect.top = kBlobVertSize;
-
-	offsetRect = srcRect;
-	OffsetMRect( &offsetRect, 0, -kBlobVertSize );
-
-	SDLU_BlitSurface( boardSurface[player],     SDLU_MRectToSDLRect( &offsetRect, &offsetSDLRect ),
-	                  SDLU_GetCurrentSurface(), SDLU_MRectToSDLRect( &srcRect, &srcSDLRect )         );
-}
-
-
-void SurfaceDrawBlob( int player, const MRect *myRect, int blob, int state, int charred )
-{
-	SurfaceDrawBoard( player, myRect );
-	SurfaceDrawSprite( myRect, blob, state );
-
-	if( charred & 0x0F )
-	{
-		MRect blobRect, charRect, alphaRect;
-		
-		CalcBlobRect( (charred & 0x0F), kBombTop-1, &charRect );
-		CalcBlobRect( (charred & 0x0F), kBombBottom-1, &alphaRect );
-		CalcBlobRect( state, blob-1, &blobRect );
-
-		SurfaceBlitWeightedDualAlpha( SDLU_GetCurrentSurface(),  blobSurface,  charMaskSurface,  blobSurface,  SDLU_GetCurrentSurface(),
-                                       myRect,                   &charRect,    &blobRect,        &alphaRect,    myRect, 
-                                      (charred & 0xF0) );
-	}
-}
-
-void SurfaceDrawShadow( const MRect *myRect, int blob, int state )
-{
-	int x;
-	const MPoint offset[4] = { {-2, 0}, {0, -2}, {2, 0}, {0, 2} };
-	
-	if( blob > kEmpty )
-	{
-		MRect blobRect, destRect;
-
-		for( x=0; x<4; x++ )
-		{
-			destRect = *myRect;
-			OffsetMRect( &destRect, offset[x].h, offset[x].v );
-			
-			CalcBlobRect( state, blob-1, &blobRect );
-			SurfaceBlitColor( maskSurface,  SDLU_GetCurrentSurface(),
-			                  &blobRect,    &destRect, 
-			                   0, 0, 0, _5TO8(3) );
-		}
-	}
-}
-
-void SurfaceDrawColor( const MRect *myRect, int blob, int state, int r, int g, int b, int w )
-{
-	MRect blobRect;
-	if( blob > kEmpty )
-	{
-		CalcBlobRect( state, blob-1, &blobRect );
-		SurfaceBlitColor( charMaskSurface,  SDLU_GetCurrentSurface(),
-						  &blobRect,         myRect, 
-						   r, g, b, w );
-	}
-}
-
-void SurfaceDrawAlpha( const MRect *myRect, int blob, int mask, int state )
-{
-	if( blob > kEmpty )
-	{
-		MRect blobRect, alphaRect;
-
-		CalcBlobRect( state, blob-1, &blobRect );
-		CalcBlobRect( state, mask-1, &alphaRect );
-		
-		SurfaceBlitAlpha( SDLU_GetCurrentSurface(),  blobSurface,  blobSurface, SDLU_GetCurrentSurface(),
-		                  myRect,                   &blobRect,    &alphaRect,   myRect );
-	}
-}
-
-void SurfaceDrawSprite( const MRect *myRect, int blob, int state )
-{
-	MRect blobRect;
-	if( blob > kEmpty )
-	{
-		CalcBlobRect( state, blob-1, &blobRect );
-		SurfaceBlitBlob( &blobRect, myRect );
-	}
-}
-
-
-MBoolean PICTExists( int pictID )
-{
-	if( FileExists( QuickResourceName( "PICT", pictID, ".jpg" ) ) )
-		return true;
-
-	if( FileExists( QuickResourceName( "PICT", pictID, ".png" ) ) )
-		return true;
-	
-	return false;
-}
-
-
-SDL_Surface* LoadPICTAsSurface( int pictID, int depth )
-{
-	const char*  filename;
-	SDL_Surface* surface;
-    SDL_Rect     rect = {};
-    uint8_t*     pixels = nullptr;
-
-	filename = QuickResourceName( "PICT", pictID, ".jpg" );
-	if( !FileExists( filename ) )
-	{
-		filename = QuickResourceName( "PICT", pictID, ".png" );
-	}
-    if( !FileExists( filename ) )
-    {
-        return nullptr;
-    }
-
-    pixels = stbi_load(filename, &rect.w, &rect.h, NULL, 3);
-
-    surface = SDLU_InitSurface(&rect, 32);
-    SDL_LockSurface(surface);
-
-    uint8_t* srcPixels = pixels;
-    uint8_t* destPixels = (uint8_t*) surface->pixels;
-    for (int i = 0; i < rect.w*rect.h; i++)
-    {
-        destPixels[0] = srcPixels[2];
-        destPixels[1] = srcPixels[1];
-        destPixels[2] = srcPixels[0];
-        destPixels[3] = 0xFF;
-        destPixels += 4;
-        srcPixels += 3;
-    }
-
-    SDL_UnlockSurface(surface);
-
-    free(pixels);
-    pixels = NULL;
-
-	if( depth != 0 )
-	{
-		SDLU_ChangeSurfaceDepth( &surface, depth );
-	}
-	
-	return surface;
-}
-
-void DrawPICTInSurface( SDL_Surface* surface, int pictID )
-{
-	SDL_Surface* image;
-	
-	image = LoadPICTAsSurface( pictID, 0 );
-	if( image != NULL )
-	{
-		SDLU_BlitSurface( image,    &image->clip_rect,
-		                  surface,  &surface->clip_rect );
-		                 
-		SDL_FreeSurface( image );
-	}
-}
--- /dev/null
+++ b/src/hiscore.c
@@ -1,0 +1,498 @@
+// hiscore.c
+
+#include "SDLU.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "main.h"
+#include "gworld.h"
+#include "graphics.h"
+#include "score.h"
+#include "hiscore.h"
+#include "keyselect.h"
+#include "font.h"
+#include "blitter.h"
+#include "random.h"
+#include "pause.h"
+#include "level.h"
+#include "tutorial.h"
+#include "graymonitor.h"
+#include "players.h"
+#include "gameticks.h"
+#include "music.h"
+#include "soundfx.h"
+
+Combo defaultBest = 
+{
+	/*bestGrid[kGridAcross][kGridDown] = */ 
+	{ { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,                          1, 1, 1, 2, 2 },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,           1, 1 },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty } },
+
+	/*bestA      = */ 2,
+	/*bestB      = */ 2,
+	/*bestM      = */ false, 
+	/*bestG      = */ false, 
+	/*bestLv     = */ kTutorialLevel,
+	/*bestX      = */ 1,
+	/*bestR      = */ upRotate,
+	/*bestPlayer = */ 0,
+	/*bestValue  = */ (40*1) + (50*9),
+	/*bestName   = */ "Tutorial"
+};
+
+Combo best = 
+{
+	/*bestGrid[kGridAcross][kGridDown] = */ 
+	{ { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,                          1, 1, 1, 2, 2 },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,           1, 1 },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
+	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty } },
+
+	/*bestA      = */ 2,
+	/*bestB      = */ 2,
+	/*bestM      = */ false, 
+	/*bestG      = */ false, 
+	/*bestLv     = */ kTutorialLevel,
+	/*bestX      = */ 1,
+	/*bestR      = */ upRotate,
+	/*bestPlayer = */ 0,
+	/*bestValue  = */ (40*1) + (50*9),
+	/*bestName   = */ "Tutorial"
+};
+
+Combo evenBetter = {0};
+Combo potentialCombo[2];
+
+AutoPattern hiScorePattern[] =
+{
+	{ kIdleTicks,          60, 0,   NULL },	
+	{ kBlockUntilDrop,     0,  0,   NULL },	
+	{ kBlockUntilComplete, 0,  0,   NULL },	
+	{ kIdleTicks,          60, 0,   NULL },	
+	{ kComplete,           0,  0,   NULL }
+};
+
+HighScore scores[10] = 
+{	
+	{"Leviathan", 40000},
+	{"Dr. Crisis", 36000},
+	{"Angel", 32000},
+	{"Spike", 28000},
+	{"Fox", 24000},
+	{"Raguel", 20000},
+	{"Kumo", 16000},
+	{"Patty", 12000},
+	{"Yuurei", 8000},
+	{"Glurp", 4000}  
+};
+
+HighScore defaultScores[10] = 
+{	
+	{"Leviathan", 40000},
+	{"Dr. Crisis", 36000},
+	{"Angel", 32000},
+	{"Spike", 28000},
+	{"Fox", 24000},
+	{"Raguel", 20000},
+	{"Kumo", 16000},
+	{"Patty", 12000},
+	{"Yuurei", 8000},
+	{"Glurp", 4000}  
+};
+
+char highScoreName[256];
+const char *highScoreText;
+const char *highScoreRank;
+
+static void FadeScreen( SDL_Surface* hiScoreSurface, SDL_Surface* fadeSurface, int start, int end )
+{
+	MTicks    timer;
+	int       skip, frame, fade, color, direction, fadeStart, fadeEnd;
+	SDL_Rect  destSDLRect;
+	SDL_Rect  fullSDLRect = { 0, 0, 640, 480 };
+	int       black;
+	
+	black = SDL_MapRGB( fadeSurface->format, 0, 0, 0 );
+	
+	if( start < end )
+	{
+		direction = 1;
+		fadeStart = 0;
+		fadeEnd = 32;
+	}
+	else
+	{
+		direction = -1;
+		fadeStart = 32;
+		fadeEnd = 0;
+	}
+	
+	skip = 1;
+	timer = MTickCount( ) + 1;
+	while( timer > MTickCount() ) { }
+
+	for( frame = start; (direction>0)? (frame <= end): (frame >= end); frame += direction )
+	{
+		MRect drawRect = {0, 0, 15, 640};
+		timer += skip;
+		
+		for( fade = fadeStart; fade != fadeEnd; fade += direction )
+		{
+			color = frame + fade;
+			if( color <  0 ) color = 0;
+			if( color > 31 ) color = 31;
+			
+			SDLU_MRectToSDLRect( &drawRect, &destSDLRect );
+
+			switch( color )
+			{
+				case 0:
+					SDLU_BlitSurface( hiScoreSurface, &destSDLRect,
+					                  fadeSurface,    &destSDLRect  ); 
+					break;
+			
+				case 31:
+					SDL_FillRect( fadeSurface, &destSDLRect, black );
+					break;
+				
+				default:
+					SurfaceBlitColorOver( hiScoreSurface,  fadeSurface,
+					                      &drawRect,       &drawRect,
+					                       0, 0, 0, _5TO8(color) );
+					break;
+			}
+			
+			OffsetMRect( &drawRect, 0, 15 );
+		}
+
+		SDLU_BlitFrontSurface( fadeSurface, &fullSDLRect, &fullSDLRect );
+        SDLU_Present();
+
+		if( timer <= MTickCount( ) )
+		{
+			skip = 2;
+		}
+		else
+		{
+			skip = 1;
+			while( timer > MTickCount( ) )
+			{
+				SDLU_Yield();
+			}
+		}
+	}
+}
+
+void ShowHiscore( void )
+{
+	short            count, length;
+	char             myString[256];
+	int              stringLength;
+	SDL_Surface*     hiScoreSurface;
+	SDL_Surface*     fadeSurface;
+	SDL_Rect         fullSDLRect = { 0, 0, 640, 480 };
+	SkittlesFontPtr  font;
+	SDL_Color        anyColor;
+	MPoint           dPoint;
+	const char*      highScores = "HIGH SCORES";
+	int              r, g, b;
+	
+	if (DeleteKeyIsPressed())
+	{
+		// If the user holds delete while opening the high scores,
+		// clear the high score table.
+		
+		memcpy( &scores, &defaultScores, sizeof( scores ) );
+		memcpy( &best,   &defaultBest,   sizeof( best   ) );
+	}
+	
+	hiScoreSurface = LoadPICTAsSurface( picBackdrop + (100 * RandomBefore(kLevels)), 32 );
+	fadeSurface    = SDLU_InitSurface( &fullSDLRect, 32 );
+
+
+	SDLU_AcquireSurface( hiScoreSurface );
+		
+	SDLU_GetPixel( hiScoreSurface, RandomBefore( fullSDLRect.w ), RandomBefore( fullSDLRect.h ), &anyColor );
+
+	anyColor.r = MinInt( 255, anyColor.r + 112 );
+	anyColor.g = MinInt( 255, anyColor.g + 112 );
+	anyColor.b = MinInt( 255, anyColor.b + 112 );
+
+	dPoint.v = widescreen? 100: 20;
+	dPoint.h = 225;
+	font = GetFont( picHiScoreFont );
+	for( count=0; highScores[count]; count++ )
+	{
+		SurfaceBlitCharacter( font, highScores[count], &dPoint, 255, 255, 255, 1 );
+	}
+	
+    font = GetFont(widescreen ? picFont : picHiScoreFont);
+	for( count=0; count<=9; count++ )
+	{
+		r = ((255 * (10-count)) + (anyColor.r * count)) / 10;
+		g = ((255 * (10-count)) + (anyColor.g * count)) / 10;
+		b = ((255 * (10-count)) + (anyColor.b * count)) / 10;
+		
+		if (widescreen)
+			dPoint.v = 150 + count * 24;
+		else
+			dPoint.v = 75 + count * 38;
+		dPoint.h = 85;
+		
+		if( count<9 )
+		{
+			SurfaceBlitCharacter( font, count + '1', &dPoint, r, g, b, 1 );
+		}
+		else
+		{
+			SurfaceBlitCharacter( font, '1', &dPoint, r, g, b, 1 );
+			SurfaceBlitCharacter( font, '0', &dPoint, r, g, b, 1 );
+		}
+		
+		SurfaceBlitCharacter( font, '.', &dPoint, r, g, b, 1 );
+		SurfaceBlitCharacter( font, ' ', &dPoint, r, g, b, 1 );
+
+		length = 0;
+		while( scores[count].name[length] && dPoint.h < 430 )
+		{
+			SurfaceBlitCharacter( font, scores[count].name[length++], &dPoint, r, g, b, 1 );
+		}
+		
+		while( dPoint.h < 450 )
+		{
+			SurfaceBlitCharacter( font, '.', &dPoint, r, g, b, 1 );
+		}
+		
+		dPoint.h = 470;
+
+		stringLength = sprintf( myString, "%d", scores[count].score );
+		for( length=0; length < stringLength; length++ )
+		{
+			SurfaceBlitCharacter( font, myString[length], &dPoint, r, g, b, 1 );
+		}
+	}
+	
+	SDLU_ReleaseSurface( hiScoreSurface );
+	
+	SDL_FillRect( g_frontSurface, &g_frontSurface->clip_rect, SDL_MapRGB( g_frontSurface->format, 0, 0, 0 ));
+    SDLU_Present();
+	
+	SDLU_SetBrightness( 1.0f );	
+
+	FadeScreen( hiScoreSurface, fadeSurface, 31, -32 );
+	do
+	{
+	}
+	while( !AnyKeyIsPressed( ) && !SDLU_Button() );
+	FadeScreen( hiScoreSurface, fadeSurface, -31, 32 );
+	
+	SDLU_SetBrightness( 0.0f );	
+
+	SDL_FreeSurface( hiScoreSurface );
+	SDL_FreeSurface( fadeSurface );
+}
+
+void InitPotentialCombos( void )
+{
+	memset( &potentialCombo[0], 0, sizeof(Combo) );
+	memset( &potentialCombo[1], 0, sizeof(Combo) );
+	potentialCombo[0].player = 0;
+	potentialCombo[1].player = 1;
+}
+
+void SubmitCombo( Combo *in )
+{
+	if( in->value > best.value && in->value > evenBetter.value )
+	{
+		PlayMono( kContinueSnd );
+		memcpy( &evenBetter, in, sizeof( Combo ) );
+	}	
+}
+
+// Please note: This function may well be the biggest kludge in Candy Crisis.
+// It relies on tons of insider knowledge. But it works.
+void ShowBestCombo( void )
+{
+	SkittlesFontPtr font;
+	const char *bestCombo = "BEST COMBO";
+    const char *scan;
+    char bestInfo[256];
+	MPoint dPoint;
+	int levelCap;
+	
+	StopBalloon( );
+	InitGame( kAutoControl, kNobodyControl );
+	scoreWindowVisible[0] = false;
+	grayMonitorVisible[0] = false;
+
+	level = best.lv;
+	levelCap = kLevels;
+	if( (level < 1 || level > levelCap) && level != kTutorialLevel ) 
+	{
+		memcpy( &best, &defaultBest, sizeof(best) );
+		showStartMenu = true;
+		return;
+	}
+
+	BeginRound( false );
+	character[0].dropSpeed = 12;
+	
+	SDLU_AcquireSurface( backdropSurface );
+	
+	font = GetFont(picHiScoreFont);
+	dPoint.v = widescreen? 70: 40;
+	dPoint.h = 225;
+	for( scan = bestCombo; *scan; scan++ )
+	{
+		SurfaceBlitCharacter( font, *scan, &dPoint, 255, 255, 255, 1 );
+	}
+		
+	sprintf( bestInfo, "%s (%d points)", best.name, best.value );
+
+    font = GetFont(widescreen ? picFont : picHiScoreFont);
+	dPoint.v = widescreen? 388: 410;
+	dPoint.h = 320 - (GetTextWidth( font, bestInfo ) / 2);
+
+	for( scan = bestInfo; *scan; scan++ )
+	{
+		SurfaceBlitCharacter( font, *scan, &dPoint, 255, 255, 255, 1 );
+	}
+
+	SDLU_ReleaseSurface( backdropSurface );
+	
+	memcpy( grid[0], best.grid, kGridAcross * kGridDown );
+	ResolveSuction( 0 );
+	RedrawBoardContents( 0 );
+	RefreshAll( );
+
+	nextA[0] = best.a;
+	nextB[0] = best.b;
+	nextG[0] = best.g;
+	nextM[0] = best.m;
+	RetrieveBlobs( 0 );
+
+	EraseSpriteBlobs( 0 );
+	blobX[0] = best.x;
+	blobR[0] = best.r;
+	DrawSpriteBlobs( 0, kNoSuction );
+	
+	QuickFadeIn( NULL );
+	blobTime[0] = animTime[0] = GameTickCount( );
+
+	autoPattern = hiScorePattern;	
+	tutorialTime = 0;
+}
+
+void AddHiscore( int score )
+{
+	short count, item;
+	char rank[50];
+    char text[256];
+    const char *playerName = "You";
+    const char *twoPlayerNames[] = { "Player 1", "Player 2" };
+	int highScoreLevel = -1;
+	
+
+	// Check for high score
+	// (only do high scores if it's player vs CPU)
+			
+	if( players == 1 &&
+	    control[0] == kPlayerControl &&
+	    control[1] == kAIControl        )
+	{		
+		for( count=0; count<=9; count++ )
+		{
+			if( score >= scores[count].score )
+			{				
+				sprintf( rank, "%d points", score );
+				highScoreLevel = count;
+				break;
+			}
+		}
+	}
+	
+	// Determine player's name for best combo
+
+	if( players == 2 ) playerName = twoPlayerNames[evenBetter.player];
+
+
+	// See if both best combo AND high score
+		
+	if( evenBetter.value > best.value && highScoreLevel != -1 )
+	{
+		
+		sprintf( text, "You got a high score and the best combo!" );
+
+		highScoreText = text;
+		highScoreRank = rank;
+
+		HandleDialog( kHiScoreDialog );
+
+		if( !finished )
+		{
+			highScoreName[kNameLength] = '\0';
+
+			memcpy( &best, &evenBetter, sizeof(Combo) );
+			strcpy( best.name, highScoreName );
+
+			for( item=8; item>=highScoreLevel; item-- )
+			{
+				memmove( &scores[item+1], &scores[item], sizeof( HighScore ) );
+			}
+			
+			scores[highScoreLevel].score = score;
+			strcpy( scores[highScoreLevel].name, highScoreName );				
+		}
+	}
+	
+	// See if best combo has been won
+		
+	else if( evenBetter.value > best.value )
+	{
+		sprintf( text, "Congratulations! %s got best combo!", playerName );
+		
+		highScoreText = text;
+		highScoreRank = "";
+				
+		HandleDialog( kHiScoreDialog );
+		
+		if( !finished )
+		{
+			highScoreName[kNameLength] = '\0';
+
+			memcpy( &best, &evenBetter, sizeof(Combo) );
+			strcpy( best.name, highScoreName );
+		}
+	}
+
+	// See if high score has been won
+	
+	else if( highScoreLevel != -1 )
+	{
+		highScoreText = "Congratulations! You got a high score!";
+		highScoreRank = rank;
+
+		HandleDialog( kHiScoreDialog );
+		
+		if( !finished )
+		{
+			highScoreName[kNameLength] = '\0';
+
+			for( item=8; item>=highScoreLevel; item-- )
+			{
+				memmove( &scores[item+1], &scores[item], sizeof( HighScore ) );
+			}
+			
+			scores[highScoreLevel].score = score;
+			strcpy( scores[highScoreLevel].name, highScoreName );				
+		}
+	}
+}
--- a/src/hiscore.cpp
+++ /dev/null
@@ -1,501 +1,0 @@
-// hiscore.c
-
-#include "SDLU.h"
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <algorithm>
-
-#include "main.h"
-#include "gworld.h"
-#include "graphics.h"
-#include "score.h"
-#include "hiscore.h"
-#include "keyselect.h"
-#include "font.h"
-#include "blitter.h"
-#include "random.h"
-#include "pause.h"
-#include "level.h"
-#include "tutorial.h"
-#include "graymonitor.h"
-#include "players.h"
-#include "gameticks.h"
-#include "music.h"
-#include "soundfx.h"
-
-using std::min;
-
-Combo defaultBest = 
-{
-	/*bestGrid[kGridAcross][kGridDown] = */ 
-	{ { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,                          1, 1, 1, 2, 2 },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,           1, 1 },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty } },
-
-	/*bestA      = */ 2,
-	/*bestB      = */ 2,
-	/*bestM      = */ false, 
-	/*bestG      = */ false, 
-	/*bestLv     = */ kTutorialLevel,
-	/*bestX      = */ 1,
-	/*bestR      = */ upRotate,
-	/*bestPlayer = */ 0,
-	/*bestValue  = */ (40*1) + (50*9),
-	/*bestName   = */ "Tutorial"
-};
-
-Combo best = 
-{
-	/*bestGrid[kGridAcross][kGridDown] = */ 
-	{ { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,                          1, 1, 1, 2, 2 },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty,           1, 1 },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty },
-	  { kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty, kEmpty } },
-
-	/*bestA      = */ 2,
-	/*bestB      = */ 2,
-	/*bestM      = */ false, 
-	/*bestG      = */ false, 
-	/*bestLv     = */ kTutorialLevel,
-	/*bestX      = */ 1,
-	/*bestR      = */ upRotate,
-	/*bestPlayer = */ 0,
-	/*bestValue  = */ (40*1) + (50*9),
-	/*bestName   = */ "Tutorial"
-};
-
-Combo evenBetter = {0};
-Combo potentialCombo[2];
-
-AutoPattern hiScorePattern[] =
-{
-	{ kIdleTicks,          60, 0,   NULL },	
-	{ kBlockUntilDrop,     0,  0,   NULL },	
-	{ kBlockUntilComplete, 0,  0,   NULL },	
-	{ kIdleTicks,          60, 0,   NULL },	
-	{ kComplete,           0,  0,   NULL }
-};
-
-HighScore scores[10] = 
-{	
-	{"Leviathan", 40000},
-	{"Dr. Crisis", 36000},
-	{"Angel", 32000},
-	{"Spike", 28000},
-	{"Fox", 24000},
-	{"Raguel", 20000},
-	{"Kumo", 16000},
-	{"Patty", 12000},
-	{"Yuurei", 8000},
-	{"Glurp", 4000}  
-};
-
-HighScore defaultScores[10] = 
-{	
-	{"Leviathan", 40000},
-	{"Dr. Crisis", 36000},
-	{"Angel", 32000},
-	{"Spike", 28000},
-	{"Fox", 24000},
-	{"Raguel", 20000},
-	{"Kumo", 16000},
-	{"Patty", 12000},
-	{"Yuurei", 8000},
-	{"Glurp", 4000}  
-};
-
-char highScoreName[256];
-const char *highScoreText;
-const char *highScoreRank;
-
-static void FadeScreen( SDL_Surface* hiScoreSurface, SDL_Surface* fadeSurface, int start, int end )
-{
-	MTicks    timer;
-	int       skip, frame, fade, color, direction, fadeStart, fadeEnd;
-	SDL_Rect  destSDLRect;
-	SDL_Rect  fullSDLRect = { 0, 0, 640, 480 };
-	int       black;
-	
-	black = SDL_MapRGB( fadeSurface->format, 0, 0, 0 );
-	
-	if( start < end )
-	{
-		direction = 1;
-		fadeStart = 0;
-		fadeEnd = 32;
-	}
-	else
-	{
-		direction = -1;
-		fadeStart = 32;
-		fadeEnd = 0;
-	}
-	
-	skip = 1;
-	timer = MTickCount( ) + 1;
-	while( timer > MTickCount() ) { }
-
-	for( frame = start; (direction>0)? (frame <= end): (frame >= end); frame += direction )
-	{
-		MRect drawRect = {0, 0, 15, 640};
-		timer += skip;
-		
-		for( fade = fadeStart; fade != fadeEnd; fade += direction )
-		{
-			color = frame + fade;
-			if( color <  0 ) color = 0;
-			if( color > 31 ) color = 31;
-			
-			SDLU_MRectToSDLRect( &drawRect, &destSDLRect );
-
-			switch( color )
-			{
-				case 0:
-					SDLU_BlitSurface( hiScoreSurface, &destSDLRect,
-					                  fadeSurface,    &destSDLRect  ); 
-					break;
-			
-				case 31:
-					SDL_FillRect( fadeSurface, &destSDLRect, black );
-					break;
-				
-				default:
-					SurfaceBlitColorOver( hiScoreSurface,  fadeSurface,
-					                      &drawRect,       &drawRect,
-					                       0, 0, 0, _5TO8(color) );
-					break;
-			}
-			
-			OffsetMRect( &drawRect, 0, 15 );
-		}
-
-		SDLU_BlitFrontSurface( fadeSurface, &fullSDLRect, &fullSDLRect );
-        SDLU_Present();
-
-		if( timer <= MTickCount( ) )
-		{
-			skip = 2;
-		}
-		else
-		{
-			skip = 1;
-			while( timer > MTickCount( ) )
-			{
-				SDLU_Yield();
-			}
-		}
-	}
-}
-
-void ShowHiscore( void )
-{
-	short            count, length;
-	char             myString[256];
-	int              stringLength;
-	SDL_Surface*     hiScoreSurface;
-	SDL_Surface*     fadeSurface;
-	SDL_Rect         fullSDLRect = { 0, 0, 640, 480 };
-	SkittlesFontPtr  font;
-	SDL_Color        anyColor;
-	MPoint           dPoint;
-	const char*      highScores = "HIGH SCORES";
-	int              r, g, b;
-	
-	if (DeleteKeyIsPressed())
-	{
-		// If the user holds delete while opening the high scores,
-		// clear the high score table.
-		
-		memcpy( &scores, &defaultScores, sizeof( scores ) );
-		memcpy( &best,   &defaultBest,   sizeof( best   ) );
-	}
-	
-	hiScoreSurface = LoadPICTAsSurface( picBackdrop + (100 * RandomBefore(kLevels)), 32 );
-	fadeSurface    = SDLU_InitSurface( &fullSDLRect, 32 );
-
-
-	SDLU_AcquireSurface( hiScoreSurface );
-		
-	SDLU_GetPixel( hiScoreSurface, RandomBefore( fullSDLRect.w ), RandomBefore( fullSDLRect.h ), &anyColor );
-
-	anyColor.r = min( 255, anyColor.r + 112 );
-	anyColor.g = min( 255, anyColor.g + 112 );
-	anyColor.b = min( 255, anyColor.b + 112 );
-
-	dPoint.v = widescreen? 100: 20;
-	dPoint.h = 225;
-	font = GetFont( picHiScoreFont );
-	for( count=0; highScores[count]; count++ )
-	{
-		SurfaceBlitCharacter( font, highScores[count], &dPoint, 255, 255, 255, 1 );
-	}
-	
-    font = GetFont(widescreen ? picFont : picHiScoreFont);
-	for( count=0; count<=9; count++ )
-	{
-		r = ((255 * (10-count)) + (anyColor.r * count)) / 10;
-		g = ((255 * (10-count)) + (anyColor.g * count)) / 10;
-		b = ((255 * (10-count)) + (anyColor.b * count)) / 10;
-		
-		if (widescreen)
-			dPoint.v = 150 + count * 24;
-		else
-			dPoint.v = 75 + count * 38;
-		dPoint.h = 85;
-		
-		if( count<9 )
-		{
-			SurfaceBlitCharacter( font, count + '1', &dPoint, r, g, b, 1 );
-		}
-		else
-		{
-			SurfaceBlitCharacter( font, '1', &dPoint, r, g, b, 1 );
-			SurfaceBlitCharacter( font, '0', &dPoint, r, g, b, 1 );
-		}
-		
-		SurfaceBlitCharacter( font, '.', &dPoint, r, g, b, 1 );
-		SurfaceBlitCharacter( font, ' ', &dPoint, r, g, b, 1 );
-
-		length = 0;
-		while( scores[count].name[length] && dPoint.h < 430 )
-		{
-			SurfaceBlitCharacter( font, scores[count].name[length++], &dPoint, r, g, b, 1 );
-		}
-		
-		while( dPoint.h < 450 )
-		{
-			SurfaceBlitCharacter( font, '.', &dPoint, r, g, b, 1 );
-		}
-		
-		dPoint.h = 470;
-
-		stringLength = sprintf( myString, "%d", scores[count].score );
-		for( length=0; length < stringLength; length++ )
-		{
-			SurfaceBlitCharacter( font, myString[length], &dPoint, r, g, b, 1 );
-		}
-	}
-	
-	SDLU_ReleaseSurface( hiScoreSurface );
-	
-	SDL_FillRect( g_frontSurface, &g_frontSurface->clip_rect, SDL_MapRGB( g_frontSurface->format, 0, 0, 0 ));
-    SDLU_Present();
-	
-	SDLU_SetBrightness( 1.0f );	
-
-	FadeScreen( hiScoreSurface, fadeSurface, 31, -32 );
-	do
-	{
-	}
-	while( !AnyKeyIsPressed( ) && !SDLU_Button() );
-	FadeScreen( hiScoreSurface, fadeSurface, -31, 32 );
-	
-	SDLU_SetBrightness( 0.0f );	
-
-	SDL_FreeSurface( hiScoreSurface );
-	SDL_FreeSurface( fadeSurface );
-}
-
-void InitPotentialCombos( void )
-{
-	memset( &potentialCombo[0], 0, sizeof(Combo) );
-	memset( &potentialCombo[1], 0, sizeof(Combo) );
-	potentialCombo[0].player = 0;
-	potentialCombo[1].player = 1;
-}
-
-void SubmitCombo( Combo *in )
-{
-	if( in->value > best.value && in->value > evenBetter.value )
-	{
-		PlayMono( kContinueSnd );
-		memcpy( &evenBetter, in, sizeof( Combo ) );
-	}	
-}
-
-// Please note: This function may well be the biggest kludge in Candy Crisis.
-// It relies on tons of insider knowledge. But it works.
-void ShowBestCombo( void )
-{
-	SkittlesFontPtr font;
-	const char *bestCombo = "BEST COMBO";
-    const char *scan;
-    char bestInfo[256];
-	MPoint dPoint;
-	int levelCap;
-	
-	StopBalloon( );
-	InitGame( kAutoControl, kNobodyControl );
-	scoreWindowVisible[0] = false;
-	grayMonitorVisible[0] = false;
-
-	level = best.lv;
-	levelCap = kLevels;
-	if( (level < 1 || level > levelCap) && level != kTutorialLevel ) 
-	{
-		memcpy( &best, &defaultBest, sizeof(best) );
-		showStartMenu = true;
-		return;
-	}
-
-	BeginRound( false );
-	character[0].dropSpeed = 12;
-	
-	SDLU_AcquireSurface( backdropSurface );
-	
-	font = GetFont(picHiScoreFont);
-	dPoint.v = widescreen? 70: 40;
-	dPoint.h = 225;
-	for( scan = bestCombo; *scan; scan++ )
-	{
-		SurfaceBlitCharacter( font, *scan, &dPoint, 255, 255, 255, 1 );
-	}
-		
-	sprintf( bestInfo, "%s (%d points)", best.name, best.value );
-
-    font = GetFont(widescreen ? picFont : picHiScoreFont);
-	dPoint.v = widescreen? 388: 410;
-	dPoint.h = 320 - (GetTextWidth( font, bestInfo ) / 2);
-
-	for( scan = bestInfo; *scan; scan++ )
-	{
-		SurfaceBlitCharacter( font, *scan, &dPoint, 255, 255, 255, 1 );
-	}
-
-	SDLU_ReleaseSurface( backdropSurface );
-	
-	memcpy( grid[0], best.grid, kGridAcross * kGridDown );
-	ResolveSuction( 0 );
-	RedrawBoardContents( 0 );
-	RefreshAll( );
-
-	nextA[0] = best.a;
-	nextB[0] = best.b;
-	nextG[0] = best.g;
-	nextM[0] = best.m;
-	RetrieveBlobs( 0 );
-
-	EraseSpriteBlobs( 0 );
-	blobX[0] = best.x;
-	blobR[0] = best.r;
-	DrawSpriteBlobs( 0, kNoSuction );
-	
-	QuickFadeIn( NULL );
-	blobTime[0] = animTime[0] = GameTickCount( );
-
-	autoPattern = hiScorePattern;	
-	tutorialTime = 0;
-}
-
-void AddHiscore( int score )
-{
-	short count, item;
-	char rank[50];
-    char text[256];
-    const char *playerName = "You";
-    const char *twoPlayerNames[] = { "Player 1", "Player 2" };
-	int highScoreLevel = -1;
-	
-
-	// Check for high score
-	// (only do high scores if it's player vs CPU)
-			
-	if( players == 1 &&
-	    control[0] == kPlayerControl &&
-	    control[1] == kAIControl        )
-	{		
-		for( count=0; count<=9; count++ )
-		{
-			if( score >= scores[count].score )
-			{				
-				sprintf( rank, "%d points", score );
-				highScoreLevel = count;
-				break;
-			}
-		}
-	}
-	
-	// Determine player's name for best combo
-
-	if( players == 2 ) playerName = twoPlayerNames[evenBetter.player];
-
-
-	// See if both best combo AND high score
-		
-	if( evenBetter.value > best.value && highScoreLevel != -1 )
-	{
-		
-		sprintf( text, "You got a high score and the best combo!" );
-
-		highScoreText = text;
-		highScoreRank = rank;
-
-		HandleDialog( kHiScoreDialog );
-
-		if( !finished )
-		{
-			highScoreName[kNameLength] = '\0';
-
-			memcpy( &best, &evenBetter, sizeof(Combo) );
-			strcpy( best.name, highScoreName );
-
-			for( item=8; item>=highScoreLevel; item-- )
-			{
-				memmove( &scores[item+1], &scores[item], sizeof( HighScore ) );
-			}
-			
-			scores[highScoreLevel].score = score;
-			strcpy( scores[highScoreLevel].name, highScoreName );				
-		}
-	}
-	
-	// See if best combo has been won
-		
-	else if( evenBetter.value > best.value )
-	{
-		sprintf( text, "Congratulations! %s got best combo!", playerName );
-		
-		highScoreText = text;
-		highScoreRank = "";
-				
-		HandleDialog( kHiScoreDialog );
-		
-		if( !finished )
-		{
-			highScoreName[kNameLength] = '\0';
-
-			memcpy( &best, &evenBetter, sizeof(Combo) );
-			strcpy( best.name, highScoreName );
-		}
-	}
-
-	// See if high score has been won
-	
-	else if( highScoreLevel != -1 )
-	{
-		highScoreText = "Congratulations! You got a high score!";
-		highScoreRank = rank;
-
-		HandleDialog( kHiScoreDialog );
-		
-		if( !finished )
-		{
-			highScoreName[kNameLength] = '\0';
-
-			for( item=8; item>=highScoreLevel; item-- )
-			{
-				memmove( &scores[item+1], &scores[item], sizeof( HighScore ) );
-			}
-			
-			scores[highScoreLevel].score = score;
-			strcpy( scores[highScoreLevel].name, highScoreName );				
-		}
-	}
-}
--- /dev/null
+++ b/src/keyselect.c
@@ -1,0 +1,65 @@
+// keyselect.c
+
+#include "SDLU.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "main.h"
+#include "players.h"
+#include "keyselect.h"
+
+
+SDL_Keycode playerKeys[2][4] =
+{
+	{ SDLK_a, SDLK_d, SDLK_x, SDLK_s },
+	{ SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN, SDLK_UP }
+};
+
+const SDL_Keycode defaultPlayerKeys[2][4] =
+{
+	{ SDLK_a, SDLK_d, SDLK_x, SDLK_s },
+	{ SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN, SDLK_UP }
+};
+
+
+void CheckKeys()
+{
+	int player;
+	int arraySize;
+	const Uint8* pressedKeys;
+				                 
+	SDLU_PumpEvents();          
+	pressedKeys = SDL_GetKeyboardState( &arraySize );
+    
+    // Check for game keys
+	for( player = 0; player < 2; player++ )
+	{
+        if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][0])])
+			hitKey[player].left++; 
+		else
+			hitKey[player].left = 0;
+
+
+        if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][1])])
+			hitKey[player].right++;
+		else
+			hitKey[player].right = 0;
+
+
+        if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][2])])
+			hitKey[player].drop++;
+		else
+			hitKey[player].drop = 0;
+
+
+        if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][3])])
+			hitKey[player].rotate++;
+		else
+			hitKey[player].rotate = 0;
+	}
+	
+	pauseKey = pressedKeys[SDL_SCANCODE_ESCAPE];
+}
--- a/src/keyselect.cpp
+++ /dev/null
@@ -1,65 +1,0 @@
-// keyselect.c
-
-#include "SDLU.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <ctype.h>
-
-#include "main.h"
-#include "players.h"
-#include "keyselect.h"
-
-
-SDL_Keycode playerKeys[2][4] =
-{
-	{ SDLK_a, SDLK_d, SDLK_x, SDLK_s },
-	{ SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN, SDLK_UP }
-};
-
-const SDL_Keycode defaultPlayerKeys[2][4] =
-{
-	{ SDLK_a, SDLK_d, SDLK_x, SDLK_s },
-	{ SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN, SDLK_UP }
-};
-
-
-void CheckKeys()
-{
-	int player;
-	int arraySize;
-	const Uint8* pressedKeys;
-				                 
-	SDLU_PumpEvents();          
-	pressedKeys = SDL_GetKeyboardState( &arraySize );
-    
-    // Check for game keys
-	for( player = 0; player < 2; player++ )
-	{
-        if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][0])])
-			hitKey[player].left++; 
-		else
-			hitKey[player].left = 0;
-
-
-        if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][1])])
-			hitKey[player].right++;
-		else
-			hitKey[player].right = 0;
-
-
-        if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][2])])
-			hitKey[player].drop++;
-		else
-			hitKey[player].drop = 0;
-
-
-        if (pressedKeys[SDL_GetScancodeFromKey(playerKeys[player][3])])
-			hitKey[player].rotate++;
-		else
-			hitKey[player].rotate = 0;
-	}
-	
-	pauseKey = pressedKeys[SDL_SCANCODE_ESCAPE];
-}
--- /dev/null
+++ b/src/level.c
@@ -1,0 +1,1435 @@
+// level.c
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "level.h"
+#include "score.h"
+#include "random.h"
+#include "grays.h"
+#include "gameticks.h"
+#include "players.h"
+#include "graymonitor.h"
+#include "opponent.h"
+#include "gworld.h"
+#include "graphics.h"
+#include "music.h"
+#include "control.h"
+#include "tweak.h"
+#include "soundfx.h"
+#include "next.h"
+#include "hiscore.h"
+#include "victory.h"
+#include "blitter.h"
+#include "zap.h"
+#include "keyselect.h"
+#include "tutorial.h"
+#include "pause.h"
+
+MRect stageWindowZRect, stageWindowRect;
+Character character[2];
+int level, players, credits, difficulty[2] = {kHardLevel, kHardLevel};
+int difficultyTicks, backdropTicks, backdropFrame;
+
+#define kNumSplats 16
+#define kIdleSplat -2
+#define kFallingSplat -1
+#define kTitleItems 8
+#define kIncrementPerFrame 2
+#define kSplatType 4
+
+const int startSkip = 1;
+static MBoolean shouldFullRepaint = false;
+static MTicks startMenuTime = 0;
+static int splatState[kNumSplats], splatColor[kNumSplats], splatSide[kNumSplats];
+static MRect splatBlob[kNumSplats];
+
+enum
+{
+    kTitleItemTutorial,
+    kTitleItem1PGame,
+    kTitleItem2PGame,
+    kTitleItemSolitaire,
+    kTitleItemHighScores,
+    kTitleItemControls,
+    kTitleItemCredits,
+    kTitleItemQuit,
+};
+
+typedef struct TitleItemDef
+{
+	const char* name;
+	MRGBColor color1;
+	MRGBColor color2;
+	MRect rect;
+} TitleItemDef;
+
+static const TitleItemDef k_titleItemDefs[kTitleItems] =
+{
+		{ "\x03 Tutorial Mode",     {204, 67,137}, {101, 74,207}, {155, 203, 207, 426} },
+		{ "\x03 One Player Game",   { 35, 31,240}, { 81,237,252}, {225, 179, 281, 451} },
+		{ "\x03 Two Player Game",   {212,194, 48}, {255,196, 56}, {297, 182, 352, 454} },
+		{ "\x03 Solitaire Crisis",  {102,247,106}, {125,237,179}, {358, 183, 428, 458} },
+		{ "\x03 High Scores",       {234,244,132}, {192,218, 85}, {429, 280, 478, 390} },
+		{ "\x03 Controls",          { 64, 88,212}, { 62, 87,205}, {430, 187, 479, 280} },
+		{ "\x03 Credits",           {245,  7, 78}, {254,128,156}, {  6, 370,  59, 423} },
+		{ "\x03 Quit",              {107,105,106}, {169,167,168}, {433, 390, 477, 446} }
+};
+
+const int kCursorWidth  = 32;
+const int kCursorHeight = 32;
+
+#if USE_CURSOR_SPRITE
+static void InsertCursor( MPoint mouseHere, SDL_Surface* scratch, SDL_Surface* surface )
+{
+	SkittlesFontPtr cursorFont = GetFont( picFont );
+	SDL_Rect        cursorBackSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
+	SDL_Rect        cursorFrontSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
+	MPoint          mouseHereToo = mouseHere;
+	
+	cursorFrontSDLRect.x = mouseHere.h;
+	cursorFrontSDLRect.y = mouseHere.v;
+	
+	SDLU_BlitSurface( surface, &cursorFrontSDLRect,
+	                  scratch, &cursorBackSDLRect   );
+	
+	SDLU_AcquireSurface( surface );
+	SurfaceBlitCharacter( cursorFont, '\x05', &mouseHere,    0,   0,   0,   0 );
+	SurfaceBlitCharacter( cursorFont, '\x04', &mouseHereToo, 255, 255, 255, 0 );
+	SDLU_ReleaseSurface( surface );
+}
+
+static void RemoveCursor( MPoint mouseHere, SDL_Surface* scratch, SDL_Surface* surface )
+{
+	SDL_Rect      cursorBackSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
+	SDL_Rect      cursorFrontSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
+	
+	cursorFrontSDLRect.x = mouseHere.h;
+	cursorFrontSDLRect.y = mouseHere.v;
+	
+	SDLU_BlitSurface( scratch, &cursorBackSDLRect,
+	                  surface, &cursorFrontSDLRect );
+}
+#endif
+
+static void GameStartMenuRepaint()
+{
+	shouldFullRepaint = true;
+}
+
+void GameStartMenu( void )
+{
+	MBoolean        useNewTitle = widescreen;
+
+	// NOTE: be wary of initializing variables here! This function can run top-to-bottom
+	// multiple times in a row, thanks to "redo". Put initializations after redo.
+    SDL_Surface*    gameStartSurface;
+    SDL_Surface*    gameStartDrawSurface;
+#if USE_CURSOR_SPRITE
+    SDL_Surface*    cursorBackSurface;
+#endif
+	SDL_Rect        backdropSDLRect = { 0, 0, 640, 480 };
+	SDL_Rect        cursorBackSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
+	SDL_Rect        destSDLRect;
+	MRect           drawRect[4], chunkRect, tempRect;
+	int             blob, count, oldGlow, splat, chunkType, selected;
+	int             skip;
+	MPoint          mouse;
+	MPoint          dPoint;
+	unsigned int    black;
+	int             combo[2], comboBright[2], missBright[2];
+	SkittlesFontPtr smallFont = GetFont( picFont );
+	SkittlesFontPtr tinyFont = GetFont( picTinyFont );
+	SDL_Rect        meterRect[2] = { { 30, 360, 110, 20 }, { 530, 360, 110, 20 } };
+	TitleItemDef    titleItems[kTitleItems];
+    int             titleGlow[kTitleItems];
+    int             shouldAddBlob;
+    const int       kTitleGlowOff = useNewTitle? 150: 192;
+    const bool      secretCreditsItem = !useNewTitle;
+    
+	const int       kLeftSide = 0, kRightSide = 1, kGlow = 2, kCursor = 3;
+	
+	
+redo:
+	memcpy(titleItems, k_titleItemDefs, sizeof(titleItems));
+
+	combo[0] = combo[1] = 0;
+	comboBright[0] = comboBright[1] = 0;
+	missBright[0] = missBright[1] = 0;
+		
+	skip = 1;
+	selected = -1;
+	mouse.h = mouse.v = 0;
+	
+	if( finished ) return;
+	
+	if( musicSelection != 13 ) ChooseMusic( 13 );
+	
+	for( count=0; count<kTitleItems; count++ )
+	{
+		titleGlow[count] = kTitleGlowOff;
+	}
+
+    if (secretCreditsItem)
+    {
+        titleGlow[kTitleItemCredits] = 0;
+    }
+	
+	for( count=0; count<kNumSplats; count++ )
+	{
+		splatState[count] = kIdleSplat;
+	}
+	
+	// make background surface
+	gameStartSurface     = LoadPICTAsSurface( picGameStart, 32 );
+	black = SDL_MapRGB( gameStartSurface->format, 0, 0, 0 );
+
+	// make cursor backing store
+#if USE_CURSOR_SPRITE
+	cursorBackSurface    = SDLU_InitSurface( &cursorBackSDLRect, 32 );
+	SDL_FillRect( cursorBackSurface, &cursorBackSDLRect, black );
+#endif
+	
+	// make drawing surface
+	gameStartDrawSurface = SDLU_InitSurface( &backdropSDLRect, 32 );
+	if (!useNewTitle)
+	{
+		SDLU_BlitSurface(gameStartSurface, &gameStartSurface->clip_rect,
+						 gameStartDrawSurface, &gameStartDrawSurface->clip_rect);
+	}
+	else
+	{
+		// Prepare new title screen
+		SDL_FillRect(gameStartDrawSurface, &gameStartDrawSurface->clip_rect, black);
+
+		// Copy logo from original title screen to where we want it
+		SDL_Rect r1 = {0, 0, 640, 150};
+		SDL_Rect r2 = {0, 70, 640, 150};
+		SDLU_BlitSurface(gameStartSurface, &r1, gameStartDrawSurface, &r2);
+
+		// Now we're going to draw title items on gameStartSurface
+		SDL_FillRect(gameStartSurface, &gameStartSurface->clip_rect, black);
+		SDLU_AcquireSurface(gameStartSurface);
+
+		SkittlesFontPtr font = GetFont(picFont);
+		int left = 225;
+		dPoint.h = left;
+		dPoint.v = 215;
+		for (int i = 0; i < kTitleItems; i++)
+		{
+			TitleItemDef* item = &titleItems[i];
+			item->rect.left = dPoint.h;
+			item->rect.top = dPoint.v - 6;
+			item->rect.bottom = dPoint.v + 16 + 6;
+			int nameLength = (int) strlen(item->name);
+			for (int charNo = 0; charNo < nameLength; charNo++)
+			{
+				char c = item->name[charNo];
+				float p = charNo / (float) (nameLength - 1);
+				int red = item->color1.red * (1.0f - p) + item->color2.red * p;
+				int green = item->color1.green * (1.0f - p) + item->color2.green * p;
+				int blue = item->color1.blue * (1.0f - p) + item->color2.blue * p;
+				SurfaceBlitCharacter(font, c, &dPoint, red, green, blue, 1);
+			}
+			item->rect.right = dPoint.h;
+			dPoint.h = left;
+			dPoint.v += 24;
+		}
+		SDLU_ReleaseSurface(gameStartSurface);
+	}
+	
+	// darken menu items
+	for( count=0; count<kTitleItems; count++ )
+	{
+		SurfaceBlitColorOver( gameStartSurface,  gameStartDrawSurface,
+							  &titleItems[count].rect, &titleItems[count].rect, 
+							   0, 0, 0, titleGlow[count] );
+	}
+	
+	SDLU_BlitFrontSurface( gameStartDrawSurface, &backdropSDLRect, &backdropSDLRect );
+    SDLU_Present();
+
+	WaitForRelease();
+
+	QuickFadeIn( NULL );
+	
+	DoFullRepaint = GameStartMenuRepaint;
+
+    shouldAddBlob = 5;
+	startMenuTime = MTickCount( );
+	while( ( selected == -1 || !SDLU_Button() ) && !finished )
+	{	
+		startMenuTime += skip;
+
+        UpdateSound();
+        
+		// Add a new falling blob
+        --shouldAddBlob;
+		if (shouldAddBlob <= 0)
+		{
+			for( blob=0; blob<kNumSplats; blob++ )
+			{
+				if( splatState[blob] == kIdleSplat )
+				{
+					splatSide[blob] = RandomBefore(2);
+					splatBlob[blob].top = -24 - RandomBefore(15);
+					splatBlob[blob].left = (splatSide[blob] == 0)? RandomBefore( 110 ): 640 - kBlobHorizSize - RandomBefore( 110 );
+					splatBlob[blob].bottom = splatBlob[blob].top + kBlobVertSize;
+					splatBlob[blob].right = splatBlob[blob].left + kBlobHorizSize;
+					splatColor[blob] = ((startMenuTime >> 2) % kBlobTypes) + 1;
+					splatState[blob] = kFallingSplat;
+					
+					break;
+				}
+			}
+            shouldAddBlob = 5;
+		}
+	
+		// Erase and redraw falling blobs and chunks
+
+		SDLU_AcquireSurface( gameStartDrawSurface );
+	
+		// Take the cursor out of the scene
+#if USE_CURSOR_SPRITE
+		RemoveCursor( mouse, cursorBackSurface, gameStartDrawSurface );
+		drawRect[kCursor].top    = mouse.v;
+		drawRect[kCursor].left   = mouse.h;
+		drawRect[kCursor].bottom = mouse.v + kCursorHeight;
+		drawRect[kCursor].right  = mouse.h + kCursorWidth;
+#endif
+		
+		// Inverted rectangles mean "nothing to do."
+		drawRect[kLeftSide].top    = drawRect[kRightSide].top    = drawRect[kGlow].top    = 
+		drawRect[kLeftSide].left   = drawRect[kRightSide].left   = drawRect[kGlow].left   = 9999;
+		drawRect[kLeftSide].bottom = drawRect[kRightSide].bottom = drawRect[kGlow].bottom =
+		drawRect[kLeftSide].right  = drawRect[kRightSide].right  = drawRect[kGlow].right  = -9999;
+	
+		// Get cursor position		
+		SDLU_GetMouse( &mouse );
+		if( mouse.v > (widescreen ? 400 : 460) )
+			mouse.v = (widescreen ? 400 : 460);
+		
+		// Erase falling blobs
+		for( blob=0; blob<kNumSplats; blob++ )
+		{
+			if( splatState[blob] == kFallingSplat )
+			{
+				SDL_FillRect( gameStartDrawSurface, SDLU_MRectToSDLRect( &splatBlob[blob], &destSDLRect ), black );
+				UnionMRect( &drawRect[splatSide[blob]], &splatBlob[blob], &drawRect[splatSide[blob]] );
+				
+				OffsetMRect( &splatBlob[blob], 0, startSkip * (6 + (splatBlob[blob].bottom / 20)) );
+			}
+			else if( splatState[blob] >= kIncrementPerFrame )
+			{
+				for( splat=-3; splat<=3; splat++ )
+				{
+					if( splat )
+					{
+						chunkRect = splatBlob[blob];
+						GetZapStyle( 0, &chunkRect, &splatColor[blob], &chunkType, splat, splatState[blob]-kIncrementPerFrame, kSplatType );
+						SDL_FillRect( gameStartDrawSurface, SDLU_MRectToSDLRect( &chunkRect, &destSDLRect ), black );
+						UnionMRect( &drawRect[splatSide[blob]], &chunkRect, &drawRect[splatSide[blob]] );
+					}
+				}
+				
+				SDL_FillRect( gameStartDrawSurface, SDLU_MRectToSDLRect( &splatBlob[blob], &destSDLRect ), black );
+				UnionMRect( &drawRect[splatSide[blob]], &splatBlob[blob], &drawRect[splatSide[blob]] );
+			}
+		}
+	
+		// Draw combo meters
+        
+		for( count=0; count<2; count++ )
+		{
+			int bright = comboBright[count];
+			int mBright = missBright[count];
+            if( bright || mBright )
+			{
+                SDL_FillRect( gameStartDrawSurface, &meterRect[count], black );
+                UnionMRect( &drawRect[count], SDLU_SDLRectToMRect( &meterRect[count], &tempRect ), &drawRect[count] );
+                
+                if( mBright > 1 )
+                {
+                  dPoint.v = meterRect[count].y;
+                  dPoint.h = meterRect[count].x + 10;
+                  SurfaceBlitCharacter( smallFont, 'M', &dPoint, mBright, mBright >> 2, mBright >> 2, 1 );
+                  SurfaceBlitCharacter( smallFont, 'I', &dPoint, mBright, mBright >> 2, mBright >> 2, 1 );
+                  SurfaceBlitCharacter( smallFont, 'S', &dPoint, mBright, mBright >> 2, mBright >> 2, 1 );
+                  SurfaceBlitCharacter( smallFont, 'S', &dPoint, mBright, mBright >> 2, mBright >> 2, 1 );
+                  missBright[count] -= 8;
+                }
+                else if( (combo[count] >= 10) && (bright > 1) )
+                {
+				  char  number[16] = { 0 };
+				  char* scan;
+				  sprintf( number, "%d", combo[count] );
+
+                  dPoint.v = meterRect[count].y + 3;
+                  dPoint.h = meterRect[count].x;
+     
+				  SurfaceBlitCharacter( tinyFont, 'C', &dPoint, bright, bright, bright, 1 );
+				  SurfaceBlitCharacter( tinyFont, 'O', &dPoint, bright, bright, bright, 1 );
+				  SurfaceBlitCharacter( tinyFont, 'M', &dPoint, bright, bright, bright, 1 );
+				  SurfaceBlitCharacter( tinyFont, 'B', &dPoint, bright, bright, bright, 1 );
+				  SurfaceBlitCharacter( tinyFont, 'O', &dPoint, bright, bright, bright, 1 );
+				  SurfaceBlitCharacter( tinyFont, ' ', &dPoint, bright, bright, bright, 1 );
+				  dPoint.v -= 3;
+				
+				  for( scan = number; *scan; scan++ )
+				  {
+				 	SurfaceBlitCharacter( smallFont, *scan, &dPoint, bright>>2, bright>>2, bright, 1 );
+			      }
+                  
+                  comboBright[count] -= 16;
+				}
+                else 
+                {
+                  comboBright[count] = 0;
+                }
+   			}
+		}
+
+		// Redraw falling blobs
+		for( blob=0; blob<kNumSplats; blob++ )
+		{
+			if( splatState[blob] == kFallingSplat )
+			{
+				int bottom = widescreen? 420: 480;
+				if( splatBlob[blob].bottom >= bottom ) 
+				{
+					splatBlob[blob].top = bottom - kBlobVertSize;
+					splatBlob[blob].bottom = bottom;
+					splatState[blob] = 1;
+					
+					// Process combos
+					if( mouse.v > bottom &&
+					    mouse.h >= (splatBlob[blob].left - 30) &&
+					    mouse.h <= (splatBlob[blob].right + 10)    )
+					{
+						combo[splatSide[blob]]++;
+						comboBright[splatSide[blob]] = 255;
+					}
+					else
+					{
+                        if( combo[splatSide[blob]] >= 10 ) missBright[splatSide[blob]] = 255;
+						combo[splatSide[blob]] = 0;
+                        comboBright[splatSide[blob]] = 0;                        
+					}
+				}
+				else
+				{
+					SurfaceDrawSprite( &splatBlob[blob], splatColor[blob], kNoSuction );
+					UnionMRect( &drawRect[splatSide[blob]], &splatBlob[blob], &drawRect[splatSide[blob]] );
+				}
+			}
+			
+			if( splatState[blob] >= 0 && splatState[blob] <= kZapFrames )
+			{
+				if( splatState[blob] <= (kZapFrames - kIncrementPerFrame) ) 
+				{
+                    chunkType = 0;
+                    
+					for( splat=-3; splat<=3; splat++ )
+					{
+						if( splat )
+						{
+							chunkRect = splatBlob[blob];
+							GetZapStyle( 0, &chunkRect, &splatColor[blob], &chunkType, splat, splatState[blob], kSplatType );
+							SurfaceDrawSprite( &chunkRect, splatColor[blob], chunkType );
+							UnionMRect( &drawRect[splatSide[blob]], &chunkRect, &drawRect[splatSide[blob]] );
+						}
+					}
+					
+					SurfaceDrawSprite( &splatBlob[blob], splatColor[blob], chunkType );
+					UnionMRect( &drawRect[splatSide[blob]], &splatBlob[blob], &drawRect[splatSide[blob]] );
+				}
+				
+				splatState[blob] += kIncrementPerFrame;
+				if( splatState[blob] > kZapFrames ) splatState[blob] = kIdleSplat;
+			}
+		}
+				
+		SDLU_ReleaseSurface( gameStartDrawSurface );		
+		
+		// Find mouse coords
+		
+		selected = -1;			
+		for( count=0; count<kTitleItems; count++ )
+		{
+			if( MPointInMRect( mouse, &titleItems[count].rect ) )
+			{
+				selected = count;
+				break;
+			}
+		}
+
+        if (secretCreditsItem)
+        {
+            titleGlow[kTitleItemCredits] = 0;
+        }
+
+		// update glows
+		for (int glowUpdate=0; glowUpdate < kTitleItems; ++glowUpdate)
+		{
+			const MRect* titleRect = &titleItems[glowUpdate].rect;
+			oldGlow = titleGlow[glowUpdate];
+			
+			if( selected == glowUpdate )
+			{
+				titleGlow[glowUpdate] -= (6 * startSkip);
+				if( titleGlow[glowUpdate] < 0 ) titleGlow[glowUpdate] = 0;
+			}
+			else 
+			{
+				titleGlow[glowUpdate] += (6 * startSkip);
+				if( titleGlow[glowUpdate] > kTitleGlowOff ) titleGlow[glowUpdate] = kTitleGlowOff;
+			}
+			
+			if( titleGlow[glowUpdate] != oldGlow )
+			{
+				SurfaceBlitColorOver( gameStartSurface,       gameStartDrawSurface,
+									  titleRect, titleRect,
+									   0, 0, 0, titleGlow[glowUpdate] );
+
+				drawRect[kGlow].top    = MinShort(drawRect[kGlow].top, titleRect->top);
+				drawRect[kGlow].left   = MinShort(drawRect[kGlow].left, titleRect->left);
+				drawRect[kGlow].bottom = MaxShort(drawRect[kGlow].bottom, titleRect->bottom);
+				drawRect[kGlow].right  = MaxShort(drawRect[kGlow].right, titleRect->right);
+			}
+		}
+
+		// Reinsert the cursor into the scene
+#if USE_CURSOR_SPRITE
+		InsertCursor( mouse, cursorBackSurface, gameStartDrawSurface );
+		drawRect[kCursor].top    = min<short>( drawRect[kCursor].top,    mouse.v );
+		drawRect[kCursor].left   = min<short>( drawRect[kCursor].left,   mouse.h );
+		drawRect[kCursor].bottom = max<short>( drawRect[kCursor].bottom, mouse.v + kCursorHeight );
+		drawRect[kCursor].right  = max<short>( drawRect[kCursor].right,  mouse.h + kCursorWidth );
+#endif
+		SDLU_SetSystemCursor( selected < 0 ? SYSTEM_CURSOR_ARROW : SYSTEM_CURSOR_HAND );
+
+		// Copy down everything		
+		if( shouldFullRepaint )
+		{
+			SDLU_BlitFrontSurface( gameStartDrawSurface, &gameStartDrawSurface->clip_rect, &gameStartDrawSurface->clip_rect );
+			shouldFullRepaint = false;
+		}
+		else
+		{
+			for( count=0; count<4; count++ )
+			{		
+				if( drawRect[count].left < drawRect[count].right )
+				{
+					SDLU_MRectToSDLRect( &drawRect[count], &destSDLRect );			
+					SDLU_BlitFrontSurface( gameStartDrawSurface, &destSDLRect, &destSDLRect );
+				}
+			}
+		}
+
+        SDLU_Present();
+
+		// Skip frames? Or delay?
+		if( startMenuTime <= MTickCount( ) )
+		{
+			startMenuTime = MTickCount( );
+			skip = 2;
+		}
+		else
+		{
+			skip = 1;
+			while( startMenuTime > MTickCount( ) ) 
+			{   
+                SDLU_Yield();
+			}
+		}
+	}
+
+	DoFullRepaint = NoPaint;
+
+	if( finished ) 
+	{
+		selected = kTitleItemQuit;
+	}
+	else
+	{
+		SDLU_SetSystemCursor( SYSTEM_CURSOR_OFF );
+	}
+	
+	switch( selected )
+	{
+		case kTitleItemTutorial:
+		case kTitleItem1PGame:
+		case kTitleItem2PGame:
+		case kTitleItemSolitaire:
+			PlayMono( kChime ); 
+			break;
+	}
+
+	SDL_FreeSurface( gameStartSurface );
+	SDL_FreeSurface( gameStartDrawSurface );
+#if USE_CURSOR_SPRITE
+	SDL_FreeSurface( cursorBackSurface );
+#endif
+	
+	QuickFadeOut( NULL );
+
+	switch( selected )
+	{
+		case kTitleItemTutorial:
+			InitGame( kAutoControl, kNobodyControl );
+			level = kTutorialLevel;
+			BeginRound( true );
+			InitTutorial( );
+			QuickFadeIn( NULL );
+			break;				
+
+		case kTitleItem1PGame:
+		case kTitleItem2PGame:
+		case kTitleItemSolitaire:
+        {
+            int player2[] = { 0, kAIControl, kPlayerControl, kNobodyControl };
+            
+            InitGame( kPlayerControl, player2[selected] );
+            BeginRound( true );
+            QuickFadeIn( NULL );
+            break;
+        }
+		
+		case kTitleItemHighScores:
+			ShowHiscore();
+			ShowBestCombo();
+			break;
+			
+		case kTitleItemControls:
+		{
+			int currentID = RandomBefore(kLevels) * 100;
+	
+			DrawPICTInSurface( boardSurface[0], picBoard + currentID );	
+			DrawPICTInSurface( g_frontSurface, picBackdrop + currentID );
+			SDLU_Present();
+	
+			QuickFadeIn( NULL );
+			HandleDialog( kControlsDialog );
+			QuickFadeOut( NULL );
+			goto redo;
+		}
+
+        case kTitleItemCredits:
+        {
+            SharewareVictory();
+            goto redo;
+        }
+		
+		case kTitleItemQuit:
+			finished = true;
+			break;
+	}
+}
+
+void ShowGameOverScreen( void )
+{
+	unsigned int  timer = MTickCount() + (60*3);
+	
+	QuickFadeOut(NULL);
+
+	DrawPICTInSurface( g_frontSurface, picGameOver );
+
+    SDL_Rect widescreenCropBackup = g_widescreenCrop;
+    g_widescreenCrop.y = 30;
+
+    SDLU_Present();
+
+	QuickFadeIn( NULL );
+	do
+	{
+		if( MTickCount() > timer ) break;
+		SDLU_Yield();
+	}
+	while( !AnyKeyIsPressed( ) && !SDLU_Button() );
+	QuickFadeOut( NULL );
+
+    g_widescreenCrop = widescreenCropBackup;
+}
+
+void InitStage( void )
+{
+	stageWindowZRect.top = stageWindowZRect.left = 0;
+	stageWindowZRect.bottom = 32; stageWindowZRect.right = 64; 
+	
+	stageWindowRect = stageWindowZRect;
+	CenterRectOnScreen( &stageWindowRect, 0.5, 0.65 );
+}
+
+void DrawStage( void )
+{
+	SDL_Surface* levelSurface;
+	SDL_Rect     sourceSDLRect, destSDLRect;
+	MRect        numberRect = { 0, kNumberHorizSize/8, kNumberVertSize, kNumberHorizSize*9/8 };
+			
+	switch( players )
+	{
+		case 0:
+		case 2:
+			break;
+			
+		case 1:
+			SDLU_MRectToSDLRect( &stageWindowZRect, &sourceSDLRect );
+			SDLU_MRectToSDLRect( &stageWindowRect,  &destSDLRect );
+			
+			levelSurface = SDLU_InitSurface( &sourceSDLRect, 32 );
+
+			SDLU_AcquireSurface( levelSurface );
+			
+			SDLU_BlitSurface( boardSurface[0], &sourceSDLRect,
+							  levelSurface,    &sourceSDLRect   );
+			
+			if( level < 10 )
+			{
+				OffsetMRect( &numberRect, kNumberHorizSize*3/8, 0 );
+			}
+			
+			DrawCharacter( kCharacterStage,   &numberRect );
+			OffsetMRect( &numberRect, kNumberHorizSize, 0 );
+			DrawCharacter( kCharacterStage+1, &numberRect );
+
+			if( level < 10 )
+			{
+				OffsetMRect( &numberRect, kNumberHorizSize, 0 );
+				DrawCharacter( level + '0', &numberRect );
+			}
+			else
+			{
+				OffsetMRect( &numberRect, kNumberHorizSize*3/4, 0 );
+				DrawCharacter( (level / 10) + '0', &numberRect );
+				OffsetMRect( &numberRect, kNumberHorizSize, 0 );
+				DrawCharacter( (level % 10) + '0', &numberRect );
+			}
+
+			SDLU_BlitFrontSurface( levelSurface, &sourceSDLRect, &destSDLRect );
+			
+			SDLU_ReleaseSurface( levelSurface );
+			SDL_FreeSurface( levelSurface );
+						
+			break;
+	}
+}
+
+void InitGame( int player1, int player2 )
+{
+	playerWindowVisible[0] = true;
+	nextWindowVisible[0] = true;
+	scoreWindowVisible[0] = true;
+	grayMonitorVisible[0] = true;
+	
+	if( player2 == kNobodyControl )
+	{
+		playerWindowVisible[1] = false;
+		nextWindowVisible[1] = false;
+		scoreWindowVisible[1] = false;
+		grayMonitorVisible[1] = false;
+
+		CenterRectOnScreen( &playerWindowRect[0], 0.5, 0.5  );
+		CenterRectOnScreen( &scoreWindowRect[0],  0.5, 0.89 );
+		CenterRectOnScreen( &grayMonitorRect[0],  0.5, 0.11 );
+		CenterRectOnScreen( &nextWindowRect[0],   0.3, 0.25 );
+		
+		CenterRectOnScreen( &stageWindowRect,	  0.3, 0.65 );		
+		CenterRectOnScreen( &opponentWindowRect,  0.3, 0.5 );		
+	}
+	else
+	{
+		playerWindowVisible[1] = true;
+		nextWindowVisible[1] = true;
+		scoreWindowVisible[1] = true;
+		grayMonitorVisible[1] = true;
+
+		CenterRectOnScreen( &playerWindowRect[0], kLeftPlayerWindowCenter, 0.5  );
+		CenterRectOnScreen( &scoreWindowRect[0],  kLeftPlayerWindowCenter, 0.89 );
+		CenterRectOnScreen( &grayMonitorRect[0],  kLeftPlayerWindowCenter, 0.11 );
+		CenterRectOnScreen( &nextWindowRect[0],   0.46, 0.25 );
+
+		CenterRectOnScreen( &playerWindowRect[1], kRightPlayerWindowCenter, 0.5  );
+		CenterRectOnScreen( &scoreWindowRect[1],  kRightPlayerWindowCenter, 0.89 );
+		CenterRectOnScreen( &grayMonitorRect[1],  kRightPlayerWindowCenter, 0.11 );
+		CenterRectOnScreen( &nextWindowRect[1],   0.54, 0.25 );
+
+		CenterRectOnScreen( &stageWindowRect,    0.5, 0.65 );		
+		CenterRectOnScreen( &opponentWindowRect, 0.5, 0.5 );		
+	}
+	
+	// In widescreen mode, move score/gray windows closer to the playfield
+	// so they fit in the cropped screen.
+	ResetWidescreenLayout();
+	
+	nextWindowVisible[0] = ( player1 == kAutoControl )? false: true;
+	
+	players = (player1 == kPlayerControl) + (player2 == kPlayerControl);
+	
+	if( players < 2 )
+	{
+		difficulty[0] = difficulty[1] = kHardLevel;
+	}
+	
+	control[0] = player1;
+	control[1] = player2;
+	
+	score[0] = score[1] = displayedScore[0] = displayedScore[1] = 0;	
+	roundStartScore[0] = roundStartScore[1] = 0;
+	
+	level = 1;
+	credits = (player2 == kNobodyControl)? 1: 5;
+}
+
+void ResetWidescreenLayout()
+{
+	int miniWindowOffset = widescreen ? 4 : 16;
+
+	for (int i = 0; i < 2; i++)
+	{
+		grayMonitorRect[i].top = playerWindowRect[i].top - 32 - miniWindowOffset;
+		grayMonitorRect[i].bottom = playerWindowRect[i].top - miniWindowOffset;
+		scoreWindowRect[i].top = playerWindowRect[i].bottom + miniWindowOffset;
+		scoreWindowRect[i].bottom = playerWindowRect[i].bottom + 16 + miniWindowOffset;
+	}
+}
+
+MBoolean InitCharacter( int player, int level )
+{
+	const Character characterList[] = {
+		{ -1 }, // no zero'th character
+		{ 0, 3, 1, { 8, 8, 8, 8, 8, 8 }, 13, 9, 0, 25,          { 0, _15TO8_8_8(0),     0, 0, _15TO8_8_8(0), 0 }, true },
+		{ 1, 6, 2, { 10, 9, 8, 8, 9, 10 }, 12, 7, 1, 20,        { 0, _15TO8_8_8(223),   7, 0, _15TO8_8_8(0), 0 }, true },
+		{ 2, 9, 3, { 7, 7, 7, 11, 7, 7 }, 10, 6, 2, 17,         { 0, _15TO8_8_8(0),     0, 0, _15TO8_8_8(0), 0 }, false },
+		{ 3, 12, 4, { 11, 10, 9, 8, 7, 6 }, 8, 5, 3, 13,        { 0, _15TO8_8_8(32767), 4, 0, _15TO8_8_8(16912), 4 }, false },
+		{ 4, 15, 0, { 5, 9, 10, 10, 9, 5 }, 7, 4, 4, 10,        { 0, _15TO8_8_8(32767), 1, 0, _15TO8_8_8(0), 0 }, false },
+		{ 5, 17, 1, { 4, 7, 11, 11, 6, 3 }, 7, 2, 5, 8,         { 0, _15TO8_8_8(14835), 8, 0, _15TO8_8_8(0), 0 }, false },
+		{ 6, 18, 2, { 7, 9, 10, 10, 9, 7 }, 6, 4, 6, 7,         { 0, _15TO8_8_8(0),     0, 0, _15TO8_8_8(0), 0 }, false },
+		{ 7, 20, 3, { 5, 10, 10, 10, 10, 5 }, 5, 3, 7, 5,       { 0, _15TO8_8_8(9696),  2, 0, _15TO8_8_8(21151), 3 }, false },
+		{ 8, 21, 4, { 11, 11, 10, 10, 9, 9 }, 4, 3, 8, 5,       { 0, _15TO8_8_8(32738), 5, 0, _15TO8_8_8(0), 0 }, false },
+		{ 9, 22, 0, { 11, 7, 11, 7, 11, 7 }, 3, 1, 9, 4,        { 0, _15TO8_8_8(32356), 5, 0, _15TO8_8_8(17392), 3 }, false },
+		{ 10, 23, 1, { 11, 11, 11, 11, 11, 11 }, 2, 1, 10, 2,   { 0, _15TO8_8_8(6337),  1, 0, _15TO8_8_8(0), 0 }, false },
+		{ 11, 24, 2, { 11, 11, 11, 11, 11, 11 }, 2, 1, 11, 2,   { 1, _15TO8_8_8(32767), 7, 0, _15TO8_8_8(0), 0 }, false },
+		{ -1 }, // skip
+		{ 13, 24, 1, { 11, 11, 11, 11, 11, 11 }, 10, 5, 0, 30,  { 0, _15TO8_8_8(0),     0, 0, _15TO8_8_8(0), 0 }, true }
+	};
+		
+	character[player] = characterList[level];
+	return (character[player].picture != -1);
+}
+
+void PrepareStageGraphics( int type )
+{
+	int player;
+	                            
+	MRect blobBoard = { 0, 0, kGridDown * kBlobVertSize, kGridAcross * kBlobHorizSize };
+	
+	backgroundID = type * 100;
+	
+	DrawPICTInSurface( boardSurface[0], picBoard + backgroundID );	
+	
+	// NOTE: Many levels have no right-side board, so we copy the left
+	// side over to the right side. This way, if DrawPICTInSurface flunks,
+	// we still have a valid picture.
+	
+	SDLU_BlitSurface( boardSurface[0], &boardSurface[0]->clip_rect,
+	                  boardSurface[1], &boardSurface[1]->clip_rect  );
+	
+	DrawPICTInSurface( boardSurface[1], picBoardRight + backgroundID );	
+
+	DrawPICTInSurface( backdropSurface, picBackdrop + backgroundID );
+
+	DrawPICTInSurface( nextSurface, picNext + backgroundID );
+	
+	for( player=0; player<=1; player++ )
+	{
+		SDLU_AcquireSurface( playerSurface[player] );
+		SurfaceDrawBoard( player, &blobBoard );
+		SDLU_ReleaseSurface( playerSurface[player] );
+		
+		CleanSpriteArea( player, &blobBoard );
+	}
+	
+	BeginOpponent( type );
+
+	RedrawBoardContents( 0 );
+	RedrawBoardContents( 1 );
+	
+	RefreshAll( );
+
+	backdropTicks = MTickCount( );
+	backdropFrame = 0;
+}
+
+void BeginRound( MBoolean changeMusic )
+{
+	int player, count, count2;
+	
+	InitGrays( );
+	InitPotentialCombos( );
+	
+	switch( players )
+	{
+		case 0:
+		case 1:
+			if( InitCharacter( 1, level ) )
+			{
+				score[1] = roundStartScore[1] = displayedScore[1] = 0;
+				character[0] = character[1];
+				character[0].zapStyle = RandomBefore(5);
+			}
+			else
+			{
+				TotalVictory( );
+				return;
+			}
+			
+			if( control[1] == kNobodyControl )
+			{
+				InitRandom( 3 );
+			}
+			else
+			{
+				InitRandom( 5 );
+			}
+			break;
+			
+		case 2:
+			score[0] = score[1] = roundStartScore[0] = roundStartScore[1] = displayedScore[0] = displayedScore[1] = 0;	
+
+			InitRandom( 5 );
+
+			SelectRandomLevel( );
+			InitCharacter( 0, level );
+			
+			SelectRandomLevel( );
+			InitCharacter( 1, level );
+			
+			character[0].hints = (difficulty[0] == kEasyLevel) || (difficulty[0] == kMediumLevel);
+			character[1].hints = (difficulty[1] == kEasyLevel) || (difficulty[1] == kMediumLevel);
+			break;
+	}
+	
+	for( player=0; player<=1; player++ )
+	{
+		for( count=0; count<kGridAcross; count++ )
+		{
+			grays[player][count] = 0;
+			
+			for( count2=0; count2<kGridDown; count2++ )
+			{
+				grid[player][count][count2] = kEmpty;
+				suction[player][count][count2] = kNoSuction;
+				charred[player][count][count2] = kNoCharring;
+				glow[player][count][count2] = false;
+			}
+		}
+		
+		nextA[player] = GetPiece( player );
+		nextB[player] = GetPiece( player );
+		nextM[player] = false;
+		nextG[player] = false;
+		
+		halfway[player] = false;
+		
+		unallocatedGrays[player] = 0;
+		anim[player] = 0;
+		lockGrays[player] = 0;
+		roundStartScore[player] = score[player];
+		
+		RedrawBoardContents(player);
+		
+		if( control[player] != kNobodyControl )
+		{
+			role[player] = kWaitForRetrieval;
+		}
+		else
+		{
+			role[player] = kIdlePlayer;
+		}
+	}
+	
+	PrepareStageGraphics( character[1].picture );
+	if( changeMusic ) ChooseMusic( character[1].music );
+	
+	blobTime[0]     = blobTime[1]     = 
+	boredTime[0]    = boredTime[1]    = 
+	hintTime[0]     = hintTime[1]     =
+	timeAI[0]       = timeAI[1]       =
+	fadeCharTime[0] = fadeCharTime[1] = 
+	messageTime     = startTime       =
+	blinkTime[0]    = blinkTime[1]    = GameTickCount( );
+	
+	blinkTime[1] += 60;
+	
+	if( players == 2 )
+		InitDifficulty( );
+    
+    SDLU_Present();
+}
+
+void IncrementLevel( void )
+{
+	level++;
+}
+
+void SelectRandomLevel( void )
+{
+	level = RandomBefore(kLevels) + 1;
+}
+
+void InitDifficulty( )
+{
+	MRect blobBoard = { 0, 0, kGridDown * kBlobVertSize, kGridAcross * kBlobHorizSize };
+	int player;
+	const int selectionRow = 5;
+	int count;
+	MRect blobRect;
+	
+	for( player=0; player<=1; player++ )
+	{
+		// Set up variables
+		role[player] = kChooseDifficulty;
+		colorA[player] = RandomBefore(kBlobTypes)+1;
+		colorB[player] = kEmpty;
+		switch( difficulty[player] )
+		{
+			case kEasyLevel:      blobX[player] = 1; break;
+			case kMediumLevel:    blobX[player] = 2; break;
+			case kHardLevel:      blobX[player] = 3; break;
+			case kUltraLevel:     blobX[player] = 4; break;
+		}
+		
+		blobY[player] = selectionRow;
+		blobR[player] = upRotate;
+		blobTime[player] = GameTickCount( ) + (60*8);
+		animTime[player] = GameTickCount( );
+		shadowDepth[player] = kBlobShadowDepth;
+		magic[player] = false;
+		grenade[player] = false;
+		
+		DrawPICTInSurface( boardSurface[player], picSelectDifficulty + backgroundID );
+		
+		SDLU_AcquireSurface( playerSurface[player] );
+
+		SurfaceDrawBoard( player, &blobBoard );
+		
+		grid[player][0][selectionRow] = kGray;
+		suction[player][0][selectionRow] = kEasyGray;
+		charred[player][0][selectionRow] = kNoCharring;
+		CalcBlobRect( 0, selectionRow, &blobRect );
+		SurfaceDrawBlob( player, &blobRect, kGray, kEasyGray, kNoCharring );
+		
+		grid[player][kGridAcross-1][selectionRow] = kGray;	
+		suction[player][kGridAcross-1][selectionRow] = kHardGray;
+		charred[player][kGridAcross-1][selectionRow] = kNoCharring;
+		CalcBlobRect( kGridAcross-1, selectionRow, &blobRect );
+		SurfaceDrawBlob( player, &blobRect, kGray, kHardGray, kNoCharring );
+
+		CalcBlobRect( 1, selectionRow, &blobRect );
+		blobRect.top -= 4; blobRect.bottom += 4;
+		blobRect.left += 4; blobRect.right -= 4;
+		for( count=1; count<=4; count++ )
+		{
+			DrawCharacter( count + '0', &blobRect );
+			OffsetMRect( &blobRect, kBlobHorizSize, 0 );
+		}
+		
+		SDLU_ReleaseSurface( playerSurface[player] );
+		
+		DrawSpriteBlobs( player, kNoSuction );
+		CleanSpriteArea( player, &blobBoard );
+	}
+}
+
+void ChooseDifficulty( int player )
+{
+	MRect blobBoard = { 0, 0, kGridDown * kBlobVertSize, kGridAcross * kBlobHorizSize };
+	const int selectionRow = 5;
+	const int difficultyMap[kGridAcross] = {kEasyLevel, kEasyLevel, kMediumLevel, kHardLevel, kUltraLevel, kUltraLevel};
+	const int fallingSpeed[kGridAcross] = {0, 15, 9, 7, 4, 0};
+	const int startGrays[kGridAcross] = {0, 0,  0, 10, 20, 0};
+	const int difficultyFrame[] = { kNoSuction, blobBlinkAnimation, blobBlinkAnimation,
+									  blobJiggleAnimation, blobCryAnimation, kNoSuction };
+	int oldX = blobX[player];
+	
+	PlayerControl( player );
+	if( blobX[player] != oldX ) anim[player] = 0;
+	
+	UpdateTweak( player, difficultyFrame[blobX[player]] );
+
+	if( GameTickCount( ) >= blobTime[player] )
+	{
+		if( player == 1 && PICTExists( picBoardRight + backgroundID ) )
+		{
+			DrawPICTInSurface( boardSurface[player], picBoardRight + backgroundID );
+		}
+		else
+		{
+			DrawPICTInSurface( boardSurface[player], picBoard + backgroundID );
+		}
+		
+		SDLU_AcquireSurface( playerSurface[player] );
+		SurfaceDrawBoard( player, &blobBoard );
+		SDLU_ReleaseSurface( playerSurface[player] );
+		
+		CleanSpriteArea( player, &blobBoard );
+	
+		grid[player][0][selectionRow] = kEmpty;
+		grid[player][5][selectionRow] = kEmpty;
+												
+		suction[player][0][selectionRow] = kNoSuction;
+		suction[player][5][selectionRow] = kNoSuction;
+		
+		difficulty[player]          = difficultyMap[ blobX[player] ];
+		character[player].dropSpeed = fallingSpeed[ blobX[player] ];
+		unallocatedGrays[player] = lockGrays[player] = startGrays[blobX[player]];
+		character[player].hints     = (startGrays[blobX[player]] == 0);
+		role[player] = kWaitingToStart;
+		
+		PlayStereoFrequency( player, kPause, player );
+	}
+}
+
+const char *gameCredits[][6] = 
+{
+	{ "Programming", "John Stiles", "", "", "", "" },
+	{ "Artwork", "Kate Davis", "Leanne Stiles", "Arnauld de la Grandiere", "Bob Frasure", "Ryan Bliss" },
+	{ "Music", "Leanne Stiles", "fmod", "Lizardking", "Armadon, Explizit", "Leviathan, Nemesis" },
+	{ "Music", "Jester, Pygmy", "Siren", "Sirrus", "Scaven, FC", "Spring" }, 		  
+	{ "Music", "Timewalker", "Jason, Silents", "Chromatic Dragon", "Ng Pei Sin", "" },
+	{ "Source Port", "Iliyas Jorio", "github.com/jorio/candycrisis", "", "", "" },
+	{ "Special Thanks", "Sam Lantinga", "Carey Lening", "modarchive.com", "digitalblasphemy.com", "" },	  
+	{ "", "", "", "", "", "" }
+};
+
+void SharewareVictory( void )
+{
+	SkittlesFontPtr textFont, titleFont;
+	SDL_Surface*    backBuffer;
+	SDL_Surface*    frontBuffer;
+	SDL_Rect        creditSrcSDLRect, bufferSrcSDLRect, bufferDstSDLRect;
+	MRect           creditSrcRect = { 0, 0, 369, 150 };
+	MRect           bufferSrcRect = { 0, 50, 480, 350 };
+	MRect           bufferDstRect = { 0, 0, 480, 300 };
+	MPoint          dPoint = { 450, 50 }, lPoint, cPoint;
+	int             scroll, x, y;
+	MTicks          ticks;
+	const char*     text;
+	int             thisFade;
+	const char fade[120] =   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0
+				               1, 2, 3, 4, 5, 6, 7, 8, 9,10, //1
+				              11,12,13,14,15,16,17,18,19,20, //2
+				              20,20,20,20,20,20,20,20,20,20, //3
+				              20,20,20,20,20,20,20,20,20,20, //4
+				              20,20,20,20,20,20,20,20,20,20, //5
+				              20,20,20,20,20,20,20,20,20,20, //6
+				              20,20,20,20,20,20,20,20,20,20, //7
+				              20,20,20,20,20,20,20,20,20,20, //8
+							  20,19,18,17,16,15,14,13,12,11, //9
+							  10, 9, 8, 7, 6, 5, 4, 3, 2, 1, //10
+							   0, 0, 0, 0, 0, 0, 0, 0, 0, 0  //11
+						     };
+	              
+	titleFont = GetFont( picFont );
+	textFont = GetFont( picTinyFont );
+	
+	SDLU_MRectToSDLRect( &creditSrcRect, &creditSrcSDLRect );
+	SDLU_MRectToSDLRect( &bufferSrcRect, &bufferSrcSDLRect );
+	SDLU_MRectToSDLRect( &bufferDstRect, &bufferDstSDLRect );
+	
+	DrawPICTInSurface( g_frontSurface, picSharewareVictory );
+    SDLU_Present();
+	
+	backBuffer = SDLU_InitSurface( &bufferDstSDLRect, 32 );
+	
+	SDLU_BlitSurface( g_frontSurface, &bufferSrcSDLRect,
+	                  backBuffer,   &bufferDstSDLRect   );
+
+	frontBuffer = SDLU_InitSurface( &bufferDstSDLRect, 32 );
+	
+	QuickFadeIn( NULL );	
+
+	ChooseMusic( 12 ); 
+	
+	ticks = MTickCount();
+	for( scroll=0; scroll<1500; scroll++ )
+	{
+		SDLU_AcquireSurface( frontBuffer );	
+		
+		SDLU_BlitSurface( backBuffer,   &bufferDstSDLRect,
+		                  frontBuffer,  &bufferDstSDLRect  );
+		
+		ticks += 3;
+		lPoint = dPoint;
+		for( y=0; y<8; y++ )
+		{
+			if( y != 3 && y != 4 )
+			{
+				cPoint.v = lPoint.v + 25;
+				cPoint.h = lPoint.h;
+				if( cPoint.v > 480 ) break;
+				
+				if( cPoint.v > 0 )
+				{
+					text = gameCredits[y][0];
+					thisFade = fade[cPoint.v >> 2];
+
+					while( *text )
+					{
+						SurfaceBlitWeightedCharacter( titleFont, *text++, &cPoint, 255, 255, 0, _5TO8(thisFade) );
+					}
+				}
+
+				lPoint.v += 50;
+			}
+			
+			for( x=1; x<6; x++ )
+			{
+				if( gameCredits[y][x][0] )
+				{
+					cPoint.v = lPoint.v;
+					cPoint.h = lPoint.h + 20;
+					if( cPoint.v > 480 ) break;
+
+					if( cPoint.v > 0 )
+					{
+						text = gameCredits[y][x];
+						thisFade = fade[cPoint.v >> 2];
+
+						while( *text )
+						{
+							SurfaceBlitWeightedCharacter( textFont, *text++, &cPoint, 255, 255, 0, _5TO8(thisFade) );
+						}
+					}
+
+					lPoint.v += 20;
+				}
+			}
+		}
+		
+		SDLU_ReleaseSurface( frontBuffer );
+
+		dPoint.v--;
+
+		SDLU_BlitFrontSurface( frontBuffer, &bufferDstSDLRect, &bufferSrcSDLRect );
+        SDLU_Present();
+
+		do
+		{
+			if( SDLU_Button() ) goto out;	
+            SDLU_Yield();	
+		}
+		while( ticks >= MTickCount() );
+	}
+	
+	do
+	{
+        SDLU_Yield();
+	}
+	while( !AnyKeyIsPressed( ) && !SDLU_Button() );
+
+out:
+	QuickFadeOut( NULL );	
+	
+	SDL_FreeSurface( backBuffer );
+	SDL_FreeSurface( frontBuffer );
+}
+
+void RegisteredVictory( void )
+{
+	SkittlesFontPtr textFont, titleFont, bubbleFont;
+	SDL_Surface*    backBuffer;
+	SDL_Surface*    frontBuffer;
+	MPoint          dPoint[] = { { 230, 340 }, { 230, 30 }, { 230, 30 }, { 30, 30 }, { 30, 340 }, { 230, 340 }, { 230, 30 } }; 
+	MPoint          bubblePoint, textPoint, shadowPoint;
+	MPoint          setPoint[7][6];
+	MPoint          msgSetPoint[7][2];
+	MTicks          ticks;
+	int             vertScroll, picture, weight, line, minimum;
+	int             scrollDir[] = {1, -1, 1, -1, 1, -1, -1};
+	int             spacing[] = {40, 19, 19, 19, 23, 19, 23 };
+	const char*     text;
+	SDL_Rect        fullSDLRect = { 0, 0, 640, 480 };
+	SDL_Rect        highSDLRect = { 0, 0, 640, 480 };
+	SDL_Rect        lowSDLRect = { 0, 250, 640, 480 };
+	SDL_Rect        backBufferSDLRect = { 0, 0, 640, 730 };
+	SDL_Rect        scrollSDLRect;
+
+	const char *messages[7][2] =
+	{
+		{ "Congratulations!", "" },
+		{ "You've managed to vaporize all", "of the rampaging candies!" },
+		{ "Your quick thinking and sharp", "reflexes have saved the day." },
+		{ "", "" },
+		{ "", "" },
+		{ "", "" },
+		{ "Thanks for playing Candy Crisis!", "" },
+	};
+
+	// In widescreen mode, move vertical text positions closer to the center
+	if (widescreen)
+	{
+		for (int i = 0; i < arrsize(dPoint); i++)
+		{
+			if (dPoint[i].v >= 130)
+				dPoint[i].v -= 20;
+			else
+				dPoint[i].v += 20;
+		}
+	}
+	
+	textFont = GetFont( picTinyFont );
+	titleFont = GetFont( picHiScoreFont );
+	bubbleFont = GetFont( picBubbleFont );
+	
+	ChooseMusic( 14 ); 
+
+	for( picture=0; picture<7; picture++ )
+	{
+		for( line=0; line<2; line++ )
+		{
+			if (dPoint[picture].v >= 130) {
+				msgSetPoint[picture][line].v = (widescreen? 120: 100) + line * 30;
+			} else {
+				msgSetPoint[picture][line].v = (widescreen? 380: 300) + line * 30;
+			}
+			msgSetPoint[picture][line].h = 320 - (GetTextWidth( titleFont, messages[picture][line] ) / 2);
+		}
+		
+		for( line=0; line<6; line++ )
+		{
+			SkittlesFontPtr font;
+
+			if( line == 0 )
+			{
+				font = titleFont;
+				textPoint.v = 45;				
+			}
+			else
+			{
+				font = textFont;
+				textPoint.v = 65 + (spacing[picture] * line);				
+			}
+
+			textPoint.h = (bubbleFont->width['*'] - GetTextWidth( font, gameCredits[picture][line] )) / 2;
+
+			setPoint[picture][line].v = dPoint[picture].v + textPoint.v;
+			setPoint[picture][line].h = dPoint[picture].h + textPoint.h;
+		}
+		
+		minimum = 640;
+		for( line=1; line<6; line++ )
+		{
+			if( setPoint[picture][line].h < minimum ) minimum = setPoint[picture][line].h;
+		}
+		
+		for( line=1; line<6; line++ )
+		{
+			setPoint[picture][line].h = minimum;
+		}		
+	}
+	
+	backBuffer  = SDLU_InitSurface( &backBufferSDLRect, 32 );
+	frontBuffer = SDLU_InitSurface( &fullSDLRect, 32 );
+	
+	for( picture = 0; picture<7; picture++ )
+	{
+		scrollSDLRect = ( scrollDir[picture] > 0 )? highSDLRect: lowSDLRect;
+		
+		DrawPICTInSurface( backBuffer, picture + picVictory1 );
+
+		SDLU_BlitFrontSurface( backBuffer, &scrollSDLRect, &fullSDLRect );
+        SDLU_Present();
+
+		QuickFadeIn( NULL );
+		
+		ticks = MTickCount();
+		for( vertScroll = 0; vertScroll < 250; vertScroll++ )
+		{
+			SDLU_AcquireSurface( frontBuffer );
+					
+			SDLU_BlitSurface( backBuffer,  &scrollSDLRect,
+			                  frontBuffer, &fullSDLRect );
+
+			weight = vertScroll - 20;
+			for( line=0; line<2; line++ )
+			{
+				textPoint = msgSetPoint[picture][line];
+				shadowPoint.v = textPoint.v + 1;
+				shadowPoint.h = textPoint.h + 1;
+				
+				text = messages[picture][line];
+				
+				while( *text && weight > 0 )
+				{
+					int fixedWeight = (weight > 31)? 31: weight;
+					
+					SurfaceBlitWeightedCharacter( titleFont, *text, &shadowPoint, 0,   0,   0,   _5TO8(fixedWeight) );
+					SurfaceBlitWeightedCharacter( titleFont, *text, &textPoint,   255, 255, 255, _5TO8(fixedWeight) );
+					weight--;
+					text++;
+				}
+			}
+			
+			bubblePoint = dPoint[picture];
+			weight = ( vertScroll <= 210 )? vertScroll - 10: 241 - vertScroll;
+			if( weight < 0  ) weight = 0;
+			if( weight > 31 ) weight = 31;
+			
+			if( weight > 0 )
+			{
+                int bubbleWeight = (weight+1)>>1;
+                
+				SurfaceBlitWeightedCharacter( bubbleFont, '*', &bubblePoint, 255, 255, 255, _5TO8(bubbleWeight) );
+				
+				for( line=0; line<6; line++ )
+				{
+					SkittlesFontPtr font = (line == 0)? titleFont: textFont;
+					
+					textPoint = setPoint[picture][line];
+					text = gameCredits[picture][line];
+					
+					while( *text )
+					{
+						SurfaceBlitWeightedCharacter( font, *text++, &textPoint, 0, 0, 0, _5TO8(weight) );
+					}
+				}
+			}
+			
+			SDLU_ReleaseSurface( frontBuffer );
+			
+			SDLU_BlitFrontSurface( frontBuffer, &fullSDLRect, &fullSDLRect );
+            SDLU_Present();
+            
+			scrollSDLRect.y += scrollDir[picture];
+			
+			ticks += 4;
+			do
+			{ 
+				if( SDLU_Button() ) vertScroll = 250;
+			    SDLU_Yield();
+			}			
+			while( ticks >= MTickCount() );
+		}
+
+		QuickFadeOut( NULL );
+	}
+	
+	SDL_FreeSurface( backBuffer  );
+	SDL_FreeSurface( frontBuffer );
+}
+
+void TotalVictory( void )
+{	
+	AddHiscore( score[0] );
+	QuickFadeOut( NULL );	
+
+	DoFullRepaint = NoPaint;
+
+    RegisteredVictory( );
+	
+	showStartMenu = true;
+}
--- a/src/level.cpp
+++ /dev/null
@@ -1,1438 +1,0 @@
-// level.c
-
-#include <stdlib.h>
-#include <math.h>
-#include <algorithm>
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "level.h"
-#include "score.h"
-#include "random.h"
-#include "grays.h"
-#include "gameticks.h"
-#include "players.h"
-#include "graymonitor.h"
-#include "opponent.h"
-#include "gworld.h"
-#include "graphics.h"
-#include "music.h"
-#include "control.h"
-#include "tweak.h"
-#include "soundfx.h"
-#include "next.h"
-#include "hiscore.h"
-#include "victory.h"
-#include "blitter.h"
-#include "zap.h"
-#include "keyselect.h"
-#include "tutorial.h"
-#include "pause.h"
-
-MRect stageWindowZRect, stageWindowRect;
-Character character[2];
-int level, players, credits, difficulty[2] = {kHardLevel, kHardLevel};
-int difficultyTicks, backdropTicks, backdropFrame;
-
-#define kNumSplats 16
-#define kIdleSplat -2
-#define kFallingSplat -1
-#define kTitleItems 8
-#define kIncrementPerFrame 2
-#define kSplatType 4
-
-using std::min;
-using std::max;
-
-const int startSkip = 1;
-static MBoolean shouldFullRepaint = false;
-static MTicks startMenuTime = 0;
-static int splatState[kNumSplats], splatColor[kNumSplats], splatSide[kNumSplats];
-static MRect splatBlob[kNumSplats];
-
-enum
-{
-    kTitleItemTutorial,
-    kTitleItem1PGame,
-    kTitleItem2PGame,
-    kTitleItemSolitaire,
-    kTitleItemHighScores,
-    kTitleItemControls,
-    kTitleItemCredits,
-    kTitleItemQuit,
-};
-
-struct TitleItemDef
-{
-	const char* name;
-	MRGBColor color1;
-	MRGBColor color2;
-	MRect rect;
-};
-
-static const TitleItemDef k_titleItemDefs[kTitleItems] =
-{
-		{ "\x03 Tutorial Mode",     {204, 67,137}, {101, 74,207}, {155, 203, 207, 426} },
-		{ "\x03 One Player Game",   { 35, 31,240}, { 81,237,252}, {225, 179, 281, 451} },
-		{ "\x03 Two Player Game",   {212,194, 48}, {255,196, 56}, {297, 182, 352, 454} },
-		{ "\x03 Solitaire Crisis",  {102,247,106}, {125,237,179}, {358, 183, 428, 458} },
-		{ "\x03 High Scores",       {234,244,132}, {192,218, 85}, {429, 280, 478, 390} },
-		{ "\x03 Controls",          { 64, 88,212}, { 62, 87,205}, {430, 187, 479, 280} },
-		{ "\x03 Credits",           {245,  7, 78}, {254,128,156}, {  6, 370,  59, 423} },
-		{ "\x03 Quit",              {107,105,106}, {169,167,168}, {433, 390, 477, 446} }
-};
-
-const int kCursorWidth  = 32;
-const int kCursorHeight = 32;
-
-#if USE_CURSOR_SPRITE
-static void InsertCursor( MPoint mouseHere, SDL_Surface* scratch, SDL_Surface* surface )
-{
-	SkittlesFontPtr cursorFont = GetFont( picFont );
-	SDL_Rect        cursorBackSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
-	SDL_Rect        cursorFrontSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
-	MPoint          mouseHereToo = mouseHere;
-	
-	cursorFrontSDLRect.x = mouseHere.h;
-	cursorFrontSDLRect.y = mouseHere.v;
-	
-	SDLU_BlitSurface( surface, &cursorFrontSDLRect,
-	                  scratch, &cursorBackSDLRect   );
-	
-	SDLU_AcquireSurface( surface );
-	SurfaceBlitCharacter( cursorFont, '\x05', &mouseHere,    0,   0,   0,   0 );
-	SurfaceBlitCharacter( cursorFont, '\x04', &mouseHereToo, 255, 255, 255, 0 );
-	SDLU_ReleaseSurface( surface );
-}
-
-static void RemoveCursor( MPoint mouseHere, SDL_Surface* scratch, SDL_Surface* surface )
-{
-	SDL_Rect      cursorBackSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
-	SDL_Rect      cursorFrontSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
-	
-	cursorFrontSDLRect.x = mouseHere.h;
-	cursorFrontSDLRect.y = mouseHere.v;
-	
-	SDLU_BlitSurface( scratch, &cursorBackSDLRect,
-	                  surface, &cursorFrontSDLRect );
-}
-#endif
-
-static void GameStartMenuRepaint()
-{
-	shouldFullRepaint = true;
-}
-
-void GameStartMenu( void )
-{
-	MBoolean        useNewTitle = widescreen;
-
-	// NOTE: be wary of initializing variables here! This function can run top-to-bottom
-	// multiple times in a row, thanks to "redo". Put initializations after redo.
-    SDL_Surface*    gameStartSurface;
-    SDL_Surface*    gameStartDrawSurface;
-#if USE_CURSOR_SPRITE
-    SDL_Surface*    cursorBackSurface;
-#endif
-	SDL_Rect        backdropSDLRect = { 0, 0, 640, 480 };
-	SDL_Rect        cursorBackSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
-	SDL_Rect        destSDLRect;
-	MRect           drawRect[4], chunkRect, tempRect;
-	int             blob, count, oldGlow, splat, chunkType, selected;
-	int             skip;
-	MPoint          mouse;
-	MPoint          dPoint;
-	unsigned int    black;
-	int             combo[2], comboBright[2], missBright[2];
-	SkittlesFontPtr smallFont = GetFont( picFont );
-	SkittlesFontPtr tinyFont = GetFont( picTinyFont );
-	SDL_Rect        meterRect[2] = { { 30, 360, 110, 20 }, { 530, 360, 110, 20 } };
-	TitleItemDef    titleItems[kTitleItems];
-    int             titleGlow[kTitleItems];
-    int             shouldAddBlob;
-    const int       kTitleGlowOff = useNewTitle? 150: 192;
-    const bool      secretCreditsItem = !useNewTitle;
-    
-	const int       kLeftSide = 0, kRightSide = 1, kGlow = 2, kCursor = 3;
-	
-	
-redo:
-	memcpy(titleItems, k_titleItemDefs, sizeof(titleItems));
-
-	combo[0] = combo[1] = 0;
-	comboBright[0] = comboBright[1] = 0;
-	missBright[0] = missBright[1] = 0;
-		
-	skip = 1;
-	selected = -1;
-	mouse.h = mouse.v = 0;
-	
-	if( finished ) return;
-	
-	if( musicSelection != 13 ) ChooseMusic( 13 );
-	
-	for( count=0; count<kTitleItems; count++ )
-	{
-		titleGlow[count] = kTitleGlowOff;
-	}
-
-    if (secretCreditsItem)
-    {
-        titleGlow[kTitleItemCredits] = 0;
-    }
-	
-	for( count=0; count<kNumSplats; count++ )
-	{
-		splatState[count] = kIdleSplat;
-	}
-	
-	// make background surface
-	gameStartSurface     = LoadPICTAsSurface( picGameStart, 32 );
-	black = SDL_MapRGB( gameStartSurface->format, 0, 0, 0 );
-
-	// make cursor backing store
-#if USE_CURSOR_SPRITE
-	cursorBackSurface    = SDLU_InitSurface( &cursorBackSDLRect, 32 );
-	SDL_FillRect( cursorBackSurface, &cursorBackSDLRect, black );
-#endif
-	
-	// make drawing surface
-	gameStartDrawSurface = SDLU_InitSurface( &backdropSDLRect, 32 );
-	if (!useNewTitle)
-	{
-		SDLU_BlitSurface(gameStartSurface, &gameStartSurface->clip_rect,
-						 gameStartDrawSurface, &gameStartDrawSurface->clip_rect);
-	}
-	else
-	{
-		// Prepare new title screen
-		SDL_FillRect(gameStartDrawSurface, &gameStartDrawSurface->clip_rect, black);
-
-		// Copy logo from original title screen to where we want it
-		SDL_Rect r1 = {0, 0, 640, 150};
-		SDL_Rect r2 = {0, 70, 640, 150};
-		SDLU_BlitSurface(gameStartSurface, &r1, gameStartDrawSurface, &r2);
-
-		// Now we're going to draw title items on gameStartSurface
-		SDL_FillRect(gameStartSurface, &gameStartSurface->clip_rect, black);
-		SDLU_AcquireSurface(gameStartSurface);
-
-		SkittlesFontPtr font = GetFont(picFont);
-		int left = 225;
-		dPoint.h = left;
-		dPoint.v = 215;
-		for (int i = 0; i < kTitleItems; i++)
-		{
-			auto &item = titleItems[i];
-			item.rect.left = dPoint.h;
-			item.rect.top = dPoint.v - 6;
-			item.rect.bottom = dPoint.v + 16 + 6;
-			auto nameLength = strlen(item.name);
-			for (int charNo = 0; charNo < nameLength; charNo++)
-			{
-				char c = item.name[charNo];
-				float p = charNo / (float) (nameLength - 1);
-				int red = item.color1.red * (1.0f - p) + item.color2.red * p;
-				int green = item.color1.green * (1.0f - p) + item.color2.green * p;
-				int blue = item.color1.blue * (1.0f - p) + item.color2.blue * p;
-				SurfaceBlitCharacter(font, c, &dPoint, red, green, blue, 1);
-			}
-			item.rect.right = dPoint.h;
-			dPoint.h = left;
-			dPoint.v += 24;
-		}
-		SDLU_ReleaseSurface(gameStartSurface);
-	}
-	
-	// darken menu items
-	for( count=0; count<kTitleItems; count++ )
-	{
-		SurfaceBlitColorOver( gameStartSurface,  gameStartDrawSurface,
-							  &titleItems[count].rect, &titleItems[count].rect, 
-							   0, 0, 0, titleGlow[count] );
-	}
-	
-	SDLU_BlitFrontSurface( gameStartDrawSurface, &backdropSDLRect, &backdropSDLRect );
-    SDLU_Present();
-
-	WaitForRelease();
-
-	QuickFadeIn( NULL );
-	
-	DoFullRepaint = GameStartMenuRepaint;
-
-    shouldAddBlob = 5;
-	startMenuTime = MTickCount( );
-	while( ( selected == -1 || !SDLU_Button() ) && !finished )
-	{	
-		startMenuTime += skip;
-
-        UpdateSound();
-        
-		// Add a new falling blob
-        --shouldAddBlob;
-		if (shouldAddBlob <= 0)
-		{
-			for( blob=0; blob<kNumSplats; blob++ )
-			{
-				if( splatState[blob] == kIdleSplat )
-				{
-					splatSide[blob] = RandomBefore(2);
-					splatBlob[blob].top = -24 - RandomBefore(15);
-					splatBlob[blob].left = (splatSide[blob] == 0)? RandomBefore( 110 ): 640 - kBlobHorizSize - RandomBefore( 110 );
-					splatBlob[blob].bottom = splatBlob[blob].top + kBlobVertSize;
-					splatBlob[blob].right = splatBlob[blob].left + kBlobHorizSize;
-					splatColor[blob] = ((startMenuTime >> 2) % kBlobTypes) + 1;
-					splatState[blob] = kFallingSplat;
-					
-					break;
-				}
-			}
-            shouldAddBlob = 5;
-		}
-	
-		// Erase and redraw falling blobs and chunks
-
-		SDLU_AcquireSurface( gameStartDrawSurface );
-	
-		// Take the cursor out of the scene
-#if USE_CURSOR_SPRITE
-		RemoveCursor( mouse, cursorBackSurface, gameStartDrawSurface );
-		drawRect[kCursor].top    = mouse.v;
-		drawRect[kCursor].left   = mouse.h;
-		drawRect[kCursor].bottom = mouse.v + kCursorHeight;
-		drawRect[kCursor].right  = mouse.h + kCursorWidth;
-#endif
-		
-		// Inverted rectangles mean "nothing to do."
-		drawRect[kLeftSide].top    = drawRect[kRightSide].top    = drawRect[kGlow].top    = 
-		drawRect[kLeftSide].left   = drawRect[kRightSide].left   = drawRect[kGlow].left   = 9999;
-		drawRect[kLeftSide].bottom = drawRect[kRightSide].bottom = drawRect[kGlow].bottom =
-		drawRect[kLeftSide].right  = drawRect[kRightSide].right  = drawRect[kGlow].right  = -9999;
-	
-		// Get cursor position		
-		SDLU_GetMouse( &mouse );
-		if( mouse.v > (widescreen ? 400 : 460) )
-			mouse.v = (widescreen ? 400 : 460);
-		
-		// Erase falling blobs
-		for( blob=0; blob<kNumSplats; blob++ )
-		{
-			if( splatState[blob] == kFallingSplat )
-			{
-				SDL_FillRect( gameStartDrawSurface, SDLU_MRectToSDLRect( &splatBlob[blob], &destSDLRect ), black );
-				UnionMRect( &drawRect[splatSide[blob]], &splatBlob[blob], &drawRect[splatSide[blob]] );
-				
-				OffsetMRect( &splatBlob[blob], 0, startSkip * (6 + (splatBlob[blob].bottom / 20)) );
-			}
-			else if( splatState[blob] >= kIncrementPerFrame )
-			{
-				for( splat=-3; splat<=3; splat++ )
-				{
-					if( splat )
-					{
-						chunkRect = splatBlob[blob];
-						GetZapStyle( 0, &chunkRect, &splatColor[blob], &chunkType, splat, splatState[blob]-kIncrementPerFrame, kSplatType );
-						SDL_FillRect( gameStartDrawSurface, SDLU_MRectToSDLRect( &chunkRect, &destSDLRect ), black );
-						UnionMRect( &drawRect[splatSide[blob]], &chunkRect, &drawRect[splatSide[blob]] );
-					}
-				}
-				
-				SDL_FillRect( gameStartDrawSurface, SDLU_MRectToSDLRect( &splatBlob[blob], &destSDLRect ), black );
-				UnionMRect( &drawRect[splatSide[blob]], &splatBlob[blob], &drawRect[splatSide[blob]] );
-			}
-		}
-	
-		// Draw combo meters
-        
-		for( count=0; count<2; count++ )
-		{
-			int bright = comboBright[count];
-			int mBright = missBright[count];
-            if( bright || mBright )
-			{
-                SDL_FillRect( gameStartDrawSurface, &meterRect[count], black );
-                UnionMRect( &drawRect[count], SDLU_SDLRectToMRect( &meterRect[count], &tempRect ), &drawRect[count] );
-                
-                if( mBright > 1 )
-                {
-                  dPoint.v = meterRect[count].y;
-                  dPoint.h = meterRect[count].x + 10;
-                  SurfaceBlitCharacter( smallFont, 'M', &dPoint, mBright, mBright >> 2, mBright >> 2, 1 );
-                  SurfaceBlitCharacter( smallFont, 'I', &dPoint, mBright, mBright >> 2, mBright >> 2, 1 );
-                  SurfaceBlitCharacter( smallFont, 'S', &dPoint, mBright, mBright >> 2, mBright >> 2, 1 );
-                  SurfaceBlitCharacter( smallFont, 'S', &dPoint, mBright, mBright >> 2, mBright >> 2, 1 );
-                  missBright[count] -= 8;
-                }
-                else if( (combo[count] >= 10) && (bright > 1) )
-                {
-				  char  number[16] = { 0 };
-				  char* scan;
-				  sprintf( number, "%d", combo[count] );
-
-                  dPoint.v = meterRect[count].y + 3;
-                  dPoint.h = meterRect[count].x;
-     
-				  SurfaceBlitCharacter( tinyFont, 'C', &dPoint, bright, bright, bright, 1 );
-				  SurfaceBlitCharacter( tinyFont, 'O', &dPoint, bright, bright, bright, 1 );
-				  SurfaceBlitCharacter( tinyFont, 'M', &dPoint, bright, bright, bright, 1 );
-				  SurfaceBlitCharacter( tinyFont, 'B', &dPoint, bright, bright, bright, 1 );
-				  SurfaceBlitCharacter( tinyFont, 'O', &dPoint, bright, bright, bright, 1 );
-				  SurfaceBlitCharacter( tinyFont, ' ', &dPoint, bright, bright, bright, 1 );
-				  dPoint.v -= 3;
-				
-				  for( scan = number; *scan; scan++ )
-				  {
-				 	SurfaceBlitCharacter( smallFont, *scan, &dPoint, bright>>2, bright>>2, bright, 1 );
-			      }
-                  
-                  comboBright[count] -= 16;
-				}
-                else 
-                {
-                  comboBright[count] = 0;
-                }
-   			}
-		}
-
-		// Redraw falling blobs
-		for( blob=0; blob<kNumSplats; blob++ )
-		{
-			if( splatState[blob] == kFallingSplat )
-			{
-				int bottom = widescreen? 420: 480;
-				if( splatBlob[blob].bottom >= bottom ) 
-				{
-					splatBlob[blob].top = bottom - kBlobVertSize;
-					splatBlob[blob].bottom = bottom;
-					splatState[blob] = 1;
-					
-					// Process combos
-					if( mouse.v > bottom &&
-					    mouse.h >= (splatBlob[blob].left - 30) &&
-					    mouse.h <= (splatBlob[blob].right + 10)    )
-					{
-						combo[splatSide[blob]]++;
-						comboBright[splatSide[blob]] = 255;
-					}
-					else
-					{
-                        if( combo[splatSide[blob]] >= 10 ) missBright[splatSide[blob]] = 255;
-						combo[splatSide[blob]] = 0;
-                        comboBright[splatSide[blob]] = 0;                        
-					}
-				}
-				else
-				{
-					SurfaceDrawSprite( &splatBlob[blob], splatColor[blob], kNoSuction );
-					UnionMRect( &drawRect[splatSide[blob]], &splatBlob[blob], &drawRect[splatSide[blob]] );
-				}
-			}
-			
-			if( splatState[blob] >= 0 && splatState[blob] <= kZapFrames )
-			{
-				if( splatState[blob] <= (kZapFrames - kIncrementPerFrame) ) 
-				{
-                    chunkType = 0;
-                    
-					for( splat=-3; splat<=3; splat++ )
-					{
-						if( splat )
-						{
-							chunkRect = splatBlob[blob];
-							GetZapStyle( 0, &chunkRect, &splatColor[blob], &chunkType, splat, splatState[blob], kSplatType );
-							SurfaceDrawSprite( &chunkRect, splatColor[blob], chunkType );
-							UnionMRect( &drawRect[splatSide[blob]], &chunkRect, &drawRect[splatSide[blob]] );
-						}
-					}
-					
-					SurfaceDrawSprite( &splatBlob[blob], splatColor[blob], chunkType );
-					UnionMRect( &drawRect[splatSide[blob]], &splatBlob[blob], &drawRect[splatSide[blob]] );
-				}
-				
-				splatState[blob] += kIncrementPerFrame;
-				if( splatState[blob] > kZapFrames ) splatState[blob] = kIdleSplat;
-			}
-		}
-				
-		SDLU_ReleaseSurface( gameStartDrawSurface );		
-		
-		// Find mouse coords
-		
-		selected = -1;			
-		for( count=0; count<kTitleItems; count++ )
-		{
-			if( MPointInMRect( mouse, &titleItems[count].rect ) )
-			{
-				selected = count;
-				break;
-			}
-		}
-
-        if (secretCreditsItem)
-        {
-            titleGlow[kTitleItemCredits] = 0;
-        }
-
-		// update glows
-		for (int glowUpdate=0; glowUpdate < kTitleItems; ++glowUpdate)
-		{
-			const MRect& titleRect = titleItems[glowUpdate].rect;
-			oldGlow = titleGlow[glowUpdate];
-			
-			if( selected == glowUpdate )
-			{
-				titleGlow[glowUpdate] -= (6 * startSkip);
-				if( titleGlow[glowUpdate] < 0 ) titleGlow[glowUpdate] = 0;
-			}
-			else 
-			{
-				titleGlow[glowUpdate] += (6 * startSkip);
-				if( titleGlow[glowUpdate] > kTitleGlowOff ) titleGlow[glowUpdate] = kTitleGlowOff;
-			}
-			
-			if( titleGlow[glowUpdate] != oldGlow )
-			{
-				SurfaceBlitColorOver( gameStartSurface,       gameStartDrawSurface,
-									  &titleRect, &titleRect,
-									   0, 0, 0, titleGlow[glowUpdate] );
-
-				drawRect[kGlow].top    = min<short>(drawRect[kGlow].top, titleRect.top);
-				drawRect[kGlow].left   = min<short>(drawRect[kGlow].left, titleRect.left);
-				drawRect[kGlow].bottom = max<short>(drawRect[kGlow].bottom, titleRect.bottom);
-				drawRect[kGlow].right  = max<short>(drawRect[kGlow].right, titleRect.right);
-			}
-		}
-
-		// Reinsert the cursor into the scene
-#if USE_CURSOR_SPRITE
-		InsertCursor( mouse, cursorBackSurface, gameStartDrawSurface );
-		drawRect[kCursor].top    = min<short>( drawRect[kCursor].top,    mouse.v );
-		drawRect[kCursor].left   = min<short>( drawRect[kCursor].left,   mouse.h );
-		drawRect[kCursor].bottom = max<short>( drawRect[kCursor].bottom, mouse.v + kCursorHeight );
-		drawRect[kCursor].right  = max<short>( drawRect[kCursor].right,  mouse.h + kCursorWidth );
-#endif
-		SDLU_SetSystemCursor( selected < 0 ? SYSTEM_CURSOR_ARROW : SYSTEM_CURSOR_HAND );
-
-		// Copy down everything		
-		if( shouldFullRepaint )
-		{
-			SDLU_BlitFrontSurface( gameStartDrawSurface, &gameStartDrawSurface->clip_rect, &gameStartDrawSurface->clip_rect );
-			shouldFullRepaint = false;
-		}
-		else
-		{
-			for( count=0; count<4; count++ )
-			{		
-				if( drawRect[count].left < drawRect[count].right )
-				{
-					SDLU_MRectToSDLRect( &drawRect[count], &destSDLRect );			
-					SDLU_BlitFrontSurface( gameStartDrawSurface, &destSDLRect, &destSDLRect );
-				}
-			}
-		}
-
-        SDLU_Present();
-
-		// Skip frames? Or delay?
-		if( startMenuTime <= MTickCount( ) )
-		{
-			startMenuTime = MTickCount( );
-			skip = 2;
-		}
-		else
-		{
-			skip = 1;
-			while( startMenuTime > MTickCount( ) ) 
-			{   
-                SDLU_Yield();
-			}
-		}
-	}
-
-	DoFullRepaint = NoPaint;
-
-	if( finished ) 
-	{
-		selected = kTitleItemQuit;
-	}
-	else
-	{
-		SDLU_SetSystemCursor( SYSTEM_CURSOR_OFF );
-	}
-	
-	switch( selected )
-	{
-		case kTitleItemTutorial:
-		case kTitleItem1PGame:
-		case kTitleItem2PGame:
-		case kTitleItemSolitaire:
-			PlayMono( kChime ); 
-			break;
-	}
-
-	SDL_FreeSurface( gameStartSurface );
-	SDL_FreeSurface( gameStartDrawSurface );
-#if USE_CURSOR_SPRITE
-	SDL_FreeSurface( cursorBackSurface );
-#endif
-	
-	QuickFadeOut( NULL );
-
-	switch( selected )
-	{
-		case kTitleItemTutorial:
-			InitGame( kAutoControl, kNobodyControl );
-			level = kTutorialLevel;
-			BeginRound( true );
-			InitTutorial( );
-			QuickFadeIn( NULL );
-			break;				
-
-		case kTitleItem1PGame:
-		case kTitleItem2PGame:
-		case kTitleItemSolitaire:
-        {
-            int player2[] = { 0, kAIControl, kPlayerControl, kNobodyControl };
-            
-            InitGame( kPlayerControl, player2[selected] );
-            BeginRound( true );
-            QuickFadeIn( NULL );
-            break;
-        }
-		
-		case kTitleItemHighScores:
-			ShowHiscore();
-			ShowBestCombo();
-			break;
-			
-		case kTitleItemControls:
-		{
-			int currentID = RandomBefore(kLevels) * 100;
-	
-			DrawPICTInSurface( boardSurface[0], picBoard + currentID );	
-			DrawPICTInSurface( g_frontSurface, picBackdrop + currentID );
-			SDLU_Present();
-	
-			QuickFadeIn( NULL );
-			HandleDialog( kControlsDialog );
-			QuickFadeOut( NULL );
-			goto redo;
-		}
-
-        case kTitleItemCredits:
-        {
-            SharewareVictory();
-            goto redo;
-        }
-		
-		case kTitleItemQuit:
-			finished = true;
-			break;
-	}
-}
-
-void ShowGameOverScreen( void )
-{
-	unsigned int  timer = MTickCount() + (60*3);
-	
-	QuickFadeOut(NULL);
-
-	DrawPICTInSurface( g_frontSurface, picGameOver );
-
-    SDL_Rect widescreenCropBackup = g_widescreenCrop;
-    g_widescreenCrop.y = 30;
-
-    SDLU_Present();
-
-	QuickFadeIn( NULL );
-	do
-	{
-		if( MTickCount() > timer ) break;
-		SDLU_Yield();
-	}
-	while( !AnyKeyIsPressed( ) && !SDLU_Button() );
-	QuickFadeOut( NULL );
-
-    g_widescreenCrop = widescreenCropBackup;
-}
-
-void InitStage( void )
-{
-	stageWindowZRect.top = stageWindowZRect.left = 0;
-	stageWindowZRect.bottom = 32; stageWindowZRect.right = 64; 
-	
-	stageWindowRect = stageWindowZRect;
-	CenterRectOnScreen( &stageWindowRect, 0.5, 0.65 );
-}
-
-void DrawStage( void )
-{
-	SDL_Surface* levelSurface;
-	SDL_Rect     sourceSDLRect, destSDLRect;
-	MRect        numberRect = { 0, kNumberHorizSize/8, kNumberVertSize, kNumberHorizSize*9/8 };
-			
-	switch( players )
-	{
-		case 0:
-		case 2:
-			break;
-			
-		case 1:
-			SDLU_MRectToSDLRect( &stageWindowZRect, &sourceSDLRect );
-			SDLU_MRectToSDLRect( &stageWindowRect,  &destSDLRect );
-			
-			levelSurface = SDLU_InitSurface( &sourceSDLRect, 32 );
-
-			SDLU_AcquireSurface( levelSurface );
-			
-			SDLU_BlitSurface( boardSurface[0], &sourceSDLRect,
-							  levelSurface,    &sourceSDLRect   );
-			
-			if( level < 10 )
-			{
-				OffsetMRect( &numberRect, kNumberHorizSize*3/8, 0 );
-			}
-			
-			DrawCharacter( kCharacterStage,   &numberRect );
-			OffsetMRect( &numberRect, kNumberHorizSize, 0 );
-			DrawCharacter( kCharacterStage+1, &numberRect );
-
-			if( level < 10 )
-			{
-				OffsetMRect( &numberRect, kNumberHorizSize, 0 );
-				DrawCharacter( level + '0', &numberRect );
-			}
-			else
-			{
-				OffsetMRect( &numberRect, kNumberHorizSize*3/4, 0 );
-				DrawCharacter( (level / 10) + '0', &numberRect );
-				OffsetMRect( &numberRect, kNumberHorizSize, 0 );
-				DrawCharacter( (level % 10) + '0', &numberRect );
-			}
-
-			SDLU_BlitFrontSurface( levelSurface, &sourceSDLRect, &destSDLRect );
-			
-			SDLU_ReleaseSurface( levelSurface );
-			SDL_FreeSurface( levelSurface );
-						
-			break;
-	}
-}
-
-void InitGame( int player1, int player2 )
-{
-	playerWindowVisible[0] = true;
-	nextWindowVisible[0] = true;
-	scoreWindowVisible[0] = true;
-	grayMonitorVisible[0] = true;
-	
-	if( player2 == kNobodyControl )
-	{
-		playerWindowVisible[1] = false;
-		nextWindowVisible[1] = false;
-		scoreWindowVisible[1] = false;
-		grayMonitorVisible[1] = false;
-
-		CenterRectOnScreen( &playerWindowRect[0], 0.5, 0.5  );
-		CenterRectOnScreen( &scoreWindowRect[0],  0.5, 0.89 );
-		CenterRectOnScreen( &grayMonitorRect[0],  0.5, 0.11 );
-		CenterRectOnScreen( &nextWindowRect[0],   0.3, 0.25 );
-		
-		CenterRectOnScreen( &stageWindowRect,	  0.3, 0.65 );		
-		CenterRectOnScreen( &opponentWindowRect,  0.3, 0.5 );		
-	}
-	else
-	{
-		playerWindowVisible[1] = true;
-		nextWindowVisible[1] = true;
-		scoreWindowVisible[1] = true;
-		grayMonitorVisible[1] = true;
-
-		CenterRectOnScreen( &playerWindowRect[0], kLeftPlayerWindowCenter, 0.5  );
-		CenterRectOnScreen( &scoreWindowRect[0],  kLeftPlayerWindowCenter, 0.89 );
-		CenterRectOnScreen( &grayMonitorRect[0],  kLeftPlayerWindowCenter, 0.11 );
-		CenterRectOnScreen( &nextWindowRect[0],   0.46, 0.25 );
-
-		CenterRectOnScreen( &playerWindowRect[1], kRightPlayerWindowCenter, 0.5  );
-		CenterRectOnScreen( &scoreWindowRect[1],  kRightPlayerWindowCenter, 0.89 );
-		CenterRectOnScreen( &grayMonitorRect[1],  kRightPlayerWindowCenter, 0.11 );
-		CenterRectOnScreen( &nextWindowRect[1],   0.54, 0.25 );
-
-		CenterRectOnScreen( &stageWindowRect,    0.5, 0.65 );		
-		CenterRectOnScreen( &opponentWindowRect, 0.5, 0.5 );		
-	}
-	
-	// In widescreen mode, move score/gray windows closer to the playfield
-	// so they fit in the cropped screen.
-	ResetWidescreenLayout();
-	
-	nextWindowVisible[0] = ( player1 == kAutoControl )? false: true;
-	
-	players = (player1 == kPlayerControl) + (player2 == kPlayerControl);
-	
-	if( players < 2 )
-	{
-		difficulty[0] = difficulty[1] = kHardLevel;
-	}
-	
-	control[0] = player1;
-	control[1] = player2;
-	
-	score[0] = score[1] = displayedScore[0] = displayedScore[1] = 0;	
-	roundStartScore[0] = roundStartScore[1] = 0;
-	
-	level = 1;
-	credits = (player2 == kNobodyControl)? 1: 5;
-}
-
-void ResetWidescreenLayout()
-{
-	int miniWindowOffset = widescreen ? 4 : 16;
-
-	for (int i = 0; i < 2; i++)
-	{
-		grayMonitorRect[i].top = playerWindowRect[i].top - 32 - miniWindowOffset;
-		grayMonitorRect[i].bottom = playerWindowRect[i].top - miniWindowOffset;
-		scoreWindowRect[i].top = playerWindowRect[i].bottom + miniWindowOffset;
-		scoreWindowRect[i].bottom = playerWindowRect[i].bottom + 16 + miniWindowOffset;
-	}
-}
-
-MBoolean InitCharacter( int player, int level )
-{
-	const Character characterList[] = {
-		{ -1 }, // no zero'th character
-		{ 0, 3, 1, { 8, 8, 8, 8, 8, 8 }, 13, 9, 0, 25,          { 0, _15TO8_8_8(0),     0, 0, _15TO8_8_8(0), 0 }, true },
-		{ 1, 6, 2, { 10, 9, 8, 8, 9, 10 }, 12, 7, 1, 20,        { 0, _15TO8_8_8(223),   7, 0, _15TO8_8_8(0), 0 }, true },
-		{ 2, 9, 3, { 7, 7, 7, 11, 7, 7 }, 10, 6, 2, 17,         { 0, _15TO8_8_8(0),     0, 0, _15TO8_8_8(0), 0 }, false },
-		{ 3, 12, 4, { 11, 10, 9, 8, 7, 6 }, 8, 5, 3, 13,        { 0, _15TO8_8_8(32767), 4, 0, _15TO8_8_8(16912), 4 }, false },
-		{ 4, 15, 0, { 5, 9, 10, 10, 9, 5 }, 7, 4, 4, 10,        { 0, _15TO8_8_8(32767), 1, 0, _15TO8_8_8(0), 0 }, false },
-		{ 5, 17, 1, { 4, 7, 11, 11, 6, 3 }, 7, 2, 5, 8,         { 0, _15TO8_8_8(14835), 8, 0, _15TO8_8_8(0), 0 }, false },
-		{ 6, 18, 2, { 7, 9, 10, 10, 9, 7 }, 6, 4, 6, 7,         { 0, _15TO8_8_8(0),     0, 0, _15TO8_8_8(0), 0 }, false },
-		{ 7, 20, 3, { 5, 10, 10, 10, 10, 5 }, 5, 3, 7, 5,       { 0, _15TO8_8_8(9696),  2, 0, _15TO8_8_8(21151), 3 }, false },
-		{ 8, 21, 4, { 11, 11, 10, 10, 9, 9 }, 4, 3, 8, 5,       { 0, _15TO8_8_8(32738), 5, 0, _15TO8_8_8(0), 0 }, false },
-		{ 9, 22, 0, { 11, 7, 11, 7, 11, 7 }, 3, 1, 9, 4,        { 0, _15TO8_8_8(32356), 5, 0, _15TO8_8_8(17392), 3 }, false },
-		{ 10, 23, 1, { 11, 11, 11, 11, 11, 11 }, 2, 1, 10, 2,   { 0, _15TO8_8_8(6337),  1, 0, _15TO8_8_8(0), 0 }, false },
-		{ 11, 24, 2, { 11, 11, 11, 11, 11, 11 }, 2, 1, 11, 2,   { 1, _15TO8_8_8(32767), 7, 0, _15TO8_8_8(0), 0 }, false },
-		{ -1 }, // skip
-		{ 13, 24, 1, { 11, 11, 11, 11, 11, 11 }, 10, 5, 0, 30,  { 0, _15TO8_8_8(0),     0, 0, _15TO8_8_8(0), 0 }, true }
-	};
-		
-	character[player] = characterList[level];
-	return (character[player].picture != -1);
-}
-
-void PrepareStageGraphics( int type )
-{
-	int player;
-	                            
-	MRect blobBoard = { 0, 0, kGridDown * kBlobVertSize, kGridAcross * kBlobHorizSize };
-	
-	backgroundID = type * 100;
-	
-	DrawPICTInSurface( boardSurface[0], picBoard + backgroundID );	
-	
-	// NOTE: Many levels have no right-side board, so we copy the left
-	// side over to the right side. This way, if DrawPICTInSurface flunks,
-	// we still have a valid picture.
-	
-	SDLU_BlitSurface( boardSurface[0], &boardSurface[0]->clip_rect,
-	                  boardSurface[1], &boardSurface[1]->clip_rect  );
-	
-	DrawPICTInSurface( boardSurface[1], picBoardRight + backgroundID );	
-
-	DrawPICTInSurface( backdropSurface, picBackdrop + backgroundID );
-
-	DrawPICTInSurface( nextSurface, picNext + backgroundID );
-	
-	for( player=0; player<=1; player++ )
-	{
-		SDLU_AcquireSurface( playerSurface[player] );
-		SurfaceDrawBoard( player, &blobBoard );
-		SDLU_ReleaseSurface( playerSurface[player] );
-		
-		CleanSpriteArea( player, &blobBoard );
-	}
-	
-	BeginOpponent( type );
-
-	RedrawBoardContents( 0 );
-	RedrawBoardContents( 1 );
-	
-	RefreshAll( );
-
-	backdropTicks = MTickCount( );
-	backdropFrame = 0;
-}
-
-void BeginRound( MBoolean changeMusic )
-{
-	int player, count, count2;
-	
-	InitGrays( );
-	InitPotentialCombos( );
-	
-	switch( players )
-	{
-		case 0:
-		case 1:
-			if( InitCharacter( 1, level ) )
-			{
-				score[1] = roundStartScore[1] = displayedScore[1] = 0;
-				character[0] = character[1];
-				character[0].zapStyle = RandomBefore(5);
-			}
-			else
-			{
-				TotalVictory( );
-				return;
-			}
-			
-			if( control[1] == kNobodyControl )
-			{
-				InitRandom( 3 );
-			}
-			else
-			{
-				InitRandom( 5 );
-			}
-			break;
-			
-		case 2:
-			score[0] = score[1] = roundStartScore[0] = roundStartScore[1] = displayedScore[0] = displayedScore[1] = 0;	
-
-			InitRandom( 5 );
-
-			SelectRandomLevel( );
-			InitCharacter( 0, level );
-			
-			SelectRandomLevel( );
-			InitCharacter( 1, level );
-			
-			character[0].hints = (difficulty[0] == kEasyLevel) || (difficulty[0] == kMediumLevel);
-			character[1].hints = (difficulty[1] == kEasyLevel) || (difficulty[1] == kMediumLevel);
-			break;
-	}
-	
-	for( player=0; player<=1; player++ )
-	{
-		for( count=0; count<kGridAcross; count++ )
-		{
-			grays[player][count] = 0;
-			
-			for( count2=0; count2<kGridDown; count2++ )
-			{
-				grid[player][count][count2] = kEmpty;
-				suction[player][count][count2] = kNoSuction;
-				charred[player][count][count2] = kNoCharring;
-				glow[player][count][count2] = false;
-			}
-		}
-		
-		nextA[player] = GetPiece( player );
-		nextB[player] = GetPiece( player );
-		nextM[player] = false;
-		nextG[player] = false;
-		
-		halfway[player] = false;
-		
-		unallocatedGrays[player] = 0;
-		anim[player] = 0;
-		lockGrays[player] = 0;
-		roundStartScore[player] = score[player];
-		
-		RedrawBoardContents(player);
-		
-		if( control[player] != kNobodyControl )
-		{
-			role[player] = kWaitForRetrieval;
-		}
-		else
-		{
-			role[player] = kIdlePlayer;
-		}
-	}
-	
-	PrepareStageGraphics( character[1].picture );
-	if( changeMusic ) ChooseMusic( character[1].music );
-	
-	blobTime[0]     = blobTime[1]     = 
-	boredTime[0]    = boredTime[1]    = 
-	hintTime[0]     = hintTime[1]     =
-	timeAI[0]       = timeAI[1]       =
-	fadeCharTime[0] = fadeCharTime[1] = 
-	messageTime     = startTime       =
-	blinkTime[0]    = blinkTime[1]    = GameTickCount( );
-	
-	blinkTime[1] += 60;
-	
-	if( players == 2 )
-		InitDifficulty( );
-    
-    SDLU_Present();
-}
-
-void IncrementLevel( void )
-{
-	level++;
-}
-
-void SelectRandomLevel( void )
-{
-	level = RandomBefore(kLevels) + 1;
-}
-
-void InitDifficulty( )
-{
-	MRect blobBoard = { 0, 0, kGridDown * kBlobVertSize, kGridAcross * kBlobHorizSize };
-	int player;
-	const int selectionRow = 5;
-	int count;
-	MRect blobRect;
-	
-	for( player=0; player<=1; player++ )
-	{
-		// Set up variables
-		role[player] = kChooseDifficulty;
-		colorA[player] = RandomBefore(kBlobTypes)+1;
-		colorB[player] = kEmpty;
-		switch( difficulty[player] )
-		{
-			case kEasyLevel:      blobX[player] = 1; break;
-			case kMediumLevel:    blobX[player] = 2; break;
-			case kHardLevel:      blobX[player] = 3; break;
-			case kUltraLevel:     blobX[player] = 4; break;
-		}
-		
-		blobY[player] = selectionRow;
-		blobR[player] = upRotate;
-		blobTime[player] = GameTickCount( ) + (60*8);
-		animTime[player] = GameTickCount( );
-		shadowDepth[player] = kBlobShadowDepth;
-		magic[player] = false;
-		grenade[player] = false;
-		
-		DrawPICTInSurface( boardSurface[player], picSelectDifficulty + backgroundID );
-		
-		SDLU_AcquireSurface( playerSurface[player] );
-
-		SurfaceDrawBoard( player, &blobBoard );
-		
-		grid[player][0][selectionRow] = kGray;
-		suction[player][0][selectionRow] = kEasyGray;
-		charred[player][0][selectionRow] = kNoCharring;
-		CalcBlobRect( 0, selectionRow, &blobRect );
-		SurfaceDrawBlob( player, &blobRect, kGray, kEasyGray, kNoCharring );
-		
-		grid[player][kGridAcross-1][selectionRow] = kGray;	
-		suction[player][kGridAcross-1][selectionRow] = kHardGray;
-		charred[player][kGridAcross-1][selectionRow] = kNoCharring;
-		CalcBlobRect( kGridAcross-1, selectionRow, &blobRect );
-		SurfaceDrawBlob( player, &blobRect, kGray, kHardGray, kNoCharring );
-
-		CalcBlobRect( 1, selectionRow, &blobRect );
-		blobRect.top -= 4; blobRect.bottom += 4;
-		blobRect.left += 4; blobRect.right -= 4;
-		for( count=1; count<=4; count++ )
-		{
-			DrawCharacter( count + '0', &blobRect );
-			OffsetMRect( &blobRect, kBlobHorizSize, 0 );
-		}
-		
-		SDLU_ReleaseSurface( playerSurface[player] );
-		
-		DrawSpriteBlobs( player, kNoSuction );
-		CleanSpriteArea( player, &blobBoard );
-	}
-}
-
-void ChooseDifficulty( int player )
-{
-	MRect blobBoard = { 0, 0, kGridDown * kBlobVertSize, kGridAcross * kBlobHorizSize };
-	const int selectionRow = 5;
-	const int difficultyMap[kGridAcross] = {kEasyLevel, kEasyLevel, kMediumLevel, kHardLevel, kUltraLevel, kUltraLevel};
-	const int fallingSpeed[kGridAcross] = {0, 15, 9, 7, 4, 0};
-	const int startGrays[kGridAcross] = {0, 0,  0, 10, 20, 0};
-	const int difficultyFrame[] = { kNoSuction, blobBlinkAnimation, blobBlinkAnimation,
-									  blobJiggleAnimation, blobCryAnimation, kNoSuction };
-	int oldX = blobX[player];
-	
-	PlayerControl( player );
-	if( blobX[player] != oldX ) anim[player] = 0;
-	
-	UpdateTweak( player, difficultyFrame[blobX[player]] );
-
-	if( GameTickCount( ) >= blobTime[player] )
-	{
-		if( player == 1 && PICTExists( picBoardRight + backgroundID ) )
-		{
-			DrawPICTInSurface( boardSurface[player], picBoardRight + backgroundID );
-		}
-		else
-		{
-			DrawPICTInSurface( boardSurface[player], picBoard + backgroundID );
-		}
-		
-		SDLU_AcquireSurface( playerSurface[player] );
-		SurfaceDrawBoard( player, &blobBoard );
-		SDLU_ReleaseSurface( playerSurface[player] );
-		
-		CleanSpriteArea( player, &blobBoard );
-	
-		grid[player][0][selectionRow] = kEmpty;
-		grid[player][5][selectionRow] = kEmpty;
-												
-		suction[player][0][selectionRow] = kNoSuction;
-		suction[player][5][selectionRow] = kNoSuction;
-		
-		difficulty[player]          = difficultyMap[ blobX[player] ];
-		character[player].dropSpeed = fallingSpeed[ blobX[player] ];
-		unallocatedGrays[player] = lockGrays[player] = startGrays[blobX[player]];
-		character[player].hints     = (startGrays[blobX[player]] == 0);
-		role[player] = kWaitingToStart;
-		
-		PlayStereoFrequency( player, kPause, player );
-	}
-}
-
-const char *gameCredits[][6] = 
-{
-	{ "Programming", "John Stiles", "", "", "", "" },
-	{ "Artwork", "Kate Davis", "Leanne Stiles", "Arnauld de la Grandiere", "Bob Frasure", "Ryan Bliss" },
-	{ "Music", "Leanne Stiles", "fmod", "Lizardking", "Armadon, Explizit", "Leviathan, Nemesis" },
-	{ "Music", "Jester, Pygmy", "Siren", "Sirrus", "Scaven, FC", "Spring" }, 		  
-	{ "Music", "Timewalker", "Jason, Silents", "Chromatic Dragon", "Ng Pei Sin", "" },
-	{ "Source Port", "Iliyas Jorio", "github.com/jorio/candycrisis", "", "", "" },
-	{ "Special Thanks", "Sam Lantinga", "Carey Lening", "modarchive.com", "digitalblasphemy.com", "" },	  
-	{ "", "", "", "", "", "" }
-};
-
-void SharewareVictory( void )
-{
-	SkittlesFontPtr textFont, titleFont;
-	SDL_Surface*    backBuffer;
-	SDL_Surface*    frontBuffer;
-	SDL_Rect        creditSrcSDLRect, bufferSrcSDLRect, bufferDstSDLRect;
-	MRect           creditSrcRect = { 0, 0, 369, 150 };
-	MRect           bufferSrcRect = { 0, 50, 480, 350 };
-	MRect           bufferDstRect = { 0, 0, 480, 300 };
-	MPoint          dPoint = { 450, 50 }, lPoint, cPoint;
-	int             scroll, x, y;
-	MTicks          ticks;
-	const char*     text;
-	int             thisFade;
-	const char fade[120] =   { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //0
-				               1, 2, 3, 4, 5, 6, 7, 8, 9,10, //1
-				              11,12,13,14,15,16,17,18,19,20, //2
-				              20,20,20,20,20,20,20,20,20,20, //3
-				              20,20,20,20,20,20,20,20,20,20, //4
-				              20,20,20,20,20,20,20,20,20,20, //5
-				              20,20,20,20,20,20,20,20,20,20, //6
-				              20,20,20,20,20,20,20,20,20,20, //7
-				              20,20,20,20,20,20,20,20,20,20, //8
-							  20,19,18,17,16,15,14,13,12,11, //9
-							  10, 9, 8, 7, 6, 5, 4, 3, 2, 1, //10
-							   0, 0, 0, 0, 0, 0, 0, 0, 0, 0  //11
-						     };
-	              
-	titleFont = GetFont( picFont );
-	textFont = GetFont( picTinyFont );
-	
-	SDLU_MRectToSDLRect( &creditSrcRect, &creditSrcSDLRect );
-	SDLU_MRectToSDLRect( &bufferSrcRect, &bufferSrcSDLRect );
-	SDLU_MRectToSDLRect( &bufferDstRect, &bufferDstSDLRect );
-	
-	DrawPICTInSurface( g_frontSurface, picSharewareVictory );
-    SDLU_Present();
-	
-	backBuffer = SDLU_InitSurface( &bufferDstSDLRect, 32 );
-	
-	SDLU_BlitSurface( g_frontSurface, &bufferSrcSDLRect,
-	                  backBuffer,   &bufferDstSDLRect   );
-
-	frontBuffer = SDLU_InitSurface( &bufferDstSDLRect, 32 );
-	
-	QuickFadeIn( NULL );	
-
-	ChooseMusic( 12 ); 
-	
-	ticks = MTickCount();
-	for( scroll=0; scroll<1500; scroll++ )
-	{
-		SDLU_AcquireSurface( frontBuffer );	
-		
-		SDLU_BlitSurface( backBuffer,   &bufferDstSDLRect,
-		                  frontBuffer,  &bufferDstSDLRect  );
-		
-		ticks += 3;
-		lPoint = dPoint;
-		for( y=0; y<8; y++ )
-		{
-			if( y != 3 && y != 4 )
-			{
-				cPoint.v = lPoint.v + 25;
-				cPoint.h = lPoint.h;
-				if( cPoint.v > 480 ) break;
-				
-				if( cPoint.v > 0 )
-				{
-					text = gameCredits[y][0];
-					thisFade = fade[cPoint.v >> 2];
-
-					while( *text )
-					{
-						SurfaceBlitWeightedCharacter( titleFont, *text++, &cPoint, 255, 255, 0, _5TO8(thisFade) );
-					}
-				}
-
-				lPoint.v += 50;
-			}
-			
-			for( x=1; x<6; x++ )
-			{
-				if( gameCredits[y][x][0] )
-				{
-					cPoint.v = lPoint.v;
-					cPoint.h = lPoint.h + 20;
-					if( cPoint.v > 480 ) break;
-
-					if( cPoint.v > 0 )
-					{
-						text = gameCredits[y][x];
-						thisFade = fade[cPoint.v >> 2];
-
-						while( *text )
-						{
-							SurfaceBlitWeightedCharacter( textFont, *text++, &cPoint, 255, 255, 0, _5TO8(thisFade) );
-						}
-					}
-
-					lPoint.v += 20;
-				}
-			}
-		}
-		
-		SDLU_ReleaseSurface( frontBuffer );
-
-		dPoint.v--;
-
-		SDLU_BlitFrontSurface( frontBuffer, &bufferDstSDLRect, &bufferSrcSDLRect );
-        SDLU_Present();
-
-		do
-		{
-			if( SDLU_Button() ) goto out;	
-            SDLU_Yield();	
-		}
-		while( ticks >= MTickCount() );
-	}
-	
-	do
-	{
-        SDLU_Yield();
-	}
-	while( !AnyKeyIsPressed( ) && !SDLU_Button() );
-
-out:
-	QuickFadeOut( NULL );	
-	
-	SDL_FreeSurface( backBuffer );
-	SDL_FreeSurface( frontBuffer );
-}
-
-void RegisteredVictory( void )
-{
-	SkittlesFontPtr textFont, titleFont, bubbleFont;
-	SDL_Surface*    backBuffer;
-	SDL_Surface*    frontBuffer;
-	MPoint          dPoint[] = { { 230, 340 }, { 230, 30 }, { 230, 30 }, { 30, 30 }, { 30, 340 }, { 230, 340 }, { 230, 30 } }; 
-	MPoint          bubblePoint, textPoint, shadowPoint;
-	MPoint          setPoint[7][6];
-	MPoint          msgSetPoint[7][2];
-	MTicks          ticks;
-	int             vertScroll, picture, weight, line, minimum;
-	int             scrollDir[] = {1, -1, 1, -1, 1, -1, -1};
-	int             spacing[] = {40, 19, 19, 19, 23, 19, 23 };
-	const char*     text;
-	SDL_Rect        fullSDLRect = { 0, 0, 640, 480 };
-	SDL_Rect        highSDLRect = { 0, 0, 640, 480 };
-	SDL_Rect        lowSDLRect = { 0, 250, 640, 480 };
-	SDL_Rect        backBufferSDLRect = { 0, 0, 640, 730 };
-	SDL_Rect        scrollSDLRect;
-
-	const char *messages[7][2] =
-	{
-		{ "Congratulations!", "" },
-		{ "You've managed to vaporize all", "of the rampaging candies!" },
-		{ "Your quick thinking and sharp", "reflexes have saved the day." },
-		{ "", "" },
-		{ "", "" },
-		{ "", "" },
-		{ "Thanks for playing Candy Crisis!", "" },
-	};
-
-	// In widescreen mode, move vertical text positions closer to the center
-	if (widescreen) {
-		for (auto& dp : dPoint) {
-			if (dp.v >= 130) {
-				dp.v -= 20;
-			} else {
-				dp.v += 20;
-			}
-		}
-	}
-	
-	textFont = GetFont( picTinyFont );
-	titleFont = GetFont( picHiScoreFont );
-	bubbleFont = GetFont( picBubbleFont );
-	
-	ChooseMusic( 14 ); 
-
-	for( picture=0; picture<7; picture++ )
-	{
-		for( line=0; line<2; line++ )
-		{
-			if (dPoint[picture].v >= 130) {
-				msgSetPoint[picture][line].v = (widescreen? 120: 100) + line * 30;
-			} else {
-				msgSetPoint[picture][line].v = (widescreen? 380: 300) + line * 30;
-			}
-			msgSetPoint[picture][line].h = 320 - (GetTextWidth( titleFont, messages[picture][line] ) / 2);
-		}
-		
-		for( line=0; line<6; line++ )
-		{
-			SkittlesFontPtr font;
-
-			if( line == 0 )
-			{
-				font = titleFont;
-				textPoint.v = 45;				
-			}
-			else
-			{
-				font = textFont;
-				textPoint.v = 65 + (spacing[picture] * line);				
-			}
-
-			textPoint.h = (bubbleFont->width['*'] - GetTextWidth( font, gameCredits[picture][line] )) / 2;
-
-			setPoint[picture][line].v = dPoint[picture].v + textPoint.v;
-			setPoint[picture][line].h = dPoint[picture].h + textPoint.h;
-		}
-		
-		minimum = 640;
-		for( line=1; line<6; line++ )
-		{
-			if( setPoint[picture][line].h < minimum ) minimum = setPoint[picture][line].h;
-		}
-		
-		for( line=1; line<6; line++ )
-		{
-			setPoint[picture][line].h = minimum;
-		}		
-	}
-	
-	backBuffer  = SDLU_InitSurface( &backBufferSDLRect, 32 );
-	frontBuffer = SDLU_InitSurface( &fullSDLRect, 32 );
-	
-	for( picture = 0; picture<7; picture++ )
-	{
-		scrollSDLRect = ( scrollDir[picture] > 0 )? highSDLRect: lowSDLRect;
-		
-		DrawPICTInSurface( backBuffer, picture + picVictory1 );
-
-		SDLU_BlitFrontSurface( backBuffer, &scrollSDLRect, &fullSDLRect );
-        SDLU_Present();
-
-		QuickFadeIn( NULL );
-		
-		ticks = MTickCount();
-		for( vertScroll = 0; vertScroll < 250; vertScroll++ )
-		{
-			SDLU_AcquireSurface( frontBuffer );
-					
-			SDLU_BlitSurface( backBuffer,  &scrollSDLRect,
-			                  frontBuffer, &fullSDLRect );
-
-			weight = vertScroll - 20;
-			for( line=0; line<2; line++ )
-			{
-				textPoint = msgSetPoint[picture][line];
-				shadowPoint.v = textPoint.v + 1;
-				shadowPoint.h = textPoint.h + 1;
-				
-				text = messages[picture][line];
-				
-				while( *text && weight > 0 )
-				{
-					int fixedWeight = (weight > 31)? 31: weight;
-					
-					SurfaceBlitWeightedCharacter( titleFont, *text, &shadowPoint, 0,   0,   0,   _5TO8(fixedWeight) );
-					SurfaceBlitWeightedCharacter( titleFont, *text, &textPoint,   255, 255, 255, _5TO8(fixedWeight) );
-					weight--;
-					text++;
-				}
-			}
-			
-			bubblePoint = dPoint[picture];
-			weight = ( vertScroll <= 210 )? vertScroll - 10: 241 - vertScroll;
-			if( weight < 0  ) weight = 0;
-			if( weight > 31 ) weight = 31;
-			
-			if( weight > 0 )
-			{
-                int bubbleWeight = (weight+1)>>1;
-                
-				SurfaceBlitWeightedCharacter( bubbleFont, '*', &bubblePoint, 255, 255, 255, _5TO8(bubbleWeight) );
-				
-				for( line=0; line<6; line++ )
-				{
-					SkittlesFontPtr font = (line == 0)? titleFont: textFont;
-					
-					textPoint = setPoint[picture][line];
-					text = gameCredits[picture][line];
-					
-					while( *text )
-					{
-						SurfaceBlitWeightedCharacter( font, *text++, &textPoint, 0, 0, 0, _5TO8(weight) );
-					}
-				}
-			}
-			
-			SDLU_ReleaseSurface( frontBuffer );
-			
-			SDLU_BlitFrontSurface( frontBuffer, &fullSDLRect, &fullSDLRect );
-            SDLU_Present();
-            
-			scrollSDLRect.y += scrollDir[picture];
-			
-			ticks += 4;
-			do
-			{ 
-				if( SDLU_Button() ) vertScroll = 250;
-			    SDLU_Yield();
-			}			
-			while( ticks >= MTickCount() );
-		}
-
-		QuickFadeOut( NULL );
-	}
-	
-	SDL_FreeSurface( backBuffer  );
-	SDL_FreeSurface( frontBuffer );
-}
-
-void TotalVictory( void )
-{	
-	AddHiscore( score[0] );
-	QuickFadeOut( NULL );	
-
-	DoFullRepaint = NoPaint;
-
-    RegisteredVictory( );
-	
-	showStartMenu = true;
-}
binary files /dev/null b/src/main.c differ
binary files a/src/main.cpp /dev/null differ
--- a/src/main.h
+++ b/src/main.h
@@ -34,10 +34,10 @@
 void WaitForRegainFocus();
 
 
-struct KeyList
+typedef struct KeyList
 {
 	short left, right, drop, rotate;
-};
+} KeyList;
 
 
 #define kGridAcross 6
@@ -238,9 +238,9 @@
 #define kLastBlob kBlob7
 #define kBlobTypes (kLastBlob - kFirstBlob + 1)
 
-constexpr double pi = 3.14159265358979323846264338327950288;
+#define kPi 3.14159265358979323846264338327950288
 
-#define arrsize(x) int(sizeof((x)) / sizeof((x)[0]))
+#define arrsize(x) ( (int)(sizeof((x)) / sizeof((x)[0])) )
 
 extern SDL_Renderer* g_renderer;
 extern SDL_Window*   g_window;
--- /dev/null
+++ b/src/moving.c
@@ -1,0 +1,208 @@
+// moving.c
+
+#include "main.h"
+#include "moving.h"
+#include "players.h"
+#include "graphics.h"
+#include "soundfx.h"
+#include "tweak.h"
+#include "gameticks.h"
+#include "level.h"
+
+void CalcSecondBlobOffset( int player, int *x, int *y )
+{
+	*x = *y = 0;
+	
+	switch( blobR[player] )
+	{
+		case rightRotate:
+			*x = 1;
+			break;
+		
+		case downRotate:
+			*y = 1;
+			break;
+			
+		case leftRotate:
+			*x = -1;
+			break;
+		
+		case upRotate:
+			*y = -1;
+			break;
+	}
+}
+
+MBoolean CanGoLeft( int player )
+{
+	return CanMoveDirection( player, -1, halfway[player]? 1: 0 );
+}
+
+void GoLeft( int player )
+{
+	EraseSpriteBlobs( player );
+	blobX[player]--;
+	StartTweak( player, -1, 0, 0 );
+	DrawSpriteBlobs( player, kNoSuction );
+	
+	PlayStereo( player, kShift );
+}
+
+MBoolean CanGoRight( int player )
+{
+	return CanMoveDirection( player, 1, halfway[player]? 1: 0 );
+}
+
+void GoRight( int player )
+{
+	EraseSpriteBlobs( player );
+	blobX[player]++;
+	StartTweak( player, 1, 0, 0 );
+	DrawSpriteBlobs( player, kNoSuction );
+	
+	PlayStereo( player, kShift );
+}
+
+MBoolean CanFall( int player )
+{
+	return CanMoveDirection( player, 0, 1 );
+}
+
+MBoolean CanMoveDirection( int player, int dirX, int dirY )
+{
+	int currentX = blobX[player], currentY = blobY[player], x, y;
+	
+	currentX += dirX;
+	currentY += dirY;
+	
+	if( currentX < 0 || currentX >= kGridAcross || currentY >= kGridDown )
+		return false;
+	
+	if( currentY >= 0 )
+		if( grid[player][currentX][currentY] != kEmpty )
+			return false;
+
+	CalcSecondBlobOffset( player, &x, &y );
+	
+	currentX += x;
+	currentY += y;
+	
+	if( currentX < 0 || currentX >= kGridAcross || currentY >= kGridDown )
+		return false;
+	
+	if( currentY >= 0 )
+		if( grid[player][currentX][currentY] != kEmpty )
+			return false;
+
+	return true;
+}
+
+void DoFall( int player )
+{
+	EraseSpriteBlobs( player );
+	
+	if( halfway[player] )
+		blobY[player]++;
+	halfway[player] = !halfway[player];
+	
+	StartTweak( player, 0, 0, 1 );
+
+	DrawSpriteBlobs( player, kNoSuction );
+}
+
+MBoolean CanRotate( int player )
+{
+	if( role[player] == kChooseDifficulty ) return false;
+	
+	if( grenade[player] ) return false;
+	
+	return true;
+	
+}
+
+void DoRotate( int player )
+{
+	MBoolean possible;
+	
+	EraseSpriteBlobs( player );
+	
+	blobR[player] = ( blobR[player] + 1 ) % 4;
+	possible = CanMoveDirection( player, 0, halfway[player]? 1: 0 );
+	StartTweak( player, 0, 1, 0 ); // only rotates clockwise
+	
+	if( !possible )
+	{
+		if( blobR[player] == downRotate )
+		{
+			if( halfway[player] )
+				halfway[player] = false;
+			else
+				blobY[player]--;
+				
+			if( ++blobSpin[player] >= 4 )
+			{
+				blobTime[player] = animTime[player] = GameTickCount( );
+				role[player] = kLockdownBlobs;
+				anim[player] = 0;
+				PlayStereoFrequency( player, kPlace, player );
+			}
+		}
+		
+		if( blobR[player] == leftRotate )
+		{
+			if( CanGoRight(player) )
+				GoRight( player );
+			else
+			{
+				blobR[player]++;
+				StartTweak( player, 0, 2, 0 );
+			}
+		}
+		
+		if( blobR[player] == rightRotate  )
+		{
+			if( CanGoLeft(player) )
+				GoLeft( player );
+			else
+			{
+				blobR[player]++;
+				StartTweak( player, 0, 2, 0 );
+				
+				if( !CanMoveDirection( player, 0, halfway[player]? 1: 0 ) )
+				{
+					if( halfway[player] )
+						halfway[player] = false;
+					else
+						blobY[player]--;
+
+					if( ++blobSpin[player] >= 4 )
+					{
+						blobTime[player] = animTime[player] = GameTickCount( );
+						role[player] = kLockdownBlobs;
+						anim[player] = 0;
+						PlayStereoFrequency( player, kPlace, player );
+					}
+				}
+			}
+		}
+	}
+	
+	DrawSpriteBlobs( player, kNoSuction );
+	
+	PlayStereo( player, kRotate );
+}
+
+void DoDrop( int player )
+{
+	dropping[player] = true;
+	
+	if( role[player] != kJiggleBlobs &&
+		role[player] != kFastJiggleBlobs &&
+		role[player] != kLockdownBlobs      )
+		blobTime[player] = GameTickCount( );
+}
+
+void StopDrop( int player )
+{
+	dropping[player] = false;
+}
--- a/src/moving.cpp
+++ /dev/null
@@ -1,208 +1,0 @@
-// moving.c
-
-#include "main.h"
-#include "moving.h"
-#include "players.h"
-#include "graphics.h"
-#include "soundfx.h"
-#include "tweak.h"
-#include "gameticks.h"
-#include "level.h"
-
-void CalcSecondBlobOffset( int player, int *x, int *y )
-{
-	*x = *y = 0;
-	
-	switch( blobR[player] )
-	{
-		case rightRotate:
-			*x = 1;
-			break;
-		
-		case downRotate:
-			*y = 1;
-			break;
-			
-		case leftRotate:
-			*x = -1;
-			break;
-		
-		case upRotate:
-			*y = -1;
-			break;
-	}
-}
-
-MBoolean CanGoLeft( int player )
-{
-	return CanMoveDirection( player, -1, halfway[player]? 1: 0 );
-}
-
-void GoLeft( int player )
-{
-	EraseSpriteBlobs( player );
-	blobX[player]--;
-	StartTweak( player, -1, 0, 0 );
-	DrawSpriteBlobs( player, kNoSuction );
-	
-	PlayStereo( player, kShift );
-}
-
-MBoolean CanGoRight( int player )
-{
-	return CanMoveDirection( player, 1, halfway[player]? 1: 0 );
-}
-
-void GoRight( int player )
-{
-	EraseSpriteBlobs( player );
-	blobX[player]++;
-	StartTweak( player, 1, 0, 0 );
-	DrawSpriteBlobs( player, kNoSuction );
-	
-	PlayStereo( player, kShift );
-}
-
-MBoolean CanFall( int player )
-{
-	return CanMoveDirection( player, 0, 1 );
-}
-
-MBoolean CanMoveDirection( int player, int dirX, int dirY )
-{
-	int currentX = blobX[player], currentY = blobY[player], x, y;
-	
-	currentX += dirX;
-	currentY += dirY;
-	
-	if( currentX < 0 || currentX >= kGridAcross || currentY >= kGridDown )
-		return false;
-	
-	if( currentY >= 0 )
-		if( grid[player][currentX][currentY] != kEmpty )
-			return false;
-
-	CalcSecondBlobOffset( player, &x, &y );
-	
-	currentX += x;
-	currentY += y;
-	
-	if( currentX < 0 || currentX >= kGridAcross || currentY >= kGridDown )
-		return false;
-	
-	if( currentY >= 0 )
-		if( grid[player][currentX][currentY] != kEmpty )
-			return false;
-
-	return true;
-}
-
-void DoFall( int player )
-{
-	EraseSpriteBlobs( player );
-	
-	if( halfway[player] )
-		blobY[player]++;
-	halfway[player] = !halfway[player];
-	
-	StartTweak( player, 0, 0, 1 );
-
-	DrawSpriteBlobs( player, kNoSuction );
-}
-
-MBoolean CanRotate( int player )
-{
-	if( role[player] == kChooseDifficulty ) return false;
-	
-	if( grenade[player] ) return false;
-	
-	return true;
-	
-}
-
-void DoRotate( int player )
-{
-	MBoolean possible;
-	
-	EraseSpriteBlobs( player );
-	
-	blobR[player] = ( blobR[player] + 1 ) % 4;
-	possible = CanMoveDirection( player, 0, halfway[player]? 1: 0 );
-	StartTweak( player, 0, 1, 0 ); // only rotates clockwise
-	
-	if( !possible )
-	{
-		if( blobR[player] == downRotate )
-		{
-			if( halfway[player] )
-				halfway[player] = false;
-			else
-				blobY[player]--;
-				
-			if( ++blobSpin[player] >= 4 )
-			{
-				blobTime[player] = animTime[player] = GameTickCount( );
-				role[player] = kLockdownBlobs;
-				anim[player] = 0;
-				PlayStereoFrequency( player, kPlace, player );
-			}
-		}
-		
-		if( blobR[player] == leftRotate )
-		{
-			if( CanGoRight(player) )
-				GoRight( player );
-			else
-			{
-				blobR[player]++;
-				StartTweak( player, 0, 2, 0 );
-			}
-		}
-		
-		if( blobR[player] == rightRotate  )
-		{
-			if( CanGoLeft(player) )
-				GoLeft( player );
-			else
-			{
-				blobR[player]++;
-				StartTweak( player, 0, 2, 0 );
-				
-				if( !CanMoveDirection( player, 0, halfway[player]? 1: 0 ) )
-				{
-					if( halfway[player] )
-						halfway[player] = false;
-					else
-						blobY[player]--;
-
-					if( ++blobSpin[player] >= 4 )
-					{
-						blobTime[player] = animTime[player] = GameTickCount( );
-						role[player] = kLockdownBlobs;
-						anim[player] = 0;
-						PlayStereoFrequency( player, kPlace, player );
-					}
-				}
-			}
-		}
-	}
-	
-	DrawSpriteBlobs( player, kNoSuction );
-	
-	PlayStereo( player, kRotate );
-}
-
-void DoDrop( int player )
-{
-	dropping[player] = true;
-	
-	if( role[player] != kJiggleBlobs &&
-		role[player] != kFastJiggleBlobs &&
-		role[player] != kLockdownBlobs      )
-		blobTime[player] = GameTickCount( );
-}
-
-void StopDrop( int player )
-{
-	dropping[player] = false;
-}
--- a/src/music.cpp
+++ b/src/music.cpp
@@ -4,6 +4,9 @@
 #include <vector>
 #include <fstream>
 
+extern "C"
+{
+
 #include "main.h"
 #include "music.h"
 #include "gworld.h"
@@ -11,16 +14,21 @@
 #include "soundfx.h"
 #include "graphics.h"
 
+}
+
 #include "support/ModStream.h"
 
 const int               k_noMusic = -1;
 const int               k_songs = 14;
 
+extern "C"
+{
 MBoolean                musicOn = true;
 int                     musicSelection = k_noMusic;
 
 static MBoolean         s_musicFast = false;
 int                     s_musicPaused = 0;
+}
 
 static cmixer::ModStream* s_musicChannel = NULL;
 
--- /dev/null
+++ b/src/next.c
@@ -1,0 +1,208 @@
+// next.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "next.h"
+#include "graphics.h"
+#include "gworld.h"
+#include "gameticks.h"
+#include "random.h"
+#include "blitter.h"
+#include "level.h"
+
+#define kJiggleFrames 8
+#define kPulling 10
+#define kPullingFrames 18
+
+SDL_Surface* nextSurface;
+SDL_Surface* nextDrawSurface;
+
+MRect nextWindowZRect, nextWindowRect[2];
+MBoolean nextWindowVisible[2] = {true, true};
+MTicks nextTime[2][2];
+int nextStage[2][2], pullA[2], pullB[2];
+
+void InitNext( void )
+{
+	const double windowLoc[] = {0.46, 0.54};
+	SDL_Rect     sdlRect;
+
+	nextWindowZRect.top = nextWindowZRect.left = 0;
+	nextWindowZRect.bottom = 72; nextWindowZRect.right = 32;
+	
+	nextWindowRect[0] = nextWindowRect[1] = nextWindowZRect;
+	CenterRectOnScreen( &nextWindowRect[0], windowLoc[0], 0.25 );
+	CenterRectOnScreen( &nextWindowRect[1], windowLoc[1], 0.25 );	
+	
+	nextSurface = LoadPICTAsSurface( picNext, 32 );
+	
+	nextDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &nextWindowZRect, &sdlRect ), 32 );
+}
+
+void RefreshNext( int player )
+{
+	nextStage[player][0] = 0;
+    nextStage[player][1] = 0;
+
+	nextTime[player][0] = GameTickCount( ) + RandomBefore( 60 );
+	nextTime[player][1] = GameTickCount( ) + RandomBefore( 60 );
+
+	ShowNext( player );
+}
+
+void PullNext( int player )
+{
+	pullA[player] = nextA[player];
+	pullB[player] = nextB[player];
+	nextStage[player][0] = kPulling;
+	nextTime[player][0] = GameTickCount( );
+}
+
+#define kNoDraw 999
+void ShowPull( int player )
+{
+	MRect    srcRect;
+	int      yank[8] = { 20, 18, 15, 8, -6, -26, -46, -66 };
+	int      slide[8] = { kNoDraw, 66, 48, 36, 29, 26, 24, 23 };
+	int      drawA, drawB, offset, count;
+	SDL_Rect sourceSDLRect, destSDLRect;
+	
+	if( !nextWindowVisible[player] ) return;
+	
+	SDLU_AcquireSurface( nextDrawSurface );
+	
+	SDLU_BlitSurface( nextSurface,     &nextSurface->clip_rect,
+					  nextDrawSurface, &nextDrawSurface->clip_rect );
+	
+	for( count=0; count<2; count++ )
+	{
+		offset = nextStage[player][0] - kPulling;
+		
+		switch( count )
+		{
+			case 0: drawA = pullA[player]; drawB = pullB[player]; offset = yank[offset];  break;
+			case 1: drawA = nextA[player]; drawB = nextB[player]; offset = slide[offset]; break;
+		}
+		
+		if( offset != kNoDraw )
+		{
+			MRect blobRect = { 0, 4, 0 + kBlobVertSize, 4 + kBlobHorizSize };
+			MRect shadowRect = { 4, 8, 4 + kBlobVertSize, 8 + kBlobHorizSize };
+
+			OffsetMRect( &blobRect, 0, offset );
+			OffsetMRect( &shadowRect, 0, offset );
+					
+			SurfaceDrawShadow( &shadowRect, drawB, kNoSuction );
+			
+			CalcBlobRect( kNoSuction, drawB-1, &srcRect );
+			SurfaceBlitBlob( &srcRect, &blobRect );	  
+					  
+			OffsetMRect( &blobRect, 0, kBlobVertSize );
+			OffsetMRect( &shadowRect, 0, kBlobVertSize );
+			
+			SurfaceDrawShadow( &shadowRect, drawA, nextM[player]? kFlashDarkBlob: kNoSuction );
+
+			CalcBlobRect( nextM[player]? kFlashDarkBlob: kNoSuction, drawA-1, &srcRect );
+			SurfaceBlitBlob( &srcRect, &blobRect );	  
+		}
+	}
+	
+	SDLU_ReleaseSurface( nextDrawSurface );
+	
+	SDLU_BlitFrontSurface( nextDrawSurface, 
+	                       SDLU_MRectToSDLRect( &nextWindowZRect, &sourceSDLRect ),
+	                       SDLU_MRectToSDLRect( &nextWindowRect[player], &destSDLRect ) );
+}
+
+void UpdateNext( int player )
+{
+	MBoolean changed = false;
+	int blob;
+	
+	if( nextStage[player][0] >= kPulling )
+	{
+		if( GameTickCount() > nextTime[player][0] )
+		{
+			if( ++nextStage[player][0] >= kPullingFrames )
+			{
+				RefreshNext( player );
+			}
+			else
+			{
+				ShowPull( player );
+				nextTime[player][0]++;
+			}
+		}
+	} 
+	else
+	{
+		for( blob=0; blob<2; blob++ )
+		{	
+			if( GameTickCount() > nextTime[player][blob] )
+			{
+				if( ++nextStage[player][blob] >= kJiggleFrames )
+				{
+					nextStage[player][blob] = 0;
+					nextTime[player][blob] += 40 + RandomBefore( 80 );
+				}
+				else
+				{
+					nextTime[player][blob] += 2;
+				}
+					
+				changed = true;
+			}
+		}
+		
+		if( changed ) ShowNext( player );
+	}
+}
+
+void ShowNext( int player )
+{
+	int      jiggle[kJiggleFrames] = { kNoSuction,  kSquish,  kNoSuction,  kSquash,    
+	                                   kNoSuction,  kSquish,  kNoSuction,  kSquash   };
+	int      nextFrame = kNoSuction;
+	MRect    blobRect = { 22, 4, 22 + kBlobVertSize, 4 + kBlobHorizSize };
+	MRect    shadowRect = { 26, 8, 26 + kBlobVertSize, 8 + kBlobHorizSize };
+	MRect    srcRect;
+	SDL_Rect sourceSDLRect, destSDLRect;
+	
+	if( !nextWindowVisible[player] ) return;
+
+	if( control[player] == kNobodyControl )
+	{
+	}
+	else
+	{
+		SDLU_AcquireSurface( nextDrawSurface );
+
+		SDLU_BlitSurface( nextSurface,     &nextSurface->clip_rect,
+						  nextDrawSurface, &nextDrawSurface->clip_rect );
+				
+		nextFrame = nextG[player]? kNoSuction: jiggle[nextStage[player][0]];
+		
+		SurfaceDrawShadow( &shadowRect, nextB[player], nextFrame );
+		
+		CalcBlobRect( nextFrame, nextB[player]-1, &srcRect );
+		SurfaceBlitBlob( &srcRect, &blobRect );	  
+				  
+		OffsetMRect( &blobRect, 0, kBlobVertSize );
+		OffsetMRect( &shadowRect, 0, kBlobVertSize );
+
+		nextFrame = nextG[player]? kNoSuction: 
+						(nextM[player]? kFlashDarkBlob: jiggle[nextStage[player][1]]);
+		
+		SurfaceDrawShadow( &shadowRect, nextA[player], nextFrame );
+
+		CalcBlobRect( nextFrame, nextA[player]-1, &srcRect );
+		SurfaceBlitBlob( &srcRect, &blobRect );	  
+		
+		SDLU_ReleaseSurface( nextDrawSurface );
+
+		SDLU_BlitFrontSurface( nextDrawSurface, 
+		                       SDLU_MRectToSDLRect( &nextWindowZRect, &sourceSDLRect ),
+		                       SDLU_MRectToSDLRect( &nextWindowRect[player], &destSDLRect ) );
+	}
+}
--- a/src/next.cpp
+++ /dev/null
@@ -1,208 +1,0 @@
-// next.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "next.h"
-#include "graphics.h"
-#include "gworld.h"
-#include "gameticks.h"
-#include "random.h"
-#include "blitter.h"
-#include "level.h"
-
-#define kJiggleFrames 8
-#define kPulling 10
-#define kPullingFrames 18
-
-SDL_Surface* nextSurface;
-SDL_Surface* nextDrawSurface;
-
-MRect nextWindowZRect, nextWindowRect[2];
-MBoolean nextWindowVisible[2] = {true, true};
-MTicks nextTime[2][2];
-int nextStage[2][2], pullA[2], pullB[2];
-
-void InitNext( void )
-{
-	const double windowLoc[] = {0.46, 0.54};
-	SDL_Rect     sdlRect;
-
-	nextWindowZRect.top = nextWindowZRect.left = 0;
-	nextWindowZRect.bottom = 72; nextWindowZRect.right = 32;
-	
-	nextWindowRect[0] = nextWindowRect[1] = nextWindowZRect;
-	CenterRectOnScreen( &nextWindowRect[0], windowLoc[0], 0.25 );
-	CenterRectOnScreen( &nextWindowRect[1], windowLoc[1], 0.25 );	
-	
-	nextSurface = LoadPICTAsSurface( picNext, 32 );
-	
-	nextDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &nextWindowZRect, &sdlRect ), 32 );
-}
-
-void RefreshNext( int player )
-{
-	nextStage[player][0] = 0;
-    nextStage[player][1] = 0;
-
-	nextTime[player][0] = GameTickCount( ) + RandomBefore( 60 );
-	nextTime[player][1] = GameTickCount( ) + RandomBefore( 60 );
-
-	ShowNext( player );
-}
-
-void PullNext( int player )
-{
-	pullA[player] = nextA[player];
-	pullB[player] = nextB[player];
-	nextStage[player][0] = kPulling;
-	nextTime[player][0] = GameTickCount( );
-}
-
-#define kNoDraw 999
-void ShowPull( int player )
-{
-	MRect    srcRect;
-	int      yank[8] = { 20, 18, 15, 8, -6, -26, -46, -66 };
-	int      slide[8] = { kNoDraw, 66, 48, 36, 29, 26, 24, 23 };
-	int      drawA, drawB, offset, count;
-	SDL_Rect sourceSDLRect, destSDLRect;
-	
-	if( !nextWindowVisible[player] ) return;
-	
-	SDLU_AcquireSurface( nextDrawSurface );
-	
-	SDLU_BlitSurface( nextSurface,     &nextSurface->clip_rect,
-					  nextDrawSurface, &nextDrawSurface->clip_rect );
-	
-	for( count=0; count<2; count++ )
-	{
-		offset = nextStage[player][0] - kPulling;
-		
-		switch( count )
-		{
-			case 0: drawA = pullA[player]; drawB = pullB[player]; offset = yank[offset];  break;
-			case 1: drawA = nextA[player]; drawB = nextB[player]; offset = slide[offset]; break;
-		}
-		
-		if( offset != kNoDraw )
-		{
-			MRect blobRect = { 0, 4, 0 + kBlobVertSize, 4 + kBlobHorizSize };
-			MRect shadowRect = { 4, 8, 4 + kBlobVertSize, 8 + kBlobHorizSize };
-
-			OffsetMRect( &blobRect, 0, offset );
-			OffsetMRect( &shadowRect, 0, offset );
-					
-			SurfaceDrawShadow( &shadowRect, drawB, kNoSuction );
-			
-			CalcBlobRect( kNoSuction, drawB-1, &srcRect );
-			SurfaceBlitBlob( &srcRect, &blobRect );	  
-					  
-			OffsetMRect( &blobRect, 0, kBlobVertSize );
-			OffsetMRect( &shadowRect, 0, kBlobVertSize );
-			
-			SurfaceDrawShadow( &shadowRect, drawA, nextM[player]? kFlashDarkBlob: kNoSuction );
-
-			CalcBlobRect( nextM[player]? kFlashDarkBlob: kNoSuction, drawA-1, &srcRect );
-			SurfaceBlitBlob( &srcRect, &blobRect );	  
-		}
-	}
-	
-	SDLU_ReleaseSurface( nextDrawSurface );
-	
-	SDLU_BlitFrontSurface( nextDrawSurface, 
-	                       SDLU_MRectToSDLRect( &nextWindowZRect, &sourceSDLRect ),
-	                       SDLU_MRectToSDLRect( &nextWindowRect[player], &destSDLRect ) );
-}
-
-void UpdateNext( int player )
-{
-	MBoolean changed = false;
-	int blob;
-	
-	if( nextStage[player][0] >= kPulling )
-	{
-		if( GameTickCount() > nextTime[player][0] )
-		{
-			if( ++nextStage[player][0] >= kPullingFrames )
-			{
-				RefreshNext( player );
-			}
-			else
-			{
-				ShowPull( player );
-				nextTime[player][0]++;
-			}
-		}
-	} 
-	else
-	{
-		for( blob=0; blob<2; blob++ )
-		{	
-			if( GameTickCount() > nextTime[player][blob] )
-			{
-				if( ++nextStage[player][blob] >= kJiggleFrames )
-				{
-					nextStage[player][blob] = 0;
-					nextTime[player][blob] += 40 + RandomBefore( 80 );
-				}
-				else
-				{
-					nextTime[player][blob] += 2;
-				}
-					
-				changed = true;
-			}
-		}
-		
-		if( changed ) ShowNext( player );
-	}
-}
-
-void ShowNext( int player )
-{
-	int      jiggle[kJiggleFrames] = { kNoSuction,  kSquish,  kNoSuction,  kSquash,    
-	                                   kNoSuction,  kSquish,  kNoSuction,  kSquash   };
-	int      nextFrame = kNoSuction;
-	MRect    blobRect = { 22, 4, 22 + kBlobVertSize, 4 + kBlobHorizSize };
-	MRect    shadowRect = { 26, 8, 26 + kBlobVertSize, 8 + kBlobHorizSize };
-	MRect    srcRect;
-	SDL_Rect sourceSDLRect, destSDLRect;
-	
-	if( !nextWindowVisible[player] ) return;
-
-	if( control[player] == kNobodyControl )
-	{
-	}
-	else
-	{
-		SDLU_AcquireSurface( nextDrawSurface );
-
-		SDLU_BlitSurface( nextSurface,     &nextSurface->clip_rect,
-						  nextDrawSurface, &nextDrawSurface->clip_rect );
-				
-		nextFrame = nextG[player]? kNoSuction: jiggle[nextStage[player][0]];
-		
-		SurfaceDrawShadow( &shadowRect, nextB[player], nextFrame );
-		
-		CalcBlobRect( nextFrame, nextB[player]-1, &srcRect );
-		SurfaceBlitBlob( &srcRect, &blobRect );	  
-				  
-		OffsetMRect( &blobRect, 0, kBlobVertSize );
-		OffsetMRect( &shadowRect, 0, kBlobVertSize );
-
-		nextFrame = nextG[player]? kNoSuction: 
-						(nextM[player]? kFlashDarkBlob: jiggle[nextStage[player][1]]);
-		
-		SurfaceDrawShadow( &shadowRect, nextA[player], nextFrame );
-
-		CalcBlobRect( nextFrame, nextA[player]-1, &srcRect );
-		SurfaceBlitBlob( &srcRect, &blobRect );	  
-		
-		SDLU_ReleaseSurface( nextDrawSurface );
-
-		SDLU_BlitFrontSurface( nextDrawSurface, 
-		                       SDLU_MRectToSDLRect( &nextWindowZRect, &sourceSDLRect ),
-		                       SDLU_MRectToSDLRect( &nextWindowRect[player], &destSDLRect ) );
-	}
-}
--- /dev/null
+++ b/src/opponent.c
@@ -1,0 +1,257 @@
+// opponent.c
+
+#include "SDLU.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "main.h"
+#include "level.h"
+#include "opponent.h"
+#include "gworld.h"
+#include "graphics.h"
+#include "random.h"
+#include "control.h"
+#include "players.h"
+#include "gameticks.h"
+#include "blitter.h"
+
+SDL_Surface* opponentSurface;
+SDL_Surface* opponentMaskSurface;
+SDL_Surface* opponentDrawSurface;
+
+MRect opponentWindowZRect, opponentWindowRect;
+int opponentMood, opponentFrame;
+MTicks opponentTime, glowTime[kGlows], panicTime;
+int glowFrame[kGlows], panicFrame;
+int heavyGlowArray[kGlowArraySize], glowArray[kGlowArraySize], lightGlowArray[kGlowArraySize];
+
+void InitOpponent( void )
+{
+	MRect    littleRect = {0, 0, 64, 64}, bigRect = {0, 0, 64, 64*(kOppFrames*3) };
+	SDL_Rect sdlRect;
+	double   index, value;
+	
+	opponentDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &littleRect, &sdlRect ), 32 );
+	opponentSurface     = SDLU_InitSurface( SDLU_MRectToSDLRect( &bigRect, &sdlRect ), 32 );
+
+	bigRect.bottom *= kGlows + 1;
+	opponentMaskSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &bigRect, &sdlRect ), MASK_DEPTH );
+	
+	opponentWindowZRect.top = opponentWindowZRect.left = 0;
+	opponentWindowZRect.bottom = opponentWindowZRect.right = 64;
+	opponentWindowRect = opponentWindowZRect;
+	CenterRectOnScreen( &opponentWindowRect, 0.5, 0.5 );
+		
+	opponentMood = 0;
+	
+	for( index=0; index<kGlowArraySize; index++ )
+	{
+		value = sin( index*kPi/kGlowArraySize );
+		value *= value;
+		
+		heavyGlowArray[(int)index] = (int)(value * 0.75  * 256);
+		glowArray     [(int)index] = (int)(value * 0.50  * 256);
+		lightGlowArray[(int)index] = (int)(value * 0.375 * 256);
+	}
+}
+
+void BeginOpponent( int which )
+{
+	int count;
+
+	DrawPICTInSurface( opponentSurface,     5000 + which );
+	DrawPICTInSurface( opponentMaskSurface, 5100 + which );
+
+	opponentTime = panicTime = GameTickCount( );
+	for( count=0; count<kGlows; count++ )
+	{
+		glowTime[count] = panicTime;
+		glowFrame[count] = 0;
+	}
+	
+	opponentMood = 0;
+	emotions[0] = emotions[1] = kEmotionNeutral;
+}
+
+void DrawFrozenOpponent( void )
+{
+	SDL_Rect   sourceSDLRect, destSDLRect;
+	MRect      myRect = {0, 0, 64, 64};
+
+	OffsetMRect( &myRect, opponentFrame * 64, 0 );
+
+	SDLU_BlitFrontSurface( opponentSurface, 
+	                       SDLU_MRectToSDLRect( &myRect, &sourceSDLRect ),
+	                       SDLU_MRectToSDLRect( &opponentWindowRect, &destSDLRect ) );
+}
+
+void OpponentPissed( void )
+{
+	opponentMood = 7;
+	opponentTime = GameTickCount();
+}
+
+void OpponentChatter( MBoolean on )
+{
+	switch( on )
+	{
+		case true:
+			opponentMood = 5;
+			opponentTime = GameTickCount();
+			break;
+			
+		case false:
+			opponentMood = 0;
+			opponentTime = GameTickCount();
+			break;
+	}
+}
+
+void UpdateOpponent( void )
+{
+	MRect    myRect = {0,0,64,64}, dstRect = {0,0,64,64}, maskRect;
+	int      emotiMap[] = {0, 1, 2, 1}, draw = false, count;
+	SDL_Rect srcSDLRect, dstSDLRect;
+	
+	if( GameTickCount( ) > opponentTime )
+	{
+		switch( opponentMood )
+		{
+			case 0: 				// Idle
+				opponentTime += 60 + RandomBefore(180);
+				opponentMood = RandomBefore(2) + 1;
+				opponentFrame = (emotiMap[emotions[1]] * kOppFrames);
+				break;
+			
+			case 1:					// Shifty Eyes
+				opponentTime += 40 + RandomBefore(60);
+				opponentMood = 0;
+				opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + RandomBefore(2) + 1;
+				break;
+
+			case 2:					// Blinks
+				opponentTime += 3;
+				opponentMood = 3;
+				opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 3;
+				break;
+			
+			case 3:					// Blinks (more)
+				opponentTime += 3;
+				opponentMood = 4;
+				opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 4;
+				break;
+			
+			case 4: 				// Blinks (more)
+				opponentTime += 3;
+				opponentMood = 0;
+				opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 3;
+				break;
+			
+			case 5:                 // Chatter (only good for tutorial)
+				opponentTime += 8;
+				opponentMood = 6;
+				opponentFrame = 5;
+				break;
+
+			case 6:					// Chatter 2 (only good for tutorial)
+				opponentTime += 8;
+				opponentMood = 5;
+				opponentFrame = 6;
+				break;
+			
+			case 7:					// Pissed (when hit with punishments)
+				opponentTime += 60;
+				opponentFrame = 7;
+				opponentMood = 0;
+				break;
+		}
+		
+		draw = true;
+	}
+	
+	if( GameTickCount( ) > panicTime )
+	{
+		panicTime += 2;
+		
+		if( emotions[1] == kEmotionPanic )
+		{
+			if( ++panicFrame >= kGlowArraySize ) panicFrame = 0;
+			draw = true;
+		}
+		else
+		{
+			panicFrame = 0;
+		}
+	}
+	
+	for( count=0; count<kGlows; count++ )
+	{
+		if( GameTickCount( ) > glowTime[count] )
+		{
+			glowTime[count] += character[1].glow[count].time;
+			
+			if( character[1].glow[count].colorR || character[1].glow[count].colorG || character[1].glow[count].colorB )
+			{
+				if( ++glowFrame[count] >= kGlowArraySize ) glowFrame[count] = 0;
+				draw = true;
+			}
+			else
+			{
+				glowFrame[count] = 0;
+			}
+		}
+	}
+	
+	if( draw )
+	{
+		OffsetMRect( &myRect, 64*opponentFrame, 0 );
+		
+		SDLU_AcquireSurface( opponentDrawSurface );
+		
+		SDLU_BlitSurface( opponentSurface,     SDLU_MRectToSDLRect( &myRect, &srcSDLRect ),
+		                  opponentDrawSurface, SDLU_MRectToSDLRect( &dstRect, &dstSDLRect )  );
+		
+		maskRect = myRect;
+		for( count=0; count<kGlows; count++ )
+		{
+			OffsetMRect( &maskRect, 0, 64 );
+
+			if( glowFrame[count] )
+			{
+				if( character[1].glow[count].isHeavy )
+				{
+					SurfaceBlitColor( opponentMaskSurface,  opponentDrawSurface,
+					                  &maskRect,            &dstRect, 
+					                   character[1].glow[count].colorR,
+									   character[1].glow[count].colorG,
+									   character[1].glow[count].colorB,
+									   heavyGlowArray[glowFrame[count]] );
+				}
+				else
+				{
+					SurfaceBlitColor( opponentMaskSurface,  opponentDrawSurface,
+					                  &maskRect,            &dstRect, 
+					                   character[1].glow[count].colorR,
+									   character[1].glow[count].colorG,
+									   character[1].glow[count].colorB,
+									   lightGlowArray[glowFrame[count]] );
+				}
+			}
+		}
+		
+		if( panicFrame )
+		{
+			SurfaceBlitColor(  opponentMaskSurface,  opponentDrawSurface,
+			                  &myRect,              &dstRect, 
+			                   _5TO8(31), _5TO8(31), _5TO8(22), glowArray[panicFrame] );
+		}
+		
+		SDLU_ReleaseSurface( opponentDrawSurface );
+		
+		SDLU_BlitFrontSurface( opponentDrawSurface, 
+		                       SDLU_MRectToSDLRect( &opponentWindowZRect, &srcSDLRect ),
+		                       SDLU_MRectToSDLRect( &opponentWindowRect,  &dstSDLRect )  );
+	}
+}
--- a/src/opponent.cpp
+++ /dev/null
@@ -1,257 +1,0 @@
-// opponent.c
-
-#include "SDLU.h"
-
-#include <math.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "main.h"
-#include "level.h"
-#include "opponent.h"
-#include "gworld.h"
-#include "graphics.h"
-#include "random.h"
-#include "control.h"
-#include "players.h"
-#include "gameticks.h"
-#include "blitter.h"
-
-SDL_Surface* opponentSurface;
-SDL_Surface* opponentMaskSurface;
-SDL_Surface* opponentDrawSurface;
-
-MRect opponentWindowZRect, opponentWindowRect;
-int opponentMood, opponentFrame;
-MTicks opponentTime, glowTime[kGlows], panicTime;
-int glowFrame[kGlows], panicFrame;
-int heavyGlowArray[kGlowArraySize], glowArray[kGlowArraySize], lightGlowArray[kGlowArraySize];
-
-void InitOpponent( void )
-{
-	MRect    littleRect = {0, 0, 64, 64}, bigRect = {0, 0, 64, 64*(kOppFrames*3) };
-	SDL_Rect sdlRect;
-	double   index, value;
-	
-	opponentDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &littleRect, &sdlRect ), 32 );
-	opponentSurface     = SDLU_InitSurface( SDLU_MRectToSDLRect( &bigRect, &sdlRect ), 32 );
-
-	bigRect.bottom *= kGlows + 1;
-	opponentMaskSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &bigRect, &sdlRect ), MASK_DEPTH );
-	
-	opponentWindowZRect.top = opponentWindowZRect.left = 0;
-	opponentWindowZRect.bottom = opponentWindowZRect.right = 64;
-	opponentWindowRect = opponentWindowZRect;
-	CenterRectOnScreen( &opponentWindowRect, 0.5, 0.5 );
-		
-	opponentMood = 0;
-	
-	for( index=0; index<kGlowArraySize; index++ )
-	{
-		value = sin( index*pi/kGlowArraySize );
-		value *= value;
-		
-		heavyGlowArray[(int)index] = (int)(value * 0.75  * 256);
-		glowArray     [(int)index] = (int)(value * 0.50  * 256);
-		lightGlowArray[(int)index] = (int)(value * 0.375 * 256);
-	}
-}
-
-void BeginOpponent( int which )
-{
-	int count;
-
-	DrawPICTInSurface( opponentSurface,     5000 + which );
-	DrawPICTInSurface( opponentMaskSurface, 5100 + which );
-
-	opponentTime = panicTime = GameTickCount( );
-	for( count=0; count<kGlows; count++ )
-	{
-		glowTime[count] = panicTime;
-		glowFrame[count] = 0;
-	}
-	
-	opponentMood = 0;
-	emotions[0] = emotions[1] = kEmotionNeutral;
-}
-
-void DrawFrozenOpponent( void )
-{
-	SDL_Rect   sourceSDLRect, destSDLRect;
-	MRect      myRect = {0, 0, 64, 64};
-
-	OffsetMRect( &myRect, opponentFrame * 64, 0 );
-
-	SDLU_BlitFrontSurface( opponentSurface, 
-	                       SDLU_MRectToSDLRect( &myRect, &sourceSDLRect ),
-	                       SDLU_MRectToSDLRect( &opponentWindowRect, &destSDLRect ) );
-}
-
-void OpponentPissed( void )
-{
-	opponentMood = 7;
-	opponentTime = GameTickCount();
-}
-
-void OpponentChatter( MBoolean on )
-{
-	switch( on )
-	{
-		case true:
-			opponentMood = 5;
-			opponentTime = GameTickCount();
-			break;
-			
-		case false:
-			opponentMood = 0;
-			opponentTime = GameTickCount();
-			break;
-	}
-}
-
-void UpdateOpponent( void )
-{
-	MRect    myRect = {0,0,64,64}, dstRect = {0,0,64,64}, maskRect;
-	int      emotiMap[] = {0, 1, 2, 1}, draw = false, count;
-	SDL_Rect srcSDLRect, dstSDLRect;
-	
-	if( GameTickCount( ) > opponentTime )
-	{
-		switch( opponentMood )
-		{
-			case 0: 				// Idle
-				opponentTime += 60 + RandomBefore(180);
-				opponentMood = RandomBefore(2) + 1;
-				opponentFrame = (emotiMap[emotions[1]] * kOppFrames);
-				break;
-			
-			case 1:					// Shifty Eyes
-				opponentTime += 40 + RandomBefore(60);
-				opponentMood = 0;
-				opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + RandomBefore(2) + 1;
-				break;
-
-			case 2:					// Blinks
-				opponentTime += 3;
-				opponentMood = 3;
-				opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 3;
-				break;
-			
-			case 3:					// Blinks (more)
-				opponentTime += 3;
-				opponentMood = 4;
-				opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 4;
-				break;
-			
-			case 4: 				// Blinks (more)
-				opponentTime += 3;
-				opponentMood = 0;
-				opponentFrame = (emotiMap[emotions[1]] * kOppFrames) + 3;
-				break;
-			
-			case 5:                 // Chatter (only good for tutorial)
-				opponentTime += 8;
-				opponentMood = 6;
-				opponentFrame = 5;
-				break;
-
-			case 6:					// Chatter 2 (only good for tutorial)
-				opponentTime += 8;
-				opponentMood = 5;
-				opponentFrame = 6;
-				break;
-			
-			case 7:					// Pissed (when hit with punishments)
-				opponentTime += 60;
-				opponentFrame = 7;
-				opponentMood = 0;
-				break;
-		}
-		
-		draw = true;
-	}
-	
-	if( GameTickCount( ) > panicTime )
-	{
-		panicTime += 2;
-		
-		if( emotions[1] == kEmotionPanic )
-		{
-			if( ++panicFrame >= kGlowArraySize ) panicFrame = 0;
-			draw = true;
-		}
-		else
-		{
-			panicFrame = 0;
-		}
-	}
-	
-	for( count=0; count<kGlows; count++ )
-	{
-		if( GameTickCount( ) > glowTime[count] )
-		{
-			glowTime[count] += character[1].glow[count].time;
-			
-			if( character[1].glow[count].colorR || character[1].glow[count].colorG || character[1].glow[count].colorB )
-			{
-				if( ++glowFrame[count] >= kGlowArraySize ) glowFrame[count] = 0;
-				draw = true;
-			}
-			else
-			{
-				glowFrame[count] = 0;
-			}
-		}
-	}
-	
-	if( draw )
-	{
-		OffsetMRect( &myRect, 64*opponentFrame, 0 );
-		
-		SDLU_AcquireSurface( opponentDrawSurface );
-		
-		SDLU_BlitSurface( opponentSurface,     SDLU_MRectToSDLRect( &myRect, &srcSDLRect ),
-		                  opponentDrawSurface, SDLU_MRectToSDLRect( &dstRect, &dstSDLRect )  );
-		
-		maskRect = myRect;
-		for( count=0; count<kGlows; count++ )
-		{
-			OffsetMRect( &maskRect, 0, 64 );
-
-			if( glowFrame[count] )
-			{
-				if( character[1].glow[count].isHeavy )
-				{
-					SurfaceBlitColor( opponentMaskSurface,  opponentDrawSurface,
-					                  &maskRect,            &dstRect, 
-					                   character[1].glow[count].colorR,
-									   character[1].glow[count].colorG,
-									   character[1].glow[count].colorB,
-									   heavyGlowArray[glowFrame[count]] );
-				}
-				else
-				{
-					SurfaceBlitColor( opponentMaskSurface,  opponentDrawSurface,
-					                  &maskRect,            &dstRect, 
-					                   character[1].glow[count].colorR,
-									   character[1].glow[count].colorG,
-									   character[1].glow[count].colorB,
-									   lightGlowArray[glowFrame[count]] );
-				}
-			}
-		}
-		
-		if( panicFrame )
-		{
-			SurfaceBlitColor(  opponentMaskSurface,  opponentDrawSurface,
-			                  &myRect,              &dstRect, 
-			                   _5TO8(31), _5TO8(31), _5TO8(22), glowArray[panicFrame] );
-		}
-		
-		SDLU_ReleaseSurface( opponentDrawSurface );
-		
-		SDLU_BlitFrontSurface( opponentDrawSurface, 
-		                       SDLU_MRectToSDLRect( &opponentWindowZRect, &srcSDLRect ),
-		                       SDLU_MRectToSDLRect( &opponentWindowRect,  &dstSDLRect )  );
-	}
-}
--- /dev/null
+++ b/src/pause.c
@@ -1,0 +1,1334 @@
+// pause.cpp
+
+// All of this code is fugly. I really needed a dialog manager, but I didn't know it at the time,
+// and instead I cobbled this together. It is just barely good enough to work. Fortunately it looks
+// decent to the end user...
+
+
+#include "SDLU.h"
+
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "main.h"
+#include "gameticks.h"
+#include "blitter.h"
+#include "graphics.h"
+#include "gworld.h"
+#include "pause.h"
+#include "random.h"
+#include "font.h"
+#include "music.h"
+#include "soundfx.h"
+#include "keyselect.h"
+#include "level.h"
+#include "victory.h"
+#include "hiscore.h"
+#include "score.h"
+
+typedef struct FRGBColor
+{
+	float red, green, blue;
+} FRGBColor;
+
+typedef struct ClickableZone
+{
+	int item;
+	MRect rect;
+} ClickableZone;
+
+SDL_Surface* backSurface;
+SDL_Surface* drawSurface;
+SDL_Surface* logoSurface;
+SDL_Surface* logoMaskSurface;
+SDL_Surface* logoAlphaSurface;
+
+SkittlesFontPtr smallFont, bigFont, dashedLineFont, continueFont, tinyFont, batsuFont;
+FRGBColor backColor[4];
+MBoolean continueTimeOut;
+
+static int dialogType, dialogStage, dialogTarget, dialogShade, dialogItem;
+MTicks dialogTimer;
+static float colorWrap = 0, colorInc;
+static MRect logoRect = {0, 0, 111, 246}, lastPauseRect;
+static MBoolean dialogStageComplete;
+static MBoolean timeToRedraw = false;
+
+// for the controls dialog
+static int controlToReplace = -1;
+
+// for the enter code dialog
+static char  nameField[256], keyField[256];
+static char* whichField = nameField;
+static int   batsuAlpha = 0;
+
+static void ItsTimeToRedraw()
+{
+	timeToRedraw = true;
+}
+
+enum
+{
+	kTextRainbow,
+	kTextBrightRainbow,
+	kTextWhite,
+	kTextBlueGlow,
+	kTextGray,
+	kTextAlmostWhite
+};
+
+static MPoint DrawRainbowText( SkittlesFontPtr font, const char *line, MPoint dPoint, float wave, int bright )
+{
+	int   current;
+	int   r,g,b;
+	float s;
+	
+	current = 0;
+	
+	switch( bright )
+	{	
+			case kTextGray:
+				r = g = b = 96;
+				break;
+				
+			case kTextBlueGlow:
+				s = sin(wave);
+				r = (int)(88.0 + 120.0 * s * s);
+				g = r;
+				b = 255;
+				break;
+				
+			case kTextWhite:
+				r = g = b = 255;
+				break;
+				
+			case kTextAlmostWhite:
+            default:
+				r = g = b = 224;
+				break;
+				
+	}
+
+	while( line[current] )
+	{
+		switch( bright )
+		{
+			case kTextBrightRainbow:
+				r = (int)(208.0 + 40.0 * sin(wave                    ));
+				g = (int)(208.0 + 40.0 * sin(wave + ((2.*kPi) * 1./3.)));
+				b = (int)(208.0 + 40.0 * sin(wave + ((2.*kPi) * 2./3.)));
+				break;
+
+			case kTextRainbow:
+				r = (int)(128.0 + 96.0 * sin(wave                    ));
+				g = (int)(128.0 + 96.0 * sin(wave + ((2.*kPi) * 1./3.)));
+				b = (int)(128.0 + 96.0 * sin(wave + ((2.*kPi) * 2./3.)));
+				break;
+		}
+
+		SurfaceBlitCharacter( font, line[current], &dPoint, r, g, b, 1 );
+		
+		wave += 0.2;
+		current++;
+	}
+	
+	return dPoint;
+}
+
+
+#define kEdgeSize 8
+static COLOR_T edge[4][kEdgeSize][kEdgeSize];
+
+void SurfaceGetEdges( SDL_Surface* edgeSurface, const MRect *rect )
+{
+	unsigned char* src[4];
+	int            srcRowBytes;
+	
+	src[0] = src[1] = src[2] = src[3] = (unsigned char*) edgeSurface->pixels;
+	srcRowBytes = edgeSurface->pitch;
+
+	src[0] += (srcRowBytes * (rect->top               )) + ((rect->left             ) * BYTES_PER_PIXEL);
+	src[1] += (srcRowBytes * (rect->top               )) + ((rect->right - kEdgeSize) * BYTES_PER_PIXEL);
+	src[2] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->left             ) * BYTES_PER_PIXEL);
+	src[3] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->right - kEdgeSize) * BYTES_PER_PIXEL);
+	
+	for (int count=0; count<4; count++)
+	{
+		for (int height=0; height<kEdgeSize; height++)
+		{
+			memcpy( edge[count][height], src[count], kEdgeSize * BYTES_PER_PIXEL );
+			src[count] += srcRowBytes;
+		}
+	}
+}
+
+
+void SurfaceCurveEdges( SDL_Surface* edgeSurface, const MRect *rect )
+{
+	unsigned char* src[4];
+	int srcRowBytes, width, height, count;
+	char edgeMap[4][kEdgeSize][kEdgeSize+1]={  "      --",
+					                           "    -...",
+					                           "   -.xxX",
+					                           "  -.xXXX",
+					                           " -.xXXXX",
+					                           " .xXXXXX",
+					                           "-.xXXXXX",
+					                           "-.XXXXXX",
+					                           "--      ",
+					                           "...-    ",
+					                           "Xxx.-   ",
+					                           "XXXx.-  ",
+					                           "XXXXx.- ",
+					                           "XXXXXx. ",
+					                           "XXXXXx.-",
+					                           "XXXXXX.-",
+					                           "-.XXXXXX",
+					                           "-.xXXXXX",
+					                           " .xXXXXX",
+					                           " -.xXXXX",
+					                           "  -.xXXX",
+					                           "   -.xxX",
+					                           "    -...",
+					                           "      --",
+					                           "XXXXXX.-",
+					                           "XXXXXx.-",
+					                           "XXXXXx. ",
+					                           "XXXXx.- ",
+					                           "XXXx.-  ",
+					                           "Xxx.-   ",
+					                           "...-    ",
+					                           "--      "  };
+	                         	                         
+	
+	src[0] = src[1] = src[2] = src[3] = (unsigned char*) edgeSurface->pixels;
+	srcRowBytes = edgeSurface->pitch;
+
+	src[0] += (srcRowBytes * (rect->top               )) + ((rect->left             ) * BYTES_PER_PIXEL);
+	src[1] += (srcRowBytes * (rect->top               )) + ((rect->right - kEdgeSize) * BYTES_PER_PIXEL);
+	src[2] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->left             ) * BYTES_PER_PIXEL);
+	src[3] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->right - kEdgeSize) * BYTES_PER_PIXEL);
+	
+    const int HALFBRIGHT_MASK =
+           (CHANNEL_MASK >> 1)
+        | ((CHANNEL_MASK >> 1) << BITS_PER_1CHANNEL)
+        | ((CHANNEL_MASK >> 1) << BITS_PER_2CHANNELS);
+
+    // Draw top/bottom border
+	{
+		COLOR_T *srcT1 = (COLOR_T*) (src[0]) + kEdgeSize;
+		COLOR_T *srcB1 = (COLOR_T*) (src[2] + (srcRowBytes*(kEdgeSize-1))) + kEdgeSize;
+		COLOR_T *srcT2 = srcT1 + (srcRowBytes / BYTES_PER_PIXEL);
+		COLOR_T *srcB2 = srcB1 - (srcRowBytes / BYTES_PER_PIXEL);
+        
+		for( width = rect->right - rect->left - (kEdgeSize * 2); width > 0; width-- )
+		{
+			*srcT1 = 0; srcT1++;
+			*srcB1 = 0; srcB1++;
+			*srcT2 = (*srcT2 >> 1) & HALFBRIGHT_MASK; srcT2++;
+			*srcB2 = (*srcB2 >> 1) & HALFBRIGHT_MASK; srcB2++;
+		}
+	}
+	
+	// Draw left/right border
+	{
+		unsigned char *srcL1 = (src[0] + (srcRowBytes * kEdgeSize));
+		unsigned char *srcR1 = (src[1] + (srcRowBytes * kEdgeSize)) + BYTES_PER_PIXEL * (kEdgeSize-1);
+
+		unsigned char *srcL2 = srcL1 + BYTES_PER_PIXEL;
+		unsigned char *srcR2 = srcR1 - BYTES_PER_PIXEL;
+		
+		for( height = rect->bottom - rect->top - (kEdgeSize * 2); height > 0; height-- )
+		{
+			*(COLOR_T*)srcL1 = 0;
+			*(COLOR_T*)srcR1 = 0;
+			*(COLOR_T*)srcL2 = (*(COLOR_T*)srcL2 >> 1) & HALFBRIGHT_MASK;
+			*(COLOR_T*)srcR2 = (*(COLOR_T*)srcR2 >> 1) & HALFBRIGHT_MASK;
+			
+			srcL1 += srcRowBytes; 
+			srcR1 += srcRowBytes;
+			srcL2 += srcRowBytes; 
+			srcR2 += srcRowBytes;
+		}
+	}
+		
+	// Draw curved edges
+	for( count=0; count<4; count++ )
+	{
+		COLOR_T *srcS = (COLOR_T*) src[count];
+		
+		for( height=0; height<kEdgeSize; height++ )
+		{
+			for( width=0; width<kEdgeSize; width++ )
+			{
+				switch( edgeMap[count][height][width] )
+				{
+					case ' ': 	*srcS = edge[count][height][width]; break;
+                    case '-':	*srcS = (edge[count][height][width] >> 1) & HALFBRIGHT_MASK; break;
+					case '.': 	*srcS = 0; break;
+					case 'x': 	*srcS = (*srcS >> 1) & HALFBRIGHT_MASK; break;
+					case 'X': 	break;
+				}
+				srcS++;
+			}
+			srcS += (srcRowBytes / BYTES_PER_PIXEL) - kEdgeSize;
+		}
+	}
+}
+
+enum
+{
+	kOpening = 0, 
+	kClosing
+};
+
+static MBoolean DrawDialogBox( MBoolean larger, int animationType, int *target, int skip, float *colorWrap, float colorInc, MRect *pauseRect )
+{
+	MBoolean animationStageComplete = false;
+	MRect normalRect[2][19]     = { { { 240 - 10,  320 - 30,  240 + 10,  320 + 30  },
+	                                  { 240 - 40,  320 - 120, 240 + 40,  320 + 120 },
+	                                  { 240 - 60,  320 - 180, 240 + 60,  320 + 180 },
+	                                  { 240 - 70,  320 - 210, 240 + 70,  320 + 210 },
+	                                  { 240 - 80,  320 - 230, 240 + 80,  320 + 230 },
+	                                  { 240 - 88,  320 - 245, 240 + 88,  320 + 245 },
+	                                  { 240 - 95,  320 - 252, 240 + 95,  320 + 252 },
+	                                  { 240 - 101, 320 - 255, 240 + 101, 320 + 255 },
+	                                  { 240 - 106, 320 - 252, 240 + 106, 320 + 252 },
+	                                  { 240 - 110, 320 - 245, 240 + 110, 320 + 245 },
+	                                  { 240 - 113, 320 - 238, 240 + 113, 320 + 238 },
+	                                  { 240 - 115, 320 - 232, 240 + 115, 320 + 232 },
+	                                  { 240 - 116, 320 - 228, 240 + 116, 320 + 228 },
+	                                  { 240 - 118, 320 - 232, 240 + 118, 320 + 230 },
+	                                  { 240 - 118, 320 - 238, 240 + 118, 320 + 232 },
+	                                  { 240 - 119, 320 - 242, 240 + 119, 320 + 242 },
+	                                  { 240 - 119, 320 - 244, 240 + 119, 320 + 244 },
+	                                  { 240 - 119, 320 - 242, 240 + 119, 320 + 242 },
+	                                  { 240 - 120, 320 - 240, 240 + 120, 320 + 240 }  },
+	                                { { 240 - 110, 320 - 220, 240 + 110, 320 + 220 }, 
+	                                  { 240 - 105, 320 - 210, 240 + 105, 320 + 210 }, 
+	                                  { 240 - 100, 320 - 200, 240 + 100, 320 + 200 }, 
+	                                  { 240 - 95,  320 - 190, 240 + 95,  320 + 190 }, 
+	                                  { 240 - 90,  320 - 180, 240 + 90,  320 + 180 }, 
+	                                  { 240 - 85,  320 - 170, 240 + 85,  320 + 170 }, 
+	                                  { 240 - 80,  320 - 160, 240 + 80,  320 + 160 }, 
+	                                  { 240 - 75,  320 - 150, 240 + 75,  320 + 150 }, 
+	                                  { 240 - 70,  320 - 140, 240 + 70,  320 + 140 }, 
+	                                  { 240 - 65,  320 - 130, 240 + 65,  320 + 130 }, 
+	                                  { 240 - 60,  320 - 120, 240 + 60,  320 + 120 }, 
+	                                  { 240 - 55,  320 - 110, 240 + 55,  320 + 110 }, 
+	                                  { 240 - 50,  320 - 100, 240 + 50,  320 + 100 }, 
+	                                  { 240 - 45,  320 - 90,  240 + 45,  320 + 90  }, 
+	                                  { 240 - 40,  320 - 80,  240 + 40,  320 + 80  }, 
+	                                  { 240 - 35,  320 - 70,  240 + 35,  320 + 70  }, 
+	                                  { 240 - 30,  320 - 60,  240 + 30,  320 + 60  }, 
+	                                  { 240 - 25,  320 - 50,  240 + 25,  320 + 50  }, 	                                
+	                                  { 240 - 20,  320 - 40,  240 + 20,  320 + 40  }  }
+	                              };
+
+	MRect largerRect[2][19]     = { { { 240 - 11,  320 - 30,  240 + 11,  320 + 30  },
+	                                  { 240 - 44,  320 - 120, 240 + 44,  320 + 120 },
+	                                  { 240 - 66,  320 - 180, 240 + 66,  320 + 180 },
+	                                  { 240 - 77,  320 - 210, 240 + 77,  320 + 210 },
+	                                  { 240 - 88,  320 - 230, 240 + 88,  320 + 230 },
+	                                  { 240 - 97,  320 - 245, 240 + 97,  320 + 245 },
+	                                  { 240 - 104, 320 - 252, 240 + 104, 320 + 252 },
+	                                  { 240 - 111, 320 - 255, 240 + 111, 320 + 255 },
+	                                  { 240 - 117, 320 - 252, 240 + 117, 320 + 252 },
+	                                  { 240 - 121, 320 - 245, 240 + 121, 320 + 245 },
+	                                  { 240 - 124, 320 - 238, 240 + 124, 320 + 238 },
+	                                  { 240 - 126, 320 - 232, 240 + 126, 320 + 232 },
+	                                  { 240 - 128, 320 - 228, 240 + 128, 320 + 228 },
+	                                  { 240 - 130, 320 - 232, 240 + 130, 320 + 230 },
+	                                  { 240 - 130, 320 - 238, 240 + 130, 320 + 232 },
+	                                  { 240 - 131, 320 - 242, 240 + 131, 320 + 242 },
+	                                  { 240 - 131, 320 - 244, 240 + 131, 320 + 244 },
+	                                  { 240 - 131, 320 - 242, 240 + 131, 320 + 242 },
+	                                  { 240 - 132, 320 - 240, 240 + 132, 320 + 240 }  },
+	                                { { 240 - 121, 320 - 220, 240 + 121, 320 + 220 }, 
+	                                  { 240 - 115, 320 - 210, 240 + 115, 320 + 210 }, 
+	                                  { 240 - 110, 320 - 200, 240 + 110, 320 + 200 }, 
+	                                  { 240 - 104, 320 - 190, 240 + 104, 320 + 190 }, 
+	                                  { 240 - 99,  320 - 180, 240 + 99,  320 + 180 }, 
+	                                  { 240 - 93,  320 - 170, 240 + 93,  320 + 170 }, 
+	                                  { 240 - 88,  320 - 160, 240 + 88,  320 + 160 }, 
+	                                  { 240 - 82,  320 - 150, 240 + 82,  320 + 150 }, 
+	                                  { 240 - 77,  320 - 140, 240 + 77,  320 + 140 }, 
+	                                  { 240 - 71,  320 - 130, 240 + 71,  320 + 130 }, 
+	                                  { 240 - 66,  320 - 120, 240 + 66,  320 + 120 }, 
+	                                  { 240 - 60,  320 - 110, 240 + 60,  320 + 110 }, 
+	                                  { 240 - 55,  320 - 100, 240 + 55,  320 + 100 }, 
+	                                  { 240 - 49,  320 - 90,  240 + 49,  320 + 90  }, 
+	                                  { 240 - 44,  320 - 80,  240 + 44,  320 + 80  }, 
+	                                  { 240 - 38,  320 - 70,  240 + 38,  320 + 70  }, 
+	                                  { 240 - 33,  320 - 60,  240 + 33,  320 + 60  }, 
+	                                  { 240 - 27,  320 - 50,  240 + 27,  320 + 50  }, 	                                
+	                                  { 240 - 22,  320 - 40,  240 + 22,  320 + 40  }  }
+	                              };
+
+	int      colorInt, shading;
+	float    colorFrac, nColorFrac;
+	MRect    newRect;
+	SDL_Rect sdlRect;
+	
+	if( *target > 18 )
+	{
+		*target = 18;
+		animationStageComplete = true;
+	}
+
+	colorInt  = (int) floor( *colorWrap );
+	colorFrac = *colorWrap - colorInt;
+
+	newRect = larger? largerRect[animationType][*target]: normalRect[animationType][*target];
+	shading = ((animationType == 0) ? (*target * 24 / 18): (24 - (*target * 2 / 3)));
+	
+	{
+		float r1 = backColor[colorInt      ].red, g1 = backColor[colorInt      ].green, b1 = backColor[colorInt      ].blue,
+		      r2 = backColor[(colorInt+1)&3].red, g2 = backColor[(colorInt+1)&3].green, b2 = backColor[(colorInt+1)&3].blue,
+		      r3 = backColor[(colorInt+2)&3].red, g3 = backColor[(colorInt+2)&3].green, b3 = backColor[(colorInt+2)&3].blue,
+		      r4 = backColor[(colorInt+3)&3].red, g4 = backColor[(colorInt+3)&3].green, b4 = backColor[(colorInt+3)&3].blue;
+		
+		nColorFrac = 1 - colorFrac;
+		
+		SDLU_AcquireSurface( drawSurface );
+		
+		SurfaceBlitBlendOver(  backSurface,  drawSurface,
+		                      &newRect,     &newRect,
+  		                       (int)((r1 * nColorFrac) + (r2 * colorFrac)), 
+						       (int)((g1 * nColorFrac) + (g2 * colorFrac)), 
+						       (int)((b1 * nColorFrac) + (b2 * colorFrac)), 
+						       (int)((r2 * nColorFrac) + (r3 * colorFrac)), 
+						       (int)((g2 * nColorFrac) + (g3 * colorFrac)), 
+						       (int)((b2 * nColorFrac) + (b3 * colorFrac)), 
+						       (int)((r4 * nColorFrac) + (r1 * colorFrac)), 
+						       (int)((g4 * nColorFrac) + (g1 * colorFrac)), 
+						       (int)((b4 * nColorFrac) + (b1 * colorFrac)), 
+						       (int)((r3 * nColorFrac) + (r4 * colorFrac)), 
+						       (int)((g3 * nColorFrac) + (g4 * colorFrac)), 
+						       (int)((b3 * nColorFrac) + (b4 * colorFrac)), 
+						       _5TO8(shading) );
+
+		if( pauseRect->left < newRect.left ) 
+		{
+			MRect eraseRect = *pauseRect;
+			pauseRect->left = eraseRect.right = newRect.left;
+			
+			SDLU_MRectToSDLRect( &eraseRect, &sdlRect );
+			SDLU_BlitSurface( backSurface, &sdlRect,
+			                  drawSurface, &sdlRect  );
+		}
+
+		if( pauseRect->right > newRect.right ) 
+		{
+			MRect eraseRect = *pauseRect;
+			pauseRect->right = eraseRect.left = newRect.right;
+			
+			SDLU_MRectToSDLRect( &eraseRect, &sdlRect );
+			SDLU_BlitSurface( backSurface, &sdlRect,
+			                  drawSurface, &sdlRect  );
+		}
+
+		if( pauseRect->top < newRect.top ) 
+		{
+			MRect eraseRect = *pauseRect;
+			pauseRect->top = eraseRect.bottom = newRect.top;
+
+			SDLU_MRectToSDLRect( &eraseRect, &sdlRect );
+			SDLU_BlitSurface( backSurface, &sdlRect,
+			                  drawSurface, &sdlRect  );
+		}
+
+		if( pauseRect->bottom > newRect.bottom ) 
+		{
+			MRect eraseRect = *pauseRect;
+			pauseRect->bottom = eraseRect.top = newRect.bottom;
+
+			SDLU_MRectToSDLRect( &eraseRect, &sdlRect );
+			SDLU_BlitSurface( backSurface, &sdlRect,
+			                  drawSurface, &sdlRect  );
+		}
+
+		SDLU_ReleaseSurface( drawSurface );
+	}
+	
+	*pauseRect = newRect;
+	
+	*colorWrap += colorInc * skip;
+	if( *colorWrap >= 4 ) *colorWrap -= 4;
+
+	*target += skip;
+
+	return animationStageComplete;
+}
+
+#if USE_CURSOR_SPRITE
+static void DrawDialogCursor( MRect *pauseRect, int *shade )
+{
+	MPoint p, q;
+    (void) shade;
+    
+	SDLU_GetMouse( &p );
+	
+	if( p.h < (pauseRect->left      ) ) p.h = pauseRect->left;
+	if( p.h > (pauseRect->right  - 5) ) p.h = pauseRect->right  - 5;
+	if( p.v < (pauseRect->top       ) ) p.v = pauseRect->top;
+	if( p.v > (pauseRect->bottom - 5) ) p.v = pauseRect->bottom - 5;
+	q = p;
+	
+	SDLU_AcquireSurface( drawSurface );
+
+	SurfaceBlitCharacter( smallFont, '\x05', &p,  0,  0,  0, 0 );
+	SurfaceBlitCharacter( smallFont, '\x04', &q, 255, 255, 255, 0 );
+	
+	SDLU_ReleaseSurface( drawSurface );
+}
+#endif
+
+static void DrawDialogLogo( MRect *pauseRect, int shade )
+{
+	MRect drawRect;
+	int alpha;
+
+	drawRect.left   = (pauseRect->left + ((pauseRect->right - pauseRect->left) * 1 / 2) ) - (logoRect.right / 2);
+	drawRect.top    = (pauseRect->top + 14);
+	drawRect.bottom = drawRect.top + logoRect.bottom;
+	drawRect.right  = drawRect.left + logoRect.right;
+	
+	SDLU_AcquireSurface( drawSurface );
+	
+	alpha = (shade > 63)? 255: (shade * 4);
+		
+	SurfaceBlitWeightedDualAlpha( drawSurface,  logoSurface,  logoMaskSurface,  logoAlphaSurface,  drawSurface,
+                                  &drawRect,    &logoRect,    &logoRect,        &logoRect,         &drawRect,
+                                   alpha );
+
+	SDLU_ReleaseSurface( drawSurface );
+}
+
+
+enum
+{ 
+	kNothing = -1,
+	kBack = -2,
+
+// main pause screen (kEndGame is reused in continue and register)
+	kMusic = 0,		kResume,
+	kSound,         kEndGame,
+	kVideo,         kControls,
+	kWarp,
+
+// continue screen
+    kContinue,      
+    
+// controls screen
+    k1PLeft,        k2PLeft,
+    k1PRight,       k2PRight,
+    k1PDrop,        k2PDrop,
+    k1PRotate,      k2PRotate,
+    kControlsOK,    kControlsReset,
+
+// video settings screen
+    kFullscreen,
+	kWidescreen,
+    kScalingMode,
+	kVideoOK,
+};
+
+static void DrawContinueContents( int *item, int shade )
+{
+	char line[4][50] = { "Do you want to continue?",
+	                     "Yes",
+	                     "No",
+	                     "" };	                 
+	MPoint dPoint[4] = { {233, 210}, {280, 220}, {280, 400}, {335, 400} }, hPoint = {255, 320};
+	static int lastCountdown = 0;
+	int index, countdown, fade;
+	int r, g, b;
+	                 
+	sprintf( line[3], "%d credit%c", credits, (credits != 1)? 's': ' ' );
+
+	SDLU_AcquireSurface( drawSurface );
+
+	for( index=0; index<4; index++ )
+	{	
+		DrawRainbowText( smallFont, line[index], dPoint[index], (0.25 * index) + (0.075 * shade), 
+						 ( (index == 0)                          ||
+						  ((index == 1) && (*item == kContinue)) ||
+						  ((index == 2) && (*item == kEndGame ))    )? kTextBrightRainbow: kTextRainbow );
+	}
+	
+	countdown = shade / 100;
+	if( countdown < 10 )
+	{
+		continueTimeOut = false;
+		
+		if( (countdown != 0) && (countdown != lastCountdown) )
+		{
+			PlayMono( kContinueSnd );
+		}
+		lastCountdown = countdown;
+		
+		if( countdown < 5 )
+		{
+			r = (countdown * 31) / 5;
+			g = 31;
+		}
+		else
+		{
+			r = 31;
+			g = ((10 - countdown) * 31) / 5;
+		}
+			
+		fade = shade % 100;
+		if( fade > 50 ) fade = 50;
+		r = ((31 * (49 - fade)) + (r * fade)) / 49;
+		g = ((31 * (49 - fade)) + (g * fade)) / 49;
+		b = ((31 * (49 - fade))) / 49;
+		
+		countdown = '9' - countdown;
+		hPoint.h -= continueFont->width[countdown] / 2;
+
+		for( shade = 4; shade > 0; shade-- )
+		{
+			MPoint hP = hPoint;
+			
+			hP.h += 2 * shade;
+			hP.v += 2 * shade;
+
+            int weight = 20 - 4*shade;
+            
+			SurfaceBlitWeightedCharacter( continueFont, countdown, &hP, 0, 0, 0, _5TO8(weight) );
+		}
+
+		SurfaceBlitCharacter( continueFont, countdown, &hPoint, _5TO8(r), _5TO8(g), _5TO8(b), 0 );
+	}
+	else
+	{
+		continueTimeOut = true;
+	}
+	
+	SDLU_ReleaseSurface( drawSurface );
+}
+
+static void DrawHiScoreContents( int *item, int shade )
+{
+	MPoint       dPoint[3] = { {240, 640}, {260, 640}, {335, 400} }, hPoint = {294, 145};
+	MPoint       dashedLinePoint = { 320, 140 };
+	int          index;
+	int          nameLength;
+    const char*  line[3];
+    const char*  scan;
+	
+	(void) item; // is unused
+
+	line[0] = highScoreText;
+	line[1] = "Please enter your name and press return:";
+	line[2] = highScoreRank;
+
+	for( index=0; index<2; index++ )
+	{
+		scan = line[index];
+		while( *scan )
+			dPoint[index].h -= smallFont->width[(uint8_t) * scan++];
+		
+		dPoint[index].h /= 2;
+	}	
+		
+	SDLU_AcquireSurface( drawSurface );	
+
+	while( dashedLinePoint.h < 490 )
+	{
+		SurfaceBlitCharacter( dashedLineFont, '.', &dashedLinePoint, 0, 0, 0, 0 );
+	}
+	
+	nameLength = (int) strlen(highScoreName);
+	for( index = 0; index < nameLength; index++ )
+	{
+		SurfaceBlitCharacter( bigFont, highScoreName[index], &hPoint, 255, 255, 255, 1 );
+		if( hPoint.h >= 475 )
+		{
+			highScoreName[index] = '\0';
+			break;
+		}
+	}
+
+	index = (int)(( 1.0 + sin( MTickCount() / 7.5 ) ) * 120.0);
+	SurfaceBlitCharacter( bigFont, '|', &hPoint, index, index, 255, 1 );
+
+	for( index=0; index<3; index++ )
+	{	
+		DrawRainbowText( smallFont, line[index], dPoint[index], (0.25 * index) + (0.075 * shade), (index != 2)? kTextBrightRainbow: kTextRainbow );
+	}
+	
+	SDLU_ReleaseSurface( drawSurface );	
+}
+
+static void DrawControlsContents( int *item, int shade )
+{
+	MBoolean    highlight;
+	MPoint      dPoint;
+	int         index;
+	const char* controlName;
+	int         r, g, b;
+	const char  label[8][20] = { "1P Left",   "2P Left", 
+	                             "1P Right",  "2P Right", 
+	                             "1P Drop",   "2P Drop",
+	                             "1P Rotate", "2P Rotate" };
+	                           
+	                         
+	SDLU_AcquireSurface( drawSurface );	
+
+	for( index=0; index<8; index++ )
+	{	
+		highlight = (index == (*item - k1PLeft));
+		
+		dPoint.v = 229 + ((index & ~1) * 13);
+		dPoint.h = (index & 1)? 325: 130;
+		DrawRainbowText( smallFont, label[index], dPoint, (0.25 * index) + (0.075 * shade), highlight? kTextBrightRainbow: kTextRainbow );
+				
+		dPoint.v = 245 + ((index & ~1) * 13);
+		dPoint.h = (index & 1)? 420: 225;		
+
+        r = highlight? 255: 0;
+		g = b = (int)(highlight? 255.0 - (88.0 * (sin(shade * 0.2) + 1.0)): 0.0);
+		
+        SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );
+		SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );
+		SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );
+		SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );
+		SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );  // 80 pixels across
+		
+		controlName = SDL_GetKeyName( playerKeys[index & 1][index >> 1] );
+		if( controlName == NULL ) controlName = "???";
+		
+		dPoint.v = 231 + ((index & ~1) * 13);
+		dPoint.h = (index & 1)? 460: 265;		
+		dPoint.h -= GetTextWidth( tinyFont, controlName ) / 2;
+		DrawRainbowText( tinyFont, controlName, dPoint, (0.1 * shade), (controlToReplace == index)? kTextBlueGlow: kTextWhite );		
+	}
+
+	dPoint.h = 200;
+	dPoint.v = 340;
+	DrawRainbowText( smallFont, "\x03 OK", dPoint, 8.0 + (0.075 * shade), (*item == kControlsOK)? kTextBrightRainbow: kTextRainbow );
+
+	dPoint.h = 365;
+	dPoint.v = 340;
+	DrawRainbowText( smallFont, "\x03 Reset", dPoint, 8.25 + (0.075 * shade), (*item == kControlsReset)? kTextBrightRainbow: kTextRainbow );
+	
+	SDLU_ReleaseSurface( drawSurface );
+}
+
+static void DrawPauseContents( int *item, int shade )
+{
+	const char *line[] =
+	{
+		musicOn ? "\x01 Music" : "\x02 Music",
+		level == kTutorialLevel ? "\x03 Skip Tutorial" : "\x03 Resume",
+		soundOn ? "\x01 Sound" : "\x02 Sound",
+		"\x03 End Game",
+        "\x03 Video",
+		"\x03 Controls",
+	};
+
+	SDLU_AcquireSurface( drawSurface );	
+	
+	for( int i = 0; i < arrsize(line); i++ )
+	{	
+		MPoint dPoint;
+		dPoint.h = (i & 1)? 340: 180;
+		dPoint.v = 240 + ((i & ~1) * 15);
+		
+		DrawRainbowText( smallFont, line[i], dPoint, (0.25 * i) + (0.075 * shade), (*item == i)? kTextBrightRainbow: kTextRainbow );
+	}
+	
+	SDLU_ReleaseSurface( drawSurface );
+}
+
+static void DrawVideoSettingsContents(int* item, int shade)
+{
+	struct ZoneLabel
+	{
+		int item;
+		const char* text;
+	}
+	labels[] =
+	{
+		{ kFullscreen,		fullscreen ? "\x01 Fullscreen" : "\x02 Fullscreen" },
+		{ kWidescreen,		widescreen ? "\x01 Widescreen" : "\x02 Widescreen" },
+		{ kScalingMode,		crispUpscaling ? "\x01 Crisp upscaling" : "\x02 Crisp upscaling" },
+		{ kVideoOK,			"\x03 OK" },
+	};
+
+	SDLU_AcquireSurface(drawSurface);
+
+	for (int i = 0; i < arrsize(labels); i++)
+	{
+		MPoint dPoint;
+		dPoint.h = 180;
+		dPoint.v = 240 + (i * 30);
+
+		DrawRainbowText(smallFont, labels[i].text, dPoint, (0.25 * i) + (0.075 * shade), (*item == labels[i].item) ? kTextBrightRainbow : kTextRainbow);
+	}
+
+	SDLU_ReleaseSurface(drawSurface);
+}
+
+static MBoolean GetClickedZone( int* item, SDL_Keycode inSDLKey, int numZones, const ClickableZone* zones )
+{
+	if( inSDLKey == SDLK_ESCAPE )
+	{
+		*item = kBack;
+		return true;
+	}
+
+	MPoint p;
+	SDLU_GetMouse(&p);
+
+	int trigger = SDLU_Button();
+
+	*item = kNothing;
+	for( int i = 0; i < numZones; i++ )
+	{
+		if( MPointInMRect( p, &zones[i].rect ) )
+		{
+			*item = zones[i].item;
+		}
+	}
+
+	return trigger;
+}
+
+
+static MBoolean ContinueSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
+{
+	static const ClickableZone zones[] =
+	{
+		{ kContinue,	{280, 220, 300, 260} },
+		{ kEndGame,		{280, 400, 300, 440} },
+	};
+	
+	(void) inSDLKey; // is unused
+	
+	if( continueTimeOut )
+	{
+		*item = kEndGame;
+		return true;
+	}
+
+	int trigger = GetClickedZone( item, inSDLKey, arrsize(zones), zones );
+
+	if (trigger && *item == kBack)
+		*item = kContinue;
+	
+	return trigger && *item != kNothing;
+}
+
+static MBoolean HiScoreSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
+{
+	int nameLength = (int) strlen(highScoreName);
+	
+	// return (SDL key)
+	if( inSDLKey == SDLK_RETURN )
+	{
+		if( nameLength > 0 )
+		{
+			*item = kResume;
+			PlayMono( kSquishy );
+            return true;
+		}
+		else
+		{
+			PlayMono( kClick );
+		}
+	}
+	
+	// backspace (SDL key)
+	if( inSDLKey == SDLK_BACKSPACE )
+	{
+		if( nameLength > 0 )
+		{
+			highScoreName[ nameLength-1 ] = '\0';
+			PlayMono( kClick );
+		}
+	}
+	
+	// characters (ASCII key!)
+	if( bigFont->width[inKey] != 0 )
+	{
+		highScoreName[ nameLength++ ] = inKey;
+		highScoreName[ nameLength   ] = '\0';
+		PlayMono( kPlace );
+	}
+	
+	*item = kNothing;
+	return false;
+}
+
+
+static MBoolean ControlsSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
+{
+	MPoint          p;
+	MRect           dRect;
+	int             index;
+	static MBoolean lastDown = false;
+	MBoolean        down;
+	int             returnValue = 0;
+
+	static const ClickableZone buttonZones[] =
+	{
+		{ kControlsOK,		{ 340, 200, 360, 255 } },
+		{ kControlsReset,	{ 340, 365, 360, 450 } },
+	};
+	
+	(void) inKey; // unused
+	
+	*item = kNothing;
+
+
+	down = GetClickedZone( item, inSDLKey, arrsize(buttonZones), buttonZones );
+
+	if ( down && *item != kNothing )
+	{
+		if (!lastDown)
+		{
+			switch (*item)
+			{
+				case kBack:
+				case kControlsOK:
+					PlayMono(kClick);
+					returnValue = 1;
+					controlToReplace = -1;
+					break;
+
+				case kControlsReset:
+					PlayMono(kClick);
+					memcpy(playerKeys, defaultPlayerKeys, sizeof(playerKeys));
+					break;
+			}
+		}
+	}
+	else
+	{
+		SDLU_GetMouse(&p);
+
+		for( index=0; index<8; index++ )
+		{
+			dRect.top    = 229 + ((index & ~1) * 13);
+			dRect.left   = (index & 1)? 325: 130;
+			dRect.bottom = dRect.top + 24;
+			dRect.right  = dRect.left + 175;
+
+			if( MPointInMRect( p, &dRect ) )
+			{
+				*item = k1PLeft + index;
+				if( down && !lastDown && !AnyKeyIsPressed() ) 
+				{
+					controlToReplace = (controlToReplace == index)? -1: index;
+				}
+				break;
+			}
+		}
+	}
+	
+	if( inSDLKey != 0 && controlToReplace != -1 )
+	{
+		playerKeys[controlToReplace & 1][controlToReplace >> 1] = inSDLKey;
+		controlToReplace = -1;
+	}
+	
+	lastDown = down;
+	
+	return returnValue;
+}
+
+
+static MBoolean PauseSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
+{
+	(void) inSDLKey; // is unused
+	
+	static const ClickableZone zones[] = 
+	{	
+		{ kMusic,		{ 240, 180, 260, 320 } },		{ kResume,		{ 240, 340, 260, 480 } },
+		{ kSound,		{ 270, 180, 290, 320 } },		{ kEndGame,		{ 270, 340, 290, 480 } },
+		{ kVideo,		{ 300, 180, 320, 320 } },		{ kControls,	{ 300, 340, 320, 480 } },
+		{ kWarp,		{ 330, 180, 350, 320 } },
+	};
+
+	static MBoolean lastDown = false;
+	
+	int trigger = GetClickedZone( item, inSDLKey, arrsize(zones), zones );
+	
+	if( trigger )
+	{
+		if( !lastDown )
+		{
+			lastDown = true;
+			
+			switch( *item )
+			{
+				case kSound:
+                    PlayMono( kClick );
+                    soundOn = !soundOn;
+                    PlayMono( kClick );
+                    return false;
+
+				case kMusic:
+                    PlayMono( kClick );
+                    musicOn = !musicOn;
+                    EnableMusic( musicOn );
+                    return false;
+
+				case kFullscreen:
+                    fullscreen = !fullscreen;
+                    SetFullscreen( fullscreen );
+                    PlayMono( kClick );
+                    return false;
+
+				case kScalingMode:
+					crispUpscaling = !crispUpscaling;
+					SDLU_CreateRendererTexture();
+					PlayMono(kClick);
+					return false;
+
+                case kEndGame:
+				case kResume:
+				case kBack:
+                case kControls:
+                case kVideo:
+                    PlayMono( kClick );
+                    return true;
+
+				case kWarp:
+					if( ControlKeyIsPressed( ) )
+					{
+						PlayMono(kLevelUp);
+						level++; //Warp( );
+						return true;
+					}
+					else
+					{
+						*item = kNothing;
+					}
+					return false;
+			}
+		}
+	}
+	else
+	{
+		lastDown = false;
+	}
+	
+	return false;
+}
+
+
+
+static MBoolean VideoSettingsSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
+{
+	(void) inSDLKey; // is unused
+	
+	static const ClickableZone zones[] = 
+	{	
+		{ kFullscreen,		{ 240, 180, 260, 320 } },
+		{ kWidescreen,		{ 270, 180, 290, 320 } },
+		{ kScalingMode,		{ 300, 180, 320, 320 } },
+		{ kVideoOK,			{ 330, 180, 350, 320 } },
+	};
+
+	static MBoolean lastDown = false;
+	
+	int trigger = GetClickedZone( item, inSDLKey, arrsize(zones), zones );
+	
+	if( trigger )
+	{
+		if( !lastDown )
+		{
+			lastDown = true;
+			
+			switch( *item )
+			{
+				case kNothing:
+					break;
+
+				case kFullscreen:
+                    fullscreen = !fullscreen;
+                    SetFullscreen( fullscreen );
+                    PlayMono( kClick );
+                    return false;
+
+				case kScalingMode:
+					crispUpscaling = !crispUpscaling;
+					SDLU_CreateRendererTexture();
+					PlayMono(kClick);
+					return false;
+
+				case kWidescreen:
+					widescreen= !widescreen;
+					ResetWidescreenLayout();
+					SDLU_CreateRendererTexture();
+					SetFullscreen(fullscreen);
+					PlayMono(kClick);
+
+					NeedRefresh();
+					//RefreshAll();
+					//RefreshPlayerWindow(0);
+					//RefreshPlayerWindow(1);
+
+					return false;
+
+				default:
+                    PlayMono( kClick );
+                    return true;
+			}
+		}
+	}
+	else
+	{
+		lastDown = false;
+	}
+	
+	return false;
+}
+
+void HandleDialog( int type )
+{	
+	const float    lighten[4] = { 96.0f, 48.0f, 8.0f, 48.0f };
+	const MRect    boardWorldZRect = {0, 0, kBlobVertSize * (kGridDown-1), kBlobHorizSize * kGridAcross};
+	SDL_Rect       fullSDLRect = { 0, 0, 640, 480 };
+	SDL_Rect       joinSDLRect;
+	int            skip = 1;
+	int            count;
+	char           inASCII;
+	SDL_Keycode    inSDLKey;
+	MRect          pauseRect, joinRect;
+	
+	// Clear state 
+	whichField = nameField;
+	nameField[0] = '\0';
+	keyField[0] = '\0';
+	batsuAlpha = 0;
+	controlToReplace = -1;
+	
+	// Remember dialog info
+	dialogType = type;
+	dialogStage = kOpening;
+	colorWrap = 0;
+	colorInc = (RandomBefore(250) + 250.0) / 10000.0;
+
+	smallFont      = GetFont( picFont );
+	tinyFont       = GetFont( picTinyFont );
+	bigFont        = GetFont( picHiScoreFont );
+	dashedLineFont = GetFont( picDashedLineFont );
+	continueFont   = GetFont( picContinueFont );
+	batsuFont      = GetFont( picBatsuFont );
+		
+    // Pick some colors to animate.
+    for( count=0; count<4; count++ )
+    {
+        SDL_Color inColor;
+        
+        SDLU_GetPixel( boardSurface[0], RandomBefore( boardWorldZRect.right ), RandomBefore( boardWorldZRect.bottom ), &inColor );
+    
+        backColor[count].red   = MinInt( 255.0f, inColor.r + lighten[count] );
+        backColor[count].green = MinInt( 255.0f, inColor.g + lighten[count] );
+        backColor[count].blue  = MinInt( 255.0f, inColor.b + lighten[count] );
+    }
+	
+	// Get some graphics that we're going to need
+	logoSurface      = LoadPICTAsSurface( picLogo, 32 );
+	logoAlphaSurface = LoadPICTAsSurface( picLogoAlpha, 32 );
+	logoMaskSurface  = LoadPICTAsSurface( picLogoMask, MASK_DEPTH );
+
+	// Get a copy of the current game window contents
+	backSurface      = SDLU_InitSurface( &fullSDLRect, 32 );
+	
+	SDLU_BlitSurface( g_frontSurface, &g_frontSurface->clip_rect,
+	                  backSurface,  &backSurface->clip_rect );
+		
+	drawSurface      = SDLU_InitSurface( &fullSDLRect, 32 );
+
+	SDLU_BlitSurface( backSurface, &backSurface->clip_rect,
+	                  drawSurface, &drawSurface->clip_rect  );
+
+	//
+		
+	PlayMono( kWhomp );
+	dialogTimer = MTickCount();
+	dialogTarget = 0;
+	dialogShade = 0;
+	dialogStageComplete = false;
+	dialogItem = kNothing;
+	lastPauseRect.top = lastPauseRect.left = 9999;
+	lastPauseRect.bottom = lastPauseRect.right = -9999;
+
+	SDLU_StartWatchingTyping();
+
+	DoFullRepaint = ItsTimeToRedraw;
+
+	while( ((dialogStage != kClosing) || !dialogStageComplete) && !finished )
+	{
+		dialogTimer += skip;
+
+        UpdateSound();
+        
+		// Check mouse and keyboard
+        SDLU_CheckASCIITyping( &inASCII );
+        SDLU_CheckSDLTyping( &inSDLKey );
+		
+		if( (dialogStage == kOpening) && dialogStageComplete )
+		{
+			MBoolean (*DialogSelected[kNumDialogs])( int *item, unsigned char inKey, SDL_Keycode inSDLKey ) =
+			{
+				PauseSelected,
+				HiScoreSelected,
+				ContinueSelected,
+				ControlsSelected,
+				VideoSettingsSelected,
+			};
+			
+			if( DialogSelected[dialogType]( &dialogItem, inASCII, inSDLKey ) )
+			{
+				SDLU_SetSystemCursor( SYSTEM_CURSOR_OFF );
+
+				dialogStage = kClosing; 
+				dialogTarget = 0;
+			}
+		}
+
+		// Do animation ...
+		{
+			MBoolean dialogIsLarge = dialogType == kControlsDialog;
+
+			pauseRect = lastPauseRect;
+			dialogStageComplete = DrawDialogBox( dialogIsLarge, dialogStage, &dialogTarget, skip, &colorWrap, colorInc, &pauseRect );
+			SurfaceGetEdges( backSurface, &pauseRect );
+		}
+
+		if( (dialogStage == kOpening) && dialogStageComplete )
+		{
+			void (*DialogDraw[kNumDialogs])( int *item, int shade ) =
+			{
+				DrawPauseContents,
+				DrawHiScoreContents,
+				DrawContinueContents,
+				DrawControlsContents,
+				DrawVideoSettingsContents,
+			};
+
+			// Refresh screen if necessary
+			if( timeToRedraw )
+			{
+				SDLU_BlitFrontSurface( backSurface, &fullSDLRect, &fullSDLRect );
+
+				timeToRedraw = false;
+			}
+			
+			// ... and fade in the logo
+
+			dialogShade += skip;
+
+			{
+				bool dialogHasCandyCrisisLogo = true;
+				
+				if( dialogHasCandyCrisisLogo )
+					DrawDialogLogo( &pauseRect, dialogShade );
+			}
+			
+			// ... and animation is complete so add content			
+			DialogDraw[dialogType]( &dialogItem, dialogShade );
+
+#if USE_CURSOR_SPRITE
+			// ... and cursor
+			DrawDialogCursor( &pauseRect, &dialogShade );
+#endif
+			SDLU_SetSystemCursor( dialogItem == kNothing ? SYSTEM_CURSOR_ARROW : SYSTEM_CURSOR_HAND );
+		}
+
+		SurfaceCurveEdges( drawSurface, &pauseRect );
+
+		// Draw new animation on screen
+		UnionMRect( &lastPauseRect, &pauseRect, &joinRect );
+		SDLU_MRectToSDLRect( &joinRect, &joinSDLRect );
+		SDLU_BlitFrontSurface( drawSurface, &joinSDLRect, &joinSDLRect );
+        SDLU_Present();
+
+		lastPauseRect = pauseRect;
+
+		// Wait for next frame
+		if( dialogTimer <= MTickCount( ) )
+		{
+			dialogTimer = MTickCount( );
+			skip = 2;
+		}
+		else
+		{
+			skip = 1;
+			while( dialogTimer > MTickCount( ) ) 
+			{
+				SDLU_Yield();  
+			}
+		}
+	}
+	
+	DoFullRepaint = NoPaint;
+
+	SDLU_StopWatchingTyping();
+
+	// Bring back previous screen
+	SDLU_BlitFrontSurface( backSurface, &fullSDLRect, &fullSDLRect );
+    SDLU_Present();
+
+	// Dispose the GWorlds and fonts we used
+	SDL_FreeSurface( backSurface );
+	SDL_FreeSurface( drawSurface );
+	SDL_FreeSurface( logoSurface );
+	SDL_FreeSurface( logoAlphaSurface );
+	SDL_FreeSurface( logoMaskSurface );
+			
+	switch( dialogItem )
+	{
+		case kVideo:
+			HandleDialog( kVideoDialog );
+			HandleDialog( kPauseDialog );
+			break;
+
+		case kControls:
+			HandleDialog( kControlsDialog );
+			HandleDialog( kPauseDialog );
+			break;
+		
+		case kEndGame:
+			ChooseMusic( -1 );
+			AddHiscore( score[0] );
+			if( players == 1 )
+			{
+				ShowGameOverScreen( );
+			}
+			else
+			{
+				QuickFadeOut(NULL);
+			}
+			
+			showStartMenu = true;
+			break;
+		
+		case kContinue:
+			displayedScore[0] = score[0] = roundStartScore[0];
+			ShowScore( 0 );
+			BeginRound( true );
+			break;
+			
+		case kWarp:
+		{
+			int newLevel = level;
+			
+			InitGame( OptionKeyIsPressed()? kAIControl: kPlayerControl, kAIControl ); // this clears "level" ...
+			level = newLevel;                                                         // so we need to set "level" afterwards
+			BeginRound( true );	
+			break;
+		}
+	}
+}
--- a/src/pause.cpp
+++ /dev/null
@@ -1,1338 +1,0 @@
-// pause.cpp
-
-// All of this code is fugly. I really needed a dialog manager, but I didn't know it at the time,
-// and instead I cobbled this together. It is just barely good enough to work. Fortunately it looks
-// decent to the end user...
-
-
-#include "SDLU.h"
-
-#include <algorithm>
-#include <math.h>
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-
-#include "main.h"
-#include "gameticks.h"
-#include "blitter.h"
-#include "graphics.h"
-#include "gworld.h"
-#include "pause.h"
-#include "random.h"
-#include "font.h"
-#include "music.h"
-#include "soundfx.h"
-#include "keyselect.h"
-#include "level.h"
-#include "victory.h"
-#include "hiscore.h"
-#include "score.h"
-
-using std::min;
-using std::max;
-
-struct FRGBColor
-{
-	float red, green, blue;
-};
-
-struct ClickableZone
-{
-	int item;
-	MRect rect;
-};
-
-SDL_Surface* backSurface;
-SDL_Surface* drawSurface;
-SDL_Surface* logoSurface;
-SDL_Surface* logoMaskSurface;
-SDL_Surface* logoAlphaSurface;
-
-SkittlesFontPtr smallFont, bigFont, dashedLineFont, continueFont, tinyFont, batsuFont;
-FRGBColor backColor[4];
-MBoolean continueTimeOut;
-
-static int dialogType, dialogStage, dialogTarget, dialogShade, dialogItem;
-MTicks dialogTimer;
-static float colorWrap = 0, colorInc;
-static MRect logoRect = {0, 0, 111, 246}, lastPauseRect;
-static MBoolean dialogStageComplete;
-static MBoolean timeToRedraw = false;
-
-// for the controls dialog
-static int controlToReplace = -1;
-
-// for the enter code dialog
-static char  nameField[256], keyField[256];
-static char* whichField = nameField;
-static int   batsuAlpha = 0;
-
-static void ItsTimeToRedraw()
-{
-	timeToRedraw = true;
-}
-
-enum
-{
-	kTextRainbow,
-	kTextBrightRainbow,
-	kTextWhite,
-	kTextBlueGlow,
-	kTextGray,
-	kTextAlmostWhite
-};
-
-static MPoint DrawRainbowText( SkittlesFontPtr font, const char *line, MPoint dPoint, float wave, int bright )
-{
-	int   current;
-	int   r,g,b;
-	float s;
-	
-	current = 0;
-	
-	switch( bright )
-	{	
-			case kTextGray:
-				r = g = b = 96;
-				break;
-				
-			case kTextBlueGlow:
-				s = sin(wave);
-				r = (int)(88.0 + 120.0 * s * s);
-				g = r;
-				b = 255;
-				break;
-				
-			case kTextWhite:
-				r = g = b = 255;
-				break;
-				
-			case kTextAlmostWhite:
-            default:
-				r = g = b = 224;
-				break;
-				
-	}
-
-	while( line[current] )
-	{
-		switch( bright )
-		{
-			case kTextBrightRainbow:
-				r = (int)(208.0 + 40.0 * sin(wave                    ));
-				g = (int)(208.0 + 40.0 * sin(wave + ((2.*pi) * 1./3.)));
-				b = (int)(208.0 + 40.0 * sin(wave + ((2.*pi) * 2./3.)));
-				break;
-
-			case kTextRainbow:
-				r = (int)(128.0 + 96.0 * sin(wave                    ));
-				g = (int)(128.0 + 96.0 * sin(wave + ((2.*pi) * 1./3.)));
-				b = (int)(128.0 + 96.0 * sin(wave + ((2.*pi) * 2./3.)));
-				break;
-		}
-
-		SurfaceBlitCharacter( font, line[current], &dPoint, r, g, b, 1 );
-		
-		wave += 0.2;
-		current++;
-	}
-	
-	return dPoint;
-}
-
-
-#define kEdgeSize 8
-static COLOR_T edge[4][kEdgeSize][kEdgeSize];
-
-void SurfaceGetEdges( SDL_Surface* edgeSurface, const MRect *rect )
-{
-	unsigned char* src[4];
-	int            srcRowBytes;
-	
-	src[0] = src[1] = src[2] = src[3] = (unsigned char*) edgeSurface->pixels;
-	srcRowBytes = edgeSurface->pitch;
-
-	src[0] += (srcRowBytes * (rect->top               )) + ((rect->left             ) * BYTES_PER_PIXEL);
-	src[1] += (srcRowBytes * (rect->top               )) + ((rect->right - kEdgeSize) * BYTES_PER_PIXEL);
-	src[2] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->left             ) * BYTES_PER_PIXEL);
-	src[3] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->right - kEdgeSize) * BYTES_PER_PIXEL);
-	
-	for (int count=0; count<4; count++)
-	{
-		for (int height=0; height<kEdgeSize; height++)
-		{
-			memcpy( edge[count][height], src[count], kEdgeSize * BYTES_PER_PIXEL );
-			src[count] += srcRowBytes;
-		}
-	}
-}
-
-
-void SurfaceCurveEdges( SDL_Surface* edgeSurface, const MRect *rect )
-{
-	unsigned char* src[4];
-	int srcRowBytes, width, height, count;
-	char edgeMap[4][kEdgeSize][kEdgeSize+1]={  "      --",
-					                           "    -...",
-					                           "   -.xxX",
-					                           "  -.xXXX",
-					                           " -.xXXXX",
-					                           " .xXXXXX",
-					                           "-.xXXXXX",
-					                           "-.XXXXXX",
-					                           "--      ",
-					                           "...-    ",
-					                           "Xxx.-   ",
-					                           "XXXx.-  ",
-					                           "XXXXx.- ",
-					                           "XXXXXx. ",
-					                           "XXXXXx.-",
-					                           "XXXXXX.-",
-					                           "-.XXXXXX",
-					                           "-.xXXXXX",
-					                           " .xXXXXX",
-					                           " -.xXXXX",
-					                           "  -.xXXX",
-					                           "   -.xxX",
-					                           "    -...",
-					                           "      --",
-					                           "XXXXXX.-",
-					                           "XXXXXx.-",
-					                           "XXXXXx. ",
-					                           "XXXXx.- ",
-					                           "XXXx.-  ",
-					                           "Xxx.-   ",
-					                           "...-    ",
-					                           "--      "  };
-	                         	                         
-	
-	src[0] = src[1] = src[2] = src[3] = (unsigned char*) edgeSurface->pixels;
-	srcRowBytes = edgeSurface->pitch;
-
-	src[0] += (srcRowBytes * (rect->top               )) + ((rect->left             ) * BYTES_PER_PIXEL);
-	src[1] += (srcRowBytes * (rect->top               )) + ((rect->right - kEdgeSize) * BYTES_PER_PIXEL);
-	src[2] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->left             ) * BYTES_PER_PIXEL);
-	src[3] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->right - kEdgeSize) * BYTES_PER_PIXEL);
-	
-    const int HALFBRIGHT_MASK =
-           (CHANNEL_MASK >> 1)
-        | ((CHANNEL_MASK >> 1) << BITS_PER_1CHANNEL)
-        | ((CHANNEL_MASK >> 1) << BITS_PER_2CHANNELS);
-
-    // Draw top/bottom border
-	{
-		COLOR_T *srcT1 = (COLOR_T*) (src[0]) + kEdgeSize;
-		COLOR_T *srcB1 = (COLOR_T*) (src[2] + (srcRowBytes*(kEdgeSize-1))) + kEdgeSize;
-		COLOR_T *srcT2 = srcT1 + (srcRowBytes / BYTES_PER_PIXEL);
-		COLOR_T *srcB2 = srcB1 - (srcRowBytes / BYTES_PER_PIXEL);
-        
-		for( width = rect->right - rect->left - (kEdgeSize * 2); width > 0; width-- )
-		{
-			*srcT1 = 0; srcT1++;
-			*srcB1 = 0; srcB1++;
-			*srcT2 = (*srcT2 >> 1) & HALFBRIGHT_MASK; srcT2++;
-			*srcB2 = (*srcB2 >> 1) & HALFBRIGHT_MASK; srcB2++;
-		}
-	}
-	
-	// Draw left/right border
-	{
-		unsigned char *srcL1 = (src[0] + (srcRowBytes * kEdgeSize));
-		unsigned char *srcR1 = (src[1] + (srcRowBytes * kEdgeSize)) + BYTES_PER_PIXEL * (kEdgeSize-1);
-
-		unsigned char *srcL2 = srcL1 + BYTES_PER_PIXEL;
-		unsigned char *srcR2 = srcR1 - BYTES_PER_PIXEL;
-		
-		for( height = rect->bottom - rect->top - (kEdgeSize * 2); height > 0; height-- )
-		{
-			*(COLOR_T*)srcL1 = 0;
-			*(COLOR_T*)srcR1 = 0;
-			*(COLOR_T*)srcL2 = (*(COLOR_T*)srcL2 >> 1) & HALFBRIGHT_MASK;
-			*(COLOR_T*)srcR2 = (*(COLOR_T*)srcR2 >> 1) & HALFBRIGHT_MASK;
-			
-			srcL1 += srcRowBytes; 
-			srcR1 += srcRowBytes;
-			srcL2 += srcRowBytes; 
-			srcR2 += srcRowBytes;
-		}
-	}
-		
-	// Draw curved edges
-	for( count=0; count<4; count++ )
-	{
-		COLOR_T *srcS = (COLOR_T*) src[count];
-		
-		for( height=0; height<kEdgeSize; height++ )
-		{
-			for( width=0; width<kEdgeSize; width++ )
-			{
-				switch( edgeMap[count][height][width] )
-				{
-					case ' ': 	*srcS = edge[count][height][width]; break;
-                    case '-':	*srcS = (edge[count][height][width] >> 1) & HALFBRIGHT_MASK; break;
-					case '.': 	*srcS = 0; break;
-					case 'x': 	*srcS = (*srcS >> 1) & HALFBRIGHT_MASK; break;
-					case 'X': 	break;
-				}
-				srcS++;
-			}
-			srcS += (srcRowBytes / BYTES_PER_PIXEL) - kEdgeSize;
-		}
-	}
-}
-
-enum
-{
-	kOpening = 0, 
-	kClosing
-};
-
-static MBoolean DrawDialogBox( MBoolean larger, int animationType, int *target, int skip, float *colorWrap, float colorInc, MRect *pauseRect )
-{
-	MBoolean animationStageComplete = false;
-	MRect normalRect[2][19]     = { { { 240 - 10,  320 - 30,  240 + 10,  320 + 30  },
-	                                  { 240 - 40,  320 - 120, 240 + 40,  320 + 120 },
-	                                  { 240 - 60,  320 - 180, 240 + 60,  320 + 180 },
-	                                  { 240 - 70,  320 - 210, 240 + 70,  320 + 210 },
-	                                  { 240 - 80,  320 - 230, 240 + 80,  320 + 230 },
-	                                  { 240 - 88,  320 - 245, 240 + 88,  320 + 245 },
-	                                  { 240 - 95,  320 - 252, 240 + 95,  320 + 252 },
-	                                  { 240 - 101, 320 - 255, 240 + 101, 320 + 255 },
-	                                  { 240 - 106, 320 - 252, 240 + 106, 320 + 252 },
-	                                  { 240 - 110, 320 - 245, 240 + 110, 320 + 245 },
-	                                  { 240 - 113, 320 - 238, 240 + 113, 320 + 238 },
-	                                  { 240 - 115, 320 - 232, 240 + 115, 320 + 232 },
-	                                  { 240 - 116, 320 - 228, 240 + 116, 320 + 228 },
-	                                  { 240 - 118, 320 - 232, 240 + 118, 320 + 230 },
-	                                  { 240 - 118, 320 - 238, 240 + 118, 320 + 232 },
-	                                  { 240 - 119, 320 - 242, 240 + 119, 320 + 242 },
-	                                  { 240 - 119, 320 - 244, 240 + 119, 320 + 244 },
-	                                  { 240 - 119, 320 - 242, 240 + 119, 320 + 242 },
-	                                  { 240 - 120, 320 - 240, 240 + 120, 320 + 240 }  },
-	                                { { 240 - 110, 320 - 220, 240 + 110, 320 + 220 }, 
-	                                  { 240 - 105, 320 - 210, 240 + 105, 320 + 210 }, 
-	                                  { 240 - 100, 320 - 200, 240 + 100, 320 + 200 }, 
-	                                  { 240 - 95,  320 - 190, 240 + 95,  320 + 190 }, 
-	                                  { 240 - 90,  320 - 180, 240 + 90,  320 + 180 }, 
-	                                  { 240 - 85,  320 - 170, 240 + 85,  320 + 170 }, 
-	                                  { 240 - 80,  320 - 160, 240 + 80,  320 + 160 }, 
-	                                  { 240 - 75,  320 - 150, 240 + 75,  320 + 150 }, 
-	                                  { 240 - 70,  320 - 140, 240 + 70,  320 + 140 }, 
-	                                  { 240 - 65,  320 - 130, 240 + 65,  320 + 130 }, 
-	                                  { 240 - 60,  320 - 120, 240 + 60,  320 + 120 }, 
-	                                  { 240 - 55,  320 - 110, 240 + 55,  320 + 110 }, 
-	                                  { 240 - 50,  320 - 100, 240 + 50,  320 + 100 }, 
-	                                  { 240 - 45,  320 - 90,  240 + 45,  320 + 90  }, 
-	                                  { 240 - 40,  320 - 80,  240 + 40,  320 + 80  }, 
-	                                  { 240 - 35,  320 - 70,  240 + 35,  320 + 70  }, 
-	                                  { 240 - 30,  320 - 60,  240 + 30,  320 + 60  }, 
-	                                  { 240 - 25,  320 - 50,  240 + 25,  320 + 50  }, 	                                
-	                                  { 240 - 20,  320 - 40,  240 + 20,  320 + 40  }  }
-	                              };
-
-	MRect largerRect[2][19]     = { { { 240 - 11,  320 - 30,  240 + 11,  320 + 30  },
-	                                  { 240 - 44,  320 - 120, 240 + 44,  320 + 120 },
-	                                  { 240 - 66,  320 - 180, 240 + 66,  320 + 180 },
-	                                  { 240 - 77,  320 - 210, 240 + 77,  320 + 210 },
-	                                  { 240 - 88,  320 - 230, 240 + 88,  320 + 230 },
-	                                  { 240 - 97,  320 - 245, 240 + 97,  320 + 245 },
-	                                  { 240 - 104, 320 - 252, 240 + 104, 320 + 252 },
-	                                  { 240 - 111, 320 - 255, 240 + 111, 320 + 255 },
-	                                  { 240 - 117, 320 - 252, 240 + 117, 320 + 252 },
-	                                  { 240 - 121, 320 - 245, 240 + 121, 320 + 245 },
-	                                  { 240 - 124, 320 - 238, 240 + 124, 320 + 238 },
-	                                  { 240 - 126, 320 - 232, 240 + 126, 320 + 232 },
-	                                  { 240 - 128, 320 - 228, 240 + 128, 320 + 228 },
-	                                  { 240 - 130, 320 - 232, 240 + 130, 320 + 230 },
-	                                  { 240 - 130, 320 - 238, 240 + 130, 320 + 232 },
-	                                  { 240 - 131, 320 - 242, 240 + 131, 320 + 242 },
-	                                  { 240 - 131, 320 - 244, 240 + 131, 320 + 244 },
-	                                  { 240 - 131, 320 - 242, 240 + 131, 320 + 242 },
-	                                  { 240 - 132, 320 - 240, 240 + 132, 320 + 240 }  },
-	                                { { 240 - 121, 320 - 220, 240 + 121, 320 + 220 }, 
-	                                  { 240 - 115, 320 - 210, 240 + 115, 320 + 210 }, 
-	                                  { 240 - 110, 320 - 200, 240 + 110, 320 + 200 }, 
-	                                  { 240 - 104, 320 - 190, 240 + 104, 320 + 190 }, 
-	                                  { 240 - 99,  320 - 180, 240 + 99,  320 + 180 }, 
-	                                  { 240 - 93,  320 - 170, 240 + 93,  320 + 170 }, 
-	                                  { 240 - 88,  320 - 160, 240 + 88,  320 + 160 }, 
-	                                  { 240 - 82,  320 - 150, 240 + 82,  320 + 150 }, 
-	                                  { 240 - 77,  320 - 140, 240 + 77,  320 + 140 }, 
-	                                  { 240 - 71,  320 - 130, 240 + 71,  320 + 130 }, 
-	                                  { 240 - 66,  320 - 120, 240 + 66,  320 + 120 }, 
-	                                  { 240 - 60,  320 - 110, 240 + 60,  320 + 110 }, 
-	                                  { 240 - 55,  320 - 100, 240 + 55,  320 + 100 }, 
-	                                  { 240 - 49,  320 - 90,  240 + 49,  320 + 90  }, 
-	                                  { 240 - 44,  320 - 80,  240 + 44,  320 + 80  }, 
-	                                  { 240 - 38,  320 - 70,  240 + 38,  320 + 70  }, 
-	                                  { 240 - 33,  320 - 60,  240 + 33,  320 + 60  }, 
-	                                  { 240 - 27,  320 - 50,  240 + 27,  320 + 50  }, 	                                
-	                                  { 240 - 22,  320 - 40,  240 + 22,  320 + 40  }  }
-	                              };
-
-	int      colorInt, shading;
-	float    colorFrac, nColorFrac;
-	MRect    newRect;
-	SDL_Rect sdlRect;
-	
-	if( *target > 18 )
-	{
-		*target = 18;
-		animationStageComplete = true;
-	}
-
-	colorInt  = (int) floor( *colorWrap );
-	colorFrac = *colorWrap - colorInt;
-
-	newRect = larger? largerRect[animationType][*target]: normalRect[animationType][*target];
-	shading = ((animationType == 0) ? (*target * 24 / 18): (24 - (*target * 2 / 3)));
-	
-	{
-		float r1 = backColor[colorInt      ].red, g1 = backColor[colorInt      ].green, b1 = backColor[colorInt      ].blue,
-		      r2 = backColor[(colorInt+1)&3].red, g2 = backColor[(colorInt+1)&3].green, b2 = backColor[(colorInt+1)&3].blue,
-		      r3 = backColor[(colorInt+2)&3].red, g3 = backColor[(colorInt+2)&3].green, b3 = backColor[(colorInt+2)&3].blue,
-		      r4 = backColor[(colorInt+3)&3].red, g4 = backColor[(colorInt+3)&3].green, b4 = backColor[(colorInt+3)&3].blue;
-		
-		nColorFrac = 1 - colorFrac;
-		
-		SDLU_AcquireSurface( drawSurface );
-		
-		SurfaceBlitBlendOver(  backSurface,  drawSurface,
-		                      &newRect,     &newRect,
-  		                       (int)((r1 * nColorFrac) + (r2 * colorFrac)), 
-						       (int)((g1 * nColorFrac) + (g2 * colorFrac)), 
-						       (int)((b1 * nColorFrac) + (b2 * colorFrac)), 
-						       (int)((r2 * nColorFrac) + (r3 * colorFrac)), 
-						       (int)((g2 * nColorFrac) + (g3 * colorFrac)), 
-						       (int)((b2 * nColorFrac) + (b3 * colorFrac)), 
-						       (int)((r4 * nColorFrac) + (r1 * colorFrac)), 
-						       (int)((g4 * nColorFrac) + (g1 * colorFrac)), 
-						       (int)((b4 * nColorFrac) + (b1 * colorFrac)), 
-						       (int)((r3 * nColorFrac) + (r4 * colorFrac)), 
-						       (int)((g3 * nColorFrac) + (g4 * colorFrac)), 
-						       (int)((b3 * nColorFrac) + (b4 * colorFrac)), 
-						       _5TO8(shading) );
-
-		if( pauseRect->left < newRect.left ) 
-		{
-			MRect eraseRect = *pauseRect;
-			pauseRect->left = eraseRect.right = newRect.left;
-			
-			SDLU_MRectToSDLRect( &eraseRect, &sdlRect );
-			SDLU_BlitSurface( backSurface, &sdlRect,
-			                  drawSurface, &sdlRect  );
-		}
-
-		if( pauseRect->right > newRect.right ) 
-		{
-			MRect eraseRect = *pauseRect;
-			pauseRect->right = eraseRect.left = newRect.right;
-			
-			SDLU_MRectToSDLRect( &eraseRect, &sdlRect );
-			SDLU_BlitSurface( backSurface, &sdlRect,
-			                  drawSurface, &sdlRect  );
-		}
-
-		if( pauseRect->top < newRect.top ) 
-		{
-			MRect eraseRect = *pauseRect;
-			pauseRect->top = eraseRect.bottom = newRect.top;
-
-			SDLU_MRectToSDLRect( &eraseRect, &sdlRect );
-			SDLU_BlitSurface( backSurface, &sdlRect,
-			                  drawSurface, &sdlRect  );
-		}
-
-		if( pauseRect->bottom > newRect.bottom ) 
-		{
-			MRect eraseRect = *pauseRect;
-			pauseRect->bottom = eraseRect.top = newRect.bottom;
-
-			SDLU_MRectToSDLRect( &eraseRect, &sdlRect );
-			SDLU_BlitSurface( backSurface, &sdlRect,
-			                  drawSurface, &sdlRect  );
-		}
-
-		SDLU_ReleaseSurface( drawSurface );
-	}
-	
-	*pauseRect = newRect;
-	
-	*colorWrap += colorInc * skip;
-	if( *colorWrap >= 4 ) *colorWrap -= 4;
-
-	*target += skip;
-
-	return animationStageComplete;
-}
-
-#if USE_CURSOR_SPRITE
-static void DrawDialogCursor( MRect *pauseRect, int *shade )
-{
-	MPoint p, q;
-    (void) shade;
-    
-	SDLU_GetMouse( &p );
-	
-	if( p.h < (pauseRect->left      ) ) p.h = pauseRect->left;
-	if( p.h > (pauseRect->right  - 5) ) p.h = pauseRect->right  - 5;
-	if( p.v < (pauseRect->top       ) ) p.v = pauseRect->top;
-	if( p.v > (pauseRect->bottom - 5) ) p.v = pauseRect->bottom - 5;
-	q = p;
-	
-	SDLU_AcquireSurface( drawSurface );
-
-	SurfaceBlitCharacter( smallFont, '\x05', &p,  0,  0,  0, 0 );
-	SurfaceBlitCharacter( smallFont, '\x04', &q, 255, 255, 255, 0 );
-	
-	SDLU_ReleaseSurface( drawSurface );
-}
-#endif
-
-static void DrawDialogLogo( MRect *pauseRect, int shade )
-{
-	MRect drawRect;
-	int alpha;
-
-	drawRect.left   = (pauseRect->left + ((pauseRect->right - pauseRect->left) * 1 / 2) ) - (logoRect.right / 2);
-	drawRect.top    = (pauseRect->top + 14);
-	drawRect.bottom = drawRect.top + logoRect.bottom;
-	drawRect.right  = drawRect.left + logoRect.right;
-	
-	SDLU_AcquireSurface( drawSurface );
-	
-	alpha = (shade > 63)? 255: (shade * 4);
-		
-	SurfaceBlitWeightedDualAlpha( drawSurface,  logoSurface,  logoMaskSurface,  logoAlphaSurface,  drawSurface,
-                                  &drawRect,    &logoRect,    &logoRect,        &logoRect,         &drawRect,
-                                   alpha );
-
-	SDLU_ReleaseSurface( drawSurface );
-}
-
-
-enum
-{ 
-	kNothing = -1,
-	kBack = -2,
-
-// main pause screen (kEndGame is reused in continue and register)
-	kMusic = 0,		kResume,
-	kSound,         kEndGame,
-	kVideo,         kControls,
-	kWarp,
-
-// continue screen
-    kContinue,      
-    
-// controls screen
-    k1PLeft,        k2PLeft,
-    k1PRight,       k2PRight,
-    k1PDrop,        k2PDrop,
-    k1PRotate,      k2PRotate,
-    kControlsOK,    kControlsReset,
-
-// video settings screen
-    kFullscreen,
-	kWidescreen,
-    kScalingMode,
-	kVideoOK,
-};
-
-static void DrawContinueContents( int *item, int shade )
-{
-	char line[4][50] = { "Do you want to continue?",
-	                     "Yes",
-	                     "No",
-	                     "" };	                 
-	MPoint dPoint[4] = { {233, 210}, {280, 220}, {280, 400}, {335, 400} }, hPoint = {255, 320};
-	static int lastCountdown = 0;
-	int index, countdown, fade;
-	int r, g, b;
-	                 
-	sprintf( line[3], "%d credit%c", credits, (credits != 1)? 's': ' ' );
-
-	SDLU_AcquireSurface( drawSurface );
-
-	for( index=0; index<4; index++ )
-	{	
-		DrawRainbowText( smallFont, line[index], dPoint[index], (0.25 * index) + (0.075 * shade), 
-						 ( (index == 0)                          ||
-						  ((index == 1) && (*item == kContinue)) ||
-						  ((index == 2) && (*item == kEndGame ))    )? kTextBrightRainbow: kTextRainbow );
-	}
-	
-	countdown = shade / 100;
-	if( countdown < 10 )
-	{
-		continueTimeOut = false;
-		
-		if( (countdown != 0) && (countdown != lastCountdown) )
-		{
-			PlayMono( kContinueSnd );
-		}
-		lastCountdown = countdown;
-		
-		if( countdown < 5 )
-		{
-			r = (countdown * 31) / 5;
-			g = 31;
-		}
-		else
-		{
-			r = 31;
-			g = ((10 - countdown) * 31) / 5;
-		}
-			
-		fade = shade % 100;
-		if( fade > 50 ) fade = 50;
-		r = ((31 * (49 - fade)) + (r * fade)) / 49;
-		g = ((31 * (49 - fade)) + (g * fade)) / 49;
-		b = ((31 * (49 - fade))) / 49;
-		
-		countdown = '9' - countdown;
-		hPoint.h -= continueFont->width[countdown] / 2;
-
-		for( shade = 4; shade > 0; shade-- )
-		{
-			MPoint hP = hPoint;
-			
-			hP.h += 2 * shade;
-			hP.v += 2 * shade;
-
-            int weight = 20 - 4*shade;
-            
-			SurfaceBlitWeightedCharacter( continueFont, countdown, &hP, 0, 0, 0, _5TO8(weight) );
-		}
-
-		SurfaceBlitCharacter( continueFont, countdown, &hPoint, _5TO8(r), _5TO8(g), _5TO8(b), 0 );
-	}
-	else
-	{
-		continueTimeOut = true;
-	}
-	
-	SDLU_ReleaseSurface( drawSurface );
-}
-
-static void DrawHiScoreContents( int *item, int shade )
-{
-	MPoint       dPoint[3] = { {240, 640}, {260, 640}, {335, 400} }, hPoint = {294, 145};
-	MPoint       dashedLinePoint = { 320, 140 };
-	int          index;
-	int          nameLength;
-    const char*  line[3];
-    const char*  scan;
-	
-	(void) item; // is unused
-
-	line[0] = highScoreText;
-	line[1] = "Please enter your name and press return:";
-	line[2] = highScoreRank;
-
-	for( index=0; index<2; index++ )
-	{
-		scan = line[index];
-		while( *scan )
-			dPoint[index].h -= smallFont->width[(uint8_t) * scan++];
-		
-		dPoint[index].h /= 2;
-	}	
-		
-	SDLU_AcquireSurface( drawSurface );	
-
-	while( dashedLinePoint.h < 490 )
-	{
-		SurfaceBlitCharacter( dashedLineFont, '.', &dashedLinePoint, 0, 0, 0, 0 );
-	}
-	
-	nameLength = int(strlen(highScoreName));
-	for( index = 0; index < nameLength; index++ )
-	{
-		SurfaceBlitCharacter( bigFont, highScoreName[index], &hPoint, 255, 255, 255, 1 );
-		if( hPoint.h >= 475 )
-		{
-			highScoreName[index] = '\0';
-			break;
-		}
-	}
-
-	index = (int)(( 1.0 + sin( MTickCount() / 7.5 ) ) * 120.0);
-	SurfaceBlitCharacter( bigFont, '|', &hPoint, index, index, 255, 1 );
-
-	for( index=0; index<3; index++ )
-	{	
-		DrawRainbowText( smallFont, line[index], dPoint[index], (0.25 * index) + (0.075 * shade), (index != 2)? kTextBrightRainbow: kTextRainbow );
-	}
-	
-	SDLU_ReleaseSurface( drawSurface );	
-}
-
-static void DrawControlsContents( int *item, int shade )
-{
-	MBoolean    highlight;
-	MPoint      dPoint;
-	int         index;
-	const char* controlName;
-	int         r, g, b;
-	const char  label[8][20] = { "1P Left",   "2P Left", 
-	                             "1P Right",  "2P Right", 
-	                             "1P Drop",   "2P Drop",
-	                             "1P Rotate", "2P Rotate" };
-	                           
-	                         
-	SDLU_AcquireSurface( drawSurface );	
-
-	for( index=0; index<8; index++ )
-	{	
-		highlight = (index == (*item - k1PLeft));
-		
-		dPoint.v = 229 + ((index & ~1) * 13);
-		dPoint.h = (index & 1)? 325: 130;
-		DrawRainbowText( smallFont, label[index], dPoint, (0.25 * index) + (0.075 * shade), highlight? kTextBrightRainbow: kTextRainbow );
-				
-		dPoint.v = 245 + ((index & ~1) * 13);
-		dPoint.h = (index & 1)? 420: 225;		
-
-        r = highlight? 255: 0;
-		g = b = (int)(highlight? 255.0 - (88.0 * (sin(shade * 0.2) + 1.0)): 0.0);
-		
-        SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );
-		SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );
-		SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );
-		SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );
-		SurfaceBlitCharacter( dashedLineFont, '.', &dPoint, r, g, b, 0 );  // 80 pixels across
-		
-		controlName = SDL_GetKeyName( playerKeys[index & 1][index >> 1] );
-		if( controlName == NULL ) controlName = "???";
-		
-		dPoint.v = 231 + ((index & ~1) * 13);
-		dPoint.h = (index & 1)? 460: 265;		
-		dPoint.h -= GetTextWidth( tinyFont, controlName ) / 2;
-		DrawRainbowText( tinyFont, controlName, dPoint, (0.1 * shade), (controlToReplace == index)? kTextBlueGlow: kTextWhite );		
-	}
-
-	dPoint.h = 200;
-	dPoint.v = 340;
-	DrawRainbowText( smallFont, "\x03 OK", dPoint, 8.0 + (0.075 * shade), (*item == kControlsOK)? kTextBrightRainbow: kTextRainbow );
-
-	dPoint.h = 365;
-	dPoint.v = 340;
-	DrawRainbowText( smallFont, "\x03 Reset", dPoint, 8.25 + (0.075 * shade), (*item == kControlsReset)? kTextBrightRainbow: kTextRainbow );
-	
-	SDLU_ReleaseSurface( drawSurface );
-}
-
-static void DrawPauseContents( int *item, int shade )
-{
-	const char *line[] =
-	{
-		musicOn ? "\x01 Music" : "\x02 Music",
-		level == kTutorialLevel ? "\x03 Skip Tutorial" : "\x03 Resume",
-		soundOn ? "\x01 Sound" : "\x02 Sound",
-		"\x03 End Game",
-        "\x03 Video",
-		"\x03 Controls",
-	};
-
-	SDLU_AcquireSurface( drawSurface );	
-	
-	for( int i = 0; i < arrsize(line); i++ )
-	{	
-		MPoint dPoint;
-		dPoint.h = (i & 1)? 340: 180;
-		dPoint.v = 240 + ((i & ~1) * 15);
-		
-		DrawRainbowText( smallFont, line[i], dPoint, (0.25 * i) + (0.075 * shade), (*item == i)? kTextBrightRainbow: kTextRainbow );
-	}
-	
-	SDLU_ReleaseSurface( drawSurface );
-}
-
-static void DrawVideoSettingsContents(int* item, int shade)
-{
-	struct ZoneLabel
-	{
-		int item;
-		const char* text;
-	}
-	labels[] =
-	{
-		{ kFullscreen,		fullscreen ? "\x01 Fullscreen" : "\x02 Fullscreen" },
-		{ kWidescreen,		widescreen ? "\x01 Widescreen" : "\x02 Widescreen" },
-		{ kScalingMode,		crispUpscaling ? "\x01 Crisp upscaling" : "\x02 Crisp upscaling" },
-		{ kVideoOK,			"\x03 OK" },
-	};
-
-	SDLU_AcquireSurface(drawSurface);
-
-	for (int i = 0; i < arrsize(labels); i++)
-	{
-		MPoint dPoint;
-		dPoint.h = 180;
-		dPoint.v = 240 + (i * 30);
-
-		DrawRainbowText(smallFont, labels[i].text, dPoint, (0.25 * i) + (0.075 * shade), (*item == labels[i].item) ? kTextBrightRainbow : kTextRainbow);
-	}
-
-	SDLU_ReleaseSurface(drawSurface);
-}
-
-static MBoolean GetClickedZone( int* item, SDL_Keycode inSDLKey, int numZones, const ClickableZone* zones )
-{
-	if( inSDLKey == SDLK_ESCAPE )
-	{
-		*item = kBack;
-		return true;
-	}
-
-	MPoint p;
-	SDLU_GetMouse(&p);
-
-	int trigger = SDLU_Button();
-
-	*item = kNothing;
-	for( int i = 0; i < numZones; i++ )
-	{
-		if( MPointInMRect( p, &zones[i].rect ) )
-		{
-			*item = zones[i].item;
-		}
-	}
-
-	return trigger;
-}
-
-
-static MBoolean ContinueSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
-{
-	static const ClickableZone zones[] =
-	{
-		{ kContinue,	{280, 220, 300, 260} },
-		{ kEndGame,		{280, 400, 300, 440} },
-	};
-	
-	(void) inSDLKey; // is unused
-	
-	if( continueTimeOut )
-	{
-		*item = kEndGame;
-		return true;
-	}
-
-	int trigger = GetClickedZone( item, inSDLKey, arrsize(zones), zones );
-
-	if (trigger && *item == kBack)
-		*item = kContinue;
-	
-	return trigger && *item != kNothing;
-}
-
-static MBoolean HiScoreSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
-{
-	int nameLength = int(strlen(highScoreName));
-	
-	// return (SDL key)
-	if( inSDLKey == SDLK_RETURN )
-	{
-		if( nameLength > 0 )
-		{
-			*item = kResume;
-			PlayMono( kSquishy );
-            return true;
-		}
-		else
-		{
-			PlayMono( kClick );
-		}
-	}
-	
-	// backspace (SDL key)
-	if( inSDLKey == SDLK_BACKSPACE )
-	{
-		if( nameLength > 0 )
-		{
-			highScoreName[ nameLength-1 ] = '\0';
-			PlayMono( kClick );
-		}
-	}
-	
-	// characters (ASCII key!)
-	if( bigFont->width[inKey] != 0 )
-	{
-		highScoreName[ nameLength++ ] = inKey;
-		highScoreName[ nameLength   ] = '\0';
-		PlayMono( kPlace );
-	}
-	
-	*item = kNothing;
-	return false;
-}
-
-
-static MBoolean ControlsSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
-{
-	MPoint          p;
-	MRect           dRect;
-	int             index;
-	static MBoolean lastDown = false;
-	MBoolean        down;
-	int             returnValue = 0;
-
-	static const ClickableZone buttonZones[] =
-	{
-		{ kControlsOK,		{ 340, 200, 360, 255 } },
-		{ kControlsReset,	{ 340, 365, 360, 450 } },
-	};
-	
-	(void) inKey; // unused
-	
-	*item = kNothing;
-
-
-	down = GetClickedZone( item, inSDLKey, arrsize(buttonZones), buttonZones );
-
-	if ( down && *item != kNothing )
-	{
-		if (!lastDown)
-		{
-			switch (*item)
-			{
-				case kBack:
-				case kControlsOK:
-					PlayMono(kClick);
-					returnValue = 1;
-					controlToReplace = -1;
-					break;
-
-				case kControlsReset:
-					PlayMono(kClick);
-					memcpy(playerKeys, defaultPlayerKeys, sizeof(playerKeys));
-					break;
-			}
-		}
-	}
-	else
-	{
-		SDLU_GetMouse(&p);
-
-		for( index=0; index<8; index++ )
-		{
-			dRect.top    = 229 + ((index & ~1) * 13);
-			dRect.left   = (index & 1)? 325: 130;
-			dRect.bottom = dRect.top + 24;
-			dRect.right  = dRect.left + 175;
-
-			if( MPointInMRect( p, &dRect ) )
-			{
-				*item = k1PLeft + index;
-				if( down && !lastDown && !AnyKeyIsPressed() ) 
-				{
-					controlToReplace = (controlToReplace == index)? -1: index;
-				}
-				break;
-			}
-		}
-	}
-	
-	if( inSDLKey != 0 && controlToReplace != -1 )
-	{
-		playerKeys[controlToReplace & 1][controlToReplace >> 1] = inSDLKey;
-		controlToReplace = -1;
-	}
-	
-	lastDown = down;
-	
-	return returnValue;
-}
-
-
-static MBoolean PauseSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
-{
-	(void) inSDLKey; // is unused
-	
-	static const ClickableZone zones[] = 
-	{	
-		{ kMusic,		{ 240, 180, 260, 320 } },		{ kResume,		{ 240, 340, 260, 480 } },
-		{ kSound,		{ 270, 180, 290, 320 } },		{ kEndGame,		{ 270, 340, 290, 480 } },
-		{ kVideo,		{ 300, 180, 320, 320 } },		{ kControls,	{ 300, 340, 320, 480 } },
-		{ kWarp,		{ 330, 180, 350, 320 } },
-	};
-
-	static MBoolean lastDown = false;
-	
-	int trigger = GetClickedZone( item, inSDLKey, arrsize(zones), zones );
-	
-	if( trigger )
-	{
-		if( !lastDown )
-		{
-			lastDown = true;
-			
-			switch( *item )
-			{
-				case kSound:
-                    PlayMono( kClick );
-                    soundOn = !soundOn;
-                    PlayMono( kClick );
-                    return false;
-
-				case kMusic:
-                    PlayMono( kClick );
-                    musicOn = !musicOn;
-                    EnableMusic( musicOn );
-                    return false;
-
-				case kFullscreen:
-                    fullscreen = !fullscreen;
-                    SetFullscreen( fullscreen );
-                    PlayMono( kClick );
-                    return false;
-
-				case kScalingMode:
-					crispUpscaling = !crispUpscaling;
-					SDLU_CreateRendererTexture();
-					PlayMono(kClick);
-					return false;
-
-                case kEndGame:
-				case kResume:
-				case kBack:
-                case kControls:
-                case kVideo:
-                    PlayMono( kClick );
-                    return true;
-
-				case kWarp:
-					if( ControlKeyIsPressed( ) )
-					{
-						PlayMono(kLevelUp);
-						level++; //Warp( );
-						return true;
-					}
-					else
-					{
-						*item = kNothing;
-					}
-					return false;
-			}
-		}
-	}
-	else
-	{
-		lastDown = false;
-	}
-	
-	return false;
-}
-
-
-
-static MBoolean VideoSettingsSelected( int *item, unsigned char inKey, SDL_Keycode inSDLKey )
-{
-	(void) inSDLKey; // is unused
-	
-	static const ClickableZone zones[] = 
-	{	
-		{ kFullscreen,		{ 240, 180, 260, 320 } },
-		{ kWidescreen,		{ 270, 180, 290, 320 } },
-		{ kScalingMode,		{ 300, 180, 320, 320 } },
-		{ kVideoOK,			{ 330, 180, 350, 320 } },
-	};
-
-	static MBoolean lastDown = false;
-	
-	int trigger = GetClickedZone( item, inSDLKey, arrsize(zones), zones );
-	
-	if( trigger )
-	{
-		if( !lastDown )
-		{
-			lastDown = true;
-			
-			switch( *item )
-			{
-				case kNothing:
-					break;
-
-				case kFullscreen:
-                    fullscreen = !fullscreen;
-                    SetFullscreen( fullscreen );
-                    PlayMono( kClick );
-                    return false;
-
-				case kScalingMode:
-					crispUpscaling = !crispUpscaling;
-					SDLU_CreateRendererTexture();
-					PlayMono(kClick);
-					return false;
-
-				case kWidescreen:
-					widescreen= !widescreen;
-					ResetWidescreenLayout();
-					SDLU_CreateRendererTexture();
-					SetFullscreen(fullscreen);
-					PlayMono(kClick);
-
-					NeedRefresh();
-					//RefreshAll();
-					//RefreshPlayerWindow(0);
-					//RefreshPlayerWindow(1);
-
-					return false;
-
-				default:
-                    PlayMono( kClick );
-                    return true;
-			}
-		}
-	}
-	else
-	{
-		lastDown = false;
-	}
-	
-	return false;
-}
-
-void HandleDialog( int type )
-{	
-	const float    lighten[4] = { 96.0f, 48.0f, 8.0f, 48.0f };
-	const MRect    boardWorldZRect = {0, 0, kBlobVertSize * (kGridDown-1), kBlobHorizSize * kGridAcross};
-	SDL_Rect       fullSDLRect = { 0, 0, 640, 480 };
-	SDL_Rect       joinSDLRect;
-	int            skip = 1;
-	int            count;
-	char           inASCII;
-	SDL_Keycode    inSDLKey;
-	MRect          pauseRect, joinRect;
-	
-	// Clear state 
-	whichField = nameField;
-	nameField[0] = '\0';
-	keyField[0] = '\0';
-	batsuAlpha = 0;
-	controlToReplace = -1;
-	
-	// Remember dialog info
-	dialogType = type;
-	dialogStage = kOpening;
-	colorWrap = 0;
-	colorInc = (RandomBefore(250) + 250.0) / 10000.0;
-
-	smallFont      = GetFont( picFont );
-	tinyFont       = GetFont( picTinyFont );
-	bigFont        = GetFont( picHiScoreFont );
-	dashedLineFont = GetFont( picDashedLineFont );
-	continueFont   = GetFont( picContinueFont );
-	batsuFont      = GetFont( picBatsuFont );
-		
-    // Pick some colors to animate.
-    for( count=0; count<4; count++ )
-    {
-        SDL_Color inColor;
-        
-        SDLU_GetPixel( boardSurface[0], RandomBefore( boardWorldZRect.right ), RandomBefore( boardWorldZRect.bottom ), &inColor );
-    
-        backColor[count].red   = min( 255.0f, inColor.r + lighten[count] );
-        backColor[count].green = min( 255.0f, inColor.g + lighten[count] );
-        backColor[count].blue  = min( 255.0f, inColor.b + lighten[count] );
-    }
-	
-	// Get some graphics that we're going to need
-	logoSurface      = LoadPICTAsSurface( picLogo, 32 );
-	logoAlphaSurface = LoadPICTAsSurface( picLogoAlpha, 32 );
-	logoMaskSurface  = LoadPICTAsSurface( picLogoMask, MASK_DEPTH );
-
-	// Get a copy of the current game window contents
-	backSurface      = SDLU_InitSurface( &fullSDLRect, 32 );
-	
-	SDLU_BlitSurface( g_frontSurface, &g_frontSurface->clip_rect,
-	                  backSurface,  &backSurface->clip_rect );
-		
-	drawSurface      = SDLU_InitSurface( &fullSDLRect, 32 );
-
-	SDLU_BlitSurface( backSurface, &backSurface->clip_rect,
-	                  drawSurface, &drawSurface->clip_rect  );
-
-	//
-		
-	PlayMono( kWhomp );
-	dialogTimer = MTickCount();
-	dialogTarget = 0;
-	dialogShade = 0;
-	dialogStageComplete = false;
-	dialogItem = kNothing;
-	lastPauseRect.top = lastPauseRect.left = 9999;
-	lastPauseRect.bottom = lastPauseRect.right = -9999;
-
-	SDLU_StartWatchingTyping();
-
-	DoFullRepaint = ItsTimeToRedraw;
-
-	while( ((dialogStage != kClosing) || !dialogStageComplete) && !finished )
-	{
-		dialogTimer += skip;
-
-        UpdateSound();
-        
-		// Check mouse and keyboard
-        SDLU_CheckASCIITyping( &inASCII );
-        SDLU_CheckSDLTyping( &inSDLKey );
-		
-		if( (dialogStage == kOpening) && dialogStageComplete )
-		{
-			MBoolean (*DialogSelected[kNumDialogs])( int *item, unsigned char inKey, SDL_Keycode inSDLKey ) =
-			{
-				PauseSelected,
-				HiScoreSelected,
-				ContinueSelected,
-				ControlsSelected,
-				VideoSettingsSelected,
-			};
-			
-			if( DialogSelected[dialogType]( &dialogItem, inASCII, inSDLKey ) )
-			{
-				SDLU_SetSystemCursor( SYSTEM_CURSOR_OFF );
-
-				dialogStage = kClosing; 
-				dialogTarget = 0;
-			}
-		}
-
-		// Do animation ...
-		{
-			MBoolean dialogIsLarge = dialogType == kControlsDialog;
-
-			pauseRect = lastPauseRect;
-			dialogStageComplete = DrawDialogBox( dialogIsLarge, dialogStage, &dialogTarget, skip, &colorWrap, colorInc, &pauseRect );
-			SurfaceGetEdges( backSurface, &pauseRect );
-		}
-
-		if( (dialogStage == kOpening) && dialogStageComplete )
-		{
-			void (*DialogDraw[kNumDialogs])( int *item, int shade ) =
-			{
-				DrawPauseContents,
-				DrawHiScoreContents,
-				DrawContinueContents,
-				DrawControlsContents,
-				DrawVideoSettingsContents,
-			};
-
-			// Refresh screen if necessary
-			if( timeToRedraw )
-			{
-				SDLU_BlitFrontSurface( backSurface, &fullSDLRect, &fullSDLRect );
-
-				timeToRedraw = false;
-			}
-			
-			// ... and fade in the logo
-
-			dialogShade += skip;
-
-			{
-				bool dialogHasCandyCrisisLogo = true;
-				
-				if( dialogHasCandyCrisisLogo )
-					DrawDialogLogo( &pauseRect, dialogShade );
-			}
-			
-			// ... and animation is complete so add content			
-			DialogDraw[dialogType]( &dialogItem, dialogShade );
-
-#if USE_CURSOR_SPRITE
-			// ... and cursor
-			DrawDialogCursor( &pauseRect, &dialogShade );
-#endif
-			SDLU_SetSystemCursor( dialogItem == kNothing ? SYSTEM_CURSOR_ARROW : SYSTEM_CURSOR_HAND );
-		}
-
-		SurfaceCurveEdges( drawSurface, &pauseRect );
-
-		// Draw new animation on screen
-		UnionMRect( &lastPauseRect, &pauseRect, &joinRect );
-		SDLU_MRectToSDLRect( &joinRect, &joinSDLRect );
-		SDLU_BlitFrontSurface( drawSurface, &joinSDLRect, &joinSDLRect );
-        SDLU_Present();
-
-		lastPauseRect = pauseRect;
-
-		// Wait for next frame
-		if( dialogTimer <= MTickCount( ) )
-		{
-			dialogTimer = MTickCount( );
-			skip = 2;
-		}
-		else
-		{
-			skip = 1;
-			while( dialogTimer > MTickCount( ) ) 
-			{
-				SDLU_Yield();  
-			}
-		}
-	}
-	
-	DoFullRepaint = NoPaint;
-
-	SDLU_StopWatchingTyping();
-
-	// Bring back previous screen
-	SDLU_BlitFrontSurface( backSurface, &fullSDLRect, &fullSDLRect );
-    SDLU_Present();
-
-	// Dispose the GWorlds and fonts we used
-	SDL_FreeSurface( backSurface );
-	SDL_FreeSurface( drawSurface );
-	SDL_FreeSurface( logoSurface );
-	SDL_FreeSurface( logoAlphaSurface );
-	SDL_FreeSurface( logoMaskSurface );
-			
-	switch( dialogItem )
-	{
-		case kVideo:
-			HandleDialog( kVideoDialog );
-			HandleDialog( kPauseDialog );
-			break;
-
-		case kControls:
-			HandleDialog( kControlsDialog );
-			HandleDialog( kPauseDialog );
-			break;
-		
-		case kEndGame:
-			ChooseMusic( -1 );
-			AddHiscore( score[0] );
-			if( players == 1 )
-			{
-				ShowGameOverScreen( );
-			}
-			else
-			{
-				QuickFadeOut(NULL);
-			}
-			
-			showStartMenu = true;
-			break;
-		
-		case kContinue:
-			displayedScore[0] = score[0] = roundStartScore[0];
-			ShowScore( 0 );
-			BeginRound( true );
-			break;
-			
-		case kWarp:
-		{
-			int newLevel = level;
-			
-			InitGame( OptionKeyIsPressed()? kAIControl: kPlayerControl, kAIControl ); // this clears "level" ...
-			level = newLevel;                                                         // so we need to set "level" afterwards
-			BeginRound( true );	
-			break;
-		}
-	}
-}
--- /dev/null
+++ b/src/players.c
@@ -1,0 +1,1078 @@
+// players.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "players.h"
+#include "gworld.h"
+#include "moving.h"
+#include "graphics.h"
+#include "control.h"
+#include "grays.h"
+#include "soundfx.h"
+#include "next.h"
+#include "random.h"
+#include "victory.h"
+#include "tweak.h"
+#include "zap.h"
+#include "level.h"
+#include "opponent.h"
+#include "gameticks.h"
+#include "blitter.h"
+#include "music.h"
+#include "score.h"
+#include "hiscore.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+unsigned int  boredTime[2], hintTime[2], fadeCharTime[2], animTime[2], shadowDepth[2], hintGlow, messageTime;
+int emotions[2];
+int glowColors[][3] = { { 0,  0,  0},
+						{_5TO8(13), _5TO8(26), _5TO8(31)},
+						{_5TO8(13), _5TO8(29), _5TO8(13)},
+						{_5TO8(31), _5TO8(18), _5TO8(31)},
+						{_5TO8(31), _5TO8(14), _5TO8(18)},
+						{_5TO8(31), _5TO8(31), _5TO8(15)},
+						{_5TO8(31), _5TO8(21), _5TO8(13)},
+						{_5TO8(30), _5TO8(22), _5TO8(30)},
+						{_5TO8(20), _5TO8(20), _5TO8(20)} };
+
+void HandlePlayers( void )
+{
+	int player;
+	
+	for( player = 0; player<=1; player++ )
+	{
+		UpdateScore( player );
+		UpdateNext( player );
+		BlinkGrays( player );
+		GlowBlobs( player );
+		BlinkBored( player );
+		FadeCharred( player );
+
+		switch( role[player] )
+		{
+			case kWaitForRetrieval:
+				if( control[player] == kAutoControl )
+				{
+					AutoControl( player );
+					break;
+				}
+				
+				role[player] = kRetrieveBlobs;
+				// fallthrough
+				
+			case kRetrieveBlobs:
+				LockGrays( 1-player );
+				RetrieveBlobs( player );
+				break;
+				
+			case kFalling:
+				Falling( player );
+				break;
+			
+			case kLockdownBlobs:
+				LockdownBlobs( player );
+				break;
+				
+			case kJiggleBlobs:
+				JiggleBlobs( player );
+				break;
+				
+			case kFastJiggleBlobs:
+				FastJiggleBlobs( player );
+				break;
+				
+			case kPlaceBlobs:
+				PlaceBlobs( player );
+				break;
+			
+			case kDropBlobs:
+				DropBlobs( player );
+				break;
+			
+			case kZapBlobs:
+				ZapBlobs( player );
+				break;
+			
+			case kKillBlobs:
+				KillBlobs( player );
+				break;
+			
+			case kDropGrays:
+				DropGrays( player );
+				break;
+			
+			case kLosing:
+				Lose( player );
+				break;
+			
+			case kWinning:
+				Win( player );
+				break;
+				
+			case kChooseDifficulty:
+				ChooseDifficulty( player );
+				break;
+			
+			case kWaitingToStart:
+				if( role[1-player] == kWaitingToStart )
+					role[0] = role[1] = kZapBlobs;
+				break;
+			
+			case kIdlePlayer:
+				break;
+		}
+		
+		UpdatePlayerWindow( player );
+	}
+}
+
+void LockdownBlobs( int player )
+{
+	const int shadowList[] = { 
+								 kBlobShadowDepth, 
+								 kBlobShadowDepth*5/6, 
+								 kBlobShadowDepth*5/6, 
+								 kBlobShadowDepth*4/6, 
+								 kBlobShadowDepth*3/6, 
+								 kBlobShadowDepth*1/6, 
+								};
+
+	int frame = (GameTickCount( ) - blobTime[player]) / 2;
+
+	if( frame >= arrsize(shadowList) )
+	{
+		role[player] = kPlaceBlobs;
+		return;
+	}
+	
+	shadowDepth[player] = shadowList[frame];
+	UpdateTweak( player, blobJiggleAnimation );
+}
+
+void FastJiggleBlobs( int player )
+{
+	const int shadowList[] = { 
+								 kBlobShadowDepth, 
+								 kBlobShadowDepth*5/6, 
+								 kBlobShadowDepth*5/6, 
+								 kBlobShadowDepth*4/6, 
+								 kBlobShadowDepth*3/6, 
+								 kBlobShadowDepth*2/6, 
+								 kBlobShadowDepth*1/6,
+								 0 
+								};
+
+	int frame = (GameTickCount( ) - blobTime[player]) / 2;
+
+	if( frame >= 8 )
+	{
+		role[player] = kPlaceBlobs;
+		return;
+	}
+
+	switch( control[player] )
+	{
+		case kPlayerControl:
+			PlayerControl( player );
+			break;
+		
+		case kAIControl:
+			AIControl( player );
+			break;
+		
+		case kAutoControl:
+			AutoControl( player );
+			break;
+	}
+	
+	shadowDepth[player] = shadowList[frame];
+	UpdateTweak( player, blobJiggleAnimation );
+
+	if( CanFall( player ) )
+		role[player] = kFalling;
+}
+
+
+void JiggleBlobs( int player )
+{
+	const int shadowList[] = { 
+								 kBlobShadowDepth, kBlobShadowDepth,
+								 kBlobShadowDepth*5/6, kBlobShadowDepth*5/6,
+								 kBlobShadowDepth*5/6, kBlobShadowDepth*4/6,
+								 kBlobShadowDepth*4/6, kBlobShadowDepth*3/6,
+								 kBlobShadowDepth*3/6, kBlobShadowDepth*2/6,
+								 kBlobShadowDepth*2/6, kBlobShadowDepth*1/6,
+								 kBlobShadowDepth*1/6, 0
+								};
+
+	int frame = (GameTickCount( ) - blobTime[player]) / 2;
+	
+	if( frame >= 14 )
+	{
+		role[player] = kPlaceBlobs;
+		return;
+	}
+
+	switch( control[player] )
+	{
+		case kPlayerControl:
+			PlayerControl( player );
+			break;
+		
+		case kAIControl:
+			AIControl( player );
+			break;
+			
+		case kAutoControl:
+			AutoControl( player );
+			break;
+	}
+
+	shadowDepth[player] = shadowList[frame];
+	UpdateTweak( player, kJiggleAnimation );
+	
+	if( CanFall( player ) )
+		role[player] = kFalling;
+}
+
+void PlaceGrenade( int player )
+{
+	MRect myRect;
+	int x, y, atX, atY, color, delay = -6;
+	int currentX = blobX[player], currentY = blobY[player];
+	int charTypes[3][5] = { { kDarkChar | kChar11, kDarkChar | kChar12, kDarkChar | kChar13, kDarkChar | kChar14, kNoCharring         },
+	                        { kNoCharring,         kNoCharring,         kNoCharring,         kNoCharring,         kDarkChar | kChar24 },
+	                        { kDarkChar | kChar31, kDarkChar | kChar32, kDarkChar | kChar33, kDarkChar | kChar34, kNoCharring         } };
+	
+	int amount = ((level <= kLevels)? level: 1) * 100;
+	int multiplier = 0;
+	
+	grenadeFrame[player] = 0;
+	grenadeRect [player].top    = (currentY * kBlobVertSize ) - (kBlastHeight/2);
+	grenadeRect [player].left   = (currentX * kBlobHorizSize) + (kBlobHorizSize/2) - (kBlastWidth/2);
+	grenadeRect [player].bottom = grenadeRect[player].top  + kBlastHeight;
+	grenadeRect [player].right  = grenadeRect[player].left + kBlastWidth ;
+
+	SDLU_AcquireSurface( playerSurface[player] );
+	
+	for( x=-1; x<=1; x++ )
+	{
+		atX = currentX + x;
+		
+		for( y=-2; y<=2; y++ )
+		{
+			atY = currentY + y;
+
+			if( atX >= 0 && atX < kGridAcross &&
+			    atY >= 0 && atY < kGridDown )
+			{
+				if( charTypes[x+1][y+2] != kNoCharring   &&
+				    grid[player][atX][atY] >= kFirstBlob &&
+					grid[player][atX][atY] <= kLastBlob     )
+				{
+					charred[player][atX][atY] =  charTypes[x+1][y+2];
+
+					CalcBlobRect( atX, atY, &myRect );
+					SurfaceDrawBlob( player, &myRect, grid[player][atX][atY], suction[player][atX][atY], charred[player][atX][atY] );
+					CleanSpriteArea( player, &myRect );
+				}
+			}
+		}
+	}
+
+	SDLU_ReleaseSurface( playerSurface[player] );
+	
+	if( currentY < (kGridDown-1) && 
+		( grid[player][currentX][currentY+1] >= kFirstBlob && 
+		  grid[player][currentX][currentY+1] <= kLastBlob    ) )
+	{
+		color = grid[player][currentX][currentY+1];
+		
+		for( x=0; x<kGridAcross; x++ )
+		{
+			for( y=0; y<kGridDown; y++ )
+			{
+				if( grid[player][x][y] == color )
+				{
+					suction[player][x][y] = kInDeath;
+					death[player][x][y] = -abs( x - currentX + y - currentY );
+					multiplier++;
+					
+					if( (x <= (kGridAcross-2)) && (grid[player][x+1][y] == kGray) )
+					{
+						suction[player][x+1][y] = kGrayBlink1;
+						death[player][x+1][y] = delay;
+					}
+
+					if( (x >= 1) && (grid[player][x-1][y] == kGray) )
+					{
+						suction[player][x-1][y] = kGrayBlink1;
+						death[player][x-1][y] = delay;
+					}
+
+					if( (y <= (kGridDown-2)) && (grid[player][x][y+1] == kGray) )
+					{
+						suction[player][x][y+1] = kGrayBlink1;
+						death[player][x][y+1] = delay;
+					}
+
+					if( (y >= 1) && (grid[player][x][y-1] == kGray) )
+					{
+						suction[player][x][y-1] = kGrayBlink1;
+						death[player][x][y-1] = delay;
+					}
+				}
+			}
+		}
+	}
+	else
+	{
+		for( x=currentX-1; x<=currentX+1; x++ )
+		{
+			for( y=currentY-1; y<=currentY+1; y++ )
+			{
+				if( x>=0 && x<kGridAcross && y>=0 && y<kGridDown )
+				{
+					if( grid[player][x][y] == kGray )
+					{
+						suction[player][x][y] = kGrayBlink1;
+						death[player][x][y] = delay;
+					}
+					else if( grid[player][x][y] >= kFirstBlob &&
+					         grid[player][x][y] <= kLastBlob     )
+					{
+						suction[player][x][y] = kInDeath;
+						death[player][x][y] = -abs( x - currentX + y - currentY );
+						multiplier++;
+					}
+				}
+			}
+		}
+	}
+	
+	PlayStereo( player, kSplop );
+
+	if( multiplier > 0 )
+	{
+		score[player] += amount * multiplier;	
+		ZapScoreDisplay( player, amount, multiplier, blobX[player], blobY[player], 8 );
+	}
+	
+	blobTime[player] = GameTickCount( );
+	role[player] = kKillBlobs;
+}
+
+void PlaceBlobs( int player )
+{
+	MRect myRect;
+	int x, y, height;
+	int currentX = blobX[player], currentY = blobY[player];
+	
+	potentialCombo[player].x = currentX;
+	potentialCombo[player].r = blobR[player];
+
+	SDLU_AcquireSurface( playerSurface[player] );
+
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=0; y<kGridDown; y++ )
+		{
+			if( glow[player][x][y] )
+			{
+				glow[player][x][y] = false;
+				CalcBlobRect( x, y, &myRect );
+				SurfaceDrawBlob( player, &myRect, grid[player][x][y], suction[player][x][y], charred[player][x][y] );
+				CleanSpriteArea( player, &myRect );
+			}
+		}
+	}
+
+	SDLU_ReleaseSurface( playerSurface[player] );
+
+	EraseSpriteBlobs( player );
+	
+	CalcBlobRect( currentX, currentY, &myRect );
+	CalcSecondBlobOffset( player, &x, &y );
+	
+	if( grenade[player] )
+	{
+		PlaceGrenade( player );	
+		return;
+	}
+	
+	if( magic[player] )
+	{
+		switch( blobR[player] )
+		{
+			case upRotate:
+				height = GetRowHeight(player, currentX)-1;
+				grid[player][currentX][height] = colorB[player];
+				colorA[player] = BestColor( player, currentX, height+1 );
+				grid[player][currentX][height] = kEmpty;
+				break;
+			
+			case downRotate:
+				height = GetRowHeight(player, currentX);
+				grid[player][currentX][height] = colorB[player];
+				colorA[player] = BestColor( player, currentX, height-1 );
+				grid[player][currentX][height] = kEmpty;
+				break;
+			
+			case rightRotate:
+				height = GetRowHeight(player, currentX+1);
+				grid[player][currentX+1][height] = colorB[player];
+				colorA[player] = BestColor( player, currentX, GetRowHeight(player, currentX) );
+				grid[player][currentX+1][height] = kEmpty;
+				break;
+
+			case leftRotate:
+				height = GetRowHeight(player, currentX-1);
+				grid[player][currentX-1][height] = colorB[player];
+				colorA[player] = BestColor( player, currentX, GetRowHeight(player, currentX) );
+				grid[player][currentX-1][height] = kEmpty;
+				break;
+		}
+	}
+	
+	SDLU_AcquireSurface( playerSurface[player] );
+
+	if( currentX >= 0 && currentX < kGridAcross &&
+		currentY >= 0 && currentY < kGridDown )
+	{	
+		grid[player][currentX][currentY] = colorA[player];
+		suction[player][currentX][currentY] = kNoSuction;
+		charred[player][currentX][currentY] = kNoCharring;
+
+		SurfaceDrawBlob( player, &myRect, colorA[player], kNoSuction, kNoCharring );
+		CleanSpriteArea( player, &myRect );
+	}
+	
+	OffsetMRect( &myRect, x * kBlobHorizSize, y * kBlobVertSize );
+	currentX += x; 
+	currentY += y;
+	
+	if( currentX >= 0 && currentX < kGridAcross &&
+		currentY >= 0 && currentY < kGridDown )
+	{
+		grid[player][currentX][currentY] = colorB[player];
+		suction[player][currentX][currentY] = kNoSuction;
+		charred[player][currentX][currentY] = kNoCharring;
+		
+		SurfaceDrawBlob( player, &myRect, colorB[player], kNoSuction, kNoCharring );
+		CleanSpriteArea( player, &myRect );
+	}
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+	
+	blobTime[player] = GameTickCount( );
+	halfway[player] = false;
+	role[player] = kDropBlobs;
+}
+
+void DropBlobs( int player )
+{
+	MBoolean busy = false;
+	signed char tempG[kGridDown], tempC[kGridDown];
+	const int jiggleList[] = { kNoSuction, kSquish,
+					      	   kNoSuction, kSquash,
+						  	   kNoSuction, kSquish,
+						  	   kNoSuction, kSquash };
+	MRect myRect;
+	int x, y;
+	
+	if( GameTickCount( ) < blobTime[player] )
+		return;
+	
+	blobTime[player] += 1;
+	
+	halfway[player] = !halfway[player];
+	
+	SDLU_AcquireSurface( playerSurface[player] );
+	
+	if( halfway[player] )
+	{
+		for( x=0; x<kGridAcross; x++ )
+		{
+			for( y = 0; y<kGridDown; y++ )
+			{
+				tempG[y] = grid[player][x][y];
+				tempC[y] = charred[player][x][y];
+			}
+			
+			for( y = kGridDown-1; y; y-- )
+			{
+				if( tempG[y] == kEmpty && tempG[y-1] != kEmpty )
+				{
+					CalcBlobRect( x, y, &myRect );
+					OffsetMRect( &myRect, 0, -kBlobVertSize/2 );
+					
+					if( tempG[y-1] == kGray )
+					{
+						SurfaceDrawBoard( player, &myRect );
+						SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
+					}
+					else
+					{
+						SurfaceDrawBlob( player, &myRect, tempG[y-1], kNoSuction, tempC[y-1] );
+					}
+					
+					tempG[y]   = tempG[y-1];
+					tempG[y-1] = kEmpty;
+					tempC[y]   = tempC[y-1];
+					tempC[y-1] = kNoCharring;
+
+					CleanSpriteArea( player, &myRect );
+					
+					OffsetMRect( &myRect, 0, -kBlobVertSize );
+					SurfaceDrawBoard( player, &myRect );
+					CleanSpriteArea( player, &myRect );
+				}
+			}
+		}
+	}
+	else
+	{
+		for( x=0; x<kGridAcross; x++ )
+		{
+			for( y = kGridDown-1; y; y-- )
+			{
+				if( suction[player][x][y] >= kJiggle1 &&
+					suction[player][x][y] <  kInDoubt )
+				{
+					CalcBlobRect( x, y, &myRect );
+					SurfaceDrawBlob( player, &myRect, grid[player][x][y],
+						jiggleList[ suction[player][x][y] - kJiggle1 ],
+						charred[player][x][y] );
+					CleanSpriteArea( player, &myRect );
+					
+					suction[player][x][y]++;
+					
+					busy = true;
+				}
+				else if( grid[player][x][y] == kEmpty && grid[player][x][y-1] != kEmpty )
+				{
+					grid[player][x][y] = grid[player][x][y-1];
+					grid[player][x][y-1] = kEmpty;
+					charred[player][x][y] = charred[player][x][y-1];
+					charred[player][x][y-1] = kNoCharring;
+					suction[player][x][y-1] = kNoSuction;
+					
+					CalcBlobRect( x, y, &myRect );
+					if( grid[player][x][y] == kGray )
+					{
+						SurfaceDrawBoard( player, &myRect );
+						SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
+					}
+					else
+					{
+						SurfaceDrawBlob( player, &myRect, grid[player][x][y], kNoSuction, charred[player][x][y] );
+					}
+
+					CleanSpriteArea( player, &myRect );
+					
+					OffsetMRect( &myRect, 0, -kBlobVertSize );
+					SurfaceDrawBoard( player, &myRect );
+					CleanSpriteArea( player, &myRect );
+					
+					if( grid[player][x][y] >= kFirstBlob && grid[player][x][y] <= kLastBlob )
+					{
+						if( y >= (kGridDown-1) || grid[player][x][y+1] != kEmpty )
+						{
+							suction[player][x][y] = kJiggle1;
+						}
+					}
+					else
+					{
+						suction[player][x][y] = kNoSuction;
+					}
+					
+					busy = true;
+				}
+			}
+		}
+	}
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+	
+	if( !busy && !halfway[player] )
+	{
+		ResolveSuction( player );
+		role[player] = kZapBlobs;
+	}
+}
+
+void RedrawBoardContents( int player )
+{
+	int x, y;
+	MRect myRect;
+	
+	SDLU_AcquireSurface( playerSurface[player] );
+	
+	for( y=0; y<kGridDown; y++ )
+	{
+		for( x=0; x<kGridAcross; x++ )
+		{
+			CalcBlobRect( x, y, &myRect );
+			if( grid[player][x][y] == kGray )
+			{
+				SurfaceDrawBoard( player, &myRect );
+				SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
+			}
+			else
+			{
+				SurfaceDrawBlob( player, &myRect, grid[player][x][y], suction[player][x][y], charred[player][x][y] );
+			}
+			
+			CleanSpriteArea( player, &myRect );
+		}
+	}
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+}
+
+void ResolveSuction( int player )
+{
+	int x, y, suck, actualSuck, color;
+	MRect myRect;
+	
+	SDLU_AcquireSurface( playerSurface[player] );
+	
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=1; y<kGridDown; y++ )
+		{
+			suck = kNoSuction;
+			color = grid[player][x][y];
+			
+			if( color >= kFirstBlob && color <= kLastBlob )
+			{
+				if( x > 0 )
+					if( grid[player][x-1][y] == color ) suck |= kLeft;
+				
+				if( x < (kGridAcross-1) )
+					if( grid[player][x+1][y] == color ) suck |= kRight;
+					
+				if( y > 1 )
+					if( grid[player][x][y-1] == color ) suck |= kUp;
+					
+				if( y < (kGridDown-1) )
+					if( grid[player][x][y+1] == color ) suck |= kDown;
+				
+				actualSuck = suction[player][x][y];
+				if( actualSuck == kBlinkBlob || actualSuck == kSobBlob ) actualSuck = kNoSuction;
+				
+				if( actualSuck != suck )
+				{
+					suction[player][x][y] = suck;
+					
+					CalcBlobRect( x, y, &myRect );
+					SurfaceDrawBlob( player, &myRect, grid[player][x][y], suck, charred[player][x][y] );
+					CleanSpriteArea( player, &myRect );
+				}
+			}
+			else
+			{
+				suction[player][x][y] = kNoSuction;
+			}
+		}
+	}
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+}
+
+void HandleMagic( int player )
+{
+	if( magic[player] )
+	{
+		colorA[player]++;
+		if( colorA[player] > kBlobTypes ) colorA[player] = 1;
+	}
+}
+
+void Falling( int player )
+{	
+	if( role[1-player] == kLosing )
+	{
+		BeginVictory( player );
+		return;
+	}
+	
+	shadowDepth[player] = kBlobShadowDepth;
+	
+	UpdateTweak( player, kNoSuction );
+
+	if( GameTickCount( ) >= blobTime[player] )
+	{
+		if( CanFall( player ) )
+		{
+			blobTime[player] += dropping[player]? kDropSpeed: speed[player];
+			DoFall( player );
+		}
+		else
+		{
+			blobTime[player] = animTime[player] = GameTickCount( );
+			role[player] = dropping[player]? kFastJiggleBlobs: kJiggleBlobs;
+			anim[player] = 0;
+			PlayStereoFrequency( player, kPlace, player );
+			return;
+		}
+	}
+
+	switch( control[player] )
+	{
+		case kPlayerControl:
+			PlayerControl( player );
+			break;
+		
+		case kAIControl:
+			AIControl( player );
+			break;
+
+		case kAutoControl:
+			AutoControl( player );
+			break;
+	}
+}
+
+void RetrieveBlobs( int player )
+{
+	if( (role[1-player] != kLosing) && (grid[player][2][1] != kEmpty) )
+	{
+		EndRound( player );
+		return;
+	}
+	
+	// See if it's time to update levels in Solitaire mode
+	if( control[1] == kNobodyControl )
+	{
+		int levelClear[] = { 0,
+		                     2500,
+		                     7000,
+		                     14000,
+		                     35000,
+		                     50000,
+		                     70000,
+		                     90000,
+		                     120000,
+		                     160000,
+		                     200000,
+		                     500000,
+		                     1000000 };
+		
+		if( (level <= kLevels) && (score[player] > levelClear[level]) )
+		{
+			FreezeGameTickCount( );
+			
+			PlayMono( kLevelUp );
+			
+			level++;
+			if( InitCharacter( 1, level ) )
+			{
+				InitCharacter( 1, level );
+				character[0] = character[1];
+				character[0].zapStyle = 0;
+			}
+			else
+			{
+				UnfreezeGameTickCount( );
+				TotalVictory( );
+				return;
+			}
+
+			if( level == 2 || level == 4 || level == 7 || level == 9 ) AddExtraPiece( );
+			
+			PrepareStageGraphics( character[1].picture );
+			ChooseMusic( character[1].music );
+
+			UnfreezeGameTickCount( );
+		}		                     
+	}
+	
+	PullNext( player );
+	
+	dropping[player] = false;
+	anim[player] = 0;
+	magic[player] = nextM[player];
+	grenade[player] = nextG[player];
+	zapIteration[player] = 0;
+	chain[player] = 1;
+	
+	emotions[player] = DetermineEmotion(player);
+	if( players == 1 && player == 0 ) 
+	{
+		if( emotions[0] == kEmotionPanic ) FastMusic(); else SlowMusic();
+	}
+	
+	if( magic[player] || grenade[player] )
+	{
+		PlayStereoFrequency( player, kMagic, player );
+	}
+	
+	colorA[player] = nextA[player];
+	colorB[player] = nextB[player];
+	
+	nextG[player] = GetGrenade( player );
+
+	if( nextG[player] )
+	{
+		nextA[player] = kBombBottom;
+		nextB[player] = kBombTop;
+		nextM[player] = false;
+	}
+	else
+	{
+		nextA[player] = GetPiece( player );
+		nextB[player] = GetPiece( player );
+		nextM[player] = GetMagic( player );
+	}
+
+	ChooseGlowingBlobs( player );
+	
+	if( control[player] == kPlayerControl )
+	{
+		memcpy( potentialCombo[player].grid, grid[player], kGridAcross * kGridDown );
+		potentialCombo[player].a = colorA[player];
+		potentialCombo[player].b = colorB[player];
+		potentialCombo[player].m = magic[player];
+		potentialCombo[player].g = grenade[player];
+		potentialCombo[player].lv = level;
+		potentialCombo[player].x = 0;
+		potentialCombo[player].r = 0;
+		potentialCombo[player].player = player;
+		potentialCombo[player].value = 0;
+		potentialCombo[player].name[0] = 0;
+	}
+	
+	blobX[player] = 2;
+	blobY[player] = 0;
+	blobR[player] = upRotate;
+	blobSpin[player] = 0;
+	halfway[player] = false;
+	
+	DrawSpriteBlobs( player, kNoSuction );
+	
+	speed[player] = (signed char)(character[player].dropSpeed);
+	blobTime[player] = animTime[player] = GameTickCount( );
+	role[player] = kFalling;
+	
+	if( control[player] == kAIControl )
+		ChooseAIDestination( player );
+}
+
+void ChooseGlowingBlobs( int player )
+{
+	int x, height[kGridAcross];
+	
+    if( character[player].hints && !grenade[player] && !magic[player] )
+    {
+		for( x=0; x<kGridAcross; x++ )
+		{
+			height[x] = GetRowHeight( player, x );
+		}
+				
+		for( x=0; x<kGridAcross; x++ )
+		{
+			if( x>0 && height[x]>1 && height[x-1]>1 ) 
+			{
+				// left
+
+				grid[player][x  ][height[x  ]] = colorA[player];
+				grid[player][x-1][height[x-1]] = colorB[player];
+				ConsiderGlow( player, colorA[player], x, height[x  ] );
+				ConsiderGlow( player, colorB[player], x, height[x-1] );
+				grid[player][x  ][height[x  ]] = kEmpty;
+				grid[player][x-1][height[x-1]] = kEmpty;
+			}
+
+			if( x<(kGridAcross-1) && height[x]>1 && height[x+1]>1 ) 
+			{
+				// right
+
+				grid[player][x  ][height[x  ]] = colorA[player];
+				grid[player][x+1][height[x+1]] = colorB[player];
+				ConsiderGlow( player, colorA[player], x, height[x  ] );
+				ConsiderGlow( player, colorB[player], x, height[x+1] );
+				grid[player][x  ][height[x  ]] = kEmpty;
+				grid[player][x+1][height[x+1]] = kEmpty;
+			}
+
+			if( height[x]>2 ) 
+			{
+				// up
+
+				grid[player][x][height[x]  ] = colorA[player];
+				grid[player][x][height[x]-1] = colorB[player];
+				ConsiderGlow( player, colorA[player], x, height[x]   );
+				ConsiderGlow( player, colorB[player], x, height[x]-1 );
+				grid[player][x][height[x]  ] = kEmpty;
+				grid[player][x][height[x]-1] = kEmpty;
+
+				// down
+
+				grid[player][x][height[x]  ] = colorB[player];
+				grid[player][x][height[x]-1] = colorA[player];
+				ConsiderGlow( player, colorB[player], x, height[x]   );
+				ConsiderGlow( player, colorA[player], x, height[x]-1 );
+				grid[player][x][height[x]  ] = kEmpty;
+				grid[player][x][height[x]-1] = kEmpty;
+			}
+		}
+	}
+}
+
+void ConsiderGlow( int player, int color, int x, int y )
+{
+	if( GetChainSize( grid[player], x, y, color ) >= kBlobClusterSize )
+	{
+		CleanWithPolish( grid[player], glow[player], x, y, color );					
+	}
+	else
+	{
+		CleanSize( grid[player], x, y, color );
+	}
+}
+
+void GlowBlobs( int player )
+{
+	int x, y, color, suck;
+	MRect myRect;
+	
+	if( (!character[player].hints) || (GameTickCount() < hintTime[player]) )
+		return;
+		
+	hintTime[player] += 3;
+	if( ++hintGlow >= kGlowArraySize ) hintGlow = 0;
+
+	SDLU_AcquireSurface( playerSurface[player] );
+
+	for( x=0; x<kGridAcross; x++ )
+	{
+		if( rowBounce[player][x] == -1 )
+		{
+			for( y=0; y<kGridDown; y++ )
+			{
+				if( glow[player][x][y] && grid[player][x][y] != kEmpty )
+				{
+					CalcBlobRect( x, y, &myRect );	
+
+					color = grid[player][x][y];
+					suck = suction[player][x][y];
+					
+					SurfaceDrawBlob( player, &myRect, color, suck, charred[player][x][y] );
+					SurfaceDrawColor( &myRect, color, suck, glowColors[color][0], glowColors[color][1], glowColors[color][2], glowArray[hintGlow] );
+					CleanSpriteArea( player, &myRect );
+				}
+			}
+		}
+	}
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+}
+
+void FadeCharred( int player )
+{
+	int x, y;
+	MRect myRect;
+	
+	if( GameTickCount() < fadeCharTime[player] || role[player] != kFalling ) return;
+	
+	fadeCharTime[player] += 135;
+	
+	SDLU_AcquireSurface( playerSurface[player] ); 
+	
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=0; y<kGridDown; y++ )
+		{
+			if( charred[player][x][y] & 0xF0 )
+			{
+				charred[player][x][y] = ( charred[player][x][y] & 0x0F ) | ( ( charred[player][x][y] & 0xF0 ) - 0x10 );
+				
+				if( rowBounce[player][x] == -1 )
+				{
+					CalcBlobRect( x, y, &myRect );
+					SurfaceDrawBlob( player, &myRect, grid[player][x][y], suction[player][x][y], charred[player][x][y] );
+					CleanSpriteArea( player, &myRect );
+				}
+			}
+		}
+	}
+
+	SDLU_ReleaseSurface( playerSurface[player] ); 
+}
+
+void BlinkBored( int player )
+{
+	MRect myRect;
+	int which, x, y, count;
+	
+	if( GameTickCount() < boredTime[player] )
+		return;
+	
+	SDLU_AcquireSurface( playerSurface[player] ); 
+	
+	// undo any previously bored blobs
+	
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=0; y<kGridDown; y++ )
+		{
+			if( ( rowBounce[player][x] == -1 ) &&
+				( suction[player][x][y] == kBlinkBlob || suction[player][x][y] == kSobBlob ) )
+			{
+				suction[player][x][y] = kNoSuction;
+				
+				CalcBlobRect( x, y, &myRect );
+				SurfaceDrawBlob( player, &myRect, grid[player][x][y],
+								   suction[player][x][y],
+								   charred[player][x][y] );
+				CleanSpriteArea( player, &myRect );
+			}				
+		}
+	}
+		
+	// make some boredom
+
+	which = RandomBefore( 2 );
+	boredTime[player] += which? 15: 80;
+	which = which? kBlinkBlob: kSobBlob;
+
+	for( count=0; count<5; count++ )
+	{
+		x = RandomBefore( kGridAcross );
+		y = RandomBefore( kGridDown );
+		
+		if( rowBounce[player][x] == -1 &&
+			suction[player][x][y] == kNoSuction &&
+			grid[player][x][y] >= kFirstBlob &&
+			grid[player][x][y] <= kLastBlob   
+		  ) 
+		{
+			suction[player][x][y] = which;
+			
+			CalcBlobRect( x, y, &myRect );
+			SurfaceDrawBlob( player, &myRect,
+					    grid[player][x][y],
+					    suction[player][x][y],
+					    charred[player][x][y] );
+			CleanSpriteArea( player, &myRect );
+		}
+	}
+
+	SDLU_ReleaseSurface( playerSurface[player] ); 
+}
+
+void InitPlayers( void )
+{
+	const double windowLoc[ ] = { kLeftPlayerWindowCenter, kRightPlayerWindowCenter };
+	
+	playerWindowZRect.top = playerWindowZRect.left = 0;
+	playerWindowZRect.bottom = 288; playerWindowZRect.right = 144;
+	
+	playerWindowRect[0] = playerWindowRect[1] = playerWindowZRect;
+	CenterRectOnScreen( &playerWindowRect[0], windowLoc[0], 0.5 );
+	CenterRectOnScreen( &playerWindowRect[1], windowLoc[1], 0.5 );
+}
--- a/src/players.cpp
+++ /dev/null
@@ -1,1078 +1,0 @@
-// players.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "players.h"
-#include "gworld.h"
-#include "moving.h"
-#include "graphics.h"
-#include "control.h"
-#include "grays.h"
-#include "soundfx.h"
-#include "next.h"
-#include "random.h"
-#include "victory.h"
-#include "tweak.h"
-#include "zap.h"
-#include "level.h"
-#include "opponent.h"
-#include "gameticks.h"
-#include "blitter.h"
-#include "music.h"
-#include "score.h"
-#include "hiscore.h"
-
-#include <string.h>
-#include <stdlib.h>
-
-unsigned int  boredTime[2], hintTime[2], fadeCharTime[2], animTime[2], shadowDepth[2], hintGlow, messageTime;
-int emotions[2];
-int glowColors[][3] = { { 0,  0,  0},
-						{_5TO8(13), _5TO8(26), _5TO8(31)},
-						{_5TO8(13), _5TO8(29), _5TO8(13)},
-						{_5TO8(31), _5TO8(18), _5TO8(31)},
-						{_5TO8(31), _5TO8(14), _5TO8(18)},
-						{_5TO8(31), _5TO8(31), _5TO8(15)},
-						{_5TO8(31), _5TO8(21), _5TO8(13)},
-						{_5TO8(30), _5TO8(22), _5TO8(30)},
-						{_5TO8(20), _5TO8(20), _5TO8(20)} };
-
-void HandlePlayers( void )
-{
-	int player;
-	
-	for( player = 0; player<=1; player++ )
-	{
-		UpdateScore( player );
-		UpdateNext( player );
-		BlinkGrays( player );
-		GlowBlobs( player );
-		BlinkBored( player );
-		FadeCharred( player );
-
-		switch( role[player] )
-		{
-			case kWaitForRetrieval:
-				if( control[player] == kAutoControl )
-				{
-					AutoControl( player );
-					break;
-				}
-				
-				role[player] = kRetrieveBlobs;
-				// fallthrough
-				
-			case kRetrieveBlobs:
-				LockGrays( 1-player );
-				RetrieveBlobs( player );
-				break;
-				
-			case kFalling:
-				Falling( player );
-				break;
-			
-			case kLockdownBlobs:
-				LockdownBlobs( player );
-				break;
-				
-			case kJiggleBlobs:
-				JiggleBlobs( player );
-				break;
-				
-			case kFastJiggleBlobs:
-				FastJiggleBlobs( player );
-				break;
-				
-			case kPlaceBlobs:
-				PlaceBlobs( player );
-				break;
-			
-			case kDropBlobs:
-				DropBlobs( player );
-				break;
-			
-			case kZapBlobs:
-				ZapBlobs( player );
-				break;
-			
-			case kKillBlobs:
-				KillBlobs( player );
-				break;
-			
-			case kDropGrays:
-				DropGrays( player );
-				break;
-			
-			case kLosing:
-				Lose( player );
-				break;
-			
-			case kWinning:
-				Win( player );
-				break;
-				
-			case kChooseDifficulty:
-				ChooseDifficulty( player );
-				break;
-			
-			case kWaitingToStart:
-				if( role[1-player] == kWaitingToStart )
-					role[0] = role[1] = kZapBlobs;
-				break;
-			
-			case kIdlePlayer:
-				break;
-		}
-		
-		UpdatePlayerWindow( player );
-	}
-}
-
-void LockdownBlobs( int player )
-{
-	const int shadowList[] = { 
-								 kBlobShadowDepth, 
-								 kBlobShadowDepth*5/6, 
-								 kBlobShadowDepth*5/6, 
-								 kBlobShadowDepth*4/6, 
-								 kBlobShadowDepth*3/6, 
-								 kBlobShadowDepth*1/6, 
-								};
-
-	int frame = (GameTickCount( ) - blobTime[player]) / 2;
-
-	if( frame >= arrsize(shadowList) )
-	{
-		role[player] = kPlaceBlobs;
-		return;
-	}
-	
-	shadowDepth[player] = shadowList[frame];
-	UpdateTweak( player, blobJiggleAnimation );
-}
-
-void FastJiggleBlobs( int player )
-{
-	const int shadowList[] = { 
-								 kBlobShadowDepth, 
-								 kBlobShadowDepth*5/6, 
-								 kBlobShadowDepth*5/6, 
-								 kBlobShadowDepth*4/6, 
-								 kBlobShadowDepth*3/6, 
-								 kBlobShadowDepth*2/6, 
-								 kBlobShadowDepth*1/6,
-								 0 
-								};
-
-	int frame = (GameTickCount( ) - blobTime[player]) / 2;
-
-	if( frame >= 8 )
-	{
-		role[player] = kPlaceBlobs;
-		return;
-	}
-
-	switch( control[player] )
-	{
-		case kPlayerControl:
-			PlayerControl( player );
-			break;
-		
-		case kAIControl:
-			AIControl( player );
-			break;
-		
-		case kAutoControl:
-			AutoControl( player );
-			break;
-	}
-	
-	shadowDepth[player] = shadowList[frame];
-	UpdateTweak( player, blobJiggleAnimation );
-
-	if( CanFall( player ) )
-		role[player] = kFalling;
-}
-
-
-void JiggleBlobs( int player )
-{
-	const int shadowList[] = { 
-								 kBlobShadowDepth, kBlobShadowDepth,
-								 kBlobShadowDepth*5/6, kBlobShadowDepth*5/6,
-								 kBlobShadowDepth*5/6, kBlobShadowDepth*4/6,
-								 kBlobShadowDepth*4/6, kBlobShadowDepth*3/6,
-								 kBlobShadowDepth*3/6, kBlobShadowDepth*2/6,
-								 kBlobShadowDepth*2/6, kBlobShadowDepth*1/6,
-								 kBlobShadowDepth*1/6, 0
-								};
-
-	int frame = (GameTickCount( ) - blobTime[player]) / 2;
-	
-	if( frame >= 14 )
-	{
-		role[player] = kPlaceBlobs;
-		return;
-	}
-
-	switch( control[player] )
-	{
-		case kPlayerControl:
-			PlayerControl( player );
-			break;
-		
-		case kAIControl:
-			AIControl( player );
-			break;
-			
-		case kAutoControl:
-			AutoControl( player );
-			break;
-	}
-
-	shadowDepth[player] = shadowList[frame];
-	UpdateTweak( player, kJiggleAnimation );
-	
-	if( CanFall( player ) )
-		role[player] = kFalling;
-}
-
-void PlaceGrenade( int player )
-{
-	MRect myRect;
-	int x, y, atX, atY, color, delay = -6;
-	int currentX = blobX[player], currentY = blobY[player];
-	int charTypes[3][5] = { { kDarkChar | kChar11, kDarkChar | kChar12, kDarkChar | kChar13, kDarkChar | kChar14, kNoCharring         },
-	                        { kNoCharring,         kNoCharring,         kNoCharring,         kNoCharring,         kDarkChar | kChar24 },
-	                        { kDarkChar | kChar31, kDarkChar | kChar32, kDarkChar | kChar33, kDarkChar | kChar34, kNoCharring         } };
-	
-	int amount = ((level <= kLevels)? level: 1) * 100;
-	int multiplier = 0;
-	
-	grenadeFrame[player] = 0;
-	grenadeRect [player].top    = (currentY * kBlobVertSize ) - (kBlastHeight/2);
-	grenadeRect [player].left   = (currentX * kBlobHorizSize) + (kBlobHorizSize/2) - (kBlastWidth/2);
-	grenadeRect [player].bottom = grenadeRect[player].top  + kBlastHeight;
-	grenadeRect [player].right  = grenadeRect[player].left + kBlastWidth ;
-
-	SDLU_AcquireSurface( playerSurface[player] );
-	
-	for( x=-1; x<=1; x++ )
-	{
-		atX = currentX + x;
-		
-		for( y=-2; y<=2; y++ )
-		{
-			atY = currentY + y;
-
-			if( atX >= 0 && atX < kGridAcross &&
-			    atY >= 0 && atY < kGridDown )
-			{
-				if( charTypes[x+1][y+2] != kNoCharring   &&
-				    grid[player][atX][atY] >= kFirstBlob &&
-					grid[player][atX][atY] <= kLastBlob     )
-				{
-					charred[player][atX][atY] =  charTypes[x+1][y+2];
-
-					CalcBlobRect( atX, atY, &myRect );
-					SurfaceDrawBlob( player, &myRect, grid[player][atX][atY], suction[player][atX][atY], charred[player][atX][atY] );
-					CleanSpriteArea( player, &myRect );
-				}
-			}
-		}
-	}
-
-	SDLU_ReleaseSurface( playerSurface[player] );
-	
-	if( currentY < (kGridDown-1) && 
-		( grid[player][currentX][currentY+1] >= kFirstBlob && 
-		  grid[player][currentX][currentY+1] <= kLastBlob    ) )
-	{
-		color = grid[player][currentX][currentY+1];
-		
-		for( x=0; x<kGridAcross; x++ )
-		{
-			for( y=0; y<kGridDown; y++ )
-			{
-				if( grid[player][x][y] == color )
-				{
-					suction[player][x][y] = kInDeath;
-					death[player][x][y] = -abs( x - currentX + y - currentY );
-					multiplier++;
-					
-					if( (x <= (kGridAcross-2)) && (grid[player][x+1][y] == kGray) )
-					{
-						suction[player][x+1][y] = kGrayBlink1;
-						death[player][x+1][y] = delay;
-					}
-
-					if( (x >= 1) && (grid[player][x-1][y] == kGray) )
-					{
-						suction[player][x-1][y] = kGrayBlink1;
-						death[player][x-1][y] = delay;
-					}
-
-					if( (y <= (kGridDown-2)) && (grid[player][x][y+1] == kGray) )
-					{
-						suction[player][x][y+1] = kGrayBlink1;
-						death[player][x][y+1] = delay;
-					}
-
-					if( (y >= 1) && (grid[player][x][y-1] == kGray) )
-					{
-						suction[player][x][y-1] = kGrayBlink1;
-						death[player][x][y-1] = delay;
-					}
-				}
-			}
-		}
-	}
-	else
-	{
-		for( x=currentX-1; x<=currentX+1; x++ )
-		{
-			for( y=currentY-1; y<=currentY+1; y++ )
-			{
-				if( x>=0 && x<kGridAcross && y>=0 && y<kGridDown )
-				{
-					if( grid[player][x][y] == kGray )
-					{
-						suction[player][x][y] = kGrayBlink1;
-						death[player][x][y] = delay;
-					}
-					else if( grid[player][x][y] >= kFirstBlob &&
-					         grid[player][x][y] <= kLastBlob     )
-					{
-						suction[player][x][y] = kInDeath;
-						death[player][x][y] = -abs( x - currentX + y - currentY );
-						multiplier++;
-					}
-				}
-			}
-		}
-	}
-	
-	PlayStereo( player, kSplop );
-
-	if( multiplier > 0 )
-	{
-		score[player] += amount * multiplier;	
-		ZapScoreDisplay( player, amount, multiplier, blobX[player], blobY[player], 8 );
-	}
-	
-	blobTime[player] = GameTickCount( );
-	role[player] = kKillBlobs;
-}
-
-void PlaceBlobs( int player )
-{
-	MRect myRect;
-	int x, y, height;
-	int currentX = blobX[player], currentY = blobY[player];
-	
-	potentialCombo[player].x = currentX;
-	potentialCombo[player].r = blobR[player];
-
-	SDLU_AcquireSurface( playerSurface[player] );
-
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=0; y<kGridDown; y++ )
-		{
-			if( glow[player][x][y] )
-			{
-				glow[player][x][y] = false;
-				CalcBlobRect( x, y, &myRect );
-				SurfaceDrawBlob( player, &myRect, grid[player][x][y], suction[player][x][y], charred[player][x][y] );
-				CleanSpriteArea( player, &myRect );
-			}
-		}
-	}
-
-	SDLU_ReleaseSurface( playerSurface[player] );
-
-	EraseSpriteBlobs( player );
-	
-	CalcBlobRect( currentX, currentY, &myRect );
-	CalcSecondBlobOffset( player, &x, &y );
-	
-	if( grenade[player] )
-	{
-		PlaceGrenade( player );	
-		return;
-	}
-	
-	if( magic[player] )
-	{
-		switch( blobR[player] )
-		{
-			case upRotate:
-				height = GetRowHeight(player, currentX)-1;
-				grid[player][currentX][height] = colorB[player];
-				colorA[player] = BestColor( player, currentX, height+1 );
-				grid[player][currentX][height] = kEmpty;
-				break;
-			
-			case downRotate:
-				height = GetRowHeight(player, currentX);
-				grid[player][currentX][height] = colorB[player];
-				colorA[player] = BestColor( player, currentX, height-1 );
-				grid[player][currentX][height] = kEmpty;
-				break;
-			
-			case rightRotate:
-				height = GetRowHeight(player, currentX+1);
-				grid[player][currentX+1][height] = colorB[player];
-				colorA[player] = BestColor( player, currentX, GetRowHeight(player, currentX) );
-				grid[player][currentX+1][height] = kEmpty;
-				break;
-
-			case leftRotate:
-				height = GetRowHeight(player, currentX-1);
-				grid[player][currentX-1][height] = colorB[player];
-				colorA[player] = BestColor( player, currentX, GetRowHeight(player, currentX) );
-				grid[player][currentX-1][height] = kEmpty;
-				break;
-		}
-	}
-	
-	SDLU_AcquireSurface( playerSurface[player] );
-
-	if( currentX >= 0 && currentX < kGridAcross &&
-		currentY >= 0 && currentY < kGridDown )
-	{	
-		grid[player][currentX][currentY] = colorA[player];
-		suction[player][currentX][currentY] = kNoSuction;
-		charred[player][currentX][currentY] = kNoCharring;
-
-		SurfaceDrawBlob( player, &myRect, colorA[player], kNoSuction, kNoCharring );
-		CleanSpriteArea( player, &myRect );
-	}
-	
-	OffsetMRect( &myRect, x * kBlobHorizSize, y * kBlobVertSize );
-	currentX += x; 
-	currentY += y;
-	
-	if( currentX >= 0 && currentX < kGridAcross &&
-		currentY >= 0 && currentY < kGridDown )
-	{
-		grid[player][currentX][currentY] = colorB[player];
-		suction[player][currentX][currentY] = kNoSuction;
-		charred[player][currentX][currentY] = kNoCharring;
-		
-		SurfaceDrawBlob( player, &myRect, colorB[player], kNoSuction, kNoCharring );
-		CleanSpriteArea( player, &myRect );
-	}
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-	
-	blobTime[player] = GameTickCount( );
-	halfway[player] = false;
-	role[player] = kDropBlobs;
-}
-
-void DropBlobs( int player )
-{
-	MBoolean busy = false;
-	signed char tempG[kGridDown], tempC[kGridDown];
-	const int jiggleList[] = { kNoSuction, kSquish,
-					      	   kNoSuction, kSquash,
-						  	   kNoSuction, kSquish,
-						  	   kNoSuction, kSquash };
-	MRect myRect;
-	int x, y;
-	
-	if( GameTickCount( ) < blobTime[player] )
-		return;
-	
-	blobTime[player] += 1;
-	
-	halfway[player] = !halfway[player];
-	
-	SDLU_AcquireSurface( playerSurface[player] );
-	
-	if( halfway[player] )
-	{
-		for( x=0; x<kGridAcross; x++ )
-		{
-			for( y = 0; y<kGridDown; y++ )
-			{
-				tempG[y] = grid[player][x][y];
-				tempC[y] = charred[player][x][y];
-			}
-			
-			for( y = kGridDown-1; y; y-- )
-			{
-				if( tempG[y] == kEmpty && tempG[y-1] != kEmpty )
-				{
-					CalcBlobRect( x, y, &myRect );
-					OffsetMRect( &myRect, 0, -kBlobVertSize/2 );
-					
-					if( tempG[y-1] == kGray )
-					{
-						SurfaceDrawBoard( player, &myRect );
-						SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
-					}
-					else
-					{
-						SurfaceDrawBlob( player, &myRect, tempG[y-1], kNoSuction, tempC[y-1] );
-					}
-					
-					tempG[y]   = tempG[y-1];
-					tempG[y-1] = kEmpty;
-					tempC[y]   = tempC[y-1];
-					tempC[y-1] = kNoCharring;
-
-					CleanSpriteArea( player, &myRect );
-					
-					OffsetMRect( &myRect, 0, -kBlobVertSize );
-					SurfaceDrawBoard( player, &myRect );
-					CleanSpriteArea( player, &myRect );
-				}
-			}
-		}
-	}
-	else
-	{
-		for( x=0; x<kGridAcross; x++ )
-		{
-			for( y = kGridDown-1; y; y-- )
-			{
-				if( suction[player][x][y] >= kJiggle1 &&
-					suction[player][x][y] <  kInDoubt )
-				{
-					CalcBlobRect( x, y, &myRect );
-					SurfaceDrawBlob( player, &myRect, grid[player][x][y],
-						jiggleList[ suction[player][x][y] - kJiggle1 ],
-						charred[player][x][y] );
-					CleanSpriteArea( player, &myRect );
-					
-					suction[player][x][y]++;
-					
-					busy = true;
-				}
-				else if( grid[player][x][y] == kEmpty && grid[player][x][y-1] != kEmpty )
-				{
-					grid[player][x][y] = grid[player][x][y-1];
-					grid[player][x][y-1] = kEmpty;
-					charred[player][x][y] = charred[player][x][y-1];
-					charred[player][x][y-1] = kNoCharring;
-					suction[player][x][y-1] = kNoSuction;
-					
-					CalcBlobRect( x, y, &myRect );
-					if( grid[player][x][y] == kGray )
-					{
-						SurfaceDrawBoard( player, &myRect );
-						SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
-					}
-					else
-					{
-						SurfaceDrawBlob( player, &myRect, grid[player][x][y], kNoSuction, charred[player][x][y] );
-					}
-
-					CleanSpriteArea( player, &myRect );
-					
-					OffsetMRect( &myRect, 0, -kBlobVertSize );
-					SurfaceDrawBoard( player, &myRect );
-					CleanSpriteArea( player, &myRect );
-					
-					if( grid[player][x][y] >= kFirstBlob && grid[player][x][y] <= kLastBlob )
-					{
-						if( y >= (kGridDown-1) || grid[player][x][y+1] != kEmpty )
-						{
-							suction[player][x][y] = kJiggle1;
-						}
-					}
-					else
-					{
-						suction[player][x][y] = kNoSuction;
-					}
-					
-					busy = true;
-				}
-			}
-		}
-	}
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-	
-	if( !busy && !halfway[player] )
-	{
-		ResolveSuction( player );
-		role[player] = kZapBlobs;
-	}
-}
-
-void RedrawBoardContents( int player )
-{
-	int x, y;
-	MRect myRect;
-	
-	SDLU_AcquireSurface( playerSurface[player] );
-	
-	for( y=0; y<kGridDown; y++ )
-	{
-		for( x=0; x<kGridAcross; x++ )
-		{
-			CalcBlobRect( x, y, &myRect );
-			if( grid[player][x][y] == kGray )
-			{
-				SurfaceDrawBoard( player, &myRect );
-				SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
-			}
-			else
-			{
-				SurfaceDrawBlob( player, &myRect, grid[player][x][y], suction[player][x][y], charred[player][x][y] );
-			}
-			
-			CleanSpriteArea( player, &myRect );
-		}
-	}
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-}
-
-void ResolveSuction( int player )
-{
-	int x, y, suck, actualSuck, color;
-	MRect myRect;
-	
-	SDLU_AcquireSurface( playerSurface[player] );
-	
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=1; y<kGridDown; y++ )
-		{
-			suck = kNoSuction;
-			color = grid[player][x][y];
-			
-			if( color >= kFirstBlob && color <= kLastBlob )
-			{
-				if( x > 0 )
-					if( grid[player][x-1][y] == color ) suck |= kLeft;
-				
-				if( x < (kGridAcross-1) )
-					if( grid[player][x+1][y] == color ) suck |= kRight;
-					
-				if( y > 1 )
-					if( grid[player][x][y-1] == color ) suck |= kUp;
-					
-				if( y < (kGridDown-1) )
-					if( grid[player][x][y+1] == color ) suck |= kDown;
-				
-				actualSuck = suction[player][x][y];
-				if( actualSuck == kBlinkBlob || actualSuck == kSobBlob ) actualSuck = kNoSuction;
-				
-				if( actualSuck != suck )
-				{
-					suction[player][x][y] = suck;
-					
-					CalcBlobRect( x, y, &myRect );
-					SurfaceDrawBlob( player, &myRect, grid[player][x][y], suck, charred[player][x][y] );
-					CleanSpriteArea( player, &myRect );
-				}
-			}
-			else
-			{
-				suction[player][x][y] = kNoSuction;
-			}
-		}
-	}
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-}
-
-void HandleMagic( int player )
-{
-	if( magic[player] )
-	{
-		colorA[player]++;
-		if( colorA[player] > kBlobTypes ) colorA[player] = 1;
-	}
-}
-
-void Falling( int player )
-{	
-	if( role[1-player] == kLosing )
-	{
-		BeginVictory( player );
-		return;
-	}
-	
-	shadowDepth[player] = kBlobShadowDepth;
-	
-	UpdateTweak( player, kNoSuction );
-
-	if( GameTickCount( ) >= blobTime[player] )
-	{
-		if( CanFall( player ) )
-		{
-			blobTime[player] += dropping[player]? kDropSpeed: speed[player];
-			DoFall( player );
-		}
-		else
-		{
-			blobTime[player] = animTime[player] = GameTickCount( );
-			role[player] = dropping[player]? kFastJiggleBlobs: kJiggleBlobs;
-			anim[player] = 0;
-			PlayStereoFrequency( player, kPlace, player );
-			return;
-		}
-	}
-
-	switch( control[player] )
-	{
-		case kPlayerControl:
-			PlayerControl( player );
-			break;
-		
-		case kAIControl:
-			AIControl( player );
-			break;
-
-		case kAutoControl:
-			AutoControl( player );
-			break;
-	}
-}
-
-void RetrieveBlobs( int player )
-{
-	if( (role[1-player] != kLosing) && (grid[player][2][1] != kEmpty) )
-	{
-		EndRound( player );
-		return;
-	}
-	
-	// See if it's time to update levels in Solitaire mode
-	if( control[1] == kNobodyControl )
-	{
-		int levelClear[] = { 0,
-		                     2500,
-		                     7000,
-		                     14000,
-		                     35000,
-		                     50000,
-		                     70000,
-		                     90000,
-		                     120000,
-		                     160000,
-		                     200000,
-		                     500000,
-		                     1000000 };
-		
-		if( (level <= kLevels) && (score[player] > levelClear[level]) )
-		{
-			FreezeGameTickCount( );
-			
-			PlayMono( kLevelUp );
-			
-			level++;
-			if( InitCharacter( 1, level ) )
-			{
-				InitCharacter( 1, level );
-				character[0] = character[1];
-				character[0].zapStyle = 0;
-			}
-			else
-			{
-				UnfreezeGameTickCount( );
-				TotalVictory( );
-				return;
-			}
-
-			if( level == 2 || level == 4 || level == 7 || level == 9 ) AddExtraPiece( );
-			
-			PrepareStageGraphics( character[1].picture );
-			ChooseMusic( character[1].music );
-
-			UnfreezeGameTickCount( );
-		}		                     
-	}
-	
-	PullNext( player );
-	
-	dropping[player] = false;
-	anim[player] = 0;
-	magic[player] = nextM[player];
-	grenade[player] = nextG[player];
-	zapIteration[player] = 0;
-	chain[player] = 1;
-	
-	emotions[player] = DetermineEmotion(player);
-	if( players == 1 && player == 0 ) 
-	{
-		if( emotions[0] == kEmotionPanic ) FastMusic(); else SlowMusic();
-	}
-	
-	if( magic[player] || grenade[player] )
-	{
-		PlayStereoFrequency( player, kMagic, player );
-	}
-	
-	colorA[player] = nextA[player];
-	colorB[player] = nextB[player];
-	
-	nextG[player] = GetGrenade( player );
-
-	if( nextG[player] )
-	{
-		nextA[player] = kBombBottom;
-		nextB[player] = kBombTop;
-		nextM[player] = false;
-	}
-	else
-	{
-		nextA[player] = GetPiece( player );
-		nextB[player] = GetPiece( player );
-		nextM[player] = GetMagic( player );
-	}
-
-	ChooseGlowingBlobs( player );
-	
-	if( control[player] == kPlayerControl )
-	{
-		memcpy( potentialCombo[player].grid, grid[player], kGridAcross * kGridDown );
-		potentialCombo[player].a = colorA[player];
-		potentialCombo[player].b = colorB[player];
-		potentialCombo[player].m = magic[player];
-		potentialCombo[player].g = grenade[player];
-		potentialCombo[player].lv = level;
-		potentialCombo[player].x = 0;
-		potentialCombo[player].r = 0;
-		potentialCombo[player].player = player;
-		potentialCombo[player].value = 0;
-		potentialCombo[player].name[0] = 0;
-	}
-	
-	blobX[player] = 2;
-	blobY[player] = 0;
-	blobR[player] = upRotate;
-	blobSpin[player] = 0;
-	halfway[player] = false;
-	
-	DrawSpriteBlobs( player, kNoSuction );
-	
-	speed[player] = (signed char)(character[player].dropSpeed);
-	blobTime[player] = animTime[player] = GameTickCount( );
-	role[player] = kFalling;
-	
-	if( control[player] == kAIControl )
-		ChooseAIDestination( player );
-}
-
-void ChooseGlowingBlobs( int player )
-{
-	int x, height[kGridAcross];
-	
-    if( character[player].hints && !grenade[player] && !magic[player] )
-    {
-		for( x=0; x<kGridAcross; x++ )
-		{
-			height[x] = GetRowHeight( player, x );
-		}
-				
-		for( x=0; x<kGridAcross; x++ )
-		{
-			if( x>0 && height[x]>1 && height[x-1]>1 ) 
-			{
-				// left
-
-				grid[player][x  ][height[x  ]] = colorA[player];
-				grid[player][x-1][height[x-1]] = colorB[player];
-				ConsiderGlow( player, colorA[player], x, height[x  ] );
-				ConsiderGlow( player, colorB[player], x, height[x-1] );
-				grid[player][x  ][height[x  ]] = kEmpty;
-				grid[player][x-1][height[x-1]] = kEmpty;
-			}
-
-			if( x<(kGridAcross-1) && height[x]>1 && height[x+1]>1 ) 
-			{
-				// right
-
-				grid[player][x  ][height[x  ]] = colorA[player];
-				grid[player][x+1][height[x+1]] = colorB[player];
-				ConsiderGlow( player, colorA[player], x, height[x  ] );
-				ConsiderGlow( player, colorB[player], x, height[x+1] );
-				grid[player][x  ][height[x  ]] = kEmpty;
-				grid[player][x+1][height[x+1]] = kEmpty;
-			}
-
-			if( height[x]>2 ) 
-			{
-				// up
-
-				grid[player][x][height[x]  ] = colorA[player];
-				grid[player][x][height[x]-1] = colorB[player];
-				ConsiderGlow( player, colorA[player], x, height[x]   );
-				ConsiderGlow( player, colorB[player], x, height[x]-1 );
-				grid[player][x][height[x]  ] = kEmpty;
-				grid[player][x][height[x]-1] = kEmpty;
-
-				// down
-
-				grid[player][x][height[x]  ] = colorB[player];
-				grid[player][x][height[x]-1] = colorA[player];
-				ConsiderGlow( player, colorB[player], x, height[x]   );
-				ConsiderGlow( player, colorA[player], x, height[x]-1 );
-				grid[player][x][height[x]  ] = kEmpty;
-				grid[player][x][height[x]-1] = kEmpty;
-			}
-		}
-	}
-}
-
-void ConsiderGlow( int player, int color, int x, int y )
-{
-	if( GetChainSize( grid[player], x, y, color ) >= kBlobClusterSize )
-	{
-		CleanWithPolish( grid[player], glow[player], x, y, color );					
-	}
-	else
-	{
-		CleanSize( grid[player], x, y, color );
-	}
-}
-
-void GlowBlobs( int player )
-{
-	int x, y, color, suck;
-	MRect myRect;
-	
-	if( (!character[player].hints) || (GameTickCount() < hintTime[player]) )
-		return;
-		
-	hintTime[player] += 3;
-	if( ++hintGlow >= kGlowArraySize ) hintGlow = 0;
-
-	SDLU_AcquireSurface( playerSurface[player] );
-
-	for( x=0; x<kGridAcross; x++ )
-	{
-		if( rowBounce[player][x] == -1 )
-		{
-			for( y=0; y<kGridDown; y++ )
-			{
-				if( glow[player][x][y] && grid[player][x][y] != kEmpty )
-				{
-					CalcBlobRect( x, y, &myRect );	
-
-					color = grid[player][x][y];
-					suck = suction[player][x][y];
-					
-					SurfaceDrawBlob( player, &myRect, color, suck, charred[player][x][y] );
-					SurfaceDrawColor( &myRect, color, suck, glowColors[color][0], glowColors[color][1], glowColors[color][2], glowArray[hintGlow] );
-					CleanSpriteArea( player, &myRect );
-				}
-			}
-		}
-	}
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-}
-
-void FadeCharred( int player )
-{
-	int x, y;
-	MRect myRect;
-	
-	if( GameTickCount() < fadeCharTime[player] || role[player] != kFalling ) return;
-	
-	fadeCharTime[player] += 135;
-	
-	SDLU_AcquireSurface( playerSurface[player] ); 
-	
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=0; y<kGridDown; y++ )
-		{
-			if( charred[player][x][y] & 0xF0 )
-			{
-				charred[player][x][y] = ( charred[player][x][y] & 0x0F ) | ( ( charred[player][x][y] & 0xF0 ) - 0x10 );
-				
-				if( rowBounce[player][x] == -1 )
-				{
-					CalcBlobRect( x, y, &myRect );
-					SurfaceDrawBlob( player, &myRect, grid[player][x][y], suction[player][x][y], charred[player][x][y] );
-					CleanSpriteArea( player, &myRect );
-				}
-			}
-		}
-	}
-
-	SDLU_ReleaseSurface( playerSurface[player] ); 
-}
-
-void BlinkBored( int player )
-{
-	MRect myRect;
-	int which, x, y, count;
-	
-	if( GameTickCount() < boredTime[player] )
-		return;
-	
-	SDLU_AcquireSurface( playerSurface[player] ); 
-	
-	// undo any previously bored blobs
-	
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=0; y<kGridDown; y++ )
-		{
-			if( ( rowBounce[player][x] == -1 ) &&
-				( suction[player][x][y] == kBlinkBlob || suction[player][x][y] == kSobBlob ) )
-			{
-				suction[player][x][y] = kNoSuction;
-				
-				CalcBlobRect( x, y, &myRect );
-				SurfaceDrawBlob( player, &myRect, grid[player][x][y],
-								   suction[player][x][y],
-								   charred[player][x][y] );
-				CleanSpriteArea( player, &myRect );
-			}				
-		}
-	}
-		
-	// make some boredom
-
-	which = RandomBefore( 2 );
-	boredTime[player] += which? 15: 80;
-	which = which? kBlinkBlob: kSobBlob;
-
-	for( count=0; count<5; count++ )
-	{
-		x = RandomBefore( kGridAcross );
-		y = RandomBefore( kGridDown );
-		
-		if( rowBounce[player][x] == -1 &&
-			suction[player][x][y] == kNoSuction &&
-			grid[player][x][y] >= kFirstBlob &&
-			grid[player][x][y] <= kLastBlob   
-		  ) 
-		{
-			suction[player][x][y] = which;
-			
-			CalcBlobRect( x, y, &myRect );
-			SurfaceDrawBlob( player, &myRect,
-					    grid[player][x][y],
-					    suction[player][x][y],
-					    charred[player][x][y] );
-			CleanSpriteArea( player, &myRect );
-		}
-	}
-
-	SDLU_ReleaseSurface( playerSurface[player] ); 
-}
-
-void InitPlayers( void )
-{
-	const double windowLoc[ ] = { kLeftPlayerWindowCenter, kRightPlayerWindowCenter };
-	
-	playerWindowZRect.top = playerWindowZRect.left = 0;
-	playerWindowZRect.bottom = 288; playerWindowZRect.right = 144;
-	
-	playerWindowRect[0] = playerWindowRect[1] = playerWindowZRect;
-	CenterRectOnScreen( &playerWindowRect[0], windowLoc[0], 0.5 );
-	CenterRectOnScreen( &playerWindowRect[1], windowLoc[1], 0.5 );
-}
--- /dev/null
+++ b/src/prefs.c
@@ -1,0 +1,103 @@
+// prefs.c
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "main.h"
+#include "prefs.h"
+#include "music.h"
+#include "soundfx.h"
+#include "hiscore.h"
+#include "keyselect.h"
+
+typedef struct Preference
+{
+    const char*   keyName;
+    void*         valuePtr;
+    unsigned int  valueLength;
+} Preference;
+
+Preference prefList[] =
+{
+    { "MusicOn",            &musicOn,           sizeof(MBoolean         ) },
+    { "SoundOn",            &soundOn,           sizeof(MBoolean         ) },
+    { "KeyBindings",        playerKeys,         sizeof(playerKeys       ) },
+    { "HighScores",         scores,             sizeof(scores           ) },
+    { "BestCombo",          &best,              sizeof(best             ) },
+    { "Fullscreen",         &fullscreen,        sizeof(fullscreen       ) },
+    { "Widescreen",         &widescreen,        sizeof(widescreen       ) },
+    { "CrispUpscaling",     &crispUpscaling,    sizeof(crispUpscaling   ) },
+};
+
+static FILE* GetPrefsStream(const char* openmode)
+{
+    static char path[1024];
+    const char* userDir = SDL_GetPrefPath(NULL, "CandyCrisis");
+    snprintf(path, sizeof(path), "%sCandyCrisisPrefs.bin", userDir);
+    return fopen(path, openmode);
+}
+
+void LoadPrefs()
+{
+    FILE* stream = GetPrefsStream("rb");
+    if (!stream)
+    {
+        return;
+    }
+
+    for (int i = 0; i < arrsize(prefList); i++)
+    {
+        Preference* pref = &prefList[i];
+        
+        fseek(stream, 0, SEEK_SET);
+
+        while (!feof(stream))
+        {
+            int keyLength;
+            char key[256];
+            unsigned int contentsLength;
+
+            keyLength = fgetc(stream);
+            if (keyLength < 0 || feof(stream))
+                break;
+            fread(key, keyLength, 1, stream);
+            key[keyLength] = '\0';
+            fread(&contentsLength, sizeof(contentsLength), 1, stream);
+
+            if (!strncmp(key, pref->keyName, strlen(pref->keyName)))
+            {
+                if (contentsLength != pref->valueLength)
+                    break;
+                fread(pref->valuePtr, pref->valueLength, 1, stream);
+                break;
+            }
+            else
+            {
+                fseek(stream, contentsLength, SEEK_CUR);
+            }
+        }
+    }
+
+    fclose(stream);
+}
+
+void SavePrefs()
+{
+    FILE* stream = GetPrefsStream("wb");
+    if (!stream)
+    {
+        return;
+    }
+
+    for (int i = 0; i < arrsize(prefList); i++)
+    {
+        const Preference* pref = &prefList[i];
+        fputc(strlen(pref->keyName), stream);
+        fwrite(pref->keyName, strlen(pref->keyName), 1, stream);
+        fwrite(&pref->valueLength, sizeof(pref->valueLength), 1, stream);
+        fwrite(pref->valuePtr, pref->valueLength, 1, stream);
+    }
+    
+    fclose(stream);
+}
--- a/src/prefs.cpp
+++ /dev/null
@@ -1,114 +1,0 @@
-// prefs.c
-
-#include <fstream>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "main.h"
-#include "prefs.h"
-#include "music.h"
-#include "soundfx.h"
-#include "hiscore.h"
-#include "keyselect.h"
-
-struct Preference
-{
-    const char*   keyName;
-    void*         valuePtr;
-    unsigned int  valueLength;
-};
-
-Preference prefList[] =
-{
-    { "MusicOn",            &musicOn,           sizeof(MBoolean         ) },
-    { "SoundOn",            &soundOn,           sizeof(MBoolean         ) },
-    { "KeyBindings",        playerKeys,         sizeof(playerKeys       ) },
-    { "HighScores",         scores,             sizeof(scores           ) },
-    { "BestCombo",          &best,              sizeof(best             ) },
-    { "Fullscreen",         &fullscreen,        sizeof(fullscreen       ) },
-    { "Widescreen",         &widescreen,        sizeof(widescreen       ) },
-    { "CrispUpscaling",     &crispUpscaling,    sizeof(crispUpscaling   ) },
-};
-
-static std::fstream GetPrefsStream(std::ios::openmode openmode)
-{
-    static char path[1024];
-    const char* userDir = SDL_GetPrefPath(NULL, "CandyCrisis");
-    snprintf(path, sizeof(path), "%sCandyCrisisPrefs.bin", userDir);
-
-    return std::fstream(path, std::ios::binary | openmode);
-}
-
-void LoadPrefs()
-{
-    std::fstream stream;
-    try
-    {
-        stream = GetPrefsStream(std::ios::in);
-    }
-    catch (...)
-    {
-        return;
-    }
-
-    if (!stream.good())
-    {
-        return;
-    }
-
-    for (Preference& pref: prefList)
-    {
-        stream.seekg(0, std::ios::beg);
-        while (stream.good() && !stream.eof())
-        {
-            int keyLength;
-            char key[256];
-            unsigned int contentsLength;
-
-            keyLength = stream.get();
-            if (stream.eof()) break;
-            stream.read(key, keyLength);
-            key[keyLength] = '\0';
-            stream.read((char*)&contentsLength, sizeof(contentsLength));
-
-            if (!strncmp(key, pref.keyName, strlen(pref.keyName)))
-            {
-                if (contentsLength != pref.valueLength)
-                    break;
-                stream.read((char*) pref.valuePtr, pref.valueLength);
-                break;
-            }
-            else
-            {
-                stream.seekg(contentsLength, std::ios::cur);
-            }
-        }
-    }
-}
-
-void SavePrefs()
-{
-    std::fstream stream;
-    try
-    {
-        stream = GetPrefsStream(std::ios::out);
-    }
-    catch (...)
-    {
-        return;
-    }
-
-    if (!stream.good())
-    {
-        return;
-    }
-
-    for (Preference& pref: prefList)
-    {
-        stream.put(strlen(pref.keyName));
-        stream.write(pref.keyName, strlen(pref.keyName));
-        stream.write((const char*)&pref.valueLength, sizeof(pref.valueLength));
-        stream.write((const char*)pref.valuePtr, pref.valueLength);
-    }
-}
--- /dev/null
+++ b/src/random.c
@@ -1,0 +1,109 @@
+// random.c
+
+#include <stdlib.h>
+
+#include "main.h"
+#include "random.h"
+
+unsigned int  randomSeed[2], pieceCount[2], grenadeTimer[2];
+int pieceMap[kBlobTypes], numPieces;
+
+
+static unsigned int  internalRandomSeed = 1;
+
+static int internalRandom() // uses a generic rand() algorithm
+{
+	internalRandomSeed = internalRandomSeed * 1103515245 + 12345;
+	return ((internalRandomSeed >> 16) & 0x7FFF);
+}
+
+
+void InitRandom( int inNumPieces )
+{
+	int count, swap, swapWith;
+	
+	numPieces = inNumPieces;
+	randomSeed[0] = randomSeed[1] = SDL_GetTicks();
+	pieceCount[0] = pieceCount[1] = 0;
+	grenadeTimer[0] = grenadeTimer[1] = 40;
+	
+	for( count=0; count<kBlobTypes; count++ )
+	{
+		pieceMap[count] = count+1;
+	}
+
+	for( count=0; count<kBlobTypes; count++ )
+	{
+		swapWith = RandomBefore( kBlobTypes );
+		swap = pieceMap[swapWith];
+		pieceMap[swapWith] = pieceMap[count];
+		pieceMap[count] = swap;
+	}
+}
+
+void AddExtraPiece( void )
+{
+	numPieces++;
+}
+
+int GetPiece( int player )
+{
+	int result;
+	unsigned int  realSeed;
+	
+	realSeed = internalRandomSeed;
+	
+	internalRandomSeed = randomSeed[player];
+	result = pieceMap[RandomBefore(numPieces)];
+	randomSeed[player] = internalRandomSeed;
+	
+	internalRandomSeed = realSeed;
+
+	return result;
+}
+
+int GetMagic( int player )
+{
+	int result;
+	int realSeed;
+	
+	realSeed = internalRandomSeed;
+	
+	internalRandomSeed = randomSeed[player];
+	result = (RandomBefore(19) == 0)? true: false;
+	randomSeed[player] = internalRandomSeed;
+	
+	internalRandomSeed = realSeed;
+
+	return result;
+}
+
+int GetGrenade( int player )
+{
+	pieceCount[player]++;
+	if( pieceCount[player] == grenadeTimer[player] )
+	{
+		grenadeTimer[player] += grenadeTimer[player] * 3 / 2;
+		return true;
+	}
+	else
+	{
+		return false;
+	}
+}
+
+static inline int RandomRange( double min, double max )
+{
+	const double kMinRand = 0.0;
+	const double kMaxRand = 32767.0;
+	double x;
+	
+	x = (internalRandom() - kMinRand) / (kMaxRand - kMinRand + 1.0);
+	return (int) (x * (max + 1.0 - min) + min);
+}
+
+int RandomBefore( int what )
+{	
+	return RandomRange( 0.0, what-1 );
+}
+
--- a/src/random.cpp
+++ /dev/null
@@ -1,109 +1,0 @@
-// random.c
-
-#include <stdlib.h>
-
-#include "main.h"
-#include "random.h"
-
-unsigned int  randomSeed[2], pieceCount[2], grenadeTimer[2];
-int pieceMap[kBlobTypes], numPieces;
-
-
-static unsigned int  internalRandomSeed = 1;
-
-static int internalRandom() // uses a generic rand() algorithm
-{
-	internalRandomSeed = internalRandomSeed * 1103515245 + 12345;
-	return ((internalRandomSeed >> 16) & 0x7FFF);
-}
-
-
-void InitRandom( int inNumPieces )
-{
-	int count, swap, swapWith;
-	
-	numPieces = inNumPieces;
-	randomSeed[0] = randomSeed[1] = SDL_GetTicks();
-	pieceCount[0] = pieceCount[1] = 0;
-	grenadeTimer[0] = grenadeTimer[1] = 40;
-	
-	for( count=0; count<kBlobTypes; count++ )
-	{
-		pieceMap[count] = count+1;
-	}
-
-	for( count=0; count<kBlobTypes; count++ )
-	{
-		swapWith = RandomBefore( kBlobTypes );
-		swap = pieceMap[swapWith];
-		pieceMap[swapWith] = pieceMap[count];
-		pieceMap[count] = swap;
-	}
-}
-
-void AddExtraPiece( void )
-{
-	numPieces++;
-}
-
-int GetPiece( int player )
-{
-	int result;
-	unsigned int  realSeed;
-	
-	realSeed = internalRandomSeed;
-	
-	internalRandomSeed = randomSeed[player];
-	result = pieceMap[RandomBefore(numPieces)];
-	randomSeed[player] = internalRandomSeed;
-	
-	internalRandomSeed = realSeed;
-
-	return result;
-}
-
-int GetMagic( int player )
-{
-	int result;
-	int realSeed;
-	
-	realSeed = internalRandomSeed;
-	
-	internalRandomSeed = randomSeed[player];
-	result = (RandomBefore(19) == 0)? true: false;
-	randomSeed[player] = internalRandomSeed;
-	
-	internalRandomSeed = realSeed;
-
-	return result;
-}
-
-int GetGrenade( int player )
-{
-	pieceCount[player]++;
-	if( pieceCount[player] == grenadeTimer[player] )
-	{
-		grenadeTimer[player] += grenadeTimer[player] * 3 / 2;
-		return true;
-	}
-	else
-	{
-		return false;
-	}
-}
-
-static inline int RandomRange( double min, double max )
-{
-	const double kMinRand = 0.0;
-	const double kMaxRand = 32767.0;
-	double x;
-	
-	x = (internalRandom() - kMinRand) / (kMaxRand - kMinRand + 1.0);
-	return (int) (x * (max + 1.0 - min) + min);
-}
-
-int RandomBefore( int what )
-{	
-	return RandomRange( 0.0, what-1 );
-}
-
--- /dev/null
+++ b/src/score.c
@@ -1,0 +1,154 @@
+// score.c
+
+#include "SDLU.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include "main.h"
+#include "score.h"
+#include "gworld.h"
+#include "graphics.h"
+#include "blitter.h"
+#include "hiscore.h"
+#include "gameticks.h"
+#include "level.h"
+
+SDL_Surface* scoreSurface;
+SDL_Surface* numberSurface;
+SDL_Surface* numberMaskSurface;
+
+
+MRect scoreWindowZRect, scoreWindowRect[2];
+MBoolean scoreWindowVisible[2] = {true, true};
+int roundStartScore[2], score[2], displayedScore[2];
+MTicks scoreTime[2];
+const char characterList[] = 
+{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
+  'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+  'U', 'V', 'W', 'X', 'Y', 'Z', '.', '0', '1', '2', 
+  '3', '4', '5', '6', '7', '8', '9', '!', '"', '#',
+  '$' };
+
+
+void InitScore( void )
+{
+	const double windowLoc[ ] = { 0.16, 0.84 };
+	SDL_Rect     sdlRect;
+	
+	scoreWindowZRect.top = scoreWindowZRect.left = 0;
+	scoreWindowZRect.bottom = 32; scoreWindowZRect.right = 144;
+	
+	scoreWindowRect[0] = scoreWindowRect[1] = scoreWindowZRect;
+	CenterRectOnScreen( &scoreWindowRect[0], windowLoc[0], 0.89 );
+	CenterRectOnScreen( &scoreWindowRect[1], windowLoc[1], 0.89 );
+	
+	scoreSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &scoreWindowZRect, &sdlRect ), 32 );
+	DrawPICTInSurface( scoreSurface, picBoard );
+	
+	numberSurface = LoadPICTAsSurface( picNumber, 32 );
+
+	numberMaskSurface = LoadPICTAsSurface( picNumberMask, MASK_DEPTH );
+	
+	displayedScore[0] = displayedScore[1] = 0;
+	score[0]          = score[1]          = 0;
+	scoreTime[0]      = scoreTime[1]      = 0;
+}
+
+void UpdateScore( int player )
+{
+	if( GameTickCount( ) >= scoreTime[player] )
+	{		
+		scoreTime[player] = GameTickCount() + 1;
+		
+		if( displayedScore[player] < score[player] )
+		{
+			if( (score[player] - displayedScore[player]) > 5000 )
+			{
+				displayedScore[player] += 1525;
+			}
+			else if( (score[player] - displayedScore[player]) > 1000 )
+			{
+				displayedScore[player] += 175;
+			}
+			else
+			{
+				displayedScore[player] += 25;
+			}
+			
+			if( displayedScore[player] > score[player] )
+				displayedScore[player] = score[player];
+			
+			ShowScore( player );
+		}
+	}
+}
+
+void ShowScore( int player )
+{
+	SDL_Rect   sourceSDLRect, destSDLRect;
+	MRect      myRect;
+	char       myString[256];
+	int        count;
+	
+	if( !scoreWindowVisible[player] ) return;
+	
+	if( control[player] != kNobodyControl )
+	{
+		sprintf( myString, "%d", displayedScore[player] );
+				
+		SDLU_AcquireSurface( scoreSurface );
+		
+		SDLU_BlitSurface( boardSurface[player], &scoreSurface->clip_rect,
+				 		  scoreSurface,         &scoreSurface->clip_rect   );
+		
+		myRect.top = 0;
+		myRect.left = 2;
+		myRect.bottom = kNumberVertSize;
+		myRect.right = myRect.left + kNumberHorizSize;
+		DrawCharacter( kCharacterScore,   &myRect );
+		OffsetMRect( &myRect, kNumberHorizSize, 0 );
+		DrawCharacter( kCharacterScore+1, &myRect );
+
+		myRect = scoreWindowZRect;
+		myRect.right -= 2;
+		myRect.left = myRect.right - kNumberHorizSize;
+		for( count = (int) strlen(myString) - 1; count >= 0; count-- )
+		{
+			DrawCharacter( myString[count], &myRect );
+			OffsetMRect( &myRect, -kNumberHorizSize - 1, 0 );
+		}
+		
+		SDLU_ReleaseSurface( scoreSurface );
+		
+		SDLU_BlitFrontSurface( scoreSurface, 
+		                       SDLU_MRectToSDLRect( &scoreWindowZRect, &sourceSDLRect ),
+		                       SDLU_MRectToSDLRect( &scoreWindowRect[player], &destSDLRect ) );
+	}
+}
+
+void DrawCharacter( char which, const MRect *myRect )
+{
+	MRect   srcRect;
+	int     count, result;
+	
+	result = -1;
+	for( count = 0; count < arrsize(characterList); count++ )
+	{
+		if( characterList[count] == which ) 
+		{
+			result = count;
+			break;
+		}
+	}
+	
+	if( result == -1 ) return;
+	
+	srcRect.top    = 0;
+	srcRect.left   = result * kNumberHorizSize;
+	srcRect.bottom = kNumberVertSize;
+	srcRect.right  = srcRect.left + kNumberHorizSize;
+	
+	SurfaceBlitMask(  numberSurface,  numberMaskSurface,  SDLU_GetCurrentSurface(),
+			         &srcRect,       &srcRect,            myRect );
+}
--- a/src/score.cpp
+++ /dev/null
@@ -1,154 +1,0 @@
-// score.c
-
-#include "SDLU.h"
-
-#include <stdio.h>
-#include <string.h>
-
-#include "main.h"
-#include "score.h"
-#include "gworld.h"
-#include "graphics.h"
-#include "blitter.h"
-#include "hiscore.h"
-#include "gameticks.h"
-#include "level.h"
-
-SDL_Surface* scoreSurface;
-SDL_Surface* numberSurface;
-SDL_Surface* numberMaskSurface;
-
-
-MRect scoreWindowZRect, scoreWindowRect[2];
-MBoolean scoreWindowVisible[2] = {true, true};
-int roundStartScore[2], score[2], displayedScore[2];
-MTicks scoreTime[2];
-const char characterList[] = 
-{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
-  'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
-  'U', 'V', 'W', 'X', 'Y', 'Z', '.', '0', '1', '2', 
-  '3', '4', '5', '6', '7', '8', '9', '!', '"', '#',
-  '$' };
-
-
-void InitScore( void )
-{
-	const double windowLoc[ ] = { 0.16, 0.84 };
-	SDL_Rect     sdlRect;
-	
-	scoreWindowZRect.top = scoreWindowZRect.left = 0;
-	scoreWindowZRect.bottom = 32; scoreWindowZRect.right = 144;
-	
-	scoreWindowRect[0] = scoreWindowRect[1] = scoreWindowZRect;
-	CenterRectOnScreen( &scoreWindowRect[0], windowLoc[0], 0.89 );
-	CenterRectOnScreen( &scoreWindowRect[1], windowLoc[1], 0.89 );
-	
-	scoreSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &scoreWindowZRect, &sdlRect ), 32 );
-	DrawPICTInSurface( scoreSurface, picBoard );
-	
-	numberSurface = LoadPICTAsSurface( picNumber, 32 );
-
-	numberMaskSurface = LoadPICTAsSurface( picNumberMask, MASK_DEPTH );
-	
-	displayedScore[0] = displayedScore[1] = 0;
-	score[0]          = score[1]          = 0;
-	scoreTime[0]      = scoreTime[1]      = 0;
-}
-
-void UpdateScore( int player )
-{
-	if( GameTickCount( ) >= scoreTime[player] )
-	{		
-		scoreTime[player] = GameTickCount() + 1;
-		
-		if( displayedScore[player] < score[player] )
-		{
-			if( (score[player] - displayedScore[player]) > 5000 )
-			{
-				displayedScore[player] += 1525;
-			}
-			else if( (score[player] - displayedScore[player]) > 1000 )
-			{
-				displayedScore[player] += 175;
-			}
-			else
-			{
-				displayedScore[player] += 25;
-			}
-			
-			if( displayedScore[player] > score[player] )
-				displayedScore[player] = score[player];
-			
-			ShowScore( player );
-		}
-	}
-}
-
-void ShowScore( int player )
-{
-	SDL_Rect   sourceSDLRect, destSDLRect;
-	MRect      myRect;
-	char       myString[256];
-	int        count;
-	
-	if( !scoreWindowVisible[player] ) return;
-	
-	if( control[player] != kNobodyControl )
-	{
-		sprintf( myString, "%d", displayedScore[player] );
-				
-		SDLU_AcquireSurface( scoreSurface );
-		
-		SDLU_BlitSurface( boardSurface[player], &scoreSurface->clip_rect,
-				 		  scoreSurface,         &scoreSurface->clip_rect   );
-		
-		myRect.top = 0;
-		myRect.left = 2;
-		myRect.bottom = kNumberVertSize;
-		myRect.right = myRect.left + kNumberHorizSize;
-		DrawCharacter( kCharacterScore,   &myRect );
-		OffsetMRect( &myRect, kNumberHorizSize, 0 );
-		DrawCharacter( kCharacterScore+1, &myRect );
-
-		myRect = scoreWindowZRect;
-		myRect.right -= 2;
-		myRect.left = myRect.right - kNumberHorizSize;
-		for( count = int(strlen(myString)) - 1; count >= 0; count-- )
-		{
-			DrawCharacter( myString[count], &myRect );
-			OffsetMRect( &myRect, -kNumberHorizSize - 1, 0 );
-		}
-		
-		SDLU_ReleaseSurface( scoreSurface );
-		
-		SDLU_BlitFrontSurface( scoreSurface, 
-		                       SDLU_MRectToSDLRect( &scoreWindowZRect, &sourceSDLRect ),
-		                       SDLU_MRectToSDLRect( &scoreWindowRect[player], &destSDLRect ) );
-	}
-}
-
-void DrawCharacter( char which, const MRect *myRect )
-{
-	MRect   srcRect;
-	int     count, result;
-	
-	result = -1;
-	for( count = 0; count < arrsize(characterList); count++ )
-	{
-		if( characterList[count] == which ) 
-		{
-			result = count;
-			break;
-		}
-	}
-	
-	if( result == -1 ) return;
-	
-	srcRect.top    = 0;
-	srcRect.left   = result * kNumberHorizSize;
-	srcRect.bottom = kNumberVertSize;
-	srcRect.right  = srcRect.left + kNumberHorizSize;
-	
-	SurfaceBlitMask(  numberSurface,  numberMaskSurface,  SDLU_GetCurrentSurface(),
-			         &srcRect,       &srcRect,            myRect );
-}
--- a/src/soundfx.cpp
+++ b/src/soundfx.cpp
@@ -1,13 +1,16 @@
 // soundfx.c
 
-#include "main.h"
-#include "soundfx.h"
-#include "music.h"
-
 #include "support/cmixer.h"
 #include <stdio.h>
 
-MBoolean soundOn = true;
+extern "C"
+{
+    #include "main.h"
+    #include "soundfx.h"
+    #include "music.h"
+
+    MBoolean soundOn = true;
+}
 
 static std::vector<cmixer::WavStream> s_soundBank;
 static constexpr float k_playerStereoSeparation = 0.5f;
--- /dev/null
+++ b/src/tutorial.c
@@ -1,0 +1,500 @@
+// tutorial.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "tutorial.h"
+#include "level.h"
+#include "font.h"
+#include "pause.h"
+#include "gworld.h"
+#include "graphics.h"
+#include "control.h"
+#include "blitter.h"
+#include "gameticks.h"
+#include "soundfx.h"
+#include "opponent.h"
+#include "keyselect.h"
+
+#include <string.h>
+#include <stdio.h>
+
+AutoPattern tutorialPattern[] =
+{
+	{ kMessage,        0,   0,   "Welcome to the\nCandy Crisis\ntutorial!" },
+	{ kIdleTicks,      180, 0,   NULL },
+	{ kMessage,        0,   0,   "I'll be your guide\nthroughout the\ntutorial. Let's\nget started!" },
+	{ kRetrieve,       1,   1,   NULL },
+	{ kIdleTicks,      240, 0,   NULL },	
+	{ kMessage,        0,   0,   "When you start the\ngame, you'll find a\npair of candies\nfalling from the sky." },
+	{ kIdleTicks,      240, 0,   NULL },	
+	{ kMessage,        0,   0,   "Your goal is to\nkeep these candies\nfrom overflowing the\nboard!" },
+	{ kBlockUntilLand, 0,   0,   NULL },
+	{ kRetrieve,       2,   2,   NULL },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kMessage,        0,   0,   "You control the\ncandy by moving\nthem left and right." },
+	{ kIdleTicks,      120, 0,   NULL },
+	{ kPosition,       0,   0,   NULL },
+	{ kIdleTicks,      120, 0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kMessage,        0,   0,   "Press '~~'\nto make the pieces\nmove left." },
+	{ kRetrieve,       3,   3,   NULL },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kPosition,       5,   0,   NULL },
+	{ kIdleTicks,      90,  0,   NULL },
+	{ kMessage,        0,   0,  "Use '||' to\nmake the pieces\nmove right." },
+	{ kIdleTicks,      180, 0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kMessage,        0,   0,   "You can also\nmake the pieces\nrotate around each\nother." },
+	{ kRetrieve,       4,   3,   NULL },
+	{ kIdleTicks,      180, 0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kMessage,        0,   0,   "Press '{{'\nto make the pieces\nrotate." },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       5,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kMessage,        0,   0,   "Also, '``'\ncauses the candy\nto drop faster." },
+	{ kIdleTicks,      180, 0,   NULL },	
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kMessage,        0,   0,   "To pause the game\nor adjust settings,\npress 'esc.'" },
+	{ kIdleTicks,      280, 0,   NULL },	
+	{ kMessage,        0,   0,   "The candy in\nthis game is made\nfrom a highly\nunstable substance!" },
+	{ kRetrieve,       2,   2,   NULL },
+	{ kIdleTicks,      200, 0,   NULL },
+	{ kMessage,        0,   0,   "So when four\npieces of the same\ncolor come into\ncontact..." },
+	{ kPosition,       1,   0,   NULL },
+	{ kIdleTicks,      180, 0,   NULL },
+	{ kMessage,        0,   0,   "... they vaporize!" },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kMessage,        0,   0,   "Let's see that\nonce again." },
+	{ kRetrieve,       1,   1,   NULL },
+	{ kIdleTicks,      120, 0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kMessage,        0,   0,   "Pop!" },
+	{ kIdleTicks,      120, 0,   NULL },	
+	{ kMessage,        0,   0,   "You can even get\nfive or more\npieces to pop\nall at the same\ntime!" },
+	{ kRetrieve,       4,   4,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kPosition,       4,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       4,   4,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kPosition,       3,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       4,   4,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       4,   0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kMessage,        0,   0,   "Pop!\n\nTechniques like this\ncan earn lots of\nbonus points." },
+	{ kIdleTicks,      180, 0,   NULL },
+	{ kMessage,        0,   0,   "You can also pop\nmore than one color\nat once." },
+	{ kRetrieve,       5,   5,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       4,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       6,   5,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       3,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       3,   5,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       5,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kMessage,        0,   0,   "All right!" },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       0,   6,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kMessage,        0,   0,   "You can even set\nup devastating\nchain reactions..." },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       0,   6,   NULL },
+	{ kIdleTicks,      90,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       6,   6,   NULL },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       1,   0,   NULL },
+	{ kMessage,        0,   0,   "... like this!" },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kMessage,        0,   0,   "Tricky, isn't it?" },
+	{ kRetrieve,       1,   2,   NULL },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       1,   2,   NULL },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       3,   1,   NULL },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       3,   2,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       3,   0,   NULL },
+	{ kMessage,        0,   0,  "Let's see one more\nexample of a chain\nreaction." },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kRetrieve,       1,   2,   NULL },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       5,   5,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       1,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       5,   3,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kPosition,       1,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       3,   5,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       0,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       3,   3,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kPosition,       0,   0,   NULL },
+	{ kIdleTicks,      20,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kIdleTicks,      90,  0,   NULL }, 
+	{ kMessage,        0,   0,   "There's one more\nthing you need to\nknow about..." },
+	{ kIdleTicks,      180, 0,   NULL },
+	{ kMessage,        0,   0,   "Watch out for the\nsee-through candy!" },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kPunish,         18,  0,   NULL },
+	{ kRetrieve,       1,   1,   NULL },
+	{ kIdleTicks,      120, 0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kPosition,       0,   0,   NULL },
+	{ kIdleTicks,      20,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kMessage,        0,   0,   "When your opponent\nvaporizes a group of\ncandies, they also\nsend some transparent\npieces to you!" },
+	{ kRetrieve,       1,   1,   NULL },
+	{ kIdleTicks,      90,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kPosition,       4,   0,   NULL },
+	{ kIdleTicks,      30,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       1,   1,   NULL },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      90,  0,   NULL },
+	{ kMessage,        0,   0,   "You can get rid of\nthese pieces by\nvaporizing something\nnext to them." },
+	{ kIdleTicks,      150, 0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kRetrieve,       1,   2,   NULL },
+	{ kMessage,        0,   0,   "There are also\nsome bonus items\nwhich can come\nin handy." },
+	{ kIdleTicks,      20,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kPosition,       0,   0,   NULL },
+	{ kIdleTicks,      20,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       3,   1,   NULL },
+	{ kIdleTicks,      20,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kPosition,       0,   0,   NULL },
+	{ kIdleTicks,      20,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       1,   3,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kPosition,       1,   0,   NULL },
+	{ kIdleTicks,      15,  0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kMessage,        0,   0,   "One of these bonus\nitems is the Crazy\nCandy!" },
+	{ kRetrieve,       -1,  2,   NULL },
+	{ kIdleTicks,      15,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      10,  0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      100, 0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kMessage,        0,   0,   "The Crazy Candy\ntakes on the\ncolor of one of\nits neighbors." },
+	{ kIdleTicks,      60,  0,   NULL },
+	{ kRetrieve,       3,   4,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kPosition,       4,   0,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       1,   5,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kPosition,       1,   0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kMessage,        0,   0,   "Another useful bonus\nitem is the bomb!" },
+	{ kRetrieve,       2,   3,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kPosition,       0,   0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       0,   3,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       3,   2,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kPosition,       5,   0,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       4,   3,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kPosition,       3,   0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve,       2,   3,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kSpin,           0,   0,   NULL },
+	{ kIdleTicks,      5,   0,   NULL },
+	{ kPosition,       5,   0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },	
+	{ kRetrieve, kBombBottom, kBombTop, NULL },
+	{ kMessage,        0,   0,   "When the bomb lands\non one color\nof candy, all\npieces of that\ncolor will\nvaporize!" },
+	{ kIdleTicks,      100, 0,   NULL },
+	{ kPosition,       4,   0,   NULL },
+	{ kIdleTicks,      120, 0,   NULL },
+	{ kBlockUntilDrop, 0,   0,   NULL },
+	{ kIdleTicks,      200, 0,   NULL },
+	{ kMessage,        0,   0,   "Now you're ready\nto play Candy Crisis.\nGood luck!" },
+	{ kIdleTicks,      270, 0,   NULL },
+
+	{ kComplete,       0,   0,   NULL }
+};
+
+MRect           balloonRect = {0, 0, 208, 236};
+SkittlesFontPtr balloonFont;
+MPoint          balloonPt;
+char*           balloonChar;
+char            balloonMsg[256];
+MTicks          balloonTime, tutorialTime;
+SDL_Surface*    balloonSurface = NULL;
+
+void InitTutorial( void )
+{
+	// Balloon font
+	balloonFont = GetFont( picBalloonFont );
+	
+	// Balloon backbuffer
+	if( balloonSurface == NULL )
+	{
+		SDL_Rect surfaceRect = { 0, 0, backdropSurface->w, backdropSurface->h }; 
+		balloonSurface = SDLU_InitSurface( &surfaceRect, 32 );
+	}
+	
+	// Set up auto pattern
+	autoPattern = tutorialPattern;	
+	tutorialTime = 0;
+}
+
+void EndTutorial( void )
+{
+	QuickFadeOut( NULL );
+	
+	showStartMenu = true;
+}
+
+static int CalculateBalloonWidth( char *message )
+{
+	int maxWidth = 40;
+	int currentWidth = 0;
+	
+	for( ;; )
+	{
+		char in = *message++;
+		
+		switch(in)
+		{
+			case 0:
+				return (currentWidth > maxWidth)? currentWidth: maxWidth;
+				
+			case '\n':
+				maxWidth = (currentWidth > maxWidth)? currentWidth: maxWidth;
+				currentWidth = 0;
+				break;
+			
+			default:
+				currentWidth += balloonFont->width[(uint8_t) in];
+				break;
+		}
+	}
+}
+
+static int CalculateBalloonHeight( char *message )
+{
+	int lines = 2;
+	char *scan = message;
+	
+	while( *scan ) lines += (*scan++ == '\n');
+
+	return lines * 20;
+}
+
+void StopBalloon( void )
+{
+	balloonTime = 0x7FFFFFFF;
+}
+
+void StartBalloon( const char *message )
+{
+	MPoint      balloonTip, balloonFill;
+	int         replace;
+	const char* match[] = { "~~", "||", "``", "{{" };
+	char*       search;
+	SDL_Rect    balloonSDLRect, balloonContentsSDLRect;
+	MRect       balloonContentsRect;
+	
+	strcpy( balloonMsg, message );
+	for( replace=0; replace<4; replace++ )
+	{
+		search = strstr( balloonMsg, match[replace] );
+		if( search )
+		{
+			char temp[256];
+			
+			search[0] = '%';
+			search[1] = 's';
+			sprintf( temp, balloonMsg, SDL_GetKeyName( playerKeys[1][replace] ) );
+			strcpy( balloonMsg, temp );
+		}
+	}
+	
+	// Erase previous balloons
+	SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
+	SDLU_BlitFrontSurface( backdropSurface, &balloonSDLRect, &balloonSDLRect );
+
+	// Draw empty balloon outline
+	SDLU_AcquireSurface( balloonSurface );
+
+	balloonRect.left = balloonRect.right - 25 - CalculateBalloonWidth ( balloonMsg );
+	balloonRect.top = balloonRect.bottom - 25 - CalculateBalloonHeight( balloonMsg );
+
+	SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
+	SDLU_BlitSurface( backdropSurface, &balloonSDLRect,
+	                  balloonSurface,  &balloonSDLRect  );
+	
+	balloonContentsRect = balloonRect;
+	balloonContentsRect.bottom -= 25;
+		
+	SurfaceGetEdges( balloonSurface, &balloonContentsRect );
+	SDL_FillRect( balloonSurface, 
+				  SDLU_MRectToSDLRect( &balloonContentsRect, &balloonContentsSDLRect ), 
+				  SDL_MapRGB( balloonSurface->format, 0xFF, 0xFF, 0xFF ) );
+	SurfaceCurveEdges( balloonSurface, &balloonContentsRect );
+	
+	balloonTip.v = balloonContentsRect.bottom - 2;
+	balloonTip.h = balloonContentsRect.right - 60;
+	balloonFill = balloonTip;
+
+	SurfaceBlitCharacter( balloonFont, '\x01', &balloonFill,  0,   0,   0,  0 );
+	SurfaceBlitCharacter( balloonFont, '\x02', &balloonTip, 255, 255, 255,  0 );
+	
+	SDLU_ReleaseSurface( balloonSurface );
+
+	// Blit empty balloon to screen
+	SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
+	SDLU_BlitFrontSurface( balloonSurface, &balloonSDLRect, &balloonSDLRect );
+	
+	balloonPt.h = balloonRect.left + 10;
+	balloonPt.v = balloonRect.top + 10;
+	balloonChar = balloonMsg;
+	balloonTime = GameTickCount( );
+	
+	OpponentChatter( true );
+}
+
+void UpdateBalloon( void )
+{
+	SDL_Rect balloonSDLRect;
+	
+	if( control[0] != kAutoControl ) return;
+	if( GameTickCount() < balloonTime ) return;
+	
+	if( balloonChar )
+	{
+		unsigned char in = *balloonChar++;
+				
+		switch( in )
+		{
+			case 0:
+				OpponentChatter( false );
+				balloonChar = NULL;
+				balloonTime += 120;
+				break;
+				
+			case '\n':
+				balloonPt.h = balloonRect.left + 10;
+				balloonPt.v += 20;
+				break;
+				
+			default:
+				if( balloonFont->width[in] > 0 )
+				{
+					SDLU_AcquireSurface( balloonSurface );
+					SurfaceBlitCharacter( balloonFont, in, &balloonPt, 0, 0, 0, 0 );
+					SDLU_ReleaseSurface( balloonSurface );
+					
+					SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
+					SDLU_BlitFrontSurface( balloonSurface, &balloonSDLRect, &balloonSDLRect );
+
+					balloonTime += 2;
+				}
+				break;			
+		}	
+	}
+	else
+	{
+		SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
+		SDLU_BlitFrontSurface( backdropSurface, &balloonSDLRect, &balloonSDLRect );
+		                    
+		StopBalloon();
+	}
+}
--- a/src/tutorial.cpp
+++ /dev/null
@@ -1,500 +1,0 @@
-// tutorial.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "tutorial.h"
-#include "level.h"
-#include "font.h"
-#include "pause.h"
-#include "gworld.h"
-#include "graphics.h"
-#include "control.h"
-#include "blitter.h"
-#include "gameticks.h"
-#include "soundfx.h"
-#include "opponent.h"
-#include "keyselect.h"
-
-#include <string.h>
-#include <stdio.h>
-
-AutoPattern tutorialPattern[] =
-{
-	{ kMessage,        0,   0,   "Welcome to the\nCandy Crisis\ntutorial!" },
-	{ kIdleTicks,      180, 0,   NULL },
-	{ kMessage,        0,   0,   "I'll be your guide\nthroughout the\ntutorial. Let's\nget started!" },
-	{ kRetrieve,       1,   1,   NULL },
-	{ kIdleTicks,      240, 0,   NULL },	
-	{ kMessage,        0,   0,   "When you start the\ngame, you'll find a\npair of candies\nfalling from the sky." },
-	{ kIdleTicks,      240, 0,   NULL },	
-	{ kMessage,        0,   0,   "Your goal is to\nkeep these candies\nfrom overflowing the\nboard!" },
-	{ kBlockUntilLand, 0,   0,   NULL },
-	{ kRetrieve,       2,   2,   NULL },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kMessage,        0,   0,   "You control the\ncandy by moving\nthem left and right." },
-	{ kIdleTicks,      120, 0,   NULL },
-	{ kPosition,       0,   0,   NULL },
-	{ kIdleTicks,      120, 0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kMessage,        0,   0,   "Press '~~'\nto make the pieces\nmove left." },
-	{ kRetrieve,       3,   3,   NULL },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kPosition,       5,   0,   NULL },
-	{ kIdleTicks,      90,  0,   NULL },
-	{ kMessage,        0,   0,  "Use '||' to\nmake the pieces\nmove right." },
-	{ kIdleTicks,      180, 0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kMessage,        0,   0,   "You can also\nmake the pieces\nrotate around each\nother." },
-	{ kRetrieve,       4,   3,   NULL },
-	{ kIdleTicks,      180, 0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kMessage,        0,   0,   "Press '{{'\nto make the pieces\nrotate." },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       5,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kMessage,        0,   0,   "Also, '``'\ncauses the candy\nto drop faster." },
-	{ kIdleTicks,      180, 0,   NULL },	
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kMessage,        0,   0,   "To pause the game\nor adjust settings,\npress 'esc.'" },
-	{ kIdleTicks,      280, 0,   NULL },	
-	{ kMessage,        0,   0,   "The candy in\nthis game is made\nfrom a highly\nunstable substance!" },
-	{ kRetrieve,       2,   2,   NULL },
-	{ kIdleTicks,      200, 0,   NULL },
-	{ kMessage,        0,   0,   "So when four\npieces of the same\ncolor come into\ncontact..." },
-	{ kPosition,       1,   0,   NULL },
-	{ kIdleTicks,      180, 0,   NULL },
-	{ kMessage,        0,   0,   "... they vaporize!" },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kMessage,        0,   0,   "Let's see that\nonce again." },
-	{ kRetrieve,       1,   1,   NULL },
-	{ kIdleTicks,      120, 0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kMessage,        0,   0,   "Pop!" },
-	{ kIdleTicks,      120, 0,   NULL },	
-	{ kMessage,        0,   0,   "You can even get\nfive or more\npieces to pop\nall at the same\ntime!" },
-	{ kRetrieve,       4,   4,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kPosition,       4,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       4,   4,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kPosition,       3,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       4,   4,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       4,   0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kMessage,        0,   0,   "Pop!\n\nTechniques like this\ncan earn lots of\nbonus points." },
-	{ kIdleTicks,      180, 0,   NULL },
-	{ kMessage,        0,   0,   "You can also pop\nmore than one color\nat once." },
-	{ kRetrieve,       5,   5,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       4,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       6,   5,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       3,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       3,   5,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       5,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kMessage,        0,   0,   "All right!" },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       0,   6,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kMessage,        0,   0,   "You can even set\nup devastating\nchain reactions..." },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       0,   6,   NULL },
-	{ kIdleTicks,      90,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       6,   6,   NULL },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       1,   0,   NULL },
-	{ kMessage,        0,   0,   "... like this!" },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kMessage,        0,   0,   "Tricky, isn't it?" },
-	{ kRetrieve,       1,   2,   NULL },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       1,   2,   NULL },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       3,   1,   NULL },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       3,   2,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       3,   0,   NULL },
-	{ kMessage,        0,   0,  "Let's see one more\nexample of a chain\nreaction." },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kRetrieve,       1,   2,   NULL },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       5,   5,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       1,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       5,   3,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kPosition,       1,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       3,   5,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       0,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       3,   3,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kPosition,       0,   0,   NULL },
-	{ kIdleTicks,      20,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kIdleTicks,      90,  0,   NULL }, 
-	{ kMessage,        0,   0,   "There's one more\nthing you need to\nknow about..." },
-	{ kIdleTicks,      180, 0,   NULL },
-	{ kMessage,        0,   0,   "Watch out for the\nsee-through candy!" },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kPunish,         18,  0,   NULL },
-	{ kRetrieve,       1,   1,   NULL },
-	{ kIdleTicks,      120, 0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kPosition,       0,   0,   NULL },
-	{ kIdleTicks,      20,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kMessage,        0,   0,   "When your opponent\nvaporizes a group of\ncandies, they also\nsend some transparent\npieces to you!" },
-	{ kRetrieve,       1,   1,   NULL },
-	{ kIdleTicks,      90,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kPosition,       4,   0,   NULL },
-	{ kIdleTicks,      30,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       1,   1,   NULL },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      90,  0,   NULL },
-	{ kMessage,        0,   0,   "You can get rid of\nthese pieces by\nvaporizing something\nnext to them." },
-	{ kIdleTicks,      150, 0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kRetrieve,       1,   2,   NULL },
-	{ kMessage,        0,   0,   "There are also\nsome bonus items\nwhich can come\nin handy." },
-	{ kIdleTicks,      20,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kPosition,       0,   0,   NULL },
-	{ kIdleTicks,      20,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       3,   1,   NULL },
-	{ kIdleTicks,      20,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kPosition,       0,   0,   NULL },
-	{ kIdleTicks,      20,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       1,   3,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kPosition,       1,   0,   NULL },
-	{ kIdleTicks,      15,  0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kMessage,        0,   0,   "One of these bonus\nitems is the Crazy\nCandy!" },
-	{ kRetrieve,       -1,  2,   NULL },
-	{ kIdleTicks,      15,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      10,  0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      100, 0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kMessage,        0,   0,   "The Crazy Candy\ntakes on the\ncolor of one of\nits neighbors." },
-	{ kIdleTicks,      60,  0,   NULL },
-	{ kRetrieve,       3,   4,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kPosition,       4,   0,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       1,   5,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kPosition,       1,   0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kMessage,        0,   0,   "Another useful bonus\nitem is the bomb!" },
-	{ kRetrieve,       2,   3,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kPosition,       0,   0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       0,   3,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       3,   2,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kPosition,       5,   0,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       4,   3,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kPosition,       3,   0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve,       2,   3,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kSpin,           0,   0,   NULL },
-	{ kIdleTicks,      5,   0,   NULL },
-	{ kPosition,       5,   0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },	
-	{ kRetrieve, kBombBottom, kBombTop, NULL },
-	{ kMessage,        0,   0,   "When the bomb lands\non one color\nof candy, all\npieces of that\ncolor will\nvaporize!" },
-	{ kIdleTicks,      100, 0,   NULL },
-	{ kPosition,       4,   0,   NULL },
-	{ kIdleTicks,      120, 0,   NULL },
-	{ kBlockUntilDrop, 0,   0,   NULL },
-	{ kIdleTicks,      200, 0,   NULL },
-	{ kMessage,        0,   0,   "Now you're ready\nto play Candy Crisis.\nGood luck!" },
-	{ kIdleTicks,      270, 0,   NULL },
-
-	{ kComplete,       0,   0,   NULL }
-};
-
-MRect           balloonRect = {0, 0, 208, 236};
-SkittlesFontPtr balloonFont;
-MPoint          balloonPt;
-char*           balloonChar;
-char            balloonMsg[256];
-MTicks          balloonTime, tutorialTime;
-SDL_Surface*    balloonSurface = NULL;
-
-void InitTutorial( void )
-{
-	// Balloon font
-	balloonFont = GetFont( picBalloonFont );
-	
-	// Balloon backbuffer
-	if( balloonSurface == NULL )
-	{
-		SDL_Rect surfaceRect = { 0, 0, backdropSurface->w, backdropSurface->h }; 
-		balloonSurface = SDLU_InitSurface( &surfaceRect, 32 );
-	}
-	
-	// Set up auto pattern
-	autoPattern = tutorialPattern;	
-	tutorialTime = 0;
-}
-
-void EndTutorial( void )
-{
-	QuickFadeOut( NULL );
-	
-	showStartMenu = true;
-}
-
-static int CalculateBalloonWidth( char *message )
-{
-	int maxWidth = 40;
-	int currentWidth = 0;
-	
-	for( ;; )
-	{
-		char in = *message++;
-		
-		switch(in)
-		{
-			case 0:
-				return (currentWidth > maxWidth)? currentWidth: maxWidth;
-				
-			case '\n':
-				maxWidth = (currentWidth > maxWidth)? currentWidth: maxWidth;
-				currentWidth = 0;
-				break;
-			
-			default:
-				currentWidth += balloonFont->width[(uint8_t) in];
-				break;
-		}
-	}
-}
-
-static int CalculateBalloonHeight( char *message )
-{
-	int lines = 2;
-	char *scan = message;
-	
-	while( *scan ) lines += (*scan++ == '\n');
-
-	return lines * 20;
-}
-
-void StopBalloon( void )
-{
-	balloonTime = 0x7FFFFFFF;
-}
-
-void StartBalloon( const char *message )
-{
-	MPoint      balloonTip, balloonFill;
-	int         replace;
-	const char* match[] = { "~~", "||", "``", "{{" };
-	char*       search;
-	SDL_Rect    balloonSDLRect, balloonContentsSDLRect;
-	MRect       balloonContentsRect;
-	
-	strcpy( balloonMsg, message );
-	for( replace=0; replace<4; replace++ )
-	{
-		search = strstr( balloonMsg, match[replace] );
-		if( search )
-		{
-			char temp[256];
-			
-			search[0] = '%';
-			search[1] = 's';
-			sprintf( temp, balloonMsg, SDL_GetKeyName( playerKeys[1][replace] ) );
-			strcpy( balloonMsg, temp );
-		}
-	}
-	
-	// Erase previous balloons
-	SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
-	SDLU_BlitFrontSurface( backdropSurface, &balloonSDLRect, &balloonSDLRect );
-
-	// Draw empty balloon outline
-	SDLU_AcquireSurface( balloonSurface );
-
-	balloonRect.left = balloonRect.right - 25 - CalculateBalloonWidth ( balloonMsg );
-	balloonRect.top = balloonRect.bottom - 25 - CalculateBalloonHeight( balloonMsg );
-
-	SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
-	SDLU_BlitSurface( backdropSurface, &balloonSDLRect,
-	                  balloonSurface,  &balloonSDLRect  );
-	
-	balloonContentsRect = balloonRect;
-	balloonContentsRect.bottom -= 25;
-		
-	SurfaceGetEdges( balloonSurface, &balloonContentsRect );
-	SDL_FillRect( balloonSurface, 
-				  SDLU_MRectToSDLRect( &balloonContentsRect, &balloonContentsSDLRect ), 
-				  SDL_MapRGB( balloonSurface->format, 0xFF, 0xFF, 0xFF ) );
-	SurfaceCurveEdges( balloonSurface, &balloonContentsRect );
-	
-	balloonTip.v = balloonContentsRect.bottom - 2;
-	balloonTip.h = balloonContentsRect.right - 60;
-	balloonFill = balloonTip;
-
-	SurfaceBlitCharacter( balloonFont, '\x01', &balloonFill,  0,   0,   0,  0 );
-	SurfaceBlitCharacter( balloonFont, '\x02', &balloonTip, 255, 255, 255,  0 );
-	
-	SDLU_ReleaseSurface( balloonSurface );
-
-	// Blit empty balloon to screen
-	SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
-	SDLU_BlitFrontSurface( balloonSurface, &balloonSDLRect, &balloonSDLRect );
-	
-	balloonPt.h = balloonRect.left + 10;
-	balloonPt.v = balloonRect.top + 10;
-	balloonChar = balloonMsg;
-	balloonTime = GameTickCount( );
-	
-	OpponentChatter( true );
-}
-
-void UpdateBalloon( void )
-{
-	SDL_Rect balloonSDLRect;
-	
-	if( control[0] != kAutoControl ) return;
-	if( GameTickCount() < balloonTime ) return;
-	
-	if( balloonChar )
-	{
-		unsigned char in = *balloonChar++;
-				
-		switch( in )
-		{
-			case 0:
-				OpponentChatter( false );
-				balloonChar = NULL;
-				balloonTime += 120;
-				break;
-				
-			case '\n':
-				balloonPt.h = balloonRect.left + 10;
-				balloonPt.v += 20;
-				break;
-				
-			default:
-				if( balloonFont->width[in] > 0 )
-				{
-					SDLU_AcquireSurface( balloonSurface );
-					SurfaceBlitCharacter( balloonFont, in, &balloonPt, 0, 0, 0, 0 );
-					SDLU_ReleaseSurface( balloonSurface );
-					
-					SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
-					SDLU_BlitFrontSurface( balloonSurface, &balloonSDLRect, &balloonSDLRect );
-
-					balloonTime += 2;
-				}
-				break;			
-		}	
-	}
-	else
-	{
-		SDLU_MRectToSDLRect( &balloonRect, &balloonSDLRect );
-		SDLU_BlitFrontSurface( backdropSurface, &balloonSDLRect, &balloonSDLRect );
-		                    
-		StopBalloon();
-	}
-}
--- /dev/null
+++ b/src/tweak.c
@@ -1,0 +1,128 @@
+// tweak.c
+
+#include <math.h>
+
+#include "main.h"
+#include "tweak.h"
+#include "gworld.h"
+#include "moving.h"
+#include "gameticks.h"
+#include "graphics.h"
+#include "players.h"
+
+MTicks xTweakTime[2], yTweakTime[2], rTweakTime[2];
+int yTweak[2], xTweak[2], xDirection[2], rTweak[2], rDirection[2];
+int lastShadow[2];
+int tweakOffsetX[4][11], tweakOffsetY[4][11];
+
+void InitTweak( void )
+{
+	int rTweakValues[] = { 0, 5, 10, 30, 50, 70, 90, 110, 130, 150, 170 };
+	int count, rotate;
+	double tweakRad;
+	
+	for( rotate = 0; rotate<2; rotate++ )
+	{
+		for( count=0; count<=10; count++ )
+		{
+			tweakRad = d2r( (90*rotate) - rTweakValues[count] );
+			tweakOffsetX[rotate][count] = (int) floor( 0.5 + cos( tweakRad ) * kBlobHorizSize );
+			tweakOffsetY[rotate][count] = (int) floor( 0.5 + sin( tweakRad ) * kBlobVertSize  );
+
+			tweakOffsetX[rotate+2][count] = -tweakOffsetX[rotate][count];
+			tweakOffsetY[rotate+2][count] = -tweakOffsetY[rotate][count];
+		}
+	}
+}
+
+void TweakFirstBlob( int player, MRect *first )
+{
+	int tweakValues[] = {0, -1, -2, -3, -6, -12};
+	
+	if( xTweak[player] > 0 )
+	{
+		OffsetMRect( first, xDirection[player] * tweakValues[xTweak[player]], 0 );
+	}
+
+	if( yTweak[player] > 0 )
+	{
+		OffsetMRect( first, 0, tweakValues[yTweak[player]] );
+	}
+}
+
+void TweakSecondBlob( int player, MRect *second )
+{
+	int x, y;
+	
+	CalcSecondBlobOffset( player, &x, &y );
+	OffsetMRect( second,
+				 tweakOffsetX[blobR[player]][rTweak[player]],
+				 tweakOffsetY[blobR[player]][rTweak[player]] );
+}
+
+void StartTweak( int player, int direction, int rotate, int fall )
+{
+	if( fall != 0 )
+	{
+		yTweak[player] = 3;
+		yTweakTime[player] = GameTickCount() + kTweakDelay;
+	}
+
+	if( direction != 0 )
+	{
+		xDirection[player] = direction;
+		xTweak[player] = 5;
+		xTweakTime[player] = GameTickCount() + kTweakDelay;
+	}
+	
+	if( rotate != 0 )
+	{
+		rTweak[player] = rotate * 5; 
+		rDirection[player] = rotate;
+		rTweakTime[player] = GameTickCount() + kTweakDelay;
+	}
+}
+
+void UpdateTweak( int player, int animation )
+{
+	MBoolean isXTweaked, isYTweaked, isRTweaked, isAnimTweaked = false;
+	
+	if( GameTickCount( ) >= animTime[player] )
+	{
+		isAnimTweaked = true;
+		animTime[player] += 2;
+		anim[player]++;	
+			
+		HandleMagic( player );
+	}
+
+	isXTweaked = ( (GameTickCount() >= xTweakTime[player]) && (xTweak[player] > 0) );
+	isYTweaked = ( (GameTickCount() >= yTweakTime[player]) && (yTweak[player] > 0) );
+	isRTweaked = ( (GameTickCount() >= rTweakTime[player]) && (rTweak[player] > 0) );
+	
+	if( isXTweaked || isRTweaked || isYTweaked || 
+	    isAnimTweaked || (shadowDepth[player] != lastShadow[player]) )
+	{	
+		EraseSpriteBlobs( player );
+		
+		if( isXTweaked )
+		{
+			xTweak[player]--;
+			xTweakTime[player] += kTweakDelay;
+		}
+		
+		if( isYTweaked )
+		{
+			yTweak[player]--;
+			yTweakTime[player] += kTweakDelay;
+		}
+
+		if( isRTweaked )
+		{
+			rTweak[player]--;
+			rTweakTime[player] += kTweakDelay;		
+		}
+		
+		DrawSpriteBlobs( player, animation );
+	}
+}
--- a/src/tweak.cpp
+++ /dev/null
@@ -1,128 +1,0 @@
-// tweak.c
-
-#include <math.h>
-
-#include "main.h"
-#include "tweak.h"
-#include "gworld.h"
-#include "moving.h"
-#include "gameticks.h"
-#include "graphics.h"
-#include "players.h"
-
-MTicks xTweakTime[2], yTweakTime[2], rTweakTime[2];
-int yTweak[2], xTweak[2], xDirection[2], rTweak[2], rDirection[2];
-int lastShadow[2];
-int tweakOffsetX[4][11], tweakOffsetY[4][11];
-
-void InitTweak( void )
-{
-	int rTweakValues[] = { 0, 5, 10, 30, 50, 70, 90, 110, 130, 150, 170 };
-	int count, rotate;
-	double tweakRad;
-	
-	for( rotate = 0; rotate<2; rotate++ )
-	{
-		for( count=0; count<=10; count++ )
-		{
-			tweakRad = d2r( (90*rotate) - rTweakValues[count] );
-			tweakOffsetX[rotate][count] = (int) floor( 0.5 + cos( tweakRad ) * kBlobHorizSize );
-			tweakOffsetY[rotate][count] = (int) floor( 0.5 + sin( tweakRad ) * kBlobVertSize  );
-
-			tweakOffsetX[rotate+2][count] = -tweakOffsetX[rotate][count];
-			tweakOffsetY[rotate+2][count] = -tweakOffsetY[rotate][count];
-		}
-	}
-}
-
-void TweakFirstBlob( int player, MRect *first )
-{
-	int tweakValues[] = {0, -1, -2, -3, -6, -12};
-	
-	if( xTweak[player] > 0 )
-	{
-		OffsetMRect( first, xDirection[player] * tweakValues[xTweak[player]], 0 );
-	}
-
-	if( yTweak[player] > 0 )
-	{
-		OffsetMRect( first, 0, tweakValues[yTweak[player]] );
-	}
-}
-
-void TweakSecondBlob( int player, MRect *second )
-{
-	int x, y;
-	
-	CalcSecondBlobOffset( player, &x, &y );
-	OffsetMRect( second,
-				 tweakOffsetX[blobR[player]][rTweak[player]],
-				 tweakOffsetY[blobR[player]][rTweak[player]] );
-}
-
-void StartTweak( int player, int direction, int rotate, int fall )
-{
-	if( fall != 0 )
-	{
-		yTweak[player] = 3;
-		yTweakTime[player] = GameTickCount() + kTweakDelay;
-	}
-
-	if( direction != 0 )
-	{
-		xDirection[player] = direction;
-		xTweak[player] = 5;
-		xTweakTime[player] = GameTickCount() + kTweakDelay;
-	}
-	
-	if( rotate != 0 )
-	{
-		rTweak[player] = rotate * 5; 
-		rDirection[player] = rotate;
-		rTweakTime[player] = GameTickCount() + kTweakDelay;
-	}
-}
-
-void UpdateTweak( int player, int animation )
-{
-	MBoolean isXTweaked, isYTweaked, isRTweaked, isAnimTweaked = false;
-	
-	if( GameTickCount( ) >= animTime[player] )
-	{
-		isAnimTweaked = true;
-		animTime[player] += 2;
-		anim[player]++;	
-			
-		HandleMagic( player );
-	}
-
-	isXTweaked = ( (GameTickCount() >= xTweakTime[player]) && (xTweak[player] > 0) );
-	isYTweaked = ( (GameTickCount() >= yTweakTime[player]) && (yTweak[player] > 0) );
-	isRTweaked = ( (GameTickCount() >= rTweakTime[player]) && (rTweak[player] > 0) );
-	
-	if( isXTweaked || isRTweaked || isYTweaked || 
-	    isAnimTweaked || (shadowDepth[player] != lastShadow[player]) )
-	{	
-		EraseSpriteBlobs( player );
-		
-		if( isXTweaked )
-		{
-			xTweak[player]--;
-			xTweakTime[player] += kTweakDelay;
-		}
-		
-		if( isYTweaked )
-		{
-			yTweak[player]--;
-			yTweakTime[player] += kTweakDelay;
-		}
-
-		if( isRTweaked )
-		{
-			rTweak[player]--;
-			rTweakTime[player] += kTweakDelay;		
-		}
-		
-		DrawSpriteBlobs( player, animation );
-	}
-}
--- a/src/tweak.h
+++ b/src/tweak.h
@@ -6,6 +6,6 @@
 void UpdateTweak( int player, int suction );
 void InitTweak( void );
 
-#define d2r(x) ((x)*(pi/180))
+#define d2r(x) ((x)*(kPi/180))
 
 #define kTweakDelay 1
--- /dev/null
+++ b/src/victory.c
@@ -1,0 +1,344 @@
+// victory.c
+
+#include "SDLU.h"
+
+#include "main.h"
+#include "victory.h"
+#include "players.h"
+#include "gworld.h"
+#include "grays.h"
+#include "graphics.h"
+#include "soundfx.h"
+#include "score.h"
+#include "control.h"
+#include "random.h"
+#include "tweak.h"
+#include "gameticks.h"
+#include "level.h"
+#include "blitter.h"
+#include "music.h"
+#include "hiscore.h"
+#include "keyselect.h"
+#include "zap.h"
+#include "pause.h"
+#include "font.h"
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+MTicks winTime, loseTime;
+int winStage, loseStage;
+float drop[kGridAcross], last[kGridAcross];
+SkittlesFontPtr victoryFont;
+
+void InitVictory( void )
+{
+	victoryFont = GetFont( picVictoryFont );
+}
+
+void EndRound( int player )
+{
+	int count;
+
+	loseTime = GameTickCount( );
+	loseStage = 0;
+
+	role[player] = kLosing;
+	emotions[player] = kEmotionPanic;
+
+	for( count=0; count<kGridAcross; count++ )
+	{
+		last[count] = 0.1f;
+		drop[count] = 0.4f + RandomBefore(1000)/20000;
+		rowBounce[player][count] = 99;
+	}
+
+	if( player == 0 )
+	{
+		ChooseMusic( -1 );
+		PlayMono( kLoss );
+	}
+}
+
+void BeginVictory( int player )
+{
+	int count;
+	
+	endTime = GameTickCount( );
+	winTime = GameTickCount( );
+	winStage = 0;
+	
+	role[player] = kWinning;	
+	emotions[player] = kEmotionHappy;
+	
+	EraseSpriteBlobs( player );
+	
+	for( count=0; count<kGridAcross; count++ )
+	{
+		rowBounce[player][count] = 99;
+	}
+
+	if( player == 0 )
+	{
+		ChooseMusic( -1 );
+		PlayMono( kVictory );
+	}
+}
+
+void Lose( int player )
+{
+	MTicks    gameTime = GameTickCount();
+	int       skip = 1;
+	MRect     boardRect;
+	
+	if( gameTime < loseTime )
+		return;
+	
+	if( gameTime > loseTime )
+	{
+		skip = 2;
+	}
+		
+	loseTime  += skip;
+	loseStage += skip;
+
+	if( loseStage < 120 )
+	{
+		DropLosers( player, skip );
+	}
+	else if( loseStage == 120 || loseStage == 121 )
+	{
+		loseStage = 122;
+
+		boardRect.top    = boardRect.left = 0;
+		boardRect.bottom = playerSurface[player]->h;
+		boardRect.bottom = playerSurface[player]->w;
+
+		SDLU_AcquireSurface( playerSurface[player] );
+		SurfaceDrawBoard( player, &boardRect );
+		SDLU_ReleaseSurface( playerSurface[player] );
+
+		CleanSpriteArea( player, &boardRect );
+	}
+	else if( loseStage == 240 || loseStage == 241 )
+	{
+		loseStage = 242;
+		if( players == 1 && control[player] == kPlayerControl )
+		{
+			if( --credits > 0 )
+			{
+				HandleDialog( kContinueDialog );
+			}
+			else
+			{
+				AddHiscore( score[player] );
+				ShowGameOverScreen( );
+				
+				showStartMenu = true;
+			}
+		}
+		else if( players == 2 )
+		{
+			AddHiscore( score[player] );
+		}
+	}
+}
+
+void DropLosers( int player, int skip )
+{
+	int x, y, suck;
+	int beginDrop[] = { 28, 14, 0, 7, 21, 35 };
+	float thisDrop;
+	MRect myRect;
+	
+	SDLU_AcquireSurface( playerSpriteSurface[player] );
+
+	for( x=0; x<kGridAcross; x++ )
+	{
+		if( loseStage >= beginDrop[x] )
+		{
+			thisDrop = last[x] + ( (float)(skip) * ( 0.7f + last[x] / 12.0f ) );
+			
+			CalcBlobRect( x, 0, &myRect );
+			myRect.top = (int) last[x];
+			myRect.bottom = kGridDown * kBlobVertSize;
+			SurfaceDrawBoard( player, &myRect );
+			SetUpdateRect( player, &myRect );
+
+			if( thisDrop <  (kGridDown*kBlobVertSize) )
+			{
+				myRect.top = (int) thisDrop;
+				myRect.bottom = myRect.top + kBlobVertSize;
+				
+				y=0;
+				while( myRect.top < (kGridDown*kBlobVertSize) )
+				{
+					if( grid[player][x][y] >= kFirstBlob && 
+						grid[player][x][y] <= kLastBlob )
+					{
+						suck = suction[player][x][y] & (kUpDown);
+						if( suck == kNoSuction ) suck = kDying;
+						SurfaceDrawBlob( player, &myRect, grid[player][x][y], suck, charred[player][x][y] );
+					}
+					else if( grid[player][x][y] == kGray )
+					{
+						SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
+					} 
+					
+					OffsetMRect( &myRect, 0, kBlobVertSize );
+					y++;
+				}
+				
+				last[x] = thisDrop;
+			}
+		}
+	}
+
+	SDLU_ReleaseSurface( playerSpriteSurface[player] );
+}
+
+void Win( int player )
+{
+	int x, y;
+	
+	if( GameTickCount() >= winTime )
+	{			
+		if( winStage < (kGridDown * kGridAcross) )
+		{
+			y = (kGridDown-1) - (winStage / kGridAcross);
+			x = (winStage % kGridAcross);
+			if( y & 2 ) x = (kGridAcross-1) - x;
+						
+			if( grid[player][x][y] == kGray )
+			{
+				suction[player][x][y] = kGrayBlink1;
+				death[player][x][y] = 0;
+				score[player] += 20;
+			}
+			else if( grid[player][x][y] >= kFirstBlob && grid[player][x][y] <= kLastBlob )
+			{
+				suction[player][x][y] = kInDeath;
+				death[player][x][y] = 0;
+				score[player] += 100;
+			}
+		}
+		else if( winStage == 140 && control[player] == kPlayerControl )
+		{
+			DrawTimerCount( player );
+		}
+		else if( winStage == 200 && control[player] == kPlayerControl )
+		{
+			DrawTimerBonus( player );
+		}
+		
+		winTime++;
+		winStage++;
+	}
+	
+	if( winStage < 140 )
+	{	
+		KillBlobs( player );
+	}
+
+	if( winStage >= 280 )
+	{
+		if( control[player] == kPlayerControl )
+		{
+			IncrementLevel( );
+			BeginRound( true );
+		}
+	}
+}
+
+void DrawTimerCount( int player )
+{
+	MRect playerRect;
+	
+	SDLU_AcquireSurface( playerSurface[player] );
+
+	{
+		MPoint dPoint  = { (kBlobVertSize * 3), 15 };		
+
+		SurfaceBlitCharacter( victoryFont, 'A', &dPoint,  255, 255, 0, 1  );
+	}	
+
+	{
+		MPoint dPoint  = { (kBlobVertSize * 4), kBlobHorizSize };
+		char seconds[20];
+		char *scan = seconds;
+		
+		sprintf( seconds, "%d", (endTime - startTime) / 60 );
+		while( *scan )
+		{
+			SurfaceBlitCharacter( zapFont, *scan++, &dPoint, 255, 255, 255, 1  );
+			dPoint.h--;
+		}
+		
+		dPoint.h += 6;
+		SurfaceBlitCharacter( zapFont, 'S', &dPoint,  255, 255, 255, 1  );
+	}
+
+	playerRect.top    = playerRect.left = 0;
+	playerRect.bottom = playerSurface[player]->h;
+	playerRect.right  = playerSurface[player]->w;
+	
+	CleanSpriteArea( player, &playerRect );
+	PlayStereo( player, kSplop );
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+}
+
+void DrawTimerBonus( int player )
+{
+	MRect playerRect;
+	int timer, bonus;
+	
+	SDLU_AcquireSurface( playerSurface[player] );
+	
+	{
+		MPoint dPoint  = { (kBlobVertSize * 6),     15     };
+
+		SurfaceBlitCharacter( victoryFont, 'B', &dPoint,  255, 255, 0, 1  );
+	}
+	
+	timer = (endTime - startTime) / 60;
+	     if( timer <= 10 ) bonus = 30000;
+	else if( timer <= 20 ) bonus = 10000;
+	else if( timer <= 30 ) bonus =  5000;
+	else if( timer <= 45 ) bonus =  4000;
+	else if( timer <= 60 ) bonus =  3000;
+	else if( timer <= 80 ) bonus =  2000;
+	else if( timer <=100 ) bonus =  1000;
+	else if( timer <=120 ) bonus =   500;
+	else                   bonus =     0;
+	
+	if( players == 1 ) bonus *= level;
+	
+	score[player] += bonus;
+	
+	{
+		MPoint dPoint  = { (kBlobVertSize * 7), kBlobHorizSize };
+		char points[20];
+		char *scan = points;
+		
+		sprintf( points, "%d", bonus );
+		while( *scan )
+		{
+			SurfaceBlitCharacter( zapFont, *scan++, &dPoint, 255, 255, 255, 1  );
+			dPoint.h--;
+		}
+		
+		dPoint.h += 6;
+		SurfaceBlitCharacter( zapFont, 'P', &dPoint,  255, 255, 255, 1  );
+	}
+	
+	playerRect.top    = playerRect.left = 0;
+	playerRect.bottom = playerSurface[player]->h;
+	playerRect.right  = playerSurface[player]->w;
+
+	CleanSpriteArea( player, &playerRect );
+	PlayStereo( player, kSplop );
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+}
--- a/src/victory.cpp
+++ /dev/null
@@ -1,344 +1,0 @@
-// victory.c
-
-#include "SDLU.h"
-
-#include "main.h"
-#include "victory.h"
-#include "players.h"
-#include "gworld.h"
-#include "grays.h"
-#include "graphics.h"
-#include "soundfx.h"
-#include "score.h"
-#include "control.h"
-#include "random.h"
-#include "tweak.h"
-#include "gameticks.h"
-#include "level.h"
-#include "blitter.h"
-#include "music.h"
-#include "hiscore.h"
-#include "keyselect.h"
-#include "zap.h"
-#include "pause.h"
-#include "font.h"
-#include <math.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-MTicks winTime, loseTime;
-int winStage, loseStage;
-float drop[kGridAcross], last[kGridAcross];
-SkittlesFontPtr victoryFont;
-
-void InitVictory( void )
-{
-	victoryFont = GetFont( picVictoryFont );
-}
-
-void EndRound( int player )
-{
-	int count;
-
-	loseTime = GameTickCount( );
-	loseStage = 0;
-
-	role[player] = kLosing;
-	emotions[player] = kEmotionPanic;
-
-	for( count=0; count<kGridAcross; count++ )
-	{
-		last[count] = 0.1f;
-		drop[count] = 0.4f + RandomBefore(1000)/20000;
-		rowBounce[player][count] = 99;
-	}
-
-	if( player == 0 )
-	{
-		ChooseMusic( -1 );
-		PlayMono( kLoss );
-	}
-}
-
-void BeginVictory( int player )
-{
-	int count;
-	
-	endTime = GameTickCount( );
-	winTime = GameTickCount( );
-	winStage = 0;
-	
-	role[player] = kWinning;	
-	emotions[player] = kEmotionHappy;
-	
-	EraseSpriteBlobs( player );
-	
-	for( count=0; count<kGridAcross; count++ )
-	{
-		rowBounce[player][count] = 99;
-	}
-
-	if( player == 0 )
-	{
-		ChooseMusic( -1 );
-		PlayMono( kVictory );
-	}
-}
-
-void Lose( int player )
-{
-	MTicks    gameTime = GameTickCount();
-	int       skip = 1;
-	MRect     boardRect;
-	
-	if( gameTime < loseTime )
-		return;
-	
-	if( gameTime > loseTime )
-	{
-		skip = 2;
-	}
-		
-	loseTime  += skip;
-	loseStage += skip;
-
-	if( loseStage < 120 )
-	{
-		DropLosers( player, skip );
-	}
-	else if( loseStage == 120 || loseStage == 121 )
-	{
-		loseStage = 122;
-
-		boardRect.top    = boardRect.left = 0;
-		boardRect.bottom = playerSurface[player]->h;
-		boardRect.bottom = playerSurface[player]->w;
-
-		SDLU_AcquireSurface( playerSurface[player] );
-		SurfaceDrawBoard( player, &boardRect );
-		SDLU_ReleaseSurface( playerSurface[player] );
-
-		CleanSpriteArea( player, &boardRect );
-	}
-	else if( loseStage == 240 || loseStage == 241 )
-	{
-		loseStage = 242;
-		if( players == 1 && control[player] == kPlayerControl )
-		{
-			if( --credits > 0 )
-			{
-				HandleDialog( kContinueDialog );
-			}
-			else
-			{
-				AddHiscore( score[player] );
-				ShowGameOverScreen( );
-				
-				showStartMenu = true;
-			}
-		}
-		else if( players == 2 )
-		{
-			AddHiscore( score[player] );
-		}
-	}
-}
-
-void DropLosers( int player, int skip )
-{
-	int x, y, suck;
-	int beginDrop[] = { 28, 14, 0, 7, 21, 35 };
-	float thisDrop;
-	MRect myRect;
-	
-	SDLU_AcquireSurface( playerSpriteSurface[player] );
-
-	for( x=0; x<kGridAcross; x++ )
-	{
-		if( loseStage >= beginDrop[x] )
-		{
-			thisDrop = last[x] + ( (float)(skip) * ( 0.7f + last[x] / 12.0f ) );
-			
-			CalcBlobRect( x, 0, &myRect );
-			myRect.top = (int) last[x];
-			myRect.bottom = kGridDown * kBlobVertSize;
-			SurfaceDrawBoard( player, &myRect );
-			SetUpdateRect( player, &myRect );
-
-			if( thisDrop <  (kGridDown*kBlobVertSize) )
-			{
-				myRect.top = (int) thisDrop;
-				myRect.bottom = myRect.top + kBlobVertSize;
-				
-				y=0;
-				while( myRect.top < (kGridDown*kBlobVertSize) )
-				{
-					if( grid[player][x][y] >= kFirstBlob && 
-						grid[player][x][y] <= kLastBlob )
-					{
-						suck = suction[player][x][y] & (kUpDown);
-						if( suck == kNoSuction ) suck = kDying;
-						SurfaceDrawBlob( player, &myRect, grid[player][x][y], suck, charred[player][x][y] );
-					}
-					else if( grid[player][x][y] == kGray )
-					{
-						SurfaceDrawAlpha( &myRect, kGray, kLight, kGrayNoBlink );
-					} 
-					
-					OffsetMRect( &myRect, 0, kBlobVertSize );
-					y++;
-				}
-				
-				last[x] = thisDrop;
-			}
-		}
-	}
-
-	SDLU_ReleaseSurface( playerSpriteSurface[player] );
-}
-
-void Win( int player )
-{
-	int x, y;
-	
-	if( GameTickCount() >= winTime )
-	{			
-		if( winStage < (kGridDown * kGridAcross) )
-		{
-			y = (kGridDown-1) - (winStage / kGridAcross);
-			x = (winStage % kGridAcross);
-			if( y & 2 ) x = (kGridAcross-1) - x;
-						
-			if( grid[player][x][y] == kGray )
-			{
-				suction[player][x][y] = kGrayBlink1;
-				death[player][x][y] = 0;
-				score[player] += 20;
-			}
-			else if( grid[player][x][y] >= kFirstBlob && grid[player][x][y] <= kLastBlob )
-			{
-				suction[player][x][y] = kInDeath;
-				death[player][x][y] = 0;
-				score[player] += 100;
-			}
-		}
-		else if( winStage == 140 && control[player] == kPlayerControl )
-		{
-			DrawTimerCount( player );
-		}
-		else if( winStage == 200 && control[player] == kPlayerControl )
-		{
-			DrawTimerBonus( player );
-		}
-		
-		winTime++;
-		winStage++;
-	}
-	
-	if( winStage < 140 )
-	{	
-		KillBlobs( player );
-	}
-
-	if( winStage >= 280 )
-	{
-		if( control[player] == kPlayerControl )
-		{
-			IncrementLevel( );
-			BeginRound( true );
-		}
-	}
-}
-
-void DrawTimerCount( int player )
-{
-	MRect playerRect;
-	
-	SDLU_AcquireSurface( playerSurface[player] );
-
-	{
-		MPoint dPoint  = { (kBlobVertSize * 3), 15 };		
-
-		SurfaceBlitCharacter( victoryFont, 'A', &dPoint,  255, 255, 0, 1  );
-	}	
-
-	{
-		MPoint dPoint  = { (kBlobVertSize * 4), kBlobHorizSize };
-		char seconds[20];
-		char *scan = seconds;
-		
-		sprintf( seconds, "%d", (endTime - startTime) / 60 );
-		while( *scan )
-		{
-			SurfaceBlitCharacter( zapFont, *scan++, &dPoint, 255, 255, 255, 1  );
-			dPoint.h--;
-		}
-		
-		dPoint.h += 6;
-		SurfaceBlitCharacter( zapFont, 'S', &dPoint,  255, 255, 255, 1  );
-	}
-
-	playerRect.top    = playerRect.left = 0;
-	playerRect.bottom = playerSurface[player]->h;
-	playerRect.right  = playerSurface[player]->w;
-	
-	CleanSpriteArea( player, &playerRect );
-	PlayStereo( player, kSplop );
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-}
-
-void DrawTimerBonus( int player )
-{
-	MRect playerRect;
-	int timer, bonus;
-	
-	SDLU_AcquireSurface( playerSurface[player] );
-	
-	{
-		MPoint dPoint  = { (kBlobVertSize * 6),     15     };
-
-		SurfaceBlitCharacter( victoryFont, 'B', &dPoint,  255, 255, 0, 1  );
-	}
-	
-	timer = (endTime - startTime) / 60;
-	     if( timer <= 10 ) bonus = 30000;
-	else if( timer <= 20 ) bonus = 10000;
-	else if( timer <= 30 ) bonus =  5000;
-	else if( timer <= 45 ) bonus =  4000;
-	else if( timer <= 60 ) bonus =  3000;
-	else if( timer <= 80 ) bonus =  2000;
-	else if( timer <=100 ) bonus =  1000;
-	else if( timer <=120 ) bonus =   500;
-	else                   bonus =     0;
-	
-	if( players == 1 ) bonus *= level;
-	
-	score[player] += bonus;
-	
-	{
-		MPoint dPoint  = { (kBlobVertSize * 7), kBlobHorizSize };
-		char points[20];
-		char *scan = points;
-		
-		sprintf( points, "%d", bonus );
-		while( *scan )
-		{
-			SurfaceBlitCharacter( zapFont, *scan++, &dPoint, 255, 255, 255, 1  );
-			dPoint.h--;
-		}
-		
-		dPoint.h += 6;
-		SurfaceBlitCharacter( zapFont, 'P', &dPoint,  255, 255, 255, 1  );
-	}
-	
-	playerRect.top    = playerRect.left = 0;
-	playerRect.bottom = playerSurface[player]->h;
-	playerRect.right  = playerSurface[player]->w;
-
-	CleanSpriteArea( player, &playerRect );
-	PlayStereo( player, kSplop );
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-}
--- /dev/null
+++ b/src/zap.c
@@ -1,0 +1,639 @@
+// zap.cpp
+
+#include "SDLU.h"
+
+#include <stdio.h>
+
+#include "main.h"
+#include "players.h"
+#include "zap.h"
+#include "grays.h"
+#include "soundfx.h"
+#include "gworld.h"
+#include "graphics.h"
+#include "gameticks.h"
+#include "level.h"
+#include "random.h"
+#include "tweak.h"
+#include "blitter.h"
+#include "font.h"
+#include "score.h"
+#include "hiscore.h"
+
+#include <stdlib.h>
+#include <math.h>
+#include <stdio.h>
+
+signed char death[2][kGridAcross][kGridDown];
+int zapIteration[2];
+int grenadeFrame[2] = {kBlastFrames + 1, kBlastFrames + 1}, zapScoreFrame[2];
+MPoint zapScorePt[2];
+MRect grenadeRect[2];
+SkittlesFontPtr zapFont, zapOutline;
+char zapScore[2][20] = { "", "" };
+int zapScoreWidth[2];
+int zapScoreR[2], zapScoreG[2], zapScoreB[2];
+int zapOffsetX[7][kZapFrames], zapOffsetY[7][kZapFrames];
+
+void ZapScoreDisplay( int player, int amount, int multiplier, int x, int y, int c )
+{
+	char *scan;
+
+	if( amount     > 0 && 
+	    multiplier > 0 && 
+	    x >= 0 && x < kGridAcross &&
+	    y >= 0 && y < kGridDown   &&
+	    c >= kFirstBlob && c <= (kLastBlob+1) )
+	{
+		zapScorePt[player].v = y * kBlobVertSize  + 6;
+		zapScorePt[player].h = x * kBlobHorizSize + 6;
+
+		zapScoreR[player] = glowColors[c][0];
+		zapScoreG[player] = glowColors[c][1];
+		zapScoreB[player] = glowColors[c][2];
+
+	    sprintf( zapScore[player], (multiplier == 1)? "%d": "%d*%d", amount, multiplier );
+
+		zapScoreWidth[player] = 0;
+		scan = zapScore[player];
+		while( *scan ) zapScoreWidth[player] += zapFont->width[(uint8_t) * scan++];
+		
+		if( (zapScorePt[player].h + zapScoreWidth[player] + 8) > (kGridAcross * kBlobHorizSize) )
+		{
+			zapScorePt[player].h = (kGridAcross * kBlobHorizSize) - zapScoreWidth[player] - 8;
+		}
+	}
+}
+
+void ZapBlobs( int player )
+{
+	int x, y, cluster, clusterCount = 0, multiplier, amount = 0;
+	int zapFocusX = -1, zapFocusY = -1, zapFocusC = 0;
+	
+	
+	zapScorePt[player].v = 0;
+	zapScoreFrame[player] = 0;
+	
+	switch( chain[player] )
+	{
+		case 1:  multiplier = 1;                  break;
+		default: multiplier = 2 << chain[player]; break;
+	}
+
+	for( y=kGridDown-1; y>=0; y-- )
+	{
+		for( x=kGridAcross-1; x>=0; x-- )
+		{
+			if( grid[player][x][y] >= kFirstBlob &&
+				grid[player][x][y] <= kLastBlob &&
+				suction[player][x][y] != kInDeath )
+			{
+				cluster = SizeUp( grid[player], x, y, grid[player][x][y] );
+				if( cluster >= kBlobClusterSize )
+				{
+					clusterCount++;
+					zapFocusX = x;
+					zapFocusY = y;
+					zapFocusC = grid[player][x][y];
+					
+					amount += cluster * 10;
+					
+					multiplier += cluster - kBlobClusterSize;
+					
+					RemoveBlobs( player, x, y, grid[player][x][y], 0 );
+				}
+			}
+		}
+	}
+
+	if( clusterCount > 0 )
+	{
+		switch( clusterCount )
+		{
+			case 1:                     break;
+			case 2:   multiplier += 3;  break;
+			case 3:   multiplier += 6;  break;
+			case 4:   multiplier += 12; break;
+			default:  multiplier += 24; break;
+		}
+
+		if( multiplier > 999 ) multiplier = 999;
+		CalculateGrays( 1-player, amount * multiplier / difficulty[player] );
+		potentialCombo[player].value += amount * multiplier;
+		
+		if( players == 1 ) amount *= ((level <= kLevels)? level: 1);
+		score[player] += amount * multiplier;
+		
+		ZapScoreDisplay( player, amount, multiplier, zapFocusX, zapFocusY, zapFocusC );
+	}
+	
+	blobTime[player] = GameTickCount( );
+	
+	if( clusterCount > 0 )
+	{
+		chain[player]++;
+		role[player] = kKillBlobs;
+		PlayStereoFrequency( player, kSquishy, zapIteration[player]++ );
+	}
+	else
+	{
+		if( control[player] == kPlayerControl )
+		{
+			SubmitCombo( &potentialCombo[player] );
+		}
+		
+		SetupGrays( player );
+		role[player] = kDropGrays;
+		
+		if( BusyDroppingGrays( player ) )
+		{
+			PlayStereoFrequency( player, kWhistle, player );
+		}
+	}
+}
+
+void RemoveBlobs( int player, int x, int y, int color, int generation )
+{
+	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) )
+		return;
+	
+	if( grid[player][x][y] == kGray )
+	{
+		suction[player][x][y] = kGrayBlink1;
+		death[player][x][y] = -8 - generation;
+		return;
+	}
+	
+	if( grid[player][x][y] != color || suction[player][x][y] == kInDeath )
+		return;
+	
+	suction[player][x][y] = kInDeath;
+	death[player][x][y] = -12 - generation;
+	
+	RemoveBlobs( player, x-1, y,   color, generation+3 );
+	RemoveBlobs( player, x+1, y,   color, generation+3 );
+	RemoveBlobs( player, x,   y-1, color, generation+3 );
+	RemoveBlobs( player, x,   y+1, color, generation+3 );
+}
+
+void KillBlobs( int player )
+{
+	int x,y;
+	const int   position[] = { 0, 15, 27, 39, 51, 63, 72, 81, 90, 99, 105,111,117,123,126,129,131,132,133,134,135,135,136,136,137,137,138,138,138,139,139,139,139,140,140,140 };
+	const int   shading [] =
+    {
+        _5TO8(20),
+        _5TO8(21),
+        _5TO8(22),
+        _5TO8(23),
+        _5TO8(24),
+        _5TO8(25),
+        _5TO8(26),
+        _5TO8(27),
+        _5TO8(28),
+        _5TO8(29),
+        _5TO8(30),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(31),
+        _5TO8(30),
+        _5TO8(29),
+        _5TO8(28),
+        _5TO8(26),
+        _5TO8(24),
+        _5TO8(21),
+        _5TO8(18),
+        _5TO8(15),
+        _5TO8(12),
+        _5TO8(9),
+        _5TO8(6),
+        _5TO8(3),
+        _5TO8(0)
+    };
+	const int   blobGraphic[kZapFrames] = { kDying,   kDying,   kDying,   kDying,   kSquish1,
+								            kSquish1, kSquish1, kSquish1, kSquish2, kSquish2, 
+			 					            kSquish2, kSquish2, kSquish3, kSquish3, kSquish3,
+								            kSquish3, kSquish4, kSquish4, kSquish4, kSquish4 },
+		  		grayGraphic[kZapFrames] = { kGrayBlink1, kGrayBlink1, kGrayBlink1,
+		  						            kGrayBlink1, kGrayBlink1, kGrayBlink1, 
+		  						            kGrayBlink2, kGrayBlink2, kGrayBlink2,
+		  						            kGrayBlink2, kGrayBlink2, kGrayBlink2, 
+		  						            kGrayBlink3, kGrayBlink3, kGrayBlink3,
+		  					            	kGrayBlink3, kGrayBlink3, kGrayBlink3, 
+		  						            kGrayBlink3, kGrayBlink3 };
+	MRect myRect;
+	MBoolean busy = false;
+	MPoint dPoint, oPoint;
+	char *scan;
+	
+	if( blobTime[player] > GameTickCount( ) )
+		return;
+	
+	blobTime[player]++;
+
+	SDLU_AcquireSurface( playerSurface[player] );
+	
+	// clear grenade sprite
+	if( grenadeFrame[player] <= kBlastFrames )
+	{
+		CleanSpriteArea( player, &grenadeRect[player] );
+		if( grenadeFrame[player] == kBlastFrames ) grenadeFrame[player]++;
+	}
+		
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=0; y<kGridDown; y++ )
+		{
+			if( grid[player][x][y] >= kFirstBlob &&  // if a blob is dying
+				grid[player][x][y] <= kLastBlob &&
+				suction[player][x][y] == kInDeath )
+			{
+				death[player][x][y]++;
+				busy = true;
+				
+				CalcBlobRect( x, y, &myRect );
+				
+				if( death[player][x][y] >= 0 && death[player][x][y] <= kZapFrames ) // draw its death
+				{					
+					if( death[player][x][y] == kZapFrames )
+					{
+						grid[player][x][y] = kEmpty;
+						suction[player][x][y] = kNoSuction;
+						charred[player][x][y] = kNoCharring;
+						SurfaceDrawBlob( player, &myRect, kEmpty, kNoSuction, kNoCharring );
+						CleanSpriteArea( player, &myRect );
+					}
+					else
+					{
+						SurfaceDrawBlob( player, &myRect,
+								         grid[player][x][y],
+								         blobGraphic[ death[player][x][y] ],
+								         kNoCharring );
+						CleanSpriteArea( player, &myRect );
+					}
+					
+					CleanChunks( player, x, y, death[player][x][y], character[player].zapStyle );
+				}
+				else
+				{
+					SurfaceDrawBlob( player, &myRect, grid[player][x][y],
+								(blobTime[player] & 2)? kFlashDarkBlob: kNoSuction, kNoCharring );
+					CleanSpriteArea( player, &myRect );
+				}
+			}
+			else
+			{
+				if( grid[player][x][y] == kGray &&					// gray dying
+					suction[player][x][y] == kGrayBlink1 )
+				{
+					CalcBlobRect( x, y, &myRect );
+					
+					if( death[player][x][y] >= 0 && death[player][x][y] <= kZapFrames )
+					{
+						if( death[player][x][y] == kZapFrames )
+						{
+							grid[player][x][y] = kEmpty;
+							suction[player][x][y] = kNoSuction;
+							SurfaceDrawBlob( player, &myRect, kEmpty, kNoSuction, kNoCharring );
+						}
+						else
+						{
+							SurfaceDrawBoard( player, &myRect );
+							SurfaceDrawAlpha( &myRect, kGray, kLight, grayGraphic[ death[player][x][y] ] );
+							busy = true;
+						}
+						CleanSpriteArea( player, &myRect );
+					}
+					
+					death[player][x][y]++;
+				}
+			}
+		}
+	}
+	
+	// draw score info above blobs but below chunks and explosions
+	
+	if( zapScoreFrame[player] < arrsize(position) )
+	{
+		myRect.top    = zapScorePt[player].v - (position[zapScoreFrame[player]    ]);
+		myRect.left   = zapScorePt[player].h;
+		myRect.bottom = zapScorePt[player].v - (position[zapScoreFrame[player] - 1]) + 15;
+		myRect.right  = myRect.left + zapScoreWidth[player];
+		CleanSpriteArea( player, &myRect );
+
+		if( zapScoreFrame[player] < arrsize(position)-1 )
+		{		
+			SDLU_AcquireSurface( playerSpriteSurface[player] );	
+			
+			dPoint.v = oPoint.v = myRect.top;
+			dPoint.h = oPoint.h = myRect.left;
+			scan = zapScore[player];
+			while( *scan )
+			{
+				SurfaceBlitWeightedCharacter( zapFont,    *scan, &dPoint, zapScoreR[player], zapScoreG[player], zapScoreB[player], shading[zapScoreFrame[player]] );
+				SurfaceBlitWeightedCharacter( zapOutline, *scan, &oPoint, 0,                 0,                 0,                 shading[zapScoreFrame[player]] );
+				scan++;
+			}
+			
+			SDLU_ReleaseSurface( playerSpriteSurface[player] );	
+
+			zapScoreFrame[player]++;
+			busy = true;
+		}	
+	}
+		
+	///////////////////////////////////////////////////////////////
+
+	for( x=0; x<kGridAcross; x++ )
+	{
+		for( y=0; y<kGridDown; y++ )
+		{
+			if( grid[player][x][y] >= kFirstBlob &&  // if a blob is dying
+				grid[player][x][y] <= kLastBlob &&
+				suction[player][x][y] == kInDeath &&
+				death[player][x][y] >= 0 && death[player][x][y] < kZapFrames ) // draw chunks (after all that stuff)
+			{
+				DrawChunks( player, x, y, death[player][x][y], character[player].zapStyle );
+			}
+		}
+	}
+	
+	SDLU_ReleaseSurface( playerSurface[player] );
+	
+	if( grenadeFrame[player] < kBlastFrames )
+	{
+		busy = true;
+		
+		SDLU_AcquireSurface( playerSpriteSurface[player] );
+		
+		myRect.top = grenadeFrame[player] * kBlastHeight;
+		myRect.left = 0;
+		myRect.bottom = myRect.top + kBlastHeight;
+		myRect.right = kBlastWidth;
+		
+		SurfaceBlitAlpha(  playerSpriteSurface[player],  blastSurface,  blastMaskSurface,  playerSpriteSurface[player],
+						  &grenadeRect[player],         &myRect,       &myRect,           &grenadeRect[player]          );
+
+		grenadeFrame[player]++;
+
+		SDLU_ReleaseSurface( playerSpriteSurface[player] );
+	}
+	
+	if( !busy && role[player] == kKillBlobs )
+	{
+		blobTime[player] = GameTickCount( );
+		halfway[player] = false;
+		role[player] = kDropBlobs;
+	}
+}
+
+int SizeUp( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color )
+{	
+	int total;
+	
+	total = GetChainSize( myGrid, x, y, color );
+	CleanSize( myGrid, x, y, color );
+	
+	return total;
+}
+
+int GetChainSize( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color )
+{
+	int total;
+	
+	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) ) return 0;
+	if( myGrid[x][y] != color ) return 0;
+
+	myGrid[x][y] = -color;
+	
+	total = 1 + GetChainSize( myGrid, x-1, y, color )
+			  + GetChainSize( myGrid, x+1, y, color )
+			  + GetChainSize( myGrid, x, y-1, color )
+			  + GetChainSize( myGrid, x, y+1, color );
+	
+	return total;
+}
+
+void CleanWithPolish( signed char myGrid[kGridAcross][kGridDown], signed char polish[kGridAcross][kGridDown], int x, int y, int color )
+{
+	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) ) return;
+	
+	if( myGrid[x][y] == -color )
+	{
+		myGrid[x][y] = color;
+		polish[x][y] = true;
+		
+		CleanWithPolish( myGrid, polish, x-1, y, color );
+		CleanWithPolish( myGrid, polish, x+1, y, color );
+		CleanWithPolish( myGrid, polish, x, y-1, color );
+		CleanWithPolish( myGrid, polish, x, y+1, color );
+	}
+}
+
+void CleanSize( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color )
+{
+	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) ) return;
+	
+	if( myGrid[x][y] == -color )
+	{
+		myGrid[x][y] = color;
+		
+		CleanSize( myGrid, x-1, y, color );
+		CleanSize( myGrid, x+1, y, color );
+		CleanSize( myGrid, x, y-1, color );
+		CleanSize( myGrid, x, y+1, color );
+	}
+}
+
+void CleanChunks( int player, int x, int y, int level, int style )
+{
+	int count, color, type;
+	MRect chunkRect;
+	
+    SDLU_AcquireSurface( playerSpriteSurface[player] );
+		
+    for( count=-3; count<=3; count++ )
+    {
+        if( count != 0 )
+        {
+            if( level > 0 )
+            {
+                CalcBlobRect( x, y, &chunkRect );
+                GetZapStyle( player, &chunkRect, &color, &type, count, level-1, style );
+                CleanSpriteArea( player, &chunkRect );
+            }
+            
+            if( level < kZapFrames )
+            {
+                CalcBlobRect( x, y, &chunkRect );
+                GetZapStyle( player, &chunkRect, &color, &type, count, level, style );
+                CleanSpriteArea( player, &chunkRect );
+            }
+        }
+    }
+    
+    SDLU_ReleaseSurface( playerSpriteSurface[player] );
+}
+
+void DrawChunks( int player, int x, int y, int level, int style )
+{
+	int count, color, type;
+	MRect chunkRect;
+	
+    SDLU_AcquireSurface( playerSpriteSurface[player] );
+    
+    for( count=-3; count<=3; count++ )
+    {
+        if( count != 0 )
+        {
+            CalcBlobRect( x, y, &chunkRect );
+            color = grid[player][x][y];
+            GetZapStyle( player, &chunkRect, &color, &type, count, level, style );
+            SurfaceDrawSprite( &chunkRect, color, type );
+        }
+    }
+    
+    SDLU_ReleaseSurface( playerSpriteSurface[player] );
+}
+
+void CleanSplat( int player, int x, int y, int level )
+{
+	int count, color, type;
+	MRect chunkRect;
+
+    SDLU_AcquireSurface( playerSpriteSurface[player] );
+    
+    for( count=-2; count<=2; count++ )
+    {
+        if( count != 0 )
+        {
+            if( level > 0 )
+            {
+                CalcBlobRect( x, y, &chunkRect );
+                GetZapStyle( player, &chunkRect, &color, &type, count, level-1, 4 );
+                CleanSpriteArea( player, &chunkRect );
+            }
+            
+            if( level < kZapFrames )
+            {
+                CalcBlobRect( x, y, &chunkRect );
+                GetZapStyle( player, &chunkRect, &color, &type, count, level, 4 );
+                CleanSpriteArea( player, &chunkRect );
+            }
+        }
+    }
+    
+    SDLU_ReleaseSurface( playerSpriteSurface[player] );
+}
+
+void DrawSplat( int player, int x, int y, int level )
+{
+	int count, color = kGray, type;
+	MRect chunkRect;
+	
+    SDLU_AcquireSurface( playerSpriteSurface[player] );
+    
+    for( count=-2; count<=2; count++ )
+    {
+        if( level < kZapFrames && count != 0 )
+        {
+            CalcBlobRect( x, y, &chunkRect );
+            GetZapStyle( player, &chunkRect, &color, &type, count, level, 4 );
+            SurfaceDrawAlpha( &chunkRect, kGray, kLight, type );
+        }
+    }
+    
+    SDLU_ReleaseSurface( playerSpriteSurface[player] );
+}
+
+void InitZapStyle( void )
+{
+	int count, which;
+	double x;
+	const double position[kZapFrames] = {0, 10, 20, 28, 35, 42, 48, 54, 60, 64, 68, 70, 72, 73, 73, 74, 74, 75, 75, 75};
+	const double offset[7]   = {-30, -50, -70, 0, -110, -130, -150};
+	
+	zapFont    = GetFont( picZapFont );
+	zapOutline = GetFont( picZapOutlineFont );
+	
+	for( count=0; count<kZapFrames; count++ )
+	{
+		for( which=0; which<7; which++ )
+		{
+			x = d2r(offset[which]);
+				
+			zapOffsetX[which][count] = (int) floor( 0.5 + position[count] * cos( x ) );
+			zapOffsetY[which][count] = (int) floor( 0.5 + position[count] * sin( x ) );
+		}
+	}
+}
+
+
+void GetZapStyle( int player, MRect *myRect, int *color, int *type, int which, int level, int style )
+{
+	const int chunkGraphic[] = { kSquish1, kSquish1, kSquish1, kSquish1, kSquish1, 
+								 kSquish2, kSquish2, kSquish2, kSquish2, kSquish2,
+								 kSquish3, kSquish3, kSquish3, kSquish3, kSquish3,
+								 kSquish4, kSquish4, kSquish4, kSquish4, kSquish4 };
+	
+	(void) color; // later
+	*type = chunkGraphic[level];
+	
+	switch(  style )
+	{
+		case 0:
+		{
+			const int direction[7][2] = { {0, -2}, {-2,-1}, {-2,1}, {0,0}, {2,-1}, {2,1}, {0, 2} };
+			const int position[kZapFrames] = {0, 5, 9, 13, 17, 21, 24, 26, 30, 33, 35, 37, 39, 41, 42, 43, 43, 44, 44, 44 };
+			
+			OffsetMRect( myRect, direction[which+3][0] * position[level],
+								direction[which+3][1] * position[level] );
+			break;
+		}
+
+				
+		case 1:
+		{
+			const int xVelocity = 3;
+			const int yOffset[3][kZapFrames]   = {  { -4, -8, -12, -17, -22, -26, -30, -33, -36, -39, -41, -42, -43, -44, -44, -44, -43, -42, -41, -39 },
+											        { -4, -7, -10, -14, -18, -21, -23, -25, -26, -27, -27, -27, -26, -25, -23, -21, -18, -14, -10, -7 },
+											        { -2, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -11, -10, -9, -8, -7, -6, -5, -3, -1 } };
+
+			OffsetMRect( myRect, xVelocity * level * which, yOffset[abs(which)-1][level] );
+			
+			break;
+		}
+		
+		case 2:
+		{
+			const int position[kZapFrames] = {0, 5, 9, 13, 17, 21, 24, 27, 30, 33, 35, 37, 39, 41, 42, 43, 43, 44, 44, 44 };
+			
+			OffsetMRect( myRect, 0, position[level] * which );
+			break;
+		}
+		
+		case 3:
+		{
+			double fLevel = ((double)level) / 2;
+			OffsetMRect( myRect, (int)((player? -1.0: 1.0) * abs(which) * fLevel * (fLevel-1)), (int)((which-3) * fLevel) );
+			break;
+		}
+		
+		case 4:
+		{
+			OffsetMRect( myRect, zapOffsetX[which+3][level],
+							 	 zapOffsetY[which+3][level] );
+			
+			break;
+		}
+	}
+}
--- a/src/zap.cpp
+++ /dev/null
@@ -1,642 +1,0 @@
-// zap.cpp
-
-#include "SDLU.h"
-
-#include <stdio.h>
-#include <algorithm>
-
-#include "main.h"
-#include "players.h"
-#include "zap.h"
-#include "grays.h"
-#include "soundfx.h"
-#include "gworld.h"
-#include "graphics.h"
-#include "gameticks.h"
-#include "level.h"
-#include "random.h"
-#include "tweak.h"
-#include "blitter.h"
-#include "font.h"
-#include "score.h"
-#include "hiscore.h"
-
-#include <stdlib.h>
-#include <math.h>
-#include <stdio.h>
-
-signed char death[2][kGridAcross][kGridDown];
-int zapIteration[2];
-int grenadeFrame[2] = {kBlastFrames + 1, kBlastFrames + 1}, zapScoreFrame[2];
-MPoint zapScorePt[2];
-MRect grenadeRect[2];
-SkittlesFontPtr zapFont, zapOutline;
-char zapScore[2][20] = { "", "" };
-int zapScoreWidth[2];
-int zapScoreR[2], zapScoreG[2], zapScoreB[2];
-int zapOffsetX[7][kZapFrames], zapOffsetY[7][kZapFrames];
-
-using std::min;
-
-void ZapScoreDisplay( int player, int amount, int multiplier, int x, int y, int c )
-{
-	char *scan;
-
-	if( amount     > 0 && 
-	    multiplier > 0 && 
-	    x >= 0 && x < kGridAcross &&
-	    y >= 0 && y < kGridDown   &&
-	    c >= kFirstBlob && c <= (kLastBlob+1) )
-	{
-		zapScorePt[player].v = y * kBlobVertSize  + 6;
-		zapScorePt[player].h = x * kBlobHorizSize + 6;
-
-		zapScoreR[player] = glowColors[c][0];
-		zapScoreG[player] = glowColors[c][1];
-		zapScoreB[player] = glowColors[c][2];
-
-	    sprintf( zapScore[player], (multiplier == 1)? "%d": "%d*%d", amount, multiplier );
-
-		zapScoreWidth[player] = 0;
-		scan = zapScore[player];
-		while( *scan ) zapScoreWidth[player] += zapFont->width[(uint8_t) * scan++];
-		
-		if( (zapScorePt[player].h + zapScoreWidth[player] + 8) > (kGridAcross * kBlobHorizSize) )
-		{
-			zapScorePt[player].h = (kGridAcross * kBlobHorizSize) - zapScoreWidth[player] - 8;
-		}
-	}
-}
-
-void ZapBlobs( int player )
-{
-	int x, y, cluster, clusterCount = 0, multiplier, amount = 0;
-	int zapFocusX = -1, zapFocusY = -1, zapFocusC = 0;
-	
-	
-	zapScorePt[player].v = 0;
-	zapScoreFrame[player] = 0;
-	
-	switch( chain[player] )
-	{
-		case 1:  multiplier = 1;                  break;
-		default: multiplier = 2 << chain[player]; break;
-	}
-
-	for( y=kGridDown-1; y>=0; y-- )
-	{
-		for( x=kGridAcross-1; x>=0; x-- )
-		{
-			if( grid[player][x][y] >= kFirstBlob &&
-				grid[player][x][y] <= kLastBlob &&
-				suction[player][x][y] != kInDeath )
-			{
-				cluster = SizeUp( grid[player], x, y, grid[player][x][y] );
-				if( cluster >= kBlobClusterSize )
-				{
-					clusterCount++;
-					zapFocusX = x;
-					zapFocusY = y;
-					zapFocusC = grid[player][x][y];
-					
-					amount += cluster * 10;
-					
-					multiplier += cluster - kBlobClusterSize;
-					
-					RemoveBlobs( player, x, y, grid[player][x][y], 0 );
-				}
-			}
-		}
-	}
-
-	if( clusterCount > 0 )
-	{
-		switch( clusterCount )
-		{
-			case 1:                     break;
-			case 2:   multiplier += 3;  break;
-			case 3:   multiplier += 6;  break;
-			case 4:   multiplier += 12; break;
-			default:  multiplier += 24; break;
-		}
-
-		if( multiplier > 999 ) multiplier = 999;
-		CalculateGrays( 1-player, amount * multiplier / difficulty[player] );
-		potentialCombo[player].value += amount * multiplier;
-		
-		if( players == 1 ) amount *= ((level <= kLevels)? level: 1);
-		score[player] += amount * multiplier;
-		
-		ZapScoreDisplay( player, amount, multiplier, zapFocusX, zapFocusY, zapFocusC );
-	}
-	
-	blobTime[player] = GameTickCount( );
-	
-	if( clusterCount > 0 )
-	{
-		chain[player]++;
-		role[player] = kKillBlobs;
-		PlayStereoFrequency( player, kSquishy, zapIteration[player]++ );
-	}
-	else
-	{
-		if( control[player] == kPlayerControl )
-		{
-			SubmitCombo( &potentialCombo[player] );
-		}
-		
-		SetupGrays( player );
-		role[player] = kDropGrays;
-		
-		if( BusyDroppingGrays( player ) )
-		{
-			PlayStereoFrequency( player, kWhistle, player );
-		}
-	}
-}
-
-void RemoveBlobs( int player, int x, int y, int color, int generation )
-{
-	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) )
-		return;
-	
-	if( grid[player][x][y] == kGray )
-	{
-		suction[player][x][y] = kGrayBlink1;
-		death[player][x][y] = -8 - generation;
-		return;
-	}
-	
-	if( grid[player][x][y] != color || suction[player][x][y] == kInDeath )
-		return;
-	
-	suction[player][x][y] = kInDeath;
-	death[player][x][y] = -12 - generation;
-	
-	RemoveBlobs( player, x-1, y,   color, generation+3 );
-	RemoveBlobs( player, x+1, y,   color, generation+3 );
-	RemoveBlobs( player, x,   y-1, color, generation+3 );
-	RemoveBlobs( player, x,   y+1, color, generation+3 );
-}
-
-void KillBlobs( int player )
-{
-	int x,y;
-	const int   position[] = { 0, 15, 27, 39, 51, 63, 72, 81, 90, 99, 105,111,117,123,126,129,131,132,133,134,135,135,136,136,137,137,138,138,138,139,139,139,139,140,140,140 };
-	const int   shading [] =
-    {
-        _5TO8(20),
-        _5TO8(21),
-        _5TO8(22),
-        _5TO8(23),
-        _5TO8(24),
-        _5TO8(25),
-        _5TO8(26),
-        _5TO8(27),
-        _5TO8(28),
-        _5TO8(29),
-        _5TO8(30),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(31),
-        _5TO8(30),
-        _5TO8(29),
-        _5TO8(28),
-        _5TO8(26),
-        _5TO8(24),
-        _5TO8(21),
-        _5TO8(18),
-        _5TO8(15),
-        _5TO8(12),
-        _5TO8(9),
-        _5TO8(6),
-        _5TO8(3),
-        _5TO8(0)
-    };
-	const int   blobGraphic[kZapFrames] = { kDying,   kDying,   kDying,   kDying,   kSquish1,
-								            kSquish1, kSquish1, kSquish1, kSquish2, kSquish2, 
-			 					            kSquish2, kSquish2, kSquish3, kSquish3, kSquish3,
-								            kSquish3, kSquish4, kSquish4, kSquish4, kSquish4 },
-		  		grayGraphic[kZapFrames] = { kGrayBlink1, kGrayBlink1, kGrayBlink1,
-		  						            kGrayBlink1, kGrayBlink1, kGrayBlink1, 
-		  						            kGrayBlink2, kGrayBlink2, kGrayBlink2,
-		  						            kGrayBlink2, kGrayBlink2, kGrayBlink2, 
-		  						            kGrayBlink3, kGrayBlink3, kGrayBlink3,
-		  					            	kGrayBlink3, kGrayBlink3, kGrayBlink3, 
-		  						            kGrayBlink3, kGrayBlink3 };
-	MRect myRect;
-	MBoolean busy = false;
-	MPoint dPoint, oPoint;
-	char *scan;
-	
-	if( blobTime[player] > GameTickCount( ) )
-		return;
-	
-	blobTime[player]++;
-
-	SDLU_AcquireSurface( playerSurface[player] );
-	
-	// clear grenade sprite
-	if( grenadeFrame[player] <= kBlastFrames )
-	{
-		CleanSpriteArea( player, &grenadeRect[player] );
-		if( grenadeFrame[player] == kBlastFrames ) grenadeFrame[player]++;
-	}
-		
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=0; y<kGridDown; y++ )
-		{
-			if( grid[player][x][y] >= kFirstBlob &&  // if a blob is dying
-				grid[player][x][y] <= kLastBlob &&
-				suction[player][x][y] == kInDeath )
-			{
-				death[player][x][y]++;
-				busy = true;
-				
-				CalcBlobRect( x, y, &myRect );
-				
-				if( death[player][x][y] >= 0 && death[player][x][y] <= kZapFrames ) // draw its death
-				{					
-					if( death[player][x][y] == kZapFrames )
-					{
-						grid[player][x][y] = kEmpty;
-						suction[player][x][y] = kNoSuction;
-						charred[player][x][y] = kNoCharring;
-						SurfaceDrawBlob( player, &myRect, kEmpty, kNoSuction, kNoCharring );
-						CleanSpriteArea( player, &myRect );
-					}
-					else
-					{
-						SurfaceDrawBlob( player, &myRect,
-								         grid[player][x][y],
-								         blobGraphic[ death[player][x][y] ],
-								         kNoCharring );
-						CleanSpriteArea( player, &myRect );
-					}
-					
-					CleanChunks( player, x, y, death[player][x][y], character[player].zapStyle );
-				}
-				else
-				{
-					SurfaceDrawBlob( player, &myRect, grid[player][x][y],
-								(blobTime[player] & 2)? kFlashDarkBlob: kNoSuction, kNoCharring );
-					CleanSpriteArea( player, &myRect );
-				}
-			}
-			else
-			{
-				if( grid[player][x][y] == kGray &&					// gray dying
-					suction[player][x][y] == kGrayBlink1 )
-				{
-					CalcBlobRect( x, y, &myRect );
-					
-					if( death[player][x][y] >= 0 && death[player][x][y] <= kZapFrames )
-					{
-						if( death[player][x][y] == kZapFrames )
-						{
-							grid[player][x][y] = kEmpty;
-							suction[player][x][y] = kNoSuction;
-							SurfaceDrawBlob( player, &myRect, kEmpty, kNoSuction, kNoCharring );
-						}
-						else
-						{
-							SurfaceDrawBoard( player, &myRect );
-							SurfaceDrawAlpha( &myRect, kGray, kLight, grayGraphic[ death[player][x][y] ] );
-							busy = true;
-						}
-						CleanSpriteArea( player, &myRect );
-					}
-					
-					death[player][x][y]++;
-				}
-			}
-		}
-	}
-	
-	// draw score info above blobs but below chunks and explosions
-	
-	if( zapScoreFrame[player] < arrsize(position) )
-	{
-		myRect.top    = zapScorePt[player].v - (position[zapScoreFrame[player]    ]);
-		myRect.left   = zapScorePt[player].h;
-		myRect.bottom = zapScorePt[player].v - (position[zapScoreFrame[player] - 1]) + 15;
-		myRect.right  = myRect.left + zapScoreWidth[player];
-		CleanSpriteArea( player, &myRect );
-
-		if( zapScoreFrame[player] < arrsize(position)-1 )
-		{		
-			SDLU_AcquireSurface( playerSpriteSurface[player] );	
-			
-			dPoint.v = oPoint.v = myRect.top;
-			dPoint.h = oPoint.h = myRect.left;
-			scan = zapScore[player];
-			while( *scan )
-			{
-				SurfaceBlitWeightedCharacter( zapFont,    *scan, &dPoint, zapScoreR[player], zapScoreG[player], zapScoreB[player], shading[zapScoreFrame[player]] );
-				SurfaceBlitWeightedCharacter( zapOutline, *scan, &oPoint, 0,                 0,                 0,                 shading[zapScoreFrame[player]] );
-				scan++;
-			}
-			
-			SDLU_ReleaseSurface( playerSpriteSurface[player] );	
-
-			zapScoreFrame[player]++;
-			busy = true;
-		}	
-	}
-		
-	///////////////////////////////////////////////////////////////
-
-	for( x=0; x<kGridAcross; x++ )
-	{
-		for( y=0; y<kGridDown; y++ )
-		{
-			if( grid[player][x][y] >= kFirstBlob &&  // if a blob is dying
-				grid[player][x][y] <= kLastBlob &&
-				suction[player][x][y] == kInDeath &&
-				death[player][x][y] >= 0 && death[player][x][y] < kZapFrames ) // draw chunks (after all that stuff)
-			{
-				DrawChunks( player, x, y, death[player][x][y], character[player].zapStyle );
-			}
-		}
-	}
-	
-	SDLU_ReleaseSurface( playerSurface[player] );
-	
-	if( grenadeFrame[player] < kBlastFrames )
-	{
-		busy = true;
-		
-		SDLU_AcquireSurface( playerSpriteSurface[player] );
-		
-		myRect.top = grenadeFrame[player] * kBlastHeight;
-		myRect.left = 0;
-		myRect.bottom = myRect.top + kBlastHeight;
-		myRect.right = kBlastWidth;
-		
-		SurfaceBlitAlpha(  playerSpriteSurface[player],  blastSurface,  blastMaskSurface,  playerSpriteSurface[player],
-						  &grenadeRect[player],         &myRect,       &myRect,           &grenadeRect[player]          );
-
-		grenadeFrame[player]++;
-
-		SDLU_ReleaseSurface( playerSpriteSurface[player] );
-	}
-	
-	if( !busy && role[player] == kKillBlobs )
-	{
-		blobTime[player] = GameTickCount( );
-		halfway[player] = false;
-		role[player] = kDropBlobs;
-	}
-}
-
-int SizeUp( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color )
-{	
-	int total;
-	
-	total = GetChainSize( myGrid, x, y, color );
-	CleanSize( myGrid, x, y, color );
-	
-	return total;
-}
-
-int GetChainSize( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color )
-{
-	int total;
-	
-	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) ) return 0;
-	if( myGrid[x][y] != color ) return 0;
-
-	myGrid[x][y] = -color;
-	
-	total = 1 + GetChainSize( myGrid, x-1, y, color )
-			  + GetChainSize( myGrid, x+1, y, color )
-			  + GetChainSize( myGrid, x, y-1, color )
-			  + GetChainSize( myGrid, x, y+1, color );
-	
-	return total;
-}
-
-void CleanWithPolish( signed char myGrid[kGridAcross][kGridDown], signed char polish[kGridAcross][kGridDown], int x, int y, int color )
-{
-	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) ) return;
-	
-	if( myGrid[x][y] == -color )
-	{
-		myGrid[x][y] = color;
-		polish[x][y] = true;
-		
-		CleanWithPolish( myGrid, polish, x-1, y, color );
-		CleanWithPolish( myGrid, polish, x+1, y, color );
-		CleanWithPolish( myGrid, polish, x, y-1, color );
-		CleanWithPolish( myGrid, polish, x, y+1, color );
-	}
-}
-
-void CleanSize( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color )
-{
-	if( (x<0) || (x>=kGridAcross) || (y<0) || (y>=kGridDown) ) return;
-	
-	if( myGrid[x][y] == -color )
-	{
-		myGrid[x][y] = color;
-		
-		CleanSize( myGrid, x-1, y, color );
-		CleanSize( myGrid, x+1, y, color );
-		CleanSize( myGrid, x, y-1, color );
-		CleanSize( myGrid, x, y+1, color );
-	}
-}
-
-void CleanChunks( int player, int x, int y, int level, int style )
-{
-	int count, color, type;
-	MRect chunkRect;
-	
-    SDLU_AcquireSurface( playerSpriteSurface[player] );
-		
-    for( count=-3; count<=3; count++ )
-    {
-        if( count != 0 )
-        {
-            if( level > 0 )
-            {
-                CalcBlobRect( x, y, &chunkRect );
-                GetZapStyle( player, &chunkRect, &color, &type, count, level-1, style );
-                CleanSpriteArea( player, &chunkRect );
-            }
-            
-            if( level < kZapFrames )
-            {
-                CalcBlobRect( x, y, &chunkRect );
-                GetZapStyle( player, &chunkRect, &color, &type, count, level, style );
-                CleanSpriteArea( player, &chunkRect );
-            }
-        }
-    }
-    
-    SDLU_ReleaseSurface( playerSpriteSurface[player] );
-}
-
-void DrawChunks( int player, int x, int y, int level, int style )
-{
-	int count, color, type;
-	MRect chunkRect;
-	
-    SDLU_AcquireSurface( playerSpriteSurface[player] );
-    
-    for( count=-3; count<=3; count++ )
-    {
-        if( count != 0 )
-        {
-            CalcBlobRect( x, y, &chunkRect );
-            color = grid[player][x][y];
-            GetZapStyle( player, &chunkRect, &color, &type, count, level, style );
-            SurfaceDrawSprite( &chunkRect, color, type );
-        }
-    }
-    
-    SDLU_ReleaseSurface( playerSpriteSurface[player] );
-}
-
-void CleanSplat( int player, int x, int y, int level )
-{
-	int count, color, type;
-	MRect chunkRect;
-
-    SDLU_AcquireSurface( playerSpriteSurface[player] );
-    
-    for( count=-2; count<=2; count++ )
-    {
-        if( count != 0 )
-        {
-            if( level > 0 )
-            {
-                CalcBlobRect( x, y, &chunkRect );
-                GetZapStyle( player, &chunkRect, &color, &type, count, level-1, 4 );
-                CleanSpriteArea( player, &chunkRect );
-            }
-            
-            if( level < kZapFrames )
-            {
-                CalcBlobRect( x, y, &chunkRect );
-                GetZapStyle( player, &chunkRect, &color, &type, count, level, 4 );
-                CleanSpriteArea( player, &chunkRect );
-            }
-        }
-    }
-    
-    SDLU_ReleaseSurface( playerSpriteSurface[player] );
-}
-
-void DrawSplat( int player, int x, int y, int level )
-{
-	int count, color = kGray, type;
-	MRect chunkRect;
-	
-    SDLU_AcquireSurface( playerSpriteSurface[player] );
-    
-    for( count=-2; count<=2; count++ )
-    {
-        if( level < kZapFrames && count != 0 )
-        {
-            CalcBlobRect( x, y, &chunkRect );
-            GetZapStyle( player, &chunkRect, &color, &type, count, level, 4 );
-            SurfaceDrawAlpha( &chunkRect, kGray, kLight, type );
-        }
-    }
-    
-    SDLU_ReleaseSurface( playerSpriteSurface[player] );
-}
-
-void InitZapStyle( void )
-{
-	int count, which;
-	double x;
-	const double position[kZapFrames] = {0, 10, 20, 28, 35, 42, 48, 54, 60, 64, 68, 70, 72, 73, 73, 74, 74, 75, 75, 75};
-	const double offset[7]   = {-30, -50, -70, 0, -110, -130, -150};
-	
-	zapFont    = GetFont( picZapFont );
-	zapOutline = GetFont( picZapOutlineFont );
-	
-	for( count=0; count<kZapFrames; count++ )
-	{
-		for( which=0; which<7; which++ )
-		{
-			x = d2r(offset[which]);
-				
-			zapOffsetX[which][count] = (int) floor( 0.5 + position[count] * cos( x ) );
-			zapOffsetY[which][count] = (int) floor( 0.5 + position[count] * sin( x ) );
-		}
-	}
-}
-
-
-void GetZapStyle( int player, MRect *myRect, int *color, int *type, int which, int level, int style )
-{
-	const int chunkGraphic[] = { kSquish1, kSquish1, kSquish1, kSquish1, kSquish1, 
-								 kSquish2, kSquish2, kSquish2, kSquish2, kSquish2,
-								 kSquish3, kSquish3, kSquish3, kSquish3, kSquish3,
-								 kSquish4, kSquish4, kSquish4, kSquish4, kSquish4 };
-	
-	(void) color; // later
-	*type = chunkGraphic[level];
-	
-	switch(  style )
-	{
-		case 0:
-		{
-			const int direction[7][2] = { {0, -2}, {-2,-1}, {-2,1}, {0,0}, {2,-1}, {2,1}, {0, 2} };
-			const int position[kZapFrames] = {0, 5, 9, 13, 17, 21, 24, 26, 30, 33, 35, 37, 39, 41, 42, 43, 43, 44, 44, 44 };
-			
-			OffsetMRect( myRect, direction[which+3][0] * position[level],
-								direction[which+3][1] * position[level] );
-			break;
-		}
-
-				
-		case 1:
-		{
-			const int xVelocity = 3;
-			const int yOffset[3][kZapFrames]   = {  { -4, -8, -12, -17, -22, -26, -30, -33, -36, -39, -41, -42, -43, -44, -44, -44, -43, -42, -41, -39 },
-											        { -4, -7, -10, -14, -18, -21, -23, -25, -26, -27, -27, -27, -26, -25, -23, -21, -18, -14, -10, -7 },
-											        { -2, -4, -5, -6, -7, -8, -9, -10, -10, -11, -11, -11, -10, -9, -8, -7, -6, -5, -3, -1 } };
-
-			OffsetMRect( myRect, xVelocity * level * which, yOffset[abs(which)-1][level] );
-			
-			break;
-		}
-		
-		case 2:
-		{
-			const int position[kZapFrames] = {0, 5, 9, 13, 17, 21, 24, 27, 30, 33, 35, 37, 39, 41, 42, 43, 43, 44, 44, 44 };
-			
-			OffsetMRect( myRect, 0, position[level] * which );
-			break;
-		}
-		
-		case 3:
-		{
-			double fLevel = ((double)level) / 2;
-			OffsetMRect( myRect, (int)((player? -1.0: 1.0) * abs(which) * fLevel * (fLevel-1)), (int)((which-3) * fLevel) );
-			break;
-		}
-		
-		case 4:
-		{
-			OffsetMRect( myRect, zapOffsetX[which+3][level],
-							 	 zapOffsetY[which+3][level] );
-			
-			break;
-		}
-	}
-}