/* * Warehouse for JoeyLib - A Sokoban Clone * Copyright (C) 2020 Scott Duensing * * 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 #include #include #include #include "stddclmr.h" #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 #ifdef JOEY_IIGS #define ANIMATION_DELAY #else #define ANIMATION_DELAY jlUtilSleep(2); #endif #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 #define AVATAR_UP 0 #define AVATAR_RIGHT 1 #define AVATAR_DOWN 2 #define AVATAR_LEFT 3 #define AVATAR_PUSH_UP 4 #define AVATAR_PUSH_LOOP_UP 5 #define AVATAR_PUSH_RIGHT 6 #define AVATAR_PUSH_LOOP_RIGHT 7 #define AVATAR_PUSH_DOWN 8 #define AVATAR_PUSH_LOOP_DOWN 9 #define AVATAR_PUSH_LEFT 10 #define AVATAR_PUSH_LOOP_LEFT 11 #define AVATAR_IDLE 12 #define AVATAR_NO 13 #define AVATAR_COUNT 14 #define AVATAR_MAX_FRAMES 8 #define AVATAR_X_ON_SCREEN (((avatarX << 1) + puzzle.offsetX) << 3) - 8 #define AVATAR_Y_ON_SCREEN (((avatarY << 1) + puzzle.offsetY) << 3) - 8 typedef struct PuzzleS { jbyte offsetX; jbyte offsetY; jbyte width; jbyte height; jbyte puzzle[MAX_WIDTH][MAX_HEIGHT]; } PuzzleT; typedef struct SaveS { jint16 lastPuzzle; jint16 solvedSize; jbyte *solved; } SaveT; typedef struct CoordS { jint16 x; jint16 y; } CoordT; const jint16 blockLeftX = ((jbyte)221 % 40) << 3; // Left-hand block ASCII const jint16 blockLeftY = ((jbyte)221 / 40) << 3; const jint16 blockRightX = ((jbyte)222 % 40) << 3; // Right-hand block ASCII const jint16 blockRightY = ((jbyte)222 / 40) << 3; const jint16 blockTopX = ((jbyte)223 % 40) << 3; // Top-half block ASCII const jint16 blockTopY = ((jbyte)223 / 40) << 3; const jint16 blockBottomX = ((jbyte)220 % 40) << 3; // Bottom-half block ASCII const jint16 blockBottomY = ((jbyte)220 / 40) << 3; const jint16 rightArrowsX = ((jbyte)175 % 40) << 3; // Right Arrows ASCII const jint16 rightArrowsY = ((jbyte)175 / 40) << 3; const jint16 spaceX = ((jbyte)32 % 40) << 3; // Space ASCII const jint16 spaceY = ((jbyte)32 / 40) << 3; const char *menuOptions[] = { "Return to Game", "About", "How to Play", "Select Level", "Reset Level", "Exit", 0 }; const char *menuYesNo[] = { "What? No!", "Yeah, I've had enough.", 0 }; const char *menuReset[] = { "No! I didn't mean it!", "Yes, let me try again.", 0 }; static jlImgT *fontI = NULL; static jlImgT *tilesI = NULL; static jlImgT *aboutI = NULL; static jlImgT *helpI = NULL; static jlImgT *savedScreen1 = NULL; static jlImgT *savedScreen2 = NULL; static jlStnT *tilesS = NULL; static jint16 puzzleCount; static jint16 puzzleCurrent; static jint16 puzzleLast; static jint16 puzzleSolved; static jint32 *puzzleIndex = NULL; static PuzzleT puzzle; static SaveT saveGameData; static jbyte puzzleNow[MAX_WIDTH][MAX_HEIGHT]; static jbyte puzzleBefore[MAX_WIDTH][MAX_HEIGHT]; static jbyte avatarX; static jbyte avatarY; static jbyte avatarXLast; static jbyte avatarYLast; static jbyte avatarXStart; static jbyte avatarYStart; static jbyte avatarFacing = AVATAR_RIGHT; // 0=Up, 1=Right, 2=Down, 3=Left static jbyte crateCount; static jbyte crateInitialCount; static jbyte cratesOnTarget; static jbyte cratesInitiallyOnTarget; //static char puzzleChars[] = { "_# .$@+*" }; static CoordT avatar[AVATAR_COUNT][AVATAR_MAX_FRAMES] = { // These are sprite numbers and get converted later to tile coordinates. { { 1, 0 }, { 2, 0 }, { 3, 0 }, { 2, 0 }, { 1, 0 }, { 4, 0 }, { 5, 0 }, { 4, 0 } }, // AVATAR_UP { { 21, 0 }, { 22, 0 }, { 23, 0 }, { 24, 0 }, { 25, 0 }, { 26, 0 }, { 27, 0 }, { 0, 0 } }, // AVATAR_RIGHT { { 6, 0 }, { 7, 0 }, { 8, 0 }, { 7, 0 }, { 6, 0 }, { 8, 0 }, { 9, 0 }, { 8, 0 } }, // AVATAR_DOWN { { 11, 0 }, { 12, 0 }, { 13, 0 }, { 14, 0 }, { 15, 0 }, { 16, 0 }, { 17, 0 }, { 0, 0 } }, // AVATAR_LEFT { { 31, 0 }, { 32, 0 }, { 33, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_PUSH_UP { { 34, 0 }, { 35, 0 }, { 36, 0 }, { 37, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_PUSH_LOOP_UP { { 61, 0 }, { 62, 0 }, { 63, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_PUSH_RIGHT { { 64, 0 }, { 65, 0 }, { 66, 0 }, { 67, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_PUSH_LOOP_RIGHT { { 41, 0 }, { 42, 0 }, { 43, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_PUSH_DOWN { { 44, 0 }, { 45, 0 }, { 46, 0 }, { 47, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_PUSH_LOOP_DOWN { { 51, 0 }, { 52, 0 }, { 53, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_PUSH_LEFT { { 54, 0 }, { 55, 0 }, { 56, 0 }, { 57, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_PUSH_LOOP_LEFT { { 11, 0 }, { 19, 0 }, { 20, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } }, // AVATAR_IDLE { { 28, 0 }, { 29, 0 }, { 28, 0 }, { 29, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } } // AVATAR_NO }; static CoordT tileLookup[TILE_COUNT] = { { 8, 23 }, // Nothing { 2, 23 }, // Wall { 6, 23 }, // Floor { 4, 23 }, // Goal { 0, 23 }, // Crate { 17, 3 }, // Player { 17, 3 }, // Player on Goal { 0, 23 } // Crate on Goal }; void avatarCrateHide(jbyte x1, jbyte y1, jbool pushing); void avatarCrateShow(void); void avatarDraw(jint16 x1, jint16 y1, jint16 sequence, jint16 frame); void avatarWalkDown(jbool pushing); void avatarWalkLeft(jbool pushing); void avatarWalkRight(jbool pushing); void avatarWalkUp(jbool pushing); void crateMove(jbyte sx, jbyte sy, jbyte dx, jbyte dy); void fontPrint(jlImgT *font, jint16 cx, jint16 cy, const char *what, ...); void gamePlay(void); void gameSave(void); void imageShow(jlImgT *image); jbyte menuDraw(const char *titleShow, const char *menuItems[], jbyte *height, jbyte *offsetX, jbyte *offsetY); jint16 menuHandle(const char *titleShow, const char *menuItems[], jint16 selected); jbool menuMain(void); //void paletteShow(void); void puzzleComplete(void); void puzzleDraw(void); void puzzleForceFullRedraw(void); void puzzleLoad(void); void puzzleRedraw(void); void puzzleReset(void); void puzzleSelect(void); void solvedCount(void); void tickerUpdate(void); void tileDraw(jbyte tile, jint16 x1, jint16 y1); void titleShow(void); void avatarCrateHide(jbyte x1, jbyte y1, jbool pushing) { jbyte save = 0; // Erase the avatar from the playfield. puzzleBefore[avatarX][avatarY] = TILE_NOTHING; puzzleBefore[avatarX - 1][avatarY] = TILE_NOTHING; puzzleBefore[avatarX + 1][avatarY] = TILE_NOTHING; puzzleBefore[avatarX][avatarY - 1] = TILE_NOTHING; // Erase crate if we're pushing. if (pushing) { save = puzzleNow[x1][y1]; if (puzzleNow[x1][y1] == TILE_CRATE_ON_GOAL) { puzzleNow[x1][y1] = TILE_GOAL; } else { puzzleNow[x1][y1] = TILE_FLOOR; } } puzzleDraw(); jlImgCreate(savedScreen1); if (pushing) { // Put the crate data back in the puzzle. puzzleNow[x1][y1] = save; } } void avatarCrateShow(void) { avatarDraw(AVATAR_X_ON_SCREEN, AVATAR_Y_ON_SCREEN, avatarFacing, 0); } void avatarDraw(jint16 x1, jint16 y1, jint16 sequence, jint16 frame) { jint16 x2 = x1 + 8; jint16 x3 = x2 + 8; jint16 x4 = x3 + 8; jint16 y2 = y1 + 8; jint16 y3 = y2 + 8; CoordT t = avatar[sequence][frame]; jint16 tx1 = t.x; jint16 tx2 = tx1 + 8; jint16 tx3 = tx2 + 8; jint16 tx4 = tx3 + 8; jint16 ty1 = t.y; jint16 ty2 = ty1 + 8; jint16 ty3 = ty2 + 8; jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx1, ty1, x1, y1); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx2, ty1, x2, y1); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx3, ty1, x3, y1); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx4, ty1, x4, y1); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx1, ty2, x1, y2); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx2, ty2, x2, y2); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx3, ty2, x3, y2); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx4, ty2, x4, y2); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx1, ty3, x1, y3); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx2, ty3, x2, y3); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx3, ty3, x3, y3); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx4, ty3, x4, y3); } void avatarErase(jint16 x1, jint16 y1) { jint16 x2 = x1 + 8; jint16 x3 = x2 + 8; jint16 x4 = x3 + 8; jint16 y2 = y1 + 8; jint16 y3 = y2 + 8; jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x1, y1, x1, y1); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x2, y1, x2, y1); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x3, y1, x3, y1); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x4, y1, x4, y1); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x1, y2, x1, y2); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x2, y2, x2, y2); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x3, y2, x3, y2); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x4, y2, x4, y2); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x1, y3, x1, y3); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x2, y3, x2, y3); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x3, y3, x3, y3); jlDrawBlit8x8(jlImgSurfaceGet(savedScreen1), x4, y3, x4, y3); } void avatarWalkDown(jbool pushing) { jint16 i = 0; jint16 x = AVATAR_X_ON_SCREEN; jint16 y = AVATAR_Y_ON_SCREEN; jint16 tx = x + 8; jint16 frame = 0; jint16 seq = pushing ? AVATAR_PUSH_DOWN : AVATAR_DOWN; jint16 seq2 = pushing ? AVATAR_PUSH_LOOP_DOWN : AVATAR_DOWN; avatarCrateHide(avatarX, avatarY + 1, pushing); avatarFacing = AVATAR_DOWN; for (i=y; i= AVATAR_MAX_FRAMES) { frame = 0; seq = seq2; } else { if (avatar[seq][frame].x == -1) { frame = 0; seq = seq2; } } } avatarY++; avatarCrateShow(); if (pushing) { // Move crate. crateMove(avatarX, avatarY, avatarX, avatarY + 1); } } void avatarWalkLeft(jbool pushing) { jint16 i = 0; jint16 x = AVATAR_X_ON_SCREEN; jint16 y = AVATAR_Y_ON_SCREEN; jint16 ty = y + 8; jint16 frame = 0; jint16 seq = pushing ? AVATAR_PUSH_LEFT : AVATAR_LEFT; jint16 seq2 = pushing ? AVATAR_PUSH_LOOP_LEFT : AVATAR_LEFT; avatarCrateHide(avatarX - 1, avatarY, pushing); avatarFacing = AVATAR_LEFT; for (i=x; i>x-16; i-=2) { avatarDraw(i, y, seq, frame); if (pushing) { tileDraw(TILE_CRATE, i - 8, ty); } tickerUpdate(); ANIMATION_DELAY avatarErase(i, y); frame++; if (frame >= AVATAR_MAX_FRAMES) { frame = 0; seq = seq2; } else { if (avatar[seq][frame].x == -1) { frame = 0; seq = seq2; } } } avatarX--; avatarCrateShow(); if (pushing) { // Move crate. crateMove(avatarX, avatarY, avatarX - 1, avatarY); } } void avatarWalkRight(jbool pushing) { jint16 i = 0; jint16 x = AVATAR_X_ON_SCREEN; jint16 y = AVATAR_Y_ON_SCREEN; jint16 ty = y + 8; jint16 frame = 0; jint16 seq = pushing ? AVATAR_PUSH_RIGHT : AVATAR_RIGHT; jint16 seq2 = pushing ? AVATAR_PUSH_LOOP_RIGHT : AVATAR_RIGHT; avatarCrateHide(avatarX + 1, avatarY, pushing); avatarFacing = AVATAR_RIGHT; for (i=x; i= AVATAR_MAX_FRAMES) { frame = 0; seq = seq2; } else { if (avatar[seq][frame].x == -1) { frame = 0; seq = seq2; } } } avatarX++; avatarCrateShow(); if (pushing) { // Move crate. crateMove(avatarX, avatarY, avatarX + 1, avatarY); } } void avatarWalkUp(jbool pushing) { jint16 i = 0; jint16 x = AVATAR_X_ON_SCREEN; jint16 y = AVATAR_Y_ON_SCREEN; jint16 tx = x + 8; jint16 frame = 0; jint16 seq = pushing ? AVATAR_PUSH_UP : AVATAR_UP; jint16 seq2 = pushing ? AVATAR_PUSH_LOOP_UP : AVATAR_UP; avatarCrateHide(avatarX, avatarY - 1, pushing); avatarFacing = AVATAR_UP; for (i=y; i>y-16; i-=2) { if (pushing) { tileDraw(TILE_CRATE, tx, i - 8); } avatarDraw(x, i, seq, frame); tickerUpdate(); ANIMATION_DELAY avatarErase(x, i); frame++; if (frame >= AVATAR_MAX_FRAMES) { frame = 0; seq = seq2; } else { if (avatar[seq][frame].x == -1) { frame = 0; seq = seq2; } } } avatarY--; avatarCrateShow(); if (pushing) { // Move crate. crateMove(avatarX, avatarY, avatarX, avatarY - 1); } } void crateMove(jbyte sx, jbyte sy, jbyte dx, jbyte 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) { puzzleComplete(); } } // (Slow) Font Printing __attribute__((__format__ (__printf__, 4, 0))) void fontPrint(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 = ((jbyte)msg[counter] % 40) << 3; y = ((jbyte)msg[counter] / 40) << 3; jlDrawBlit8x8(jlImgSurfaceGet(font), x, y, tx, ty); tx += 8; } } void gamePlay(void) { jbyte key = 0; jbool playing = jtrue; jbyte tile = 0; while (playing && !jlUtilMustExit()) { if (jlUtilInputRead(&key)) { avatarXLast = avatarX; avatarYLast = avatarY; switch (key) { case JOEY_INPUT_SECONDARY: playing = menuMain(); break; case JOEY_INPUT_PRIMARY: //paletteShow(); if (1 == menuHandle("Reset Level?", menuReset, 0)) { puzzleReset(); puzzleForceFullRedraw(); } break; case JOEY_INPUT_UP: // Can we move up? tile = puzzleNow[avatarX][avatarY - 1]; if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) { avatarWalkUp(jfalse); } // 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)) { avatarWalkUp(jtrue); } } break; case JOEY_INPUT_LEFT: // Can we move left? tile = puzzleNow[avatarX - 1][avatarY]; if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) { avatarWalkLeft(jfalse); } // 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)) { avatarWalkLeft(jtrue); } } break; case JOEY_INPUT_RIGHT: // Can we move right? tile = puzzleNow[avatarX + 1][avatarY]; if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) { avatarWalkRight(jfalse); } // 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)) { avatarWalkRight(jtrue); } } break; case JOEY_INPUT_DOWN: // Can we move down? tile = puzzleNow[avatarX][avatarY + 1]; if ((tile == TILE_FLOOR) || (tile == TILE_GOAL)) { avatarWalkDown(jfalse); } // 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)) { avatarWalkDown(jtrue); } } break; } } // Load new level? if (puzzleLast != puzzleCurrent) { puzzleLast = puzzleCurrent; puzzleLoad(); avatarXLast = -1; avatarYLast = -1; jlDrawClear(); } // Redraw? if ((avatarX != avatarXLast) || (avatarY != avatarYLast)) { puzzleBefore[avatarXLast][avatarYLast] = TILE_NOTHING; puzzleBefore[avatarXLast - 1][avatarYLast] = TILE_NOTHING; puzzleBefore[avatarXLast + 1][avatarYLast] = TILE_NOTHING; puzzleBefore[avatarXLast][avatarYLast - 1] = TILE_NOTHING; puzzleRedraw(); } tickerUpdate(); } } void gameSave(void) { FILE *out = NULL; saveGameData.lastPuzzle = puzzleCurrent; // Save game. out = fopen(jlUtilMakePathname("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 imageShow(jlImgT *image) { jbyte key; jlImgCreate(savedScreen2); jlImgDisplay(image); jlDisplayPresent(); while (!jlUtilInputRead(&key) && !jlUtilMustExit()) { // Do nothing } jlImgDisplay(savedScreen2); jlDisplayPresent(); } jbyte menuDraw(const char *title, const char *menuItems[], jbyte *height, jbyte *offsetX, jbyte *offsetY) { jint16 count = 0; jint16 x1; jint16 y1; jint16 x2; jint16 y2; jint16 y3; jbyte width = 0; // 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), blockRightX, blockRightY, x1, y1); jlDrawBlit8x8(jlImgSurfaceGet(fontI), blockLeftX, blockLeftY, 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), blockTopX, blockTopY, x1, y1); jlDrawBlit8x8(jlImgSurfaceGet(fontI), blockBottomX, blockBottomY, x1, y2); jlDrawBlit8x8(jlImgSurfaceGet(fontI), blockBottomX, blockBottomY, x1, y3); } x1 = (width / 2 - strlen(title) / 2) + *offsetX; fontPrint(fontI, x1, *offsetY + 2, title); // Draw menu. x1 = *offsetX + 4; y1 = *offsetY + 7; count = 0; while (menuItems[count]) { fontPrint(fontI, x1, y1, menuItems[count]); y1+=2; count++; } // Returns number of menu items. return count; } jint16 menuHandle(const char *title, const char *menuItems[], jint16 selected) { jbyte count = 0; jbyte inMenuYOffset = 7; jbyte offsetX = 0; jbyte offsetY = 0; jbyte key; jbyte height; jbyte ox; jbyte oy; jbool inMenu = jtrue; jint16 result = -1; jint16 xpos; jint16 ypos; jint16 last; jint16 lastY; jlImgCreate(savedScreen1); ox = offsetX; oy = offsetY; count = menuDraw(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 (jlUtilInputRead(&key)) { switch (key) { case JOEY_INPUT_SECONDARY: inMenu = jfalse; break; case JOEY_INPUT_PRIMARY: result = selected; inMenu = jfalse; break; case JOEY_INPUT_UP: if (selected > 0) { selected--; ypos -= 16; } else { selected = count - 1; ypos = (inMenuYOffset + (count - 1) * 2) * 8; } break; case JOEY_INPUT_DOWN: if (selected < count - 1) { selected++; ypos += 16; } else { selected = 0; ypos = inMenuYOffset * 8; } break; } } if (selected != last) { jlDrawBlit8x8(jlImgSurfaceGet(fontI), spaceX, spaceY, xpos, lastY); jlDrawBlit8x8(jlImgSurfaceGet(fontI), rightArrowsX, rightArrowsY, xpos, ypos); lastY = ypos; last = selected; } tickerUpdate(); } jlImgDisplay(savedScreen1); jlDisplayPresent(); return result; } jbool menuMain(void) { jbool running = jtrue; jint16 choice = 0; while ((choice >= 0) && running && !jlUtilMustExit()) { choice = menuHandle("Main Menu", menuOptions, choice); switch (choice) { case 0: // Return to Game choice = -1; break; case 1: // About imageShow(aboutI); break; case 2: // How to Play imageShow(helpI); break; case 3: // Select Level puzzleSelect(); if (puzzleLast != puzzleCurrent) { // New puzzle selected, exit menu. choice = -1; } break; case 4: // Reset Level if (1 == menuHandle("Reset Level?", menuReset, 0)) { puzzleReset(); puzzleForceFullRedraw(); choice = -1; } break; case 5: // Exit running = (jbool)(0 == menuHandle("Exit Game?", menuYesNo, 0)); break; } } return running; } /* void paletteShow(void) { jbyte x; for (x=0; x<16; x++) { jlDrawColorSet(x); jlDrawBoxFilled(x * 20, 179, x * 20 + 19, 199); } } */ void puzzleComplete(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; } gameSave(); // Recount solves. solvedCount(); // Is the game complete? if (puzzleSolved == puzzleCount) { // They did them all! //***TODO*** Game Complete! return; } //***TODO*** Level Complete. } void puzzleDraw(void) { jbyte bx = 0; // Board coordinates. jbyte by = 0; jint16 x = 0; // Screen (tile) coordinates. jint16 y = 0; for (by=0; by> 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) { crateCount++; } if (puzzle.puzzle[x + 1][y] == TILE_CRATE) { crateCount++; } if (puzzle.puzzle[x][y] == TILE_CRATE_ON_GOAL) { crateCount++; cratesOnTarget++; } if (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; puzzleReset(); puzzleForceFullRedraw(); } void puzzleRedraw(void) { puzzleDraw(); avatarDraw(AVATAR_X_ON_SCREEN, AVATAR_Y_ON_SCREEN, avatarFacing, 0); } void puzzleReset(void) { // Copy loaded puzzle data into playable data. memcpy(&puzzleNow, puzzle.puzzle, sizeof(jbyte) * MAX_WIDTH * MAX_HEIGHT); // Reset crate count. crateCount = crateInitialCount; cratesOnTarget = cratesInitiallyOnTarget; // Put avatar back. avatarX = avatarXStart; avatarY = avatarYStart; avatarXLast = -1; avatarYLast = -1; } void puzzleSelect(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; jbyte data; jbyte oldColor = jlDrawColorGet(); jbool inMenu = jtrue; jlImgCreate(savedScreen2); 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= cols) { x = marginX; y += spacingY; c = 0; } else { x += spacingX; } } fontPrint(fontI, 0, 24, "Green are Completed. Red are Unsolved."); p = puzzleCurrent; lastX = -1; lastY = -1; while (inMenu && !jlUtilMustExit()) { if (jlUtilInputRead(&data)) { avatarXLast = avatarX; avatarYLast = avatarY; switch (data) { case JOEY_INPUT_SECONDARY: inMenu = jfalse; break; case JOEY_INPUT_PRIMARY: // Actually loaded and redrawn in play loop. puzzleCurrent = p; inMenu = jfalse; break; case JOEY_INPUT_UP: // 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 JOEY_INPUT_LEFT: if (p > 1) { p--; } else { p = puzzleCount; } break; case JOEY_INPUT_RIGHT: if (p < puzzleCount) { p++; } else { p = 1; } break; case JOEY_INPUT_DOWN: // 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; fontPrint(fontI, 6, 0, "Select Your Next Level: %d ", p); jlDisplayPresent(); } } jlDrawColorSet(oldColor); jlImgDisplay(savedScreen2); jlDisplayPresent(); } void solvedCount(void) { jbyte data; char bit = 0; jint16 index = -1; jint16 p; puzzleSolved = 0; // Count number of solved puzzles. for (p=0; p 3) { count++; if (count > 3) count = 0; last = jlUtilTimer(); } } void tileDraw(jbyte tile, jint16 x1, jint16 y1) { jint16 x2 = x1 + 8; jint16 y2 = y1 + 8; CoordT t = tileLookup[tile]; jint16 tx2 = t.x + 8; jint16 ty2 = t.y + 8; // Draw 16x16 tile. jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, t.x, t.y, x1, y1); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx2, t.y, x2, y1); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, t.x, ty2, x1, y2); jlDrawBlit8x8a(jlImgSurfaceGet(tilesI), tilesS, tx2, ty2, x2, y2); } void titleShow(void) { char *images[] = { "kanga", "title", 0 }; jbyte count = 1; // CHANGE jlDrawColorSet(0); jlDisplayBorder(BORDER_BLACK); jlDrawClear(); while (images[count] && !jlUtilMustExit()) { // For splash screens, reuse savedScreen1 to save memory. if (!jlImgLoad(savedScreen1, images[count])) jlUtilDie("Unable to open %s!", images[count]); jlImgDisplay(savedScreen1); //showPalette(); jlDisplayPresent(); jlUtilSleep(120); if (images[count+1] != 0) { jlDrawClear(); jlDisplayPresent(); jlUtilSleep(20); } count++; } } void joeyMain(void) { jint16 i = 0; jint16 j = 0; jint16 x = 0; jint16 y = 0; FILE *in = NULL; jlUtilTitleSet("Warehouse"); // Get something on the screen as quickly as possible. titleShow(); // Load the rest of our data. if (!jlImgLoad(tilesI, "biff")) jlUtilDie("Unable to load tiles!"); if (!jlStnLoad(tilesS, "biff")) jlUtilDie("Unable to load tiles stencil!"); 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(jlUtilMakePathname("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(jbyte) * (size_t)((float)(puzzleCount + 0.5f) / 8.0f); saveGameData.solved = (jbyte *)jlMalloc(saveGameData.solvedSize); // Does this user have a save file? in = fopen(jlUtilMakePathname("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 solvedCount(); } else { // No save. Prime the pump. saveGameData.lastPuzzle = 1; memset(saveGameData.solved, 0, saveGameData.solvedSize); puzzleSolved = 0; } // Convert tile coordinates to pixels. for (i=0; i 0) { x = ((avatar[i][j].x - 1) % 10) * 32; y = ((avatar[i][j].x - 1) / 10) * 24; avatar[i][j].x = x; avatar[i][j].y = y; } else { avatar[i][j].x = -1; avatar[i][j].y = -1; } } } // Force tile palette jlImgDisplay(tilesI); jlDrawClear(); // Do the fun stuff. puzzleCurrent = saveGameData.lastPuzzle; puzzleLoad(); puzzleLast = puzzleCurrent; avatarXLast = avatarX; avatarYLast = avatarY; jlDrawClear(); puzzleRedraw(); gamePlay(); gameSave(); // Clean up. jlFree(saveGameData.solved); jlFree(puzzleIndex); jlImgFree(savedScreen2); jlImgFree(savedScreen1); jlImgFree(helpI); jlImgFree(aboutI); jlStnFree(tilesS); jlImgFree(tilesI); jlImgFree(fontI); }