warehouse/warehouse/main.c

1087 lines
24 KiB
C

/*
* Warehouse for JoeyLib - A Sokoban Clone
* Copyright (C) 2020 Scott Duensing <scott@kangaroopunch.com>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*/
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#include <stddclmr.h>
#define JOEY_MAIN
#include "joey.h"
#ifdef JOEY_IIGS
segment "warehouse";
#endif
#ifdef DMALLOC
#define DMALLOC_FUNC_CHECK
#include "dmalloc.h"
#endif
#define MAX_WIDTH 20
#define MAX_HEIGHT 12
#define TILE_NOTHING 0
#define TILE_WALL 1
#define TILE_FLOOR 2
#define TILE_GOAL 3
#define TILE_CRATE 4
#define TILE_PLAYER 5
#define TILE_PLAYER_ON_GOAL 6
#define TILE_CRATE_ON_GOAL 7
#define TILE_COUNT 8
typedef struct PuzzleS {
byte offsetX;
byte offsetY;
byte width;
byte height;
byte puzzle[MAX_WIDTH][MAX_HEIGHT];
} PuzzleT;
typedef struct SaveS {
jint16 lastPuzzle;
jint16 solvedSize;
byte *solved;
} SaveT;
typedef struct CoordS {
byte x;
byte y;
} CoordT;
static jlImgT *fontI = NULL;
static jlImgT *tilesI = NULL;
static jlImgT *aboutI = NULL;
static jlImgT *helpI = NULL;
static jint16 puzzleCount;
static jint16 puzzleCurrent;
static jint16 puzzleLast;
static jint16 puzzleSolved;
static jint32 *puzzleIndex = NULL;
static PuzzleT puzzle;
static SaveT saveGameData;
static byte puzzleNow[MAX_WIDTH][MAX_HEIGHT];
static byte puzzleBefore[MAX_WIDTH][MAX_HEIGHT];
static byte avatarX;
static byte avatarY;
static byte avatarXLast;
static byte avatarYLast;
static byte avatarXStart;
static byte avatarYStart;
static byte crateCount;
static byte crateInitialCount;
static byte cratesOnTarget;
static byte cratesInitiallyOnTarget;
//static char puzzleChars[] = { "_# .$@+*" };
static CoordT tileLookup[TILE_COUNT] = {
{ 10 * 8, 0 },
{ 0 * 8, 0 },
{ 2 * 8, 0 },
{ 4 * 8, 0 },
{ 6 * 8, 0 },
{ 8 * 8, 0 },
{ 8 * 8, 0 },
{ 6 * 8, 0 }
};
void countSolved(void);
void drawAvatar(void);
byte drawMenu(const char *title, const char *menuItems[], byte *height, byte *offsetX, byte *offsetY);
void drawPuzzle(void);
void forceFullRedraw(void);
void levelComplete(void);
void loadPuzzle(void);
bool mainMenu(void);
char menu(const char *title, const char *menuItems[], char selected);
void moveCrate(byte sx, byte sy, byte dx, byte dy);
void play(void);
void printAt(jlImgT *font, jint16 cx, jint16 cy, const char *what, ...);
bool readInput(byte *key);
void redraw(void);
void resetPuzzle(void);
void saveGame(void);
void selectLevel(void);
void showImage(jlImgT *image);
void showPalette(void);
void ticker(void);
void title(void);
void countSolved(void) {
byte data;
char bit = 0;
jint16 index = -1;
jint16 p;
puzzleSolved = 0;
// Count number of solved puzzles.
for (p=0; p<puzzleCount; p++) {
bit--;
if (bit < 0) {
bit = 7;
index++;
data = saveGameData.solved[index];
}
if (data & (1 << bit)) {
puzzleSolved++;
}
}
}
void drawAvatar(void) {
jint16 x = 0; // Screen (tile) coordinates.
jint16 y = 0;
CoordT t;
x = ((avatarX << 1) + puzzle.offsetX) << 3;
y = ((avatarY << 1) + puzzle.offsetY) << 3;
t = tileLookup[TILE_PLAYER];
jlDrawBlit8x8(jlImgSurfaceGet(tilesI), t.x, t.y, x, y );
jlDrawBlit8x8(jlImgSurfaceGet(tilesI), t.x + 8, t.y, x + 8, y );
jlDrawBlit8x8(jlImgSurfaceGet(tilesI), t.x, t.y + 8, x, y + 8);
jlDrawBlit8x8(jlImgSurfaceGet(tilesI), t.x + 8, t.y + 8, x + 8, y + 8);
}
byte drawMenu(const char *title, const char *menuItems[], byte *height, byte *offsetX, byte *offsetY) {
jint16 count = 0;
jint16 lx = ((byte)221 % 40) << 3; // Left-hand block ASCII
jint16 ly = ((byte)221 / 40) << 3;
jint16 rx = ((byte)222 % 40) << 3; // Right-hand block ASCII
jint16 ry = ((byte)222 / 40) << 3;
jint16 tx = ((byte)223 % 40) << 3; // Top-half block ASCII
jint16 ty = ((byte)223 / 40) << 3;
jint16 bx = ((byte)220 % 40) << 3; // Bottom-half block ASCII
jint16 by = ((byte)220 / 40) << 3;
jint16 x1;
jint16 y1;
jint16 x2;
jint16 y2;
jint16 y3;
byte width = 0;
// 222 223 221
// 222 220 221
// Calculate height, width, and offsets from menu count.
while (menuItems[count]) {
if (width < strlen(menuItems[count]) + 6) width = strlen(menuItems[count]) + 6;
count++;
}
*height = 8 + count * 2;
if (*offsetX == 0) *offsetX = 20 - width / 2;
if (*offsetY == 0) *offsetY = 11 - *height / 2;
// Clear area behind menu. Slightly higher and lower to add borders to the non-bordered ASCII characters.
jlDrawBoxFilled(*offsetX * 8, *offsetY * 8 - 4, (*offsetX + width + 1) * 8, (*offsetY + *height) * 8 + 4);
// Draw sides.
x1 = *offsetX * 8;
x2 = (*offsetX + width) * 8;
for (y1=(*offsetY * 8); y1<(*offsetY + *height) * 8; y1+=8) {
jlDrawBlit8x8(jlImgSurfaceGet(fontI), rx, ry, x1, y1);
jlDrawBlit8x8(jlImgSurfaceGet(fontI), lx, ly, x2, y1);
}
// Draw horizontal lines.
y1 = *offsetY * 8;
y2 = (*offsetY + 4) * 8;
y3 = (*offsetY + *height - 1) * 8;
for (x1=(*offsetX + 1) * 8; x1<(*offsetX + width) * 8; x1+=8) {
jlDrawBlit8x8(jlImgSurfaceGet(fontI), tx, ty, x1, y1);
jlDrawBlit8x8(jlImgSurfaceGet(fontI), bx, by, x1, y2);
jlDrawBlit8x8(jlImgSurfaceGet(fontI), bx, by, x1, y3);
}
x1 = (width / 2 - strlen(title) / 2) + *offsetX;
printAt(fontI, x1, *offsetY + 2, title);
// Draw menu.
x1 = *offsetX + 4;
y1 = *offsetY + 7;
count = 0;
while (menuItems[count]) {
printAt(fontI, x1, y1, menuItems[count]);
y1+=2;
count++;
}
// Returns number of menu items.
return count;
}
void drawPuzzle(void) {
byte bx = 0; // Board coordinates.
byte by = 0;
jint16 x = 0; // Screen (tile) coordinates.
jint16 y = 0;
CoordT t;
for (by=0; by<puzzle.height; by++) {
y = ((by << 1) + puzzle.offsetY) << 3;
for (bx=0; bx<puzzle.width; bx++) {
// Is this tile "dirty" on the display?
if (puzzleNow[bx][by] != puzzleBefore[bx][by]) {
puzzleBefore[bx][by] = puzzleNow[bx][by];
x = ((bx << 1) + puzzle.offsetX) << 3;
t = tileLookup[puzzleNow[bx][by]];
jlDrawBlit8x8(jlImgSurfaceGet(tilesI), t.x, t.y, x, y );
jlDrawBlit8x8(jlImgSurfaceGet(tilesI), t.x + 8, t.y, x + 8, y );
jlDrawBlit8x8(jlImgSurfaceGet(tilesI), t.x, t.y + 8, x, y + 8);
jlDrawBlit8x8(jlImgSurfaceGet(tilesI), t.x + 8, t.y + 8, x + 8, y + 8);
}
}
}
}
void forceFullRedraw(void) {
// Clear "displayed" board so it has to redraw.
memset(&puzzleBefore, TILE_NOTHING, sizeof(byte) * MAX_WIDTH * MAX_HEIGHT);
}
void levelComplete(void) {
char bit;
jint16 index;
// Mark it solved.
index = puzzleCurrent - 1;
bit = 7 - (index % 8);
index /= 8;
saveGameData.solved[index] = saveGameData.solved[index] | (1 << bit);
// Move to next puzzle.
puzzleCurrent++;
if (puzzleCurrent > puzzleCount) {
puzzleCurrent = 1;
}
saveGame();
// Recount solves.
countSolved();
// Is the game complete?
if (puzzleSolved == puzzleCount) {
// They did them all!
//***TODO***
return;
}
//***TODO***
}
void loadPuzzle(void) {
FILE *in = NULL;
byte x = 0;
byte y = 0;
// NOTE: We don't use fgetc() because it's borked in ORCA/C.
in = fopen("data/puzzles.dat", "rb");
if (!in) jlUtilDie("Unable to open puzzle database!");
crateCount = 0;
cratesOnTarget = 0;
// Skip to requested puzzle.
fseek(in, puzzleIndex[puzzleCurrent - 1], SEEK_SET);
// Load width of puzzle
fread(&puzzle.width, sizeof(byte), 1, in);
// Load height of puzzle
fread(&puzzle.height, sizeof(byte), 1, in);
// Load the puzzle itself
for (y=0; y<puzzle.height; y++) {
for (x=0; x<puzzle.width; x+=2) {
fread(&puzzle.puzzle[x][y], sizeof(byte), 1, in);
// Split single byte into two tiles.
puzzle.puzzle[x + 1][y] = puzzle.puzzle[x][y] & 0x0f;
puzzle.puzzle[x][y] = puzzle.puzzle[x][y] >> 4;
// Is this our start location? If so, replace with proper tile and remember where we are.
if (puzzle.puzzle[x][y] == TILE_PLAYER) {
puzzle.puzzle[x][y] = TILE_FLOOR;
avatarX = x;
avatarY = y;
}
if (puzzle.puzzle[x][y] == TILE_PLAYER_ON_GOAL) {
puzzle.puzzle[x][y] = TILE_GOAL;
avatarX = x;
avatarY = y;
}
if (puzzle.puzzle[x + 1][y] == TILE_PLAYER) {
puzzle.puzzle[x + 1][y] = TILE_FLOOR;
avatarX = x + 1;
avatarY = y;
}
if (puzzle.puzzle[x + 1][y] == TILE_PLAYER_ON_GOAL) {
puzzle.puzzle[x + 1][y] = TILE_GOAL;
avatarX = x + 1;
avatarY = y;
}
// Get crate tallys.
if ((puzzle.puzzle[x][y] == TILE_CRATE) || (puzzle.puzzle[x + 1][y] == TILE_CRATE)) {
crateCount++;
}
if ((puzzle.puzzle[x][y] == TILE_CRATE_ON_GOAL) || (puzzle.puzzle[x + 1][y] == TILE_CRATE_ON_GOAL)) {
crateCount++;
cratesOnTarget++;
}
}
}
fclose(in);
crateInitialCount = crateCount;
cratesInitiallyOnTarget = cratesOnTarget;
avatarXStart = avatarX;
avatarYStart = avatarY;
// Center puzzle on display.
puzzle.offsetX = (10 - puzzle.width / 2) * 2;
puzzle.offsetY = (6 - puzzle.height / 2) * 2;
resetPuzzle();
forceFullRedraw();
}
bool mainMenu(void) {
const char *options[] = { "Return to Game", "About", "How to Play", "Select Level", "Reset Level", "Exit", 0 };
const char *yesno[] = { "What? No!", "Yeah, I've had enough.", 0 };
const char *reset[] = { "No! I didn't mean it!", "Yes, let me try again.", 0 };
bool running = true;
char choice = 0;
while ((choice >= 0) && running && !jlUtilMustExit()) {
choice = menu("Main Menu", options, choice);
switch (choice) {
case 0: // Return to Game
choice = -1;
break;
case 1: // About
showImage(aboutI);
break;
case 2: // How to Play
showImage(helpI);
break;
case 3: // Select Level
selectLevel();
if (puzzleLast != puzzleCurrent) {
// New puzzle selected, exit menu.
choice = -1;
}
break;
case 4: // Reset Level
if (1 == menu("Reset Level?", reset, 0)) {
resetPuzzle();
forceFullRedraw();
choice = -1;
}
break;
case 5: // Exit
running = (0 == menu("Exit Game?", yesno, 0));
break;
}
}
return running;
}
char menu(const char *title, const char *menuItems[], char selected) {
byte count = 0;
byte inMenuYOffset = 7;
byte key;
byte height;
byte ox;
byte oy;
byte offsetX = 0;
byte offsetY = 0;
char result = -1;
bool inMenu = true;
jint16 rx = ((byte)175 % 40) << 3; // Right Arrows ASCII
jint16 ry = ((byte)175 / 40) << 3;
jint16 sx = ((byte)32 % 40) << 3; // Space ASCII
jint16 sy = ((byte)32 / 40) << 3;
jint16 xpos;
jint16 ypos;
jint16 last;
jint16 lastY;
jlImgT *screen = NULL;
jlImgCreate(screen);
ox = offsetX;
oy = offsetY;
count = drawMenu(title, menuItems, &height, &ox, &oy);
inMenuYOffset += oy;
xpos = (ox + 2) * 8;
ypos = (inMenuYOffset + (selected * 2)) * 8;
lastY = ypos;
last = (selected == 0 ? 1 : 0);
while (inMenu && !jlUtilMustExit()) {
if (readInput(&key)) {
switch (key) {
case 27:
inMenu = false;
break;
case 13:
result = selected;
inMenu = false;
break;
case 'I':
case 'i':
if (selected > 0) {
selected--;
ypos -= 16;
} else {
selected = count - 1;
ypos = (inMenuYOffset + (count - 1) * 2) * 8;
}
break;
case 'M':
case 'm':
if (selected < count - 1) {
selected++;
ypos += 16;
} else {
selected = 0;
ypos = inMenuYOffset * 8;
}
break;
}
}
if (selected != last) {
jlDrawBlit8x8(jlImgSurfaceGet(fontI), sx, sy, xpos, lastY);
jlDrawBlit8x8(jlImgSurfaceGet(fontI), rx, ry, xpos, ypos);
jlDisplayPresent();
lastY = ypos;
last = selected;
}
ticker();
}
jlImgDisplay(screen);
jlDisplayPresent();
jlImgFree(screen);
return result;
}
void moveCrate(byte sx, byte sy, byte dx, byte dy) {
if (puzzleNow[sx][sy] == TILE_CRATE) {
puzzleNow[sx][sy] = TILE_FLOOR;
} else {
puzzleNow[sx][sy] = TILE_GOAL;
cratesOnTarget--;
}
if (puzzleNow[dx][dy] == TILE_FLOOR) {
puzzleNow[dx][dy] = TILE_CRATE;
} else {
puzzleNow[dx][dy] = TILE_CRATE_ON_GOAL;
cratesOnTarget++;
}
// Did they win?
if (cratesOnTarget == crateCount) {
levelComplete();
}
}
void play(void) {
byte key = 0;
bool playing = true;
byte tile = 0;
// Show menu on entry
playing = mainMenu();
// Force tile palette
jlImgDisplay(tilesI);
jlDrawClear();
while (playing && !jlUtilMustExit()) {
if (readInput(&key)) {
avatarXLast = avatarX;
avatarYLast = avatarY;
switch (key) {
case 27:
playing = mainMenu();
if (playing) {
redraw();
}
break;
case 'I':
case 'i':
// Can we move up?
tile = puzzleNow[avatarX][avatarY - 1];
if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) {
avatarY--;
}
// Can we push up?
if ((tile == TILE_CRATE) || (tile == TILE_CRATE_ON_GOAL)) {
tile = puzzleNow[avatarX][avatarY - 2];
if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) {
avatarY--;
moveCrate(avatarX, avatarY, avatarX, avatarY - 1);
}
}
break;
case 'J':
case 'j':
// Can we move left?
tile = puzzleNow[avatarX - 1][avatarY];
if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) {
avatarX--;
}
// Can we push left?
if ((tile == TILE_CRATE) || (tile == TILE_CRATE_ON_GOAL)) {
tile = puzzleNow[avatarX - 2][avatarY];
if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) {
avatarX--;
moveCrate(avatarX, avatarY, avatarX - 1, avatarY);
}
}
break;
case 'K':
case 'k':
// Can we move right?
tile = puzzleNow[avatarX + 1][avatarY];
if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) {
avatarX++;
}
// Can we push right?
if ((tile == TILE_CRATE) || (tile == TILE_CRATE_ON_GOAL)) {
tile = puzzleNow[avatarX + 2][avatarY];
if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) {
avatarX++;
moveCrate(avatarX, avatarY, avatarX + 1, avatarY);
}
}
break;
case 'M':
case 'm':
// Can we move down?
tile = puzzleNow[avatarX][avatarY + 1];
if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) {
avatarY++;
}
// Can we push down?
if ((tile == TILE_CRATE) || (tile == TILE_CRATE_ON_GOAL)) {
tile = puzzleNow[avatarX][avatarY + 2];
if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) {
avatarY++;
moveCrate(avatarX, avatarY, avatarX, avatarY + 1);
}
}
break;
}
}
// Load new level?
if (puzzleLast != puzzleCurrent) {
puzzleLast = puzzleCurrent;
loadPuzzle();
avatarXLast = -1;
avatarYLast = -1;
jlDrawClear();
}
// Redraw?
if ((avatarX != avatarXLast) || (avatarY != avatarYLast)) {
puzzleBefore[avatarXLast][avatarYLast] = TILE_NOTHING;
redraw();
}
ticker();
}
}
// (Slow) Font Printing
__attribute__((__format__ (__printf__, 4, 0)))
void printAt(jlImgT *font, jint16 cx, jint16 cy, const char *what, ...) {
jint16 x;
jint16 y;
jint16 tx;
jint16 ty;
jint16 counter;
char msg[41]; // Very short messages (screen width). Be careful!
va_list va;
va_start(va, what);
vsprintf(msg, what, va);
va_end(va);
tx = cx << 3;
ty = cy << 3;
for (counter=0; counter<(int)strlen(msg); counter++) {
x = ((byte)msg[counter] % 40) << 3;
y = ((byte)msg[counter] / 40) << 3;
jlDrawBlit8x8(jlImgSurfaceGet(font), x, y, tx, ty);
tx += 8;
}
}
bool readInput(byte *key) {
//static bool debounceController = false;
*key = 0;
// Keyboard
if (jlKeyPressed()) {
while (jlKeyPressed()) {
*key = jlKeyRead();
}
return true;
}
/*
// Joystick
if (jlGameGetAxis(0) < -50) {
if (debounceController) return false;
*key = 'J';
debounceController = true;
return true;
}
if (jlGameGetAxis(0) > 50) {
if (debounceController) return false;
*key = 'K';
debounceController = true;
return true;
}
if (jlGameGetAxis(1) < -50) {
if (debounceController) return false;
*key = 'I';
debounceController = true;
return true;
}
if (jlGameGetAxis(1) > 50) {
if (debounceController) return false;
*key = 'M';
debounceController = true;
return true;
}
if (jlGameGetButton(0)) {
if (debounceController) return false;
*key = 13;
debounceController = true;
return true;
}
if (jlGameGetButton(1)) {
if (debounceController) return false;
*key = 27;
debounceController = true;
return true;
}
debounceController = false;
*/
return false;
}
void redraw(void) {
drawPuzzle();
drawAvatar();
jlDisplayPresent();
}
void resetPuzzle(void) {
// Copy loaded puzzle data into playable data.
memcpy(&puzzleNow, puzzle.puzzle, sizeof(byte) * MAX_WIDTH * MAX_HEIGHT);
// Reset crate count.
crateCount = crateInitialCount;
cratesOnTarget = cratesInitiallyOnTarget;
// Put avatar back.
avatarX = avatarXStart;
avatarY = avatarYStart;
avatarXLast = -1;
avatarYLast = -1;
}
void saveGame(void) {
FILE *out = NULL;
saveGameData.lastPuzzle = puzzleCurrent;
// Save game.
out = fopen("data/save.dat", "wb");
if (out) {
fwrite(&puzzleCount, sizeof(jint16), 1, out);
fwrite(&saveGameData.lastPuzzle, sizeof(jint16), 1, out);
fwrite(&saveGameData.solvedSize, sizeof(jint16), 1, out);
fwrite(saveGameData.solved, saveGameData.solvedSize, 1, out);
fclose(out);
}
}
void selectLevel(void) {
jint16 x;
jint16 y;
jint16 lastX;
jint16 lastY;
jint16 p;
jint16 c = 0;
jint16 index = -1;
jint16 marginX = 16;
jint16 marginY = 16;
jint16 spacingX;
jint16 spacingY;
jint16 cols = (jint16)(sqrt(puzzleCount) + 0.5f);
jint16 dangling = puzzleCount - (cols * (cols - 1));
char bit = 0;
byte data;
byte oldColor = jlDrawColorGet();
bool inMenu = true;
jlImgT *screen = NULL;
jlImgCreate(screen);
jlPaletteSet( 0, 0, 0, 0);
jlPaletteSet( 1, 0, 15, 0);
jlPaletteSet( 2, 15, 0, 0);
jlPaletteSet(15, 15, 15, 15);
jlDrawColorSet(0);
jlDrawClear();
spacingX = (320 - (marginX * 2)) / cols;
spacingY = (200 - (marginY * 2)) / cols;
// Y spacing needs corrected to be centered on the screen
marginY = (200 - (spacingY * cols)) / 2;
x = marginX;
y = marginY;
for (p=0; p<puzzleCount; p++) {
bit--;
if (bit < 0) {
bit = 7;
index++;
data = saveGameData.solved[index];
}
if (data & (1 << bit)) {
jlDrawColorSet(1);
} else {
jlDrawColorSet(2);
}
jlDrawPixelSet(x, y);
jlDrawPixelSet(x + 1, y);
jlDrawPixelSet(x, y + 1);
jlDrawPixelSet(x + 1, y + 1);
c++;
if (c >= cols) {
x = marginX;
y += spacingY;
c = 0;
} else {
x += spacingX;
}
}
printAt(fontI, 0, 24, "Green are Completed. Red are Unsolved.");
p = puzzleCurrent;
lastX = -1;
lastY = -1;
while (inMenu && !jlUtilMustExit()) {
if (readInput(&data)) {
avatarXLast = avatarX;
avatarYLast = avatarY;
switch (data) {
case 27:
inMenu = false;
break;
case 13:
// Actually loaded and redrawn in play loop.
puzzleCurrent = p;
inMenu = false;
break;
case 'I':
case 'i':
// Case when we'll land in the 'dangling' line.
if (p <= dangling) {
p = puzzleCount - (dangling - p);
} else {
// Moving from top to bottom but not into the 'dangling' line.
if (p <= cols) {
p = p - (cols + dangling) + 1;
} else {
// General case to move up a line.
p -= cols;
}
}
break;
case 'J':
case 'j':
if (p > 1) {
p--;
} else {
p = puzzleCount;
}
break;
case 'K':
case 'k':
if (p < puzzleCount) {
p++;
} else {
p = 1;
}
break;
case 'M':
case 'm':
// Are we moving down from the 'dangling' line?
if (p > (cols * (cols - 1))) {
p = dangling - (puzzleCount - p);
} else {
// Moving from bottom to top but not from the 'dangling' line.
if (p > puzzleCount - cols) {
p = p + cols + dangling;
} else {
// General case to move down a line.
p += cols;
}
}
break;
}
}
x = ((p - 1) % cols) * spacingX + marginX;
y = ((p - 1) / cols) * spacingY + marginY;
if ((x != lastX) || (y != lastY)) {
if (lastX > -1) {
jlDrawColorSet(0);
jlDrawBox(lastX - 1, lastY - 1, lastX + 2, lastY + 2);
}
jlDrawColorSet(15);
jlDrawBox(x - 1, y - 1, x + 2, y + 2);
lastX = x;
lastY = y;
printAt(fontI, 6, 0, "Select Your Next Level: %d ", p);
jlDisplayPresent();
}
}
jlDrawColorSet(oldColor);
jlImgDisplay(screen);
jlDisplayPresent();
jlImgFree(screen);
}
void showImage(jlImgT *image) {
jlImgT *screen = NULL;
byte key;
jlImgCreate(screen);
jlImgDisplay(image);
jlDisplayPresent();
while (!readInput(&key) && !jlUtilMustExit()) {
// Do nothing
}
jlImgDisplay(screen);
jlDisplayPresent();
jlImgFree(screen);
}
void showPalette(void) {
byte x;
for (x=0; x<16; x++) {
jlDrawColorSet(x);
jlDrawBoxFilled(x * 20, 179, x * 20 + 19, 199);
}
}
void ticker(void) {
char *spinner = "/-\\|";
static byte count = 0;
static juint16 last = 0;
printAt(fontI, 0, 24, "%d puzzles. Showing #%d. %d/%d %c ",
puzzleCount, puzzleCurrent, cratesOnTarget, crateCount, spinner[count]);
jlDisplayPresent();
if (jlUtilTimeSpan(last, jlUtilTimer()) > 3) {
count++;
if (count > 3) count = 0;
last = jlUtilTimer();
}
}
void title(void) {
char *images[] = { "kanga", "title", 0 };
byte count = 1; // CHANGE
jlDrawColorSet(0);
jlDisplayBorder(BORDER_BLACK);
jlDrawClear();
while (images[count] && !jlUtilMustExit()) {
// For splash screens, reuse tilesI to save memory.
if (!jlImgLoad(tilesI, images[count])) jlUtilDie("Unable to open %s!", images[count]);
jlImgDisplay(tilesI);
//showPalette();
jlDisplayPresent();
jlUtilSleep(20);
if (images[count+1] != 0) {
jlDrawClear();
jlDisplayPresent();
jlUtilSleep(3);
}
count++;
}
}
int main(void) {
FILE *in = NULL;
jlUtilStartup("Warehouse");
// Get something on the screen as quickly as possible.
title();
// Load the rest of our data.
if (!jlImgLoad(tilesI, "tiles")) jlUtilDie("Unable to load tiles!");
if (!jlImgLoad(fontI, "font")) jlUtilDie("Unable to load font!");
if (!jlImgLoad(aboutI, "about")) jlUtilDie("Unable to load about!");
if (!jlImgLoad(helpI, "help")) jlUtilDie("Unable to load help!");
// Load the index.
in = fopen("data/index.dat", "rb");
if (!in) jlUtilDie("Unable to open puzzle index!");
// How many puzzles?
fread(&puzzleCount, sizeof(jint16), 1, in);
puzzleIndex = (jint32 *)jlMalloc(sizeof(jint32) * puzzleCount);
fread(puzzleIndex, sizeof(jint32), puzzleCount, in);
fclose(in);
#ifdef JOEY_BIG_ENDIAN
//***TODO***
#endif
// Create bit array of possible games for save file.
saveGameData.solvedSize = sizeof(byte) * (size_t)((float)(puzzleCount + 0.5f) / 8.0f);
saveGameData.solved = (byte *)jlMalloc(saveGameData.solvedSize);
// Does this user have a save file?
in = fopen("data/save.dat", "rb");
if (in) {
// Load save file.
fread(&puzzleCount, sizeof(jint16), 1, in);
fread(&saveGameData.lastPuzzle, sizeof(jint16), 1, in);
fread(&saveGameData.solvedSize, sizeof(jint16), 1, in);
fread(saveGameData.solved, saveGameData.solvedSize, 1, in);
fclose(in);
#ifdef JOEY_BIG_ENDIAN
//***TODO***
#endif
countSolved();
} else {
// No save. Prime the pump.
saveGameData.lastPuzzle = 1;
memset(saveGameData.solved, 0, saveGameData.solvedSize);
puzzleSolved = 0;
}
puzzleCurrent = saveGameData.lastPuzzle;
puzzleLast = puzzleCurrent;
avatarXLast = -1;
avatarYLast = -1;
// Do the fun stuff.
loadPuzzle();
jlDrawClear();
redraw();
play();
saveGame();
// Clean up.
jlFree(saveGameData.solved);
jlFree(puzzleIndex);
jlImgFree(helpI);
jlImgFree(aboutI);
jlImgFree(tilesI);
jlImgFree(fontI);
jlUtilShutdown();
}