diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f69576c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.dat filter=lfs diff=lfs merge=lfs -text +*.img filter=lfs diff=lfs merge=lfs -text +*.mod filter=lfs diff=lfs merge=lfs -text diff --git a/buildTest.sh b/buildTest.sh new file mode 100644 index 0000000..e62279e --- /dev/null +++ b/buildTest.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +source automated.install +source towel/towel.sh + +tSudoSetPassword "${AUTOMATED_SUDO}" +tArgsHandler $@ + +[[ -d /home/test/build ]] && tSudo rm -rf /home/test/build +tSudo mkdir -p /home/test/build +tSudo cp sampleProject/* /home/test/build + +tSudo chown -R test:test /home/test/build +tSudo chmod ugo+rwx /home/test/build +tSudo chmod ugo+rw /home/test/build/* + +./joeybuild.sh build /home/test/build diff --git a/joeybuild.sh b/joeybuild.sh index b38c054..9e24229 100755 --- a/joeybuild.sh +++ b/joeybuild.sh @@ -27,6 +27,7 @@ # +G_HTTP_PORT=6502 # Port to use for HTTP server. Must be > 1024. G_EHOME="$(getent passwd $(logname) | cut -d: -f6)" # Home for this user. G_SRC="${G_EHOME}/joeylib/joeylib/src" # Location of JoeyLib source. G_TEMP="${G_EHOME}/temp" # Directory to store temporary data. @@ -44,7 +45,7 @@ function addBuildUser() { local SALT=$(LC_ALL=C tr -cd "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" < /dev/urandom | head -c 8) local CRYPT=$(perl -e 'print crypt($ARGV[0], $ARGV[1])' ${PASS} ${SALT}) - tSudo useradd -m -G sftponly -s /sbin/false -p "${CRYPT}" "${USER}" + tSudo useradd -m -G sftponly -s /usr/bin/false -p "${CRYPT}" "${USER}" tSudo chown root:root /home/${USER} tSudo chmod 755 /home/${USER} tSudo mkdir -p /home/${USER}/build @@ -100,12 +101,15 @@ function doBuild() { local AFILES=() local CFILES=() local OFILES=() - local DATA=() + local DFILES=() local TEMP= local TARGET= local RESULT= local LOG= local PASS= + local PROJECT_TYPE= + + set -x pushd "${SOURCE}" @@ -119,11 +123,20 @@ function doBuild() { mkdir -p results G_BUILD_RESULTS=${SOURCE}/results - # Read project information. G_BUILD_PLATFORMS="[" G_BUILD_PROJECT= + PROJECT_TYPE= + + # Read project information. while IFS= read -r LINE; do - # If we don't have a project name, grab it from the first line of the project file and set things up. + # If we don't have a project type, grab it from the first line of the project file and set things up. + if [[ -z ${PROJECT_TYPE} ]]; then + PROJECT_TYPE=${LINE} + # Skip to next loop pass. + continue + fi + + # If we don't have a project name, grab it from the second line of the project file and set things up. if [[ -z ${G_BUILD_PROJECT} ]]; then G_BUILD_PROJECT=${LINE} # Generate a list of non-source files. @@ -132,46 +145,63 @@ function doBuild() { if [[ "${FILE}" != "build.start" && "${FILE}" != "build.temp" && "${FILE}" != "build.tar.bz2" ]]; then EXTENSION="${FILE##*.}" if [[ "${EXTENSION}" != "c" && "${EXTENSION}" != "h" && "${EXTENSION}" != "asm" && "${EXTENSION}" != "macro" && "${EXTENSION}" != "inc" ]]; then - DATA+=(${FILE}) + DFILES+=(${FILE}) fi fi fi done # Generate a list of C files to compile. - CFILES=($(ls -1 *.c)) + TEMP=$(ls -1 *.c 2>/dev/null | wc -l) + if [[ ${TEMP} -ne 0 ]]; then + CFILES=($(ls -1 *.c)) + fi # Generate a list of ASM files to assemble. - AFILES=($(ls -1 *.asm)) - else - # This is for the 2nd and later lines of the project that list which targets to build. - TARGET= - for TEMP in ${LINE}; do - if [[ -z ${TARGET} ]]; then - TARGET=${TEMP} - else - for PASS in "debug" "release"; do - tBoldBox tPURPLE "Building \"${G_BUILD_PROJECT}\" ${TARGET} ${TEMP} (${PASS})..." - G_DIST=dist/${TARGET}-${TEMP}/${PASS} - # We at least tried to build this target. - G_BUILD_PLATFORMS="${G_BUILD_PLATFORMS}${TARGET}.${TEMP}.${PASS} " - # Make sure we have the official JoeyLib header. - cp -f "${G_HOME}/dist/joey.h" . - # Log file. - LOG="${G_BUILD_RESULTS}/build.${TARGET}.${TEMP}.${PASS}" - # Compile C files and generate object list. - call RESULT ${TARGET} ${TEMP} compile CFILES ${PASS} ${LOG} - OFILES=(${RESULT}) - # Assemble ASM files and generate object list. - call RESULT ${TARGET} ${TEMP} assemble AFILES ${PASS} ${LOG} - OFILES+=(${RESULT}) - # Link with JoeyLib. - call RESULT ${TARGET} ${TEMP} link OFILES ${PASS} ${LOG} - # Process game data files. - call RESULT ${TARGET} ${TEMP} package DATA ${PASS} ${LOG} - done - fi - done + TEMP=$(ls -1 *.asm 2>/dev/null | wc -l) + if [[ ${TEMP} -ne 0 ]]; then + AFILES=($(ls -1 *.asm)) + fi + # Skip to next loop pass. + continue fi - done + + # This is for the 3rd and later lines of the project that list which targets to build. + TARGET= + for TEMP in ${LINE}; do + if [[ -z ${TARGET} ]]; then + TARGET=${TEMP} + else + for PASS in "debug" "release"; do + tBoldBox tPURPLE "Building \"${G_BUILD_PROJECT}\" ${TARGET} ${TEMP} (${PASS})..." + G_DIST=dist/${TARGET}-${TEMP}/${PASS} + # We at least tried to build this target. + G_BUILD_PLATFORMS="${G_BUILD_PLATFORMS}${TARGET}.${TEMP}.${PASS} " + # Make sure we have the official JoeyLib header. + cp -f "${G_EHOME}/dist/joey.h" . + # Log file. + LOG="${G_BUILD_RESULTS}/build.${TARGET}.${TEMP}.${PASS}" + # Compile C files and generate object list. + if [[ "${#CFILES[@]}" != "0" ]]; then + call RESULT ${TARGET} compile ${TEMP} ${PASS} ${LOG} "${CFILES[@]}" + OFILES=(${RESULT}) + fi + # Assemble ASM files and generate object list. + if [[ "${#AFILES[@]}" != "0" ]]; then + call RESULT ${TARGET} assemble ${TEMP} ${PASS} ${LOG} "${AFILES[@]}" + OFILES+=(${RESULT}) + fi + # Link with JoeyLib. + call RESULT ${TARGET} link ${TEMP} ${PASS} ${LOG} "${OFILES[@]}" + # Process game data files. + if [[ "${#DFILES[@]}" != "0" ]]; then + call RESULT ${TARGET} package ${TEMP} ${PASS} ${LOG} "${DFILES[@]}" + fi + done + fi + done + + done < build.start + + popd tTrim TEMP "${G_BUILD_PLATFORMS}" G_BUILD_PLATFORMS="${TEMP}]" @@ -185,6 +215,9 @@ function doInstall() { updateSystem configureSFTP + # Add 'false' as a valid shell. + echo "/usr/bin/false" | tSudo tee -a /etc/shells + withTargets install "Installing SDK" rebuildJoeyLib @@ -315,20 +348,20 @@ function startBuildServer() { # Build supported project types and target details for JoeyDev. echo "1.0" > http/joeydev.info echo "------------------------------------------------------------------------------" >> http/joeydev.info - echo "=build.application \"JoeyLib Application\"" >> http/joeydev.info - echo "=build.joeylib \"JoeyLib Itself\"" >> http/joeydev.info + echo "project application \"JoeyLib Application\"" >> http/joeydev.info +# echo "project joeylib \"JoeyLib Itself\"" >> http/joeydev.info for TARGET in ${G_EHOME}/targets/*.target; do NAME=$(basename -s .target ${TARGET}) call RESULT ${NAME} enabled if [[ ${RESULT} -eq 1 ]]; then call ARCHS ${NAME} architectures call DESCRIPTION ${NAME} friendlyName - echo "+${NAME} \"${DESCRIPTION}\" ${ARCHS}" >> http/joeydev.info + echo "target ${NAME} \"${DESCRIPTION}\" ${ARCHS}" >> http/joeydev.info fi done # Start the PHP web server if it's not already running. - php -S 0.0.0.0:6502 -t http >> joeybuild.log 2>&1 & + php -S 0.0.0.0:${G_HTTP_PORT} -t http >> joeybuild.log 2>&1 & # Start the actual build server. cd /home diff --git a/sampleProject/about.img b/sampleProject/about.img new file mode 100644 index 0000000..d991519 --- /dev/null +++ b/sampleProject/about.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fbb7a96d09e2bce059f4afa158692f106cf7af0baec6320d903cb3aeba98ca9f +size 32036 diff --git a/sampleProject/biff.img b/sampleProject/biff.img new file mode 100644 index 0000000..bf4f65a --- /dev/null +++ b/sampleProject/biff.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0d50f5c8d18d7d340fcfea7120d500b870f4e4f1da17adb5079cf00ded3473f3 +size 32036 diff --git a/sampleProject/build.start b/sampleProject/build.start new file mode 100644 index 0000000..c4f4878 --- /dev/null +++ b/sampleProject/build.start @@ -0,0 +1,3 @@ +application +Warehouse +IIgs 65816 diff --git a/sampleProject/font.img b/sampleProject/font.img new file mode 100644 index 0000000..73dfabf --- /dev/null +++ b/sampleProject/font.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5f2312250edddaca7693c8445d47bdcb667573e5ea13a79e8b724ae37d91587 +size 32036 diff --git a/sampleProject/help.img b/sampleProject/help.img new file mode 100644 index 0000000..4ac07a2 --- /dev/null +++ b/sampleProject/help.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:09b34ec4df3fff40cc31b7cbf45ece04f357f5b8ef9f3512550667dc5cf63de9 +size 32036 diff --git a/sampleProject/index.dat b/sampleProject/index.dat new file mode 100644 index 0000000..ad7552a --- /dev/null +++ b/sampleProject/index.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:167ffd7ffbf8dc227558eb680db2becf74c59a0a4f964cd7a47953e3434c7ee2 +size 4002 diff --git a/sampleProject/kanga.img b/sampleProject/kanga.img new file mode 100644 index 0000000..d1b519d --- /dev/null +++ b/sampleProject/kanga.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dbeb539596ffd5644fecef2bdaff61cee229f46c5116b8964b88338c40d482fc +size 32036 diff --git a/sampleProject/main.c b/sampleProject/main.c new file mode 100644 index 0000000..3b87384 --- /dev/null +++ b/sampleProject/main.c @@ -0,0 +1,1366 @@ +/* + * 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); +} + + diff --git a/sampleProject/puzzles.dat b/sampleProject/puzzles.dat new file mode 100644 index 0000000..acc55da --- /dev/null +++ b/sampleProject/puzzles.dat @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:87324d9166ba50c21d426ca1310eb582ca7f43f9bbb5bba0061d81f52f90b6eb +size 54071 diff --git a/sampleProject/stddclmr.h b/sampleProject/stddclmr.h new file mode 100644 index 0000000..8e6b3d9 --- /dev/null +++ b/sampleProject/stddclmr.h @@ -0,0 +1,95 @@ +#ifndef STDDCLMR_H +#define STDDCLMR_H + +/* +Action figures sold separately. Add toner. All models over 18 years of age. +All rights reserved. Allow four to six weeks for delivery. An equal +opportunity employer. Any resemblance to actual persons, living or dead, is +unintentional and purely coincidental. Apply only to affected area. Approved +for veterans. As seen on TV. At participating locations only. Avoid contact +with mucous membranes. Avoid contact with skin. Avoid extreme temperatures +and store in a cool dry place. Batteries not included. Be sure each item is +properly endorsed. Beware of dog. Booths for two or more. Breaking seal +constitutes acceptance of agreement. Call toll free number before digging. +Caveat emptor. Check here if tax deductible. Close cover before striking +Colors may fade. Contains a substantial amount of non-tobacco ingredients. +Contents may settle during shipment. Contestants have been briefed on some +questions before the show. Copyright 1995 Joker's Wild. Disclaimer does +not cover hurricane, lightning, tornado, tsunami, volcanic eruption, +earthquake, flood, and other Acts of God, misuse, neglect, unauthorized +repair, damage from improper installation, broken antenna or marred cabinet, +incorrect line voltage, missing or altered serial numbers, sonic boom +vibrations, electromagnetic radiation from nuclear blasts, customer +adjustments that are not covered in the joke list, and incidents owing to +airplane crash, ship sinking, motor vehicle accidents, leaky roof, broken +glass, falling rocks, mud slides, forest fire, flying projectiles, or +dropping the item. Do not bend, fold, mutilate, or spindle. Do not place +near flammable or magnetic source. Do not puncture, incinerate, or store +above 120 degrees Fahrenheit. Do not stamp. Use other side for additional +listings. Do not use while operating a motor vehicle or heavy equipment. Do +not write below this line. Documents are provided "as is" without any +warranties expressed or implied. Don't quote me on anything. Don't quote me +on that. Driver does not carry cash. Drop in any mailbox. Edited for +television. Employees and their families are not eligible. Falling rock. +First pull up, then pull down. Flames redirected to /dev/null. For a +limited time only. For external use only. For off-road use only. For office +use only. For recreational use only. Do not disturb. Freshest if eaten +before date on carton. Hand wash only, tumble dry on low heat. If a rash, +redness, irritation, or swelling develops, discontinue use. If condition +persists, consult your physician. If defects are discovered, do not attempt +to fix them yourself, but return to an authorized service center. If +ingested, do not induce vomiting, if symptoms persist, consult a doctor. +Keep away from open flames and avoid inhaling fumes. Keep away from +sunlight, pets, and small children. Keep cool; process promptly. Limit +one-per-family please. Limited time offer, call now to ensure prompt +delivery. List at least two alternate dates. List each check separately by +bank number. List was current at time of printing. Lost ticket pays maximum +rate. May be too intense for some viewers. Must be 18 to enter. No Canadian +coins. No alcohol, dogs or horses. No anchovies unless otherwise specified. +No animals were harmed in the production of these documents. No money down. +No other warranty expressed or implied. No passes accepted for this +engagement. No postage necessary if mailed in the United States. No +preservatives added. No purchase necessary. No salt, MSG, artificial color +or flavor added. No shoes, no shirt, no service, no kidding. No solicitors. +No substitutions allowed. No transfers issued until the bus comes to a +complete stop. No user-serviceable parts inside. Not affiliated with the +American Red Cross. Not liable for damages due to use or misuse. Not +recommended for children. Not responsible for direct, indirect, incidental +or consequential damages resulting from any defect, error or failure to +perform. Not the Beatles. Objects in mirror may be closer than they appear. +One size fits all. Many suitcases look alike. Other copyright laws for +specific entries apply wherever noted. Other restrictions may apply. Package +sold by weight, not volume. Parental advisory - explicit lyrics. Penalty for +private use. Place stamp here. Please remain seated until the ride has come +to a complete stop. Possible penalties for early withdrawal. Post office will +not deliver without postage. Postage will be paid by addressee. Prerecorded +for this time zone. Price does not include taxes. Processed at location +stamped in code at top of carton. Quantities are limited while supplies last. +Read at your own risk. Record additional transactions on back of previous +stub. Replace with same type. Reproduction strictly prohibited. Restaurant +package, not for resale. Return to sender, no forwarding order on file, +unable to forward. Safety goggles may be required during use. Sanitized for +your protection. Sealed for your protection, do not use if the safety seal is +broken. See label for sequence. Shading within a garment may occur. Sign here +without admitting guilt. Simulated picture. Slightly enlarged to show detail. +Slightly higher west of the Rockies. Slippery when wet. Smoking these may be +hazardous to your health. Some assembly required. Some equipment shown is +optional. Some of the trademarks mentioned in this product appear for +identification purposes only. Subject to FCC approval. Subject to change +without notice. Substantial penalty for early withdrawal. Text may contain +material some readers may find objectionable, parental guidance is advised. +Text used in these documents is made from 100% recycled electrons and magnetic +particles. These documents do not reflect the thoughts or opinions of either +myself, my company, my friends, or my rabbit. This is not an offer to sell +securities. This offer is void where prohibited, taxed, or otherwise +restricted. This product is meant for educational purposes only. Times +approximate. Unix is a registered trademark of AT&T. Use only as directed. Use +only in a well-ventilated are. User assumes full liabilities. Void where +prohibited. We have sent the forms which seem right for you. You must be +present to win. You need not be present to win. Your canceled check is your +receipt. Your mileage may vary. I didn't do it. You can't prove anything. + +This supersedes all previous notices. +*/ + +#endif // STDDCLMR_H diff --git a/sampleProject/title.img b/sampleProject/title.img new file mode 100644 index 0000000..451fbbb --- /dev/null +++ b/sampleProject/title.img @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:027b9c9e32f162f339eaa0c6d0aa245d5b1bc4f8919db405f472d9d9a690f35e +size 32036 diff --git a/targets/IIgs.target b/targets/IIgs.target index ca77394..a088c17 100644 --- a/targets/IIgs.target +++ b/targets/IIgs.target @@ -37,9 +37,10 @@ function architectures() { function assemble() { - local -n AFILES=$1 + local ARCH=$1 local PASS=$2 local LOG=$3 + local AFILES=(${*:4}) local OFILES="" local FILE= local O= @@ -64,12 +65,12 @@ function buildJoeyLib() { local PASS=$2 local OUT=${GOLDEN_GATE}/out/joey + setCompiler + rm -rf ${G_DIST} mkdir -p ${G_DIST} pushd ${G_DIST} - setCompiler - rm -rf ${OUT} || true mkdir -p ${OUT} @@ -98,25 +99,26 @@ function buildJoeyLib() { cp -f "${OUT}/joey.a" "joey.a#b10000" cp -f "${M_IIGS}/Tool222#ba0000" . - unSetCompiler - popd + + unSetCompiler } function compile() { - local -n CFILES=$1 + local ARCH=$1 local PASS=$2 local LOG=$3 + local CFILES=(${*:4}) local OFILES="" local FILE= local O= - [[ -d "${M_TARGET}" ]] && rm -rf "${M_TARGET}" - mkdir -p "${M_TARGET}" - setCompiler + rm -rf "${M_TARGET}" || true + mkdir -p "${M_TARGET}" + # Compile C files and generate object list. for FILE in "${CFILES[@]}"; do O=${FILE%.*} @@ -282,10 +284,11 @@ function install() { function link() { - local -n OFILES=$1 + local ARCH=$1 local PASS=$2 local LOG=$3 - local LIB="${G_HOME}/dist/IIgs-65816/${PASS}" + local OFILES=(${*:4}) + local LIB="${G_EHOME}/dist/IIgs-65816/${PASS}" setCompiler @@ -314,23 +317,29 @@ function link() { function package() { - local -n DFILES=$1 + local ARCH=$1 local PASS=$2 local LOG=$3 + local DFILES=(${*:4}) local DIR=${G_BUILD_RESULTS}/IIgs-65816/${PASS} local DISK=${DIR}/${G_BUILD_PROJECT}.po - local LIB="${G_HOME}/dist/IIgs-65816/${PASS}" + local LIB="${G_EHOME}/dist/IIgs-65816/${PASS}" local FILE= local O= local EXTENSION= + setCompiler + mkdir -p ${DIR} + #***TODO*** Maybe copy source code for everything to the disk in DEBUG so there are symbols to view? + # Create disk image, setting known file types ${M_CADIUS} createvolume ${DISK} ${G_BUILD_PROJECT} 32MB ${M_CADIUS} createfolder ${DISK} ${G_BUILD_PROJECT}/data ${M_CADIUS} addfile ${DISK} ${G_BUILD_PROJECT} ${M_TARGET}/${G_BUILD_PROJECT}#b3db03 ${M_CADIUS} addfile ${DISK} ${G_BUILD_PROJECT}/data ${LIB}/Tool222#ba0000 + # Copy game data. for FILE in "${DFILES[@]}"; do # Data conversion. @@ -338,30 +347,32 @@ function package() { case ${EXTENSION,,} in mod) php "${M_IIGS}/ntpconverter/ntpconverter.php" "${FILE}" - O=`basename -s .mod ${FILE}`.ntp#060000 + O=${M_TARGET}/$(basename -s .mod ${FILE}).ntp#060000 mv -f ${FILE} ${O} ${M_CADIUS} addfile ${DISK} ${G_BUILD_PROJECT}/data ${O} rm ${O} ;; *) - O=`basename ${FILE}`#060000 + O=${M_TARGET}/$(basename ${FILE})#060000 cp -f ${FILE} ${O} ${M_CADIUS} addfile ${DISK} ${G_BUILD_PROJECT}/data ${O} rm ${O} ;; esac done + + unSetCompiler } function setCompiler() { export PATH=${G_ORIGINAL_PATH}:${M_IIGS} - "${M_IIGS}/mountORCA.sh" + "${M_IIGS}/mountORCA.sh" &> /dev/null } function unSetCompiler() { - "${M_IIGS}/unmountORCA.sh" + "${M_IIGS}/unmountORCA.sh" &> /dev/null export PATH=${G_ORIGINAL_PATH} }