1366 lines
34 KiB
C
1366 lines
34 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"
|
|
|
|
|
|
#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<y+16; i+=2) {
|
|
|
|
avatarDraw(x, i, seq, frame);
|
|
if (pushing) {
|
|
tileDraw(TILE_CRATE, tx, i + 24);
|
|
}
|
|
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 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<x+16; i+=2) {
|
|
|
|
avatarDraw(i, y, seq, frame);
|
|
if (pushing) {
|
|
tileDraw(TILE_CRATE, i + 24, 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 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<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;
|
|
tileDraw(puzzleNow[bx][by], x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void puzzleForceFullRedraw(void) {
|
|
// Clear "displayed" board so it has to redraw.
|
|
memset(&puzzleBefore, TILE_NOTHING, sizeof(jbyte) * MAX_WIDTH * MAX_HEIGHT);
|
|
}
|
|
|
|
|
|
void puzzleLoad(void) {
|
|
FILE *in = NULL;
|
|
jbyte x = 0;
|
|
jbyte y = 0;
|
|
|
|
// NOTE: We don't use fgetc() because it's borked in ORCA/C.
|
|
|
|
in = fopen(jlUtilMakePathname("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(jbyte), 1, in);
|
|
// Load height of puzzle
|
|
fread(&puzzle.height, sizeof(jbyte), 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(jbyte), 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) {
|
|
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<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;
|
|
}
|
|
}
|
|
|
|
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<puzzleCount; p++) {
|
|
bit--;
|
|
if (bit < 0) {
|
|
bit = 7;
|
|
index++;
|
|
data = saveGameData.solved[index];
|
|
}
|
|
if (data & (1 << bit)) {
|
|
puzzleSolved++;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void tickerUpdate(void) {
|
|
char *spinner = "/-\\|";
|
|
static jbyte count = 0;
|
|
static juint16 last = 0;
|
|
|
|
fontPrint(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 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<TILE_COUNT; i++) {
|
|
tileLookup[i].x *= 8;
|
|
tileLookup[i].y *= 8;
|
|
}
|
|
|
|
// Convert avatar sprite numbers to tile coordinates.
|
|
for (i=0; i<AVATAR_COUNT; i++) {
|
|
for (j=0; j<AVATAR_MAX_FRAMES; j++) {
|
|
if (avatar[i][j].x > 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);
|
|
}
|
|
|
|
|