ref: 13aedf6a9faab2fe48c45278cd8280c76ec3816b
parent: 4ae0d05b2c6df1baee227d5b00e90e923c205119
author: CandyCrisis <>
date: Fri Nov 15 07:28:44 EST 2002
Candy Crisis 1.0 for Windows source code
--- /dev/null
+++ b/CandyCrisis.dev
@@ -1,0 +1,152 @@
+[Project]
+FileName=C:\Dev\CandyCrisis\CandyCrisis.dev
+Name=CandyCrisis
+Use_gpp=1
+UnitCount=59
+ResFiles=C:\Dev\CandyCrisis\Crisis.rc
+NoConsole=1
+IsDll=0
+Icon=C:\DevCpp\Icon\Mainicon.ico
+CompilerOptions=-lmingw32 -lSDL -lSDL_image -lfmod -funroll-loops
+IncludeDirs=C:\DevCpp\Include\SDL
+ObjFile=
+[Unit1]
+FileName=C:\Dev\CandyCrisis\Source\blitter.cpp
+[Unit2]
+FileName=C:\Dev\CandyCrisis\Source\blitter.h
+FileTime=762254186
+[Unit3]
+FileName=C:\Dev\CandyCrisis\Source\SDLU.h
+FileTime=762254268
+[Unit4]
+FileName=C:\Dev\CandyCrisis\Source\SDLU.cpp
+FileTime=760265985
+[Unit0]
+FileTime=761576470
+[Unit5]
+FileName=C:\Dev\CandyCrisis\Source\MTypes.cpp
+FileTime=761466198
+[Unit6]
+FileName=C:\Dev\CandyCrisis\Source\MTypes.h
+[Unit7]
+FileName=C:\Dev\CandyCrisis\Source\control.h
+[Unit9]
+FileName=C:\Dev\CandyCrisis\Source\gameticks.h
+[Unit10]
+FileName=C:\Dev\CandyCrisis\Source\graphics.h
+[Unit11]
+FileName=C:\Dev\CandyCrisis\Source\graymonitor.h
+[Unit12]
+FileName=C:\Dev\CandyCrisis\Source\grays.h
+[Unit13]
+FileName=C:\Dev\CandyCrisis\Source\gworld.h
+[Unit14]
+FileName=C:\Dev\CandyCrisis\Source\hiscore.h
+[Unit16]
+FileName=C:\Dev\CandyCrisis\Source\level.h
+FileTime=762254017
+[Unit17]
+FileName=C:\Dev\CandyCrisis\Source\main.h
+FileTime=760265652
+[Unit18]
+FileName=C:\Dev\CandyCrisis\Source\music.h
+[Unit19]
+FileName=C:\Dev\CandyCrisis\Source\moving.h
+[Unit20]
+FileName=C:\Dev\CandyCrisis\Source\next.h
+[Unit21]
+FileName=C:\Dev\CandyCrisis\Source\opponent.h
+[Unit22]
+FileName=C:\Dev\CandyCrisis\Source\pause.h
+[Unit23]
+FileName=C:\Dev\CandyCrisis\Source\players.h
+[Unit24]
+FileName=C:\Dev\CandyCrisis\Source\prefs.h
+[Unit25]
+FileName=C:\Dev\CandyCrisis\Source\random.h
+[Unit27]
+FileName=C:\Dev\CandyCrisis\Source\soundfx.h
+[Unit29]
+FileName=C:\Dev\CandyCrisis\Source\tweak.h
+[Unit30]
+FileName=C:\Dev\CandyCrisis\Source\victory.h
+[Unit31]
+FileName=C:\Dev\CandyCrisis\Source\zap.h
+FileTime=761597437
+[Unit33]
+FileName=C:\Dev\CandyCrisis\Source\font.cpp
+FileTime=760680850
+[Unit34]
+FileName=C:\Dev\CandyCrisis\Source\graphics.cpp
+FileTime=762252708
+[Unit35]
+FileName=C:\Dev\CandyCrisis\Source\gameticks.cpp
+[Unit8]
+FileName=C:\Dev\CandyCrisis\Source\font.h
+[Unit15]
+FileName=C:\Dev\CandyCrisis\Source\keyselect.h
+[Unit26]
+FileName=C:\Dev\CandyCrisis\Source\score.h
+FileTime=760284315
+[Unit28]
+FileName=C:\Dev\CandyCrisis\Source\tutorial.h
+[Unit36]
+FileName=C:\Dev\CandyCrisis\Source\graymonitor.cpp
+[Unit37]
+FileName=C:\Dev\CandyCrisis\Source\grays.cpp
+[Unit38]
+FileName=C:\Dev\CandyCrisis\Source\gworld.cpp
+[Unit39]
+FileName=C:\Dev\CandyCrisis\Source\hiscore.cpp
+FileTime=762254607
+[Unit40]
+FileName=C:\Dev\CandyCrisis\Source\main.cpp
+FileTime=761948219
+[Unit41]
+FileName=C:\Dev\CandyCrisis\Source\level.cpp
+FileTime=760810939
+[Unit42]
+FileName=C:\Dev\CandyCrisis\Source\keyselect.cpp
+FileTime=761332182
+[Unit32]
+FileName=C:\Dev\CandyCrisis\Source\control.cpp
+[Unit43]
+FileName=C:\Dev\CandyCrisis\Source\fmodmusic.cpp
+[Unit44]
+FileName=C:\Dev\CandyCrisis\Source\moving.cpp
+[Unit45]
+FileName=C:\Dev\CandyCrisis\Source\next.cpp
+[Unit46]
+FileName=C:\Dev\CandyCrisis\Source\opponent.cpp
+FileTime=762254036
+[Unit47]
+FileName=C:\Dev\CandyCrisis\Source\pause.cpp
+FileTime=761466338
+[Unit48]
+FileName=C:\Dev\CandyCrisis\Source\players.cpp
+[Unit49]
+FileName=C:\Dev\CandyCrisis\Source\prefs.cpp
+[Unit50]
+FileName=C:\Dev\CandyCrisis\Source\zap.cpp
+[Unit51]
+FileName=C:\Dev\CandyCrisis\Source\score.cpp
+[Unit52]
+FileName=C:\Dev\CandyCrisis\Source\tutorial.cpp
+[Unit53]
+FileName=C:\Dev\CandyCrisis\Source\tweak.cpp
+[Unit54]
+FileName=C:\Dev\CandyCrisis\Source\victory.cpp
+FileTime=761334713
+[Unit55]
+FileName=C:\Dev\CandyCrisis\Source\random.cpp
+FileTime=761531803
+[Unit56]
+FileName=C:\Dev\CandyCrisis\Source\fmodsoundfx.cpp
+FileTime=761335287
+[Unit57]
+FileName=C:\Dev\CandyCrisis\Source\RegAlgorithm.cpp
+[Unit58]
+FileName=C:\Dev\CandyCrisis\Source\RegAlgorithm.h
+FileTime=762254520
+[Unit59]
+FileName=C:\Dev\CandyCrisis\SDL_main\SDL_main.c
binary files /dev/null b/CandyCrisis.icns differ
binary files /dev/null b/CandyCrisisResources/PICT_1000.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_10000.png differ
binary files /dev/null b/CandyCrisisResources/PICT_10001.png differ
binary files /dev/null b/CandyCrisisResources/PICT_1001.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1002.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1004.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1100.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1101.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1102.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1104.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1200.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1201.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1202.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1204.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1300.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1301.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1302.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1304.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1400.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1401.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1402.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1404.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1500.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1501.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1502.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1504.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1505.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1600.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1601.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1602.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1604.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1700.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1701.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1702.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1704.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1800.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1801.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1802.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1804.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1805.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1900.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1901.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1902.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_1904.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_200.png differ
binary files /dev/null b/CandyCrisisResources/PICT_2000.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2001.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2002.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2004.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_201.png differ
binary files /dev/null b/CandyCrisisResources/PICT_202.png differ
binary files /dev/null b/CandyCrisisResources/PICT_203.png differ
binary files /dev/null b/CandyCrisisResources/PICT_204.png differ
binary files /dev/null b/CandyCrisisResources/PICT_205.png differ
binary files /dev/null b/CandyCrisisResources/PICT_206.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2100.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2101.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2102.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2104.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2105.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2300.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2301.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_2302.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_250.png differ
binary files /dev/null b/CandyCrisisResources/PICT_251.png differ
binary files /dev/null b/CandyCrisisResources/PICT_252.png differ
binary files /dev/null b/CandyCrisisResources/PICT_253.png differ
binary files /dev/null b/CandyCrisisResources/PICT_254.png differ
binary files /dev/null b/CandyCrisisResources/PICT_255.png differ
binary files /dev/null b/CandyCrisisResources/PICT_256.png differ
binary files /dev/null b/CandyCrisisResources/PICT_257.png differ
binary files /dev/null b/CandyCrisisResources/PICT_258.png differ
binary files /dev/null b/CandyCrisisResources/PICT_259.png differ
binary files /dev/null b/CandyCrisisResources/PICT_260.png differ
binary files /dev/null b/CandyCrisisResources/PICT_300.png differ
binary files /dev/null b/CandyCrisisResources/PICT_301.png differ
binary files /dev/null b/CandyCrisisResources/PICT_302.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_303.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_304.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_305.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_306.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_307.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_308.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_309.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_310.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_500.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_5000.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5001.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5002.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5003.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5004.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5005.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5006.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5007.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5008.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5009.png differ
binary files /dev/null b/CandyCrisisResources/PICT_501.jpg differ
binary files /dev/null b/CandyCrisisResources/PICT_5010.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5011.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5013.png differ
binary files /dev/null b/CandyCrisisResources/PICT_502.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5100.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5101.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5102.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5103.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5104.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5105.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5106.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5107.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5108.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5109.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5110.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5111.png differ
binary files /dev/null b/CandyCrisisResources/PICT_5113.png differ
--- /dev/null
+++ b/CandyCrisisResources/Preferences.txt
@@ -1,0 +1,7 @@
+mod : 01
+sfx : 01
+size: 000000000000000000000000000000000000
+keys: 6100000064000000780000007300000014010000130100001201000011010000
+high: 4C657669617468616E00000000000000000000000000000000000000000000000000000000000000409C000044722E20437269736973000000000000000000000000000000000000000000000000000000000000A08C0000416E67656C0000000000000000000000000000000000000000000000000000000000000000000000007D00005370696B650000000000000000000000000000000000000000000000000000000000000000000000606D0000466F7800000000000000000000000000000000000000000000000000000000000000000000000000C05D000052616775656C00000000000000000000000000000000000000000000000000000000000000000000204E00004B756D6F000000000000000000000000000000000000000000000000000000000000000000000000803E000050617474790000000000000000000000000000000000000000000000000000000000000000000000E02E000059757572656900000000000000000000000000000000000000000000000000000000000000000000401F0000476C7572700000000000000000000000000000000000000000000000000000000000000000000000A00F0000
+cmbx: 000000000000000000000000000000000000000000000000000000000000000000000101010202000000000000000000000001010000000000000000000000000000000000000000000000000000020200000E0103000000EA0100005475746F7269616C0000000000000000000000000000000000000000000000000000000000000000
+user: 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
binary files /dev/null b/CandyCrisisResources/mod_128 differ
binary files /dev/null b/CandyCrisisResources/mod_129 differ
binary files /dev/null b/CandyCrisisResources/mod_130 differ
binary files /dev/null b/CandyCrisisResources/mod_131 differ
binary files /dev/null b/CandyCrisisResources/mod_132 differ
binary files /dev/null b/CandyCrisisResources/mod_133 differ
binary files /dev/null b/CandyCrisisResources/mod_134 differ
binary files /dev/null b/CandyCrisisResources/mod_135 differ
binary files /dev/null b/CandyCrisisResources/mod_136 differ
binary files /dev/null b/CandyCrisisResources/mod_137 differ
binary files /dev/null b/CandyCrisisResources/mod_138 differ
binary files /dev/null b/CandyCrisisResources/mod_139 differ
binary files /dev/null b/CandyCrisisResources/mod_140 differ
binary files /dev/null b/CandyCrisisResources/mod_141 differ
binary files /dev/null b/CandyCrisisResources/mod_142 differ
binary files /dev/null b/CandyCrisisResources/snd_128.wav differ
binary files /dev/null b/CandyCrisisResources/snd_129.wav differ
binary files /dev/null b/CandyCrisisResources/snd_130.wav differ
binary files /dev/null b/CandyCrisisResources/snd_131.wav differ
binary files /dev/null b/CandyCrisisResources/snd_132.wav differ
binary files /dev/null b/CandyCrisisResources/snd_133.wav differ
binary files /dev/null b/CandyCrisisResources/snd_134.wav differ
binary files /dev/null b/CandyCrisisResources/snd_135.wav differ
binary files /dev/null b/CandyCrisisResources/snd_136.wav differ
binary files /dev/null b/CandyCrisisResources/snd_137.wav differ
binary files /dev/null b/CandyCrisisResources/snd_138.wav differ
binary files /dev/null b/CandyCrisisResources/snd_139.wav differ
binary files /dev/null b/CandyCrisisResources/snd_140.wav differ
binary files /dev/null b/CandyCrisisResources/snd_141.wav differ
binary files /dev/null b/CandyCrisisResources/snd_142.wav differ
binary files /dev/null b/CandyCrisisResources/snd_143.wav differ
binary files /dev/null b/CandyCrisisResources/snd_144.wav differ
binary files /dev/null b/Crisis.ico differ
--- /dev/null
+++ b/Crisis.rc
@@ -1,0 +1,1 @@
+Crisis ICON "C:/Dev/CandyCrisis/Crisis.ico"
--- /dev/null
+++ b/CrisisGen.dev
@@ -1,0 +1,22 @@
+[Project]
+FileName=C:\Dev\CandyCrisis\CrisisGen.dev
+Name=CrisisGen
+Use_gpp=1
+UnitCount=3
+ResFiles=
+NoConsole=0
+IsDll=0
+Icon=C:\DevCpp\Bin\Mainicon.ico
+CompilerOptions=
+IncludeDirs=
+ObjFile=
+[Unit1]
+FileName=C:\Dev\CandyCrisis\Source\RegAlgorithm.h
+FileTime=761464410
+[Unit2]
+FileName=C:\Dev\CandyCrisis\Source\RegAlgorithm.cpp
+FileTime=761464174
+[Unit3]
+FileName=C:\Dev\CandyCrisis\Source\RegGenerator.cpp
+[Unit0]
+FileTime=761335618
binary files /dev/null b/SDL.dll differ
binary files /dev/null b/SDL_image.dll differ
--- /dev/null
+++ b/SDL_main/SDL_main.c
@@ -1,0 +1,337 @@
+/*
+ SDL_main.c, placed in the public domain by Sam Lantinga 4/13/98
+
+ The WinMain function -- calls your program's main() function
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <windows.h>
+#include <malloc.h> /* For _alloca() */
+#include <io.h> /* For _getcwd() */
+
+/* Include the SDL main definition header */
+#include "SDL.h"
+#include "SDL_main.h"
+#ifdef main
+#ifndef _WIN32_WCE_EMULATION
+#undef main
+#endif
+#endif
+
+/* Do we really not want stdio redirection with Windows CE? */
+#if 1 || defined(_WIN32_WCE)
+#define NO_STDIO_REDIRECT
+#endif
+
+/* The standard output files */
+#define STDOUT_FILE TEXT("stdout.txt")
+#define STDERR_FILE TEXT("stderr.txt")
+
+#ifndef NO_STDIO_REDIRECT
+static char stdoutPath[MAX_PATH];
+static char stderrPath[MAX_PATH];
+#endif
+
+#if defined(_WIN32_WCE) && _WIN32_WCE < 300
+/* seems to be undefined in Win CE although in online help */
+#define isspace(a) (((CHAR)a == ' ') || ((CHAR)a == '\t'))
+
+/* seems to be undefined in Win CE although in online help */
+char *strrchr(char *str, int c)
+{
+ char *p;
+
+ /* Skip to the end of the string */
+ p=str;
+ while (*p)
+ p++;
+
+ /* Look for the given character */
+ while ( (p >= str) && (*p != (CHAR)c) )
+ p--;
+
+ /* Return NULL if character not found */
+ if ( p < str ) {
+ p = NULL;
+ }
+ return p;
+}
+#endif /* _WIN32_WCE < 300 */
+
+/* Parse a command line buffer into arguments */
+static int ParseCommandLine(char *cmdline, char **argv)
+{
+ char *bufp;
+ int argc;
+
+ argc = 0;
+ for ( bufp = cmdline; *bufp; ) {
+ /* Skip leading whitespace */
+ while ( isspace(*bufp) ) {
+ ++bufp;
+ }
+ /* Skip over argument */
+ if ( *bufp == '"' ) {
+ ++bufp;
+ if ( *bufp ) {
+ if ( argv ) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ /* Skip over word */
+ while ( *bufp && (*bufp != '"') ) {
+ ++bufp;
+ }
+ } else {
+ if ( *bufp ) {
+ if ( argv ) {
+ argv[argc] = bufp;
+ }
+ ++argc;
+ }
+ /* Skip over word */
+ while ( *bufp && ! isspace(*bufp) ) {
+ ++bufp;
+ }
+ }
+ if ( *bufp ) {
+ if ( argv ) {
+ *bufp = '\0';
+ }
+ ++bufp;
+ }
+ }
+ if ( argv ) {
+ argv[argc] = NULL;
+ }
+ return(argc);
+}
+
+/* Show an error message */
+static void ShowError(const char *title, const char *message)
+{
+/* If USE_MESSAGEBOX is defined, you need to link with user32.lib */
+#ifdef USE_MESSAGEBOX
+ MessageBox(NULL, message, title, MB_ICONEXCLAMATION|MB_OK);
+#else
+ fprintf(stderr, "%s: %s\n", title, message);
+#endif
+}
+
+/* Pop up an out of memory message, returns to Windows */
+static BOOL OutOfMemory(void)
+{
+ ShowError("Fatal Error", "Out of memory - aborting");
+ return FALSE;
+}
+
+/* Remove the output files if there was no output written */
+static void __cdecl cleanup_output(void)
+{
+#ifndef NO_STDIO_REDIRECT
+ FILE *file;
+ int empty;
+#endif
+
+ /* Flush the output in case anything is queued */
+ fclose(stdout);
+ fclose(stderr);
+
+#ifndef NO_STDIO_REDIRECT
+ /* See if the files have any output in them */
+ file = fopen(stdoutPath, "rb");
+ if ( file ) {
+ empty = (fgetc(file) == EOF) ? 1 : 0;
+ fclose(file);
+ if ( empty ) {
+ remove(stdoutPath);
+ }
+ }
+ file = fopen(stderrPath, "rb");
+ if ( file ) {
+ empty = (fgetc(file) == EOF) ? 1 : 0;
+ fclose(file);
+ if ( empty ) {
+ remove(stderrPath);
+ }
+ }
+#endif
+}
+
+#if defined(_MSC_VER) && !defined(_WIN32_WCE)
+/* The VC++ compiler needs main defined */
+#define console_main main
+#endif
+
+/* This is where execution begins [console apps] */
+int console_main(int argc, char *argv[])
+{
+ int n;
+ char *bufp, *appname;
+
+ /* Get the class name from argv[0] */
+ appname = argv[0];
+ if ( (bufp=strrchr(argv[0], '\\')) != NULL ) {
+ appname = bufp+1;
+ } else
+ if ( (bufp=strrchr(argv[0], '/')) != NULL ) {
+ appname = bufp+1;
+ }
+
+ if ( (bufp=strrchr(appname, '.')) == NULL )
+ n = strlen(appname);
+ else
+ n = (bufp-appname);
+
+ bufp = (char *)alloca(n+1);
+ if ( bufp == NULL ) {
+ return OutOfMemory();
+ }
+ strncpy(bufp, appname, n);
+ bufp[n] = '\0';
+ appname = bufp;
+
+ /* Load SDL dynamic link library */
+ if ( SDL_Init(SDL_INIT_NOPARACHUTE) < 0 ) {
+ ShowError("WinMain() error", SDL_GetError());
+ return(FALSE);
+ }
+ atexit(cleanup_output);
+ atexit(SDL_Quit);
+
+#ifndef DISABLE_VIDEO
+#if 0
+ /* Create and register our class *
+ DJM: If we do this here, the user nevers gets a chance to
+ putenv(SDL_WINDOWID). This is already called later by
+ the (DIB|DX5)_CreateWindow function, so it should be
+ safe to comment it out here.
+ if ( SDL_RegisterApp(appname, CS_BYTEALIGNCLIENT,
+ GetModuleHandle(NULL)) < 0 ) {
+ ShowError("WinMain() error", SDL_GetError());
+ exit(1);
+ }*/
+#else
+ /* Sam:
+ We still need to pass in the application handle so that
+ DirectInput will initialize properly when SDL_RegisterApp()
+ is called later in the video initialization.
+ */
+ SDL_SetModuleHandle(GetModuleHandle(NULL));
+#endif /* 0 */
+#endif /* !DISABLE_VIDEO */
+
+ /* Run the application main() code */
+ SDL_main(argc, argv);
+
+ /* Exit cleanly, calling atexit() functions */
+ exit(0);
+
+ /* Hush little compiler, don't you cry... */
+ return(0);
+}
+
+/* This is where execution begins [windowed apps] */
+#ifdef _WIN32_WCE
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPWSTR szCmdLine, int sw)
+#else
+int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR szCmdLine, int sw)
+#endif
+{
+ HINSTANCE handle;
+ char **argv;
+ int argc;
+ char *cmdline;
+#ifdef _WIN32_WCE
+ wchar_t *bufp;
+ int nLen;
+#else
+ char *bufp;
+#endif
+#ifndef NO_STDIO_REDIRECT
+ FILE *newfp;
+#endif
+
+ /* Start up DDHELP.EXE before opening any files, so DDHELP doesn't
+ keep them open. This is a hack.. hopefully it will be fixed
+ someday. DDHELP.EXE starts up the first time DDRAW.DLL is loaded.
+ */
+ handle = LoadLibrary(TEXT("DDRAW.DLL"));
+ if ( handle != NULL ) {
+ FreeLibrary(handle);
+ }
+
+#ifndef NO_STDIO_REDIRECT
+ _getcwd( stdoutPath, sizeof( stdoutPath ) );
+ strcat( stdoutPath, "\\" STDOUT_FILE );
+
+ /* Redirect standard input and standard output */
+ newfp = freopen(stdoutPath, "w", stdout);
+ if ( newfp == NULL ) { /* This happens on NT */
+#if !defined(stdout)
+ stdout = fopen(stdoutPath, "w");
+#else
+ newfp = fopen(stdoutPath, "w");
+ if ( newfp ) {
+ *stdout = *newfp;
+ }
+#endif
+ }
+
+ _getcwd( stderrPath, sizeof( stderrPath ) );
+ strcat( stderrPath, "\\" STDERR_FILE );
+
+ newfp = freopen(stderrPath, "w", stderr);
+ if ( newfp == NULL ) { /* This happens on NT */
+#if !defined(stderr)
+ stderr = fopen(stderrPath, "w");
+#else
+ newfp = fopen(stderrPath, "w");
+ if ( newfp ) {
+ *stderr = *newfp;
+ }
+#endif
+ }
+ setvbuf(stdout, NULL, _IOLBF, BUFSIZ); /* Line buffered */
+ setbuf(stderr, NULL); /* No buffering */
+#endif /* !NO_STDIO_REDIRECT */
+
+#ifdef _WIN32_WCE
+ nLen = wcslen(szCmdLine)+128+1;
+ bufp = (wchar_t *)alloca(nLen*2);
+ wcscpy (bufp, TEXT("\""));
+ GetModuleFileName(NULL, bufp+1, 128-3);
+ wcscpy (bufp+wcslen(bufp), TEXT("\" "));
+ wcsncpy(bufp+wcslen(bufp), szCmdLine,nLen-wcslen(bufp));
+ nLen = wcslen(bufp)+1;
+ cmdline = (char *)alloca(nLen);
+ if ( cmdline == NULL ) {
+ return OutOfMemory();
+ }
+ WideCharToMultiByte(CP_ACP, 0, bufp, -1, cmdline, nLen, NULL, NULL);
+#else
+ /* Grab the command line (use alloca() on Windows) */
+ bufp = GetCommandLine();
+ cmdline = (char *)alloca(strlen(bufp)+1);
+ if ( cmdline == NULL ) {
+ return OutOfMemory();
+ }
+ strcpy(cmdline, bufp);
+#endif
+
+ /* Parse it into argv and argc */
+ argc = ParseCommandLine(cmdline, NULL);
+ argv = (char **)alloca((argc+1)*(sizeof *argv));
+ if ( argv == NULL ) {
+ return OutOfMemory();
+ }
+ ParseCommandLine(cmdline, argv);
+
+ /* Run the main program (after a little SDL initialization) */
+ return(console_main(argc, argv));
+}
--- /dev/null
+++ b/Source/MTypes.cpp
@@ -1,0 +1,49 @@
+///
+/// MTypes.c
+///
+/// Generic replacements for very basic Mac types.
+///
+/// John Stiles, 2002/10/14
+///
+
+
+#include "MTypes.h"
+
+
+static inline short min( short a, short b )
+{
+ return (a < b)? a: b;
+}
+
+
+static inline short max( short a, short b )
+{
+ return (a > b)? a: b;
+}
+
+
+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, MRect* r )
+{
+ return (p.h >= r->left) &&
+ (p.h < r->right) &&
+ (p.v >= r->top) &&
+ (p.v < r->bottom);
+}
--- /dev/null
+++ b/Source/MTypes.h
@@ -1,0 +1,46 @@
+/////
+/// MTypes.h
+///
+/// Generic replacements for very basic Mac types.
+///
+/// John Stiles, 2002/10/14
+///
+
+
+#ifndef __MTYPES__
+#define __MTYPES__
+
+
+typedef signed char MBoolean;
+
+
+typedef struct MRGBColor
+{
+ unsigned short red;
+ unsigned short green;
+ unsigned short blue;
+} MRGBColor;
+
+
+typedef struct MRect
+{
+ short top;
+ short left;
+ short bottom;
+ short right;
+} MRect;
+
+
+typedef struct MPoint
+{
+ short v;
+ short h;
+} MPoint;
+
+
+void UnionMRect( const MRect* a, const MRect* b, MRect* u );
+void OffsetMRect( MRect* r, int x, int y );
+unsigned char MPointInMRect( MPoint p, MRect* r );
+
+
+#endif
--- /dev/null
+++ b/Source/Makefile
@@ -1,0 +1,45 @@
+
+OPTIMIZE := -O3
+CXXFLAGS := $(shell sdl-config --cflags) -DHAVE_MIKMOD $(OPTIMIZE)
+LIBS := $(shell sdl-config --libs) -lSDL_image -lmikmod
+
+TARGET := CandyCrisis
+
+OBJECTS := \
+ blitter.o \
+ control.o \
+ font.o \
+ gameticks.o \
+ graphics.o \
+ graymonitor.o \
+ grays.o \
+ gworld.o \
+ hiscore.o \
+ keyselect.o \
+ level.o \
+ main.o \
+ midi.o \
+ moving.o \
+ MTypes.o \
+ next.o \
+ opponent.o \
+ pause.o \
+ players.o \
+ prefs.o \
+ random.o \
+ score.o \
+ SDLU.o \
+ soundfx.o \
+ tutorial.o \
+ tweak.o \
+ victory.o \
+ zap.o
+
+$(TARGET): $(OBJECTS)
+ $(CXX) -o $@ $^ $(LIBS)
+
+clean:
+ $(RM) $(OBJECTS)
+
+distclean: clean
+ $(RM) $(TARGET)
--- /dev/null
+++ b/Source/RegAlgorithm.cpp
@@ -1,0 +1,268 @@
+///
+/// RegAlgorithm.cpp
+///
+/// Registration code algorithm based on hashing functions at
+/// http://burtleburtle.net/bob/hash/#lookup
+///
+/// John Stiles, 2002/10/31
+///
+
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "MTypes.h"
+#include "RegAlgorithm.h"
+
+
+// These bits are completely random, generated from atmospheric noise.
+// (http://www.random.org/) They have no significance other than being
+// seeds to the hash function below.
+const unsigned int keylist[13] = { 0x40691e27,
+ 0xe451380b,
+ 0x603bb484,
+ 0x93e4865e,
+ 0x964c9a5d,
+ 0xc19591e2,
+ 0xff90b43a,
+ 0x0b34b5c3,
+ 0x5ed19fb7,
+ 0xdcb4dcaf,
+ 0x6700fcfa,
+ 0x7c7ee303,
+ 0xe2016ffc };
+
+
+// The letters that the end user will receive in their code. They correspond
+// to hex digits [0-F] consecutively. Notice that the letters contain no vowels
+// (to avoid making comprehensible words), and no overtly ambiguous characters
+// (O's, I's, L's, zeros, ones, etc).
+const char* scrambleKey = "PWCDRSHBNTVFGJMZ";
+
+/*
+--------------------------------------------------------------------
+mix -- mix 3 32-bit values reversibly.
+For every delta with one or two bit set, and the deltas of all three
+ high bits or all three low bits, whether the original value of a,b,c
+ is almost all zero or is uniformly distributed,
+* If mix() is run forward or backward, at least 32 bits in a,b,c
+ have at least 1/4 probability of changing.
+* If mix() is run forward, every bit of c will change between 1/3 and
+ 2/3 of the time. (Well, 22/100 and 78/100 for some 2-bit deltas.)
+mix() was built out of 36 single-cycle latency instructions in a
+ structure that could supported 2x parallelism, like so:
+ a -= b;
+ a -= c; x = (c>>13);
+ b -= c; a ^= x;
+ b -= a; x = (a<<8);
+ c -= a; b ^= x;
+ c -= b; x = (b>>13);
+ ...
+ Unfortunately, superscalar Pentiums and Sparcs can't take advantage
+ of that parallelism. They've also turned some of those single-cycle
+ latency instructions into multi-cycle latency instructions. Still,
+ this is the fastest good hash I could find. There were about 2^^68
+ to choose from. I only looked at a billion or so.
+--------------------------------------------------------------------
+*/
+
+#define mix(a,b,c) \
+{ \
+ a -= b; a -= c; a ^= (c>>13); \
+ b -= c; b -= a; b ^= (a<<8); \
+ c -= a; c -= b; c ^= (b>>13); \
+ a -= b; a -= c; a ^= (c>>12); \
+ b -= c; b -= a; b ^= (a<<16); \
+ c -= a; c -= b; c ^= (b>>5); \
+ a -= b; a -= c; a ^= (c>>3); \
+ b -= c; b -= a; b ^= (a<<10); \
+ c -= a; c -= b; c ^= (b>>15); \
+}
+
+
+/*
+--------------------------------------------------------------------
+hash() -- hash a variable-length key into a 32-bit value
+ k : the key (the unaligned variable-length array of bytes)
+ len : the length of the key, counting by bytes
+ level : can be any 4-byte value
+Returns a 32-bit value. Every bit of the key affects every bit of
+the return value. Every 1-bit and 2-bit delta achieves avalanche.
+About 36+6len instructions.
+
+The best hash table sizes are powers of 2. There is no need to do
+mod a prime (mod is sooo slow!). If you need less than 32 bits,
+use a bitmask. For example, if you need only 10 bits, do
+ h = (h & hashmask(10));
+In which case, the hash table should have hashsize(10) elements.
+
+If you are hashing n strings (ub1 **)k, do it like this:
+ for (i=0, h=0; i<n; ++i) h = hash( k[i], len[i], h);
+
+By Bob Jenkins, 1996. [email protected]. You may use this
+code any way you wish, private, educational, or commercial. It's free.
+
+See http://burlteburtle.net/bob/hash/evahash.html
+Use for hash table lookup, or anything where one collision in 2^32 is
+acceptable. Do NOT use for cryptographic purposes.
+--------------------------------------------------------------------
+*/
+
+unsigned int hash( unsigned char* k, unsigned int length, unsigned int initval )
+{
+ unsigned int a, b, c, len;
+
+ /* Set up the internal state */
+ len = length;
+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
+ c = initval; /* the previous hash value */
+
+ /*---------------------------------------- handle most of the key */
+ while (len >= 12)
+ {
+ a += (k[0] + ((unsigned int)k[1]<<8) + ((unsigned int)k[2]<<16) + ((unsigned int)k[3]<<24));
+ b += (k[4] + ((unsigned int)k[5]<<8) + ((unsigned int)k[6]<<16) + ((unsigned int)k[7]<<24));
+ c += (k[8] + ((unsigned int)k[9]<<8) + ((unsigned int)k[10]<<16) + ((unsigned int)k[11]<<24));
+ mix(a,b,c);
+ k += 12; len -= 12;
+ }
+
+ /*------------------------------------- handle the last 11 bytes */
+ c += length;
+ switch(len) /* all the case statements fall through */
+ {
+ case 11: c+=((unsigned int)k[10]<<24);
+ case 10: c+=((unsigned int)k[9]<<16);
+ case 9 : c+=((unsigned int)k[8]<<8);
+ /* the first byte of c is reserved for the length */
+ case 8 : b+=((unsigned int)k[7]<<24);
+ case 7 : b+=((unsigned int)k[6]<<16);
+ case 6 : b+=((unsigned int)k[5]<<8);
+ case 5 : b+=k[4];
+ case 4 : a+=((unsigned int)k[3]<<24);
+ case 3 : a+=((unsigned int)k[2]<<16);
+ case 2 : a+=((unsigned int)k[1]<<8);
+ case 1 : a+=k[0];
+ /* case 0: nothing left to add */
+ }
+
+ mix(a,b,c);
+
+ /*-------------------------------------------- report the result */
+ return c;
+}
+
+
+void CopyFlattened( char* out, const char* in )
+{
+ // Convert a name like "John Stiles" to "JOHN STILES" or
+ // "Patty O'furniture" to "PATTY OFURNITURE".
+ char* start = out;
+
+ while( *in == ' ' ) in++;
+
+ for( ;; )
+ {
+ char c = *in++;
+ if( c == '\0' ) break;
+
+ c = toupper(c);
+ if( c >= 'A' && c <= 'Z' || c == ' ' )
+ {
+ *out++ = c;
+ }
+ }
+
+ while( (out > start) && (out[-1] == ' ') ) out--;
+
+ *out++ = '\0';
+}
+
+
+MBoolean ValidateCode( const char* name, const char* key )
+{
+ // Convert the key (a jumble of letters like BFGJ-TVFF) into a 32-bit
+ // hex value.
+ char* unscrambled[8];
+ int index;
+ unsigned int value = 0;
+
+ if( strlen(key) != 9 ) return false;
+
+ if( key[4] != '-' ) return false;
+
+ unscrambled[0] = strchr( scrambleKey, toupper(key[0]) );
+ unscrambled[1] = strchr( scrambleKey, toupper(key[1]) );
+ unscrambled[2] = strchr( scrambleKey, toupper(key[2]) );
+ unscrambled[3] = strchr( scrambleKey, toupper(key[3]) );
+ unscrambled[4] = strchr( scrambleKey, toupper(key[5]) );
+ unscrambled[5] = strchr( scrambleKey, toupper(key[6]) );
+ unscrambled[6] = strchr( scrambleKey, toupper(key[7]) );
+ unscrambled[7] = strchr( scrambleKey, toupper(key[8]) );
+
+ for( index=0; index<8; index++ )
+ {
+ if( unscrambled[index] == NULL ) return false;
+ value = (value << 4) | (unsigned int)(unscrambled[index] - scrambleKey);
+ }
+
+ // Get the flattened user name.
+ char flatName[256];
+ CopyFlattened( flatName, name );
+
+ // Return true if any of the hash functions succeed.
+ // Note that the hashes and comparisons mix up the ordering of bogus
+ // keys and valid keys to prevent early identification of bogus keys.
+
+ int flatLength = strlen(flatName);
+ if( flatLength < 4 ) return false;
+
+ unsigned int result0 = hash( (unsigned char*) flatName, flatLength, keylist[0] );
+ unsigned int result1 = hash( (unsigned char*) flatName, flatLength, keylist[1] );
+ unsigned int result2 = hash( (unsigned char*) flatName, flatLength, keylist[2] );
+ unsigned int result3 = hash( (unsigned char*) flatName, flatLength, keylist[3] );
+ unsigned int result4 = hash( (unsigned char*) flatName, flatLength, keylist[4] );
+ unsigned int result5 = hash( (unsigned char*) flatName, flatLength, keylist[5] );
+ unsigned int result6 = hash( (unsigned char*) flatName, flatLength, keylist[6] );
+ unsigned int result7 = hash( (unsigned char*) flatName, flatLength, keylist[7] );
+ unsigned int result8 = hash( (unsigned char*) flatName, flatLength, keylist[8] );
+ unsigned int result9 = hash( (unsigned char*) flatName, flatLength, keylist[9] );
+ unsigned int result10 = hash( (unsigned char*) flatName, flatLength, keylist[10] );
+ unsigned int result11 = hash( (unsigned char*) flatName, flatLength, keylist[11] );
+ unsigned int result12 = hash( (unsigned char*) flatName, flatLength, keylist[12] );
+
+ return (value == result0) ||
+ (value == result1) ||
+ (value == result2) ||
+ (value == result3) ||
+ (value == result4) ||
+ (value == result5) ||
+ (value == result6) ||
+ (value == result7) ||
+ (value == result8) ||
+ (value == result9) ||
+ (value == result10) ||
+ (value == result11) ||
+ (value == result12);
+
+/* A future version should become:
+
+ return ((value == result0) && (flatName[0] == 'A' || flatName[0] == 'B')) ||
+ ((value == result1) && (flatName[0] == 'C' || flatName[0] == 'D')) ||
+ ((value == result2) && (flatName[0] == 'E' || flatName[0] == 'F')) ||
+ ((value == result3) && (flatName[0] == 'G' || flatName[0] == 'H')) ||
+ ((value == result4) && (flatName[0] == 'I' || flatName[0] == 'J')) ||
+ ((value == result5) && (flatName[0] == 'K' || flatName[0] == 'L')) ||
+ ((value == result6) && (flatName[0] == 'M' || flatName[0] == 'N')) ||
+ ((value == result7) && (flatName[0] == 'O' || flatName[0] == 'P')) ||
+ ((value == result8) && (flatName[0] == 'Q' || flatName[0] == 'R')) ||
+ ((value == result9) && (flatName[0] == 'S' || flatName[0] == 'T')) ||
+ ((value == result10) && (flatName[0] == 'U' || flatName[0] == 'V')) ||
+ ((value == result11) && (flatName[0] == 'W' || flatName[0] == 'X')) ||
+ ((value == result12) && (flatName[0] == 'Y' || flatName[0] == 'Z'));
+
+*/
+}
+
+
--- /dev/null
+++ b/Source/RegAlgorithm.h
@@ -1,0 +1,19 @@
+///
+/// RegAlgorithm.h
+///
+
+
+
+#include "MTypes.h"
+
+extern const unsigned int keylist[13];
+extern const char* scrambleKey;
+
+
+// for code generator programs only!
+unsigned int hash( unsigned char* k, unsigned int length, unsigned int initval );
+void CopyFlattened( char* out, const char* in );
+
+// for client code...
+MBoolean ValidateCode( const char* name, const char* key );
+
--- /dev/null
+++ b/Source/RegGenerator.cpp
@@ -1,0 +1,82 @@
+///
+/// RegGenerator.cpp
+///
+/// Command-line tool for generating registration codes.
+///
+/// John Stiles, 2002/11/1
+///
+
+
+#include <stdio.h>
+#include "RegAlgorithm.h"
+
+
+static void GenerateCode( const char* name, char* outCode )
+{
+ int pool;
+ char flatName[256];
+ unsigned int value;
+
+ CopyFlattened( flatName, name );
+
+ switch( flatName[0] )
+ {
+ case 'A': case 'B': pool = 0; break;
+ case 'C': case 'D': pool = 1; break;
+ case 'E': case 'F': pool = 2; break;
+ case 'G': case 'H': pool = 3; break;
+ case 'I': case 'J': pool = 4; break;
+ case 'K': case 'L': pool = 5; break;
+ case 'M': case 'N': pool = 6; break;
+ case 'O': case 'P': pool = 7; break;
+ case 'Q': case 'R': pool = 8; break;
+ case 'S': case 'T': pool = 9; break;
+ case 'U': case 'V': pool = 10; break;
+ case 'W': case 'X': pool = 11; break;
+ case 'Y': case 'Z': pool = 12; break;
+ }
+
+ int flatLength = strlen(flatName);
+
+ value = hash( (unsigned char*) flatName, flatLength, keylist[pool] );
+
+ sprintf( outCode, "%c%c%c%c-%c%c%c%c",
+ scrambleKey[ (value >> 28) & 0xf ],
+ scrambleKey[ (value >> 24) & 0xf ],
+ scrambleKey[ (value >> 20) & 0xf ],
+ scrambleKey[ (value >> 16) & 0xf ],
+ scrambleKey[ (value >> 12) & 0xf ],
+ scrambleKey[ (value >> 8) & 0xf ],
+ scrambleKey[ (value >> 4) & 0xf ],
+ scrambleKey[ (value >> 0) & 0xf ] );
+}
+
+
+int main( int argc, char* argv[] )
+{
+ char code[64];
+
+ if( argc != 2 )
+ {
+ fprintf( stderr, "usage: crisisgen \"User Name\"\n" );
+ exit(1);
+ }
+
+ if( strlen(argv[1]) < 4 )
+ {
+ fprintf( stderr, "ERROR: name too short\n" );
+ exit(1);
+ }
+
+ GenerateCode( argv[1], code );
+
+ if( !ValidateCode( argv[1], code ) )
+ {
+ fprintf( stderr, "ERROR: code generation failure for name \"%s\"!", argv[1] );
+ exit(1);
+ }
+
+ printf( "%s\n", code );
+
+ return 0;
+}
--- /dev/null
+++ b/Source/SDLU.cpp
@@ -1,0 +1,880 @@
+///
+/// SDLU.c
+///
+/// SDL utilities.
+///
+/// John Stiles, 2002/10/12
+///
+
+#include "SDL.h"
+#include "SDLU.h"
+#include "gameticks.h"
+#include "music.h"
+
+#include "main.h" // for Error
+
+
+// for acquiresurface
+const int k_acquireMax = 10;
+static int s_acquireHead = -1;
+static SDL_Surface* s_acquireList[k_acquireMax];
+
+// for button and getmouse
+static int s_mouseButton;
+static MPoint s_mousePosition;
+
+// for event loop
+static MBoolean s_isForeground = true;
+
+// for checktyping
+static MBoolean s_interestedInTyping = false;
+static char s_keyBufferASCII[16] = { 0 };
+static SDLKey s_keyBufferSDL[16] = { (SDLKey) 0};
+static int s_keyBufferPullFrom = 0;
+static int s_keyBufferPutAt = 0;
+static int s_keyBufferFilled = 0;
+
+static void SDLUi_Blit8BitTo1Bit( SDL_Surface* surface8, SDL_Rect* rect8,
+ SDL_Surface* surface1, SDL_Rect* rect1 )
+{
+ // NOTE: for now this copy assumes that we're copying the whole thing.
+ // That's probably true for everything I'm doing. If it turns out that
+ // I need partial 8->1 copies, this won't be too hard to fix.
+
+ int x, y, across, down;
+ SDL_Color* palette8;
+
+ rect8; // is unused for now
+
+// ASSERTN( surface8->format->BitsPerPixel == 8, surface8->format->BitsPerPixel );
+// ASSERTN( surface1->format->BitsPerPixel == 1, surface8->format->BitsPerPixel );
+// ASSERT( rect8->w == rect1->w );
+// ASSERT( rect8->h == rect1->h );
+
+ palette8 = surface8->format->palette->colors;
+ down = rect1->h;
+ across = (rect1->w + 7) & ~7;
+
+ for( y=0; y<down; y++ )
+ {
+ unsigned char* src = (unsigned char*) surface8->pixels + (y * surface8->pitch);
+ unsigned char* dst = (unsigned char*) surface1->pixels + (y * surface1->pitch);
+
+ for( x=0; x<across; x+=8 )
+ {
+ *dst = (palette8[src[0]].r? 0: 0x80) |
+ (palette8[src[1]].r? 0: 0x40) |
+ (palette8[src[2]].r? 0: 0x20) |
+ (palette8[src[3]].r? 0: 0x10) |
+ (palette8[src[4]].r? 0: 0x08) |
+ (palette8[src[5]].r? 0: 0x04) |
+ (palette8[src[6]].r? 0: 0x02) |
+ (palette8[src[7]].r? 0: 0x01) ;
+
+ dst += 1;
+ src += 8;
+ }
+ }
+}
+
+
+static void SDLUi_Blit15BitTo16Bit( SDL_Surface* surface15, SDL_Rect* rect15,
+ SDL_Surface* surface16, SDL_Rect* rect16 )
+{
+ int x, y, srcPitch, dstPitch;
+ unsigned short* src;
+ unsigned short* dst;
+ unsigned short work;
+ int rect15x = rect15->x, rect15y = rect15->y, rect15w = rect15->w, rect15h = rect15->h;
+ int rect16x = rect16->x, rect16y = rect16->y, rect16w = rect16->w, rect16h = rect16->h;
+ int surface15w = surface15->w, surface15h = surface15->h;
+ int surface16w = surface16->w, surface16h = surface16->h;
+
+ // Clip.
+ if( rect15x < 0 ) { rect15w += rect15x; rect16w += rect15x; rect16x -= rect15x; rect15x = 0; }
+ if( rect15y < 0 ) { rect15h += rect15y; rect16h += rect15y; rect16y -= rect15y; rect15y = 0; }
+ if( (rect15x + rect15w) > surface15w ) { rect15w = surface15w - rect15x; }
+ if( (rect15y + rect15h) > surface15h ) { rect15h = surface15h - rect15y; }
+
+ if( rect16x < 0 ) { rect16w += rect16x; rect15w += rect16x; rect15x -= rect16x; rect16x = 0; }
+ if( rect16y < 0 ) { rect16h += rect16y; rect15h += rect16y; rect15y -= rect16y; rect16y = 0; }
+ if( (rect16x + rect16w) > surface16w ) { rect16w = surface16w - rect16x; }
+ if( (rect16y + rect16h) > surface16h ) { rect16h = surface16h - rect16y; }
+
+ // SDL changes the destination rectangle to the actual copied bounds. We need to do the same.
+ if( rect16w <= 0 || rect16h <= 0 )
+ {
+ rect16->w = 0; rect16->h = 0;
+ return;
+ }
+
+ rect16->x = rect16x; rect16->y = rect16y; rect16->w = rect16w; rect16->h = rect16h;
+
+ // Blit.
+ srcPitch = surface15->pitch;
+ dstPitch = surface16->pitch;
+
+ src = (unsigned short*) ( ((unsigned char*) surface15->pixels) + (rect15x * 2) + (rect15y * srcPitch) );
+ dst = (unsigned short*) ( ((unsigned char*) surface16->pixels) + (rect16x * 2) + (rect16y * dstPitch) );
+
+ for( y=0; y<rect16h; y++ )
+ {
+ for( x=0; x<rect16w; x++ )
+ {
+ work = src[x];
+ work = (work << 1) & 0xFFC0 |
+ (work >> 4) & 0x0020 |
+ (work ) & 0x001F;
+
+ dst[x] = work;
+ }
+
+ src = (unsigned short*) ( ((unsigned char*) src) + srcPitch );
+ dst = (unsigned short*) ( ((unsigned char*) dst) + dstPitch );
+ }
+}
+
+
+static void SDLUi_Blit16BitTo15Bit( SDL_Surface* surface16, SDL_Rect* rect16,
+ SDL_Surface* surface15, SDL_Rect* rect15 )
+{
+ int x, y, srcPitch, dstPitch;
+ unsigned short* src;
+ unsigned short* dst;
+ unsigned short work;
+ int rect15x = rect15->x, rect15y = rect15->y, rect15w = rect15->w, rect15h = rect15->h;
+ int rect16x = rect16->x, rect16y = rect16->y, rect16w = rect16->w, rect16h = rect16->h;
+ int surface15w = surface15->w, surface15h = surface15->h;
+ int surface16w = surface16->w, surface16h = surface16->h;
+
+ // Clip.
+ if( rect15x < 0 ) { rect15w += rect15x; rect16w += rect15x; rect16x -= rect15x; rect15x = 0; }
+ if( rect15y < 0 ) { rect15h += rect15y; rect16h += rect15y; rect16y -= rect15y; rect15y = 0; }
+ if( (rect15x + rect15w) > surface15w ) { rect15w = surface15w - rect15x; }
+ if( (rect15y + rect15h) > surface15h ) { rect15h = surface15h - rect15y; }
+
+ if( rect16x < 0 ) { rect16w += rect16x; rect15w += rect16x; rect15x -= rect16x; rect16x = 0; }
+ if( rect16y < 0 ) { rect16h += rect16y; rect15h += rect16y; rect15y -= rect16y; rect16y = 0; }
+ if( (rect16x + rect16w) > surface16w ) { rect16w = surface16w - rect16x; }
+ if( (rect16y + rect16h) > surface16h ) { rect16h = surface16h - rect16y; }
+
+ // SDL changes the destination rectangle to the actual copied bounds. We need to do the same.
+ if( rect15w <= 0 || rect15h <= 0 )
+ {
+ rect15->w = 0; rect15->h = 0;
+ return;
+ }
+
+ rect15->x = rect15x; rect15->y = rect15y; rect15->w = rect15w; rect15->h = rect15h;
+
+ // Blit.
+ srcPitch = surface16->pitch;
+ dstPitch = surface15->pitch;
+
+ src = (unsigned short*) ( ((unsigned char*) surface16->pixels) + (rect16x * 2) + (rect16y * srcPitch) );
+ dst = (unsigned short*) ( ((unsigned char*) surface15->pixels) + (rect15x * 2) + (rect15y * dstPitch) );
+
+ for( y=0; y<rect15h; y++ )
+ {
+ for( x=0; x<rect15w; x++ )
+ {
+ work = src[x];
+ work = (work >> 1) & 0x7FE0 |
+ (work ) & 0x001F;
+
+ dst[x] = work;
+ }
+
+ src = (unsigned short*) ( ((unsigned char*) src) + srcPitch );
+ dst = (unsigned short*) ( ((unsigned char*) dst) + dstPitch );
+ }
+}
+
+
+static void SDLUi_Blit24BitTo15BitHQ( SDL_Surface* surface24, SDL_Rect* rect24,
+ SDL_Surface* surface15, SDL_Rect* rect15 )
+{
+ int x, y, x3, srcPitch, dstPitch;
+ unsigned char* src;
+ unsigned short* dst;
+ unsigned long work;
+ int rect15x = rect15->x, rect15y = rect15->y, rect15w = rect15->w, rect15h = rect15->h, rect15wE;
+ int rect24x = rect24->x, rect24y = rect24->y, rect24w = rect24->w, rect24h = rect24->h;
+ int surface15w = surface15->w, surface15h = surface15->h;
+ int surface24w = surface24->w, surface24h = surface24->h;
+
+ const unsigned char* aPixels = NULL;
+ const unsigned char* bPixels = NULL;
+ const unsigned char* swapPixels = NULL;
+
+ const unsigned char copyPixels[256] =
+ {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4,
+ 5, 5, 5, 5, 5, 5, 5, 5,
+ 6, 6, 6, 6, 6, 6, 6, 6,
+ 7, 7, 7, 7, 7, 7, 7, 7,
+ 8, 8, 8, 8, 8, 8, 8, 8,
+ 9, 9, 9, 9, 9, 9, 9, 9,
+ 10, 10, 10, 10, 10, 10, 10, 10,
+ 11, 11, 11, 11, 11, 11, 11, 11,
+ 12, 12, 12, 12, 12, 12, 12, 12,
+ 13, 13, 13, 13, 13, 13, 13, 13,
+ 14, 14, 14, 14, 14, 14, 14, 14,
+ 15, 15, 15, 15, 15, 15, 15, 15,
+ 16, 16, 16, 16, 16, 16, 16, 16,
+ 17, 17, 17, 17, 17, 17, 17, 17,
+ 18, 18, 18, 18, 18, 18, 18, 18,
+ 19, 19, 19, 19, 19, 19, 19, 19,
+ 20, 20, 20, 20, 20, 20, 20, 20,
+ 21, 21, 21, 21, 21, 21, 21, 21,
+ 22, 22, 22, 22, 22, 22, 22, 22,
+ 23, 23, 23, 23, 23, 23, 23, 23,
+ 24, 24, 24, 24, 24, 24, 24, 24,
+ 25, 25, 25, 25, 25, 25, 25, 25,
+ 26, 26, 26, 26, 26, 26, 26, 26,
+ 27, 27, 27, 27, 27, 27, 27, 27,
+ 28, 28, 28, 28, 28, 28, 28, 28,
+ 29, 29, 29, 29, 29, 29, 29, 29,
+ 30, 30, 30, 30, 30, 30, 30, 30,
+ 31, 31, 31, 31, 31, 31, 31, 31
+ };
+
+ const unsigned char ditherPixels[256] =
+ {
+ 0, 0, 0, 0, 1, 1, 1, 1,
+ 1, 1, 1, 1, 2, 2, 2, 2,
+ 2, 2, 2, 2, 3, 3, 3, 3,
+ 3, 3, 3, 3, 4, 4, 4, 4,
+ 4, 4, 4, 4, 5, 5, 5, 5,
+ 5, 5, 5, 5, 6, 6, 6, 6,
+ 6, 6, 6, 6, 7, 7, 7, 7,
+ 7, 7, 7, 7, 8, 8, 8, 8,
+ 8, 8, 8, 8, 9, 9, 9, 9,
+ 9, 9, 9, 9, 10, 10, 10, 10,
+ 10, 10, 10, 10, 11, 11, 11, 11,
+ 11, 11, 11, 11, 12, 12, 12, 12,
+ 12, 12, 12, 12, 13, 13, 13, 13,
+ 13, 13, 13, 13, 14, 14, 14, 14,
+ 14, 14, 14, 14, 15, 15, 15, 15,
+ 15, 15, 15, 15, 16, 16, 16, 16,
+ 16, 16, 16, 16, 17, 17, 17, 17,
+ 17, 17, 17, 17, 18, 18, 18, 18,
+ 18, 18, 18, 18, 19, 19, 19, 19,
+ 19, 19, 19, 19, 20, 20, 20, 20,
+ 20, 20, 20, 20, 21, 21, 21, 21,
+ 21, 21, 21, 21, 22, 22, 22, 22,
+ 22, 22, 22, 22, 23, 23, 23, 23,
+ 23, 23, 23, 23, 24, 24, 24, 24,
+ 24, 24, 24, 24, 25, 25, 25, 25,
+ 25, 25, 25, 25, 26, 26, 26, 26,
+ 26, 26, 26, 26, 27, 27, 27, 27,
+ 27, 27, 27, 27, 28, 28, 28, 28,
+ 28, 28, 28, 28, 29, 29, 29, 29,
+ 29, 29, 29, 29, 30, 30, 30, 30,
+ 30, 30, 30, 30, 31, 31, 31, 31,
+ 31, 31, 31, 31, 31, 31, 31, 31
+ };
+
+ // Clip.
+ if( rect15x < 0 ) { rect15w += rect15x; rect24w += rect15x; rect24x -= rect15x; rect15x = 0; }
+ if( rect15y < 0 ) { rect15h += rect15y; rect24h += rect15y; rect24y -= rect15y; rect15y = 0; }
+ if( (rect15x + rect15w) > surface15w ) { rect15w = surface15w - rect15x; }
+ if( (rect15y + rect15h) > surface15h ) { rect15h = surface15h - rect15y; }
+
+ if( rect24x < 0 ) { rect24w += rect24x; rect15w += rect24x; rect15x -= rect24x; rect24x = 0; }
+ if( rect24y < 0 ) { rect24h += rect24y; rect15h += rect24y; rect15y -= rect24y; rect24y = 0; }
+ if( (rect24x + rect24w) > surface24w ) { rect24w = surface24w - rect24x; }
+ if( (rect24y + rect24h) > surface24h ) { rect24h = surface24h - rect24y; }
+
+ // SDL changes the destination rectangle to the actual copied bounds. We need to do the same.
+ if( rect15w <= 0 || rect15h <= 0 )
+ {
+ rect15->w = 0; rect15->h = 0;
+ return;
+ }
+
+ rect15->x = rect15x; rect15->y = rect15y; rect15->w = rect15w; rect15->h = rect15h;
+
+ // Blit.
+ srcPitch = surface24->pitch;
+ dstPitch = surface15->pitch;
+
+ src = (unsigned char*) ( ((unsigned char*) surface24->pixels) + (rect24x * 3) + (rect24y * srcPitch) );
+ dst = (unsigned short*) ( ((unsigned char*) surface15->pixels) + (rect15x * 2) + (rect15y * dstPitch) );
+ rect15wE = rect15w & ~1;
+
+ for( y=0; y<rect15h; y++ )
+ {
+ if( y & 1 )
+ {
+ aPixels = copyPixels;
+ bPixels = ditherPixels;
+ }
+ else
+ {
+ aPixels = ditherPixels;
+ bPixels = copyPixels;
+ }
+
+ x=0; x3=0;
+ while( x < rect15wE )
+ {
+ work = (aPixels[src[x3+0]] << 10) |
+ (aPixels[src[x3+1]] << 5) |
+ (aPixels[src[x3+2]] );
+
+ dst[x] = work;
+
+ x++; x3+=3;
+
+ work = (bPixels[src[x3+0]] << 10) |
+ (bPixels[src[x3+1]] << 5) |
+ (bPixels[src[x3+2]] );
+
+ dst[x] = work;
+
+ x++; x3+=3;
+ }
+
+ if( rect15w & 1 )
+ {
+ work = (aPixels[src[x3+0]] << 10) |
+ (aPixels[src[x3+1]] << 5) |
+ (aPixels[src[x3+2]] );
+
+ dst[x] = work;
+ }
+
+ src = (unsigned char*) ( ((unsigned char*) src) + srcPitch );
+ dst = (unsigned short*) ( ((unsigned char*) dst) + dstPitch );
+ }
+}
+
+
+static void SDLUi_SetGrayscaleColors( SDL_Surface* surface )
+{
+ SDL_Color grayscalePalette[256];
+ int index;
+
+ for( index=0; index<256; index++ )
+ {
+ grayscalePalette[index].r =
+ grayscalePalette[index].g =
+ grayscalePalette[index].b = 255 - index;
+ grayscalePalette[index].unused = 0;
+ }
+
+ SDL_SetColors( surface, grayscalePalette, 0, 256 );
+}
+
+
+/*
+GWorldPtr SDLU_SurfaceToGWorld( SDL_Surface* surface )
+{
+ OSErr err;
+ int depth = 0;
+ GWorldPtr gworld;
+
+ switch( surface->format->BitsPerPixel )
+ {
+ case 1:
+ depth = 1;
+ break;
+
+ case 15:
+ depth = 16;
+ break;
+
+ default:
+ Error( errUnknown, "\pSDLU_SurfaceToGWorld: depth" );
+ break;
+ }
+
+ MRect mRect = { 0, 0, surface->h, surface->w };
+
+ err = NewGWorldFromPtr( &gworld, depth, &mRect, NULL, NULL, 0, (Ptr) surface->pixels, surface->pitch );
+ if( err != noErr )
+ {
+ Error( errUnknown, "\pSDLU_SurfaceToGWorld: NewGWorldFromPtr" );
+ }
+
+ return gworld;
+}
+*/
+
+
+/*
+void SDLU_DumpSurface( SDL_Surface* surface, const char* filenamePre, const char* filenamePost )
+{
+#if TARGET_API_MAC_CARBON
+ OSErr err;
+ int depth = 0;
+ GWorldPtr gworld;
+
+ switch( surface->format->BitsPerPixel )
+ {
+ case 1:
+ depth = 1;
+ break;
+
+ case 15:
+ depth = 16;
+ break;
+
+ case 16:
+ depth = 16;
+ break;
+
+ default:
+ Error( "SDLU_DumpSurface: depth" );
+ break;
+ }
+
+ Rect mRect = { 0, 0, surface->h, surface->w };
+
+ err = NewGWorldFromPtr( &gworld, depth, &mRect, NULL, NULL, 0, (Ptr) surface->pixels, surface->pitch );
+ if( err != noErr )
+ {
+ Error( "SDLU_DumpSurface: NewGWorldFromPtr" );
+ }
+
+ CGrafPtr port; GDHandle gdh;
+ GetGWorld( &port, &gdh );
+ SetGWorld( gworld, NULL );
+ PicHandle thePicture = OpenPicture( &mRect );
+ CopyBits( GetPortBitMapForCopyBits(gworld), GetPortBitMapForCopyBits(gworld),
+ &mRect, &mRect,
+ srcCopy, nil );
+ ClosePicture( );
+ SetGWorld( port, gdh );
+
+ FSSpec filespec = {0};
+ static int incr = 0;
+ filespec.name[0] = sprintf( (char*) &filespec.name[1], "%s%d%s.jpg", filenamePre, incr++, filenamePost );
+
+ OSType filetype = kQTFileTypeJPEG;
+ OSType filecreator = 'TVOD';
+ int filescriptcode = smSystemScript;
+
+ // see: http://developer.apple.com/quicktime/icefloe/dispatch014.html
+ {
+ Handle h;
+ OSErr err;
+ GraphicsImportComponent gi = 0;
+
+ // Convert the picture handle into a PICT file (still in a handle)
+ // by adding a 512-byte header to the start.
+ h = NewHandleClear(512);
+ err = MemError();
+ if(err) goto bail;
+ err = HandAndHand((Handle)thePicture,h);
+
+ err = OpenADefaultComponent(
+ GraphicsImporterComponentType,
+ kQTFileTypePicture,
+ &gi);
+ if(err) goto bail;
+
+ err = GraphicsImportSetDataHandle(gi, h);
+ if(err) goto bail;
+
+ err = GraphicsImportExportImageFile(
+ gi,
+ filetype,
+ filecreator,
+ &filespec,
+ filescriptcode);
+ if(err) goto bail;
+
+ bail:
+ if(gi) CloseComponent(gi);
+ if(h) DisposeHandle(h);
+ }
+
+ DisposeHandle( (Handle) thePicture );
+ DisposeGWorld( gworld );
+#endif
+}
+*/
+
+
+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 )
+{
+ if( src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 1 )
+ {
+ // SDL BUG!! SDL cannot blit 8-bit to 1-bit surfaces.
+ SDLUi_Blit8BitTo1Bit( src, srcrect,
+ dst, dstrect );
+ return 0;
+ }
+
+ // SDL PERFORMANCE: SDL sucks at blitting 15->16 and 16->15.
+ // Use hand-rolled fast blitters to solve this.
+ if( src->format->BitsPerPixel == 15 && dst->format->BitsPerPixel == 16 )
+ {
+ SDLUi_Blit15BitTo16Bit( src, srcrect,
+ dst, dstrect );
+ return 0;
+ }
+
+
+ if( src->format->BitsPerPixel == 16 && dst->format->BitsPerPixel == 15 )
+ {
+ SDLUi_Blit16BitTo15Bit( src, srcrect,
+ dst, dstrect );
+ return 0;
+ }
+
+ // Let SDL handle this.
+ return SDL_BlitSurface( src, srcrect,
+ dst, dstrect );
+}
+
+
+int SDLU_BlitSurfaceHQ( SDL_Surface* src, SDL_Rect* srcrect,
+ SDL_Surface* dst, SDL_Rect* dstrect )
+{
+ // QUALITY: Use a hand-rolled 24->15 dithering blitter.
+ if( src->format->BitsPerPixel == 24 && dst->format->BitsPerPixel == 15 )
+ {
+ SDLUi_Blit24BitTo15BitHQ( src, srcrect,
+ dst, dstrect );
+ return 0;
+ }
+
+ // Let SDL handle this.
+ return SDLU_BlitSurface( src, srcrect,
+ dst, dstrect );
+}
+
+
+void SDLU_GetPixel( SDL_Surface* surface, int x, int y, SDL_Color* pixel )
+{
+ unsigned long 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 long*) ptr;
+ break;
+ }
+
+ 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_BlitSurfaceHQ( *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;
+ SDL_Color k_oneBitPalette[2] = { { 0xFF, 0xFF, 0xFF, 0x00 },
+ { 0x00, 0x00, 0x00, 0x00 } };
+
+ switch( depth )
+ {
+ case 16:
+ surface = SDL_CreateRGBSurface(
+ SDL_SWSURFACE,
+ rect->w,
+ rect->h,
+ 15,
+ 0x7C00, 0x03E0, 0x001F, 0x0000 );
+ break;
+
+ case 8:
+ surface = SDL_CreateRGBSurface(
+ SDL_SWSURFACE,
+ rect->w,
+ rect->h,
+ 8,
+ 0, 0, 0, 0 );
+
+ SDLUi_SetGrayscaleColors( surface );
+ break;
+
+ case 1:
+ surface = SDL_CreateRGBSurface(
+ SDL_SWSURFACE,
+ rect->w,
+ rect->h,
+ 1,
+ 0, 0, 0, 0 );
+
+ SDL_SetColors( surface, k_oneBitPalette, 0, 2 );
+ break;
+ }
+
+ if( surface == NULL )
+ {
+ Error( "SDLU_InitSurface: SDL_CreateRGBSurface" );
+ return NULL;
+ }
+
+ // SDL BUG!! SDL_FillRect blows up with 1-depth surfaces. WORKAROUND: don't auto-clear them. Seems OK.
+ // (Next step is always to copy a PNG into them.)
+ 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 )
+{
+ extern SDL_Surface* frontSurface;
+ SDLU_BlitSurface( source, sourceSDLRect,
+ frontSurface, destSDLRect );
+
+ SDL_UpdateRect( frontSurface, destSDLRect->x, destSDLRect->y, destSDLRect->w, destSDLRect->h );
+}
+
+
+void SDLU_SetBrightness( float b )
+{
+ Uint16 table[256];
+ int index;
+
+ for( index=0; index<256; index++ )
+ {
+ table[index] = (int)(index * b * 257.0f); // 255 * 257 = 65535
+ }
+
+ SDL_SetGammaRamp( table, table, table );
+}
+
+void SDLU_Yield()
+{
+ SDL_Delay( 2 );
+ SDL_PumpEvents();
+}
+
+void SDLU_PumpEvents()
+{
+ static unsigned long lastPump = 0;
+ unsigned long time = MTickCount();
+
+ if( lastPump != time )
+ {
+ SDL_Event evt;
+ while( SDL_PollEvent( &evt ) ) { }
+ lastPump = time;
+ }
+}
+
+
+MBoolean SDLU_IsForeground()
+{
+ return s_isForeground;
+}
+
+
+int SDLU_EventFilter( const SDL_Event *event )
+{
+ // Put keydowns in a buffer
+ if( event->type == SDL_KEYDOWN )
+ {
+ if( s_interestedInTyping
+ && event->key.keysym.unicode <= 127
+ && s_keyBufferFilled < sizeof(s_keyBufferASCII) )
+ {
+ s_keyBufferFilled++;
+ s_keyBufferASCII[s_keyBufferPutAt] = event->key.keysym.unicode;
+ s_keyBufferSDL [s_keyBufferPutAt] = event->key.keysym.sym;
+ s_keyBufferPutAt = (s_keyBufferPutAt + 1) % sizeof(s_keyBufferASCII);
+ }
+
+ if( (event->key.keysym.sym == SDLK_F4)
+ && (event->key.keysym.mod & (KMOD_LALT | KMOD_RALT)) )
+ {
+ finished = true;
+ }
+
+ return 0;
+ }
+
+ // Get mouse state
+ if( event->type == SDL_MOUSEBUTTONDOWN )
+ {
+ if( event->button.button == SDL_BUTTON_LEFT )
+ s_mouseButton = true;
+
+ s_mousePosition.v = event->button.y;
+ s_mousePosition.h = event->button.x;
+ return 0;
+ }
+
+ if( event->type == SDL_MOUSEBUTTONUP )
+ {
+ if( event->button.button == SDL_BUTTON_LEFT )
+ s_mouseButton = false;
+
+ s_mousePosition.v = event->button.y;
+ s_mousePosition.h = event->button.x;
+ return 0;
+ }
+
+ if( event->type == SDL_MOUSEMOTION )
+ {
+ s_mousePosition.v = event->motion.y;
+ s_mousePosition.h = event->motion.x;
+ s_mouseButton = event->motion.state & SDL_BUTTON(1);
+ return 0;
+ }
+
+ if( event->type == SDL_QUIT )
+ {
+ finished = true;
+ return 0;
+ }
+
+ // Handle gaining and losing focus (kind of cheesy)
+ if( event->type == SDL_ACTIVEEVENT && (event->active.state & SDL_APPINPUTFOCUS) )
+ {
+ if( !event->active.gain && s_isForeground )
+ {
+ FreezeGameTickCount();
+ EnableMusic(false);
+ s_isForeground = false;
+ }
+ else if( event->active.gain && !s_isForeground )
+ {
+ UnfreezeGameTickCount();
+ EnableMusic(musicOn);
+ s_isForeground = true;
+
+ DoFullRepaint();
+ }
+ }
+
+ // We never poll for events, we just process them here, so discard everything.
+ return 0;
+}
+
+
+void SDLU_StartWatchingTyping()
+{
+ s_interestedInTyping = true;
+ s_keyBufferFilled = s_keyBufferPullFrom = s_keyBufferPutAt = 0;
+ SDL_EnableUNICODE( 1 );
+ SDL_EnableKeyRepeat( SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL );
+}
+
+
+void SDLU_StopWatchingTyping()
+{
+ s_interestedInTyping = false;
+ SDL_EnableUNICODE( 0 );
+ SDL_EnableKeyRepeat( 0, 0 );
+}
+
+
+MBoolean SDLU_CheckTyping( char* ascii, SDLKey* sdl )
+{
+ if( s_keyBufferFilled > 0 )
+ {
+ *ascii = s_keyBufferASCII[s_keyBufferPullFrom];
+ *sdl = s_keyBufferSDL [s_keyBufferPullFrom];
+ s_keyBufferPullFrom = (s_keyBufferPullFrom + 1) % sizeof(s_keyBufferASCII);
+ s_keyBufferFilled--;
+ return true;
+ }
+
+ *ascii = '\0';
+ *sdl = SDLK_UNKNOWN;
+ return false;
+}
+
+
+void SDLU_GetMouse( MPoint* pt )
+{
+ SDLU_PumpEvents();
+ *pt = s_mousePosition;
+}
+
+
+int SDLU_Button()
+{
+ SDLU_PumpEvents();
+ return s_mouseButton;
+}
+
+
+void SDLU_AcquireSurface( SDL_Surface* surface )
+{
+ s_acquireList[++s_acquireHead] = surface;
+}
+
+
+SDL_Surface* SDLU_GetCurrentSurface()
+{
+ return s_acquireList[s_acquireHead];
+}
+
+
+void SDLU_ReleaseSurface( SDL_Surface* surface )
+{
+ if( s_acquireList[s_acquireHead] != surface )
+ Error( "SDLU_ReleaseSurface: out of order" );
+
+ if( s_acquireHead < 0 )
+ Error( "SDLU_ReleaseSurface: underflow" );
+
+ s_acquireHead--;
+}
--- /dev/null
+++ b/Source/SDLU.h
@@ -1,0 +1,37 @@
+///
+/// SDLU.h
+///
+
+
+#ifndef __SDLU__
+#define __SDLU__
+
+
+#include "MTypes.h"
+
+
+//GWorldPtr SDLU_SurfaceToGWorld( SDL_Surface* surface );
+SDL_Rect* SDLU_MRectToSDLRect( const MRect* in, SDL_Rect* out );
+MRect* SDLU_SDLRectToMRect( const SDL_Rect* in, MRect* out );
+int SDLU_BlitSurface( SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect );
+void SDLU_GetPixel( SDL_Surface* surface, int x, int y, SDL_Color* pixel );
+void SDLU_ChangeSurfaceDepth( SDL_Surface** surface, int depth );
+SDL_Surface* SDLU_InitSurface( SDL_Rect* rect, int depth );
+void SDLU_BlitFrontSurface( SDL_Surface* source, SDL_Rect* sourceSDLRect, SDL_Rect* destSDLRect );
+void SDLU_SetBrightness( float b );
+void SDLU_AcquireSurface( SDL_Surface* surface );
+SDL_Surface* SDLU_GetCurrentSurface();
+void SDLU_ReleaseSurface( SDL_Surface* surface );
+void SDLU_GetMouse( MPoint* pt );
+int SDLU_Button();
+void SDLU_Yield();
+void SDLU_PumpEvents();
+int SDLU_EventFilter( const SDL_Event *event );
+void SDLU_StartWatchingTyping();
+void SDLU_StopWatchingTyping();
+MBoolean SDLU_CheckTyping( char* ascii, SDLKey* sdl );
+int SDLU_BlitSurfaceHQ( SDL_Surface* src, SDL_Rect* srcrect, SDL_Surface* dst, SDL_Rect* dstrect );
+MBoolean SDLU_IsForeground();
+
+
+#endif
--- /dev/null
+++ b/Source/blitter.cpp
@@ -1,0 +1,882 @@
+// blitter.c
+
+#include "SDL.h"
+#include "SDLU.h"
+#include "SDL_endian.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 long bit, startBit, maskBits;
+ 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 * 2);
+ msk += (maskRect->top * mskRowBytes) + (maskRect->left / 8);
+ dst += (destRect->top * dstRowBytes) + (destRect->left * 2);
+
+ 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;
+
+ startBit = 0x80000000 >> (startX & 31);
+ msk += (mskRowBytes * startY) + ((startX & ~31) / 8);
+ src += (srcRowBytes * startY) + (startX * 2);
+ dst += (dstRowBytes * startY) + (startX * 2);
+
+ for( y=startY; y<endY; y++ )
+ {
+ unsigned char *tSrc = src, *tDst = dst, *tMsk = msk;
+
+ maskBits = SDL_SwapBE32( *(unsigned long*)msk );
+ bit = startBit;
+
+ for( x=startX; x<endX; x++ )
+ {
+ if( maskBits & bit )
+ {
+ *(short*)dst = *(short*)src;
+ }
+
+ if( !(bit >>= 1) ) { msk += 4; maskBits = SDL_SwapBE32( *(unsigned long*)msk ); bit = 0x80000000; }
+ src += 2; dst += 2;
+ }
+
+ 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 long bit, startBit, maskBits;
+ 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 / 8);
+ dst += (destRect->top * dstRowBytes) + (destRect->left * 2);
+
+ 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;
+
+ startBit = 0x80000000 >> (startX & 31);
+ msk += (mskRowBytes * startY) + ((startX & ~31) / 8);
+ dst += (dstRowBytes * startY) + (startX * 2);
+
+ r *= weight;
+ g *= weight;
+ b *= weight;
+ weight = 32 - weight;
+
+ for( y=startY; y<endY; y++ )
+ {
+ unsigned char *tMsk = msk, *tDst = dst;
+ int work, workB, workG, workR;
+
+ maskBits = SDL_SwapBE32( *(unsigned long*)msk );
+ bit = startBit;
+
+ for( x=startX; x<endX; x++ )
+ {
+ if( maskBits & bit )
+ {
+ work = *(short*)dst;
+ workB = ((((work ) & 0x001F)*weight) + b) >> 5;
+ workG = ((((work>> 5) & 0x001F)*weight) + g);
+ workR = ((((work>>10) & 0x001F)*weight) + r) << 5;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+ }
+
+ if( !(bit >>= 1) ) { msk += 4; maskBits = SDL_SwapBE32( *(unsigned long*)msk ); bit = 0x80000000; }
+ dst += 2;
+ }
+
+ 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 * 2);
+ src += (sourceRect->top * srcRowBytes) + (sourceRect->left * 2);
+ alf += (alphaRect->top * alfRowBytes) + (alphaRect->left * 2);
+ dst += (destRect->top * dstRowBytes) + (destRect->left * 2);
+
+ 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 * 2);
+ src += (srcRowBytes * startY) + (startX * 2);
+ alf += (alfRowBytes * startY) + (startX * 2);
+ dst += (dstRowBytes * startY) + (startX * 2);
+
+ 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 = *(short*)alf;
+
+ if( weightB == 0x7FFF )
+ {
+ *(short*)dst = *(short*)bck;
+ }
+ else if( weightB == 0 )
+ {
+ *(short*)dst = *(short*)src;
+ }
+ else
+ {
+ weightS = ~weightB;
+
+ pixS = *(short*)src;
+ pixB = *(short*)bck;
+
+ workB = ((((pixS ) & 0x001F) * ((weightS ) & 0x001F)) + (((pixB ) & 0x001F) * ((weightB ) & 0x001F))) >> 5;
+ workG = ((((pixS >> 5) & 0x001F) * ((weightS >> 5) & 0x001F)) + (((pixB >> 5) & 0x001F) * ((weightB >> 5) & 0x001F)));
+ workR = ((((pixS >> 10) & 0x001F) * ((weightS >> 10) & 0x001F)) + (((pixB >> 10) & 0x001F) * ((weightB >> 10) & 0x001F))) << 5;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+ }
+
+ src += 2;
+ alf += 2;
+ bck += 2;
+ dst += 2;
+ }
+
+ 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 long bit, startBit, maskBits;
+ 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 * 2);
+ src += (sourceRect->top * srcRowBytes) + (sourceRect->left * 2);
+ alf += (alphaRect->top * alfRowBytes) + (alphaRect->left * 2);
+ dst += (destRect->top * dstRowBytes) + (destRect->left * 2);
+ msk += (maskRect->top * mskRowBytes) + (maskRect->left / 8);
+
+ 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 * 2);
+ src += (srcRowBytes * startY) + (startX * 2);
+ alf += (alfRowBytes * startY) + (startX * 2);
+ dst += (dstRowBytes * startY) + (startX * 2);
+ msk += (mskRowBytes * startY) + ((startX & ~31) / 8);
+ startBit = 0x80000000 >> (startX & 31);
+
+ 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;
+
+ maskBits = SDL_SwapBE32( *(unsigned long*)msk );
+ bit = startBit;
+
+ for( x=startX; x<endX; x++ )
+ {
+ if( maskBits & bit )
+ {
+ work = *(short*)alf;
+ workB = ((((work ) & 0x001F)*inWeight) ) >> 5;
+ workG = ((((work>> 5) & 0x001F)*inWeight) );
+ workR = ((((work>>10) & 0x001F)*inWeight) ) << 5;
+
+ weightB = ~((workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F));
+
+ if( weightB == 0x7FFF )
+ {
+ *(short*)dst = *(short*)bck;
+ }
+ else if( weightB == 0 )
+ {
+ *(short*)dst = *(short*)src;
+ }
+ else
+ {
+ weightS = ~weightB;
+
+ pixS = *(short*)src;
+ pixB = *(short*)bck;
+
+ workB = ((((pixS ) & 0x001F) * ((weightS ) & 0x001F)) + (((pixB ) & 0x001F) * ((weightB ) & 0x001F))) >> 5;
+ workG = ((((pixS >> 5) & 0x001F) * ((weightS >> 5) & 0x001F)) + (((pixB >> 5) & 0x001F) * ((weightB >> 5) & 0x001F)));
+ workR = ((((pixS >> 10) & 0x001F) * ((weightS >> 10) & 0x001F)) + (((pixB >> 10) & 0x001F) * ((weightB >> 10) & 0x001F))) << 5;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+ }
+ }
+
+ if( !(bit >>= 1) ) { msk += 4; maskBits = SDL_SwapBE32( *(unsigned long*)msk ); bit = 0x80000000; }
+ src += 2;
+ alf += 2;
+ bck += 2;
+ dst += 2;
+ }
+
+ 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 == 31 )
+ {
+ SurfaceBlitCharacter( font, text, dPoint, r, g, b, 0 );
+ }
+ else if( alpha == 0 )
+ {
+ return;
+ }
+ else
+ {
+ 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 * 2) + (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 >> 3) & 0x1F;
+
+ weightS = (weightS * alpha) >> 5;
+
+ if( weightS )
+ {
+ weightD = 32 - weightS;
+
+ work = *(short*)dst;
+ workB = ((((work ) & 0x001F) * weightD) + (b * weightS)) >> 5;
+ workG = ((((work>> 5) & 0x001F) * weightD) + (g * weightS));
+ workR = ((((work>>10) & 0x001F) * weightD) + (r * weightS)) << 5;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+ }
+
+ src++;
+ dst+=2;
+ }
+
+ 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 rgb555;
+ 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 * 2) + (dPoint->v * dstRowBytes);
+ rgb555 = (r << 10) | (g << 5) | 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 >> 3) & 0x1F;
+
+ if( weightS == 31 )
+ {
+ *(short*)dst = rgb555;
+ }
+ else if( weightS > 0 )
+ {
+ weightD = 32 - weightS;
+
+ work = *(short*)dst;
+ workB = ((((work ) & 0x001F) * weightD) + (b * weightS)) >> 5;
+ workG = ((((work>> 5) & 0x001F) * weightD) + (g * weightS));
+ workR = ((((work>>10) & 0x001F) * weightD) + (r * weightS)) << 5;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+ }
+
+ src++;
+ dst+=2;
+ }
+
+ src = tSrc + srcRowBytes;
+ dst = tDst + dstRowBytes;
+ }
+ break;
+
+ default:
+ {
+ unsigned char *drp = dst + ((dstRowBytes + 2) * 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 >> 3) & 0x1F;
+
+ if( weightS == 31 )
+ {
+ *(short*)drp = 0;
+ *(short*)dst = rgb555;
+ }
+ else if( weightS > 0 )
+ {
+ weightD = 32 - weightS;
+
+ work = *(short*)drp;
+ workB = ((((work ) & 0x001F) * weightD)) >> 5;
+ workG = ((((work>> 5) & 0x001F) * weightD));
+ workR = ((((work>>10) & 0x001F) * weightD)) << 5;
+ *(short*)drp = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+
+ work = *(short*)dst;
+ workB = ((((work ) & 0x001F) * weightD) + (b * weightS)) >> 5;
+ workG = ((((work>> 5) & 0x001F) * weightD) + (g * weightS));
+ workR = ((((work>>10) & 0x001F) * weightD) + (r * weightS)) << 5;
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+ }
+
+ src++;
+ dst+=2;
+ drp+=2;
+ }
+
+ 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 * 2);
+ dst += (destRect->top * dstRowBytes) + (destRect->left * 2);
+
+ 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 * 2);
+ dst += (dstRowBytes * startY) + (startX * 2);
+
+ r *= weight;
+ g *= weight;
+ b *= weight;
+ weight = 32 - 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 = *(short*)src;
+ workB = ((((work ) & 0x001F)*weight) + b) >> 5;
+ workG = ((((work>> 5) & 0x001F)*weight) + g);
+ workR = ((((work>>10) & 0x001F)*weight) + r) << 5;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+
+ src += 2;
+ dst += 2;
+ }
+
+ 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 31.
+// 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 weight16, 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 * 2);
+ dst += (destRect->top * dstRowBytes) + (destRect->left * 2);
+
+ 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 * 2);
+ dst += (dstRowBytes * startY) + (startX * 2);
+
+ height = endY - startY;
+ width = endX - startX;
+
+ weight16 = weight << 16;
+
+ r1 *= weight16; g1 *= weight16; b1 *= weight16;
+ r2 *= weight16; g2 *= weight16; b2 *= weight16;
+ r3 *= weight16; g3 *= weight16; b3 *= weight16;
+ r4 *= weight16; g4 *= weight16; b4 *= weight16;
+ ditherDiff = weight16 >> 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 = 32 - weight;
+
+ 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 = *(short*)src;
+ workB = ((((work<<16) & 0x001F0000)*weight) + bA) >> 21;
+ workG = ((((work<<11) & 0x001F0000)*weight) + gA) >> 16;
+ workR = ((((work<< 6) & 0x001F0000)*weight) + rA) >> 11;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+
+ src+= 2;
+ dst+= 2;
+ rA += rI;
+ gA += gI;
+ bA += bI;
+
+ work = *(short*)src;
+ workB = ((((work<<16) & 0x001F0000)*weight) + bB) >> 21;
+ workG = ((((work<<11) & 0x001F0000)*weight) + gB) >> 16;
+ workR = ((((work<< 6) & 0x001F0000)*weight) + rB) >> 11;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+
+ src+= 2;
+ dst+= 2;
+ rB += rI;
+ gB += gI;
+ bB += bI;
+ }
+
+ if( oddX )
+ {
+ work = *(short*)src;
+ workB = ((((work<<16) & 0x001F0000)*weight) + bA) >> 21;
+ workG = ((((work<<11) & 0x001F0000)*weight) + gA) >> 16;
+ workR = ((((work<< 6) & 0x001F0000)*weight) + rA) >> 11;
+
+ *(short*)dst = (workR & 0x7C00) | (workG & 0x03E0) | (workB & 0x001F);
+ }
+
+ src = tSrc + srcRowBytes;
+ dst = tDst + dstRowBytes;
+
+ r1 += vrLI; r2 += vrRI;
+ g1 += vgLI; g2 += vgRI;
+ b1 += vbLI; b2 += vbRI;
+ }
+}
+
--- /dev/null
+++ b/Source/blitter.h
@@ -1,0 +1,48 @@
+// blitter.h
+
+#include "SDL.h"
+
+#include "font.h"
+
+// SDL type
+void SurfaceBlitMask( SDL_Surface* object, SDL_Surface* mask, SDL_Surface* dest,
+ const MRect* objectRect, const MRect* maskRect, const MRect* destRect );
+
+void SurfaceBlitBlob( const MRect* blobRect, const MRect* destRect );
+
+void SurfaceBlitColor( SDL_Surface* mask, SDL_Surface* dest,
+ const MRect* maskRect, const MRect* destRect,
+ int r, int g, int b, int weight );
+
+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 );
+
+void SurfaceBlitWeightedDualAlpha( SDL_Surface* back, SDL_Surface* source, SDL_Surface* mask, SDL_Surface* alpha, SDL_Surface* dest,
+ const MRect* backRect, const MRect* srcRect, const MRect* maskRect, const MRect* alphaRect, const MRect* destRect,
+ int inWeight );
+
+void SurfaceBlitWeightedCharacter( SkittlesFontPtr font, unsigned char text, MPoint *dPoint, int r, int g, int b, int alpha );
+
+void SurfaceBlitCharacter( SkittlesFontPtr font, unsigned char text, MPoint *dPoint, int r, int g, int b, int dropShadow );
+
+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 );
+
+void SurfaceBlitColorOver( SDL_Surface* source, SDL_Surface* dest,
+ const MRect* sourceRect, const MRect* destRect,
+ int r, int g, int b, int weight );
+
+
+
+void SetUpdateRect( int player, MRect *where );
+void UpdatePlayerWindow( int player );
+void InitBlitter( void );
+
+extern MBoolean update[2][kGridAcross][kGridDown];
+extern MBoolean refresh[2];
+extern MPoint topLeft[2];
--- /dev/null
+++ b/Source/control.cpp
@@ -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];
+long 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) &&
+ (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/Source/control.h
@@ -1,0 +1,28 @@
+// control.h
+
+void PlayerControl( int player );
+void AIControl( int player );
+void ChooseAIDestination( int player );
+int TestAIDestination( int player, int testX, int testR );
+int GetRowHeight( int player, int row );
+int DetermineEmotion( int player );
+int TestTemporaryGrid( void );
+void QuickRemove( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color );
+MBoolean CanReach( int player, int testX );
+int ScoreTemporaryGrid( void );
+int BestColor( int player, int BlobX, int BlobY );
+void LogText( char string[] );
+void LogGrid( signed char myGrid[kGridAcross][kGridDown] );
+void AutoControl( int player );
+
+extern int destinationX[2], destinationR[2];
+extern signed char tempGrid[kGridAcross][kGridDown];
+extern long timeAI[2];
+extern MBoolean moveQuick[2];
+enum
+{
+ kEmotionNeutral = 0,
+ kEmotionSad,
+ kEmotionHappy,
+ kEmotionPanic
+};
--- /dev/null
+++ b/Source/fmodmusic.cpp
@@ -1,0 +1,83 @@
+// fmodmusic.c
+
+#include <string.h>
+
+#include "main.h"
+#include "music.h"
+#include "gworld.h"
+#include "gameticks.h"
+#include "soundfx.h"
+#include "graphics.h"
+
+#include "fmod.h"
+#include "fmod_errors.h"
+
+const int kNoMusic = -1;
+
+MBoolean musicOn = true, musicFast = false;
+int musicLevel = 0, musicSelection = kNoMusic;
+FMUSIC_MODULE *musicModule = NULL;
+
+void EnableMusic( MBoolean on )
+{
+ FMUSIC_SetMasterVolume( musicModule, on? 192: 0 );
+}
+
+void FastMusic( void )
+{
+ if( musicModule && !musicFast )
+ {
+ FMUSIC_SetMasterSpeed( musicModule, 1.3f );
+ musicFast = true;
+ }
+}
+
+void SlowMusic( void )
+{
+ if( musicModule && musicFast )
+ {
+ FMUSIC_SetMasterSpeed( musicModule, 1.0f );
+ musicFast = false;
+ }
+}
+
+void PauseMusic( void )
+{
+ if( musicSelection >= 0 && musicSelection <= kSongs )
+ {
+ FMUSIC_SetPaused( musicModule, true );
+ musicLevel++;
+ }
+}
+
+void ResumeMusic( void )
+{
+ if( musicSelection >= 0 && musicSelection <= kSongs )
+ {
+ musicLevel--;
+ FMUSIC_SetPaused( musicModule, false );
+ }
+}
+
+void ChooseMusic( short which )
+{
+ if( musicSelection >= 0 && musicSelection <= kSongs )
+ {
+ FMUSIC_StopSong( musicModule );
+ FMUSIC_FreeSong( musicModule );
+ musicModule = NULL;
+ }
+
+ if( which >= 0 && which <= kSongs )
+ {
+ musicModule = FMUSIC_LoadSong( QuickResourceName( "mod", which+128, "" ) );
+ if( musicModule != NULL )
+ {
+ FMUSIC_SetMasterVolume( musicModule, musicOn? 192: 0 );
+ FMUSIC_SetLooping( musicModule, true );
+ FMUSIC_PlaySong( musicModule );
+ musicSelection = which;
+ musicLevel = 0;
+ }
+ }
+}
--- /dev/null
+++ b/Source/fmodsoundfx.cpp
@@ -1,0 +1,61 @@
+// soundfx.c
+
+#include "main.h"
+#include "soundfx.h"
+#include "music.h"
+
+#include "fmod.h"
+#include "fmod_errors.h"
+
+FSOUND_SAMPLE* sound[kNumSounds];
+MBoolean soundOn = true;
+
+void InitSound( void )
+{
+ int index;
+
+ if (!FSOUND_Init(44100, 64, FSOUND_INIT_USEDEFAULTMIDISYNTH))
+ {
+ musicOn = soundOn = false;
+ return;
+ }
+
+ for( index=0; index<kNumSounds; index++ )
+ {
+ sound[index] = FSOUND_Sample_Load( FSOUND_UNMANAGED, QuickResourceName( "snd", index+128, ".wav" ), FSOUND_NORMAL | FSOUND_LOOP_OFF | FSOUND_2D, 0 );
+ if( sound[index] == NULL )
+ {
+ Error( "InitSound: files are missing" );
+ }
+ }
+}
+
+
+void PlayMono( short which )
+{
+ if( soundOn )
+ {
+ int chanHandle = FSOUND_PlaySound( FSOUND_FREE, sound[which] );
+ }
+}
+
+void PlayStereo( short player, short which )
+{
+ PlayStereoFrequency( player, which, 0 );
+}
+
+void PlayStereoFrequency( short player, short which, short freq )
+{
+ if( soundOn )
+ {
+ int chanHandle = FSOUND_PlaySoundEx( FSOUND_FREE, sound[which], NULL, true );
+ FSOUND_SetPan( chanHandle, player? 255: 0 );
+ FSOUND_SetFrequency( chanHandle, (FSOUND_GetFrequency(chanHandle) * (16 + freq)) / 16 );
+ FSOUND_SetPaused( chanHandle, false );
+ }
+}
+
+void UpdateSound()
+{
+ // no-op!
+}
--- /dev/null
+++ b/Source/font.cpp
@@ -1,0 +1,120 @@
+// font.c
+
+#include "SDL.h"
+#include "SDLU.h"
+
+#include "main.h"
+#include "font.h"
+#include "gworld.h"
+
+
+#define kNumFonts (picBatsuFont-picFont+1)
+
+static SkittlesFont s_font[kNumFonts] = {0};
+
+
+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'|-����� " );
+ LoadFont( &s_font[1], picHiScoreFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*().,/-=_+<>?|'\":; " );
+ LoadFont( &s_font[2], picContinueFont, (unsigned char*) "0123456789" );
+ LoadFont( &s_font[3], picBalloonFont, (unsigned char*) "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-=_+;:,./<>? ��'\"" );
+ 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[*text++];
+ }
+
+ return width;
+}
+
--- /dev/null
+++ b/Source/font.h
@@ -1,0 +1,25 @@
+// font.h
+
+
+#ifndef __FONT__
+#define __FONT__
+
+
+#include "SDL.h"
+
+
+typedef struct
+{
+ SDL_Surface *surface;
+ int width[256];
+ int across[256];
+}
+SkittlesFont, *SkittlesFontPtr;
+
+
+void InitFont( void );
+SkittlesFontPtr GetFont( int pictID );
+int GetTextWidth( SkittlesFontPtr font, const char *text );
+
+
+#endif
--- /dev/null
+++ b/Source/gameticks.cpp
@@ -1,0 +1,45 @@
+// gameticks.c
+
+#include "SDL.h"
+#include "gameticks.h"
+
+unsigned long baseTickCount, freezeTickCount;
+int freezeLevel;
+
+unsigned long MTickCount()
+{
+ return (unsigned long) ((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 long GameTickCount( void )
+{
+ if( freezeLevel < 0 )
+ return freezeTickCount - baseTickCount;
+
+ return MTickCount( ) - baseTickCount;
+}
--- /dev/null
+++ b/Source/gameticks.h
@@ -1,0 +1,7 @@
+// gameticks.h
+
+unsigned long MTickCount();
+void InitGameTickCount( void );
+void FreezeGameTickCount( void );
+void UnfreezeGameTickCount( void );
+unsigned long GameTickCount( void );
--- /dev/null
+++ b/Source/graphics.cpp
@@ -1,0 +1,194 @@
+// graphics.c
+
+#include <stdlib.h>
+
+#include "SDL.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, 16 );
+}
+
+void DrawBackdrop( void )
+{
+ SDL_Rect backdropRect = { 0, 0, 640, 480 };
+
+ SDLU_BlitFrontSurface( backdropSurface, &backdropRect, &backdropRect );
+}
+
+void OpeningProgress( int current, int total )
+{
+ // computers are fast now... progress bar seems silly
+ current, total;
+}
+
+void ShowTitle( void )
+{
+ int time;
+
+ DrawPICTInSurface( frontSurface, picTitle );
+
+ SDL_Flip( frontSurface );
+
+ QuickFadeIn( NULL );
+
+ time = MTickCount() + 180;
+
+ RetrieveResources( );
+
+ while( time > MTickCount() && !SDLU_Button() )
+ {
+ SDLU_Yield();
+ }
+
+ WaitForRelease();
+
+ QuickFadeOut( NULL );
+
+ SDL_FillRect( frontSurface, &frontSurface->clip_rect, SDL_MapRGB( frontSurface->format, 0, 0, 0 ) );
+ SDL_Flip( frontSurface );
+}
+
--- /dev/null
+++ b/Source/graphics.h
@@ -1,0 +1,25 @@
+// graphics.h
+
+
+#include "SDL.h"
+
+
+void DrawSpriteBlobs( int player, int type );
+void EraseSpriteBlobs( int player );
+void CleanSpriteArea( int player, MRect *myRect );
+void CalcBlobRect( int x, int y, MRect *myRect );
+void DrawBackdrop( void );
+void ShowTitle( void );
+void InitBackdrop( void );
+void OpeningProgress( int current, int total ) ;
+
+
+extern SDL_Surface* backdropSurface;
+
+
+enum
+{
+ blobBlinkAnimation = 0,
+ blobJiggleAnimation,
+ blobCryAnimation
+};
--- /dev/null
+++ b/Source/graymonitor.cpp
@@ -1,0 +1,91 @@
+// graymonitor.c
+
+#include "SDL.h"
+#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 ), 16 );
+ 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/Source/graymonitor.h
@@ -1,0 +1,7 @@
+// graymonitor.h
+
+void InitGrayMonitors( void );
+void ShowGrayMonitor( short player );
+
+extern MRect grayMonitorZRect, grayMonitorRect[2];
+extern MBoolean grayMonitorVisible[2];
--- /dev/null
+++ b/Source/grays.cpp
@@ -1,0 +1,378 @@
+// grays.c
+
+#include "SDL.h"
+#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];
+int blinkTime[2], 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;
+ }
+
+ if( flashyAnimation )
+ {
+ 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
+ };
+
+ if( flashyAnimation )
+ {
+ 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/Source/grays.h
@@ -1,0 +1,17 @@
+// grays.h
+
+void InitGrays( void );
+void CalculateGrays( int player, int BlobsToDrop );
+void SetupGrays( int player );
+void DropGrays( int player );
+void BlinkGrays( int player );
+void PlaceGrayRow( int player, int grayX );
+void Bounce( int player );
+void LockGrays( int player );
+MBoolean BusyDroppingGrays( int player );
+
+extern int grays[2][kGridAcross], grayAir[2][kGridAcross], graySpeed[2];
+extern int unallocatedGrays[2], lockGrays[2], rowBounce[2][kGridAcross], splat[2][kGridAcross];
+extern int blinkTime[2], sunTime[2], blinkStage[2], sunStage[2];
+
+#define kTimeBetweenBlinks 120
--- /dev/null
+++ b/Source/gworld.cpp
@@ -1,0 +1,227 @@
+// gworld.c
+
+#include "SDL.h"
+#include "SDLU.h"
+#include "SDL_image.h"
+
+#include "main.h"
+#include "gworld.h"
+#include "blitter.h"
+#include "graphics.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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, 16 );
+
+ boardSurface[1] = LoadPICTAsSurface( picBoardRight, 16 );
+ if( boardSurface[1] == NULL )
+ boardSurface[1] = LoadPICTAsSurface( picBoard, 16 );
+
+ // Get blob worlds
+
+ blobSurface = LoadPICTAsSurface( picBlob, 16 );
+ maskSurface = LoadPICTAsSurface( picBlobMask, 1 );
+ charMaskSurface = LoadPICTAsSurface( picCharMask, 1 );
+
+ // Get blast worlds
+
+ blastSurface = LoadPICTAsSurface( picBlast, 16 );
+ blastMaskSurface = LoadPICTAsSurface( picBlastMask, 16 );
+}
+
+
+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, 16 );
+ playerSpriteSurface[count] = SDLU_InitSurface( &sdlRect, 16 );
+ }
+}
+
+
+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)>>3 );
+ }
+}
+
+void SurfaceDrawShadow( const MRect *myRect, int blob, int state )
+{
+ int x;
+ 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, 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;
+
+ filename = QuickResourceName( "PICT", pictID, ".jpg" );
+ if( FileExists( filename ) )
+ {
+ surface = IMG_Load( filename );
+ }
+ else
+ {
+ filename = QuickResourceName( "PICT", pictID, ".png" );
+ if( FileExists( filename ) )
+ {
+ surface = IMG_Load( filename );
+ }
+ else
+ {
+ // Fail
+ return 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_BlitSurfaceHQ( image, &image->clip_rect,
+ surface, &surface->clip_rect );
+
+ SDL_FreeSurface( image );
+ }
+}
--- /dev/null
+++ b/Source/gworld.h
@@ -1,0 +1,37 @@
+// gworld.h
+
+
+#include "SDL.h"
+
+
+#define kBlobHorizSize 24
+#define kBlobVertSize 24
+#define kBlobShadowDepth 6
+#define kBlobShadowError 2
+
+#define kBlastWidth 72
+#define kBlastHeight 72
+#define kBlastFrames 14
+
+void GetBlobGraphics();
+void InitPlayerWorlds();
+
+void DrawPICTInSurface( SDL_Surface* surface, int pictID );
+SDL_Surface* LoadPICTAsSurface( int pictID, int depth );
+MBoolean PICTExists( int pictID );
+
+void SurfaceDrawBoard( int player, const MRect *myRect );
+void SurfaceDrawShadow( const MRect *myRect, int blob, int state );
+void SurfaceDrawAlpha( const MRect *myRect, int blob, int mask, int state );
+void SurfaceDrawColor( const MRect *myRect, int blob, int state, int r, int g, int b, int w );
+void SurfaceDrawBlob( int player, const MRect *myRect, int blob, int state, int charred );
+void SurfaceDrawSprite( const MRect *myRect, int blob, int state );
+
+extern SDL_Surface* blobSurface;
+extern SDL_Surface* maskSurface;
+extern SDL_Surface* charMaskSurface;
+extern SDL_Surface* boardSurface[2];
+extern SDL_Surface* blastSurface;
+extern SDL_Surface* blastMaskSurface;
+extern SDL_Surface* playerSurface[2];
+extern SDL_Surface* playerSpriteSurface[2];
--- /dev/null
+++ b/Source/hiscore.cpp
@@ -1,0 +1,493 @@
+// hiscore.c
+
+#include "SDL.h"
+#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];
+char *highScoreText, *highScoreRank;
+
+#define min(x,y) (((x)<(y))?(x):(y))
+
+static void FadeScreen( SDL_Surface* hiScoreSurface, SDL_Surface* fadeSurface, int start, int end )
+{
+ int skip, timer, 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, color );
+ break;
+ }
+
+ OffsetMRect( &drawRect, 0, 15 );
+ }
+
+ SDLU_BlitFrontSurface( fadeSurface, &fullSDLRect, &fullSDLRect );
+
+ 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( /* the delete key is held down */ 0 ) // <-- MUST BE OBSOLETED ... ACTUALLY MUST BE FIXED, BUT I ALWAYS SEARCH FOR OBSOLETE :)
+ {
+ // 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(IsRegistered()? kLevels: kSharewareLevels)), 16 );
+ fadeSurface = SDLU_InitSurface( &fullSDLRect, 16 );
+
+ font = GetFont( picHiScoreFont );
+
+ SDLU_AcquireSurface( hiScoreSurface );
+
+ SDLU_GetPixel( hiScoreSurface, RandomBefore( fullSDLRect.w ), RandomBefore( fullSDLRect.h ), &anyColor );
+
+ anyColor.r = anyColor.r * 32 / 256;
+ anyColor.g = anyColor.g * 32 / 256;
+ anyColor.b = anyColor.b * 32 / 256;
+ anyColor.r = min( 31, anyColor.r + 14 );
+ anyColor.g = min( 31, anyColor.g + 14 );
+ anyColor.b = min( 31, anyColor.b + 14 );
+
+ dPoint.v = 20;
+ dPoint.h = 225;
+ for( count=0; highScores[count]; count++ )
+ {
+ SurfaceBlitCharacter( font, highScores[count], &dPoint, 31, 31, 31, 1 );
+ }
+
+ for( count=0; count<=9; count++ )
+ {
+ r = ((31 * (10-count)) + (anyColor.r * count)) / 10;
+ g = ((31 * (10-count)) + (anyColor.g * count)) / 10;
+ b = ((31 * (10-count)) + (anyColor.b * count)) / 10;
+
+ 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( frontSurface, &frontSurface->clip_rect, SDL_MapRGB( frontSurface->format, 0, 0, 0 ));
+ SDL_Flip( frontSurface );
+
+ 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;
+ char *bestCombo = "BEST COMBO", bestInfo[256], *scan;
+ MPoint dPoint;
+ int levelCap;
+
+ font = GetFont( picHiScoreFont );
+
+ StopBalloon( );
+ InitGame( kAutoControl, kNobodyControl );
+ scoreWindowVisible[0] = false;
+ grayMonitorVisible[0] = false;
+
+ level = best.lv;
+ levelCap = IsRegistered()? kLevels: kSharewareSolitaireLevels;
+ if( (level < 1 || level > levelCap) && level != kTutorialLevel )
+ {
+ memcpy( &best, &defaultBest, sizeof(best) );
+ showStartMenu = true;
+ return;
+ }
+
+ BeginRound( false );
+ character[0].dropSpeed = 12;
+
+ SDLU_AcquireSurface( backdropSurface );
+
+ dPoint.v = 40;
+ dPoint.h = 225;
+ for( scan = bestCombo; *scan; scan++ )
+ {
+ SurfaceBlitCharacter( font, *scan, &dPoint, 31, 31, 31, 1 );
+ }
+
+ sprintf( bestInfo, "%s (%d points)", best.name, best.value );
+ dPoint.v = 410;
+ dPoint.h = 320 - (GetTextWidth( font, bestInfo ) / 2);
+
+ for( scan = bestInfo; *scan; scan++ )
+ {
+ SurfaceBlitCharacter( font, *scan, &dPoint, 31, 31, 31, 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( long score )
+{
+ short count, item;
+ char rank[50];
+ char text[256], *playerName = "You", *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/Source/hiscore.h
@@ -1,0 +1,28 @@
+// hiscore.h
+
+#define kNameLength 40
+
+typedef struct
+{
+ signed char grid[kGridAcross][kGridDown];
+ signed char a, b, m, g, lv, x, r, player;
+ int value;
+ char name[kNameLength];
+} Combo;
+
+typedef struct
+{
+ char name[kNameLength];
+ long score;
+} HighScore;
+
+void ShowHiscore( void );
+void ShowBestCombo( void );
+void AddHiscore( long score );
+void SubmitCombo( Combo *in );
+void InitPotentialCombos( void );
+
+extern Combo best, potentialCombo[2];
+extern HighScore scores[10];
+extern char highScoreName[256];
+extern char *highScoreText, *highScoreRank;
--- /dev/null
+++ b/Source/keyselect.cpp
@@ -1,0 +1,64 @@
+// keyselect.c
+
+#include "SDL.h"
+#include "SDLU.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "main.h"
+#include "players.h"
+#include "keyselect.h"
+
+
+SDLKey playerKeys[2][4] = {
+ { SDLK_a, SDLK_d, SDLK_x, SDLK_s },
+ { SDLK_LEFT, SDLK_RIGHT, SDLK_DOWN, SDLK_UP }
+ };
+
+const SDLKey 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;
+ unsigned char* pressedKeys;
+
+ SDLU_PumpEvents();
+ pressedKeys = SDL_GetKeyState( &arraySize );
+
+ // Check for game keys
+ for( player = 0; player < 2; player++ )
+ {
+ if( pressedKeys[ playerKeys[player][0] ] )
+ hitKey[player].left++;
+ else
+ hitKey[player].left = 0;
+
+
+ if( pressedKeys[ playerKeys[player][1] ] )
+ hitKey[player].right++;
+ else
+ hitKey[player].right = 0;
+
+
+ if( pressedKeys[ playerKeys[player][2] ] )
+ hitKey[player].drop++;
+ else
+ hitKey[player].drop = 0;
+
+
+ if( pressedKeys[ playerKeys[player][3] ] )
+ hitKey[player].rotate++;
+ else
+ hitKey[player].rotate = 0;
+ }
+
+ pauseKey = pressedKeys[ SDLK_ESCAPE ];
+}
--- /dev/null
+++ b/Source/keyselect.h
@@ -1,0 +1,16 @@
+// keyselect.h
+
+
+#ifdef __cplusplus
+extern "C"
+#endif
+int SDLTypingFilter(const SDL_Event *event);
+
+void StartWatchingTyping();
+void StopWatchingTyping();
+MBoolean CheckTyping( char* ascii, SDLKey* sdl );
+void CheckKeys();
+
+
+extern SDLKey playerKeys[2][4];
+extern const SDLKey defaultPlayerKeys[2][4];
--- /dev/null
+++ b/Source/level.cpp
@@ -1,0 +1,1338 @@
+// level.c
+
+#include <stdlib.h>
+#include <math.h>
+
+#include "SDL.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 7
+#define kIncrementPerFrame 2
+#define kSplatType 4
+
+#define min(x,y) (((x)<(y))?(x):(y))
+#define max(x,y) (((x)>(y))?(x):(y))
+
+
+const int startSkip = 1;
+static MBoolean shouldFullRepaint = false;
+static int startMenuTime = 0;
+static int splatState[kNumSplats], splatColor[kNumSplats], splatSide[kNumSplats];
+static MRect splatBlob[kNumSplats];
+static int glowUpdate = 0, titleGlow[kTitleItems] = {24, 24, 24, 24, 24, 24, 24};
+static MRect titleRect[kTitleItems] = {
+ { 155, 203, 207, 426 }, // tutorial
+ { 225, 179, 281, 451 }, // 1p
+ { 297, 182, 352, 454 }, // 2p
+ { 358, 183, 428, 458 }, // solitaire
+ { 429, 280, 478, 390 }, // high scores
+ { 433, 390, 477, 446 }, // quit
+ { 430, 187, 479, 280 }, // controls
+ };
+
+const int kCursorWidth = 32;
+const int kCursorHeight = 32;
+
+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, '�', &mouseHere, 0, 0, 0, 0 );
+ SurfaceBlitCharacter( cursorFont, '�', &mouseHereToo, 31, 31, 31, 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 };
+ MPoint mouseHereToo = mouseHere;
+
+ cursorFrontSDLRect.x = mouseHere.h;
+ cursorFrontSDLRect.y = mouseHere.v;
+
+ SDLU_BlitSurface( scratch, &cursorBackSDLRect,
+ surface, &cursorFrontSDLRect );
+}
+
+static void GameStartMenuRepaint()
+{
+ shouldFullRepaint = true;
+}
+
+void GameStartMenu( void )
+{
+ // 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;
+ SDL_Surface* cursorBackSurface;
+ MRect backdropPortZRect = { 0, 0, 480, 640 };
+ SDL_Rect backdropSDLRect = { 0, 0, 640, 480 };
+ SDL_Rect cursorBackSDLRect = { 0, 0, kCursorWidth, kCursorHeight };
+ SDL_Rect cursorFrontSDLRect = { 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;
+ MPoint sPoint[4];
+ unsigned long black;
+ int currentID;
+ char registeredString[256];
+ char* scan;
+ 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 } };
+
+ const int kLeftSide = 0, kRightSide = 1, kGlow = 2, kCursor = 3;
+
+redo:
+
+ 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] = 24;
+ }
+
+ for( count=0; count<kNumSplats; count++ )
+ {
+ splatState[count] = kIdleSplat;
+ }
+
+ // make background surface
+ gameStartSurface = LoadPICTAsSurface( picGameStart, 16 );
+ black = SDL_MapRGB( gameStartSurface->format, 0, 0, 0 );
+
+ // make cursor backing store
+ cursorBackSurface = SDLU_InitSurface( &cursorBackSDLRect, 16 );
+ SDL_FillRect( cursorBackSurface, &cursorBackSDLRect, black );
+
+ // draw user name with a cool blue halo
+ if( IsRegistered() )
+ {
+ sprintf( registeredString, "Registered to %s", registeredName );
+ }
+ else
+ {
+ sprintf( registeredString, "U N R E G I S T E R E D" );
+ }
+
+ dPoint.v = 131;
+ dPoint.h = 320 - (GetTextWidth( smallFont, registeredString ) / 2);
+ sPoint[0].v = dPoint.v + 1; sPoint[0].h = dPoint.h + 1;
+ sPoint[1].v = dPoint.v - 1; sPoint[1].h = dPoint.h - 1;
+ sPoint[2].v = dPoint.v - 1; sPoint[2].h = dPoint.h + 1;
+ sPoint[3].v = dPoint.v + 1; sPoint[3].h = dPoint.h - 1;
+
+ SDLU_AcquireSurface( gameStartSurface );
+ for( scan = registeredString; *scan; scan++ )
+ {
+ SurfaceBlitCharacter( smallFont, *scan, &sPoint[0], 0, 0, 25, 0 );
+ SurfaceBlitCharacter( smallFont, *scan, &sPoint[1], 0, 0, 25, 0 );
+ SurfaceBlitCharacter( smallFont, *scan, &sPoint[2], 0, 0, 25, 0 );
+ SurfaceBlitCharacter( smallFont, *scan, &sPoint[3], 0, 0, 25, 0 );
+ SurfaceBlitCharacter( smallFont, *scan, &dPoint, 30, 30, 30, 0 );
+ }
+ SDLU_ReleaseSurface( gameStartSurface );
+
+ // make drawing surface
+ gameStartDrawSurface = SDLU_InitSurface( &backdropSDLRect, 16 );
+ SDLU_BlitSurface( gameStartSurface, &gameStartSurface->clip_rect,
+ gameStartDrawSurface, &gameStartDrawSurface->clip_rect );
+
+ // darken menu items
+ for( count=0; count<kTitleItems; count++ )
+ {
+ SurfaceBlitColorOver( gameStartSurface, gameStartDrawSurface,
+ &titleRect[count], &titleRect[count],
+ 0, 0, 0, titleGlow[count] );
+ }
+
+ SDLU_BlitFrontSurface( gameStartDrawSurface, &backdropSDLRect, &backdropSDLRect );
+
+ WaitForRelease();
+
+ QuickFadeIn( NULL );
+
+ DoFullRepaint = GameStartMenuRepaint;
+
+ startMenuTime = MTickCount( );
+ while( ( selected == -1 || !SDLU_Button() ) && !finished )
+ {
+ startMenuTime += skip;
+
+ // Add a new falling blob
+ if( glowUpdate == 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;
+ }
+ }
+ }
+
+ // Erase and redraw falling blobs and chunks
+
+ SDLU_AcquireSurface( gameStartDrawSurface );
+
+ // Take the cursor out of the scene
+ 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;
+
+ // is this a hack? maybe. but it works!
+ 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 > 460 ) mouse.v = 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 (GEEK!!!!!)
+
+ 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]--;
+ }
+ 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] -= 2;
+ }
+ else
+ {
+ comboBright[count] = 0;
+ }
+ }
+ }
+
+ // Redraw falling blobs
+ for( blob=0; blob<kNumSplats; blob++ )
+ {
+ if( splatState[blob] == kFallingSplat )
+ {
+ if( splatBlob[blob].bottom >= 480 )
+ {
+ splatBlob[blob].top = 480 - kBlobVertSize;
+ splatBlob[blob].bottom = 480;
+ splatState[blob] = 1;
+
+ // Process combos
+ if( mouse.v > 420 &&
+ mouse.h >= (splatBlob[blob].left - 30) &&
+ mouse.h <= (splatBlob[blob].right + 10) )
+ {
+ combo[splatSide[blob]]++;
+ comboBright[splatSide[blob]] = 31;
+ }
+ else
+ {
+ if( combo[splatSide[blob]] >= 10 ) missBright[splatSide[blob]] = 31;
+ 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) )
+ {
+ 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, &titleRect[count] ) )
+ {
+ selected = count;
+ break;
+ }
+ }
+
+ // update glows
+ do
+ {
+ glowUpdate = (glowUpdate + 1) % 7;
+
+ oldGlow = titleGlow[glowUpdate];
+
+ if( selected == glowUpdate )
+ {
+ titleGlow[glowUpdate] -= (4 * startSkip);
+ if( titleGlow[glowUpdate] < 0 ) titleGlow[glowUpdate] = 0;
+ }
+ else
+ {
+ titleGlow[glowUpdate] += (4 * startSkip);
+ if( titleGlow[glowUpdate] > 24 ) titleGlow[glowUpdate] = 24;
+ }
+
+ if( titleGlow[glowUpdate] != oldGlow )
+ {
+ SurfaceBlitColorOver( gameStartSurface, gameStartDrawSurface,
+ &titleRect[glowUpdate], &titleRect[glowUpdate],
+ 0, 0, 0, titleGlow[glowUpdate] );
+
+ drawRect[kGlow] = titleRect[glowUpdate];
+ }
+
+ // do 5, 6, and 7 all at once because they're small
+ if( glowUpdate == 4 || glowUpdate == 5 ) continue;
+ }
+ while( 0 );
+
+ // Reinsert the cursor into the scene
+ InsertCursor( mouse, cursorBackSurface, gameStartDrawSurface );
+ drawRect[kCursor].top = min( drawRect[kCursor].top, mouse.v );
+ drawRect[kCursor].left = min( drawRect[kCursor].left, mouse.h );
+ drawRect[kCursor].bottom = max( drawRect[kCursor].bottom, mouse.v + kCursorHeight );
+ drawRect[kCursor].right = max( drawRect[kCursor].right, mouse.h + kCursorWidth );
+
+ // 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 );
+ }
+ }
+ }
+
+ // Skip frames? Or delay?
+ if( startMenuTime <= MTickCount( ) )
+ {
+ startMenuTime = MTickCount( );
+ skip = 2;
+ }
+ else
+ {
+ skip = 1;
+ while( startMenuTime > MTickCount( ) )
+ {
+ SDLU_Yield();
+ }
+ }
+ }
+
+ DoFullRepaint = NoPaint;
+
+ if( finished )
+ {
+ selected = 5; // quit
+ }
+
+ switch( selected )
+ {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ PlayMono( kChime );
+ break;
+ }
+
+ SDL_FreeSurface( gameStartSurface );
+ SDL_FreeSurface( gameStartDrawSurface );
+ SDL_FreeSurface( cursorBackSurface );
+
+ QuickFadeOut( NULL );
+
+ switch( selected )
+ {
+ case 0:
+ InitGame( kAutoControl, kNobodyControl );
+ level = kTutorialLevel;
+ BeginRound( true );
+ InitTutorial( );
+ QuickFadeIn( NULL );
+ break;
+
+ case 1:
+ case 2:
+ case 3:
+ {
+ int player2[] = { 0, kAIControl, kPlayerControl, kNobodyControl };
+
+ InitGame( kPlayerControl, player2[selected] );
+ BeginRound( true );
+ QuickFadeIn( NULL );
+ break;
+ }
+
+ case 4:
+ ShowHiscore();
+ ShowBestCombo();
+ break;
+
+ case 5:
+ finished = true;
+ break;
+
+ case 6:
+ currentID = RandomBefore( IsRegistered()? kLevels: kSharewareLevels ) * 100;
+
+ DrawPICTInSurface( boardSurface[0], picBoard + currentID );
+ DrawPICTInSurface( frontSurface, picBackdrop + currentID );
+ SDL_Flip( frontSurface );
+
+ QuickFadeIn( NULL );
+ HandleDialog( kControlsDialog );
+ QuickFadeOut( NULL );
+ goto redo;
+ }
+}
+
+void ShowGameOverScreen( void )
+{
+ unsigned long timer = MTickCount() + (60*3);
+
+ QuickFadeOut(NULL);
+
+ DrawPICTInSurface( frontSurface, picGameOver );
+ SDL_Flip( frontSurface );
+
+ QuickFadeIn( NULL );
+ do
+ {
+ if( MTickCount() > timer ) break;
+ SDLU_Yield();
+ }
+ while( !AnyKeyIsPressed( ) && !SDLU_Button() );
+ QuickFadeOut( NULL );
+}
+
+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, 16 );
+
+ 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 );
+
+ 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 );
+ }
+
+ 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;
+}
+
+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, 0, 0, 0 }, true },
+ { 1, 6, 2, { 10, 9, 8, 8, 9, 10 }, 12, 7, 1, 20, { 223, 7, 0, 0 }, true },
+ { 2, 9, 3, { 7, 7, 7, 11, 7, 7 }, 10, 6, 2, 17, { 0, 0, 0, 0 }, false },
+ { 3, 12, 4, { 11, 10, 9, 8, 7, 6 }, 8, 5, 3, 13, { 32767, 4, 16912, 4 }, false },
+ { 4, 15, 0, { 5, 9, 10, 10, 9, 5 }, 7, 4, 4, 10, { 32767, 1, 0, 0 }, false },
+ { 5, 17, 1, { 4, 7, 11, 11, 6, 3 }, 7, 2, 5, 8, { 14835, 8, 0, 0 }, false },
+ { 6, 18, 2, { 7, 9, 10, 10, 9, 7 }, 6, 4, 6, 7, { 0, 0, 0, 0 }, false },
+ { 7, 20, 3, { 5, 10, 10, 10, 10, 5 }, 5, 3, 7, 5, { 9696, 2, 21151, 3 }, false },
+ { 8, 21, 4, { 11, 11, 10, 10, 9, 9 }, 4, 3, 8, 5, { 32738, 5, 0, 0 }, false },
+ { 9, 22, 0, { 11, 7, 11, 7, 11, 7 }, 3, 1, 9, 4, { 32356, 5, 17392, 3 }, false },
+ { 10, 23, 1, { 11, 11, 11, 11, 11, 11 }, 2, 1, 10, 2, { 6337, 1, 0, 0 }, false },
+ { 11, 24, 2, { 11, 11, 11, 11, 11, 11 }, 2, 1, 11, 2, { -1, 7, 0, 0 }, false },
+ { -1 }, // skip
+ { 13, 24, 1, { 11, 11, 11, 11, 11, 11 }, 10, 5, 0, 30, { 0, 0, 0, 0 }, true }
+ };
+
+ if( !IsRegistered() )
+ {
+ int levelCap = kSharewareLevels;
+ if( control[1] == kNobodyControl ) levelCap = kSharewareSolitaireLevels;
+
+ if( level > levelCap && level != kTutorialLevel ) return false;
+ }
+
+ 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( );
+}
+
+void IncrementLevel( void )
+{
+ level++;
+}
+
+void SelectRandomLevel( void )
+{
+ level = RandomBefore( IsRegistered()? kLevels: kSharewareLevels ) + 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];
+
+ if( !IsRegistered( ) )
+ {
+ if( player == 0 )
+ {
+ HandleDialog( kRegisterDialog );
+ QuickFadeOut( NULL );
+ showStartMenu = true;
+ }
+
+ return;
+ }
+
+ 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", "" },
+ { "Open Source", "gcc, mingw", "SDL", "libpng", "IJG", "zlib" },
+ { "Special Thanks", "Sam Lantinga", "Carey Lening", "modarchive.com", "digitalblasphemy.com", "" },
+ { "Please Register!", "The full version of", "Candy Crisis features", "twelve stages and also", "includes two player", "mode." }
+};
+
+
+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, 300 };
+ MRect bufferDstRect = { 0, 0, 480, 250 };
+ MPoint dPoint = { 450, 50 }, lPoint, cPoint;
+ int scroll, ticks, x, y;
+ int delay = 2;
+ 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( frontSurface, picSharewareVictory );
+ SDL_Flip( frontSurface );
+
+ backBuffer = SDLU_InitSurface( &bufferDstSDLRect, 16 );
+
+ SDLU_BlitSurface( frontSurface, &bufferSrcSDLRect,
+ backBuffer, &bufferDstSDLRect );
+
+ frontBuffer = SDLU_InitSurface( &bufferDstSDLRect, 16 );
+
+ 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, 31, 31, 0, 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, 31, 31, 0, thisFade );
+ }
+ }
+
+ lPoint.v += 20;
+ }
+ }
+ }
+
+ SDLU_ReleaseSurface( frontBuffer );
+
+ dPoint.v--;
+
+ SDLU_BlitFrontSurface( frontBuffer, &bufferDstSDLRect, &bufferSrcSDLRect );
+
+ 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];
+ long 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!", "" },
+ };
+
+ textFont = GetFont( picFont );
+ titleFont = GetFont( picHiScoreFont );
+ bubbleFont = GetFont( picBubbleFont );
+
+ ChooseMusic( 14 );
+
+ for( picture=0; picture<7; picture++ )
+ {
+ for( line=0; line<2; line++ )
+ {
+ msgSetPoint[picture][line].v = ((dPoint[picture].v == 230)? 100: 400) + (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, 16 );
+ frontBuffer = SDLU_InitSurface( &fullSDLRect, 16 );
+
+ for( picture = 0; picture<7; picture++ )
+ {
+ scrollSDLRect = ( scrollDir[picture] > 0 )? highSDLRect: lowSDLRect;
+
+ DrawPICTInSurface( backBuffer, picture + picVictory1 );
+
+ SDLU_BlitFrontSurface( backBuffer, &scrollSDLRect, &fullSDLRect );
+
+ 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, fixedWeight );
+ SurfaceBlitWeightedCharacter( titleFont, *text, &textPoint, 31, 31, 31, 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 )
+ {
+ SurfaceBlitWeightedCharacter( bubbleFont, '*', &bubblePoint, 31, 31, 31, (weight+1)>>1 );
+
+ 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, weight );
+ }
+ }
+ }
+
+ SDLU_ReleaseSurface( frontBuffer );
+
+ SDLU_BlitFrontSurface( frontBuffer, &fullSDLRect, &fullSDLRect );
+
+ 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;
+
+ if( !IsRegistered() )
+ {
+ SharewareVictory( );
+ }
+ else
+ {
+ RegisteredVictory( );
+ }
+
+ showStartMenu = true;
+}
--- /dev/null
+++ b/Source/level.h
@@ -1,0 +1,67 @@
+// level.h
+
+void InitGame( int player1, int player2 );
+MBoolean InitCharacter( int player, int level );
+void PrepareStageGraphics( int type );
+void BeginRound( MBoolean changeMusic );
+void InitDifficulty( void );
+void ChooseDifficulty( int player );
+void SelectRandomLevel( void );
+void IncrementLevel( void );
+void TotalVictory( void );
+void SharewareVictory( void );
+void RegisteredVictory( void );
+void InitStage( void );
+void DrawStage( void );
+void GameStartMenu( void );
+void ShowGameOverScreen( void );
+
+#define kOpponentResource 'Levl'
+
+#define kGlows 2
+
+#define kSharewareLevels 4
+#define kSharewareSolitaireLevels 5
+#define kLevels 12
+#define kTutorialLevel 14
+
+#define kEasyLevel 50
+#define kMediumLevel 70
+#define kHardLevel 90
+#define kUltraLevel 110
+
+
+typedef struct
+{
+ short color;
+ short time;
+} Glow;
+
+typedef struct
+{
+ short picture;
+ short intellect;
+ short zapStyle;
+ short autoSetup[6];
+ short speedNormal;
+ short speedRush;
+ short music;
+ short dropSpeed;
+ Glow glow[kGlows];
+ short hints;
+}
+Character;
+
+enum
+{
+ kPlayerControl = 0,
+ kAIControl,
+ kNobodyControl,
+ kAutoControl
+};
+
+extern Character character[2];
+extern int level, players, credits, difficulty[2];
+extern int difficultyTicks, backdropTicks, backdropFrame;
+
+
--- /dev/null
+++ b/Source/main.cpp
@@ -1,0 +1,713 @@
+// main.c
+
+//
+// CANDY CRISIS SDL
+//
+// FEATURES:
+// � Runs natively on any platform with support for SDL. Developed
+// primarily around the lame Mac Carbon SDL. Can compile and run
+// with gcc/mingw32 on Windows and gcc on Linux.
+//
+// ETC:
+// ��Game functionality unchanged.
+// � Removed all Mac OS-specific concepts from the game, i.e.:
+// - GWorlds replaced with SDL_Surfaces.
+// - Rects and Points replaced with MRects and MPoints. (same contents)
+// ��All resources moved into external files in a folder called
+// "CandyCrisisResources." Graphics are all JPG and PNG, opened
+// with SDL_image. Sounds are WAV. Music is still MOD type.
+// � Using the following Open Source projects:
+// SDL, SDL_image (for graphics)
+// libjpeg (used by SDL_image)
+// libpng (used by SDL_image)
+// zlib (used by libpng)
+// � Using fmod for sound; replaces MikMod
+// ��Wrote a utility library, SDLU, to pick up slack in the SDL
+// implementation and to help SDL mesh with a Mac-centric universe.
+// ��New registration code algorithm based on a fast string hash.
+//
+
+//
+// CANDY CRISIS X
+//
+// FEATURES:
+// � Runs natively on Mac OS X. Developed around Mac OS X
+// Public Beta 1H39 and 2E14.
+// Updated 3/25/2001 for Mac OS X 4K78--OS X 10.0.
+// Updated 8/25/2001 for Mac OS X 10.0.4.
+//
+// ETC:
+// ��Game functionality unchanged.
+// ��Zerius Sound System scrapped, replaced with LibMikMod. I'm
+// very unhappy with performance relative to ZSS, but on G3s and up,
+// performance should not be a major concern. If only I could get
+// the source to ZSS so it could be Carbonized!
+// ��Everything runs in one window now, instead of having
+// one window per interface element. This was necessary
+// because OS X wanted to put drop shadows around everything
+// and it looked pretty weird. This was also a personal pet
+// peeve that I never had the motivation to fix until now.
+// ��Controls dialog is super Aqua savvy, using Theme Text
+// and Theme Buttons.
+// ��A couple of kludges added, to work around OS X bugs. Ugh.
+// � Found bug which was causing blitter to draw larger dirty rects
+// than necessary (top/left of dirty rect was always 0/0). Not sure
+// if it ever shipped like that or if this is something I changed
+// post-Candy Crisis 1.0.
+// ��Changed cursor management since OS X cursors don't know how to
+// hide and show themselves properly.
+//
+// UNRESOLVED:
+// ��Stopped getting Out of Memory reports. I wonder if any of the
+// Candy Crisis cleanups affected this...?
+// ��OS X displays a line of garbage when you try to put up a totally
+// blank cursor. I'm not going to spend too long analyzing this; it's
+// not my bug.
+//
+
+
+// CANDY CRISIS 1.0 UPDATE
+//
+// FEATURES:
+// � Rebranded "Candy Crisis" at the request of Mars Candy Co.
+// Many, many graphical changes as a result. (New logo thanks
+// to Bob Frasure.)
+// � "Controls" button in main menu per many user requests.
+// � Slightly improved error reporting.
+// � Option-key at startup to turn on "allow background tasks"
+// or "don't change resolutions." (Don't change resolutions
+// requires DrawSprocket 1.7.)
+//
+// NONCRITICAL:
+// � Fixed bug in 2P mode where game would say "Player 1 got
+// best combo!" when Player 2 really got it, and vice versa.
+// � Changed Magic Skittle ratio to 1/19 instead of 1/17, after
+// watching Brett get tons of Magic Skittles at work. Hmm.
+// ��Replaced RandomBefore with less hacked-up code, because
+// Magic Skittles STILL seemed to be coming up more often than
+// expected. That seemed to take care of it.
+// ��Fixed rare bug where, after losing, the game would sometimes
+// get stuck until you explicitly chose "end game." (Would manifest
+// more often on a slow computer and/or when Background Tasks were
+// activated.)
+//
+// ETC:
+// � Antipiracy measures.
+// ��Game fonts all loaded at startup time instead of dynamically,
+// in an attempt to reduce the number of GWorlds which are created,
+// then torn down, during the game (which could have been potentially
+// fragmenting the heap, though I doubt this was a real problem).
+//
+// UNRESOLVED:
+// ��Still a handful of people who get Out of Memory when they
+// try to pause a game. Damn. Hopefully now I'll at least know
+// where they're dying (though I highly suspect it's InitGWorld,
+// which without a stack crawl is pretty much useless info...)
+// One guy says this is fixed by deleting prefs. Huh??
+//
+
+//
+// 2.0.2 UPDATE
+//
+// FEATURES:
+// � When you continue, your score is now rolled back to what
+// it was when you first started the round. This prevents people
+// from racking up high scores by continuing over and over again
+// on the highest board.
+// � You can now clear the high score tables to their default
+// values by holding delete while clicking "high scores" on the
+// main screen.
+//
+// ETC:
+// ��Added small picture to the controls dialog so people know
+// which color is Player 1, and which is Player 2.
+// ��Holding option while warping causes a CPU/CPU match to
+// occur.
+// � Changed in-game registration URL to:
+// http://emulation.net/s2.com/register.html
+//
+// CRITICAL:
+// � Fixed minor memory corruption when a bomb hits floor or
+// gray Skittle. Could potentially have corrupted 3 tiles of
+// opponent's board.
+// ��Fixed bug where potential combo data would not be cleared
+// when choosing "End Game" and then starting a new game, which
+// led to really weird corruptions of potential combo data.
+// ��Fixed bug where holding down button after end-credits rolled
+// would cause the pause dialog to pop up on a zero-gamma screen
+// (whoops).
+//
+// NONCRITICAL:
+// ��Fixed bug where dropping bomb would not display associated
+// points.
+// ��Fixed bug where dropping bomb on floor/gray Skittle would
+// reward the player for "killing" empty squares, making it
+// score 100x(9-number of grays in 3x3 area) as opposed to
+// the correct 100x(number of blobs in 3x3 area).
+// ��Occasionally, when Best Combo got corrupted, it would show
+// the ending credits instead of displaying the Best Combo. Now
+// there is code to ensure that the level # of the Best Combo
+// structure is in bounds. (If it isn't, the best combo is
+// assumed to be corrupt, and it is deleted. Not the most
+// optimal solution, but what can I do?)
+// ��If you started the tutorial, ended it, then viewed the best
+// combo, you'd see a speech balloon appear for one frame.
+// ��Fixed bug where bringing up InputSprocket dialog would
+// unload ics8's used to draw key caps (damn InputSprocket bugs).
+// Does this only affect ISp < 1.7?
+// � Fixed bug where bringing up InputSprocket dialog would not
+// update game windows behind it after it got closed.
+//
+// UNRESOLVED:
+// � Slow loading time issue seems to only be affecting an
+// incredible minority of people. It's being caused by QuickTime
+// decompressing JPEGs. I think it's not a Skittles 2 issue.
+// ��One guy says if he quits the game, reopens it, starts a game,
+// then pauses it, he gets an Out of Memory condition. He's running
+// 8.6-D clean. Hmm.
+//
+
+//
+// 2.0.1 UPDATE
+//
+// FEATURES:
+//ʥ�Best Combo
+// ��New bg for level 8
+// ��New sfx for continue sound (requested by Nathan Lamont)
+//
+// ETC:
+// ��High score dialog enhanced to support Best Combo stuff
+// ��Tutorial suggests pressing esc to set up keys now
+// ��More aggressive AI for intellect>18 (does not percieve
+// 1-level zap as advantageous)
+//
+// CRITICAL:
+// ��Fix for out-of-bounds array read (->crash) inside
+// ZapScoreDisplay.
+// ��Fixed bug where falling Skittles (in DropBlobs) would
+// occasionally have their bottom half lopped off. Tough to
+// see while in motion but totally obvious in screenshots.
+// ��Fixed InputSprocket icons in ISpConfigure dialog
+// ��Workaround for System 7 bug, where setting the
+// cursor while gamma is faded causes solid black cursor.
+//
+// NONCRITICAL:
+// ��Fixed bug where char fading on blobs that were
+// in motion would cause crap to appear for one frame.
+// Only apparent on slow Macs or in screenshots. Otherwise
+// appeared as flicker and easily dismissed.
+// ��Cmd-tab inside High Scores or Game Over screen
+// no longer leaves a white box (empty window) open
+// and will not switch out with 0 gamma
+// ��Fixed DrawSprocket bug on Macs that can't do 640x480
+// (i.e. PowerBook G3 is stuck at 1024x768). The window
+// would be drawn in lower-right hand corner instead of
+// centered.
+// ��Fixed bug in multipliers for getting multiple
+// colors at once. (Should have been *3/*6/*12/*24, was
+// actually *3/*9/*21/*45! Ouch!)
+// � Hitting cmd-Q at high score dialog would allow
+// empty high score name to be added to high score table.
+//
+// UNRESOLVED:
+// ��A few users report very slow loading times between
+// levels and during loading sequence. Problem tends to
+// be alleviated by turning on VM. Can't reproduce here.
+//
+
+//
+// 2.0.0 INITIAL RELEASE
+//
+
+#if _WIN32
+#include <windows.h>
+#include <io.h> // for _chdir
+#endif
+
+#include "SDL.h"
+#include "SDLU.h"
+#include "SDL_image.h"
+
+#include "main.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "hiscore.h"
+#include "control.h"
+#include "players.h"
+#include "gworld.h"
+#include "graphics.h"
+#include "grays.h"
+#include "soundfx.h"
+#include "next.h"
+#include "random.h"
+#include "victory.h"
+#include "score.h"
+#include "graymonitor.h"
+#include "music.h"
+#include "gameticks.h"
+#include "level.h"
+#include "opponent.h"
+#include "keyselect.h"
+#include "blitter.h"
+#include "prefs.h"
+#include "tweak.h"
+#include "zap.h"
+#include "pause.h"
+#include "tutorial.h"
+#include "RegAlgorithm.h"
+
+
+SDL_Surface* frontSurface;
+signed char nextA[2], nextB[2], nextM[2], nextG[2], colorA[2], colorB[2],
+ blobX[2], blobY[2], blobR[2], blobSpin[2], speed[2], role[2], halfway[2],
+ control[2], dropping[2], magic[2], grenade[2], anim[2];
+int chain[2];
+long blobTime[2], startTime, endTime;
+MBoolean finished = false, pauseKey = false, showStartMenu = true;
+signed char grid[2][kGridAcross][kGridDown], suction[2][kGridAcross][kGridDown], charred[2][kGridAcross][kGridDown], glow[2][kGridAcross][kGridDown];
+MRect playerWindowZRect, playerWindowRect[2];
+MBoolean playerWindowVisible[2] = { true, true };
+KeyList hitKey[2];
+int backgroundID = -1;
+MPoint blobWindow[8][2];
+MBoolean playerIsRegistered = false;
+char registeredName[64] = "";
+char registeredKey[18] = ""; // size is strange just to perplex hackers
+void (*DoFullRepaint)() = NoPaint;
+MBoolean needsRefresh = false;
+
+static char candyCrisisResources[512];
+
+int main(int argc, char *argv[])
+{
+ argc, argv;
+
+ Initialize( );
+ if( IsRegistered( ) ) exit(0);
+
+ LoadPrefs( );
+
+ ReserveMonitor( );
+ ShowTitle( );
+
+ if( !IsRegistered( ) )
+ {
+ SDLU_SetBrightness( 1.0 );
+ SharewareNotice( 15*30 );
+ SDLU_SetBrightness( 0.0 );
+ }
+
+ ChooseMusic( 13 );
+
+ while( !finished )
+ {
+ if( showStartMenu )
+ {
+ GameStartMenu( );
+ showStartMenu = false;
+ }
+
+ if( !finished )
+ {
+ DoFullRepaint = NeedRefresh;
+ CheckKeys( );
+ HandlePlayers( );
+ UpdateOpponent( );
+ UpdateBalloon( );
+ UpdateSound( );
+ DoFullRepaint = NoPaint;
+
+ if( needsRefresh )
+ {
+ RefreshAll();
+ needsRefresh = false;
+ }
+
+ if( !showStartMenu && pauseKey )
+ {
+ FreezeGameTickCount( );
+ PauseMusic( );
+ MaskRect( &playerWindowRect[0] );
+ MaskRect( &playerWindowRect[1] );
+ WaitForRelease( );
+
+ HandleDialog( kPauseDialog );
+
+ WaitForRelease( );
+ RefreshPlayerWindow( 0 );
+ RefreshPlayerWindow( 1 );
+ ResumeMusic( );
+ UnfreezeGameTickCount( );
+ }
+ }
+ }
+
+ SavePrefs( );
+ ReleaseMonitor( );
+
+ return 0;
+}
+
+void NoPaint( void )
+{
+}
+
+void MaskRect( MRect *r )
+{
+ SDL_Rect sdlRect;
+ SDLU_MRectToSDLRect( r, &sdlRect );
+ SDLU_BlitFrontSurface( backdropSurface, &sdlRect, &sdlRect );
+}
+
+void RefreshPlayerWindow( short player )
+{
+ MRect fullUpdate = {0, 0, kGridDown * kBlobVertSize, kGridAcross * kBlobHorizSize };
+
+ if( control[player] == kNobodyControl )
+ {
+ MaskRect( &playerWindowRect[player] );
+ }
+ else
+ {
+ SetUpdateRect( player, &fullUpdate );
+ UpdatePlayerWindow( player );
+ }
+}
+
+void NeedRefresh()
+{
+ needsRefresh = true;
+}
+
+void RefreshAll( void )
+{
+ DrawBackdrop( );
+
+ ShowGrayMonitor( 0 );
+ ShowGrayMonitor( 1 );
+
+ RefreshNext( 0 );
+ RefreshNext( 1 );
+
+ RefreshPlayerWindow( 0 );
+ RefreshPlayerWindow( 1 );
+
+ DrawFrozenOpponent( );
+ DrawStage( );
+
+ ShowScore( 0 );
+ ShowScore( 1 );
+}
+
+void Error( const char* extra )
+{
+#if TARGET_API_MAC_CARBON
+ Str255 myString, extraP;
+
+ CopyCStringToPascal( extra, extraP );
+ ReleaseMonitor( );
+ GetIndString( myString, 131, errUnknown );
+ ParamText( myString, extraP, "\p", "\p" );
+ Alert( dFatalErrorAlert, NULL );
+ ExitToShell( );
+#else
+ char message[256];
+ sprintf( message, "Sorry, a critical error has occurred. Please report the following error message:\n %s", extra );
+ #if WIN32
+ MessageBox( NULL, message, "Candy Crisis", MB_OK );
+ #else
+ fprintf(stderr, "Candy Crisis: %s\n", message);
+ #endif
+ exit(0);
+#endif
+}
+
+void WaitForRelease( void )
+{
+ do
+ {
+ SDLU_Yield();
+ }
+ while( AnyKeyIsPressed( ) || SDLU_Button() );
+}
+
+MBoolean AnyKeyIsPressed( void )
+{
+ int index;
+ int arraySize;
+ unsigned char* pressedKeys;
+
+ SDLU_PumpEvents();
+ pressedKeys = SDL_GetKeyState( &arraySize );
+
+ // Only check ASCII keys. (Reason: some extended keys, like NUMLOCK or CAPSLOCK,
+ // can be on all the time even if a key really isn't depressed.)
+ if( arraySize > 128 ) arraySize = 128;
+
+ for( index = 0; index < arraySize; index++ )
+ {
+ if( pressedKeys[index] )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+MBoolean ControlKeyIsPressed( void )
+{
+ int arraySize;
+ unsigned char* pressedKeys;
+
+ SDLU_PumpEvents();
+ pressedKeys = SDL_GetKeyState( &arraySize );
+
+ return pressedKeys[ SDLK_LCTRL ] || pressedKeys[ SDLK_RCTRL ];
+}
+
+MBoolean OptionKeyIsPressed( void )
+{
+ int arraySize;
+ unsigned char* pressedKeys;
+
+ SDLU_PumpEvents();
+ pressedKeys = SDL_GetKeyState( &arraySize );
+
+ return pressedKeys[ SDLK_LALT ] || pressedKeys[ SDLK_RALT ];
+}
+
+void RetrieveResources( void )
+{
+ OpeningProgress( 0, 10 );
+ InitSound( ); OpeningProgress( 1, 10 );
+
+ InitBackdrop( ); OpeningProgress( 2, 10 );
+
+ GetBlobGraphics( ); OpeningProgress( 3, 10 );
+
+ InitNext( ); OpeningProgress( 4, 10 );
+
+ InitScore( ); OpeningProgress( 5, 10 );
+
+ InitRegistration();
+ InitGrayMonitors( ); OpeningProgress( 6, 10 );
+
+ InitOpponent( ); OpeningProgress( 7, 10 );
+
+ InitStage( ); // must run after backdrop window is open
+ InitGameTickCount( );
+
+ InitPlayers( ); // must run after backdrop window is open
+ InitFont( );
+ InitZapStyle( );// must run after fonts are inited
+ OpeningProgress( 8, 10 );
+
+ InitBlitter( ); // must run after player windows are open
+ InitPlayerWorlds( ); OpeningProgress( 9, 10 );
+
+ InitVictory( ); // must run after fonts are inited
+ InitTweak( ); OpeningProgress( 10, 10 );
+}
+
+
+void CenterRectOnScreen( MRect *rect, double locationX, double locationY )
+{
+ MPoint dest = {0,0};
+
+ dest.h = (short)(locationX * (640 - (rect->right - rect->left)));
+ dest.h &= ~3;
+ dest.v = (short)(locationY * (480 - (rect->bottom - rect->top)));
+
+ OffsetMRect( rect, -rect->left, -rect->top );
+ OffsetMRect( rect, dest.h, dest.v );
+}
+
+void ReserveMonitor( void )
+{
+ SDL_Surface* icon;
+ SDL_Surface* mask;
+
+ icon = LoadPICTAsSurface( 10000, 16 );
+ mask = LoadPICTAsSurface( 10001, 1 );
+ SDL_WM_SetIcon( icon, (Uint8*) mask->pixels );
+ SDL_FreeSurface( icon );
+ SDL_FreeSurface( mask );
+
+ SDL_ShowCursor( SDL_DISABLE );
+
+#if TARGET_API_MAC_CARBON
+ frontSurface = SDL_SetVideoMode( 640, 480, 15, SDL_SWSURFACE );
+#else
+ frontSurface = SDL_SetVideoMode( 640, 480, 16, SDL_SWSURFACE | SDL_FULLSCREEN );
+#endif
+
+ SDL_WM_SetCaption( "Candy Crisis", "CandyCrisis" );
+}
+
+void ReleaseMonitor( void )
+{
+ // frontSurface is released by SDL_Quit... we are not supposed to kill it
+}
+
+int Warp( void )
+{
+ return 8;
+}
+
+const char* QuickResourceName( const char* prefix, int id, const char* extension )
+{
+ static char name[512];
+ if( id )
+ {
+ sprintf( name, "%s%s_%d%s", candyCrisisResources, prefix, id, extension );
+ }
+ else
+ {
+ sprintf( name, "%s%s%s", candyCrisisResources, prefix, extension );
+ }
+
+ return name;
+}
+
+void Initialize( void )
+{
+#if _WIN32
+ HMODULE module;
+ char name[MAX_PATH+1], *lastBackslash;
+
+ module = GetModuleHandle( NULL );
+ GetModuleFileName( module, name, MAX_PATH );
+ lastBackslash = strrchr( name, '\\' );
+ if( lastBackslash != NULL )
+ {
+ *lastBackslash = '\0';
+ strcpy( candyCrisisResources, name );
+ strcat( candyCrisisResources, "\\CandyCrisisResources\\" );
+ }
+#endif
+#if TARGET_API_MAC_CARBON
+ strcpy( candyCrisisResources, ":CandyCrisisResources:" );
+#endif
+#ifdef linux
+ strcpy( candyCrisisResources, "CandyCrisisResources/" );
+#endif
+
+ if( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO ) < 0 )
+ {
+ Error( "SDL_Init failed" );
+ }
+
+ atexit( SDL_Quit );
+
+ SDL_SetEventFilter( SDLU_EventFilter );
+}
+
+void LaunchURL( const char* url )
+{
+#if TARGET_API_MAC_CARBON
+ OSStatus err = -1;
+ ICInstance inst;
+ long startSel;
+ long endSel;
+
+ if( ICStart != NULL )
+ {
+ err = ICStart( &inst, 'Skit' );
+ if (err == noErr)
+ {
+ startSel = 0;
+ endSel = strlen(url);
+ err = ICLaunchURL( inst, "\p", url, strlen(url), &startSel, &endSel );
+ ICStop(inst);
+ }
+ }
+#else
+ SDL_WM_IconifyWindow();
+ ShellExecute( NULL, "open", url, "", "c:\\", SW_SHOWNORMAL );
+ WaitForRegainFocus();
+#endif
+}
+
+void QuickFadeIn( MRGBColor *color )
+{
+ color; // is unused
+
+#ifndef TARGET_API_MAC_CARBON
+ long c;
+ float percent;
+
+ for( percent=0.0f; percent<1.0f; percent += 0.04f )
+ {
+ c = MTickCount( );
+ SDLU_SetBrightness( percent );
+ while( c == MTickCount( ) )
+ {
+ SDLU_Yield();
+ }
+ }
+
+ SDLU_SetBrightness( percent );
+#endif
+}
+
+void QuickFadeOut( MRGBColor *color )
+{
+ color; // is unused
+
+#ifndef TARGET_API_MAC_CARBON
+ long c;
+ float percent;
+
+ for( percent=1.0f; percent>0.0f; percent -= 0.04f )
+ {
+ c = MTickCount( );
+ SDLU_SetBrightness( percent );
+ while( c == MTickCount( ) )
+ {
+ SDLU_Yield();
+ }
+ }
+
+ SDLU_SetBrightness( percent );
+#endif
+}
+
+MBoolean FileExists( const char* name )
+{
+ FILE* f = fopen( name, "rb" );
+ if( f == NULL )
+ {
+ return false;
+ }
+
+ fclose( f );
+ return true;
+}
+
+
+void WaitForRegainFocus()
+{
+ do
+ {
+ SDLU_PumpEvents();
+ SDL_Delay(50);
+ }
+ while( !SDLU_IsForeground() );
+
+ DoFullRepaint();
+}
+
+
+void InitRegistration()
+{
+ playerIsRegistered = ValidateCode( registeredName, registeredKey );
+}
+
+
+MBoolean IsRegistered()
+{
+ return playerIsRegistered;
+}
--- /dev/null
+++ b/Source/main.h
@@ -1,0 +1,295 @@
+// main.h
+
+
+#include "SDL.h"
+#include "MTypes.h"
+
+
+void Initialize( void );
+void RetrieveResources( void );
+void Error( const char* extra );
+void CenterRectOnScreen( MRect *rect, double locationX, double locationY );
+MBoolean AnyKeyIsPressed( void );
+void RefreshAll( void );
+void ReserveMonitor( void );
+void ReleaseMonitor( void );
+void MaskRect( MRect *r );
+void RefreshPlayerWindow( short player );
+MBoolean IsRegistered( void );
+int Warp( void );
+void WaitForRelease( void );
+void LaunchURL( const char* url );
+void QuickFadeIn( MRGBColor* color );
+void QuickFadeOut( MRGBColor* color );
+MBoolean FileExists( const char* name );
+void GoToBackground();
+MBoolean ControlKeyIsPressed( void );
+MBoolean OptionKeyIsPressed( void );
+MBoolean IsRegistered();
+void InitRegistration();
+void NoPaint();
+void NeedRefresh();
+const char* QuickResourceName( const char* prefix, int id, const char* extension );
+void WaitForRegainFocus();
+
+
+#define flashyAnimation true
+
+
+typedef struct
+{
+ short left, right, drop, rotate;
+} KeyList;
+
+
+#define kGridAcross 6
+#define kGridDown 13
+
+enum
+{
+ eNoState = 0,
+ eStartMenu,
+ eInGame,
+ eInGameOver,
+ eSharewareVictory,
+ eRegisteredVictory,
+ eFinished
+};
+
+
+enum
+{
+ dFatalErrorAlert = 128,
+ dAbout = 130,
+ dSerialNumber,
+ dShareware,
+ dInformation,
+ dRegister,
+ dSoundTest,
+ dWarp,
+ dOptions = 500,
+ dKeySelect = 800
+};
+
+enum
+{
+ mBar = 128,
+ mApple = 128,
+ mFile,
+ iAbout = 1,
+ iQuit = 1
+};
+
+enum
+{
+ picBoard = 1000,
+ picBackdrop,
+ picNext,
+ picVictory,
+ picSelectDifficulty,
+ picBoardRight,
+ picBlob = 200,
+ picBlobMask,
+ picCharMask,
+ picNumber,
+ picNumberMask,
+ picBlast,
+ picBlastMask,
+ picFont = 250,
+ picHiScoreFont,
+ picContinueFont,
+ picBalloonFont,
+ picZapFont,
+ picZapOutlineFont,
+ picVictoryFont,
+ picBubbleFont,
+ picTinyFont,
+ picDashedLineFont,
+ picBatsuFont,
+ picTitle = 300,
+ picSharewareVictory,
+ picGameStart,
+ picGameOver,
+ picVictory1,
+ picVictory2,
+ picVictory3,
+ picVictory4,
+ picVictory5,
+ picVictory6,
+ picLogo = 500,
+ picLogoAlpha,
+ picLogoMask
+};
+
+enum
+{
+ errNoMonitor = 1,
+ errNoDrawSprocket,
+ errNoMemory,
+ errNoQuickTime,
+ errUnknown
+};
+
+enum
+{
+ winPlayer = 128,
+ winBackdrop,
+ winNext,
+ winScore,
+ winOpponent,
+ winTitle,
+ winVictory,
+ winLevel
+};
+
+enum
+{
+ rightRotate = 0,
+ downRotate,
+ leftRotate,
+ upRotate
+};
+
+enum
+{
+ kEmpty = 0,
+ kBlob,
+ kBlob2,
+ kBlob3,
+ kBlob4,
+ kBlob5,
+ kBlob6,
+ kBlob7,
+ kBombTop,
+ kBombBottom,
+ kGray,
+ kLight,
+ kSun
+};
+
+enum
+{
+ kNoSuction = 0,
+ kUp = 1,
+ kRight = 2,
+ kUpRight = 3,
+ kDown = 4,
+ kUpDown = 5,
+ kRightDown = 6,
+ kUpRightDown = 7,
+ kLeft = 8,
+ kLeftUp = 9,
+ kLeftRight = 10,
+ kLeftUpRight = 11,
+ kLeftDown = 12,
+ kLeftUpDown = 13,
+ kLeftRightDown = 14,
+ kLeftUpRightDown = 15,
+ kDying = 16,
+ kSquish = 17,
+ kSquash = 18,
+ kSquish1 = 19,
+ kSquish2 = 20,
+ kSquish3 = 21,
+ kSquish4 = 22,
+ kBlinkBlob = 23,
+ kSobBlob = 24,
+ kSob2Blob = 25,
+ kFlashDarkBlob = 26,
+ kFlashBrightBlob = 27,
+ kJiggle1 = 28,
+ kJiggle2 = 29,
+ kJiggle3 = 30,
+ kJiggle4 = 31,
+ kJiggle5 = 32,
+ kJiggle6 = 33,
+ kJiggle7 = 34,
+ kJiggle8 = 35,
+ kInDoubt = 36,
+ kInDeath = 37
+};
+
+enum
+{
+ kNoCharring = 0,
+ kBombFuse1 = 0,
+ kBombFuse2 = 1,
+ kBombFuse3 = 2,
+ kBlinkBomb1 = 3,
+ kBlinkBomb2 = 4,
+ kBlinkBomb3 = 5,
+ kChar11,
+ kChar31,
+ kChar12,
+ kChar32,
+ kChar13,
+ kChar33,
+ kChar14,
+ kChar24,
+ kChar34
+};
+
+enum
+{
+ kDarkChar = 0xF0,
+ kLightestChar = 0x00
+};
+
+enum
+{
+ kFlashAnimation = 0,
+ kJiggleAnimation
+};
+
+#define kBlobFrames (kFlashBrightBlob+1)
+
+enum
+{
+ kGrayNoBlink = 0,
+ kGrayBlink1,
+ kGrayBlink2,
+ kGrayBlink3,
+ kSunGlow1,
+ kSunGlow2,
+ kSunGlow3,
+ kSunGlow4,
+ kSmallGray1,
+ kSmallGray1b,
+ kSmallGray2,
+ kSmallGray2b,
+ kSmallGray3,
+ kSmallGray3b,
+ kSmallGray4,
+ kSmallGray4b,
+ kSmallGray5,
+ kSmallGray5b,
+ kEasyGray = 25,
+ kHardGray,
+ kStageGray
+};
+#define kGrayFrames (kGrayDying4+1)
+
+#define kFirstBlob kBlob
+#define kLastBlob kBlob7
+#define kBlobTypes (kLastBlob - kFirstBlob + 1)
+
+#define pi 3.1415926535898
+
+extern SDL_Surface* frontSurface;
+
+extern signed char nextA[2], nextB[2], nextM[2], nextG[2], colorA[2], colorB[2],
+ blobX[2], blobY[2], blobR[2], blobSpin[2], speed[2], role[2], halfway[2],
+ control[2], dropping[2], magic[2], grenade[2], anim[2];
+extern int chain[2];
+extern long blobTime[2], startTime, endTime;
+extern MBoolean finished, pauseKey, showStartMenu;
+extern signed char grid[2][kGridAcross][kGridDown], suction[2][kGridAcross][kGridDown],
+ charred[2][kGridAcross][kGridDown], glow[2][kGridAcross][kGridDown];
+extern MRect playerWindowZRect, playerWindowRect[2];
+extern MBoolean playerWindowVisible[2];
+extern KeyList hitKey[2];
+extern int backgroundID;
+extern MBoolean playerIsRegistered;
+extern char registeredName[64];
+extern char registeredKey[18];
+extern void (*DoFullRepaint)();
--- /dev/null
+++ b/Source/moving.cpp
@@ -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;
+}
--- /dev/null
+++ b/Source/moving.h
@@ -1,0 +1,14 @@
+// moving.h
+
+MBoolean CanMoveDirection( int player, int dirX, int dirY );
+void CalcSecondBlobOffset( int player, int *x, int *y );
+MBoolean CanGoLeft( int player );
+void GoLeft( int player );
+MBoolean CanGoRight( int player );
+void GoRight( int player );
+MBoolean CanFall( int player );
+void DoFall( int player );
+MBoolean CanRotate( int player );
+void DoRotate( int player );
+void DoDrop( int player );
+void StopDrop( int player );
--- /dev/null
+++ b/Source/music.h
@@ -1,0 +1,15 @@
+// music.h
+
+
+void EnableMusic( MBoolean on );
+void PauseMusic( void );
+void ResumeMusic( void );
+void FastMusic( void );
+void SlowMusic( void );
+void ChooseMusic( short which );
+
+#define kSongs 14
+
+extern MBoolean musicOn;
+extern int musicSelection;
+
--- /dev/null
+++ b/Source/next.cpp
@@ -1,0 +1,208 @@
+// next.c
+
+#include "SDL.h"
+#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};
+int nextTime[2][2], 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, 16 );
+
+ nextDrawSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &nextWindowZRect, &sdlRect ), 16 );
+}
+
+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/Source/next.h
@@ -1,0 +1,18 @@
+// next.h
+
+
+#include "SDL.h"
+
+
+void InitNext( void );
+void ShowNext( int player );
+void RefreshNext( int player );
+void UpdateNext( int player );
+void PullNext( int player );
+void ShowPull( int player );
+
+extern SDL_Surface* nextSurface;
+extern SDL_Surface* nextDrawSurface;
+
+extern MBoolean nextWindowVisible[2];
+extern MRect nextWindowZRect, nextWindowRect[2];
--- /dev/null
+++ b/Source/opponent.cpp
@@ -1,0 +1,257 @@
+// opponent.c
+
+#include "SDL.h"
+#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;
+int opponentTime, glowTime[kGlows], glowFrame[kGlows], panicTime, 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 ), 16 );
+ opponentSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &bigRect, &sdlRect ), 16 );
+
+ bigRect.bottom *= kGlows + 1;
+ opponentMaskSurface = SDLU_InitSurface( SDLU_MRectToSDLRect( &bigRect, &sdlRect ), 1 );
+
+ 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 * 24.0);
+ glowArray [(int)index] = (int)(value * 16.0);
+ lightGlowArray[(int)index] = (int)(value * 12.0);
+ }
+}
+
+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].color )
+ {
+ 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].color & 0x8000 )
+ {
+ SurfaceBlitColor( opponentMaskSurface, opponentDrawSurface,
+ &maskRect, &dstRect,
+ (character[1].glow[count].color & 0x7C00) >> 10,
+ (character[1].glow[count].color & 0x03E0) >> 5,
+ (character[1].glow[count].color & 0x001F),
+ heavyGlowArray[glowFrame[count]] );
+ }
+ else
+ {
+ SurfaceBlitColor( opponentMaskSurface, opponentDrawSurface,
+ &maskRect, &dstRect,
+ (character[1].glow[count].color & 0x7C00) >> 10,
+ (character[1].glow[count].color & 0x03E0) >> 5,
+ (character[1].glow[count].color & 0x001F),
+ lightGlowArray[glowFrame[count]] );
+ }
+ }
+ }
+
+ if( panicFrame )
+ {
+ SurfaceBlitColor( opponentMaskSurface, opponentDrawSurface,
+ &myRect, &dstRect,
+ 31, 31, 22, glowArray[panicFrame] );
+ }
+
+ SDLU_ReleaseSurface( opponentDrawSurface );
+
+ SDLU_BlitFrontSurface( opponentDrawSurface,
+ SDLU_MRectToSDLRect( &opponentWindowZRect, &srcSDLRect ),
+ SDLU_MRectToSDLRect( &opponentWindowRect, &dstSDLRect ) );
+ }
+}
--- /dev/null
+++ b/Source/opponent.h
@@ -1,0 +1,26 @@
+// opponent.h
+
+void InitOpponent( void );
+void BeginOpponent( int which );
+void UpdateOpponent( void );
+void DrawFrozenOpponent( void );
+void OpponentChatter( MBoolean on );
+void OpponentPissed( void );
+
+enum
+{
+ kBored=0,
+ kLookRight,
+ kLookLeft,
+ kBlink1,
+ kBlink2
+};
+
+#define kOppFrames (kBlink2+1)
+#define kGlowArraySize 30
+
+extern int opponentMood, opponentFrame;
+extern int opponentTime, glowTime[kGlows], glowFrame[kGlows], panicTime, panicFrame;
+extern int glowArray[kGlowArraySize], lightGlowArray[kGlowArraySize];
+
+extern MRect opponentWindowZRect, opponentWindowRect;
--- /dev/null
+++ b/Source/pause.cpp
@@ -1,0 +1,1553 @@
+// 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 "SDL.h"
+#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"
+#include "RegAlgorithm.h"
+
+
+const char kEscapeKey = 0x1B;
+
+typedef struct
+{
+ float red, green, blue;
+}
+FRGBColor;
+
+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, dialogTimer, dialogUndimTime, dialogTarget, dialogShade, dialogItem;
+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 length, current;
+ int r,g,b;
+ float s;
+
+ current = 0;
+ length = strlen(line);
+
+ switch( bright )
+ {
+ case kTextGray:
+ r = g = b = 12;
+ break;
+
+ case kTextBlueGlow:
+ s = sin(wave);
+ r = (int)(11.0 + 15.0 * s * s);
+ g = r;
+ b = 31;
+ break;
+
+ case kTextWhite:
+ r = g = b = 31;
+ break;
+
+ case kTextAlmostWhite:
+ r = g = b = 28;
+ break;
+
+ }
+
+ while( line[current] )
+ {
+ switch( bright )
+ {
+ case kTextBrightRainbow:
+ r = (int)(26.0 + 5.0 * sin(wave ));
+ g = (int)(26.0 + 5.0 * sin(wave + ((2.*pi) * 1./3.)));
+ b = (int)(26.0 + 5.0 * sin(wave + ((2.*pi) * 2./3.)));
+ break;
+
+ case kTextRainbow:
+ r = (int)(16.0 + 12.0 * sin(wave ));
+ g = (int)(16.0 + 12.0 * sin(wave + ((2.*pi) * 1./3.)));
+ b = (int)(16.0 + 12.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 short edge[4][kEdgeSize][kEdgeSize];
+
+void SurfaceGetEdges( SDL_Surface* edgeSurface, const MRect *rect )
+{
+ unsigned char* src[4];
+ int srcRowBytes, width, height, count;
+
+ src[0] = src[1] = src[2] = src[3] = (unsigned char*) edgeSurface->pixels;
+ srcRowBytes = edgeSurface->pitch;
+
+ width = rect->right - rect->left;
+ height = rect->bottom - rect->top;
+
+ src[0] += (srcRowBytes * (rect->top )) + ((rect->left ) * 2);
+ src[1] += (srcRowBytes * (rect->top )) + ((rect->right - kEdgeSize) * 2);
+ src[2] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->left ) * 2);
+ src[3] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->right - kEdgeSize) * 2);
+
+ for( count=0; count<4; count++ )
+ {
+ for( height=0; height<kEdgeSize; height++ )
+ {
+ memcpy( edge[count][height], src[count], kEdgeSize * 2 );
+ 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 ) * 2);
+ src[1] += (srcRowBytes * (rect->top )) + ((rect->right - kEdgeSize) * 2);
+ src[2] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->left ) * 2);
+ src[3] += (srcRowBytes * (rect->bottom - kEdgeSize)) + ((rect->right - kEdgeSize) * 2);
+
+ // Draw top/bottom border
+ {
+ short *srcT1 = (short*) (src[0]) + kEdgeSize;
+ short *srcB1 = (short*) (src[2] + (srcRowBytes*(kEdgeSize-1))) + kEdgeSize;
+ short *srcT2 = srcT1 + (srcRowBytes/2);
+ short *srcB2 = srcB1 - (srcRowBytes/2);
+
+ for( width = rect->right - rect->left - (kEdgeSize * 2); width > 0; width-- )
+ {
+ *srcT1 = 0; srcT1++;
+ *srcB1 = 0; srcB1++;
+ *srcT2 = (*srcT2 >> 1) & 0x3DEF; srcT2++;
+ *srcB2 = (*srcB2 >> 1) & 0x3DEF; srcB2++;
+ }
+ }
+
+ // Draw left/right border
+ {
+ unsigned char *srcL1 = (src[0] + (srcRowBytes * kEdgeSize));
+ unsigned char *srcR1 = (src[1] + (srcRowBytes * kEdgeSize)) + 2*(kEdgeSize-1);
+
+ unsigned char *srcL2 = srcL1 + 2;
+ unsigned char *srcR2 = srcR1 - 2;
+
+ for( height = rect->bottom - rect->top - (kEdgeSize * 2); height > 0; height-- )
+ {
+ *(short*)srcL1 = 0;
+ *(short*)srcR1 = 0;
+ *(short*)srcL2 = (*(short*)srcL2 >> 1) & 0x3DEF;
+ *(short*)srcR2 = (*(short*)srcR2 >> 1) & 0x3DEF;
+
+ srcL1 += srcRowBytes;
+ srcR1 += srcRowBytes;
+ srcL2 += srcRowBytes;
+ srcR2 += srcRowBytes;
+ }
+ }
+
+ // Draw curved edges
+ for( count=0; count<4; count++ )
+ {
+ short *srcS = (short*) 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 = 0; break;
+ case 'x': *srcS = (*srcS >> 1) & 0x3DEF; break;
+ case '-': *srcS = (edge[count][height][width] >> 1) & 0x3DEF; break;
+ case 'X': break;
+ }
+ srcS++;
+ }
+ srcS += (srcRowBytes / 2) - kEdgeSize;
+ }
+ }
+}
+
+static MBoolean SharewareNoticeIsStillWaiting()
+{
+ return MTickCount() < dialogUndimTime;
+}
+
+
+#define min(x,y) (((x)<(y))?(x):(y))
+#define max(x,y) (((x)>(y))?(x):(y))
+#define arrsize(x) (sizeof(x)/sizeof(x[0]))
+
+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)),
+ 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;
+}
+
+static void DrawDialogCursor( MRect *pauseRect, int *shade )
+{
+ MPoint p, q;
+ 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, '�', &p, 0, 0, 0, 0 );
+ SurfaceBlitCharacter( smallFont, '�', &q, 31, 31, 31, 0 );
+
+ SDLU_ReleaseSurface( drawSurface );
+}
+
+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)? 31: (shade / 2);
+
+ SurfaceBlitWeightedDualAlpha( drawSurface, logoSurface, logoMaskSurface, logoAlphaSurface, drawSurface,
+ &drawRect, &logoRect, &logoRect, &logoRect, &drawRect,
+ alpha );
+
+ SDLU_ReleaseSurface( drawSurface );
+}
+
+
+enum
+{
+ kNothing = -1,
+
+// main pause screen (kEndGame is reused in continue and register)
+ kMusic = 0, kEndGame,
+ kSound, kPauseGame,
+ kControls, kResume,
+ kRegisterNow, kSecret,
+ kWarp, kSoundTest,
+
+// continue screen
+ kContinue,
+
+// register screen
+ kLater,
+
+// controls screen
+ k1PLeft, k2PLeft,
+ k1PRight, k2PRight,
+ k1PDrop, k2PDrop,
+ k1PRotate, k2PRotate,
+ kControlsOK, kControlsReset,
+
+// shareware notice screen
+ kSharewareNoticeNotYet,
+ kSharewareNoticeEnterCode,
+ kSharewareNoticePurchase,
+
+// enter code screen
+ kEnterCodeOK,
+ kEnterCodeNotYet
+};
+
+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;
+
+ SurfaceBlitWeightedCharacter( continueFont, countdown, &hP, 0, 0, 0, 20 - 4*shade );
+ }
+
+ SurfaceBlitCharacter( continueFont, countdown, &hPoint, r, g, 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;
+ char *line[3], *scan;
+
+ 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[*scan++];
+
+ dPoint[index].h /= 2;
+ }
+
+ SDLU_AcquireSurface( drawSurface );
+
+ while( dashedLinePoint.h < 490 )
+ {
+ SurfaceBlitCharacter( dashedLineFont, '.', &dashedLinePoint, 0, 0, 0, 0 );
+ }
+
+ nameLength = strlen(highScoreName);
+ for( index = 0; index < nameLength; index++ )
+ {
+ SurfaceBlitCharacter( bigFont, highScoreName[index], &hPoint, 31, 31, 31, 1 );
+ if( hPoint.h >= 475 )
+ {
+ highScoreName[index] = '\0';
+ break;
+ }
+ }
+
+ index = (int)(( 1.0 + sin( MTickCount() / 7.5 ) ) * 15.0);
+ SurfaceBlitCharacter( bigFont, '|', &hPoint, index, index, 31, 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 DrawRegisterContents( int *item, int shade )
+{
+ int index;
+ MPoint dPoint[4] = { {240, 150}, {260, 160}, {305, 170}, {305, 400} };
+ char line[4][50] = { "Sorry, you must register Candy Crisis",
+ "to gain access to two player mode!",
+ "Register Now",
+ "Not Yet" };
+
+ 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) ||
+ ((index == 2) && (*item == kRegisterNow )) ||
+ ((index == 3) && (*item == kLater )) )? 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 = (int)(highlight? 31.0: 0.0);
+ g = b = (int)(highlight? 31.0 - (11.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, "� OK", dPoint, 8.0 + (0.075 * shade), (*item == kControlsOK)? kTextBrightRainbow: kTextRainbow );
+
+ dPoint.h = 365;
+ dPoint.v = 340;
+ DrawRainbowText( smallFont, "� Reset", dPoint, 8.25 + (0.075 * shade), (*item == kControlsReset)? kTextBrightRainbow: kTextRainbow );
+
+ SDLU_ReleaseSurface( drawSurface );
+}
+
+static void DrawPauseContents( int *item, int shade )
+{
+ MPoint dPoint;
+ int itemCount = IsRegistered()? 6: 7;
+ int index;
+ char *line[7] = { "� Music", "� End Game",
+ "� Sound", "� Hide Game",
+ "� Controls", "� Resume",
+ "� Register Now" };
+
+
+ if( level == kTutorialLevel ) line[1] = "� Skip Tutorial";
+
+ if( !musicOn ) line[0] = "� Music";
+ if( !soundOn ) line[2] = "� Sound";
+
+ SDLU_AcquireSurface( drawSurface );
+
+ for( index=0; index<itemCount; index++ )
+ {
+ dPoint.h = (index & 1)? 340: 180;
+ dPoint.v = 240 + ((index & ~1) * 15);
+
+ DrawRainbowText( smallFont, line[index], dPoint, (0.25 * index) + (0.075 * shade), (*item == index)? kTextBrightRainbow: kTextRainbow );
+ }
+
+ SDLU_ReleaseSurface( drawSurface );
+}
+
+static void DrawSharewareNoticeContents( int *item, int shade )
+{
+ MPoint dPoint;
+ int index;
+ MBoolean itemsAreDimmed;
+ const char* line[10] = { "Candy Crisis is not free! You have a 30 day trial",
+ "period to try out the software. After 30 days, if",
+ "you wish to continue playing Candy Crisis, you",
+ "are expected to register.",
+ "",
+ "When you register Candy Crisis, we will send a",
+ "unique registration code to you by e-mail or postal",
+ "mail. This registration code will allow you to play",
+ "all twelve levels of the game, enables two player",
+ "mode, and removes these shareware notices." };
+
+ itemsAreDimmed = SharewareNoticeIsStillWaiting();
+
+ SDLU_AcquireSurface( drawSurface );
+
+ dPoint.v = 124;
+ dPoint.h = 140;
+ DrawRainbowText( bigFont, "Candy Crisis is shareware!", dPoint, 0, kTextWhite );
+
+ for( index=0; index<10; index++ )
+ {
+ dPoint.v = 165 + (16 * index);
+ dPoint.h = 156;
+ DrawRainbowText( tinyFont, line[index], dPoint, 0, 2 );
+ }
+
+ dPoint.v = 340;
+ dPoint.h = 130;
+ DrawRainbowText( smallFont, "� Not Yet", dPoint, (shade * 0.1), itemsAreDimmed? kTextGray: (*item == kSharewareNoticeNotYet? kTextBlueGlow: kTextWhite) );
+
+ dPoint.v = 340;
+ dPoint.h = 260;
+ DrawRainbowText( smallFont, "� Purchase", dPoint, (shade * 0.1), itemsAreDimmed? kTextGray: (*item == kSharewareNoticePurchase? kTextBlueGlow: kTextWhite) );
+
+ dPoint.v = 340;
+ dPoint.h = 390;
+ DrawRainbowText( smallFont, "� Enter Code", dPoint, (shade * 0.1), itemsAreDimmed? kTextGray: (*item == kSharewareNoticeEnterCode? kTextBlueGlow: kTextWhite) );
+
+ SDLU_ReleaseSurface( drawSurface );
+}
+
+static void DrawEnterCodeContents( int *item, int shade )
+{
+ MPoint dPoint;
+ int index;
+ const char* line[4] = { "Thank you for your purchase! First, enter your",
+ "name below. Please make sure it matches your",
+ "name exactly as it appears in your registration",
+ "email." };
+
+ SDLU_AcquireSurface( drawSurface );
+
+ dPoint.v = 124;
+ dPoint.h = 240;
+ DrawRainbowText( bigFont, "Enter Code", dPoint, 0, kTextWhite );
+
+ for( index=0; index<4; index++ )
+ {
+ dPoint.v = 165 + (16 * index);
+ dPoint.h = 156;
+ DrawRainbowText( tinyFont, line[index], dPoint, 0, kTextAlmostWhite );
+ }
+
+ dPoint.v = 165 + (int)(5.75 * 16);
+ dPoint.h = 140;
+ DrawRainbowText( dashedLineFont, "......................", dPoint, 0, kTextGray );
+
+ dPoint.v = 165 + (int)(4.5 * 16);
+ dPoint.h = 150;
+ dPoint = DrawRainbowText( smallFont, nameField, dPoint, 0, kTextWhite );
+
+ if( whichField == nameField )
+ {
+ DrawRainbowText( smallFont, "|", dPoint, (shade * 0.1), kTextBlueGlow );
+ }
+
+ dPoint.v = 165 + (int)(7 * 16);
+ dPoint.h = 156;
+ DrawRainbowText( tinyFont, "Next, enter your registration code.", dPoint, 0, kTextAlmostWhite );
+
+
+ dPoint.v = 165 + (int)(9.75 * 16);
+ dPoint.h = 140;
+ DrawRainbowText( dashedLineFont, "......................", dPoint, 0, kTextGray );
+
+ dPoint.v = 165 + (int)(8.5 * 16);
+ dPoint.h = 150;
+ dPoint = DrawRainbowText( smallFont, keyField, dPoint, 0, kTextWhite );
+
+ if( whichField == keyField )
+ {
+ DrawRainbowText( smallFont, "|", dPoint, (shade * 0.1), kTextBlueGlow );
+ }
+
+ dPoint.v = 340;
+ dPoint.h = 150;
+ DrawRainbowText( smallFont, "� OK", dPoint, (shade * 0.1), (*item == kEnterCodeOK? kTextBlueGlow: kTextWhite) );
+
+ dPoint.v = 340;
+ dPoint.h = 380;
+ DrawRainbowText( smallFont, "� Go Back", dPoint, (shade * 0.1), (*item == kEnterCodeNotYet? kTextBlueGlow: kTextWhite) );
+
+ if( batsuAlpha > 0 )
+ {
+ dPoint.v = 240 - 111;
+ dPoint.h = 320 - 111;
+ SurfaceBlitWeightedCharacter( batsuFont, 'X', &dPoint, 31, 0, 0, batsuAlpha-- );
+ }
+
+ SDLU_ReleaseSurface( drawSurface );
+}
+
+static MBoolean ContinueSelected( int *item, unsigned char inKey, SDLKey inSDLKey )
+{
+ MRect yes = {280, 220, 300, 260}, no = {280, 400, 300, 440};
+ MPoint p;
+
+ inSDLKey; // is unused
+
+ if( continueTimeOut )
+ {
+ *item = kEndGame;
+ return true;
+ }
+
+ if( inKey == kEscapeKey )
+ {
+ *item = kContinue;
+ return true;
+ }
+
+ SDLU_GetMouse( &p );
+
+ if( MPointInMRect( p, &yes ) ) *item = kContinue;
+ else if( MPointInMRect( p, &no ) ) *item = kEndGame;
+ else *item = kNothing;
+
+ return( SDLU_Button( ) && (*item != kNothing) );
+}
+
+static MBoolean RegisterSelected( int *item, unsigned char inKey, SDLKey inSDLKey )
+{
+ MRect registerNow = {305, 170, 325, 290}, registerLater = {305, 400, 325, 470};
+ MPoint p;
+
+ inKey, inSDLKey; // is unused
+
+ if( inKey == kEscapeKey )
+ {
+ *item = kLater;
+ return true;
+ }
+
+ SDLU_GetMouse( &p );
+
+ if( MPointInMRect( p, ®isterNow ) ) *item = kRegisterNow;
+ else if( MPointInMRect( p, ®isterLater ) ) *item = kLater;
+ else *item = kNothing;
+
+ if( SDLU_Button( ) )
+ {
+ switch( *item )
+ {
+ case kRegisterNow:
+ PlayMono( kClick );
+ return true;
+
+ case kLater:
+ PlayMono( kClick );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static MBoolean HiScoreSelected( int *item, unsigned char inKey, SDLKey inSDLKey )
+{
+ int nameLength;
+
+ inSDLKey; // is unused
+
+ nameLength = strlen(highScoreName);
+
+ // return
+ if( inKey == 13 )
+ {
+ if( nameLength > 0 )
+ {
+ *item = kResume;
+ PlayMono( kSquishy );
+ return true;
+ }
+ else
+ {
+ PlayMono( kClick );
+ }
+ }
+
+ // backspace
+ else if( inKey == 8 )
+ {
+ if( nameLength > 0 )
+ {
+ highScoreName[ nameLength-1 ] = '\0';
+ PlayMono( kClick );
+ }
+ }
+
+ // characters
+ else 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, SDLKey inSDLKey )
+{
+ MPoint p;
+ MRect dRect;
+ int index;
+ static MBoolean lastDown = false;
+ MBoolean down;
+ MRect okRect = { 340, 200, 360, 255 };
+ MRect resetRect = { 340, 365, 360, 450 };
+ int returnValue = 0;
+
+ inKey; // unused
+
+ *item = kNothing;
+
+ down = SDLU_Button();
+ SDLU_GetMouse( &p );
+
+ if( MPointInMRect( p, &okRect ) )
+ {
+ *item = kControlsOK;
+ if( down )
+ {
+ PlayMono( kClick );
+ returnValue = 1;
+ controlToReplace = -1;
+ }
+ }
+ else if( MPointInMRect( p, &resetRect ) )
+ {
+ *item = kControlsReset;
+ if( down && !lastDown )
+ {
+ PlayMono( kClick );
+ memcpy( playerKeys, defaultPlayerKeys, sizeof(playerKeys) );
+ }
+ }
+ else
+ {
+ 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, SDLKey inSDLKey )
+{
+ inSDLKey; // is unused
+
+ MRect targetRect[] =
+ {
+ { 240, 180, 260, 320 },
+ { 240, 340, 260, 480 },
+ { 270, 180, 290, 320 },
+ { 270, 340, 290, 480 },
+ { 300, 180, 320, 320 },
+ { 300, 340, 320, 480 },
+ { 330, 180, 350, 320 },
+ { 120, 550, 130, 560 }
+ };
+
+ static MBoolean lastDown = false;
+ int trigger;
+ int index;
+ MPoint p;
+
+ SDLU_GetMouse( &p );
+
+ trigger = SDLU_Button();
+ if( inKey == kEscapeKey )
+ {
+ *item = kResume;
+ trigger = true;
+ }
+ else
+ {
+ *item = kNothing;
+ for( index=0; index<arrsize(targetRect); index++ )
+ {
+ if( MPointInMRect( p, &targetRect[index] ) )
+ {
+ *item = index;
+ }
+ }
+ }
+
+ 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 kEndGame: PlayMono( kClick ); return true;
+ case kResume: PlayMono( kClick ); return true;
+
+ case kPauseGame:
+ PlayMono( kClick );
+ SDL_WM_IconifyWindow();
+ WaitForRegainFocus();
+ ItsTimeToRedraw();
+ return false;
+
+ case kRegisterNow:
+ PlayMono( kClick );
+ return true;
+
+ case kControls:
+ PlayMono( kClick );
+ return true;
+
+ case kSecret:
+ if( ControlKeyIsPressed( ) )
+ {
+ *item = kWarp;
+ level = Warp( );
+ return true;
+ }
+ else if( OptionKeyIsPressed( ) )
+ {
+ //SoundTest( );
+ ItsTimeToRedraw();
+ }
+ return false;
+ }
+ }
+ }
+ else
+ {
+ lastDown = false;
+ }
+
+ return false;
+}
+
+static MBoolean SharewareNoticeSelected( int *item, unsigned char inKey, SDLKey inSDLKey )
+{
+ MRect notYetRect = { 340, 130, 360, 220 };
+ MRect purchaseRect = { 340, 260, 360, 365 };
+ MRect enterCodeRect = { 340, 390, 360, 520 };
+ MPoint p;
+ MBoolean button;
+
+ inKey, inSDLKey; // are unused
+
+ *item = kNothing;
+
+ if( !SharewareNoticeIsStillWaiting() )
+ {
+ SDLU_GetMouse( &p );
+ button = SDLU_Button();
+
+ if( MPointInMRect( p, ¬YetRect ) ) *item = kSharewareNoticeNotYet;
+ else if( MPointInMRect( p, &enterCodeRect ) ) *item = kSharewareNoticeEnterCode;
+ else if( MPointInMRect( p, &purchaseRect ) )
+ {
+ *item = kSharewareNoticePurchase;
+ if( button )
+ {
+ WaitForRelease();
+ LaunchURL( "http://candycrisis.com/register.html" );
+ button = false;
+ }
+ }
+
+ return (*item != kNothing) && button;
+ }
+
+ return false;
+}
+
+
+static MBoolean EnterCodeSelected( int *item, unsigned char inKey, SDLKey inSDLKey )
+{
+ MRect okRect = { 340, 150, 360, 220 };
+ MRect notYetRect = { 340, 380, 360, 500 };
+ MRect nameRect = { 237, 140, 257, 500 };
+ MRect keyRect = { 301, 140, 321, 500 };
+ MPoint p;
+ int fieldLength;
+ int button;
+
+ inSDLKey; // is unused
+
+ // -- Handle keyboard stuff. (Ripped off from high score code.)
+ fieldLength = strlen(whichField);
+
+ // return or tab
+ if( inKey == 13 || inKey == 9 )
+ {
+ whichField = (whichField == nameField)? keyField: nameField;
+ PlayMono( kRotate );
+ }
+
+ // backspace
+ else if( inKey == 8 )
+ {
+ if( fieldLength > 0 )
+ {
+ whichField[ fieldLength-1 ] = '\0';
+ PlayMono( kClick );
+ }
+ }
+
+ // characters
+ else if( (fieldLength < 40) && (smallFont->width[inKey] != 0) )
+ {
+ if( whichField == keyField ) inKey = toupper(inKey);
+
+ whichField[ fieldLength++ ] = inKey;
+ whichField[ fieldLength ] = '\0';
+ PlayMono( kPlace );
+ }
+
+ // -- Handle mouse.
+ button = SDLU_Button();
+
+ *item = kNothing;
+ SDLU_GetMouse( &p );
+
+ if( MPointInMRect( p, ¬YetRect ) ) *item = kEnterCodeNotYet;
+ else if( MPointInMRect( p, &nameRect) && button ) whichField = nameField;
+ else if( MPointInMRect( p, &keyRect) && button ) whichField = keyField;
+ else if( MPointInMRect( p, &okRect ) )
+ {
+ *item = kEnterCodeOK;
+
+ if( button )
+ {
+ WaitForRelease();
+ playerIsRegistered = ValidateCode( nameField, keyField );
+ if( playerIsRegistered )
+ {
+ strcpy( registeredName, nameField );
+ strcpy( registeredKey, keyField );
+ }
+ else
+ {
+ batsuAlpha = 31;
+ PlayMono( kBatsuSnd );
+ button = false;
+ }
+ }
+ }
+
+ return (*item != kNothing) && button;
+}
+
+
+void SharewareNotice( int forceWait )
+{
+ SDL_FillRect( frontSurface, &frontSurface->clip_rect, SDL_MapRGB( frontSurface->format, 40, 40, 40 ) );
+ SDL_Flip( frontSurface );
+ dialogUndimTime = MTickCount() + forceWait;
+
+ HandleDialog( kSharewareNoticeDialog );
+}
+
+
+void HandleDialog( int type )
+{
+ const float lighten[4] = { 12.0f, 6.0f, 1.0f, 6.0f };
+ const MRect boardWorldZRect = {0, 0, kBlobVertSize * (kGridDown-1), kBlobHorizSize * kGridAcross};
+ const MRect fullRect = { 0, 0, 480, 640 };
+ SDL_Rect fullSDLRect = { 0, 0, 640, 480 };
+ SDL_Rect joinSDLRect;
+ int skip = 1;
+ int count;
+ char inASCII;
+ SDLKey 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 );
+
+ if( type == kSharewareNoticeDialog || type == kEnterCodeDialog )
+ {
+ // People shouldn't enjoy the nag dialogs, so let's not have flashy animated colors.
+ for( count=0; count<4; count++ )
+ {
+ backColor[count].red = 2;
+ backColor[count].green = 2;
+ backColor[count].blue = 2;
+ }
+ }
+ else
+ {
+ // 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 = inColor.r * (32.0f / 256.0f);
+ backColor[count].green = inColor.g * (32.0f / 256.0f);
+ backColor[count].blue = inColor.b * (32.0f / 256.0f);
+
+ backColor[count].red = min( 31.0f, backColor[count].red + lighten[count] );
+ backColor[count].green = min( 31.0f, backColor[count].green + lighten[count] );
+ backColor[count].blue = min( 31.0f, backColor[count].blue + lighten[count] );
+ }
+ }
+
+ // Get some graphics that we're going to need
+ logoSurface = LoadPICTAsSurface( picLogo, 16 );
+ logoAlphaSurface = LoadPICTAsSurface( picLogoAlpha, 16 );
+ logoMaskSurface = LoadPICTAsSurface( picLogoMask, 1 );
+
+ // Get a copy of the current game window contents
+ backSurface = SDLU_InitSurface( &fullSDLRect, 16 );
+
+ SDLU_BlitSurface( frontSurface, &frontSurface->clip_rect,
+ backSurface, &backSurface->clip_rect );
+
+ drawSurface = SDLU_InitSurface( &fullSDLRect, 16 );
+
+ 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;
+
+ // Check mouse and keyboard
+ SDLU_CheckTyping( &inASCII, &inSDLKey );
+
+ if( (dialogStage == kOpening) && dialogStageComplete )
+ {
+ MBoolean (*DialogSelected[kNumDialogs])( int *item, unsigned char inKey, SDLKey inSDLKey ) =
+ {
+ PauseSelected,
+ HiScoreSelected,
+ ContinueSelected,
+ RegisterSelected,
+ ControlsSelected,
+ SharewareNoticeSelected,
+ EnterCodeSelected
+ };
+
+ if( DialogSelected[dialogType]( &dialogItem, inASCII, inSDLKey ) )
+ {
+ dialogStage = kClosing;
+ dialogTarget = 0;
+ }
+ }
+
+ // Do animation ...
+ {
+ const MBoolean dialogIsLarge[kNumDialogs] = { false, false, false, false, true, true, true };
+
+ pauseRect = lastPauseRect;
+ dialogStageComplete = DrawDialogBox( dialogIsLarge[dialogType], dialogStage, &dialogTarget, skip, &colorWrap, colorInc, &pauseRect );
+ SurfaceGetEdges( backSurface, &pauseRect );
+ }
+
+ if( (dialogStage == kOpening) && dialogStageComplete )
+ {
+ void (*DialogDraw[kNumDialogs])( int *item, int shade ) =
+ {
+ DrawPauseContents,
+ DrawHiScoreContents,
+ DrawContinueContents,
+ DrawRegisterContents,
+ DrawControlsContents,
+ DrawSharewareNoticeContents,
+ DrawEnterCodeContents
+ };
+
+ // Refresh screen if necessary
+ if( timeToRedraw )
+ {
+ SDLU_BlitFrontSurface( backSurface, &fullSDLRect, &fullSDLRect );
+ timeToRedraw = false;
+ }
+
+ // ... and fade in the logo
+
+ dialogShade += skip;
+
+ {
+ const MBoolean dialogHasCandyCrisisLogo[kNumDialogs] = { true, true, true, true, true, false, false };
+
+ if( dialogHasCandyCrisisLogo[dialogType] )
+ DrawDialogLogo( &pauseRect, dialogShade );
+ }
+
+ // ... and animation is complete so add content
+ DialogDraw[dialogType]( &dialogItem, dialogShade );
+
+ // ... and cursor
+ DrawDialogCursor( &pauseRect, &dialogShade );
+ }
+
+ SurfaceCurveEdges( drawSurface, &pauseRect );
+
+ // Draw new animation on screen
+ UnionMRect( &lastPauseRect, &pauseRect, &joinRect );
+ SDLU_MRectToSDLRect( &joinRect, &joinSDLRect );
+ SDLU_BlitFrontSurface( drawSurface, &joinSDLRect, &joinSDLRect );
+
+ 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 );
+
+ // 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 kRegisterNow:
+ SharewareNotice( 0 );
+ RefreshAll();
+ break;
+
+ case kSharewareNoticeEnterCode:
+ HandleDialog( kEnterCodeDialog );
+ break;
+
+ case kEnterCodeNotYet:
+ HandleDialog( kSharewareNoticeDialog );
+ 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/Source/pause.h
@@ -1,0 +1,18 @@
+// pause.h
+
+void SharewareNotice( int forcedWait );
+void HandleDialog( int type );
+void SurfaceGetEdges( SDL_Surface* edgeSurface, const MRect *rect );
+void SurfaceCurveEdges( SDL_Surface* edgeSurface, const MRect *rect );
+
+enum
+{
+ kPauseDialog = 0,
+ kHiScoreDialog,
+ kContinueDialog,
+ kRegisterDialog,
+ kControlsDialog,
+ kSharewareNoticeDialog,
+ kEnterCodeDialog,
+ kNumDialogs
+};
--- /dev/null
+++ b/Source/players.cpp
@@ -1,0 +1,1079 @@
+// players.c
+
+#include "SDL.h"
+#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 long boredTime[2], hintTime[2], fadeCharTime[2], animTime[2], shadowDepth[2], hintGlow, messageTime;
+int emotions[2];
+int glowColors[][3] = { { 0, 0, 0},
+ {13, 26, 31},
+ {13, 29, 13},
+ {31, 18, 31},
+ {31, 14, 18},
+ {31, 31, 15},
+ {31, 21, 13},
+ {30, 22, 30},
+ {20, 20, 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 >= 8 )
+ {
+ 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, delay = -6;
+ 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] = 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/Source/players.h
@@ -1,0 +1,52 @@
+// players.h
+
+void HandlePlayers( void );
+void RetrieveBlobs( int player );
+void Falling( int player );
+void PlaceBlobs( int player );
+void DropBlobs( int player );
+void ResolveSuction( int player );
+void BlinkBored( int player );
+void InitPlayers( void );
+void JiggleBlobs( int player );
+void FastJiggleBlobs( int player );
+void LockdownBlobs( int player );
+void HandleMagic( int player );
+void ChooseGlowingBlobs( int player );
+void ConsiderGlow( int player, int color, int x, int y );
+void GlowBlobs( int player );
+void PlaceGrenade( int player );
+void FadeCharred( int player );
+void RedrawBoardContents( int player );
+
+extern unsigned long boredTime[2], hintTime[2], fadeCharTime[2], animTime[2], shadowDepth[2], messageTime;
+extern MBoolean idling[2];
+extern int emotions[2];
+extern int glowColors[][3];
+
+enum
+{
+ kFalling = 0,
+ kRetrieveBlobs,
+ kPlaceBlobs,
+ kJiggleBlobs,
+ kFastJiggleBlobs,
+ kLockdownBlobs,
+ kDropBlobs,
+ kZapBlobs,
+ kKillBlobs,
+ kDropGrays,
+ kLosing,
+ kWinning,
+ kChooseDifficulty,
+ kWaitingToStart,
+ kIdlePlayer,
+ kWaitForRetrieval
+};
+
+#define kDropSpeed 1
+#define kFallSpeed 1
+#define kFlashSpeed 5
+
+#define kLeftPlayerWindowCenter 0.16
+#define kRightPlayerWindowCenter 0.84
--- /dev/null
+++ b/Source/prefs.cpp
@@ -1,0 +1,154 @@
+// prefs.c
+
+// NOTE THAT NONE OF THIS CODE IS ENDIAN-SAVVY.
+// PREFERENCES FILES WILL NOT TRANSFER BETWEEN PLATFORMS.
+
+#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"
+
+#define kPrefsMaxSize 65536
+
+PrefList prefList[] = {
+ { 'mod ', &musicOn, sizeof( MBoolean ) },
+ { 'sfx ', &soundOn, sizeof( MBoolean ) },
+ { 'size', registeredKey, sizeof( registeredKey ) },
+ { 'keys', playerKeys, sizeof( playerKeys ) },
+ { 'high', scores, sizeof( scores ) },
+ { 'cmbx', &best, sizeof( best ) },
+ { 'user', registeredName, sizeof( registeredName ) }
+ };
+
+#define kPrefListSize (sizeof(prefList)/sizeof(prefList[0]))
+
+
+/* Loads the preferences from a file in the System Folder:Preferences. */
+
+void LoadPrefs( void )
+{
+ FILE *F;
+ int fileSize, count, digitsLeft;
+ unsigned char info, *infoAt, *dataAt, *fileData;
+
+ F = fopen( QuickResourceName( "Preferences", 0, ".txt" ), "r" );
+
+ if( F != NULL )
+ {
+ fileData = (unsigned char*) calloc( 1, kPrefsMaxSize );
+ if( fileData != NULL )
+ {
+ fileSize = fread( fileData, 1, kPrefsMaxSize, F );
+ if( fileSize >= 0 )
+ {
+ for( count=0; count<kPrefListSize; count++ )
+ {
+ infoAt = FindPrefsLine( fileData, fileSize, prefList[count].itemName, prefList[count].size );
+ if( infoAt )
+ {
+ dataAt = (unsigned char*) prefList[count].itemPointer;
+ digitsLeft = prefList[count].size;
+
+ while( digitsLeft-- )
+ {
+ info = ((*infoAt >= 'A')? (*infoAt - 'A' + 0xA): (*infoAt - '0')) << 4;
+ infoAt++;
+ info |= ((*infoAt >= 'A')? (*infoAt - 'A' + 0xA): (*infoAt - '0'));
+ infoAt++;
+
+ *dataAt++ = info;
+ }
+ }
+ }
+ }
+
+ free( fileData );
+ }
+
+ fclose( F );
+ }
+}
+
+/* Finds a specific line in the prefs. */
+
+unsigned char* FindPrefsLine( unsigned char *prefsText, long prefsLength, long searchCode, long dataQuantity )
+{
+ unsigned char *prefsAt, *check, *endCheck;
+
+ for( prefsAt = prefsText; prefsAt < (prefsText+prefsLength-3); prefsAt++ )
+ {
+ if( (prefsAt[0] == ((searchCode >> 24) & 0xFF)) &&
+ (prefsAt[1] == ((searchCode >> 16) & 0xFF)) &&
+ (prefsAt[2] == ((searchCode >> 8) & 0xFF)) &&
+ (prefsAt[3] == ((searchCode ) & 0xFF)) )
+ {
+ prefsAt += 6;
+
+ // perform sizing check
+
+ dataQuantity *= 2; // hexadecimal bytes are 2 chars
+
+ if( ((prefsAt + dataQuantity) - prefsText) > prefsLength ) return NULL; // prefs block ended too early
+
+ check = prefsAt;
+ endCheck = check + dataQuantity;
+ while( check < endCheck )
+ {
+ if( (*check < '0' || *check > '9') && (*check < 'A' || *check > 'F') )
+ {
+ return NULL; // incorrect size, too short
+ }
+
+ check++;
+ }
+
+ if( (*endCheck >= '0' && *endCheck <= '9') || (*endCheck >= 'A' && *endCheck <= 'F') )
+ {
+ return NULL; // incorrect size, too long
+ }
+
+ return prefsAt;
+ }
+ }
+
+ return NULL;
+}
+
+/* Saves out preferences into a file. */
+
+void SavePrefs( void )
+{
+ FILE *F;
+ short count, size;
+ unsigned char* dataAt;
+
+ F = fopen( QuickResourceName( "Preferences", 0, ".txt" ), "w" );
+
+ if( F != NULL )
+ {
+ for( count=0; count<kPrefListSize; count++ )
+ {
+ fprintf( F, "%c%c%c%c: ", (prefList[count].itemName >> 24) & 0xFF,
+ (prefList[count].itemName >> 16) & 0xFF,
+ (prefList[count].itemName >> 8) & 0xFF,
+ (prefList[count].itemName ) & 0xFF );
+
+ dataAt = (unsigned char*) prefList[count].itemPointer;
+ for( size=0; size<prefList[count].size; size++ )
+ {
+ fprintf( F, "%02X", *dataAt );
+ dataAt++;
+ }
+
+ fputc( '\n', F );
+ }
+ }
+
+ fclose( F );
+}
--- /dev/null
+++ b/Source/prefs.h
@@ -1,0 +1,17 @@
+// prefs.h
+
+void LoadPrefs( void );
+void SavePrefs( void );
+
+void GeneratePrefsFile( void );
+long UniqueSystem( void );
+
+unsigned char *FindPrefsLine( unsigned char *prefsText, long prefsLength, long searchCode, long dataQuantity );
+
+typedef struct
+{
+ long itemName;
+ void *itemPointer;
+ short size;
+}
+PrefList;
--- /dev/null
+++ b/Source/random.cpp
@@ -1,0 +1,110 @@
+// random.c
+
+#include <stdlib.h>
+
+#include "SDL.h"
+#include "main.h"
+#include "random.h"
+
+unsigned long randomSeed[2], pieceCount[2], grenadeTimer[2];
+int pieceMap[kBlobTypes], numPieces;
+
+
+static unsigned long 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 long realSeed;
+
+ realSeed = internalRandomSeed;
+
+ internalRandomSeed = randomSeed[player];
+ result = pieceMap[RandomBefore(numPieces)];
+ randomSeed[player] = internalRandomSeed;
+
+ internalRandomSeed = realSeed;
+
+ return result;
+}
+
+int GetMagic( int player )
+{
+ int result;
+ long 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/Source/random.h
@@ -1,0 +1,10 @@
+// random.h
+
+void InitRandom( int numPieces );
+int GetPiece( int player );
+int RandomBefore( int what );
+int GetMagic( int player );
+int GetGrenade( int player );
+void AddExtraPiece( void );
+
+extern int pieceMap[kBlobTypes];
--- /dev/null
+++ b/Source/score.cpp
@@ -1,0 +1,156 @@
+// score.c
+
+#include "SDL.h"
+#include "SDLU.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};
+long roundStartScore[2], score[2], displayedScore[2], 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 ), 16 );
+ DrawPICTInSurface( scoreSurface, picBoard );
+
+ numberSurface = LoadPICTAsSurface( picNumber, 16 );
+
+ numberMaskSurface = LoadPICTAsSurface( picNumberMask, 1 );
+
+ 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 )
+ {
+ }
+ else
+ {
+ 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 = 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;
+ char count, result;
+
+ result = -1;
+ for( count = 0; count < kNumberAmount; 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 );
+}
--- /dev/null
+++ b/Source/score.h
@@ -1,0 +1,25 @@
+// score.h
+
+void InitScore( void );
+void UpdateScore( int player );
+void ShowScore( int player );
+void DrawCharacter( char which, const MRect *myRect );
+
+#define kNumberHorizSize 16
+#define kNumberVertSize 32
+#define kNumberAmount 41
+
+extern MRect scoreWindowZRect, scoreWindowRect[2];
+extern MBoolean scoreWindowVisible[2];
+extern long roundStartScore[2], score[2], displayedScore[2];
+
+extern 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' };
+*/
+#define kCharacterZero 27
+#define kCharacterScore '!'
+#define kCharacterStage '#'
--- /dev/null
+++ b/Source/soundfx.h
@@ -1,0 +1,31 @@
+// soundfx.h
+
+void InitSound( void );
+void PlayStereo( short player, short which );
+void PlayStereoFrequency( short player, short which, short freq );
+void PlayMono( short which );
+void UpdateSound();
+
+enum
+{
+ kShift = 0,
+ kRotate,
+ kPlace,
+ kSquishy,
+ kBounce,
+ kSplop,
+ kWhistle,
+ kPause,
+ kLoss,
+ kVictory,
+ kMagic,
+ kWhomp,
+ kChime,
+ kClick,
+ kLevelUp,
+ kContinueSnd,
+ kBatsuSnd,
+ kNumSounds
+};
+
+extern MBoolean soundOn;
--- /dev/null
+++ b/Source/tutorial.cpp
@@ -1,0 +1,500 @@
+// tutorial.c
+
+#include "SDL.h"
+#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>
+
+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, 190, 210};
+SkittlesFontPtr balloonFont;
+MPoint balloonPt;
+char* balloonChar;
+char balloonMsg[256];
+int 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, 16 );
+ }
+
+ // 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[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( char *message )
+{
+ MPoint balloonTip, balloonFill;
+ int replace;
+ 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 - 40;
+ balloonFill = balloonTip;
+
+ SurfaceBlitCharacter( balloonFont, '�', &balloonFill, 0, 0, 0, 0 );
+ SurfaceBlitCharacter( balloonFont, '�', &balloonTip, 31, 31, 31, 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 )
+ {
+ 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/Source/tutorial.h
@@ -1,0 +1,33 @@
+// tutorial.h
+
+typedef enum
+{
+ kMessage = 0,
+ kIdleTicks,
+ kRetrieve,
+ kPosition,
+ kSpin,
+ kBlockUntilLand,
+ kBlockUntilDrop,
+ kPunish,
+ kComplete,
+ kBlockUntilComplete
+}
+AutoCommand;
+
+typedef struct
+{
+ AutoCommand command;
+ int d1, d2;
+ char *message;
+}
+AutoPattern, *AutoPatternPtr;
+
+extern AutoPatternPtr autoPattern;
+extern int tutorialTime;
+
+void InitTutorial( void );
+void StartBalloon( char *message );
+void StopBalloon( void );
+void UpdateBalloon( void );
+void EndTutorial( void );
--- /dev/null
+++ b/Source/tweak.cpp
@@ -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"
+
+long 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 );
+ }
+}
--- /dev/null
+++ b/Source/tweak.h
@@ -1,0 +1,11 @@
+// tweak.h
+
+void TweakFirstBlob( int player, MRect *first );
+void TweakSecondBlob( int player, MRect *second );
+void StartTweak( int player, int direction, int rotate, int fall );
+void UpdateTweak( int player, int suction );
+void InitTweak( void );
+
+#define d2r(x) ((x)*(pi/180))
+
+#define kTweakDelay 1
--- /dev/null
+++ b/Source/victory.cpp
@@ -1,0 +1,345 @@
+// victory.c
+
+#include "SDL.h"
+#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>
+
+unsigned long winTime, loseTime;
+long 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.1;
+ drop[count] = 0.4 + 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 )
+{
+ int 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.7 + last[x] / 12.0 ) );
+
+ 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, 31, 31, 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, 31, 31, 31, 1 );
+ dPoint.h--;
+ }
+
+ dPoint.h += 6;
+ SurfaceBlitCharacter( zapFont, 'S', &dPoint, 31, 31, 31, 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, 31, 31, 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, 31, 31, 31, 1 );
+ dPoint.h--;
+ }
+
+ dPoint.h += 6;
+ SurfaceBlitCharacter( zapFont, 'P', &dPoint, 31, 31, 31, 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/Source/victory.h
@@ -1,0 +1,14 @@
+// victory.h
+
+void EndRound( int player );
+void BeginVictory( int player );
+void InitVictory( void );
+void Lose( int player );
+void Win( int player );
+void DropLosers( int player, int skip );
+void DrawTimerCount( int player );
+void DrawTimerBonus( int player );
+
+#define kNumLetters 11
+#define kLetterHorizSize 13
+#define kLetterVertSize 20
--- /dev/null
+++ b/Source/zap.cpp
@@ -1,0 +1,618 @@
+// zap.c
+
+#include "SDL.h"
+#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];
+
+#define min(x,y) (((x)<(y))?(x):(y))
+#define arrsize(x) (sizeof(x)/sizeof(x[0]))
+
+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[*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 [] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 29, 28, 26, 24, 21, 18, 15, 12, 9, 6, 3, 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;
+
+ if( flashyAnimation )
+ {
+ 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;
+
+ if( flashyAnimation )
+ {
+ 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;
+
+ if( flashyAnimation )
+ {
+ 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;
+
+ if( flashyAnimation )
+ {
+ 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 };
+
+ 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 };
+ const int yVelocity = 2;
+
+ 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;
+ }
+ }
+}
--- /dev/null
+++ b/Source/zap.h
@@ -1,0 +1,30 @@
+// zap.h
+
+#include "font.h"
+
+void ZapScoreDisplay( int player, int amount, int multiplier, int x, int y, int c );
+int SizeUp( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color );
+int GetChainSize( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color );
+void CleanSize( signed char myGrid[kGridAcross][kGridDown], int x, int y, int color );
+void CleanWithPolish( signed char myGrid[kGridAcross][kGridDown], signed char polish[kGridAcross][kGridDown], int x, int y, int color );
+void RemoveBlobs( int player, int x, int y, int color, int generation );
+void KillBlobs( int player );
+void ZapBlobs( int player );
+void CleanChunks( int player, int x, int y, int level, int style );
+void DrawChunks( int player, int x, int y, int level, int style );
+void CleanSplat( int player, int x, int y, int level );
+void DrawSplat( int player, int x, int y, int level );
+void GetZapStyle( int player, MRect *myRect, int *color, int *type, int which,
+ int level, int style );
+void InitZapStyle( void );
+
+extern signed char death[2][kGridAcross][kGridDown];
+extern int zapIteration[2];
+
+extern int grenadeFrame[2];
+extern MRect grenadeRect[2];
+
+extern SkittlesFontPtr zapFont, zapOutline;
+
+#define kBlobClusterSize 4
+#define kZapFrames 20
binary files /dev/null b/fmod.dll differ
binary files /dev/null b/jpeg.dll differ
binary files /dev/null b/libpng1.dll differ
binary files /dev/null b/zlib.dll differ