From 6a3413ebea8b99da8ab169cbe78f28404bb88949 Mon Sep 17 00:00:00 2001 From: David Gow Date: Mon, 14 Dec 2020 22:45:17 +0800 Subject: [PATCH] Initial commit of Keen 1 Randomiser Happy Birthday, Keen:Vorticons! --- CK1PATCH.EXE | Bin 0 -> 10767 bytes CWSDPMI.EXE | Bin 0 -> 20217 bytes LICENSE | 6 + README.TXT | 104 ++++++++ RNDKEEN1.BAT | 5 + build.sh | 9 + randkeen.c | 670 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 794 insertions(+) create mode 100755 CK1PATCH.EXE create mode 100644 CWSDPMI.EXE create mode 100644 LICENSE create mode 100644 README.TXT create mode 100644 RNDKEEN1.BAT create mode 100755 build.sh create mode 100644 randkeen.c diff --git a/CK1PATCH.EXE b/CK1PATCH.EXE new file mode 100755 index 0000000000000000000000000000000000000000..61e0bff4f1ea8b47a6e91d7be87270601fd46401 GIT binary patch literal 10767 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 G#QJ|jMEv6b literal 0 HcmV?d00001 diff --git a/CWSDPMI.EXE b/CWSDPMI.EXE new file mode 100644 index 0000000000000000000000000000000000000000..98e7f16a04c1710f4d9f25d0973abdb48a483d04 GIT binary patch literal 20217 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 literal 0 HcmV?d00001 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d56016a --- /dev/null +++ b/LICENSE @@ -0,0 +1,6 @@ +Commander Keen Randomiser +Ⓒ 2020 David Gow + +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/README.TXT b/README.TXT new file mode 100644 index 0000000..ed6bc83 --- /dev/null +++ b/README.TXT @@ -0,0 +1,104 @@ +=========================== +Commander Keen 1 Randomiser +=========================== + +Author: David Gow +Version: 1.0 +Website: https://davidgow.net/keen/randomiser.html + + +About +----- + +The Keen 1 Randomiser lets you play Keen1 with a twist: items, levels, and +enemies are all randomised. + +The randomiser takes a copy of Keen 1, and rewrites the level files to give you +a new experience. It also creates a CK1PATCH-compatible patch file which loads +the replacement files and patches the random number seed in. + +What does it change? +-------------------- + +- Entrances to all levels on the world map are randomised. +- Positions of key items (spaceship parts and pogo stick) are randomised. +- Enemy positions are shuffled (within a level). +- Point items (lollies) are shuffled within a level. +- Door/Keycard colours are randomised (cosmetic only). +- Some block colours are randomised. +- Yorp statues now hint at item locations. +- You can see the random number seed on the Status Screen (press Space) + +Note that there are some things which don't change: +- Level layouts remain the same. +- Raygun ammo remains where it is in the original game. +- There is always a Pogo Stick at the bottom of the Vorticon Commander's Castle +- The Garg statue remains unhelpful. +- The world map looks identical (but the levels within are different) + +How do I use it? +---------------- + +Just run RNDKEEN1.BAT: a differently-randomised version of the game will greet +you each time you play. + +If you want to replay the same game (or have several people play the same +randomised version), you can pass a random number seed to the randomiser: +C:\RANDKEEN> RNDKEEN1 /SEED 12345 +(Replace 12345 with the seed you want to play.) + +You can check what seed you're running from the Status Screen by pressing SPACE. + +If you just want to generate the game, not play it, you can run RANDKEEN.EXE: +this will generate the randomised maps and patch file. Note you'll still need +to rename LEVEL81.CK1 and LEVEL90.CK1 to RNDLV81.CK1 and RNDLV90.CK1 +respectively for the world map and ending screen to work (this is usually done +by the RNDKEEN1.BAT file). + +RNDKEEN1.BAT and RANDKEEN.EXE accept some extra command-line parameters: +- /? -- Show a help screen listing these parameters. +- /SEED -- set the random number seed to +- /DEBUG -- show additional debug information (can contain spoilers!) +- /NOLEVELNAMES -- Make Yorp hints use level numbers instead of names + +The Keen 1 Randomiser requires a 386 or better to run, and probably needs a +megabyte or so of memory. + +Hints! +------ + +Playing Keen 1 Randomiser is, for the most part, very similar to playing Keen1, +but there are a few things to be wary of. + +- You'll probably need to play through levels which are not necessary to win + the original game. Familiarise yourself with them. +- The Pogo Stick is much harder to get: you'll probably need to complete a + lot of levels without it! +- Ship Parts and the Pogo Stick will always be either in their original + locations, or right next to the exit. You can't miss them! +- Raygun Ammo is where you remember it from the original game, but because + you'll be playing levels in a different order, you'll probably find ammo + hard to find. Use it wisely (and get good at dodging!) +- Shrine levels may contain Yorp statues, which have hints. You'll want to know + the level names[1] to take advantage of these. (Or, if you know the level + numbers, you can use the /NOLEVELNAMES switch.) +- There's always a Pogo Stick in the Vorticon Commander's dungeon. + +[1]: http://www.shikadi.net/keenwiki/Keen_1_Levels + +Building the Source Code +------------------------ + +If you want to modify Keen 1 Randomiser (or just see how it works), the source +code is included in this archive (randkeen.c), and is also available on GitHub: + +https://github.com/sulix/randkeen + +It's normally compiled with DJGPP, but can also be built for non-DOS platforms +(just compile randkeen.c with your favourite compiler: it should be reasonably +standard C99). + +Make sure that CK1PATCH and CWSDPMI are copied to the Keen1 directory, as well +as the RNDKEEN1.BAT and RANDKEEN.EXE files. There's an included shell script +which compiles RANDKEEN.EXE and does this, though it has some hardcoded paths +in it. diff --git a/RNDKEEN1.BAT b/RNDKEEN1.BAT new file mode 100644 index 0000000..48dfe66 --- /dev/null +++ b/RNDKEEN1.BAT @@ -0,0 +1,5 @@ +@ECHO OFF +COPY LEVEL81.CK1 RNDLV81.CK1 +COPY LEVEL90.CK1 RNDLV90.CK1 +RANDKEEN %1 %2 %3 %4 %5 +CK1PATCH RNDKEEN1.PAT diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..ddc158f --- /dev/null +++ b/build.sh @@ -0,0 +1,9 @@ +#!/bin/sh + +OUTDIR=rndkeen1/ + +/usr/local/djgpp/bin/i586-pc-msdosdjgpp-gcc -o RANDKEEN.EXE randkeen.c +cp RNDKEEN1.BAT $OUTDIR +cp RANDKEEN.EXE $OUTDIR +cp CWSDPMI.EXE $OUTDIR +cp CK1PATCH.EXE $OUTDIR diff --git a/randkeen.c b/randkeen.c new file mode 100644 index 0000000..eff78c8 --- /dev/null +++ b/randkeen.c @@ -0,0 +1,670 @@ +/* + * Keen 1 Randomiser, by David Gow + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include + + +// Options +int opt_seed = 0; +bool opt_useLevelNames = true; +bool opt_debug = false; + +void debugf(const char *format, ...) +{ + if (!opt_debug) + return; + va_list args; + va_start(args, format); + vprintf(format, args); + va_end(args); +} + +uint16_t fread_u16(FILE *f) +{ + uint16_t u; + fread(&u,2,1,f); + return u; +} + +uint32_t fread_u32(FILE *f) +{ + uint32_t u; + fread(&u, 4, 1, f); + return u; +} + +void fwrite_u16(FILE *f, uint16_t val) +{ + fwrite(&val, 2, 1, f); +} + +void fwrite_u32(FILE *f, uint16_t val) +{ + fwrite(&val, 4, 1, f); +} + +typedef struct VorticonsMap +{ + uint16_t w; + uint16_t h; + uint16_t *planes[2]; +} VorticonsMap; + +#define VORT_MAP_RLE_TAG 0xFEFE + +uint16_t *VORT_DecompressRLE(FILE *f, uint32_t decompSize) +{ + uint16_t *data = malloc(decompSize); + + for(uint16_t *ptr = data; decompSize;) + { + uint16_t val = fread_u16(f); + if (val == VORT_MAP_RLE_TAG) + { + uint16_t length = fread_u16(f); + val = fread_u16(f); + while (length--) + { + *(ptr++) = val; + decompSize -= 2; + } + } + else + { + *(ptr++) = val; + decompSize -= 2; + } + } + return data; +} + +void VORT_CompressRLE(uint16_t* data, uint32_t dataSize, FILE *f) +{ + uint16_t currentVal = 0xFFFF; + uint16_t currentValCount = 0; + while (dataSize) + { + uint16_t val = *(data++); + if (val != currentVal) + { + if (currentValCount > 3) + { + fwrite_u16(f, VORT_MAP_RLE_TAG); + fwrite_u16(f, currentValCount); + fwrite_u16(f, currentVal); + } + else + { + for (int i = 0; i < currentValCount; ++i) + { + fwrite_u16(f, currentVal); + } + } + currentVal = val; + currentValCount = 1; + } + else + { + currentValCount++; + } + dataSize -= 2; + } + if (currentValCount) + { + if (currentValCount > 3) + { + fwrite_u16(f, VORT_MAP_RLE_TAG); + fwrite_u16(f, currentValCount); + fwrite_u16(f, currentVal); + } + else + { + for (int i = 0; i < currentValCount; ++i) + { + fwrite_u16(f, currentVal); + } + } + } +} + + +VorticonsMap VORT_LoadMap(FILE *f) +{ + uint32_t decompSize = fread_u32(f); + uint16_t *mapData = VORT_DecompressRLE(f, decompSize); + VorticonsMap vMap; + vMap.w = mapData[0]; + vMap.h = mapData[1]; + uint16_t mapPlaneSize = mapData[7]; + vMap.planes[0] = mapData + 16; + vMap.planes[1] = mapData + 16 + (mapPlaneSize/2); + + return vMap; +} + +uint16_t VORT_GetTileAtPos(VorticonsMap *vMap, int x, int y, int plane) +{ + return vMap->planes[plane][y * vMap->w + x]; +} + +void VORT_SetTileAtPos(VorticonsMap *vMap, int x, int y, int plane, uint16_t tile) +{ + vMap->planes[plane][y * vMap->w + x] = tile; +} + +void VORT_SaveMap(VorticonsMap *vMap, FILE *f) +{ + uint16_t planeSize = (vMap->w * vMap->h * 2 + 15) & ~15; + uint32_t dataLen = planeSize*2 + 32; + uint16_t *data = calloc(dataLen,1); + data[0] = vMap->w; + data[1] = vMap->h; + data[2] = 2; // Number of planes. + data[7] = planeSize; + + memcpy(&data[16], vMap->planes[0], planeSize); + memcpy(&data[16 + (planeSize / 2)], vMap->planes[1], planeSize); + + fwrite_u32(f, dataLen); + VORT_CompressRLE(data, dataLen, f); +} + + +bool VORT_FindTile(VorticonsMap *vMap, uint16_t tile, int plane, int *x, int *y) +{ + for (int _y = *y; _y < vMap->h; ++_y) + { + for (int _x = (_y == *y)?*x:0; _x < vMap->w; ++_x) + { + if (VORT_GetTileAtPos(vMap, _x, _y, plane) == tile) + { + *x = _x; + *y = _y; + return true; + } + } + } + return false; +} + +void VORT_ReplaceTiles(VorticonsMap *vMap, uint16_t tileFrom, uint16_t tileTo, int plane) +{ + int x = 0; + int y = 0; + while (VORT_FindTile(vMap, tileFrom, plane, &x, &y)) + { + debugf("Found tile %d at (%d, %d)\n", tileFrom, x, y); + VORT_SetTileAtPos(vMap, x, y, plane, tileTo); + } +} + +void PermuteArray(int *array, int count) +{ + for (int i = 0; i < count - 2; ++i) + { + int j = (rand() % (count - i)) + i; + if (array[i] != array[j]) + { + array[i] ^= array[j]; + array[j] ^= array[i]; + array[i] ^= array[j]; + } + } + +} + +#define K1_T_GREYSKY 143 +#define K1_T_POGOSTICK 176 +#define K1_T_JOYSTICK 221 +#define K1_T_BATTERY 237 +#define K1_T_EVERCLEAR 245 +#define K1_T_VACUUM 241 +#define K1_T_EXITSIGN1 167 +#define K1_T_EXITSIGN2 168 + +// NOTE: We don't shuffle the position of the Pogo Stick in Level 16, as it may +// be required to complete the level, and hence the game. +int slotsPerLevel[16] = {1, 1, 2, 2, 2, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1}; +int itemsPerSlot[20] = {K1_T_POGOSTICK, K1_T_JOYSTICK, K1_T_BATTERY, K1_T_EVERCLEAR, K1_T_VACUUM, + K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY, + K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY, + K1_T_GREYSKY, K1_T_GREYSKY, K1_T_GREYSKY}; + +void K1_SetSpecialItem(VorticonsMap *vMap, uint16_t item, int slot) +{ + // There are two types of special item slots: + // - A place where a special item normally goes + // - The place below the "exit" sign in a level + + if (item != K1_T_GREYSKY) + debugf("\tItem %d in slot %d\n"); + + for (int y = 0; y < vMap->h; ++y) + { + for (int x = 0; x < vMap->w; ++x) + { + uint16_t tile = VORT_GetTileAtPos(vMap, x, y, 0); + if (tile == K1_T_POGOSTICK || + (tile >= K1_T_JOYSTICK && tile < K1_T_JOYSTICK+4) || + (tile >= K1_T_BATTERY && tile < K1_T_EVERCLEAR+4) || + (tile == K1_T_EXITSIGN1 || tile == K1_T_EXITSIGN2)) + { + if (!slot--) + { + // If it's the exit sign... + if (tile == K1_T_EXITSIGN1 || tile == K1_T_EXITSIGN2) + { + y++; + uint16_t underTile = VORT_GetTileAtPos(vMap, x, y, 0); + // Check that there's a free space below the exit sign. + if (underTile != K1_T_GREYSKY) + { + y--; + slot++; + continue; + } + } + VORT_SetTileAtPos(vMap, x, y, 0, item); + } + } + } + } +} + +int mapLocations[16] = {1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16}; + +void K1_ShuffleLevelEntries(VorticonsMap *worldMap) +{ + + PermuteArray(mapLocations, 16); + + for (int y = 0; y < worldMap->h; ++y) + { + for (int x = 0; x < worldMap->w; ++x) + { + uint16_t entry = VORT_GetTileAtPos(worldMap, x, y, 1); + if (!entry) + continue; + int level = entry & 0x7F; + if (level > 16) + continue; + debugf("Changing level %d (entry %x) ->", level, entry); + level = mapLocations[level-1]; + entry = (entry & ~0x7F) | level; + debugf(" level %d (entry %x)\n", level, entry); + VORT_SetTileAtPos(worldMap, x, y, 1, entry); + } + } +} + +// Enemy heights in tiles. We need to adjust enemy positions based on this. +int enemyHeights[5] = {2, 2, 3, 1, 2}; + +void K1_ShuffleEnemies(VorticonsMap *vMap) +{ + int totalEnemies = 0; + int enemyCount[5] = {0}; + for (int y = 0; y < vMap->h; ++y) + { + for (int x = 0; x < vMap->w; ++x) + { + uint16_t sprite = VORT_GetTileAtPos(vMap, x, y, 1); + if (sprite >= 1 && sprite <= 5) + { + enemyCount[sprite-1]++; + totalEnemies++; + } + } + } + + + for (int y = 0; y < vMap->h; ++y) + { + for (int x = 0; x < vMap->w; ++x) + { + uint16_t sprite = VORT_GetTileAtPos(vMap, x, y, 1); + if (sprite >= 1 && sprite <= 5) + { + int enemyIdx = rand() % totalEnemies; + int enemyType = 0; + while (enemyIdx > enemyCount[enemyType]) + { + enemyIdx -= enemyCount[enemyType++]; + } + int oldEnemyType = VORT_GetTileAtPos(vMap, x, y, 1) - 1; + VORT_SetTileAtPos(vMap, x, y, 1, 0); + // Avoid having the enemy stuck in the map. + // Note that we only raise enemies off the ground, otherwise + // we'd end up processing the enemy twice (and dividing by 0 as + // a result) + int ny = y; + if (enemyHeights[enemyType] > enemyHeights[oldEnemyType]) + { + ny -= enemyHeights[enemyType] - enemyHeights[oldEnemyType]; + debugf("Enemy %d (%d,%d) of height %d <-=-> Enemy %d (%d, %d) (height %d)\n", oldEnemyType, x, y, enemyHeights[oldEnemyType], enemyType, x, ny, enemyHeights[enemyType]); + } + VORT_SetTileAtPos(vMap, x, ny, 1, 1 + enemyType); + enemyCount[enemyType]--; + totalEnemies--; + } + } + } +} + +void K1_ShuffleLollies(VorticonsMap *vMap) +{ + int totalLollies = 0; + int lollyCount[5] = {0}; + for (int y = 0; y < vMap->h; ++y) + { + for (int x = 0; x < vMap->w; ++x) + { + uint16_t tile = VORT_GetTileAtPos(vMap, x, y, 0); + if (tile >= 201 && tile <= 205) + { + lollyCount[tile-201]++; + totalLollies++; + } + } + } + + + for (int y = 0; y < vMap->h; ++y) + { + for (int x = 0; x < vMap->w; ++x) + { + uint16_t tile = VORT_GetTileAtPos(vMap, x, y, 0); + if (tile >= 201 && tile <= 205) + { + int lollyIdx = rand() % totalLollies; + int lollyType = 0; + while (lollyIdx > lollyCount[lollyType]) + { + lollyIdx -= lollyCount[lollyType++]; + } + VORT_SetTileAtPos(vMap, x, y, 0, 201 + lollyType); + lollyCount[lollyType]--; + totalLollies--; + } + } + } +} + +void K1_MungeBlockColours(VorticonsMap *vMap) +{ + int blockMask = rand()&3; + + int blockToJumpThruMatrix[] = {1,3,0,2}; + int blockToJumpThruInvert[] = {2,0,3,1}; + + debugf("MungeBlockColours: mask = %d\n", blockMask); + + for (int y = 0; y < vMap->h; ++y) + { + for (int x = 0; x < vMap->w; ++x) + { + uint16_t tile = VORT_GetTileAtPos(vMap, x, y, 0); + // Solid blocks + if (tile >= 331 && tile <= 334) + { + tile = ((tile - 331) ^ blockMask) + 331; + VORT_SetTileAtPos(vMap, x, y, 0, tile); + } + // Jump-thru blocks + if ((tile >= 178 && tile <= 181)) + { + int colour = blockToJumpThruInvert[tile - 178]; + colour ^= blockMask; + tile = 178 + blockToJumpThruMatrix[colour]; + VORT_SetTileAtPos(vMap, x, y, 0, tile); + } + } + } +} + +void K1_MungeKeys(VorticonsMap *vMap) +{ + int keyMask = rand()&3; + + for (int y = 0; y < vMap->h; ++y) + { + for (int x = 0; x < vMap->w; ++x) + { + uint16_t tile = VORT_GetTileAtPos(vMap, x, y, 0); + // Keys + if (tile >= 190 && tile <= 193) + { + tile = ((tile - 190) ^ keyMask) + 190; + VORT_SetTileAtPos(vMap, x, y, 0, tile); + } + // Doors + if ((tile >= 173 && tile <= 174) || (tile >= 195 && tile <= 200)) + { + int topOrBot = 1 - (tile & 1); + int colour = (tile <= 174)?0:((tile - 193) >> 1); + colour ^= keyMask; + tile = ((colour == 0)?(173):(195+(colour-1)*2)) + topOrBot; + VORT_SetTileAtPos(vMap, x, y, 0, tile); + } + } + } +} + +// We remove the GARG scream for now, as there's just not enough +// room to fit it in the existing message space. +// A future version can patch it in elsewhere. +int hintLevels[] = {2, 6, 9, 10, 12, 15, -1}; +int currentHint = 0; + +// Level names +const char *levelNames[] = { + NULL, + "Border Town", + "1st Red Rock Shrine", + "Treasury", + "Capital City", + "Pogo Shrine", + "2nd Red Rock Shrine", + "Emerald City", + "Ice City", + "3rd Red Rock Shrine", + "1st Ice Shrine", + "4th Red Rock Shrine", + "5th Red Rock Shrine", + "Red Maze City", + "Secret City", + "2nd Ice Shrine", + "Commander's Castle" +}; + +int hintTiles[] = {K1_T_POGOSTICK, K1_T_JOYSTICK, K1_T_BATTERY, K1_T_VACUUM, K1_T_EVERCLEAR, -1}; +const char *hintTileNames[] = {"Pogo Stick", "Joystick", "Battery", "Vacuum", "Everclear", 0}; + +bool WriteHintHeaderToPatch(FILE *f) +{ + if (hintLevels[currentHint] == -1) + return false; + + if (hintLevels[currentHint] != 11) + fprintf(f, "%%level.hint %d\nA Yorpy Mind\nThought Bellows:\n", hintLevels[currentHint]); + else + fprintf(f, "%%level.hint %d\nA Gargish Scream\nEchoes In Your\nHead:\n", hintLevels[currentHint]); + + currentHint++; + return true; +} + +void WriteTileHintToPatch(FILE *f, int tile, int level) +{ + int hintId = 0; + while (tile != hintTiles[hintId] && hintTiles[hintId] != -1) + hintId++; + if (hintTiles[hintId] == -1) + return; + + if (!WriteHintHeaderToPatch(f)) + return; + + if (opt_useLevelNames) + fprintf(f, "The %s is\nfound in the\n%s\n\n", hintTileNames[hintId], levelNames[level]); + else + fprintf(f, "The %s is\nfound in level %d\n\n", hintTileNames[hintId], level); +} + +void WriteMapHintToPatch(FILE *f, int oldMap, int newMap) +{ + if (oldMap == newMap) + return; + if (!WriteHintHeaderToPatch(f)) + return; + + if (opt_useLevelNames) + fprintf(f, "%s\nrests where the\n%s\nonce was...\n\n", levelNames[newMap], levelNames[oldMap]); + else + fprintf(f, "Level %d\nrests where\nlevel %d once\nwas...\n\n", newMap, oldMap); +} + +void WritePatchHeader(FILE *f) +{ + fprintf(f, "%%ext ck1\n"); + fprintf(f, "%%version 1.31\n\n"); + + // Load levels from RNDLV??.CK1 + fprintf(f, "%%patch $14D9C \"RNDLV\"\n"); + fprintf(f, "%%patch $14DA3 \"RNDLV\"\n\n"); + + // Show the seed in the status screen + fprintf(f, "%%patch $14E60 \" RANDOM SEED \"\n"); + fprintf(f, "%%patch $0FA7\t$B8 $%04XW\n", (opt_seed >> 16) & 0xFFFF); + fprintf(f, "\t\t$BA $%04XW\n", opt_seed & 0xFFFF); + fprintf(f, "\t\t$90 $90 $90 $90 $90 $90\n\n"); + +} + +void WritePatchFooter(FILE *f) +{ + fprintf(f, "%%end\n"); +} + +void PrintBanner() +{ + printf("Keen 1 Randomiser\n"); + printf("\tv1.00\n"); + printf("\tBy David Gow \n\n"); +} + +void PrintOptions() +{ + printf("Available options:\n"); + printf("\t/SEED -- set the random number seed.\n"); + printf("\t/? -- show this message\n"); + printf("\t/DEBUG -- show debug messages.\n"); + printf("\t/NOLEVELNAMES -- use level numbers instead of names in hints.\n"); +} + +void ParseOptions(int argc, char **argv) +{ + for (int i = 1; i < argc; ++i) + { + if (!strcasecmp(argv[i], "/seed")) + { + opt_seed = atoi(argv[++i]); + } + else if (!strcasecmp(argv[i], "/nolevelnames")) + { + opt_useLevelNames = false; + } + else if (!strcasecmp(argv[i], "/debug")) + { + opt_debug = true; + } + else if (!strcasecmp(argv[i], "/?")) + { + PrintOptions(); + exit(0); + } + else + { + printf("Unknown argument \"%s\"\n", argv[i]); + PrintOptions(); + exit(1); + } + } +} + +int main(int argc, char **argv) +{ + PrintBanner(); + // Default random seed. + srand(time(0)); + // Clamp to 16-bit for ease of readability, and because I'm + // not sure srand() actually accepts a 32-bit seed on all archs. + opt_seed = rand() & 0xFFFF; + ParseOptions(argc, argv); + printf("Random seed: %d\n", opt_seed); + srand(opt_seed); + int curItemSlot = 0; + + + PermuteArray(itemsPerSlot, 20); + + FILE *patchFile = fopen("RNDKEEN1.PAT", "w"); + WritePatchHeader(patchFile); + + // World map + FILE *f = fopen("LEVEL80.CK1", "rb"); + VorticonsMap wm = VORT_LoadMap(f); + fclose(f); + K1_ShuffleLevelEntries(&wm); + f = fopen("RNDLV80.CK1", "wb"); + VORT_SaveMap(&wm, f); + fclose(f); + + for (int level = 1; level <= 16; level++) + { + char fname[16]; + sprintf(fname, "LEVEL%02d.CK1", level); + debugf("Processing level %d (%s)\n", level, fname); + FILE *f = fopen(fname, "rb"); + VorticonsMap vm = VORT_LoadMap(f); + fclose(f); + K1_MungeKeys(&vm); + K1_MungeBlockColours(&vm); + K1_ShuffleLollies(&vm); + K1_ShuffleEnemies(&vm); + while (slotsPerLevel[level-1]--) + { + WriteTileHintToPatch(patchFile, itemsPerSlot[curItemSlot], level); + if (itemsPerSlot[curItemSlot] != K1_T_GREYSKY) + WriteMapHintToPatch(patchFile, level, mapLocations[level-1]); + K1_SetSpecialItem(&vm, itemsPerSlot[curItemSlot++], slotsPerLevel[level-1]); + } + sprintf(fname, "RNDLV%02d.CK1", level); + f = fopen(fname, "wb"); + VORT_SaveMap(&vm, f); + fclose(f); + } + + WritePatchFooter(patchFile); + fclose(patchFile); + return 0; +}