diff --git a/.gitignore b/.gitignore index 2967922..5f01450 100644 --- a/.gitignore +++ b/.gitignore @@ -1,84 +1,86 @@ -# Prerequisites -*.d - -# Object files -*.o -*.ko -build/*.obj -*.elf - -# Linker output -*.ilk -*.map -*.exp - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Debug files -*.dSYM/ -*.su -*.idb -*.pdb - -# Kernel Module Compile Results -*.mod* -*.cmd -.tmp_versions/ -modules.order -Module.symvers -Mkfile.old -dkms.conf - -# CMake -CMakeLists.txt.user -CMakeCache.txt -CMakeFiles -CMakeScripts -Testing -Makefile -cmake_install.cmake -install_manifest.txt -compile_commands.json -CTestTestfile.cmake -_deps - -# Custom -build -.vscode - -assets/testworld/tileset.png -oldsrc - -node_modules -yarn.lock - -*.log -obj -res - -out.c -out.c.png - -emulator \ No newline at end of file +# Prerequisites +*.d + +# Object files +*.o +*.ko +build/*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf + +# CMake +CMakeLists.txt.user +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# Custom +build +.vscode + +assets/testworld/tileset.png +oldsrc + +node_modules +yarn.lock + +*.log +obj +res + +out.c +out.c.png + +emulator + +bgb/* \ No newline at end of file diff --git a/archive/main.c b/archive/main.c new file mode 100644 index 0000000..a19f019 --- /dev/null +++ b/archive/main.c @@ -0,0 +1,235 @@ +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "main.h" + +/* + +inline uint8_t mainGetChar(char c) { + return c - TEXTBOX_FONT_START + FONT_DATA_POSITION; +} + +inline void mainBufferCard(uint8_t card, uint8_t *tiles) { + uint8_t suit, number; + + if(card >= CARD_DECK_SIZE) { + tiles[0] = mainGetChar('N'); + tiles[1] = mainGetChar('A'); + return; + } + + suit = cardGetSuit(card); + number = cardGetNumber(card); + + switch(suit) { + case CARD_SUIT_CLUBS: + tiles[0] = mainGetChar('C'); + break; + case CARD_SUIT_DIAMONDS: + tiles[0] = mainGetChar('D'); + break; + case CARD_SUIT_HEARTS: + tiles[0] = mainGetChar('H'); + break; + case CARD_SUIT_SPADES: + tiles[0] = mainGetChar('S'); + break; + } + + switch(number) { + case CARD_TWO: + tiles[1] = mainGetChar('2'); + break; + case CARD_THREE: + tiles[1] = mainGetChar('3'); + break; + case CARD_FOUR: + tiles[1] = mainGetChar('4'); + break; + case CARD_FIVE: + tiles[1] = mainGetChar('5'); + break; + case CARD_SIX: + tiles[1] = mainGetChar('6'); + break; + case CARD_SEVEN: + tiles[1] = mainGetChar('7'); + break; + case CARD_EIGHT: + tiles[1] = mainGetChar('8'); + break; + case CARD_NINE: + tiles[1] = mainGetChar('9'); + break; + case CARD_TEN: + tiles[1] = mainGetChar('T'); + break; + case CARD_JACK: + tiles[1] = mainGetChar('J'); + break; + case CARD_QUEEN: + tiles[1] = mainGetChar('Q'); + break; + case CARD_KING: + tiles[1] = mainGetChar('K'); + break; + case CARD_ACE: + tiles[1] = mainGetChar('A'); + break; + } +} + +inline void mainDebugDraw() { + uint8_t j, i, n; + + // DEBUG DRAW + uint8_t tiles[15]; + char buffer[6]; + buffer[0] = '\0'; + + for(j = 0; j < POKER_PLAYER_COUNT; j++) { + n = 0; + + if(j == POKER_PLAYER_BETTER) { + tiles[n++] = mainGetChar('>'); + } else { + tiles[n++] = COMMON_TILE_3; + } + + mainBufferCard(POKER_PLAYERS[j].hand[0], tiles+n); + n+=2; + mainBufferCard(POKER_PLAYERS[j].hand[1], tiles+n); + n+=2; + + tiles[n++] = COMMON_TILE_3; + + if((POKER_PLAYERS[j].state & POKER_PLAYER_STATE_FOLDED) == 0) { + tiles[n++] = COMMON_TILE_3; + } else { + tiles[n++] = mainGetChar('F'); + } + + if((POKER_PLAYERS[j].state & POKER_PLAYER_STATE_HAS_BET_THIS_ROUND) == 0) { + tiles[n++] = COMMON_TILE_3; + } else { + tiles[n++] = mainGetChar('H'); + } + + if((POKER_PLAYERS[j].state & POKER_PLAYER_STATE_OUT) == 0) { + tiles[n++] = COMMON_TILE_3; + } else { + tiles[n++] = mainGetChar('O'); + } + + tiles[n++] = COMMON_TILE_3; + + // Print chips + sprintf(buffer, "%u", (uint16_t)POKER_PLAYERS[j].chips); + for(i = 0; i < strlen(buffer); i++) { + tiles[n++] = mainGetChar(buffer[i]); + } + + while(n < 15) tiles[n++] = COMMON_TILE_3; + set_bkg_tiles(0x00, j, n - 1, 1, tiles); + } + + for(j = 0; j < POKER_COMMUNITY_SIZE_MAX; j++) { + if(j >= POKER_COMMUNITY_SIZE) { + tiles[j*2] = COMMON_TILE_3; + tiles[(j*2) + 1] = COMMON_TILE_3; + } else { + mainBufferCard(POKER_COMMUNITY[j], tiles + (j * 2)); + } + } + + set_bkg_tiles(0x00, POKER_PLAYER_COUNT + 1, POKER_COMMUNITY_SIZE_MAX * 2, 1, tiles); + + + // Print Pot + n = 0; + sprintf(buffer, "%u", (uint16_t)POKER_POTS[POKER_POT_CURRENT].chips); + for(i = 0; i < strlen(buffer); i++) { + tiles[n++] = mainGetChar(buffer[i]); + } + while(n < 5) tiles[n++] = COMMON_TILE_3; + set_bkg_tiles(0x00, POKER_PLAYER_COUNT + 2, n, 1, tiles); +} + +*/ + +void main() { + int16_t j; + uint8_t filled[GB_BACKGROUND_COLUMNS*GB_BACKGROUND_ROWS]; + + // Set up the GAMEBOY's registers. + disable_interrupts(); + DISPLAY_OFF; + LCDC_REG = LCDCF_OFF | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_BGON; + // Set the background color palette register + BGP_REG = OBP0_REG = OBP1_REG = 0xE4U; + + // Init the random seed + initarand(DIV_REG); + + // Init things + spriteTilesetBuffer(0x00); + spriteFontBuffer(TEXTBOX_SPRITE_FONT_POSITION); + spriteBorderBuffer(TEXTBOX_SPRITE_BORDER_POSITION); + spriteCardsBuffer(TEXTBOX_SPRITE_BORDER_POSITION + BORDER_IMAGE_TILES); + + conversationTextboxInit(); + conversationQueueInit(); + pokerInit(); + + // Fill screen white + for(j = 0; j < GB_BACKGROUND_COLUMNS * GB_BACKGROUND_ROWS; j++) filled[j] = TILESET_WHITE; + set_bkg_tiles(0x00, 0x00, GB_BACKGROUND_COLUMNS, GB_BACKGROUND_ROWS, filled); + SCX_REG = 0x00; + SCY_REG = 0x00; + + // Card Test + uint8_t cardTiles[4 * 6]; + spriteCardBufferTiles( + TEXTBOX_SPRITE_BORDER_POSITION+BORDER_IMAGE_TILES, + cardTiles, + CARD_HEARTS_TWO + ); + set_bkg_tiles(0x00, 0x00, 4, 6, cardTiles); + + // Now turn the screen on + DISPLAY_ON; + enable_interrupts(); + wait_vbl_done(); + + // Begin game + conversationQueueNext(); + + // Begin the loop + while(1) { + // Perform non-graphical code updates + wait_vbl_done(); + + // Update the input state + INPUT_LAST = INPUT_STATE; + INPUT_STATE = joypad(); + INPUT_PRESSED = (~INPUT_LAST) & INPUT_STATE; + + // Tick time. + timeUpdate(); + + // Update conversation pause effect + conversationPauseUpdate(); + + // Update question box and textbox + questionBoxUpdate(); + conversationTextboxUpdate(); + + // Update conversation fade effect + conversationFadeUpdate(); + // mainDebugDraw(); + } +} \ No newline at end of file diff --git a/assets/images/border.png b/assets/images/border.png index 24ef98b..05c2b8f 100644 Binary files a/assets/images/border.png and b/assets/images/border.png differ diff --git a/assets/images/cards_tiles.png b/assets/images/cards_tiles.png new file mode 100644 index 0000000..c3101c5 Binary files /dev/null and b/assets/images/cards_tiles.png differ diff --git a/assets/images/cards_tiles.pxo b/assets/images/cards_tiles.pxo new file mode 100644 index 0000000..8d17d00 Binary files /dev/null and b/assets/images/cards_tiles.pxo differ diff --git a/assets/images/font2.aseprite b/assets/images/font2.aseprite deleted file mode 100644 index 00f4f16..0000000 Binary files a/assets/images/font2.aseprite and /dev/null differ diff --git a/assets/images/tileset.png b/assets/images/tileset.png new file mode 100644 index 0000000..075f054 Binary files /dev/null and b/assets/images/tileset.png differ diff --git a/assets/images_noconvert/FINISHEDV2.png b/assets/images_noconvert/FINISHEDV2.png new file mode 100644 index 0000000..a711243 Binary files /dev/null and b/assets/images_noconvert/FINISHEDV2.png differ diff --git a/assets/images_noconvert/cards.png b/assets/images_noconvert/cards.png new file mode 100644 index 0000000..60d7ec2 Binary files /dev/null and b/assets/images_noconvert/cards.png differ diff --git a/assets/images_noconvert/cards.pxo b/assets/images_noconvert/cards.pxo new file mode 100644 index 0000000..4438902 Binary files /dev/null and b/assets/images_noconvert/cards.pxo differ diff --git a/assets/images_noconvert/tileset.png b/assets/images_noconvert/tileset.png new file mode 100644 index 0000000..075f054 Binary files /dev/null and b/assets/images_noconvert/tileset.png differ diff --git a/assets/tileset.png b/assets/tileset.png deleted file mode 100644 index 006d48f..0000000 Binary files a/assets/tileset.png and /dev/null differ diff --git a/package.json b/package.json index 1c04100..3a2c97d 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,17 @@ -{ - "name": "Dawn-GB", - "version": "1.0.0", - "main": "index.js", - "repository": "https://github.com/YourWishes/Dawn-GB.git", - "author": "Dominic Masters ", - "license": "MIT", - "scripts": { - "clean": "node ./scripts/clean", - "build": "npm run clean && node ./scripts/build", - "start": "npm run build && bgb64.exe ./build/Penny.gb" - }, - "dependencies": { - "pngjs": "^6.0.0", - "rimraf": "^3.0.2" - } -} +{ + "name": "Dawn-GB", + "version": "1.0.0", + "main": "index.js", + "repository": "https://github.com/YourWishes/Dawn-GB.git", + "author": "Dominic Masters ", + "license": "MIT", + "scripts": { + "clean": "node ./scripts/clean", + "build": "npm run clean && node ./scripts/build", + "start": "npm run build && wine ./bgb/bgb64.exe ./build/Penny.gb" + }, + "dependencies": { + "pngjs": "^6.0.0", + "rimraf": "^3.0.2" + } +} diff --git a/scripts/351.js b/scripts/351.js index 1f5f84a..e29b272 100644 --- a/scripts/351.js +++ b/scripts/351.js @@ -1,5 +1,5 @@ -const process = require('process'); -const { spawnSync, execSync } = require('child_process'); - -execSync(`scp ./build/Penny.gb root@ywbud3:/storage/roms/gb/Penny.gb`); +const process = require('process'); +const { spawnSync, execSync } = require('child_process'); + +execSync(`scp ./build/Penny.gb root@ywbud3:/storage/roms/gb/Penny.gb`); execSync(`echo "systemctl stop emustation.service; killall emulationstation; retroarch -L /lib/libretro/gambatte_libretro.so '/storage/roms/gb/Penny.gb';" | ssh root@ywbud3 /bin/bash`); \ No newline at end of file diff --git a/scripts/build.js b/scripts/build.js index 4a066fd..5a6e824 100644 --- a/scripts/build.js +++ b/scripts/build.js @@ -1,124 +1,124 @@ -const fs = require('fs'); -const path = require('path'); -const process = require('process'); -const { spawnSync, execSync } = require('child_process'); -const { png2gb } = require('./png2gb'); -const { string2gb } = require('./string2gb'); - -const DIR_BUILD = path.resolve('build'); -const DIR_GENERATED = path.resolve(DIR_BUILD, 'generated'); -const DIR_OBJ = path.resolve(DIR_BUILD, 'obj'); -const DIR_SRC = path.resolve('src'); -const DIR_GBDK = path.resolve(process.env['GBDKDIR']); -const DIR_ASSETS = path.resolve('assets'); -const DIR_IMAGES = path.resolve(DIR_ASSETS, 'images'); - -const FILE_OUT = path.resolve(DIR_BUILD, 'Penny.gb'); -const FILE_LINKFILE = path.join(DIR_BUILD, `linkflile.lk`); - -const LCC = path.join(DIR_GBDK, 'bin', 'lcc'); -const LCCFLAGS = `-I${DIR_GENERATED} -I${DIR_SRC}`; - -const compiledSources = []; - -// Create build dirs -[ - DIR_BUILD, DIR_GENERATED, DIR_OBJ -].forEach(d => { - if(fs.existsSync(d)) return; - fs.mkdirSync(d); -}); - -// Scandir -const buildSourceFiles = directory => { - const sources = []; - - fs.readdirSync(directory).forEach(file => { - const fullPath = path.join(directory, file); - const stats = fs.statSync(fullPath); - if(stats.isDirectory()) { - sources.push(...buildSourceFiles(fullPath)); - return; - } - - if(file.endsWith('.c')) sources.push(fullPath); - }); - - return sources; -} - -const logOut = (out, buffer) => { - const str = [ - out.stderr, out.stdout - ].filter(n => n) - .map(n => n.toString()) - .filter(n => n) - .join('') - ; - if(!str || !str.length) return; - buffer(str); -} - -const compileC = (cFile) => { - const fileNameOut = path.basename(cFile, '.c') + '.o'; - const fileOut = path.join(DIR_OBJ, fileNameOut); - - compiledSources.push(path.join(fileNameOut)); - - if(fs.existsSync(fileOut)) return; - - let result; - try { - result = execSync(`${LCC} ${LCCFLAGS} -c -o ${fileOut} ${cFile}`); - logOut(result, console.log); - } catch(e) { - logOut(e, e => { - console.error(e); - process.exit(1); - }); - throw e; - } -} - -// Generate strings -// let dataStringH = '#pragma once\n#include "libs.h"\n'; -// let dataStringC = '#include "STRINGS.h"\n'; -// Object.entries(GAME_STRINGS).forEach(entry => { -// const [ name, str ] = entry; -// const { dataH, dataC } = string2gb(str, name); -// dataStringH += dataH+'\n', dataStringC += dataC+'\n'; -// }); -// fs.writeFileSync(path.join(DIR_GENERATED, 'STRINGS.h'), dataStringH); -// fs.writeFileSync(path.join(DIR_GENERATED, 'STRINGS.c'), dataStringC); -// compileC(path.join(DIR_GENERATED, 'STRINGS.c')); - -// Gen imagery -fs.readdirSync(DIR_IMAGES).forEach(img => { - if(!img.endsWith(".png")) return; - const { fileC, fileH } = png2gb( - path.join(DIR_IMAGES, img), DIR_GENERATED, - path.basename(img, '.png').toUpperCase() - ); - compileC(fileC); -}) - -// Get a list of sources and build each of them prior to linking. -const allSources = buildSourceFiles(DIR_SRC); -for(let i = 0; i < allSources.length; i++) { - compileC(allSources[i]); -} - -// Generate a linkfile. -fs.writeFileSync(FILE_LINKFILE, compiledSources.map(cs=>{ - return path.join(DIR_OBJ, cs); -}).join('\n')); - -// Compile BIN -let result; -try { - result = execSync(`${LCC} ${LCCFLAGS} -o ${FILE_OUT} -Wl-f${FILE_LINKFILE}`); - logOut(result, console.log); -} catch(e) { - logOut(e, console.error); - process.exit(1); +const fs = require('fs'); +const path = require('path'); +const process = require('process'); +const { spawnSync, execSync } = require('child_process'); +const { png2gb } = require('./png2gb'); +const { string2gb } = require('./string2gb'); + +const DIR_BUILD = path.resolve('build'); +const DIR_GENERATED = path.resolve(DIR_BUILD, 'generated'); +const DIR_OBJ = path.resolve(DIR_BUILD, 'obj'); +const DIR_SRC = path.resolve('src'); +const DIR_GBDK = path.resolve(process.env['GBDKDIR']); +const DIR_ASSETS = path.resolve('assets'); +const DIR_IMAGES = path.resolve(DIR_ASSETS, 'images'); + +const FILE_OUT = path.resolve(DIR_BUILD, 'Penny.gb'); +const FILE_LINKFILE = path.join(DIR_BUILD, `linkflile.lk`); + +const LCC = path.join(DIR_GBDK, 'bin', 'lcc'); +const LCCFLAGS = `-I${DIR_GENERATED} -I${DIR_SRC}`; + +const compiledSources = []; + +// Create build dirs +[ + DIR_BUILD, DIR_GENERATED, DIR_OBJ +].forEach(d => { + if(fs.existsSync(d)) return; + fs.mkdirSync(d); +}); + +// Scandir +const buildSourceFiles = directory => { + const sources = []; + + fs.readdirSync(directory).forEach(file => { + const fullPath = path.join(directory, file); + const stats = fs.statSync(fullPath); + if(stats.isDirectory()) { + sources.push(...buildSourceFiles(fullPath)); + return; + } + + if(file.endsWith('.c')) sources.push(fullPath); + }); + + return sources; +} + +const logOut = (out, buffer) => { + const str = [ + out.stderr, out.stdout + ].filter(n => n) + .map(n => n.toString()) + .filter(n => n) + .join('') + ; + if(!str || !str.length) return; + buffer(str); +} + +const compileC = (cFile) => { + const fileNameOut = path.basename(cFile, '.c') + '.o'; + const fileOut = path.join(DIR_OBJ, fileNameOut); + + compiledSources.push(path.join(fileNameOut)); + + if(fs.existsSync(fileOut)) return; + + let result; + try { + result = execSync(`${LCC} ${LCCFLAGS} -c -o ${fileOut} ${cFile}`); + logOut(result, console.log); + } catch(e) { + logOut(e, e => { + console.error(e); + process.exit(1); + }); + throw e; + } +} + +// Generate strings +// let dataStringH = '#pragma once\n#include "libs.h"\n'; +// let dataStringC = '#include "STRINGS.h"\n'; +// Object.entries(GAME_STRINGS).forEach(entry => { +// const [ name, str ] = entry; +// const { dataH, dataC } = string2gb(str, name); +// dataStringH += dataH+'\n', dataStringC += dataC+'\n'; +// }); +// fs.writeFileSync(path.join(DIR_GENERATED, 'STRINGS.h'), dataStringH); +// fs.writeFileSync(path.join(DIR_GENERATED, 'STRINGS.c'), dataStringC); +// compileC(path.join(DIR_GENERATED, 'STRINGS.c')); + +// Gen imagery +fs.readdirSync(DIR_IMAGES).forEach(img => { + if(!img.endsWith(".png")) return; + const { fileC, fileH } = png2gb( + path.join(DIR_IMAGES, img), DIR_GENERATED, + path.basename(img, '.png').toUpperCase() + ); + compileC(fileC); +}) + +// Get a list of sources and build each of them prior to linking. +const allSources = buildSourceFiles(DIR_SRC); +for(let i = 0; i < allSources.length; i++) { + compileC(allSources[i]); +} + +// Generate a linkfile. +fs.writeFileSync(FILE_LINKFILE, compiledSources.map(cs=>{ + return path.join(DIR_OBJ, cs); +}).join('\n')); + +// Compile BIN +let result; +try { + result = execSync(`${LCC} ${LCCFLAGS} -o ${FILE_OUT} -Wl-f${FILE_LINKFILE}`); + logOut(result, console.log); +} catch(e) { + logOut(e, console.error); + process.exit(1); } \ No newline at end of file diff --git a/scripts/clean.js b/scripts/clean.js index 9de57db..92c9abd 100644 --- a/scripts/clean.js +++ b/scripts/clean.js @@ -1,4 +1,4 @@ -const rimraf = require('rimraf'); - -console.log('🔥 Cleaning'); +const rimraf = require('rimraf'); + +console.log('🔥 Cleaning'); rimraf.sync('build'); \ No newline at end of file diff --git a/scripts/common.js b/scripts/common.js index 3b2b200..994ea27 100644 --- a/scripts/common.js +++ b/scripts/common.js @@ -1,7 +1,7 @@ -const TILE_WIDTH = 8; -const TILE_HEIGHT = 8; - -module.exports = { - TILE_WIDTH, - TILE_HEIGHT +const TILE_WIDTH = 8; +const TILE_HEIGHT = 8; + +module.exports = { + TILE_WIDTH, + TILE_HEIGHT } \ No newline at end of file diff --git a/scripts/gb2png.js b/scripts/gb2png.js index 160ab41..c391ae0 100644 --- a/scripts/gb2png.js +++ b/scripts/gb2png.js @@ -1,88 +1,88 @@ -const PNG = require('pngjs').PNG; -const fs = require('fs'); -const { - TILE_WIDTH, - TILE_HEIGHT -} = require('./common'); - -const colorPixel = (id) => { - if(id === undefined) id = 3; - if(id === 3) return { r: 8, g: 24, b: 32 }; - if(id === 2) return { r: 52, g: 104, b: 86 }; - if(id === 1) return { r: 136, g: 192, b: 112 }; - if(id === 0) return { r: 224, g: 248, b: 208 }; - throw new Error(); -} - -const gb2png = (DATA, fileOut) => { - // Begin - const PIXELS = DATA.length / 2 * TILE_WIDTH; - const DATA_WIDTH = TILE_WIDTH; - const DATA_HEIGHT = PIXELS / DATA_WIDTH; - - // Create output image - const imageData = new PNG({ - width: DATA_WIDTH, - height: DATA_HEIGHT - }); - - // Convert data into pixels - const pixelsOut = []; - for(let i = 0; i < DATA.length; i += 2) { - const low = DATA[i]; - const high = DATA[i+1]; - - for(let j = 0; j < 8; j++) { - const mask = 0x80 >> j; - const pixel = (low & mask ? 1 : 0) + (high & mask ? 2 : 0); - pixelsOut.push(pixel); - } - } - - // Buffer data output - for(let y = 0; y < DATA_HEIGHT; y++) { - for(let x = 0; x < DATA_WIDTH; x++) { - const id = (DATA_WIDTH * y + x); - const color = colorPixel(pixelsOut[id]); - const idx = id << 2; - - imageData.data[idx] = color.r; - imageData.data[idx+1] = color.g; - imageData.data[idx+2] = color.b; - imageData.data[idx+3] = 0xFF; - } - } - const buffer = PNG.sync.write(imageData, { }); - fs.writeFileSync(fileOut, buffer); -} - -// // Now work out tile data -// if(TILEMAP.length) { -// for(let i = 0; i < TILEMAP.length; i++) { -// const tileX = i % TILEMAP_WIDTH; -// const tileY = Math.floor(i / TILEMAP_WIDTH); -// const tile = TILEMAP[i]; - -// for(let j = 0; j < TILE_WIDTH*TILE_HEIGHT; j++) { -// const outI = ( -// (tileX * TILE_WIDTH) + (tileY * TILE_HEIGHT * TILEMAP_PIXEL_WIDTH) + -// ((j % TILE_WIDTH) + (Math.floor(j / TILE_WIDTH) * TILEMAP_PIXEL_WIDTH)) -// ); -// const idx = outI << 2; -// const pixelI = (tile * TILE_WIDTH * TILE_HEIGHT) + j; -// const color = colorPixel(pixelsOut[pixelI]); - -// tileData.data[idx] = color.r; -// tileData.data[idx+1] = color.g; -// tileData.data[idx+2] = color.b; -// tileData.data[idx+3] = 0xFF; -// } -// } - -// const buffer2 = PNG.sync.write(tileData, { }); -// fs.writeFileSync('out.png', buffer2); -// } - -module.exports = { - gb2png +const PNG = require('pngjs').PNG; +const fs = require('fs'); +const { + TILE_WIDTH, + TILE_HEIGHT +} = require('./common'); + +const colorPixel = (id) => { + if(id === undefined) id = 3; + if(id === 3) return { r: 8, g: 24, b: 32 }; + if(id === 2) return { r: 52, g: 104, b: 86 }; + if(id === 1) return { r: 136, g: 192, b: 112 }; + if(id === 0) return { r: 224, g: 248, b: 208 }; + throw new Error(); +} + +const gb2png = (DATA, fileOut) => { + // Begin + const PIXELS = DATA.length / 2 * TILE_WIDTH; + const DATA_WIDTH = TILE_WIDTH; + const DATA_HEIGHT = PIXELS / DATA_WIDTH; + + // Create output image + const imageData = new PNG({ + width: DATA_WIDTH, + height: DATA_HEIGHT + }); + + // Convert data into pixels + const pixelsOut = []; + for(let i = 0; i < DATA.length; i += 2) { + const low = DATA[i]; + const high = DATA[i+1]; + + for(let j = 0; j < 8; j++) { + const mask = 0x80 >> j; + const pixel = (low & mask ? 1 : 0) + (high & mask ? 2 : 0); + pixelsOut.push(pixel); + } + } + + // Buffer data output + for(let y = 0; y < DATA_HEIGHT; y++) { + for(let x = 0; x < DATA_WIDTH; x++) { + const id = (DATA_WIDTH * y + x); + const color = colorPixel(pixelsOut[id]); + const idx = id << 2; + + imageData.data[idx] = color.r; + imageData.data[idx+1] = color.g; + imageData.data[idx+2] = color.b; + imageData.data[idx+3] = 0xFF; + } + } + const buffer = PNG.sync.write(imageData, { }); + fs.writeFileSync(fileOut, buffer); +} + +// // Now work out tile data +// if(TILEMAP.length) { +// for(let i = 0; i < TILEMAP.length; i++) { +// const tileX = i % TILEMAP_WIDTH; +// const tileY = Math.floor(i / TILEMAP_WIDTH); +// const tile = TILEMAP[i]; + +// for(let j = 0; j < TILE_WIDTH*TILE_HEIGHT; j++) { +// const outI = ( +// (tileX * TILE_WIDTH) + (tileY * TILE_HEIGHT * TILEMAP_PIXEL_WIDTH) + +// ((j % TILE_WIDTH) + (Math.floor(j / TILE_WIDTH) * TILEMAP_PIXEL_WIDTH)) +// ); +// const idx = outI << 2; +// const pixelI = (tile * TILE_WIDTH * TILE_HEIGHT) + j; +// const color = colorPixel(pixelsOut[pixelI]); + +// tileData.data[idx] = color.r; +// tileData.data[idx+1] = color.g; +// tileData.data[idx+2] = color.b; +// tileData.data[idx+3] = 0xFF; +// } +// } + +// const buffer2 = PNG.sync.write(tileData, { }); +// fs.writeFileSync('out.png', buffer2); +// } + +module.exports = { + gb2png }; \ No newline at end of file diff --git a/scripts/gbc_old.js b/scripts/gbc_old.js index 5b4a16a..36b7de1 100644 --- a/scripts/gbc_old.js +++ b/scripts/gbc_old.js @@ -1,247 +1,247 @@ -const fs = require('fs'); -const { PNG } = require('pngjs'); -const path = require('path'); - -const TRANSPARENT = { r: 0, g: 0, b: 0, a: 0 }; -const WHITE = { r: 255, g: 255, b : 255, a: 255 }; -const RED = { r: 255, g: 0, b: 0, a: 255 }; - -const TILE_WIDTH = 8; -const TILE_HEIGHT = 8; - -// Helpers -const pixelIsSame = (left, right, alpha) => { - if(left.r !== right.r) return false; - if(left.g !== right.g) return false; - if(left.b !== right.b) return false; - if(!alpha) return true; - return left.a === right.a; -} - -const imageOut = (pixels, width, fileName) => { - const png = new PNG({ - width, - height: pixels.length / width - }); - - pixels.forEach((pixel, i) => { - const x = i % width; - const y = (i - x) / width; - const idx = (width * y + x) << 2; - png.data[idx] = pixel.r; - png.data[idx+1] = pixel.g; - png.data[idx+2] = pixel.b; - png.data[idx+3] = pixel.a; - }); - - if(!fs.existsSync('out')) fs.mkdirSync('out'); - - const out = PNG.sync.write(png); - fs.writeFileSync(path.join('out', fileName), out); -} - -const imageIn = fileName => { - const data = fs.readFileSync(fileName); - const png = PNG.sync.read(data); - const pixels = []; - - for(let y = 0; y < png.height; y++) { - for (let x = 0; x < png.width; x++) { - let idx = (png.width * y + x) << 2; - const r = png.data[idx]; - const g = png.data[idx+1]; - const b = png.data[idx+2]; - const a = png.data[idx+3]; - - let pixel = { r, g, b, a }; - if(a === 0) { - pixel = { ...WHITE }; - } else { - pixel.a = 255; - } - - pixels.push(pixel); - } - } - - return { pixels, width: png.width, height: png.height }; -} - -const tileFromPixel = (x, y, original) => { - const byEightX = Math.floor(x / 8); - const byEightY = Math.floor(y / 8); - const byEightWidth = Math.floor(original.width / 8); - const byEightId = byEightX + (byEightY * byEightWidth); - - return { x: byEightX, y: byEightY, columns: byEightWidth, id: byEightId }; -} - -// Read Input File -const original = imageIn('bruh.png'); - -const columns = (original.width / TILE_WIDTH); -const rows = (original.height / TILE_HEIGHT); - -// Foreach pixel -const palette = []; -const paletteByEight = []; -const withPaletteOverflows = []; - -for(let y = 0; y < original.height; y++) { - for(let x = 0; x < original.width; x++) { - const id = x + (y * original.width); - const pixel = original.pixels[id]; - let errorPixel = { ...pixel }; - const tile = tileFromPixel(x, y, original); - - // Handle palettes - if(pixel.a != 0) { - const pb8 = (paletteByEight[tile.id] = paletteByEight[tile.id] || []); - - if(!pb8.some(p => pixelIsSame(pixel, p))) { - pb8.push(pixel); - } - // Handle palette overflow - if(pb8.length > 4) errorPixel = { ...RED }; - - // Append to palette - if(!palette.some(p => pixelIsSame(pixel, p))) palette.push(pixel); - } - - withPaletteOverflows.push(errorPixel); - } -} - -// Generate the palette set image -const outPaletteByEight = []; -let outByEightWidth = 1; -paletteByEight.forEach((pal,y) => { - pal.forEach((p,x) => { - outByEightWidth = Math.max(outByEightWidth, x+1); - }) -}); -paletteByEight.forEach((pal,y) => { - for(let x = 0; x < outByEightWidth; x++) { - outPaletteByEight.push(x >= pal.length ? TRANSPARENT : pal[x]); - } -}); - -// Now determine for each TILE what palette to use. -const paletteGroups = []; -const gbVersion = []; -const paletteImage = []; - -for(let y = 0; y < original.height; y++) { - for(let x = 0; x < original.width; x++) { - const id = x + (y * original.width); - const pixel = original.pixels[id]; - const tile = tileFromPixel(x, y, original); - - // Get the palette - const paletteSet = paletteByEight[tile.id]; - - // Check for matching - let palId = paletteGroups.findIndex(pg => { - // Check for cases where one of the pallet group palettes may have - // less pixels than the current set we're checking, e.g. we do a tile that - // has only two colors, then we iterate over a tile with 4 colors that has - // two colors shared with that other tile. In that case we just add our - // two extra colors. - if(paletteSet.length > pg.length) { - return pg.every(p => paletteSet.some(pss => pixelIsSame(pss, p))); - } else { - return paletteSet.every(p => pg.some(pgs => pixelIsSame(pgs, p))); - } - }); - - if(palId === -1) { - palId = paletteGroups.length; - paletteGroups.push(paletteSet); - } - - const paletteGroupSet = paletteGroups[palId]; - - // This is where we correct the missing pixels if we share that tileset from - // earlier - paletteSet.forEach(ps => { - const existing = paletteGroupSet.some(pgs => pixelIsSame(pgs, ps)); - if(existing) return; - paletteGroupSet.push(ps); - }); - - // Sort the paletteGroupSet... - const pgsGetWeight = thing => { - return thing.r + thing.g + thing.b; - } - paletteGroupSet.sort((l,r) => { - return pgsGetWeight(l) - pgsGetWeight(r); - }); - - const examples = [ - /* 0 */{ r: 0, g: 0, b: 0, a: 255 }, - /* 1 */{ r: 255, g: 0, b: 0, a: 255 }, - /* 2 */{ r: 0, g: 255, b: 0, a: 255 }, - /* 3 */{ r: 0, g: 0, b: 255, a: 255 }, - /* 4 */{ r: 255, g: 255, b: 0, a: 255 }, - /* 5 */{ r: 255, g: 0, b: 255, a: 255 }, - /* 6 */{ r: 0, g: 255, b: 255, a: 255 }, - /* 7 */{ r: 255, g: 255, b: 255, a: 255 }, - - /* S */{ r: 100, g: 0, b: 0, a: 255 }, - /* S */{ r: 0, g: 100, b: 0, a: 255 }, - /* S */{ r: 0, g: 0, b: 100, a: 255 }, - /* S */{ r: 100, g: 100, b: 0, a: 255 }, - /* S */{ r: 0, g: 100, b: 100, a: 255 }, - /* S */{ r: 100, g: 0, b: 100, a: 255 }, - /* S */{ r: 100, g: 100, b: 100, a: 255 }, - ]; - paletteImage.push(examples[palId]); - - const pixelIndex = paletteGroupSet.findIndex(ps => pixelIsSame(ps, pixel)); - const nonColor = [ - { r: 8, g: 24, b: 32, a: 255 }, - { r: 52, g: 104, b: 86, a: 255 }, - { r: 136, g: 192, b: 112, a: 255 }, - { r: 224, g: 248, b: 208, a: 255 } - ]; - gbVersion.push(nonColor[pixelIndex % nonColor.length]); - } -} -console.log('Found', paletteGroups.length, 'palettes'); - -imageOut(original.pixels, original.width, 'original.png'); -imageOut(withPaletteOverflows, original.width, 'errors.png'); -imageOut(palette, palette.length, 'palette.png'); -imageOut(outPaletteByEight, outByEightWidth, 'paletteByEight.png'); -imageOut(paletteImage, original.width, 'palettes.png'); -imageOut(gbVersion, original.width, 'gameboy.png'); - -// Now generate the GB files -// let rearranged = []; -// let n = 0; -// for(let i = 0; i < columns * rows; i++) { -// const tileX = i % columns; -// const tileY = Math.floor(i / columns) % rows; - -// for(let y = 0; y < TILE_HEIGHT; y++) { -// for(let x = 0; x < TILE_WIDTH; x++) { -// const px = (tileX * TILE_WIDTH) + x; -// const py = (tileY * TILE_HEIGHT) + y; -// const pi = (py * png.width) + px; -// rearranged[n++] = original.pixels[pi]; -// } -// } -// } - -// // Now turn into a tileset -// const bits = []; -// for(let i = 0; i < rearranged.length; i += TILE_WIDTH) { -// let lowBits = 0x00; -// let highBits = 0x00; -// for(let j = 0; j < TILE_WIDTH; j++) { -// const pixel = rearranged[i + j]; -// lowBits = lowBits | ((pixel & 0x01) << (7-j)); -// highBits = highBits | ((pixel & 0x02) >> 1 << (7-j)); -// } -// bits.push(lowBits, highBits); +const fs = require('fs'); +const { PNG } = require('pngjs'); +const path = require('path'); + +const TRANSPARENT = { r: 0, g: 0, b: 0, a: 0 }; +const WHITE = { r: 255, g: 255, b : 255, a: 255 }; +const RED = { r: 255, g: 0, b: 0, a: 255 }; + +const TILE_WIDTH = 8; +const TILE_HEIGHT = 8; + +// Helpers +const pixelIsSame = (left, right, alpha) => { + if(left.r !== right.r) return false; + if(left.g !== right.g) return false; + if(left.b !== right.b) return false; + if(!alpha) return true; + return left.a === right.a; +} + +const imageOut = (pixels, width, fileName) => { + const png = new PNG({ + width, + height: pixels.length / width + }); + + pixels.forEach((pixel, i) => { + const x = i % width; + const y = (i - x) / width; + const idx = (width * y + x) << 2; + png.data[idx] = pixel.r; + png.data[idx+1] = pixel.g; + png.data[idx+2] = pixel.b; + png.data[idx+3] = pixel.a; + }); + + if(!fs.existsSync('out')) fs.mkdirSync('out'); + + const out = PNG.sync.write(png); + fs.writeFileSync(path.join('out', fileName), out); +} + +const imageIn = fileName => { + const data = fs.readFileSync(fileName); + const png = PNG.sync.read(data); + const pixels = []; + + for(let y = 0; y < png.height; y++) { + for (let x = 0; x < png.width; x++) { + let idx = (png.width * y + x) << 2; + const r = png.data[idx]; + const g = png.data[idx+1]; + const b = png.data[idx+2]; + const a = png.data[idx+3]; + + let pixel = { r, g, b, a }; + if(a === 0) { + pixel = { ...WHITE }; + } else { + pixel.a = 255; + } + + pixels.push(pixel); + } + } + + return { pixels, width: png.width, height: png.height }; +} + +const tileFromPixel = (x, y, original) => { + const byEightX = Math.floor(x / 8); + const byEightY = Math.floor(y / 8); + const byEightWidth = Math.floor(original.width / 8); + const byEightId = byEightX + (byEightY * byEightWidth); + + return { x: byEightX, y: byEightY, columns: byEightWidth, id: byEightId }; +} + +// Read Input File +const original = imageIn('bruh.png'); + +const columns = (original.width / TILE_WIDTH); +const rows = (original.height / TILE_HEIGHT); + +// Foreach pixel +const palette = []; +const paletteByEight = []; +const withPaletteOverflows = []; + +for(let y = 0; y < original.height; y++) { + for(let x = 0; x < original.width; x++) { + const id = x + (y * original.width); + const pixel = original.pixels[id]; + let errorPixel = { ...pixel }; + const tile = tileFromPixel(x, y, original); + + // Handle palettes + if(pixel.a != 0) { + const pb8 = (paletteByEight[tile.id] = paletteByEight[tile.id] || []); + + if(!pb8.some(p => pixelIsSame(pixel, p))) { + pb8.push(pixel); + } + // Handle palette overflow + if(pb8.length > 4) errorPixel = { ...RED }; + + // Append to palette + if(!palette.some(p => pixelIsSame(pixel, p))) palette.push(pixel); + } + + withPaletteOverflows.push(errorPixel); + } +} + +// Generate the palette set image +const outPaletteByEight = []; +let outByEightWidth = 1; +paletteByEight.forEach((pal,y) => { + pal.forEach((p,x) => { + outByEightWidth = Math.max(outByEightWidth, x+1); + }) +}); +paletteByEight.forEach((pal,y) => { + for(let x = 0; x < outByEightWidth; x++) { + outPaletteByEight.push(x >= pal.length ? TRANSPARENT : pal[x]); + } +}); + +// Now determine for each TILE what palette to use. +const paletteGroups = []; +const gbVersion = []; +const paletteImage = []; + +for(let y = 0; y < original.height; y++) { + for(let x = 0; x < original.width; x++) { + const id = x + (y * original.width); + const pixel = original.pixels[id]; + const tile = tileFromPixel(x, y, original); + + // Get the palette + const paletteSet = paletteByEight[tile.id]; + + // Check for matching + let palId = paletteGroups.findIndex(pg => { + // Check for cases where one of the pallet group palettes may have + // less pixels than the current set we're checking, e.g. we do a tile that + // has only two colors, then we iterate over a tile with 4 colors that has + // two colors shared with that other tile. In that case we just add our + // two extra colors. + if(paletteSet.length > pg.length) { + return pg.every(p => paletteSet.some(pss => pixelIsSame(pss, p))); + } else { + return paletteSet.every(p => pg.some(pgs => pixelIsSame(pgs, p))); + } + }); + + if(palId === -1) { + palId = paletteGroups.length; + paletteGroups.push(paletteSet); + } + + const paletteGroupSet = paletteGroups[palId]; + + // This is where we correct the missing pixels if we share that tileset from + // earlier + paletteSet.forEach(ps => { + const existing = paletteGroupSet.some(pgs => pixelIsSame(pgs, ps)); + if(existing) return; + paletteGroupSet.push(ps); + }); + + // Sort the paletteGroupSet... + const pgsGetWeight = thing => { + return thing.r + thing.g + thing.b; + } + paletteGroupSet.sort((l,r) => { + return pgsGetWeight(l) - pgsGetWeight(r); + }); + + const examples = [ + /* 0 */{ r: 0, g: 0, b: 0, a: 255 }, + /* 1 */{ r: 255, g: 0, b: 0, a: 255 }, + /* 2 */{ r: 0, g: 255, b: 0, a: 255 }, + /* 3 */{ r: 0, g: 0, b: 255, a: 255 }, + /* 4 */{ r: 255, g: 255, b: 0, a: 255 }, + /* 5 */{ r: 255, g: 0, b: 255, a: 255 }, + /* 6 */{ r: 0, g: 255, b: 255, a: 255 }, + /* 7 */{ r: 255, g: 255, b: 255, a: 255 }, + + /* S */{ r: 100, g: 0, b: 0, a: 255 }, + /* S */{ r: 0, g: 100, b: 0, a: 255 }, + /* S */{ r: 0, g: 0, b: 100, a: 255 }, + /* S */{ r: 100, g: 100, b: 0, a: 255 }, + /* S */{ r: 0, g: 100, b: 100, a: 255 }, + /* S */{ r: 100, g: 0, b: 100, a: 255 }, + /* S */{ r: 100, g: 100, b: 100, a: 255 }, + ]; + paletteImage.push(examples[palId]); + + const pixelIndex = paletteGroupSet.findIndex(ps => pixelIsSame(ps, pixel)); + const nonColor = [ + { r: 8, g: 24, b: 32, a: 255 }, + { r: 52, g: 104, b: 86, a: 255 }, + { r: 136, g: 192, b: 112, a: 255 }, + { r: 224, g: 248, b: 208, a: 255 } + ]; + gbVersion.push(nonColor[pixelIndex % nonColor.length]); + } +} +console.log('Found', paletteGroups.length, 'palettes'); + +imageOut(original.pixels, original.width, 'original.png'); +imageOut(withPaletteOverflows, original.width, 'errors.png'); +imageOut(palette, palette.length, 'palette.png'); +imageOut(outPaletteByEight, outByEightWidth, 'paletteByEight.png'); +imageOut(paletteImage, original.width, 'palettes.png'); +imageOut(gbVersion, original.width, 'gameboy.png'); + +// Now generate the GB files +// let rearranged = []; +// let n = 0; +// for(let i = 0; i < columns * rows; i++) { +// const tileX = i % columns; +// const tileY = Math.floor(i / columns) % rows; + +// for(let y = 0; y < TILE_HEIGHT; y++) { +// for(let x = 0; x < TILE_WIDTH; x++) { +// const px = (tileX * TILE_WIDTH) + x; +// const py = (tileY * TILE_HEIGHT) + y; +// const pi = (py * png.width) + px; +// rearranged[n++] = original.pixels[pi]; +// } +// } +// } + +// // Now turn into a tileset +// const bits = []; +// for(let i = 0; i < rearranged.length; i += TILE_WIDTH) { +// let lowBits = 0x00; +// let highBits = 0x00; +// for(let j = 0; j < TILE_WIDTH; j++) { +// const pixel = rearranged[i + j]; +// lowBits = lowBits | ((pixel & 0x01) << (7-j)); +// highBits = highBits | ((pixel & 0x02) >> 1 << (7-j)); +// } +// bits.push(lowBits, highBits); // } \ No newline at end of file diff --git a/scripts/png2gb.js b/scripts/png2gb.js index 1cd355e..7c6aaa0 100644 --- a/scripts/png2gb.js +++ b/scripts/png2gb.js @@ -1,93 +1,101 @@ -const PNG = require('pngjs').PNG; -const path = require('path'); -const fs = require('fs'); -const { arrayToString } = require('./util'); -const { - TILE_WIDTH, - TILE_HEIGHT -} = require('./common'); - - -const getPixelValue = (pixel) => { - if(pixel.g === 188) return 0; - if(pixel.g === 172) return 1; - if(pixel.g === 98) return 2; - if(pixel.g === 56) return 3; - throw new Error(); -} - -const png2gb = (fileIn, dirOut, name) => { - const data = fs.readFileSync(fileIn); - const png = PNG.sync.read(data); - - // Convert PNG pixels into 0x00-0x03 - const pixels = []; - for(let y = 0; y < png.height; y++) { - for(let x = 0; x < png.width; x++) { - const id = x + (y * png.width); - const idx = id << 2; - const r = png.data[idx]; - const g = png.data[idx+1]; - const b = png.data[idx+2]; - const value = getPixelValue({ r, g, b }); - pixels.push(value); - } - } - - // Now take these raw pixels and extract the tiles themselves - let rearranged = []; - const columns = (png.width / TILE_WIDTH); - const rows = (png.height / TILE_HEIGHT); - let n = 0; - for(let i = 0; i < columns * rows; i++) { - const tileX = i % columns; - const tileY = Math.floor(i / columns) % rows; - - for(let y = 0; y < TILE_HEIGHT; y++) { - for(let x = 0; x < TILE_WIDTH; x++) { - const px = (tileX * TILE_WIDTH) + x; - const py = (tileY * TILE_HEIGHT) + y; - const pi = (py * png.width) + px; - rearranged[n++] = pixels[pi]; - } - } - } - - // Now turn into a tileset - const bits = []; - for(let i = 0; i < rearranged.length; i += TILE_WIDTH) { - let lowBits = 0x00; - let highBits = 0x00; - for(let j = 0; j < TILE_WIDTH; j++) { - const pixel = rearranged[i + j]; - lowBits = lowBits | ((pixel & 0x01) << (7-j)); - highBits = highBits | ((pixel & 0x02) >> 1 << (7-j)); - } - bits.push(lowBits, highBits); - } - - let outH = ''; - outH += `#include "libs.h"\n\n` - outH += `#define ${name}_IMAGE_WIDTH ${png.width}\n`; - outH += `#define ${name}_IMAGE_HEIGHT ${png.height}\n`; - outH += `#define ${name}_IMAGE_COLUMNS ${png.width / TILE_WIDTH}\n`; - outH += `#define ${name}_IMAGE_ROWS ${png.height / TILE_HEIGHT}\n`; - outH += `#define ${name}_IMAGE_TILES ${columns * rows}\n`; - outH += `extern const uint8_t ${name}_IMAGE[];`; - - let outC = `#include "${name}.h"\n`; - outC += `\nconst uint8_t ${name}_IMAGE[] = {\n${arrayToString(bits)}};`; - - const fileH = path.join(dirOut, name + '.h'); - const fileC = path.join(dirOut, name + '.c'); - - fs.writeFileSync(fileH, outH); - fs.writeFileSync(fileC, outC); - return { fileH, fileC }; -} - -module.exports = { - png2gb -} - +const PNG = require('pngjs').PNG; +const path = require('path'); +const fs = require('fs'); +const { arrayToString } = require('./util'); +const { + TILE_WIDTH, + TILE_HEIGHT +} = require('./common'); + + +const getPixelValue = (pixel) => { + if(pixel.g === 188) return 0; + if(pixel.g === 172) return 1; + if(pixel.g === 98 || pixel.g === 145) return 2; + if(pixel.g === 56) return 3; + if(pixel.a === 0) return 0; + throw new Error(); +} + +const png2gb = (fileIn, dirOut, name) => { + const data = fs.readFileSync(fileIn); + const png = PNG.sync.read(data); + + // Convert PNG pixels into 0x00-0x03 + const pixels = []; + for(let y = 0; y < png.height; y++) { + for(let x = 0; x < png.width; x++) { + const id = x + (y * png.width); + const idx = id << 2; + const r = png.data[idx]; + const g = png.data[idx+1]; + const b = png.data[idx+2]; + const a = png.data[idx+3]; + const pixel = { r, g, b, a }; + try { + const value = getPixelValue(pixel); + pixels.push(value); + } catch(e) { + console.error(`Failed to get color for `, x, y, idx, fileIn, pixel); + throw e; + } + } + } + + // Now take these raw pixels and extract the tiles themselves + let rearranged = []; + const columns = (png.width / TILE_WIDTH); + const rows = (png.height / TILE_HEIGHT); + let n = 0; + for(let i = 0; i < columns * rows; i++) { + const tileX = i % columns; + const tileY = Math.floor(i / columns) % rows; + + for(let y = 0; y < TILE_HEIGHT; y++) { + for(let x = 0; x < TILE_WIDTH; x++) { + const px = (tileX * TILE_WIDTH) + x; + const py = (tileY * TILE_HEIGHT) + y; + const pi = (py * png.width) + px; + rearranged[n++] = pixels[pi]; + } + } + } + + // Now turn into a tileset + const bits = []; + for(let i = 0; i < rearranged.length; i += TILE_WIDTH) { + let lowBits = 0x00; + let highBits = 0x00; + for(let j = 0; j < TILE_WIDTH; j++) { + const pixel = rearranged[i + j]; + lowBits = lowBits | ((pixel & 0x01) << (7-j)); + highBits = highBits | ((pixel & 0x02) >> 1 << (7-j)); + } + bits.push(lowBits, highBits); + } + + let outH = ''; + outH += `#include "libs.h"\n\n` + outH += `#define ${name}_IMAGE_WIDTH ${png.width}\n`; + outH += `#define ${name}_IMAGE_HEIGHT ${png.height}\n`; + outH += `#define ${name}_IMAGE_COLUMNS ${png.width / TILE_WIDTH}\n`; + outH += `#define ${name}_IMAGE_ROWS ${png.height / TILE_HEIGHT}\n`; + outH += `#define ${name}_IMAGE_TILES ${columns * rows}\n`; + outH += `extern const uint8_t ${name}_IMAGE[];`; + + let outC = `#include "${name}.h"\n`; + outC += `\nconst uint8_t ${name}_IMAGE[] = {\n${arrayToString(bits)}};`; + + const fileH = path.join(dirOut, name + '.h'); + const fileC = path.join(dirOut, name + '.c'); + + fs.writeFileSync(fileH, outH); + fs.writeFileSync(fileC, outC); + return { fileH, fileC }; +} + +module.exports = { + png2gb +} + // convert('images/sm.png', 'out.c', 'PENNY'); \ No newline at end of file diff --git a/scripts/string2gb.js b/scripts/string2gb.js index f9255b9..18bec73 100644 --- a/scripts/string2gb.js +++ b/scripts/string2gb.js @@ -1,32 +1,32 @@ -const fs = require('fs'); -const path = require('path'); -const { arrayToString } = require('./util'); - -const FONT_CHARACTER_FIRST = 33; -const FONT_DATA_POSITION = 4; - -const getCodeFrom = l => { - const cc = l.charCodeAt(0) - if(l == '\n' || l == ' ') return cc; - return cc - FONT_CHARACTER_FIRST + FONT_DATA_POSITION -} - -const string2gb = (string, name) => { - const letters = []; - for(let i = 0; i < string.length; i++) { - letters.push(getCodeFrom(string[i])); - } - - let dataH = `#define STR_${name}_LENGTH ${string.length}\n`; - dataH += `extern const uint8_t STR_${name}_DATA[];`; - - let dataC = `const uint8_t STR_${name}_DATA[] = {\n`; - dataC += arrayToString(letters); - dataC += `\n};`; - - return { dataH, dataC }; -} - -module.exports = { - string2gb +const fs = require('fs'); +const path = require('path'); +const { arrayToString } = require('./util'); + +const FONT_CHARACTER_FIRST = 33; +const FONT_DATA_POSITION = 4; + +const getCodeFrom = l => { + const cc = l.charCodeAt(0) + if(l == '\n' || l == ' ') return cc; + return cc - FONT_CHARACTER_FIRST + FONT_DATA_POSITION +} + +const string2gb = (string, name) => { + const letters = []; + for(let i = 0; i < string.length; i++) { + letters.push(getCodeFrom(string[i])); + } + + let dataH = `#define STR_${name}_LENGTH ${string.length}\n`; + dataH += `extern const uint8_t STR_${name}_DATA[];`; + + let dataC = `const uint8_t STR_${name}_DATA[] = {\n`; + dataC += arrayToString(letters); + dataC += `\n};`; + + return { dataH, dataC }; +} + +module.exports = { + string2gb }; \ No newline at end of file diff --git a/scripts/util.js b/scripts/util.js index 8cda31a..16cbd7f 100644 --- a/scripts/util.js +++ b/scripts/util.js @@ -1,21 +1,21 @@ -const arrayToString = arr => { - const b = arr.map(n => { - return '0x' + (n.toString(16).padStart(2, '0').toUpperCase()); - }); - - let str = ''; - for(let i = 0; i < b.length; i += 16) { - str += ' '; - for(let x = i; x < Math.min(i+16, b.length); x++) { - str += b[x]; - str += ','; - } - str += '\n'; - } - - return str; -} - -module.exports = { - arrayToString +const arrayToString = arr => { + const b = arr.map(n => { + return '0x' + (n.toString(16).padStart(2, '0').toUpperCase()); + }); + + let str = ''; + for(let i = 0; i < b.length; i += 16) { + str += ' '; + for(let x = i; x < Math.min(i+16, b.length); x++) { + str += b[x]; + str += ','; + } + str += '\n'; + } + + return str; +} + +module.exports = { + arrayToString } \ No newline at end of file diff --git a/src/conversation/fade.c b/src/conversation/fade.c index 596385f..de92deb 100644 --- a/src/conversation/fade.c +++ b/src/conversation/fade.c @@ -1,69 +1,69 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "fade.h" - -inline void conversationFadeToBlack() { - TIME_FUTURE = TIME_CURRENT; - TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_FADE_TO_BLACK; -} - -inline void conversationFadeFromBlack() { - TIME_FUTURE = TIME_CURRENT; - TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_FADE_FROM_BLACK; -} - -inline void conversationFadeToWhite() { - TIME_FUTURE = TIME_CURRENT; - TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_FADE_TO_WHITE; -} - -inline void conversationFadeFromWhite() { - TIME_FUTURE = TIME_CURRENT; - TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_FADE_FROM_WHITE; -} - -void conversationFadeUpdate() { - uint16_t diff; - - if( - TIME_FUTURE_TYPE < TIME_FUTURE_TYPE_FADE_TO_BLACK || - TIME_FUTURE_TYPE > TIME_FUTURE_TYPE_FADE_FROM_WHITE - ) return; - - diff = TIME_CURRENT - TIME_FUTURE; - - // Now we work out the steps. Time is measured in steps which are made of - // parts of a second. This code assumes that the screen STARTS at the correct - // shade. - if(diff == FADE_STEP) { - // First step - BGP_REG = ( - TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_BLACK ? COMMON_SHADE_DARK : - TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_WHITE ? COMMON_SHADE_BRIGHT : - TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_FROM_BLACK ? COMMON_SHADE_DARKER : - COMMON_SHADE_BRIGHTER - ); - } else if(diff == FADE_STEP * 2) { - // Second step - BGP_REG = ( - TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_BLACK ? COMMON_SHADE_DARKER : - TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_WHITE ? COMMON_SHADE_BRIGHTER : - TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_FROM_BLACK ? COMMON_SHADE_DARK : - COMMON_SHADE_BRIGHT - ); - } else if(diff == FADE_STEP * 3) { - // Third step - BGP_REG = ( - TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_BLACK ? COMMON_SHADE_BLACK : - TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_WHITE ? COMMON_SHADE_WHITE : - COMMON_SHADE_NORMAL - ); - TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_NULL; - conversationQueueNext(); - } +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "fade.h" + +inline void conversationFadeToBlack() { + TIME_FUTURE = TIME_CURRENT; + TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_FADE_TO_BLACK; +} + +inline void conversationFadeFromBlack() { + TIME_FUTURE = TIME_CURRENT; + TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_FADE_FROM_BLACK; +} + +inline void conversationFadeToWhite() { + TIME_FUTURE = TIME_CURRENT; + TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_FADE_TO_WHITE; +} + +inline void conversationFadeFromWhite() { + TIME_FUTURE = TIME_CURRENT; + TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_FADE_FROM_WHITE; +} + +void conversationFadeUpdate() { + uint16_t diff; + + if( + TIME_FUTURE_TYPE < TIME_FUTURE_TYPE_FADE_TO_BLACK || + TIME_FUTURE_TYPE > TIME_FUTURE_TYPE_FADE_FROM_WHITE + ) return; + + diff = TIME_CURRENT - TIME_FUTURE; + + // Now we work out the steps. Time is measured in steps which are made of + // parts of a second. This code assumes that the screen STARTS at the correct + // shade. + if(diff == FADE_STEP) { + // First step + BGP_REG = ( + TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_BLACK ? TILESET_SHADE_DARK : + TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_WHITE ? TILESET_SHADE_BRIGHT : + TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_FROM_BLACK ? TILESET_SHADE_DARKER : + TILESET_SHADE_BRIGHTER + ); + } else if(diff == FADE_STEP * 2) { + // Second step + BGP_REG = ( + TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_BLACK ? TILESET_SHADE_DARKER : + TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_WHITE ? TILESET_SHADE_BRIGHTER : + TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_FROM_BLACK ? TILESET_SHADE_DARK : + TILESET_SHADE_BRIGHT + ); + } else if(diff == FADE_STEP * 3) { + // Third step + BGP_REG = ( + TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_BLACK ? TILESET_SHADE_BLACK : + TIME_FUTURE_TYPE == TIME_FUTURE_TYPE_FADE_TO_WHITE ? TILESET_SHADE_WHITE : + TILESET_SHADE_NORMAL + ); + TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_NULL; + conversationQueueNext(); + } } \ No newline at end of file diff --git a/src/conversation/fade.h b/src/conversation/fade.h index 2355b6e..22cdf41 100644 --- a/src/conversation/fade.h +++ b/src/conversation/fade.h @@ -1,21 +1,21 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "queue.h" -#include "pause.h" -#include "../display/common.h" - -// This is how many frames it takes to change each shade. -#define FADE_STEP 20 - -inline void conversationFadeToBlack(); -inline void conversationFadeFromBlack(); -inline void conversationFadeToWhite(); -inline void conversationFadeFromWhite(); +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "queue.h" +#include "pause.h" +#include "../sprites/spritetileset.h" + +// This is how many frames it takes to change each shade. +#define FADE_STEP 20 + +inline void conversationFadeToBlack(); +inline void conversationFadeFromBlack(); +inline void conversationFadeToWhite(); +inline void conversationFadeFromWhite(); void conversationFadeUpdate(); \ No newline at end of file diff --git a/src/conversation/frame.c b/src/conversation/frame.c deleted file mode 100644 index 479dae6..0000000 --- a/src/conversation/frame.c +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "frame.h" - -inline void frameBuffer( - uint8_t buffer[], - uint8_t bufferWidth, uint8_t bufferHeight, - uint8_t fill -) { - uint8_t i, j, max; - max = bufferWidth * bufferHeight; - - // Corners - buffer[0] = BORDER_TILE_TOP_LEFT; - buffer[bufferWidth-1] = BORDER_TILE_TOP_RIGHT; - buffer[max-1] = BORDER_TILE_BOTTOM_RIGHT; - buffer[max-bufferWidth] = BORDER_TILE_BOTTOM_LEFT; - - // Edges - for(i = 1; i < bufferWidth-1; i++) { - buffer[i] = BORDER_TILE_TOP_CENTER; - buffer[max - 1 - i] = BORDER_TILE_BOTTOM_CENTER; - } - for(i = 1; i < bufferHeight - 1; i++) { - buffer[bufferWidth * i] = BORDER_TILE_CENTER_LEFT; - buffer[bufferWidth * (i+1) - 1] = BORDER_TILE_CENTER_RIGHT; - } - - // Inner - for(j = 1; j < bufferHeight-1; j++) { - for(i = 1; i < bufferWidth-1; i++) { - buffer[i + (j * bufferWidth)] = fill; - } - } -} \ No newline at end of file diff --git a/src/conversation/frame.h b/src/conversation/frame.h deleted file mode 100644 index d1cf955..0000000 --- a/src/conversation/frame.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "../util.h" -#include "../display/common.h" -#include "../display/tilemap.h" - -#define BORDER_TILE_TOP_LEFT BORDER_DATA_POSITION -#define BORDER_TILE_TOP_CENTER BORDER_TILE_TOP_LEFT + 1 -#define BORDER_TILE_TOP_RIGHT BORDER_TILE_TOP_CENTER + 1 -#define BORDER_TILE_CENTER_LEFT BORDER_TILE_TOP_RIGHT + 1 -#define BORDER_TILE_CENTER BORDER_TILE_CENTER_LEFT + 1 -#define BORDER_TILE_CENTER_RIGHT BORDER_TILE_CENTER + 1 -#define BORDER_TILE_BOTTOM_LEFT BORDER_TILE_CENTER_RIGHT + 1 -#define BORDER_TILE_BOTTOM_CENTER BORDER_TILE_BOTTOM_LEFT + 1 -#define BORDER_TILE_BOTTOM_RIGHT BORDER_TILE_BOTTOM_CENTER + 1 - -inline void frameBuffer( - uint8_t buffer[], - uint8_t bufferWidth, uint8_t bufferHeight, - uint8_t fill -); \ No newline at end of file diff --git a/src/conversation/pause.c b/src/conversation/pause.c index 89b577a..a4469b1 100644 --- a/src/conversation/pause.c +++ b/src/conversation/pause.c @@ -1,22 +1,22 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "pause.h" - -inline void conversationPause(uint16_t duration) { - TIME_FUTURE = TIME_CURRENT + (duration * TIME_PER_SECOND); - TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_PAUSE; -} - -inline void conversationPauseUpdate() { - if(TIME_FUTURE_TYPE != TIME_FUTURE_TYPE_PAUSE || TIME_CURRENT != TIME_FUTURE) { - return; - } - - TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_NULL; - conversationQueueNext(); +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "pause.h" + +inline void conversationPause(uint16_t duration) { + TIME_FUTURE = TIME_CURRENT + (duration * TIME_PER_SECOND); + TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_PAUSE; +} + +inline void conversationPauseUpdate() { + if(TIME_FUTURE_TYPE != TIME_FUTURE_TYPE_PAUSE || TIME_CURRENT != TIME_FUTURE) { + return; + } + + TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_NULL; + conversationQueueNext(); } \ No newline at end of file diff --git a/src/conversation/pause.h b/src/conversation/pause.h index b0f0c9f..8ddfe8c 100644 --- a/src/conversation/pause.h +++ b/src/conversation/pause.h @@ -1,14 +1,14 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "../time.h" -#include "queue.h" - -inline void conversationPause(uint16_t duration); +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "../time.h" +#include "queue.h" + +inline void conversationPause(uint16_t duration); inline void conversationPauseUpdate(); \ No newline at end of file diff --git a/src/conversation/questionbox.c b/src/conversation/questionbox.c index 34e802b..c5eccc3 100644 --- a/src/conversation/questionbox.c +++ b/src/conversation/questionbox.c @@ -1,87 +1,87 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "questionbox.h" - -uint8_t QUESTION_BOX_OPTION_COUNT; -uint8_t QUESTION_BOX_OPTION_CURRENT; - -void questionBoxSetOptions(char *title, char **options, uint8_t count) { - uint8_t i, j; - char buffer[TEXTBOX_CHARS_MAX + 1]; - char spaces[QUESTION_BOX_QUESTION_SPACES]; - sprintf(buffer, title); - - i = 0; - QUESTION_BOX_OPTION_CURRENT = 0; - QUESTION_BOX_OPTION_COUNT = count; - - for(i = 0; i < QUESTION_BOX_QUESTION_SPACES; i++) { - spaces[i] = ' '; - } - - // For each row... - for(i = 0; i < count; i++) { - if((i & 1) == 0) { - j = strlen(options[i]); - sprintf(buffer, "%s\n %s", buffer, options[i]); - } else { - j = QUESTION_BOX_QUESTION_SPACES - j; - spaces[j + 1] = '\0'; - sprintf(buffer, "%s%s%s", buffer, spaces, options[i]); - spaces[j + 1] = ' '; - } - } - - conversationTextboxSetText(buffer); - - // Modify the textbox state - TEXTBOX_STATE |= TEXTBOX_STATE_IS_QUESTION; - - // Repurpose old array and draw arrow - spaces[0] = QUESTION_BOX_CURSOR - TEXTBOX_FONT_START + FONT_DATA_POSITION; - set_bkg_tiles(0x01, TEXTBOX_WIN_Y + 0x02, 1, 1, spaces); -} - -inline void questionBoxUpdate() { - uint8_t tiles[1]; - uint8_t i; - - if((TEXTBOX_STATE & (TEXTBOX_STATE_VISIBLE|TEXTBOX_STATE_IS_QUESTION)) == 0) return; - - // Detect input - if(INPUT_PRESSED & J_RIGHT) { - QUESTION_BOX_OPTION_CURRENT++; - } else if(INPUT_PRESSED & J_LEFT) { - QUESTION_BOX_OPTION_CURRENT--; - } else if(INPUT_PRESSED & J_UP) { - QUESTION_BOX_OPTION_CURRENT -= 2; - } else if(INPUT_PRESSED & J_DOWN) { - QUESTION_BOX_OPTION_CURRENT += 2; - } else { - return; - } - - // Bound. - QUESTION_BOX_OPTION_CURRENT = QUESTION_BOX_OPTION_CURRENT % QUESTION_BOX_OPTION_COUNT; - - // Decide where to render arrow - for(i = 0; i < QUESTION_BOX_OPTION_COUNT; i++) { - if(i == QUESTION_BOX_OPTION_CURRENT) { - tiles[0] = QUESTION_BOX_CURSOR - TEXTBOX_FONT_START + FONT_DATA_POSITION; - } else { - tiles[0] = ' ' - TEXTBOX_FONT_START + FONT_DATA_POSITION; - } - set_bkg_tiles( - 0x01 + ((i % 0x02) * QUESTION_BOX_QUESTION_SPACES), - TEXTBOX_WIN_Y + 0x02 + (i / 0x02), - 1, 1, - tiles - ); - } - +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "questionbox.h" + +uint8_t QUESTION_BOX_OPTION_COUNT; +uint8_t QUESTION_BOX_OPTION_CURRENT; + +void questionBoxSetOptions(char *title, char **options, uint8_t count) { + uint8_t i, j; + char buffer[TEXTBOX_CHARS_MAX + 1]; + char spaces[QUESTION_BOX_QUESTION_SPACES]; + sprintf(buffer, title); + + i = 0; + QUESTION_BOX_OPTION_CURRENT = 0; + QUESTION_BOX_OPTION_COUNT = count; + + for(i = 0; i < QUESTION_BOX_QUESTION_SPACES; i++) { + spaces[i] = ' '; + } + + // For each row... + for(i = 0; i < count; i++) { + if((i & 1) == 0) { + j = strlen(options[i]); + sprintf(buffer, "%s\n %s", buffer, options[i]); + } else { + j = QUESTION_BOX_QUESTION_SPACES - j; + spaces[j + 1] = '\0'; + sprintf(buffer, "%s%s%s", buffer, spaces, options[i]); + spaces[j + 1] = ' '; + } + } + + conversationTextboxSetText(buffer); + + // Modify the textbox state + TEXTBOX_STATE |= TEXTBOX_STATE_IS_QUESTION; + + // Repurpose old array and draw arrow + spaces[0] = spriteFontTileFromChar(QUESTION_BOX_CURSOR); + set_bkg_tiles(0x01, TEXTBOX_WIN_Y + 0x02, 1, 1, spaces); +} + +inline void questionBoxUpdate() { + uint8_t tiles[1]; + uint8_t i; + + if((TEXTBOX_STATE & (TEXTBOX_STATE_VISIBLE|TEXTBOX_STATE_IS_QUESTION)) == 0) return; + + // Detect input + if(INPUT_PRESSED & J_RIGHT) { + QUESTION_BOX_OPTION_CURRENT++; + } else if(INPUT_PRESSED & J_LEFT) { + QUESTION_BOX_OPTION_CURRENT--; + } else if(INPUT_PRESSED & J_UP) { + QUESTION_BOX_OPTION_CURRENT -= 2; + } else if(INPUT_PRESSED & J_DOWN) { + QUESTION_BOX_OPTION_CURRENT += 2; + } else { + return; + } + + // Bound. + QUESTION_BOX_OPTION_CURRENT = QUESTION_BOX_OPTION_CURRENT % QUESTION_BOX_OPTION_COUNT; + + // Decide where to render arrow + for(i = 0; i < QUESTION_BOX_OPTION_COUNT; i++) { + if(i == QUESTION_BOX_OPTION_CURRENT) { + tiles[0] = spriteFontTileFromChar(QUESTION_BOX_CURSOR); + } else { + tiles[0] = spriteFontTileFromChar(' '); + } + set_bkg_tiles( + 0x01 + ((i % 0x02) * QUESTION_BOX_QUESTION_SPACES), + TEXTBOX_WIN_Y + 0x02 + (i / 0x02), + 1, 1, + tiles + ); + } + } \ No newline at end of file diff --git a/src/conversation/questionbox.h b/src/conversation/questionbox.h index 1293da6..7ab27be 100644 --- a/src/conversation/questionbox.h +++ b/src/conversation/questionbox.h @@ -1,26 +1,26 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "../util.h" -#include "../input.h" -#include "../display/common.h" -#include "../display/tilemap.h" -#include "frame.h" -#include "textbox.h" - -#define QUESTION_BOX_OPTIONS_MAX ((TEXTBOX_CHAR_ROWS-1)*2) -#define QUESTION_BOX_CURSOR '>' -#define QUESTION_BOX_QUESTION_SPACES 9 - -extern uint8_t QUESTION_BOX_OPTION_COUNT; -extern uint8_t QUESTION_BOX_OPTION_CURRENT; - -void questionBoxSetOptions(char *title, char **options, uint8_t count); - +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "../util.h" +#include "../input.h" +#include "../sprites/spritefont.h" +#include "../sprites/spriteborder.h" +#include "../sprites/spritetileset.h" +#include "textbox.h" + +#define QUESTION_BOX_OPTIONS_MAX ((TEXTBOX_CHAR_ROWS-1)*2) +#define QUESTION_BOX_CURSOR '>' +#define QUESTION_BOX_QUESTION_SPACES 9 + +extern uint8_t QUESTION_BOX_OPTION_COUNT; +extern uint8_t QUESTION_BOX_OPTION_CURRENT; + +void questionBoxSetOptions(char *title, char **options, uint8_t count); + inline void questionBoxUpdate(); \ No newline at end of file diff --git a/src/conversation/queue.c b/src/conversation/queue.c index 4898b89..6bbe776 100644 --- a/src/conversation/queue.c +++ b/src/conversation/queue.c @@ -1,315 +1,315 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "queue.h" -#include "pause.h" -#include "textbox.h" -#include "fade.h" -#include "../poker/poker.h" - -uint16_t QUEUE_ITEM; - -void conversationQueueDebug() { - conversationTextboxSetText(STR_ERROR); -} - -void conversationQueueBegin() { - QUEUE_ITEM = QUEUE_TAKE_BLINDS; - conversationTextboxSetText(STR_POKER_GAME_START); -} - -void conversationQueueTakeBlinds() { - QUEUE_ITEM = QUEUE_DEAL_CARDS; - conversationTextboxSetText(STR_POKER_GAME_TAKING_BLINDS); -} - -void conversationQueueDealCards() { - QUEUE_ITEM = QUEUE_BEGIN_BETTING; - conversationTextboxSetText(STR_POKER_GAME_CARDS_DEALT); -} - -void conversationQueueBeginBetting() { - pokerturn_t turn; - turn.chips = 0; - // Begin the betting process. First we need to decide if the player or the - // AI is betting, then based on that we decide what to do next. - - // TODO: Actually bet. - if(POKER_PLAYER_BETTER == POKER_HUMAN_INDEX) { - // This is the human player. - BGB_MESSAGE("Player folding."); - conversationTextboxSetText(STR_DEBUG_PLAYER); - POKER_PLAYERS[POKER_PLAYER_BETTER].state |= POKER_PLAYER_STATE_FOLDED; - } else { - // This is an AI player, get their turn. - BGB_MESSAGE("AI turn to bet"); - pokerAi(POKER_PLAYER_BETTER, &turn); - BGB_printf("AI Decided to %u, with %u chips and %u confidence, bluffin: %u", turn.type, turn.chips, turn.confidence, turn.bluff); - - switch(turn.type) { - case POKER_TURN_TYPE_FOLD: - POKER_PLAYERS[POKER_PLAYER_BETTER].state |= POKER_PLAYER_STATE_FOLDED; - conversationTextboxSetText(STR_POKER_GAME_AI_FOLD); - break; - - case POKER_TURN_TYPE_BET: - if(turn.bluff) { - conversationTextboxSetText(STR_POKER_GAME_AI_RAISE_BLUFF); - } else { - conversationTextboxSetText(STR_POKER_GAME_AI_RAISE); - } - break; - - case POKER_TURN_TYPE_CALL: - if(turn.bluff) { - conversationTextboxSetText(STR_POKER_GAME_AI_CALL_BLUFF); - } else { - conversationTextboxSetText(STR_POKER_GAME_AI_CALL); - } - break; - - case POKER_TURN_TYPE_ALL_IN: - if(turn.bluff) { - conversationTextboxSetText(STR_POKER_GAME_AI_ALL_IN_BLUFF); - } else { - conversationTextboxSetText(STR_POKER_GAME_AI_ALL_IN); - } - break; - - case POKER_TURN_TYPE_CHECK: - if(turn.bluff) { - conversationTextboxSetText(STR_POKER_GAME_AI_CHECK_BLUFF); - } else { - conversationTextboxSetText(STR_POKER_GAME_AI_CHECK); - } - break; - } - - // Now we have their turn, decide what to say based on that. - } - - // Update bet state - POKER_PLAYERS[POKER_PLAYER_BETTER].state |= ( - POKER_PLAYER_STATE_HAS_BET_THIS_ROUND - ); - - if(turn.chips > 0) { - pokerBet(POKER_PLAYER_BETTER, turn.chips); - } - - QUEUE_ITEM = QUEUE_NEXT_BETTER; -} - -void conversationQueueNextBetter() { - uint8_t i, j, countStillInGame; - - // Now we decide the next better. - countStillInGame = 0; - for(i = 0; i < POKER_PLAYER_COUNT; i++) { - //In theory I don't think the current better can ever be selected again. - // if(i == POKER_PLAYER_BETTER) continue; // Commented because we now count - - // Next better is the better to the right of the current better. - j = (POKER_PLAYER_BETTER + i) % POKER_PLAYER_COUNT; - if(!pokerDoesPlayerNeedToBet(j)) { - if((POKER_PLAYERS[j].state & (POKER_PLAYER_STATE_FOLDED | POKER_PLAYER_STATE_OUT)) == 0) { - countStillInGame++; - } - continue; - } - - // They haven't bet yet, make them the "next better" - POKER_PLAYER_BETTER = j; - - QUEUE_ITEM = QUEUE_BEGIN_BETTING; - conversationQueueNext(); - return; - } - - // If we reach this point then we either need to begin the betting round, or - // we are going to move to the winning decider. - if(POKER_COMMUNITY_SIZE_MAX == POKER_COMMUNITY_SIZE || countStillInGame == 1) { - QUEUE_ITEM = QUEUE_WINNER_DECIDE; - conversationQueueNext(); - return; - } - - QUEUE_ITEM = QUEUE_FLOP; - conversationQueueNext(); -} - -void conversationQueueFlopTurnRiver() { - uint8_t i, count; - pokerplayer_t *player; - - - switch(POKER_COMMUNITY_SIZE) { - case 0: - count = POKER_COUNT_FLOP; - conversationTextboxSetText(STR_POKER_GAME_CARDS_FLOPPED); - break; - - case POKER_COUNT_FLOP: - count = POKER_COUNT_TURN; - conversationTextboxSetText(STR_POKER_GAME_CARDS_TURNED); - break; - - default: - count = POKER_COUNT_RIVER; - conversationTextboxSetText(STR_POKER_GAME_CARDS_RIVERED); - break; - } - - // Reset each players required to bet state - do { - player = POKER_PLAYERS + i; - player->state &= ~POKER_PLAYER_STATE_HAS_BET_THIS_ROUND; - player->timesRaised = 0; - } while(++i < POKER_PLAYER_COUNT); - - // In reality we'd burn the top card but that would waste some CPU I need. - // Deal the top cards. - for(i = 0; i < count; i++) { - POKER_COMMUNITY[POKER_COMMUNITY_SIZE++] = POKER_DECK[--POKER_DECK_SIZE]; - } - - // Check how many players need to bet, if it's zero then all players are - // either folded or all-in - if(pokerGetRemainingBetterCount() == 0x00) { - if(POKER_COMMUNITY_SIZE == POKER_COMMUNITY_SIZE_MAX) { - QUEUE_ITEM = QUEUE_WINNER_DECIDE; - } else { - QUEUE_ITEM = QUEUE_FLOP; - } - } else { - QUEUE_ITEM = QUEUE_BEGIN_BETTING; - } -} - -void conversationQueueWinnerDecide() { - pokerpot_t *pot; - uint8_t i, j, countOfPotsWithChips, chipsEach; - - QUEUE_ITEM = QUEUE_BEGIN; - - countOfPotsWithChips = 0; - for(i = 0; i < POKER_POT_COUNT; i++) { - pot = POKER_POTS + i; - if(pot->chips == 0) break; - countOfPotsWithChips++; - } - - // TODO: Messages - if(countOfPotsWithChips == 1) { - } else { - } - - // Pots. - for(i = 0; i < countOfPotsWithChips; i++) { - pot = POKER_POTS + i; - pokerWinnerDetermineForPot( - pot, - POKER_WINNERS, - POKER_WINNER_PLAYERS, - &POKER_WINNER_COUNT, - POKER_WINNER_PARTICIPANTS, - &POKER_WINNER_PARTICIPANT_COUNT - ); - - chipsEach = pot->chips / POKER_WINNER_COUNT; - for(j = 0; j < POKER_WINNER_COUNT; j++) { - POKER_PLAYERS[POKER_WINNER_PLAYERS[j]].chips += chipsEach; - // TODO: I can determine rounding error here if I need to, just sub the - // taken chips for each player and when I get to chips remaining less than - // the chipsEach, then reward the rounding offset. - } - } - - // TODO: Decide on a winner for real. - conversationTextboxSetText(STR_DEBUG_WINNER_DECIDED); - - // New Round - pokerNewRound(); -} - -//////////////////////////////////////////////////////////////////////////////// - -queuecallback_t *QUEUE_CALLBACKS[] = { - // 0 - NULL, - &conversationQueueDebug, - NULL, - NULL, - NULL, - - // 5 - &conversationQueueBegin, - NULL, - NULL, - NULL, - NULL, - - // 10 - &conversationQueueTakeBlinds, - NULL, - NULL, - NULL, - NULL, - - // 15 - &conversationQueueDealCards, - NULL, - NULL, - NULL, - NULL, - - // 20 - &conversationQueueBeginBetting, - NULL, - NULL, - NULL, - NULL, - - // 25 - &conversationQueueNextBetter, - NULL, - NULL, - NULL, - NULL, - - // 30 - &conversationQueueFlopTurnRiver, - NULL, - NULL, - NULL, - NULL, - - // 35 - NULL, - NULL, - NULL, - NULL, - NULL, - - // 40 - &conversationQueueWinnerDecide, - // NULL, - // NULL, - // NULL, - // NULL, -}; - -inline void conversationQueueInit() { - QUEUE_ITEM = QUEUE_BEGIN; -} - -inline void conversationQueueNext() { - BGB_printf("Doing %d", QUEUE_ITEM); - if(QUEUE_ITEM > QUEUE_WINNER_DECIDE) return; - if(QUEUE_CALLBACKS[QUEUE_ITEM] == NULL) return; - QUEUE_CALLBACKS[QUEUE_ITEM](); +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "queue.h" +#include "pause.h" +#include "textbox.h" +#include "fade.h" +#include "../poker/poker.h" + +uint16_t QUEUE_ITEM; + +void conversationQueueDebug() { + conversationTextboxSetText(STR_ERROR); +} + +void conversationQueueBegin() { + QUEUE_ITEM = QUEUE_TAKE_BLINDS; + conversationTextboxSetText(STR_POKER_GAME_START); +} + +void conversationQueueTakeBlinds() { + QUEUE_ITEM = QUEUE_DEAL_CARDS; + conversationTextboxSetText(STR_POKER_GAME_TAKING_BLINDS); +} + +void conversationQueueDealCards() { + QUEUE_ITEM = QUEUE_BEGIN_BETTING; + conversationTextboxSetText(STR_POKER_GAME_CARDS_DEALT); +} + +void conversationQueueBeginBetting() { + pokerturn_t turn; + turn.chips = 0; + // Begin the betting process. First we need to decide if the player or the + // AI is betting, then based on that we decide what to do next. + + // TODO: Actually bet. + if(POKER_PLAYER_BETTER == POKER_HUMAN_INDEX) { + // This is the human player. + BGB_MESSAGE("Player folding."); + conversationTextboxSetText(STR_DEBUG_PLAYER); + POKER_PLAYERS[POKER_PLAYER_BETTER].state |= POKER_PLAYER_STATE_FOLDED; + } else { + // This is an AI player, get their turn. + BGB_MESSAGE("AI turn to bet"); + pokerAi(POKER_PLAYER_BETTER, &turn); + BGB_printf("AI Decided to %u, with %u chips and %u confidence, bluffin: %u", turn.type, turn.chips, turn.confidence, turn.bluff); + + switch(turn.type) { + case POKER_TURN_TYPE_FOLD: + POKER_PLAYERS[POKER_PLAYER_BETTER].state |= POKER_PLAYER_STATE_FOLDED; + conversationTextboxSetText(STR_POKER_GAME_AI_FOLD); + break; + + case POKER_TURN_TYPE_BET: + if(turn.bluff) { + conversationTextboxSetText(STR_POKER_GAME_AI_RAISE_BLUFF); + } else { + conversationTextboxSetText(STR_POKER_GAME_AI_RAISE); + } + break; + + case POKER_TURN_TYPE_CALL: + if(turn.bluff) { + conversationTextboxSetText(STR_POKER_GAME_AI_CALL_BLUFF); + } else { + conversationTextboxSetText(STR_POKER_GAME_AI_CALL); + } + break; + + case POKER_TURN_TYPE_ALL_IN: + if(turn.bluff) { + conversationTextboxSetText(STR_POKER_GAME_AI_ALL_IN_BLUFF); + } else { + conversationTextboxSetText(STR_POKER_GAME_AI_ALL_IN); + } + break; + + case POKER_TURN_TYPE_CHECK: + if(turn.bluff) { + conversationTextboxSetText(STR_POKER_GAME_AI_CHECK_BLUFF); + } else { + conversationTextboxSetText(STR_POKER_GAME_AI_CHECK); + } + break; + } + + // Now we have their turn, decide what to say based on that. + } + + // Update bet state + POKER_PLAYERS[POKER_PLAYER_BETTER].state |= ( + POKER_PLAYER_STATE_HAS_BET_THIS_ROUND + ); + + if(turn.chips > 0) { + pokerBet(POKER_PLAYER_BETTER, turn.chips); + } + + QUEUE_ITEM = QUEUE_NEXT_BETTER; +} + +void conversationQueueNextBetter() { + uint8_t i, j, countStillInGame; + + // Now we decide the next better. + countStillInGame = 0; + for(i = 0; i < POKER_PLAYER_COUNT; i++) { + //In theory I don't think the current better can ever be selected again. + // if(i == POKER_PLAYER_BETTER) continue; // Commented because we now count + + // Next better is the better to the right of the current better. + j = (POKER_PLAYER_BETTER + i) % POKER_PLAYER_COUNT; + if(!pokerDoesPlayerNeedToBet(j)) { + if((POKER_PLAYERS[j].state & (POKER_PLAYER_STATE_FOLDED | POKER_PLAYER_STATE_OUT)) == 0) { + countStillInGame++; + } + continue; + } + + // They haven't bet yet, make them the "next better" + POKER_PLAYER_BETTER = j; + + QUEUE_ITEM = QUEUE_BEGIN_BETTING; + conversationQueueNext(); + return; + } + + // If we reach this point then we either need to begin the betting round, or + // we are going to move to the winning decider. + if(POKER_COMMUNITY_SIZE_MAX == POKER_COMMUNITY_SIZE || countStillInGame == 1) { + QUEUE_ITEM = QUEUE_WINNER_DECIDE; + conversationQueueNext(); + return; + } + + QUEUE_ITEM = QUEUE_FLOP; + conversationQueueNext(); +} + +void conversationQueueFlopTurnRiver() { + uint8_t i, count; + pokerplayer_t *player; + + + switch(POKER_COMMUNITY_SIZE) { + case 0: + count = POKER_COUNT_FLOP; + conversationTextboxSetText(STR_POKER_GAME_CARDS_FLOPPED); + break; + + case POKER_COUNT_FLOP: + count = POKER_COUNT_TURN; + conversationTextboxSetText(STR_POKER_GAME_CARDS_TURNED); + break; + + default: + count = POKER_COUNT_RIVER; + conversationTextboxSetText(STR_POKER_GAME_CARDS_RIVERED); + break; + } + + // Reset each players required to bet state + do { + player = POKER_PLAYERS + i; + player->state &= ~POKER_PLAYER_STATE_HAS_BET_THIS_ROUND; + player->timesRaised = 0; + } while(++i < POKER_PLAYER_COUNT); + + // In reality we'd burn the top card but that would waste some CPU I need. + // Deal the top cards. + for(i = 0; i < count; i++) { + POKER_COMMUNITY[POKER_COMMUNITY_SIZE++] = POKER_DECK[--POKER_DECK_SIZE]; + } + + // Check how many players need to bet, if it's zero then all players are + // either folded or all-in + if(pokerGetRemainingBetterCount() == 0x00) { + if(POKER_COMMUNITY_SIZE == POKER_COMMUNITY_SIZE_MAX) { + QUEUE_ITEM = QUEUE_WINNER_DECIDE; + } else { + QUEUE_ITEM = QUEUE_FLOP; + } + } else { + QUEUE_ITEM = QUEUE_BEGIN_BETTING; + } +} + +void conversationQueueWinnerDecide() { + pokerpot_t *pot; + uint8_t i, j, countOfPotsWithChips, chipsEach; + + QUEUE_ITEM = QUEUE_BEGIN; + + countOfPotsWithChips = 0; + for(i = 0; i < POKER_POT_COUNT; i++) { + pot = POKER_POTS + i; + if(pot->chips == 0) break; + countOfPotsWithChips++; + } + + // TODO: Messages + if(countOfPotsWithChips == 1) { + } else { + } + + // Pots. + for(i = 0; i < countOfPotsWithChips; i++) { + pot = POKER_POTS + i; + pokerWinnerDetermineForPot( + pot, + POKER_WINNERS, + POKER_WINNER_PLAYERS, + &POKER_WINNER_COUNT, + POKER_WINNER_PARTICIPANTS, + &POKER_WINNER_PARTICIPANT_COUNT + ); + + chipsEach = pot->chips / POKER_WINNER_COUNT; + for(j = 0; j < POKER_WINNER_COUNT; j++) { + POKER_PLAYERS[POKER_WINNER_PLAYERS[j]].chips += chipsEach; + // TODO: I can determine rounding error here if I need to, just sub the + // taken chips for each player and when I get to chips remaining less than + // the chipsEach, then reward the rounding offset. + } + } + + // TODO: Decide on a winner for real. + conversationTextboxSetText(STR_DEBUG_WINNER_DECIDED); + + // New Round + pokerNewRound(); +} + +//////////////////////////////////////////////////////////////////////////////// + +queuecallback_t *QUEUE_CALLBACKS[] = { + // 0 + NULL, + &conversationQueueDebug, + NULL, + NULL, + NULL, + + // 5 + &conversationQueueBegin, + NULL, + NULL, + NULL, + NULL, + + // 10 + &conversationQueueTakeBlinds, + NULL, + NULL, + NULL, + NULL, + + // 15 + &conversationQueueDealCards, + NULL, + NULL, + NULL, + NULL, + + // 20 + &conversationQueueBeginBetting, + NULL, + NULL, + NULL, + NULL, + + // 25 + &conversationQueueNextBetter, + NULL, + NULL, + NULL, + NULL, + + // 30 + &conversationQueueFlopTurnRiver, + NULL, + NULL, + NULL, + NULL, + + // 35 + NULL, + NULL, + NULL, + NULL, + NULL, + + // 40 + &conversationQueueWinnerDecide, + // NULL, + // NULL, + // NULL, + // NULL, +}; + +inline void conversationQueueInit() { + QUEUE_ITEM = QUEUE_BEGIN; +} + +inline void conversationQueueNext() { + BGB_printf("Doing %d", QUEUE_ITEM); + if(QUEUE_ITEM > QUEUE_WINNER_DECIDE) return; + if(QUEUE_CALLBACKS[QUEUE_ITEM] == NULL) return; + QUEUE_CALLBACKS[QUEUE_ITEM](); } \ No newline at end of file diff --git a/src/conversation/queue.h b/src/conversation/queue.h index 64cd3f9..6ef2a94 100644 --- a/src/conversation/queue.h +++ b/src/conversation/queue.h @@ -1,27 +1,27 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "../strings.h" - -typedef void queuecallback_t(); - -extern uint16_t QUEUE_ITEM; -extern queuecallback_t *QUEUE_CALLBACKS[]; - -#define QUEUE_DEBUG 1 -#define QUEUE_BEGIN 5 -#define QUEUE_TAKE_BLINDS 10 -#define QUEUE_DEAL_CARDS 15 -#define QUEUE_BEGIN_BETTING 20 -#define QUEUE_NEXT_BETTER 25 -#define QUEUE_FLOP 30 -#define QUEUE_WINNER_DECIDE 40 - -inline void conversationQueueInit(); +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "../strings.h" + +typedef void queuecallback_t(); + +extern uint16_t QUEUE_ITEM; +extern queuecallback_t *QUEUE_CALLBACKS[]; + +#define QUEUE_DEBUG 1 +#define QUEUE_BEGIN 5 +#define QUEUE_TAKE_BLINDS 10 +#define QUEUE_DEAL_CARDS 15 +#define QUEUE_BEGIN_BETTING 20 +#define QUEUE_NEXT_BETTER 25 +#define QUEUE_FLOP 30 +#define QUEUE_WINNER_DECIDE 40 + +inline void conversationQueueInit(); inline void conversationQueueNext(); \ No newline at end of file diff --git a/src/conversation/textbox.c b/src/conversation/textbox.c index e18f378..634ea11 100644 --- a/src/conversation/textbox.c +++ b/src/conversation/textbox.c @@ -1,150 +1,144 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "textbox.h" - -char TEXTBOX_TEXTS[TEXTBOX_SCROLL_ROWS_MAX * TEXTBOX_CHARS_PER_ROW]; -uint8_t TEXTBOX_ROW_COUNT; -uint8_t TEXTBOX_ROW_TILE_LAST; -uint8_t TEXTBOX_ROW_CURRENT; -uint8_t TEXTBOX_STATE; -uint8_t TEXTBOX_SCROLL; - -inline void conversationTextboxInit() { - uint8_t i; - uint8_t TEXTBOX_TILES[TEXTBOX_TILES_MAX]; - - // Reset textbox state - TEXTBOX_STATE = 0; - - // Setup window data - set_win_data(FONT_DATA_POSITION, FONT_IMAGE_TILES, FONT_IMAGE); - set_win_data(BORDER_DATA_POSITION, BORDER_IMAGE_TILES, BORDER_IMAGE); -} - -void conversationTextboxSetText(char *text) { - uint8_t i, j, k, rowStart, stateFlags; - char c, c2; - - // Begin by filling the textbox text chars with whitespace to begin. - // TODO: I'm pretty sure I can move this lower and optimize. - for(i = 0; i < TEXTBOX_SCROLL_ROWS_MAX * TEXTBOX_CHARS_PER_ROW; i++) { - TEXTBOX_TEXTS[i] = ' '; - } - - // Reset textbox state - TEXTBOX_STATE = TEXTBOX_STATE_VISIBLE; - TEXTBOX_SCROLL = 0; - TEXTBOX_ROW_COUNT = 0; - TEXTBOX_ROW_COUNT = 1; - - // Copy source text to buffer, also determine wordwrapping here. - i = 0, j = 0, rowStart = 0, stateFlags = 0; - while((c = text[i]) != '\0') {// "For each char in string" - if(c == ' ') { - // Scan ahead and look at how many chars remain to the next space. - k = i + 1; - while( - (c2 = text[k]) != '\0' && - c2 != '\n' && - c2 != ' ' && - (k - rowStart) < TEXTBOX_CHARS_PER_ROW - ) k++; - - // IF that number is less than the remaining chars on the current row, - // then treat this space like a newline. - if(k >= (rowStart + TEXTBOX_CHARS_PER_ROW + 1)) { - stateFlags |= 1 << 0; - } - } else if(c == '\n') { - stateFlags |= 1 << 0; - } - - // Do we need to insert newline where we are currently? - if((stateFlags & (1 << 0)) != 0) { - stateFlags &= ~(1 << 0);// Remove newline flag - i++; - rowStart = i;// Update the row start (Should this be i+1?) - //TODO: can I optimize the next line by using rowStart somehow? - j = ((j / TEXTBOX_CHARS_PER_ROW)+1) * TEXTBOX_CHARS_PER_ROW;// Update destination character position. - TEXTBOX_ROW_COUNT++; - continue; - } - - // Insert the character - TEXTBOX_TEXTS[j] = c; - i++; - j++; - } - - // Now we have organized the string nicely we can prep for rendering. Fill the - // tiles with blank chars. - textboxFillBlank(); -} - -inline void textboxFillBlank() { - uint8_t TEXTBOX_TILES[TEXTBOX_WIDTH_IN_TILES * TEXTBOX_HEIGHT_IN_TILES]; - - frameBuffer(TEXTBOX_TILES, TEXTBOX_WIDTH_IN_TILES, TEXTBOX_HEIGHT_IN_TILES, TEXTBOX_TILE_BLANK); - - set_bkg_tiles( - 0x00, TEXTBOX_WIN_Y, - TEXTBOX_WIDTH_IN_TILES, TEXTBOX_HEIGHT_IN_TILES, - TEXTBOX_TILES - ); -} - -inline void conversationTextboxUpdate() { - uint8_t i; - char c; - uint8_t tiles[1]; - - // Is the textbox visible? - if((TEXTBOX_STATE & TEXTBOX_STATE_VISIBLE) == 0) return; - - // Have we finished scrolling? - if(TEXTBOX_STATE & TEXTBOX_STATE_SCROLLED) { - // Is the user trying to go to the next line? - if(INPUT_PRESSED & J_A) { - // First, lets figure out if there's any more text to reveal or not. - if((TEXTBOX_ROW_COUNT - TEXTBOX_ROW_CURRENT) < TEXTBOX_TILES_ROWS) { - TEXTBOX_STATE &= ~TEXTBOX_STATE_VISIBLE; - HIDE_WIN; - conversationQueueNext(); - return; - } - - TEXTBOX_STATE &= ~TEXTBOX_STATE_SCROLLED; - TEXTBOX_SCROLL = 0; - TEXTBOX_ROW_CURRENT += TEXTBOX_TILES_ROWS; - textboxFillBlank(); - } - return; - } - - // Move to the next character. - i = TEXTBOX_ROW_CURRENT * TEXTBOX_CHARS_PER_ROW; - c = TEXTBOX_TEXTS[i+TEXTBOX_SCROLL]; - - if(TEXTBOX_SCROLL == TEXTBOX_CHARS_MAX) { - TEXTBOX_STATE |= TEXTBOX_STATE_SCROLLED; - } else { - tiles[0] = c - TEXTBOX_FONT_START + FONT_DATA_POSITION; - - set_bkg_tiles( - 0x01 + (TEXTBOX_SCROLL % TEXTBOX_CHARS_PER_ROW), - TEXTBOX_WIN_Y + 0x01 + (TEXTBOX_SCROLL / TEXTBOX_CHARS_PER_ROW), - 1, 1, - tiles - ); - - TEXTBOX_SCROLL++; - - // Skip spaces - while(TEXTBOX_SCROLL < TEXTBOX_CHARS_MAX && TEXTBOX_TEXTS[i+TEXTBOX_SCROLL] == ' ') TEXTBOX_SCROLL++; - } +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "textbox.h" + +char TEXTBOX_TEXTS[TEXTBOX_SCROLL_ROWS_MAX * TEXTBOX_CHARS_PER_ROW]; +uint8_t TEXTBOX_ROW_COUNT; +uint8_t TEXTBOX_ROW_TILE_LAST; +uint8_t TEXTBOX_ROW_CURRENT; +uint8_t TEXTBOX_STATE; +uint8_t TEXTBOX_SCROLL; + +inline void conversationTextboxInit() { + // Reset textbox state + TEXTBOX_STATE = 0; +} + +void conversationTextboxSetText(char *text) { + uint8_t i, j, k, rowStart, stateFlags; + char c, c2; + + // Begin by filling the textbox text chars with whitespace to begin. + // TODO: I'm pretty sure I can move this lower and optimize. + for(i = 0; i < TEXTBOX_SCROLL_ROWS_MAX * TEXTBOX_CHARS_PER_ROW; i++) { + TEXTBOX_TEXTS[i] = ' '; + } + + // Reset textbox state + TEXTBOX_STATE = TEXTBOX_STATE_VISIBLE; + TEXTBOX_SCROLL = 0; + TEXTBOX_ROW_COUNT = 0; + TEXTBOX_ROW_COUNT = 1; + + // Copy source text to buffer, also determine wordwrapping here. + i = 0, j = 0, rowStart = 0, stateFlags = 0; + while((c = text[i]) != '\0') {// "For each char in string" + if(c == ' ') { + // Scan ahead and look at how many chars remain to the next space. + k = i + 1; + while( + (c2 = text[k]) != '\0' && + c2 != '\n' && + c2 != ' ' && + (k - rowStart) < TEXTBOX_CHARS_PER_ROW + ) k++; + + // IF that number is less than the remaining chars on the current row, + // then treat this space like a newline. + if(k >= (rowStart + TEXTBOX_CHARS_PER_ROW + 1)) { + stateFlags |= 1 << 0; + } + } else if(c == '\n') { + stateFlags |= 1 << 0; + } + + // Do we need to insert newline where we are currently? + if((stateFlags & (1 << 0)) != 0) { + stateFlags &= ~(1 << 0);// Remove newline flag + i++; + rowStart = i;// Update the row start (Should this be i+1?) + //TODO: can I optimize the next line by using rowStart somehow? + j = ((j / TEXTBOX_CHARS_PER_ROW)+1) * TEXTBOX_CHARS_PER_ROW;// Update destination character position. + TEXTBOX_ROW_COUNT++; + continue; + } + + // Insert the character + TEXTBOX_TEXTS[j] = c; + i++; + j++; + } + + // Now we have organized the string nicely we can prep for rendering. Fill the + // tiles with blank chars. + textboxFillBlank(); +} + +inline void textboxFillBlank() { + uint8_t tiles[TEXTBOX_WIDTH_IN_TILES * TEXTBOX_HEIGHT_IN_TILES]; + + spriteBorderBufferEdges( + tiles, TEXTBOX_WIDTH_IN_TILES, TEXTBOX_HEIGHT_IN_TILES, SPRITE_TILESET_WHITE + ); + + set_bkg_tiles( + 0x00, TEXTBOX_WIN_Y, + TEXTBOX_WIDTH_IN_TILES, TEXTBOX_HEIGHT_IN_TILES, + tiles + ); +} + +inline void conversationTextboxUpdate() { + uint8_t i, tile; + char c; + + // Is the textbox visible? + if((TEXTBOX_STATE & TEXTBOX_STATE_VISIBLE) == 0) return; + + // Have we finished scrolling? + if(TEXTBOX_STATE & TEXTBOX_STATE_SCROLLED) { + // Is the user trying to go to the next line? + if(INPUT_PRESSED & J_A) { + // First, lets figure out if there's any more text to reveal or not. + if((TEXTBOX_ROW_COUNT - TEXTBOX_ROW_CURRENT) < TEXTBOX_TILES_ROWS) { + TEXTBOX_STATE &= ~TEXTBOX_STATE_VISIBLE; + HIDE_WIN; + conversationQueueNext(); + return; + } + + TEXTBOX_STATE &= ~TEXTBOX_STATE_SCROLLED; + TEXTBOX_SCROLL = 0; + TEXTBOX_ROW_CURRENT += TEXTBOX_TILES_ROWS; + textboxFillBlank(); + } + return; + } + + // Move to the next character. + i = TEXTBOX_ROW_CURRENT * TEXTBOX_CHARS_PER_ROW; + c = TEXTBOX_TEXTS[i+TEXTBOX_SCROLL]; + + if(TEXTBOX_SCROLL == TEXTBOX_CHARS_MAX) { + TEXTBOX_STATE |= TEXTBOX_STATE_SCROLLED; + } else { + tile = spriteFontTileFromChar(c); + + set_bkg_tiles( + 0x01 + (TEXTBOX_SCROLL % TEXTBOX_CHARS_PER_ROW), + TEXTBOX_WIN_Y + 0x01 + (TEXTBOX_SCROLL / TEXTBOX_CHARS_PER_ROW), + 1, 1, + &tile + ); + + TEXTBOX_SCROLL++; + + // Skip spaces + while(TEXTBOX_SCROLL < TEXTBOX_CHARS_MAX && TEXTBOX_TEXTS[i+TEXTBOX_SCROLL] == ' ') TEXTBOX_SCROLL++; + } } \ No newline at end of file diff --git a/src/conversation/textbox.h b/src/conversation/textbox.h index f660f18..25b19df 100644 --- a/src/conversation/textbox.h +++ b/src/conversation/textbox.h @@ -1,49 +1,46 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "../util.h" -#include "../input.h" -#include "../display/common.h" -#include "../display/tilemap.h" -#include "queue.h" -#include "frame.h" - -#define TEXTBOX_STATE_VISIBLE (1 << 0) -#define TEXTBOX_STATE_SCROLLED (1 << 1) -#define TEXTBOX_STATE_IS_QUESTION (1 << 2) - -#define TEXTBOX_WIDTH_IN_TILES 20 -#define TEXTBOX_HEIGHT_IN_TILES 5 -#define TEXTBOX_TILES_MAX (TEXTBOX_WIDTH_IN_TILES * TEXTBOX_HEIGHT_IN_TILES) - -#define TEXTBOX_CHARS_PER_ROW (TEXTBOX_WIDTH_IN_TILES - 2) -#define TEXTBOX_CHAR_ROWS (TEXTBOX_HEIGHT_IN_TILES - 2) -#define TEXTBOX_CHARS_MAX (TEXTBOX_CHAR_ROWS * TEXTBOX_CHARS_PER_ROW) -#define TEXTBOX_BUFFER_MAX (TEXTBOX_CHARS_MAX * 3) - -#define TEXTBOX_TILES_PER_ROW TEXTBOX_WIDTH_IN_TILES -#define TEXTBOX_TILES_ROWS 3 -#define TEXTBOX_TILE_BLANK COMMON_TILE_3 - -#define TEXTBOX_SCROLL_ROWS_MAX 14 - -#define TEXTBOX_FONT_START 33 - -#define TEXTBOX_WIN_Y (18 - TEXTBOX_HEIGHT_IN_TILES) - -extern char TEXTBOX_TEXTS[TEXTBOX_SCROLL_ROWS_MAX * TEXTBOX_CHARS_PER_ROW]; -extern uint8_t TEXTBOX_ROW_COUNT; -extern uint8_t TEXTBOX_ROW_CURRENT; -extern uint8_t TEXTBOX_STATE; -extern uint8_t TEXTBOX_SCROLL; - -inline void conversationTextboxInit(); -void conversationTextboxSetText(char *text); -inline void textboxFillBlank(); +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "../util.h" +#include "../input.h" +#include "../sprites/spriteborder.h" +#include "../sprites/spritefont.h" +#include "../sprites/spritetileset.h" +#include "queue.h" + +#define TEXTBOX_STATE_VISIBLE (1 << 0) +#define TEXTBOX_STATE_SCROLLED (1 << 1) +#define TEXTBOX_STATE_IS_QUESTION (1 << 2) + +#define TEXTBOX_WIDTH_IN_TILES 20 +#define TEXTBOX_HEIGHT_IN_TILES 5 +#define TEXTBOX_TILES_MAX (TEXTBOX_WIDTH_IN_TILES * TEXTBOX_HEIGHT_IN_TILES) + +#define TEXTBOX_CHARS_PER_ROW (TEXTBOX_WIDTH_IN_TILES - 2) +#define TEXTBOX_CHAR_ROWS (TEXTBOX_HEIGHT_IN_TILES - 2) +#define TEXTBOX_CHARS_MAX (TEXTBOX_CHAR_ROWS * TEXTBOX_CHARS_PER_ROW) +#define TEXTBOX_BUFFER_MAX (TEXTBOX_CHARS_MAX * 3) + +#define TEXTBOX_TILES_PER_ROW TEXTBOX_WIDTH_IN_TILES +#define TEXTBOX_TILES_ROWS 3 + +#define TEXTBOX_SCROLL_ROWS_MAX 14 + +#define TEXTBOX_WIN_Y (18 - TEXTBOX_HEIGHT_IN_TILES) + +extern char TEXTBOX_TEXTS[TEXTBOX_SCROLL_ROWS_MAX * TEXTBOX_CHARS_PER_ROW]; +extern uint8_t TEXTBOX_ROW_COUNT; +extern uint8_t TEXTBOX_ROW_CURRENT; +extern uint8_t TEXTBOX_STATE; +extern uint8_t TEXTBOX_SCROLL; + +inline void conversationTextboxInit(); +void conversationTextboxSetText(char *text); +inline void textboxFillBlank(); inline void conversationTextboxUpdate(); \ No newline at end of file diff --git a/src/display/common.c b/src/display/common.c deleted file mode 100644 index c959b46..0000000 --- a/src/display/common.c +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "common.h" - -const uint8_t COMMON_TILES[] = { - 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, - 0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00, - 0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - -inline void commonTilesInit() { - set_bkg_data(0x00, COMMON_TILE_COUNT, COMMON_TILES); -} \ No newline at end of file diff --git a/src/display/common.h b/src/display/common.h deleted file mode 100644 index 99a2a69..0000000 --- a/src/display/common.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" - -#define COMMON_TILE_COUNT 0x04 - -#define COMMON_TILE_0 0x00 -#define COMMON_TILE_1 0x01 -#define COMMON_TILE_2 0x02 -#define COMMON_TILE_3 0x03 - -// Shades are mapped where each set of 4 colors is mapped to two bits that will -// specify its darkness. Higher = Darker, Lower = Brighter - // 11 11 11 11 -#define COMMON_SHADE_BLACK 0xFF -// 11 11 11 10 -#define COMMON_SHADE_DARKER 0xFE -// 11 11 10 01 -#define COMMON_SHADE_DARK 0xF9 -// 11 10 01 00 -#define COMMON_SHADE_NORMAL 0xE4 -// 10 01 00 00 -#define COMMON_SHADE_BRIGHT 0x90 -// 01 00 00 00 -#define COMMON_SHADE_BRIGHTER 0x40 -// 00 00 00 00 -#define COMMON_SHADE_WHITE 0x00 - -extern const uint8_t COMMON_TILES[]; - -inline void commonTilesInit(); \ No newline at end of file diff --git a/src/display/tilemap.h b/src/display/tilemap.h deleted file mode 100644 index d11227f..0000000 --- a/src/display/tilemap.h +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "FONT.h" -#include "BORDER.h" -// #include "SM.h" - -#define FONT_DATA_POSITION 0x80 -#define BORDER_DATA_POSITION FONT_DATA_POSITION+FONT_IMAGE_TILES -// #define SM_DATA_POSITION BORDER_DATA_POSITION+BORDER_IMAGE_TILES diff --git a/src/input.c b/src/input.c index 3f0274b..462b754 100644 --- a/src/input.c +++ b/src/input.c @@ -1,12 +1,12 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "input.h" - -uint8_t INPUT_STATE = 0x00; -uint8_t INPUT_PRESSED = 0x00; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "input.h" + +uint8_t INPUT_STATE = 0x00; +uint8_t INPUT_PRESSED = 0x00; uint8_t INPUT_LAST = 0x00; \ No newline at end of file diff --git a/src/input.h b/src/input.h index 7dd9e30..b57fe2d 100644 --- a/src/input.h +++ b/src/input.h @@ -1,13 +1,13 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "libs.h" - -extern uint8_t INPUT_STATE; -extern uint8_t INPUT_PRESSED; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "libs.h" + +extern uint8_t INPUT_STATE; +extern uint8_t INPUT_PRESSED; extern uint8_t INPUT_LAST; \ No newline at end of file diff --git a/src/libs.h b/src/libs.h index eaca791..2c60d0e 100644 --- a/src/libs.h +++ b/src/libs.h @@ -1,15 +1,15 @@ -/** - * Copyright (c) 2021 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include -#include -#include -#include -#include -#include +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include +#include +#include +#include +#include +#include #include \ No newline at end of file diff --git a/src/main.c b/src/main.c index c583511..55247e5 100644 --- a/src/main.c +++ b/src/main.c @@ -1,218 +1,80 @@ -/** - * Copyright (c) 2021 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "main.h" - -inline uint8_t mainGetChar(char c) { - return c - TEXTBOX_FONT_START + FONT_DATA_POSITION; -} - -inline void mainBufferCard(uint8_t card, uint8_t *tiles) { - uint8_t suit, number; - - if(card >= CARD_DECK_SIZE) { - tiles[0] = mainGetChar('N'); - tiles[1] = mainGetChar('A'); - return; - } - - suit = cardGetSuit(card); - number = cardGetNumber(card); - - switch(suit) { - case CARD_SUIT_CLUBS: - tiles[0] = mainGetChar('C'); - break; - case CARD_SUIT_DIAMONDS: - tiles[0] = mainGetChar('D'); - break; - case CARD_SUIT_HEARTS: - tiles[0] = mainGetChar('H'); - break; - case CARD_SUIT_SPADES: - tiles[0] = mainGetChar('S'); - break; - } - - switch(number) { - case CARD_TWO: - tiles[1] = mainGetChar('2'); - break; - case CARD_THREE: - tiles[1] = mainGetChar('3'); - break; - case CARD_FOUR: - tiles[1] = mainGetChar('4'); - break; - case CARD_FIVE: - tiles[1] = mainGetChar('5'); - break; - case CARD_SIX: - tiles[1] = mainGetChar('6'); - break; - case CARD_SEVEN: - tiles[1] = mainGetChar('7'); - break; - case CARD_EIGHT: - tiles[1] = mainGetChar('8'); - break; - case CARD_NINE: - tiles[1] = mainGetChar('9'); - break; - case CARD_TEN: - tiles[1] = mainGetChar('T'); - break; - case CARD_JACK: - tiles[1] = mainGetChar('J'); - break; - case CARD_QUEEN: - tiles[1] = mainGetChar('Q'); - break; - case CARD_KING: - tiles[1] = mainGetChar('K'); - break; - case CARD_ACE: - tiles[1] = mainGetChar('A'); - break; - } -} - -inline void mainDebugDraw() { - uint8_t j, i, n; - - // DEBUG DRAW - uint8_t tiles[15]; - char buffer[6]; - buffer[0] = '\0'; - - for(j = 0; j < POKER_PLAYER_COUNT; j++) { - n = 0; - - if(j == POKER_PLAYER_BETTER) { - tiles[n++] = mainGetChar('>'); - } else { - tiles[n++] = COMMON_TILE_3; - } - - mainBufferCard(POKER_PLAYERS[j].hand[0], tiles+n); - n+=2; - mainBufferCard(POKER_PLAYERS[j].hand[1], tiles+n); - n+=2; - - tiles[n++] = COMMON_TILE_3; - - if((POKER_PLAYERS[j].state & POKER_PLAYER_STATE_FOLDED) == 0) { - tiles[n++] = COMMON_TILE_3; - } else { - tiles[n++] = mainGetChar('F'); - } - - if((POKER_PLAYERS[j].state & POKER_PLAYER_STATE_HAS_BET_THIS_ROUND) == 0) { - tiles[n++] = COMMON_TILE_3; - } else { - tiles[n++] = mainGetChar('H'); - } - - if((POKER_PLAYERS[j].state & POKER_PLAYER_STATE_OUT) == 0) { - tiles[n++] = COMMON_TILE_3; - } else { - tiles[n++] = mainGetChar('O'); - } - - tiles[n++] = COMMON_TILE_3; - - // Print chips - sprintf(buffer, "%u", (uint16_t)POKER_PLAYERS[j].chips); - for(i = 0; i < strlen(buffer); i++) { - tiles[n++] = mainGetChar(buffer[i]); - } - - while(n < 15) tiles[n++] = COMMON_TILE_3; - set_bkg_tiles(0x00, j, n - 1, 1, tiles); - } - - for(j = 0; j < POKER_COMMUNITY_SIZE_MAX; j++) { - if(j >= POKER_COMMUNITY_SIZE) { - tiles[j*2] = COMMON_TILE_3; - tiles[(j*2) + 1] = COMMON_TILE_3; - } else { - mainBufferCard(POKER_COMMUNITY[j], tiles + (j * 2)); - } - } - - set_bkg_tiles(0x00, POKER_PLAYER_COUNT + 1, POKER_COMMUNITY_SIZE_MAX * 2, 1, tiles); - - - // Print Pot - n = 0; - sprintf(buffer, "%u", (uint16_t)POKER_POTS[POKER_POT_CURRENT].chips); - for(i = 0; i < strlen(buffer); i++) { - tiles[n++] = mainGetChar(buffer[i]); - } - while(n < 5) tiles[n++] = COMMON_TILE_3; - set_bkg_tiles(0x00, POKER_PLAYER_COUNT + 2, n, 1, tiles); -} - -void main() { - int16_t j; - uint8_t filled[GB_BACKGROUND_COLUMNS*GB_BACKGROUND_ROWS]; - - // Set up the GAMEBOY's registers. - disable_interrupts(); - DISPLAY_OFF; - LCDC_REG = LCDCF_OFF | LCDCF_WIN9C00 | LCDCF_BG8800 | LCDCF_BG9800 | LCDCF_BGON; - // Set the background color palette register - BGP_REG = OBP0_REG = OBP1_REG = 0xE4U; - - // Init the random seed - initarand(DIV_REG); - - // Init things - commonTilesInit(); - conversationTextboxInit(); - conversationQueueInit(); - pokerInit(); - - // Fill screen white - for(j = 0; j < GB_BACKGROUND_COLUMNS * GB_BACKGROUND_ROWS; j++) filled[j] = COMMON_TILE_3; - set_bkg_tiles(0x00, 0x00, GB_BACKGROUND_COLUMNS, GB_BACKGROUND_ROWS, filled); - SCX_REG = 0x00; - SCY_REG = 0x00; - - // Now turn the screen on - DISPLAY_ON; - enable_interrupts(); - wait_vbl_done(); - - // Begin game - conversationQueueNext(); - - // Begin the loop - while(1) { - // Perform non-graphical code updates - wait_vbl_done(); - - // Update the input state - INPUT_LAST = INPUT_STATE; - INPUT_STATE = joypad(); - INPUT_PRESSED = (~INPUT_LAST) & INPUT_STATE; - - // Tick time. - timeUpdate(); - - // Update conversation pause effect - conversationPauseUpdate(); - - // Update question box and textbox - questionBoxUpdate(); - conversationTextboxUpdate(); - - // Update conversation fade effect - conversationFadeUpdate(); - mainDebugDraw(); - } +/** + * Copyright (c) 2021 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "main.h" + +void main() { + int16_t j; + uint8_t filled[GB_BACKGROUND_COLUMNS*GB_BACKGROUND_ROWS]; + + // Set up the GAMEBOY's registers. + disable_interrupts(); + DISPLAY_OFF; + LCDC_REG = LCDCF_OFF | LCDCF_BG8000 | LCDCF_BG9800 | LCDCF_BGON; + + // Set the background color palette register + BGP_REG = OBP0_REG = OBP1_REG = 0xE4U; + + // Init the random seed + initarand(DIV_REG); + + // Init things + spriteTilesetBuffer(); + spriteFontBuffer(); + spriteBorderBuffer(); + spriteCardsBuffer(); + + conversationTextboxInit(); + conversationQueueInit(); + pokerInit(); + + // Fill screen white + for(j = 0; j < GB_BACKGROUND_COLUMNS * GB_BACKGROUND_ROWS; j++) { + filled[j] = SPRITE_TILESET_WHITE; + } + set_bkg_tiles(0x00, 0x00, GB_BACKGROUND_COLUMNS, GB_BACKGROUND_ROWS, filled); + SCX_REG = 0x00; + SCY_REG = 0x00; + + // Card Test + uint8_t cardTiles[SPRITE_CARD_TILE_COUNT]; + spriteCardBufferTiles(cardTiles, CARD_HEARTS_TWO); + set_bkg_tiles(0x00, 0x00, SPRITE_CARD_WIDTH, SPRITE_CARD_HEIGHT, cardTiles); + + // Now turn the screen on + DISPLAY_ON; + enable_interrupts(); + wait_vbl_done(); + + // Begin game + conversationQueueNext(); + + // Begin the loop + while(1) { + // Perform non-graphical code updates + wait_vbl_done(); + + // Update the input state + INPUT_LAST = INPUT_STATE; + INPUT_STATE = joypad(); + INPUT_PRESSED = (~INPUT_LAST) & INPUT_STATE; + + // Tick time. + timeUpdate(); + + // Update conversation pause effect + conversationPauseUpdate(); + + // Update question box and textbox + questionBoxUpdate(); + conversationTextboxUpdate(); + + // Update conversation fade effect + conversationFadeUpdate(); + // mainDebugDraw(); + } } \ No newline at end of file diff --git a/src/main.h b/src/main.h index 1fc3597..80b6fd8 100644 --- a/src/main.h +++ b/src/main.h @@ -1,28 +1,28 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once - -#include "libs.h" -#include "time.h" - -#include "SM.h" - -#include "display/common.h" -#include "display/tilemap.h" -#include "poker/poker.h" -#include "poker/card.h" -#include "conversation/fade.h" -#include "conversation/pause.h" -#include "conversation/queue.h" -#include "conversation/textbox.h" -#include "conversation/questionbox.h" - -#define GB_BACKGROUND_COLUMNS 0x20 -#define GB_BACKGROUND_ROWS 0x20 - +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once + +#include "libs.h" +#include "time.h" + +#include "SM.h" + +#include "poker/poker.h" +#include "poker/card.h" +#include "conversation/fade.h" +#include "conversation/pause.h" +#include "conversation/queue.h" +#include "conversation/textbox.h" +#include "conversation/questionbox.h" + +#include "sprites/spritecards.h" + +#define GB_BACKGROUND_COLUMNS 0x20 +#define GB_BACKGROUND_ROWS 0x20 + void main(); \ No newline at end of file diff --git a/src/poker/card.c b/src/poker/card.c index 813f134..b10cec3 100644 --- a/src/poker/card.c +++ b/src/poker/card.c @@ -1,50 +1,50 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "card.h" - -inline uint8_t cardGetNumber(uint8_t card) { - return card % CARD_COUNT_PER_SUIT; -} - -inline uint8_t cardGetSuit(uint8_t card) { - return card / CARD_COUNT_PER_SUIT; -} - -inline uint8_t cardGet(uint8_t number, uint8_t suit) { - return (suit * CARD_COUNT_PER_SUIT) + number; -} - -inline uint8_t cardContains(uint8_t *hand, uint8_t length, uint8_t card) { - uint8_t i; - for(i = 0; i < length; i++) { - if(hand[i] == card) return i; - } - return 0xFF; -} - -inline uint8_t cardContainsNumber(uint8_t *hand,uint8_t length,uint8_t number) { - uint8_t i; - for(i = 0; i < length; i++) { - if(cardGetNumber(hand[i]) == number) return i; - } - return 0xFF; -} - -inline uint8_t cardCountPairs( - uint8_t *in, uint8_t inCount, uint8_t number, uint8_t out[CARD_SUIT_COUNT] -) { - uint8_t i, count; - - count = 0; - for(i = 0; i < inCount; i++) {// "For each suit" - if(cardGetNumber(in[i]) != number) continue; - out[count++] = i; - } - - return count; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "card.h" + +inline uint8_t cardGetNumber(uint8_t card) { + return card % CARD_COUNT_PER_SUIT; +} + +inline uint8_t cardGetSuit(uint8_t card) { + return card / CARD_COUNT_PER_SUIT; +} + +inline uint8_t cardGet(uint8_t number, uint8_t suit) { + return (suit * CARD_COUNT_PER_SUIT) + number; +} + +inline uint8_t cardContains(uint8_t *hand, uint8_t length, uint8_t card) { + uint8_t i; + for(i = 0; i < length; i++) { + if(hand[i] == card) return i; + } + return 0xFF; +} + +inline uint8_t cardContainsNumber(uint8_t *hand,uint8_t length,uint8_t number) { + uint8_t i; + for(i = 0; i < length; i++) { + if(cardGetNumber(hand[i]) == number) return i; + } + return 0xFF; +} + +inline uint8_t cardCountPairs( + uint8_t *in, uint8_t inCount, uint8_t number, uint8_t out[CARD_SUIT_COUNT] +) { + uint8_t i, count; + + count = 0; + for(i = 0; i < inCount; i++) {// "For each suit" + if(cardGetNumber(in[i]) != number) continue; + out[count++] = i; + } + + return count; } \ No newline at end of file diff --git a/src/poker/card.h b/src/poker/card.h index 9dedc49..c095a91 100644 --- a/src/poker/card.h +++ b/src/poker/card.h @@ -1,117 +1,117 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "../util.h" - -//////////////////////////////////////////////////////////////////////////////// -// Cards -//////////////////////////////////////////////////////////////////////////////// - -// Aces -#define CARD_CLUBS_TWO 0x00 -#define CARD_CLUBS_THREE 0x01 -#define CARD_CLUBS_FOUR 0x02 -#define CARD_CLUBS_FIVE 0x03 -#define CARD_CLUBS_SIX 0x04 -#define CARD_CLUBS_SEVEN 0x05 -#define CARD_CLUBS_EIGHT 0x06 -#define CARD_CLUBS_NINE 0x07 -#define CARD_CLUBS_TEN 0x08 -#define CARD_CLUBS_JACK 0x09 -#define CARD_CLUBS_QUEEN 0x0A -#define CARD_CLUBS_KING 0x0B -#define CARD_CLUBS_ACE 0x0C - -// Diamonds -#define CARD_DIAMONDS_TWO 0x0D -#define CARD_DIAMONDS_THREE 0x0E -#define CARD_DIAMONDS_FOUR 0x0F -#define CARD_DIAMONDS_FIVE 0x10 -#define CARD_DIAMONDS_SIX 0x11 -#define CARD_DIAMONDS_SEVEN 0x12 -#define CARD_DIAMONDS_EIGHT 0x13 -#define CARD_DIAMONDS_NINE 0x14 -#define CARD_DIAMONDS_TEN 0x15 -#define CARD_DIAMONDS_JACK 0x16 -#define CARD_DIAMONDS_QUEEN 0x17 -#define CARD_DIAMONDS_KING 0x18 -#define CARD_DIAMONDS_ACE 0x19 - -// Hearts -#define CARD_HEARTS_TWO 0x1A -#define CARD_HEARTS_THREE 0x1B -#define CARD_HEARTS_FOUR 0x1C -#define CARD_HEARTS_FIVE 0x1D -#define CARD_HEARTS_SIX 0x1E -#define CARD_HEARTS_SEVEN 0x1F -#define CARD_HEARTS_EIGHT 0x20 -#define CARD_HEARTS_NINE 0x21 -#define CARD_HEARTS_TEN 0x22 -#define CARD_HEARTS_JACK 0x23 -#define CARD_HEARTS_QUEEN 0x24 -#define CARD_HEARTS_KING 0x25 -#define CARD_HEARTS_ACE 0x26 - -// Spades -#define CARD_SPADES_TWO 0x27 -#define CARD_SPADES_THREE 0x28 -#define CARD_SPADES_FOUR 0x29 -#define CARD_SPADES_FIVE 0x2A -#define CARD_SPADES_SIX 0x2B -#define CARD_SPADES_SEVEN 0x2C -#define CARD_SPADES_EIGHT 0x2D -#define CARD_SPADES_NINE 0x2E -#define CARD_SPADES_TEN 0x2F -#define CARD_SPADES_JACK 0x30 -#define CARD_SPADES_QUEEN 0x31 -#define CARD_SPADES_KING 0x32 -#define CARD_SPADES_ACE 0x33 - -//////////////////////////////////////////////////////////////////////////////// -// Suits -//////////////////////////////////////////////////////////////////////////////// -#define CARD_SUIT_CLUBS 0x00 -#define CARD_SUIT_DIAMONDS 0x01 -#define CARD_SUIT_HEARTS 0x02 -#define CARD_SUIT_SPADES 0x03 - -//////////////////////////////////////////////////////////////////////////////// -// Card numbers -//////////////////////////////////////////////////////////////////////////////// -#define CARD_TWO 0x00 -#define CARD_THREE 0x01 -#define CARD_FOUR 0x02 -#define CARD_FIVE 0x03 -#define CARD_SIX 0x04 -#define CARD_SEVEN 0x05 -#define CARD_EIGHT 0x06 -#define CARD_NINE 0x07 -#define CARD_TEN 0x08 -#define CARD_JACK 0x09 -#define CARD_QUEEN 0x0A -#define CARD_KING 0x0B -#define CARD_ACE 0x0C - -/** Count of cards in each suit */ -#define CARD_COUNT_PER_SUIT 13 - -/** Count of suits */ -#define CARD_SUIT_COUNT 4 - -/** Standard Card Deck Size */ -#define CARD_DECK_SIZE 52 - -inline uint8_t cardGetNumber(uint8_t card); -inline uint8_t cardGetSuit(uint8_t card); -inline uint8_t cardGet(uint8_t card, uint8_t suit); -inline uint8_t cardContains(uint8_t *hand, uint8_t length, uint8_t card); -inline uint8_t cardContainsNumber(uint8_t *hand,uint8_t length,uint8_t number); -inline uint8_t cardCountPairs( - uint8_t *in, uint8_t inCount, uint8_t number, uint8_t out[CARD_SUIT_COUNT] +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "../util.h" + +//////////////////////////////////////////////////////////////////////////////// +// Cards +//////////////////////////////////////////////////////////////////////////////// + +// Aces +#define CARD_CLUBS_TWO 0x00 +#define CARD_CLUBS_THREE 0x01 +#define CARD_CLUBS_FOUR 0x02 +#define CARD_CLUBS_FIVE 0x03 +#define CARD_CLUBS_SIX 0x04 +#define CARD_CLUBS_SEVEN 0x05 +#define CARD_CLUBS_EIGHT 0x06 +#define CARD_CLUBS_NINE 0x07 +#define CARD_CLUBS_TEN 0x08 +#define CARD_CLUBS_JACK 0x09 +#define CARD_CLUBS_QUEEN 0x0A +#define CARD_CLUBS_KING 0x0B +#define CARD_CLUBS_ACE 0x0C + +// Diamonds +#define CARD_DIAMONDS_TWO 0x0D +#define CARD_DIAMONDS_THREE 0x0E +#define CARD_DIAMONDS_FOUR 0x0F +#define CARD_DIAMONDS_FIVE 0x10 +#define CARD_DIAMONDS_SIX 0x11 +#define CARD_DIAMONDS_SEVEN 0x12 +#define CARD_DIAMONDS_EIGHT 0x13 +#define CARD_DIAMONDS_NINE 0x14 +#define CARD_DIAMONDS_TEN 0x15 +#define CARD_DIAMONDS_JACK 0x16 +#define CARD_DIAMONDS_QUEEN 0x17 +#define CARD_DIAMONDS_KING 0x18 +#define CARD_DIAMONDS_ACE 0x19 + +// Hearts +#define CARD_HEARTS_TWO 0x1A +#define CARD_HEARTS_THREE 0x1B +#define CARD_HEARTS_FOUR 0x1C +#define CARD_HEARTS_FIVE 0x1D +#define CARD_HEARTS_SIX 0x1E +#define CARD_HEARTS_SEVEN 0x1F +#define CARD_HEARTS_EIGHT 0x20 +#define CARD_HEARTS_NINE 0x21 +#define CARD_HEARTS_TEN 0x22 +#define CARD_HEARTS_JACK 0x23 +#define CARD_HEARTS_QUEEN 0x24 +#define CARD_HEARTS_KING 0x25 +#define CARD_HEARTS_ACE 0x26 + +// Spades +#define CARD_SPADES_TWO 0x27 +#define CARD_SPADES_THREE 0x28 +#define CARD_SPADES_FOUR 0x29 +#define CARD_SPADES_FIVE 0x2A +#define CARD_SPADES_SIX 0x2B +#define CARD_SPADES_SEVEN 0x2C +#define CARD_SPADES_EIGHT 0x2D +#define CARD_SPADES_NINE 0x2E +#define CARD_SPADES_TEN 0x2F +#define CARD_SPADES_JACK 0x30 +#define CARD_SPADES_QUEEN 0x31 +#define CARD_SPADES_KING 0x32 +#define CARD_SPADES_ACE 0x33 + +//////////////////////////////////////////////////////////////////////////////// +// Suits +//////////////////////////////////////////////////////////////////////////////// +#define CARD_SUIT_CLUBS 0x00 +#define CARD_SUIT_DIAMONDS 0x01 +#define CARD_SUIT_HEARTS 0x02 +#define CARD_SUIT_SPADES 0x03 + +//////////////////////////////////////////////////////////////////////////////// +// Card numbers +//////////////////////////////////////////////////////////////////////////////// +#define CARD_TWO 0x00 +#define CARD_THREE 0x01 +#define CARD_FOUR 0x02 +#define CARD_FIVE 0x03 +#define CARD_SIX 0x04 +#define CARD_SEVEN 0x05 +#define CARD_EIGHT 0x06 +#define CARD_NINE 0x07 +#define CARD_TEN 0x08 +#define CARD_JACK 0x09 +#define CARD_QUEEN 0x0A +#define CARD_KING 0x0B +#define CARD_ACE 0x0C + +/** Count of cards in each suit */ +#define CARD_COUNT_PER_SUIT 13 + +/** Count of suits */ +#define CARD_SUIT_COUNT 4 + +/** Standard Card Deck Size */ +#define CARD_DECK_SIZE 52 + +inline uint8_t cardGetNumber(uint8_t card); +inline uint8_t cardGetSuit(uint8_t card); +inline uint8_t cardGet(uint8_t card, uint8_t suit); +inline uint8_t cardContains(uint8_t *hand, uint8_t length, uint8_t card); +inline uint8_t cardContainsNumber(uint8_t *hand,uint8_t length,uint8_t number); +inline uint8_t cardCountPairs( + uint8_t *in, uint8_t inCount, uint8_t number, uint8_t out[CARD_SUIT_COUNT] ); \ No newline at end of file diff --git a/src/poker/player.h b/src/poker/player.h index f8734e8..f2989bd 100644 --- a/src/poker/player.h +++ b/src/poker/player.h @@ -1,28 +1,28 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "card.h" - -#define POKER_PLAYER_COUNT_MAX 5 -#define POKER_PLAYER_HAND_SIZE_MAX 2 - -#define POKER_PLAYER_STATE_FOLDED (1 << 0) -#define POKER_PLAYER_STATE_OUT (1 << 1) -#define POKER_PLAYER_STATE_HAS_BET_THIS_ROUND (1 << 2) -// #define POKER_PLAYER_STATE_SHOWING 1 << 3 - -typedef struct { - uint16_t chips; - uint8_t hand[POKER_PLAYER_HAND_SIZE_MAX]; - uint8_t state; - uint8_t timesRaised; -} pokerplayer_t; - -extern pokerplayer_t POKER_PLAYERS[]; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "card.h" + +#define POKER_PLAYER_COUNT_MAX 5 +#define POKER_PLAYER_HAND_SIZE_MAX 2 + +#define POKER_PLAYER_STATE_FOLDED (1 << 0) +#define POKER_PLAYER_STATE_OUT (1 << 1) +#define POKER_PLAYER_STATE_HAS_BET_THIS_ROUND (1 << 2) +// #define POKER_PLAYER_STATE_SHOWING 1 << 3 + +typedef struct { + uint16_t chips; + uint8_t hand[POKER_PLAYER_HAND_SIZE_MAX]; + uint8_t state; + uint8_t timesRaised; +} pokerplayer_t; + +extern pokerplayer_t POKER_PLAYERS[]; extern uint8_t POKER_PLAYER_COUNT; \ No newline at end of file diff --git a/src/poker/poker.c b/src/poker/poker.c index be098ba..50efe59 100644 --- a/src/poker/poker.c +++ b/src/poker/poker.c @@ -1,789 +1,789 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "poker.h" - -pokerplayer_t POKER_PLAYERS[POKER_PLAYER_COUNT_MAX]; - -uint8_t POKER_DECK[CARD_DECK_SIZE]; -uint8_t POKER_DECK_SIZE; -uint8_t POKER_COMMUNITY[POKER_COMMUNITY_SIZE_MAX]; -uint8_t POKER_COMMUNITY_SIZE; - -uint8_t POKER_PLAYER_DEALER; -uint8_t POKER_PLAYER_SMALL_BLIND; -uint8_t POKER_PLAYER_BIG_BLIND; -uint8_t POKER_PLAYER_BETTER; - -uint16_t POKER_GAME_BLINDS_CURRENT; - -pokerpot_t POKER_POTS[POKER_POT_COUNT_MAX]; -uint8_t POKER_POT_CURRENT; -uint8_t POKER_POT_COUNT; - -pokerplayerwinning_t POKER_WINNERS[POKER_PLAYER_COUNT_MAX]; -uint8_t POKER_WINNER_PLAYERS[POKER_PLAYER_COUNT_MAX]; -uint8_t POKER_WINNER_PARTICIPANTS[POKER_PLAYER_COUNT_MAX]; -uint8_t POKER_WINNER_COUNT; -uint8_t POKER_WINNER_PARTICIPANT_COUNT; -uint8_t POKER_PLAYER_COUNT; - -void pokerInit() { - uint8_t i; - - // Set up the initial state. - // TODO: Should this be randomized? - POKER_PLAYER_DEALER = 0; - POKER_GAME_BLINDS_CURRENT = 10; - POKER_PLAYER_COUNT = POKER_PLAYER_COUNT_MAX; - - // Set up players - for(i = 0; i < POKER_PLAYER_COUNT; i++) { - POKER_PLAYERS[i].chips = 10000; - POKER_PLAYERS[i].state = 0; - } - - // Reset the round state (For the first round) - pokerNewRound(); -} - -void pokerNewRound() { - uint8_t i, j, k; - uint8_t found; - pokerplayer_t *player; - - // Reset round state - POKER_COMMUNITY_SIZE = 0; - POKER_POT_COUNT = 0; - POKER_POT_CURRENT = 0; - - // Reset the pots. - for(i = 0; i < POKER_POT_COUNT_MAX; i++) { - POKER_POTS[i].chips = 0; - POKER_POTS[i].call = 0; - for(j = 0; j < POKER_PLAYER_COUNT; j++) { - POKER_POTS[i].players[j] = 0; - } - } - - // Fill deck - for(i = 0; i < CARD_DECK_SIZE; i++) POKER_DECK[i] = i; - POKER_DECK_SIZE = CARD_DECK_SIZE; - - // Shuffle Deck - for(i = POKER_DECK_SIZE-1; i > 0; i--) { - k = rand(); - j = k % (i+1); - k = POKER_DECK[i]; - POKER_DECK[i] = POKER_DECK[j]; - POKER_DECK[j] = k; - } - - // Reset Players and decide new blinds. - found = 0; - POKER_PLAYER_DEALER++; - - // Update players for the round. - for(i = 0; i < POKER_PLAYER_COUNT; i++) { - player = POKER_PLAYERS + i; - player->timesRaised = 0; - player->state &= ~( - POKER_PLAYER_STATE_FOLDED | - POKER_PLAYER_STATE_HAS_BET_THIS_ROUND - ); - - // Update out state. - if(player->chips == 0) player->state |= POKER_PLAYER_STATE_OUT; - - // Is the player out? - if((player->state & POKER_PLAYER_STATE_OUT) != 0) continue; - - // Have we found the dealer, small blind, and big blind players? - if(found != 3) { - k = (POKER_PLAYER_DEALER + i) % POKER_PLAYER_COUNT; - - if(found == 0) {// Have we found the dealer? - POKER_PLAYER_DEALER = i; - found++; - } else if(found == 1) {// Have we found the small blind? - POKER_PLAYER_SMALL_BLIND = i; - found++; - } else if(found == 2) {// Have we found the big blind? - POKER_PLAYER_BIG_BLIND = i; - found++; - } - } - - // Deal two cards to the player. - for(j = 0; j < POKER_PLAYER_HAND_SIZE_MAX; j++) { - player->hand[j] = POKER_DECK[--POKER_DECK_SIZE]; - } - } - - // Take blinds - // TODO: I need to make sure the blind players even have the chips to blind. - pokerBet(POKER_PLAYER_SMALL_BLIND, POKER_GAME_BLINDS_CURRENT); - pokerBet(POKER_PLAYER_BIG_BLIND, (POKER_GAME_BLINDS_CURRENT*2)); - - // Set the initial better, we set this to the BIG BLIND player because we will - // cycle to the "next better" as soon as the game starts. - POKER_PLAYER_BETTER = POKER_PLAYER_BIG_BLIND; -} - -inline void pokerBet(uint8_t player, uint16_t amount) { - // TODO: This may become a function because if a player doesn't have enough - // chips to bet to the active pot, then the pot needs to autosplit, take those - // who have bet into the pot up to the amount that the player betting can bet, - // and push them into a new pot. - // There also needs to be a limit on this, for example; - // player 0 has $1200, and bets $1000, they can't bet more than that ever. - // player 1 has $1000, and bets all of it. The remanin - // player 2 has $700, and bets all o it. A new $300 sidepot auto creates - // player 3 has $500 and bets all of it, Another sidepot with $200 is auto made. - if(POKER_POT_CURRENT == POKER_POT_COUNT) POKER_POT_COUNT++; - - POKER_PLAYERS[player].chips -= amount; - POKER_POTS[POKER_POT_CURRENT].chips += amount; - POKER_POTS[POKER_POT_CURRENT].players[player] += amount; - POKER_POTS[POKER_POT_CURRENT].call = MATH_MAX( - amount, POKER_POTS[POKER_POT_CURRENT].players[player] - ); -} - -inline uint8_t pokerGetCallBet(uint8_t player) { - return ( - POKER_POTS[POKER_POT_CURRENT].call - - POKER_POTS[POKER_POT_CURRENT].players[player] - ); -} - -void pokerAi(uint8_t player, pokerturn_t *turn) { - uint8_t i, cardNumber0, cardNumber1, suitNumber0, suitNumber1; - uint16_t callBet, maxBet, amount, bluffBet; - uint8_t random;// TODO: Determine type. - uint16_t confidence, expectedGain, potOdds; - pokerplayerwinning_t winning; - pokerplayer_t *plyr = POKER_PLAYERS + player; - - // The following logic is heavily inspired by; - // https://github.com/gorel/C-Poker-AI/blob/master/src/common/pokerai.c - // But with some changes and smarts added by me. The original source code will - // essentially just run a crap tun of simulated games and get the times that - // they are expected to win from those games, but I'm just going to use the - // odds of the hand being the winning hand. - - // Is this preflop? - if(POKER_COMMUNITY_SIZE == 0) { - // Get the hand weight - cardNumber0 = cardGetNumber(plyr->hand[0]); - cardNumber1 = cardGetNumber(plyr->hand[1]); - suitNumber0 = cardGetSuit(plyr->hand[0]); - suitNumber1 = cardGetSuit(plyr->hand[1]); - - // Get delta between cards - i = MATH_ABS(cardNumber0 - cardNumber1); - - // Get card weight - confidence = cardNumber0 + cardNumber1; - if(cardNumber0 == cardNumber1) {// Pairs - confidence += 6; - } else if(suitNumber0 == suitNumber1) {// Same suit - confidence += 4; - } - - // Get difference from cards for guessing flush - if(i > 4) { - confidence -= 4; - } else if(i > 2) { - confidence -= i; - } - - // Confidence is now a value between 0-30 (inclusive). Multiply by 1000 - confidence = (confidence * 1000) / 30; - // Now do over 30 to get the value represented as 0-1000 - } else { - // Simulate my hand being the winning hand, use that as the confidence - // TODO: Repurpose old code lmao. Just take it from Dawn-C - BGB_printf("Holy shit you guys"); - pokerWinnerGetForPlayer(player, &winning); - BGB_printf("Winning type %u", winning.type); - confidence = pokerWinnerGetTypeConfidence(winning.type); - BGB_printf("Confidence %u", confidence); - } - - // Now we know how confident the AI is, let's put a chip value to that weight - // How many chips to call? - callBet = pokerGetCallBet(player); - BGB_printf("Callbet %u", callBet); - - - // Do they need chips to call, or is it possible to check? - if(callBet > 0) { - // Work out how many chips the player could possibly win. This is a number - // between 0 and player count * initial chips, at the time of writing that - // is around 50,000. - expectedGain = 0; - for(i = 0; i < POKER_POT_COUNT; i++) { - if(POKER_POTS[i].players[player] == 0) break; - expectedGain += POKER_POTS[i].chips; - } - - - // Now work out the pot odds, this is basically "how many times the callbet - // could I win if I win this hand". e.g. Desirable hands will have an - // expected gain much higher than the callbet. - potOdds = plyr->chips / 1000; - potOdds = MATH_MAX((callBet + expectedGain), 1) / MATH_MAX(potOdds, 1); - - BGB_printf("Expected Gain and Pot Odds %u, %u", expectedGain, potOdds); - } else { - // If the call bet is zero then there's fairly equal odds, so let's just - // take the chances out of the remainig player count. - potOdds = 1000 / pokerGetRemainingBetterCount() * 2;// 0 - 1000 - BGB_printf("Pot Odds %u", potOdds); - } - - // Now determine the expected ROI - //TODO: I think these values are a bit odd. - expectedGain = (confidence*100) / (potOdds / 10); - BGB_printf("Expected Gains (2) %u", expectedGain); - - // Now get a random number 0-100. - random = rand(); - random = random % 100; - - // Determine the max bet that the AI is willing to make. The max bet is - // basically how high the AI is willing to bet. - maxBet = plyr->chips;// TODO: Replace with below code and test, just running - // With this for now. - // maxBet = plyr->chips / MATH_MAX(random / 10, 1); - // maxBet -= callBet; - // BGB_printf("Rand %u, Max Bet %u", random, callBet); - - // Determine what's a good bluff bet. - // TODO: not float - bluffBet = maxBet; - // bluffBet = ((random * 100) / maxBet) * plyr->chips; - - // Now prep the output - turn->bluff = false; - amount = 0; - - BGB_printf("Random Dice %u", random); - - - // Now the actual AI can happen. This is basically a weight to confidence - // ratio. The higher the gains and the confidence then the more likely the AI - // is to betting. There are also bluff chances within here. - if(expectedGain < 800 && confidence < 800) { - if(random < 95) { - amount = 0; - } else { - amount = bluffBet; - turn->bluff = true; - } - } else if ((expectedGain < 1000 && confidence < 850) || confidence < 100) { - if (random < 80) { - amount = 0; - } else if(random < 5) { - amount = callBet; - turn->bluff = true; - } else { - amount = bluffBet; - turn->bluff = true; - } - } else if ((expectedGain < 1300 && confidence < 900) || confidence < 500) { - if (random < 60 || confidence < 500) { - amount = callBet; - } else { - amount = maxBet; - } - } else if (confidence < 950 || POKER_COMMUNITY_SIZE < 0x04) { - if(random < 20) { - amount = callBet; - } else { - amount = maxBet; - } - } else { - // TODO: check this - amount = (plyr->chips - callBet) * 9 / 10; - } - - // If this is the first round... make it a lot less likely I'll bet - if(POKER_COMMUNITY_SIZE == 0x00 && amount > callBet) { - if(random > 5) amount = callBet; - } - - - // Did we actually bet? - if(amount > 0) { - // Let's not get caught in a raising loop with AI. - if(plyr->timesRaised >= POKER_TURN_MAX_RAISES) amount = callBet; - amount = MATH_MAX(amount, callBet); - amount = MATH_MIN(amount, plyr->chips); - turn->chips = amount; - turn->confidence = confidence; - - if(plyr->chips == amount) { - turn->type = POKER_TURN_TYPE_ALL_IN; - } else if(amount == callBet) { - turn->type = POKER_TURN_TYPE_CALL; - } else { - turn->type = POKER_TURN_TYPE_BET; - } - } else if(pokerCanPlayerCheck(player)) { - turn->type = POKER_TURN_TYPE_CHECK; - turn->chips = 0; - turn->confidence = 1000; - } else { - turn->type = POKER_TURN_TYPE_FOLD; - turn->chips = 0; - turn->confidence = 1000 - confidence; - } -} - -inline bool pokerCanPlayerCheck(uint8_t player) { - return ( - POKER_POTS[POKER_POT_CURRENT].players[player] == - POKER_POTS[POKER_POT_CURRENT].call - ); -} - -inline bool pokerDoesPlayerNeedToBet(uint8_t playerIndex) { - uint8_t i; - pokerplayer_t *player = POKER_PLAYERS + playerIndex; - - // Can this player even participate? - if( - (player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) != 0 || - player->chips == 0 - ) { - return false; - } - - // Has the player bet? If so are they in the current pot? - if((player->state & POKER_PLAYER_STATE_HAS_BET_THIS_ROUND) == 0) { - return true; - } - - //TODO: Refer to pokerbet function, but basically I can't let the player - //bet if they have bet more money than the second richest player. - - // Check each pot, if the pot is inactive or the player is CALLED/CHECKED - for(i = 0; i < POKER_POT_COUNT_MAX; i++) { - // Is this pot active? - if(POKER_POTS[i].chips == 0) break; - - // Is the player called into this pot all the way? - if(POKER_POTS[i].players[playerIndex] == POKER_POTS[i].call) continue; - return true; - } - - return false; -} - -inline uint8_t pokerGetRemainingBetterCount() { - uint8_t i, count; - count = 0; - for(i = 0 ; i < POKER_PLAYER_COUNT; i++) { - if(pokerDoesPlayerNeedToBet(i)) count++; - } - return count; -} - -void pokerWinnerFillRemaining(pokerplayerwinning_t *winning) { - uint8_t i, highest, current, highestCard, currentCard; - - // Set the kicker - winning->kicker = 0xFF; - - // Fill the remaining cards - while(winning->setSize < POKER_WINNING_SET_SIZE) { - highest = 0xFF; - - for(i = 0; i < winning->fullSize; i++) { - currentCard = winning->full[i]; - if(cardContains(winning->set, winning->setSize, currentCard) != 0xFF) { - continue; - } - - if(highest == 0xFF) { - highestCard = currentCard; - highest = cardGetNumber(highestCard); - } else { - current = cardGetNumber(currentCard); - if(current != CARD_ACE && current < highest) continue; - highestCard = currentCard; - highest = current; - } - } - - if(highest == 0xFF) break; - winning->set[winning->setSize++] = highestCard; - } - // cardHandSort(winning->set, winning->setSize); -} - -void pokerWinnerGetForPlayer(uint8_t playerIndex,pokerplayerwinning_t *winning){ - uint8_t i, j, l, card, number, suit, pairCount; - uint8_t index; - uint8_t pairs[CARD_SUIT_COUNT]; - pokerplayer_t *player; - - player = POKER_PLAYERS + playerIndex; - - // Get the full poker hand (should be a 7 card hand, but MAY not be) - for(i = 0; i < POKER_COMMUNITY_SIZE; i++) { - winning->full[i] = POKER_COMMUNITY[i]; - } - for(i = 0; i < POKER_PLAYER_HAND_SIZE_MAX; i++) { - winning->full[i + POKER_COMMUNITY_SIZE] = player->hand[i]; - } - winning->fullSize = POKER_COMMUNITY_SIZE + POKER_PLAYER_HAND_SIZE_MAX; - - // TODO: Do I need to sort this? - // cardHandSort(winning->full, winning->fullSize); - - // Reset the winning status. - winning->setSize = 0; - - //////////////////////// Now look for the winning set //////////////////////// - - // Royal / Straight Flush - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - number = cardGetNumber(card); - if(number < CARD_FIVE) continue; - - suit = cardGetSuit(card); - winning->setSize = 1; - - // Now look for the matching cards (Reverse order to order from A to 10) - for(j = 1; j <= 4; j++) { - l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low. - index = cardContains(winning->full, winning->fullSize, cardGet(l, suit)); - if(index == 0xFF) break; - winning->set[j] = winning->full[index]; - winning->setSize++; - } - - // Check if has all necessary cards. - if(winning->setSize < POKER_WINNING_SET_SIZE) continue; - - // Add self to array - winning->set[0] = winning->full[i]; - winning->type = ( - number == CARD_ACE ? POKER_WINNING_TYPE_ROYAL_FLUSH : - POKER_WINNING_TYPE_STRAIGHT_FLUSH - ); - pokerWinnerFillRemaining(winning); - return; - } - - // Four of a kind. - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - number = cardGetNumber(card); - pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); - if(pairCount < CARD_SUIT_COUNT) continue; - - winning->setSize = pairCount; - for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]]; - winning->type = POKER_WINNING_TYPE_FOUR_OF_A_KIND; - pokerWinnerFillRemaining(winning); - return; - } - - // Full House - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - // Check we haven't already added this card. - card = winning->full[i]; - if(cardContains(winning->set, winning->setSize, card) != 0xFF) continue; - - number = cardGetNumber(card); - pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); - - // Did we find either two pair or three pair? - if(pairCount != 2 && pairCount != 3) continue; - if(winning->setSize == 3) pairCount = 2;//Clamp to 5 max. - - // Copy found pairs. - for(j = 0; j < pairCount; j++) { - winning->set[winning->setSize + j] = winning->full[pairs[j]]; - } - winning->setSize += pairCount; - - // Winned? - if(winning->setSize != POKER_WINNING_SET_SIZE) continue; - winning->type = POKER_WINNING_TYPE_FULL_HOUSE; - pokerWinnerFillRemaining(winning); - return; - } - - // Flush (5 same suit) - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - suit = cardGetSuit(card); - winning->setSize = 1; - for(j = i+1; j < winning->fullSize; j++) { - if(cardGetSuit(winning->full[j]) != suit) continue; - winning->set[winning->setSize++] = winning->full[j]; - if(winning->setSize == POKER_WINNING_SET_SIZE) break; - } - if(winning->setSize < POKER_WINNING_SET_SIZE) continue; - winning->set[0] = winning->full[i]; - winning->type = POKER_WINNING_TYPE_FLUSH; - pokerWinnerFillRemaining(winning); - return; - } - - // Straight (sequence any suit) - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - number = cardGetNumber(card); - if(number < CARD_FIVE) continue; - winning->setSize = 1; - - for(j = 1; j <= 4; j++) { - l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low. - index = cardContainsNumber(winning->full, winning->fullSize, l); - if(index == 0xFF) break; - winning->set[j] = winning->full[index]; - winning->setSize++; - } - - // Check if has all necessary cards. - if(winning->setSize < POKER_WINNING_SET_SIZE) continue; - winning->set[0] = winning->full[i]; - winning->type = POKER_WINNING_TYPE_STRAIGHT; - pokerWinnerFillRemaining(winning); - return; - } - - // Three of a kind - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i]; - number = cardGetNumber(card); - pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); - if(pairCount != 3) continue; - - winning->setSize = pairCount; - for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]]; - winning->type = POKER_WINNING_TYPE_THREE_OF_A_KIND; - pokerWinnerFillRemaining(winning); - return; - } - - // Two Pair - winning->setSize = 0; - for(i = 0; i < winning->fullSize; i++) { - card = winning->full[i];// Check we haven't already added this card. - if(cardContains(winning->set, winning->setSize, card) != 0xFF) { - continue; - } - - number = cardGetNumber(card); - pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); - if(pairCount != 2) continue; - - for(j = 0; j < pairCount; j++) { - winning->set[winning->setSize++] = winning->full[pairs[j]]; - } - if(winning->setSize != 4) continue; - - winning->type = POKER_WINNING_TYPE_TWO_PAIR; - pokerWinnerFillRemaining(winning); - return; - } - - // Pair - if(winning->setSize == 2) { - winning->type = POKER_WINNING_TYPE_PAIR; - pokerWinnerFillRemaining(winning); - return; - } - - // High card - winning->setSize = 0; - pokerWinnerFillRemaining(winning); - winning->type = POKER_WINNING_TYPE_HIGH_CARD; - - return; -} - -inline uint16_t pokerWinnerGetTypeConfidence(uint8_t type) { - switch(type) { - case POKER_WINNING_TYPE_ROYAL_FLUSH: - return POKER_WINNING_CONFIDENCE_ROYAL_FLUSH; - case POKER_WINNING_TYPE_STRAIGHT_FLUSH: - return POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH; - case POKER_WINNING_TYPE_FOUR_OF_A_KIND: - return POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND; - case POKER_WINNING_TYPE_FULL_HOUSE: - return POKER_WINNING_CONFIDENCE_FULL_HOUSE; - case POKER_WINNING_TYPE_FLUSH: - return POKER_WINNING_CONFIDENCE_FLUSH; - case POKER_WINNING_TYPE_STRAIGHT: - return POKER_WINNING_CONFIDENCE_STRAIGHT; - case POKER_WINNING_TYPE_THREE_OF_A_KIND: - return POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND; - case POKER_WINNING_TYPE_TWO_PAIR: - return POKER_WINNING_CONFIDENCE_TWO_PAIR; - case POKER_WINNING_TYPE_PAIR: - return POKER_WINNING_CONFIDENCE_PAIR; - default: - return POKER_WINNING_CONFIDENCE_HIGH_CARD; - } -} - -uint8_t pokerWinnerCompare( - pokerplayerwinning_t *left, pokerplayerwinning_t *right -) { - uint8_t - i, number, card, countCardsSame, index, - highCardLeft, highCardRight, highNumberLeft, highNumberRight - ; - - highNumberLeft = 0xFF; - highNumberRight = 0xFF; - countCardsSame = 0; - - for(i = 0; i < left->setSize; i++) { - card = left->set[i]; - number = cardGetNumber(card); - if(highNumberLeft != 0xFF && number < highNumberLeft) continue;//Quick check - - // Check if this number is within the other hand or not - index = cardContainsNumber(right->set, right->setSize, number); - if(index == 0xFF) { - // This number IS within the other hand, let's check that the EXACT card - // is a match/isn't a match. - index = cardContains(right->set, right->setSize, card); - - // Exact card match - if(index != 0xFF) { - countCardsSame++; - continue; - } - // Not exact card match.. ? - } - - if(highNumberLeft == 0xFF||number == CARD_ACE||highNumberLeft < number) { - highNumberLeft = number; - highCardLeft = card; - } - } - - for(i = 0; i < right->setSize; i++) { - card = right->set[i]; - number = cardGetNumber(card); - if(highNumberRight != 0xFF && number < highNumberRight) continue; - - index = cardContainsNumber(left->set, left->setSize, number); - if(index != 0xFF) { - index = cardContains(left->set, left->setSize, card); - if(index != 0xFF) continue; - } - - if(highNumberRight == 0xFF||number == CARD_ACE||highNumberRight < number) { - highNumberRight = number; - highCardRight = card; - } - } - - - if(countCardsSame == left->setSize) { - for(i = 0; i < left->setSize; i++) { - card = left->set[i]; - number = cardGetNumber(card); - if(highNumberLeft == 0xFF||number == CARD_ACE||highNumberLeft < number) { - highNumberLeft = number; - highCardLeft = card; - } - } - return highCardLeft; - } - - if(highCardLeft == 0xFF) return 0xFF; - if(highNumberLeft < highNumberRight) return 0xFF; - return highCardLeft;//Greater or Equal to. -} - -void pokerWinnerDetermineForPot( - pokerpot_t *pot, - pokerplayerwinning_t *winners, - uint8_t *winnerPlayers, - uint8_t *winnerCount, - uint8_t *participants, - uint8_t *participantCount -) { - uint8_t i, j, countPlayers, countWinners, number, highNumber, card, highCard; - pokerplayerwinning_t *left, *right; - pokerplayer_t *player; - bool isWinner; - - countPlayers = 0; - countWinners = 0; - highCard = 0xFF; - - // Get participating players and their hands. - for(i = 0; i < POKER_PLAYER_COUNT; i++) { - if(pot->players[i] == 0) continue; - - player = POKER_PLAYERS + i; - if(player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) { - continue; - } - - participants[countPlayers] = i; - pokerWinnerGetForPlayer(i, winners + countPlayers++); - } - - // Compare participating players - for(i = 0; i < countPlayers; i++) { - left = winners + i; - isWinner = true; - highNumber = 0xFF; - - for(j = 0; j < countPlayers; j++) { - if(i == j) continue; - right = winners + j; - - // Am I the better hand / Is it the better hand? - if(left->type < right->type) continue; - if(left->type > right->type) { - isWinner = false; - break; - } - - // Equal, compare hands. - card = pokerWinnerCompare(left, right); - if(card == 0xFF) { - isWinner = false; - break; - } - - // Determine high card. - number = cardGetNumber(card); - if(highNumber == 0xFF || number == CARD_ACE || number > highNumber) { - highCard = card; - highNumber = number; - } - } - - if(!isWinner) continue; - left->kicker = highCard; - winnerPlayers[countWinners++] = participants[i]; - } - - *participantCount = countPlayers; - *winnerCount = countWinners; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "poker.h" + +pokerplayer_t POKER_PLAYERS[POKER_PLAYER_COUNT_MAX]; + +uint8_t POKER_DECK[CARD_DECK_SIZE]; +uint8_t POKER_DECK_SIZE; +uint8_t POKER_COMMUNITY[POKER_COMMUNITY_SIZE_MAX]; +uint8_t POKER_COMMUNITY_SIZE; + +uint8_t POKER_PLAYER_DEALER; +uint8_t POKER_PLAYER_SMALL_BLIND; +uint8_t POKER_PLAYER_BIG_BLIND; +uint8_t POKER_PLAYER_BETTER; + +uint16_t POKER_GAME_BLINDS_CURRENT; + +pokerpot_t POKER_POTS[POKER_POT_COUNT_MAX]; +uint8_t POKER_POT_CURRENT; +uint8_t POKER_POT_COUNT; + +pokerplayerwinning_t POKER_WINNERS[POKER_PLAYER_COUNT_MAX]; +uint8_t POKER_WINNER_PLAYERS[POKER_PLAYER_COUNT_MAX]; +uint8_t POKER_WINNER_PARTICIPANTS[POKER_PLAYER_COUNT_MAX]; +uint8_t POKER_WINNER_COUNT; +uint8_t POKER_WINNER_PARTICIPANT_COUNT; +uint8_t POKER_PLAYER_COUNT; + +void pokerInit() { + uint8_t i; + + // Set up the initial state. + // TODO: Should this be randomized? + POKER_PLAYER_DEALER = 0; + POKER_GAME_BLINDS_CURRENT = 10; + POKER_PLAYER_COUNT = POKER_PLAYER_COUNT_MAX; + + // Set up players + for(i = 0; i < POKER_PLAYER_COUNT; i++) { + POKER_PLAYERS[i].chips = 10000; + POKER_PLAYERS[i].state = 0; + } + + // Reset the round state (For the first round) + pokerNewRound(); +} + +void pokerNewRound() { + uint8_t i, j, k; + uint8_t found; + pokerplayer_t *player; + + // Reset round state + POKER_COMMUNITY_SIZE = 0; + POKER_POT_COUNT = 0; + POKER_POT_CURRENT = 0; + + // Reset the pots. + for(i = 0; i < POKER_POT_COUNT_MAX; i++) { + POKER_POTS[i].chips = 0; + POKER_POTS[i].call = 0; + for(j = 0; j < POKER_PLAYER_COUNT; j++) { + POKER_POTS[i].players[j] = 0; + } + } + + // Fill deck + for(i = 0; i < CARD_DECK_SIZE; i++) POKER_DECK[i] = i; + POKER_DECK_SIZE = CARD_DECK_SIZE; + + // Shuffle Deck + for(i = POKER_DECK_SIZE-1; i > 0; i--) { + k = rand(); + j = k % (i+1); + k = POKER_DECK[i]; + POKER_DECK[i] = POKER_DECK[j]; + POKER_DECK[j] = k; + } + + // Reset Players and decide new blinds. + found = 0; + POKER_PLAYER_DEALER++; + + // Update players for the round. + for(i = 0; i < POKER_PLAYER_COUNT; i++) { + player = POKER_PLAYERS + i; + player->timesRaised = 0; + player->state &= ~( + POKER_PLAYER_STATE_FOLDED | + POKER_PLAYER_STATE_HAS_BET_THIS_ROUND + ); + + // Update out state. + if(player->chips == 0) player->state |= POKER_PLAYER_STATE_OUT; + + // Is the player out? + if((player->state & POKER_PLAYER_STATE_OUT) != 0) continue; + + // Have we found the dealer, small blind, and big blind players? + if(found != 3) { + k = (POKER_PLAYER_DEALER + i) % POKER_PLAYER_COUNT; + + if(found == 0) {// Have we found the dealer? + POKER_PLAYER_DEALER = i; + found++; + } else if(found == 1) {// Have we found the small blind? + POKER_PLAYER_SMALL_BLIND = i; + found++; + } else if(found == 2) {// Have we found the big blind? + POKER_PLAYER_BIG_BLIND = i; + found++; + } + } + + // Deal two cards to the player. + for(j = 0; j < POKER_PLAYER_HAND_SIZE_MAX; j++) { + player->hand[j] = POKER_DECK[--POKER_DECK_SIZE]; + } + } + + // Take blinds + // TODO: I need to make sure the blind players even have the chips to blind. + pokerBet(POKER_PLAYER_SMALL_BLIND, POKER_GAME_BLINDS_CURRENT); + pokerBet(POKER_PLAYER_BIG_BLIND, (POKER_GAME_BLINDS_CURRENT*2)); + + // Set the initial better, we set this to the BIG BLIND player because we will + // cycle to the "next better" as soon as the game starts. + POKER_PLAYER_BETTER = POKER_PLAYER_BIG_BLIND; +} + +inline void pokerBet(uint8_t player, uint16_t amount) { + // TODO: This may become a function because if a player doesn't have enough + // chips to bet to the active pot, then the pot needs to autosplit, take those + // who have bet into the pot up to the amount that the player betting can bet, + // and push them into a new pot. + // There also needs to be a limit on this, for example; + // player 0 has $1200, and bets $1000, they can't bet more than that ever. + // player 1 has $1000, and bets all of it. The remanin + // player 2 has $700, and bets all o it. A new $300 sidepot auto creates + // player 3 has $500 and bets all of it, Another sidepot with $200 is auto made. + if(POKER_POT_CURRENT == POKER_POT_COUNT) POKER_POT_COUNT++; + + POKER_PLAYERS[player].chips -= amount; + POKER_POTS[POKER_POT_CURRENT].chips += amount; + POKER_POTS[POKER_POT_CURRENT].players[player] += amount; + POKER_POTS[POKER_POT_CURRENT].call = MATH_MAX( + amount, POKER_POTS[POKER_POT_CURRENT].players[player] + ); +} + +inline uint8_t pokerGetCallBet(uint8_t player) { + return ( + POKER_POTS[POKER_POT_CURRENT].call - + POKER_POTS[POKER_POT_CURRENT].players[player] + ); +} + +void pokerAi(uint8_t player, pokerturn_t *turn) { + uint8_t i, cardNumber0, cardNumber1, suitNumber0, suitNumber1; + uint16_t callBet, maxBet, amount, bluffBet; + uint8_t random;// TODO: Determine type. + uint16_t confidence, expectedGain, potOdds; + pokerplayerwinning_t winning; + pokerplayer_t *plyr = POKER_PLAYERS + player; + + // The following logic is heavily inspired by; + // https://github.com/gorel/C-Poker-AI/blob/master/src/common/pokerai.c + // But with some changes and smarts added by me. The original source code will + // essentially just run a crap tun of simulated games and get the times that + // they are expected to win from those games, but I'm just going to use the + // odds of the hand being the winning hand. + + // Is this preflop? + if(POKER_COMMUNITY_SIZE == 0) { + // Get the hand weight + cardNumber0 = cardGetNumber(plyr->hand[0]); + cardNumber1 = cardGetNumber(plyr->hand[1]); + suitNumber0 = cardGetSuit(plyr->hand[0]); + suitNumber1 = cardGetSuit(plyr->hand[1]); + + // Get delta between cards + i = MATH_ABS(cardNumber0 - cardNumber1); + + // Get card weight + confidence = cardNumber0 + cardNumber1; + if(cardNumber0 == cardNumber1) {// Pairs + confidence += 6; + } else if(suitNumber0 == suitNumber1) {// Same suit + confidence += 4; + } + + // Get difference from cards for guessing flush + if(i > 4) { + confidence -= 4; + } else if(i > 2) { + confidence -= i; + } + + // Confidence is now a value between 0-30 (inclusive). Multiply by 1000 + confidence = (confidence * 1000) / 30; + // Now do over 30 to get the value represented as 0-1000 + } else { + // Simulate my hand being the winning hand, use that as the confidence + // TODO: Repurpose old code lmao. Just take it from Dawn-C + BGB_printf("Holy shit you guys"); + pokerWinnerGetForPlayer(player, &winning); + BGB_printf("Winning type %u", winning.type); + confidence = pokerWinnerGetTypeConfidence(winning.type); + BGB_printf("Confidence %u", confidence); + } + + // Now we know how confident the AI is, let's put a chip value to that weight + // How many chips to call? + callBet = pokerGetCallBet(player); + BGB_printf("Callbet %u", callBet); + + + // Do they need chips to call, or is it possible to check? + if(callBet > 0) { + // Work out how many chips the player could possibly win. This is a number + // between 0 and player count * initial chips, at the time of writing that + // is around 50,000. + expectedGain = 0; + for(i = 0; i < POKER_POT_COUNT; i++) { + if(POKER_POTS[i].players[player] == 0) break; + expectedGain += POKER_POTS[i].chips; + } + + + // Now work out the pot odds, this is basically "how many times the callbet + // could I win if I win this hand". e.g. Desirable hands will have an + // expected gain much higher than the callbet. + potOdds = plyr->chips / 1000; + potOdds = MATH_MAX((callBet + expectedGain), 1) / MATH_MAX(potOdds, 1); + + BGB_printf("Expected Gain and Pot Odds %u, %u", expectedGain, potOdds); + } else { + // If the call bet is zero then there's fairly equal odds, so let's just + // take the chances out of the remainig player count. + potOdds = 1000 / pokerGetRemainingBetterCount() * 2;// 0 - 1000 + BGB_printf("Pot Odds %u", potOdds); + } + + // Now determine the expected ROI + //TODO: I think these values are a bit odd. + expectedGain = (confidence*100) / (potOdds / 10); + BGB_printf("Expected Gains (2) %u", expectedGain); + + // Now get a random number 0-100. + random = rand(); + random = random % 100; + + // Determine the max bet that the AI is willing to make. The max bet is + // basically how high the AI is willing to bet. + maxBet = plyr->chips;// TODO: Replace with below code and test, just running + // With this for now. + // maxBet = plyr->chips / MATH_MAX(random / 10, 1); + // maxBet -= callBet; + // BGB_printf("Rand %u, Max Bet %u", random, callBet); + + // Determine what's a good bluff bet. + // TODO: not float + bluffBet = maxBet; + // bluffBet = ((random * 100) / maxBet) * plyr->chips; + + // Now prep the output + turn->bluff = false; + amount = 0; + + BGB_printf("Random Dice %u", random); + + + // Now the actual AI can happen. This is basically a weight to confidence + // ratio. The higher the gains and the confidence then the more likely the AI + // is to betting. There are also bluff chances within here. + if(expectedGain < 800 && confidence < 800) { + if(random < 95) { + amount = 0; + } else { + amount = bluffBet; + turn->bluff = true; + } + } else if ((expectedGain < 1000 && confidence < 850) || confidence < 100) { + if (random < 80) { + amount = 0; + } else if(random < 5) { + amount = callBet; + turn->bluff = true; + } else { + amount = bluffBet; + turn->bluff = true; + } + } else if ((expectedGain < 1300 && confidence < 900) || confidence < 500) { + if (random < 60 || confidence < 500) { + amount = callBet; + } else { + amount = maxBet; + } + } else if (confidence < 950 || POKER_COMMUNITY_SIZE < 0x04) { + if(random < 20) { + amount = callBet; + } else { + amount = maxBet; + } + } else { + // TODO: check this + amount = (plyr->chips - callBet) * 9 / 10; + } + + // If this is the first round... make it a lot less likely I'll bet + if(POKER_COMMUNITY_SIZE == 0x00 && amount > callBet) { + if(random > 5) amount = callBet; + } + + + // Did we actually bet? + if(amount > 0) { + // Let's not get caught in a raising loop with AI. + if(plyr->timesRaised >= POKER_TURN_MAX_RAISES) amount = callBet; + amount = MATH_MAX(amount, callBet); + amount = MATH_MIN(amount, plyr->chips); + turn->chips = amount; + turn->confidence = confidence; + + if(plyr->chips == amount) { + turn->type = POKER_TURN_TYPE_ALL_IN; + } else if(amount == callBet) { + turn->type = POKER_TURN_TYPE_CALL; + } else { + turn->type = POKER_TURN_TYPE_BET; + } + } else if(pokerCanPlayerCheck(player)) { + turn->type = POKER_TURN_TYPE_CHECK; + turn->chips = 0; + turn->confidence = 1000; + } else { + turn->type = POKER_TURN_TYPE_FOLD; + turn->chips = 0; + turn->confidence = 1000 - confidence; + } +} + +inline bool pokerCanPlayerCheck(uint8_t player) { + return ( + POKER_POTS[POKER_POT_CURRENT].players[player] == + POKER_POTS[POKER_POT_CURRENT].call + ); +} + +inline bool pokerDoesPlayerNeedToBet(uint8_t playerIndex) { + uint8_t i; + pokerplayer_t *player = POKER_PLAYERS + playerIndex; + + // Can this player even participate? + if( + (player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) != 0 || + player->chips == 0 + ) { + return false; + } + + // Has the player bet? If so are they in the current pot? + if((player->state & POKER_PLAYER_STATE_HAS_BET_THIS_ROUND) == 0) { + return true; + } + + //TODO: Refer to pokerbet function, but basically I can't let the player + //bet if they have bet more money than the second richest player. + + // Check each pot, if the pot is inactive or the player is CALLED/CHECKED + for(i = 0; i < POKER_POT_COUNT_MAX; i++) { + // Is this pot active? + if(POKER_POTS[i].chips == 0) break; + + // Is the player called into this pot all the way? + if(POKER_POTS[i].players[playerIndex] == POKER_POTS[i].call) continue; + return true; + } + + return false; +} + +inline uint8_t pokerGetRemainingBetterCount() { + uint8_t i, count; + count = 0; + for(i = 0 ; i < POKER_PLAYER_COUNT; i++) { + if(pokerDoesPlayerNeedToBet(i)) count++; + } + return count; +} + +void pokerWinnerFillRemaining(pokerplayerwinning_t *winning) { + uint8_t i, highest, current, highestCard, currentCard; + + // Set the kicker + winning->kicker = 0xFF; + + // Fill the remaining cards + while(winning->setSize < POKER_WINNING_SET_SIZE) { + highest = 0xFF; + + for(i = 0; i < winning->fullSize; i++) { + currentCard = winning->full[i]; + if(cardContains(winning->set, winning->setSize, currentCard) != 0xFF) { + continue; + } + + if(highest == 0xFF) { + highestCard = currentCard; + highest = cardGetNumber(highestCard); + } else { + current = cardGetNumber(currentCard); + if(current != CARD_ACE && current < highest) continue; + highestCard = currentCard; + highest = current; + } + } + + if(highest == 0xFF) break; + winning->set[winning->setSize++] = highestCard; + } + // cardHandSort(winning->set, winning->setSize); +} + +void pokerWinnerGetForPlayer(uint8_t playerIndex,pokerplayerwinning_t *winning){ + uint8_t i, j, l, card, number, suit, pairCount; + uint8_t index; + uint8_t pairs[CARD_SUIT_COUNT]; + pokerplayer_t *player; + + player = POKER_PLAYERS + playerIndex; + + // Get the full poker hand (should be a 7 card hand, but MAY not be) + for(i = 0; i < POKER_COMMUNITY_SIZE; i++) { + winning->full[i] = POKER_COMMUNITY[i]; + } + for(i = 0; i < POKER_PLAYER_HAND_SIZE_MAX; i++) { + winning->full[i + POKER_COMMUNITY_SIZE] = player->hand[i]; + } + winning->fullSize = POKER_COMMUNITY_SIZE + POKER_PLAYER_HAND_SIZE_MAX; + + // TODO: Do I need to sort this? + // cardHandSort(winning->full, winning->fullSize); + + // Reset the winning status. + winning->setSize = 0; + + //////////////////////// Now look for the winning set //////////////////////// + + // Royal / Straight Flush + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + number = cardGetNumber(card); + if(number < CARD_FIVE) continue; + + suit = cardGetSuit(card); + winning->setSize = 1; + + // Now look for the matching cards (Reverse order to order from A to 10) + for(j = 1; j <= 4; j++) { + l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low. + index = cardContains(winning->full, winning->fullSize, cardGet(l, suit)); + if(index == 0xFF) break; + winning->set[j] = winning->full[index]; + winning->setSize++; + } + + // Check if has all necessary cards. + if(winning->setSize < POKER_WINNING_SET_SIZE) continue; + + // Add self to array + winning->set[0] = winning->full[i]; + winning->type = ( + number == CARD_ACE ? POKER_WINNING_TYPE_ROYAL_FLUSH : + POKER_WINNING_TYPE_STRAIGHT_FLUSH + ); + pokerWinnerFillRemaining(winning); + return; + } + + // Four of a kind. + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + number = cardGetNumber(card); + pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); + if(pairCount < CARD_SUIT_COUNT) continue; + + winning->setSize = pairCount; + for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]]; + winning->type = POKER_WINNING_TYPE_FOUR_OF_A_KIND; + pokerWinnerFillRemaining(winning); + return; + } + + // Full House + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + // Check we haven't already added this card. + card = winning->full[i]; + if(cardContains(winning->set, winning->setSize, card) != 0xFF) continue; + + number = cardGetNumber(card); + pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); + + // Did we find either two pair or three pair? + if(pairCount != 2 && pairCount != 3) continue; + if(winning->setSize == 3) pairCount = 2;//Clamp to 5 max. + + // Copy found pairs. + for(j = 0; j < pairCount; j++) { + winning->set[winning->setSize + j] = winning->full[pairs[j]]; + } + winning->setSize += pairCount; + + // Winned? + if(winning->setSize != POKER_WINNING_SET_SIZE) continue; + winning->type = POKER_WINNING_TYPE_FULL_HOUSE; + pokerWinnerFillRemaining(winning); + return; + } + + // Flush (5 same suit) + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + suit = cardGetSuit(card); + winning->setSize = 1; + for(j = i+1; j < winning->fullSize; j++) { + if(cardGetSuit(winning->full[j]) != suit) continue; + winning->set[winning->setSize++] = winning->full[j]; + if(winning->setSize == POKER_WINNING_SET_SIZE) break; + } + if(winning->setSize < POKER_WINNING_SET_SIZE) continue; + winning->set[0] = winning->full[i]; + winning->type = POKER_WINNING_TYPE_FLUSH; + pokerWinnerFillRemaining(winning); + return; + } + + // Straight (sequence any suit) + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + number = cardGetNumber(card); + if(number < CARD_FIVE) continue; + winning->setSize = 1; + + for(j = 1; j <= 4; j++) { + l = number == CARD_FIVE && j == 4 ? CARD_ACE : number - j;//Ace low. + index = cardContainsNumber(winning->full, winning->fullSize, l); + if(index == 0xFF) break; + winning->set[j] = winning->full[index]; + winning->setSize++; + } + + // Check if has all necessary cards. + if(winning->setSize < POKER_WINNING_SET_SIZE) continue; + winning->set[0] = winning->full[i]; + winning->type = POKER_WINNING_TYPE_STRAIGHT; + pokerWinnerFillRemaining(winning); + return; + } + + // Three of a kind + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i]; + number = cardGetNumber(card); + pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); + if(pairCount != 3) continue; + + winning->setSize = pairCount; + for(j = 0; j < pairCount; j++) winning->set[j] = winning->full[pairs[j]]; + winning->type = POKER_WINNING_TYPE_THREE_OF_A_KIND; + pokerWinnerFillRemaining(winning); + return; + } + + // Two Pair + winning->setSize = 0; + for(i = 0; i < winning->fullSize; i++) { + card = winning->full[i];// Check we haven't already added this card. + if(cardContains(winning->set, winning->setSize, card) != 0xFF) { + continue; + } + + number = cardGetNumber(card); + pairCount = cardCountPairs(winning->full, winning->fullSize, number, pairs); + if(pairCount != 2) continue; + + for(j = 0; j < pairCount; j++) { + winning->set[winning->setSize++] = winning->full[pairs[j]]; + } + if(winning->setSize != 4) continue; + + winning->type = POKER_WINNING_TYPE_TWO_PAIR; + pokerWinnerFillRemaining(winning); + return; + } + + // Pair + if(winning->setSize == 2) { + winning->type = POKER_WINNING_TYPE_PAIR; + pokerWinnerFillRemaining(winning); + return; + } + + // High card + winning->setSize = 0; + pokerWinnerFillRemaining(winning); + winning->type = POKER_WINNING_TYPE_HIGH_CARD; + + return; +} + +inline uint16_t pokerWinnerGetTypeConfidence(uint8_t type) { + switch(type) { + case POKER_WINNING_TYPE_ROYAL_FLUSH: + return POKER_WINNING_CONFIDENCE_ROYAL_FLUSH; + case POKER_WINNING_TYPE_STRAIGHT_FLUSH: + return POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH; + case POKER_WINNING_TYPE_FOUR_OF_A_KIND: + return POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND; + case POKER_WINNING_TYPE_FULL_HOUSE: + return POKER_WINNING_CONFIDENCE_FULL_HOUSE; + case POKER_WINNING_TYPE_FLUSH: + return POKER_WINNING_CONFIDENCE_FLUSH; + case POKER_WINNING_TYPE_STRAIGHT: + return POKER_WINNING_CONFIDENCE_STRAIGHT; + case POKER_WINNING_TYPE_THREE_OF_A_KIND: + return POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND; + case POKER_WINNING_TYPE_TWO_PAIR: + return POKER_WINNING_CONFIDENCE_TWO_PAIR; + case POKER_WINNING_TYPE_PAIR: + return POKER_WINNING_CONFIDENCE_PAIR; + default: + return POKER_WINNING_CONFIDENCE_HIGH_CARD; + } +} + +uint8_t pokerWinnerCompare( + pokerplayerwinning_t *left, pokerplayerwinning_t *right +) { + uint8_t + i, number, card, countCardsSame, index, + highCardLeft, highCardRight, highNumberLeft, highNumberRight + ; + + highNumberLeft = 0xFF; + highNumberRight = 0xFF; + countCardsSame = 0; + + for(i = 0; i < left->setSize; i++) { + card = left->set[i]; + number = cardGetNumber(card); + if(highNumberLeft != 0xFF && number < highNumberLeft) continue;//Quick check + + // Check if this number is within the other hand or not + index = cardContainsNumber(right->set, right->setSize, number); + if(index == 0xFF) { + // This number IS within the other hand, let's check that the EXACT card + // is a match/isn't a match. + index = cardContains(right->set, right->setSize, card); + + // Exact card match + if(index != 0xFF) { + countCardsSame++; + continue; + } + // Not exact card match.. ? + } + + if(highNumberLeft == 0xFF||number == CARD_ACE||highNumberLeft < number) { + highNumberLeft = number; + highCardLeft = card; + } + } + + for(i = 0; i < right->setSize; i++) { + card = right->set[i]; + number = cardGetNumber(card); + if(highNumberRight != 0xFF && number < highNumberRight) continue; + + index = cardContainsNumber(left->set, left->setSize, number); + if(index != 0xFF) { + index = cardContains(left->set, left->setSize, card); + if(index != 0xFF) continue; + } + + if(highNumberRight == 0xFF||number == CARD_ACE||highNumberRight < number) { + highNumberRight = number; + highCardRight = card; + } + } + + + if(countCardsSame == left->setSize) { + for(i = 0; i < left->setSize; i++) { + card = left->set[i]; + number = cardGetNumber(card); + if(highNumberLeft == 0xFF||number == CARD_ACE||highNumberLeft < number) { + highNumberLeft = number; + highCardLeft = card; + } + } + return highCardLeft; + } + + if(highCardLeft == 0xFF) return 0xFF; + if(highNumberLeft < highNumberRight) return 0xFF; + return highCardLeft;//Greater or Equal to. +} + +void pokerWinnerDetermineForPot( + pokerpot_t *pot, + pokerplayerwinning_t *winners, + uint8_t *winnerPlayers, + uint8_t *winnerCount, + uint8_t *participants, + uint8_t *participantCount +) { + uint8_t i, j, countPlayers, countWinners, number, highNumber, card, highCard; + pokerplayerwinning_t *left, *right; + pokerplayer_t *player; + bool isWinner; + + countPlayers = 0; + countWinners = 0; + highCard = 0xFF; + + // Get participating players and their hands. + for(i = 0; i < POKER_PLAYER_COUNT; i++) { + if(pot->players[i] == 0) continue; + + player = POKER_PLAYERS + i; + if(player->state & (POKER_PLAYER_STATE_FOLDED|POKER_PLAYER_STATE_OUT)) { + continue; + } + + participants[countPlayers] = i; + pokerWinnerGetForPlayer(i, winners + countPlayers++); + } + + // Compare participating players + for(i = 0; i < countPlayers; i++) { + left = winners + i; + isWinner = true; + highNumber = 0xFF; + + for(j = 0; j < countPlayers; j++) { + if(i == j) continue; + right = winners + j; + + // Am I the better hand / Is it the better hand? + if(left->type < right->type) continue; + if(left->type > right->type) { + isWinner = false; + break; + } + + // Equal, compare hands. + card = pokerWinnerCompare(left, right); + if(card == 0xFF) { + isWinner = false; + break; + } + + // Determine high card. + number = cardGetNumber(card); + if(highNumber == 0xFF || number == CARD_ACE || number > highNumber) { + highCard = card; + highNumber = number; + } + } + + if(!isWinner) continue; + left->kicker = highCard; + winnerPlayers[countWinners++] = participants[i]; + } + + *participantCount = countPlayers; + *winnerCount = countWinners; } \ No newline at end of file diff --git a/src/poker/poker.h b/src/poker/poker.h index 5708eaf..ae5bfad 100644 --- a/src/poker/poker.h +++ b/src/poker/poker.h @@ -1,57 +1,57 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "../util.h" -#include "card.h" -#include "player.h" -#include "pot.h" -#include "turn.h" -#include "winner.h" - -#define POKER_COMMUNITY_SIZE_MAX 5 -#define POKER_HUMAN_INDEX 0x00 - -#define POKER_COUNT_FLOP 0x03 -#define POKER_COUNT_TURN 0x01 -#define POKER_COUNT_RIVER 0x01 - -extern uint8_t POKER_DECK[]; -extern uint8_t POKER_DECK_SIZE; -extern uint8_t POKER_COMMUNITY[]; -extern uint8_t POKER_COMMUNITY_SIZE; - -extern uint8_t POKER_PLAYER_DEALER; -extern uint8_t POKER_PLAYER_SMALL_BLIND; -extern uint8_t POKER_PLAYER_BIG_BLIND; -extern uint8_t POKER_PLAYER_BETTER; - -extern uint16_t POKER_GAME_BLINDS_CURRENT; - -void pokerInit(); -void pokerNewRound(); -inline void pokerBet(uint8_t player, uint16_t amount); -inline uint8_t pokerGetCallBet(uint8_t player); -void pokerAi(uint8_t player, pokerturn_t *turn); -inline bool pokerCanPlayerCheck(uint8_t player); -inline bool pokerDoesPlayerNeedToBet(uint8_t playerIndex); -inline uint8_t pokerGetRemainingBetterCount(); -void pokerWinnerFillRemaining(pokerplayerwinning_t *winning); -void pokerWinnerGetForPlayer(uint8_t playerIndex,pokerplayerwinning_t *winning); -inline uint16_t pokerWinnerGetTypeConfidence(uint8_t type); -uint8_t pokerWinnerCompare( - pokerplayerwinning_t *left, pokerplayerwinning_t *right -); -void pokerWinnerDetermineForPot( - pokerpot_t *pot, - pokerplayerwinning_t *winners, - uint8_t *winnerPlayers, - uint8_t *winnerCount, - uint8_t *participants, - uint8_t *participantCount +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "../util.h" +#include "card.h" +#include "player.h" +#include "pot.h" +#include "turn.h" +#include "winner.h" + +#define POKER_COMMUNITY_SIZE_MAX 5 +#define POKER_HUMAN_INDEX 0x00 + +#define POKER_COUNT_FLOP 0x03 +#define POKER_COUNT_TURN 0x01 +#define POKER_COUNT_RIVER 0x01 + +extern uint8_t POKER_DECK[]; +extern uint8_t POKER_DECK_SIZE; +extern uint8_t POKER_COMMUNITY[]; +extern uint8_t POKER_COMMUNITY_SIZE; + +extern uint8_t POKER_PLAYER_DEALER; +extern uint8_t POKER_PLAYER_SMALL_BLIND; +extern uint8_t POKER_PLAYER_BIG_BLIND; +extern uint8_t POKER_PLAYER_BETTER; + +extern uint16_t POKER_GAME_BLINDS_CURRENT; + +void pokerInit(); +void pokerNewRound(); +inline void pokerBet(uint8_t player, uint16_t amount); +inline uint8_t pokerGetCallBet(uint8_t player); +void pokerAi(uint8_t player, pokerturn_t *turn); +inline bool pokerCanPlayerCheck(uint8_t player); +inline bool pokerDoesPlayerNeedToBet(uint8_t playerIndex); +inline uint8_t pokerGetRemainingBetterCount(); +void pokerWinnerFillRemaining(pokerplayerwinning_t *winning); +void pokerWinnerGetForPlayer(uint8_t playerIndex,pokerplayerwinning_t *winning); +inline uint16_t pokerWinnerGetTypeConfidence(uint8_t type); +uint8_t pokerWinnerCompare( + pokerplayerwinning_t *left, pokerplayerwinning_t *right +); +void pokerWinnerDetermineForPot( + pokerpot_t *pot, + pokerplayerwinning_t *winners, + uint8_t *winnerPlayers, + uint8_t *winnerCount, + uint8_t *participants, + uint8_t *participantCount ); \ No newline at end of file diff --git a/src/poker/pot.h b/src/poker/pot.h index a0dfc3c..2857c3f 100644 --- a/src/poker/pot.h +++ b/src/poker/pot.h @@ -1,27 +1,27 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "player.h" - -#define POKER_POT_COUNT_MAX POKER_PLAYER_COUNT_MAX - -typedef struct { - /** Current pot of chips */ - uint16_t chips; - - /** Current call value for this pot */ - uint16_t call; - - /** Players who are participating in the pots current bet (in the pot) */ - uint16_t players[POKER_PLAYER_COUNT_MAX]; -} pokerpot_t; - -extern pokerpot_t POKER_POTS[]; -extern uint8_t POKER_POT_CURRENT; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "player.h" + +#define POKER_POT_COUNT_MAX POKER_PLAYER_COUNT_MAX + +typedef struct { + /** Current pot of chips */ + uint16_t chips; + + /** Current call value for this pot */ + uint16_t call; + + /** Players who are participating in the pots current bet (in the pot) */ + uint16_t players[POKER_PLAYER_COUNT_MAX]; +} pokerpot_t; + +extern pokerpot_t POKER_POTS[]; +extern uint8_t POKER_POT_CURRENT; extern uint8_t POKER_POT_COUNT; \ No newline at end of file diff --git a/src/poker/turn.h b/src/poker/turn.h index d8c411f..a128815 100644 --- a/src/poker/turn.h +++ b/src/poker/turn.h @@ -1,30 +1,30 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" - -#define POKER_TURN_MAX_RAISES 0x02 - -/** Turn Types */ -#define POKER_TURN_TYPE_OUT 0x00 -#define POKER_TURN_TYPE_FOLD 0x01 -#define POKER_TURN_TYPE_BET 0x02 -#define POKER_TURN_TYPE_CALL 0x03 -#define POKER_TURN_TYPE_ALL_IN 0x04 -#define POKER_TURN_TYPE_CHECK 0x05 - -typedef struct { - /** What type of action the turn is */ - uint8_t type; - /** How many chips they did in their turn (if applicable) */ - uint16_t chips; - /** How confident the AI is about their turn. 0 = none, 1000 = full */ - uint16_t confidence; - /** Is this turn a bluff? */ - bool bluff; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" + +#define POKER_TURN_MAX_RAISES 0x02 + +/** Turn Types */ +#define POKER_TURN_TYPE_OUT 0x00 +#define POKER_TURN_TYPE_FOLD 0x01 +#define POKER_TURN_TYPE_BET 0x02 +#define POKER_TURN_TYPE_CALL 0x03 +#define POKER_TURN_TYPE_ALL_IN 0x04 +#define POKER_TURN_TYPE_CHECK 0x05 + +typedef struct { + /** What type of action the turn is */ + uint8_t type; + /** How many chips they did in their turn (if applicable) */ + uint16_t chips; + /** How confident the AI is about their turn. 0 = none, 1000 = full */ + uint16_t confidence; + /** Is this turn a bluff? */ + bool bluff; } pokerturn_t; \ No newline at end of file diff --git a/src/poker/winner.h b/src/poker/winner.h index ff79c99..70af1e7 100644 --- a/src/poker/winner.h +++ b/src/poker/winner.h @@ -1,64 +1,64 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "../libs.h" -#include "card.h" - -/** Maximum number of cards a winning state can hold. */ -#define POKER_WINNING_FULL_SIZE 0x07 - -/** How many cards in the winning set */ -#define POKER_WINNING_SET_SIZE 0x05 - -/** Winning Types */ -#define POKER_WINNING_TYPE_NULL 0x00 -#define POKER_WINNING_TYPE_ROYAL_FLUSH 0x01 -#define POKER_WINNING_TYPE_STRAIGHT_FLUSH 0x02 -#define POKER_WINNING_TYPE_FOUR_OF_A_KIND 0x03 -#define POKER_WINNING_TYPE_FULL_HOUSE 0x04 -#define POKER_WINNING_TYPE_FLUSH 0x05 -#define POKER_WINNING_TYPE_STRAIGHT 0x06 -#define POKER_WINNING_TYPE_THREE_OF_A_KIND 0x07 -#define POKER_WINNING_TYPE_TWO_PAIR 0x08 -#define POKER_WINNING_TYPE_PAIR 0x09 -#define POKER_WINNING_TYPE_HIGH_CARD 0x0A - -/** Confidences of winning based on the current hand type */ -#define POKER_WINNING_CONFIDENCE_ROYAL_FLUSH 1000 -#define POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH 990 -#define POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND 900 -#define POKER_WINNING_CONFIDENCE_FULL_HOUSE 850 -#define POKER_WINNING_CONFIDENCE_FLUSH 800 -#define POKER_WINNING_CONFIDENCE_STRAIGHT 700 -#define POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND 500 -#define POKER_WINNING_CONFIDENCE_TWO_PAIR 400 -#define POKER_WINNING_CONFIDENCE_PAIR 200 -#define POKER_WINNING_CONFIDENCE_HIGH_CARD 100 - -/** Holds information about a player's winning state */ -typedef struct { - /** The full set of both the dealer and player's hand */ - uint8_t full[POKER_WINNING_FULL_SIZE]; - uint8_t fullSize; - - /** Holds the winning set */ - uint8_t set[POKER_WINNING_SET_SIZE]; - uint8_t setSize; - - /** Winning Type */ - uint8_t type; - - /** If there was a kicker card it will be here, otherwise -1 for no kicker */ - uint8_t kicker; -} pokerplayerwinning_t; - -extern pokerplayerwinning_t POKER_WINNERS[POKER_PLAYER_COUNT_MAX]; -extern uint8_t POKER_WINNER_PLAYERS[POKER_PLAYER_COUNT_MAX]; -extern uint8_t POKER_WINNER_PARTICIPANTS[POKER_PLAYER_COUNT_MAX]; -extern uint8_t POKER_WINNER_COUNT; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "card.h" + +/** Maximum number of cards a winning state can hold. */ +#define POKER_WINNING_FULL_SIZE 0x07 + +/** How many cards in the winning set */ +#define POKER_WINNING_SET_SIZE 0x05 + +/** Winning Types */ +#define POKER_WINNING_TYPE_NULL 0x00 +#define POKER_WINNING_TYPE_ROYAL_FLUSH 0x01 +#define POKER_WINNING_TYPE_STRAIGHT_FLUSH 0x02 +#define POKER_WINNING_TYPE_FOUR_OF_A_KIND 0x03 +#define POKER_WINNING_TYPE_FULL_HOUSE 0x04 +#define POKER_WINNING_TYPE_FLUSH 0x05 +#define POKER_WINNING_TYPE_STRAIGHT 0x06 +#define POKER_WINNING_TYPE_THREE_OF_A_KIND 0x07 +#define POKER_WINNING_TYPE_TWO_PAIR 0x08 +#define POKER_WINNING_TYPE_PAIR 0x09 +#define POKER_WINNING_TYPE_HIGH_CARD 0x0A + +/** Confidences of winning based on the current hand type */ +#define POKER_WINNING_CONFIDENCE_ROYAL_FLUSH 1000 +#define POKER_WINNING_CONFIDENCE_STRAIGHT_FLUSH 990 +#define POKER_WINNING_CONFIDENCE_FOUR_OF_A_KIND 900 +#define POKER_WINNING_CONFIDENCE_FULL_HOUSE 850 +#define POKER_WINNING_CONFIDENCE_FLUSH 800 +#define POKER_WINNING_CONFIDENCE_STRAIGHT 700 +#define POKER_WINNING_CONFIDENCE_THREE_OF_A_KIND 500 +#define POKER_WINNING_CONFIDENCE_TWO_PAIR 400 +#define POKER_WINNING_CONFIDENCE_PAIR 200 +#define POKER_WINNING_CONFIDENCE_HIGH_CARD 100 + +/** Holds information about a player's winning state */ +typedef struct { + /** The full set of both the dealer and player's hand */ + uint8_t full[POKER_WINNING_FULL_SIZE]; + uint8_t fullSize; + + /** Holds the winning set */ + uint8_t set[POKER_WINNING_SET_SIZE]; + uint8_t setSize; + + /** Winning Type */ + uint8_t type; + + /** If there was a kicker card it will be here, otherwise -1 for no kicker */ + uint8_t kicker; +} pokerplayerwinning_t; + +extern pokerplayerwinning_t POKER_WINNERS[POKER_PLAYER_COUNT_MAX]; +extern uint8_t POKER_WINNER_PLAYERS[POKER_PLAYER_COUNT_MAX]; +extern uint8_t POKER_WINNER_PARTICIPANTS[POKER_PLAYER_COUNT_MAX]; +extern uint8_t POKER_WINNER_COUNT; extern uint8_t POKER_WINNER_PARTICIPANT_COUNT; \ No newline at end of file diff --git a/src/sprites/spriteborder.c b/src/sprites/spriteborder.c new file mode 100644 index 0000000..a3913b6 --- /dev/null +++ b/src/sprites/spriteborder.c @@ -0,0 +1,44 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "spriteborder.h" + +inline void spriteBorderBuffer() { + spriteBuffer(SPRITE_BORDER_VRAM_START, BORDER_IMAGE_TILES, BORDER_IMAGE); +} + +inline void spriteBorderBufferEdges( + uint8_t *buffer, uint8_t bufferWidth, uint8_t bufferHeight, uint8_t fill +) { + uint8_t i, j, max; + max = bufferWidth * bufferHeight; + + // Corners + buffer[0] = SPRITE_BORDER_VRAM_START + SPRITE_BORDER_TOP_LEFT; + buffer[bufferWidth-1] = SPRITE_BORDER_VRAM_START + SPRITE_BORDER_TOP_RIGHT; + buffer[max-1] = SPRITE_BORDER_VRAM_START + SPRITE_BORDER_BOTTOM_RIGHT; + buffer[max-bufferWidth] = SPRITE_BORDER_VRAM_START + SPRITE_BORDER_BOTTOM_LEFT; + + // Edges + for(i = 1; i < bufferWidth-1; i++) { + buffer[i] = SPRITE_BORDER_VRAM_START + SPRITE_BORDER_BOTTOM_TOP; + buffer[max - 1 - i] = SPRITE_BORDER_VRAM_START + SPRITE_BORDER_BOTTOM_TOP; + } + for(i = 1; i < bufferHeight - 1; i++) { + buffer[bufferWidth * i] = SPRITE_BORDER_VRAM_START + SPRITE_BORDER_LEFT_RIGHT; + buffer[bufferWidth * (i+1) - 1] = SPRITE_BORDER_VRAM_START + SPRITE_BORDER_LEFT_RIGHT; + } + + // Inner + if(fill != 0xFF) { + for(j = 1; j < bufferHeight-1; j++) { + for(i = 1; i < bufferWidth-1; i++) { + buffer[i + (j * bufferWidth)] = fill; + } + } + } +} \ No newline at end of file diff --git a/src/sprites/spriteborder.h b/src/sprites/spriteborder.h new file mode 100644 index 0000000..7daa9d4 --- /dev/null +++ b/src/sprites/spriteborder.h @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "sprites.h" +#include "BORDER.h" +#include "spritefont.h" + +#define SPRITE_BORDER_VRAM_START SPRITE_FONT_VRAM_END +#define SPRITE_BORDER_VRAM_END SPRITE_BORDER_VRAM_START + BORDER_IMAGE_TILES + +#define SPRITE_BORDER_TOP_LEFT 0x00 +#define SPRITE_BORDER_TOP_RIGHT 0x01 +#define SPRITE_BORDER_LEFT_RIGHT 0x02 +#define SPRITE_BORDER_BOTTOM_LEFT 0x03 +#define SPRITE_BORDER_BOTTOM_RIGHT 0x04 +#define SPRITE_BORDER_BOTTOM_TOP 0x05 + +/** + * Buffer the border sprite into VRAM. + */ +inline void spriteBorderBuffer(); + +/** + * Buffer the edges of a border to a tile buffer. + * + * @param buffer Buffer to write the tiles in to. + * @param bufferWidth Width of the buffer that you want to write. + * @param bufferHeight Height of the buffer that you want to write. + * @param fill NULL for no fill, otherwise TILE INDEX of fill within border. + */ +inline void spriteBorderBufferEdges( + uint8_t *buffer, uint8_t bufferWidth, uint8_t bufferHeight, uint8_t fill +); \ No newline at end of file diff --git a/src/sprites/spritecards.c b/src/sprites/spritecards.c new file mode 100644 index 0000000..d41cac3 --- /dev/null +++ b/src/sprites/spritecards.c @@ -0,0 +1,38 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "spritecards.h" + +const uint8_t SPRITE_HEARTS_ACE[SPRITE_CARD_TILE_COUNT] = { + SPRITE_CARD_ACE_TOP, SPRITE_CARD_TOP, SPRITE_CARD_TOP, SPRITE_CARD_TOP_RIGHT, + SPRITE_CARD_LEFT, SPRITE_CARD_BLANK, SPRITE_CARD_BLANK, SPRITE_CARD_RIGHT, + SPRITE_CARD_LEFT, SPRITE_CARD_HEART_BIG_TOP_LEFT, SPRITE_CARD_HEART_BIG_TOP_RIGHT, SPRITE_CARD_RIGHT, + SPRITE_CARD_LEFT, SPRITE_CARD_HEART_BIG_BOTTOM_LEFT, SPRITE_CARD_HEART_BIG_BOTTOM_RIGHT, SPRITE_CARD_RIGHT, + SPRITE_CARD_LEFT, SPRITE_CARD_BLANK, SPRITE_CARD_BLANK, SPRITE_CARD_RIGHT, + SPRITE_CARD_BOTTOM_LEFT, SPRITE_CARD_BOTTOM, SPRITE_CARD_BOTTOM, SPRITE_CARD_BOTTOM_RIGHT +}; + +// const uint8_t *SPRITE_CARD_LIST[] = { +// SPRITE_HEARTS_ACE +// }; + +inline void spriteCardsBuffer() { + spriteBuffer(SPRITE_CARD_VRAM_START, CARDS_TILES_IMAGE_TILES, CARDS_TILES_IMAGE); +} + +inline uint8_t * spriteCardsForCard(uint8_t card) { + return SPRITE_HEARTS_ACE; +} + +inline void spriteCardBufferTiles(uint8_t *buffer, uint8_t card) { + uint8_t i; + uint8_t *spriteTiles = spriteCardsForCard(card); + while(i != SPRITE_CARD_TILE_COUNT) { + buffer[i] = spriteTiles[i]; + i++; + } +} \ No newline at end of file diff --git a/src/sprites/spritecards.h b/src/sprites/spritecards.h new file mode 100644 index 0000000..6a269ff --- /dev/null +++ b/src/sprites/spritecards.h @@ -0,0 +1,68 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "CARDS_TILES.h" +#include "../poker/card.h" +#include "spritetileset.h" +#include "sprites.h" +#include "spriteborder.h" + +#define SPRITE_CARD_VRAM_START SPRITE_BORDER_VRAM_END + +#define SPRITE_CARD_BLANK SPRITE_TILESET_WHITE + +#define SPRITE_CARD_ACE_TOP SPRITE_CARD_VRAM_START + 0 +#define SPRITE_CARD_ACE_BOTTOM SPRITE_CARD_ACE_TOP/* ref */ +#define SPRITE_CARD_TWO_TOP SPRITE_CARD_VRAM_START + 1 +#define SPRITE_CARD_TWO_BOTTOM SPRITE_CARD_TWO_TOP/* ref */ +#define SPRITE_CARD_THREE_TOP SPRITE_CARD_VRAM_START + 2 +#define SPRITE_CARD_THREE_BOTTOM SPRITE_CARD_THREE_TOP/* ref */ +#define SPRITE_CARD_FOUR_TOP SPRITE_CARD_VRAM_START + 3 +#define SPRITE_CARD_FOUR_BOTTOM SPRITE_CARD_THREE_TOP/* ref */ +#define SPRITE_CARD_FIVE_TOP SPRITE_CARD_VRAM_START + 4 +#define SPRITE_CARD_FIVE_BOTTOM SPRITE_CARD_FIVE_TOP/* ref */ +#define SPRITE_CARD_SIX_TOP SPRITE_CARD_VRAM_START + 5 +#define SPRITE_CARD_SIX_BOTTOM SPRITE_CARD_SIX_TOP/* ref */ +#define SPRITE_CARD_SEVEN_TOP SPRITE_CARD_VRAM_START + 6 +#define SPRITE_CARD_SEVEN_BOTTOM SPRITE_CARD_SEVEN_TOP/* ref */ +#define SPRITE_CARD_EIGHT_TOP SPRITE_CARD_VRAM_START + 7 +#define SPRITE_CARD_EIGHT_BOTTOM SPRITE_CARD_EIGHT_TOP/* ref */ +#define SPRITE_CARD_NINE_TOP SPRITE_CARD_VRAM_START + 8 +#define SPRITE_CARD_NINE_BOTTOM SPRITE_CARD_NINE_TOP/* ref */ +#define SPRITE_CARD_TEN_TOP SPRITE_CARD_VRAM_START + 9 +#define SPRITE_CARD_TEN_BOTTOM/* ref */ + +#define SPRITE_CARD_TOP SPRITE_CARD_VRAM_START + 11 +#define SPRITE_CARD_TOP_RIGHT SPRITE_CARD_VRAM_START + 10 +#define SPRITE_CARD_LEFT SPRITE_CARD_TOP/* ref */ +#define SPRITE_CARD_RIGHT SPRITE_CARD_TOP/* ref */ +#define SPRITE_CARD_BOTTOM_LEFT SPRITE_CARD_TOP_RIGHT/* ref */ +#define SPRITE_CARD_BOTTOM_RIGHT SPRITE_CARD_TOP_RIGHT/* ref */ +#define SPRITE_CARD_BOTTOM SPRITE_CARD_TOP/* ref */ + +#define SPRITE_CARD_DIAMOND_BIG_TOP_LEFT SPRITE_CARD_VRAM_START + 12 +#define SPRITE_CARD_DIAMOND_BIG_TOP_RIGHT SPRITE_CARD_DIAMOND_BIG_TOP_LEFT/* ref */ +#define SPRITE_CARD_DIAMOND_BIG_BOTTOM_LEFT SPRITE_CARD_DIAMOND_BIG_TOP_LEFT/* ref */ +#define SPRITE_CARD_DIAMOND_BIG_BOTTOM_RIGHT SPRITE_CARD_DIAMOND_BIG_TOP_LEFT/* ref */ +#define SPRITE_CARD_HEART_BIG_TOP_LEFT SPRITE_CARD_VRAM_START + 13 +#define SPRITE_CARD_HEART_BIG_TOP_RIGHT SPRITE_CARD_HEART_BIG_TOP_LEFT/* ref */ +#define SPRITE_CARD_HEART_BIG_BOTTOM_LEFT SPRITE_CARD_DIAMOND_BIG_BOTTOM_LEFT +#define SPRITE_CARD_HEART_BIG_BOTTOM_RIGHT SPRITE_CARD_DIAMOND_BIG_BOTTOM_RIGHT + +#define SPRITE_CARD_WIDTH 4 +#define SPRITE_CARD_HEIGHT 6 +#define SPRITE_CARD_TILE_COUNT (SPRITE_CARD_WIDTH * SPRITE_CARD_HEIGHT) + +extern const uint8_t SPRITE_HEARTS_ACE[]; + +inline void spriteCardsBuffer(); + +inline uint8_t * spriteCardsForCard(uint8_t card); + +inline void spriteCardBufferTiles(uint8_t *buffer, uint8_t card); \ No newline at end of file diff --git a/src/sprites/spritefont.c b/src/sprites/spritefont.c new file mode 100644 index 0000000..b2ccbe2 --- /dev/null +++ b/src/sprites/spritefont.c @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "spritefont.h" + +inline void spriteFontBuffer() { + spriteBuffer(SPRITE_FONT_VRAM_START, FONT_IMAGE_TILES, FONT_IMAGE); +} + +inline uint8_t spriteFontTileFromChar(char character) { + return character - SPRITE_FONT_FIRST_CHARACTER + SPRITE_FONT_VRAM_START; +} \ No newline at end of file diff --git a/src/sprites/spritefont.h b/src/sprites/spritefont.h new file mode 100644 index 0000000..0a4a69f --- /dev/null +++ b/src/sprites/spritefont.h @@ -0,0 +1,30 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "FONT.h" +#include "sprites.h" +#include "spritetileset.h" + +#define SPRITE_FONT_FIRST_CHARACTER 33 + +#define SPRITE_FONT_VRAM_START SPRITE_TILESET_VRAM_END +#define SPRITE_FONT_VRAM_END SPRITE_FONT_VRAM_START + FONT_IMAGE_TILES + +/** + * Buffer the font tiles to VRAM. + */ +inline void spriteFontBuffer(); + +/** + * Get the tile index for a given character (ASCII). + * + * @param character Character to get the tile index from. + * @return The tile index for the given character. + */ +inline uint8_t spriteFontTileFromChar(char character); \ No newline at end of file diff --git a/src/sprites/sprites.c b/src/sprites/sprites.c new file mode 100644 index 0000000..13834ba --- /dev/null +++ b/src/sprites/sprites.c @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "sprites.h" + +inline void spriteBuffer(uint8_t position, uint8_t length, uint8_t *tiles) { + set_bkg_data(position, length, tiles); +} \ No newline at end of file diff --git a/src/sprites/sprites.h b/src/sprites/sprites.h new file mode 100644 index 0000000..7247de8 --- /dev/null +++ b/src/sprites/sprites.h @@ -0,0 +1,18 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" + +/** + * Buffer tiles data into VRAM + * + * @param position Position in VRAM to buffer in to. + * @param length Length of the data being buffered (count of tiles). + * @param tiles Pointer to array of tiles to be bufffered. + */ +inline void spriteBuffer(uint8_t position, uint8_t length, uint8_t *tiles); \ No newline at end of file diff --git a/src/sprites/spritetileset.c b/src/sprites/spritetileset.c new file mode 100644 index 0000000..c42f74b --- /dev/null +++ b/src/sprites/spritetileset.c @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "spritetileset.h" + +void spriteTilesetBuffer() { + spriteBuffer(SPRITE_TILESET_VRAM_START, TILESET_IMAGE_TILES, TILESET_IMAGE); +} \ No newline at end of file diff --git a/src/sprites/spritetileset.h b/src/sprites/spritetileset.h new file mode 100644 index 0000000..ada36f1 --- /dev/null +++ b/src/sprites/spritetileset.h @@ -0,0 +1,50 @@ +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "../libs.h" +#include "TILESET.h" +#include "sprites.h" + +#define SPRITE_TILESET_VRAM_START 0x00 +#define SPRITE_TILESET_VRAM_END SPRITE_TILESET_VRAM_START + TILESET_IMAGE_TILES + +#define SPRITE_TILESET_TILE_0 SPRITE_TILESET_VRAM_START + 0x00 +#define SPRITE_TILESET_TILE_1 SPRITE_TILESET_VRAM_START + 0x01 +#define SPRITE_TILESET_TILE_2 SPRITE_TILESET_VRAM_START + 0x02 +#define SPRITE_TILESET_TILE_3 SPRITE_TILESET_VRAM_START + 0x03 + +#define SPRITE_TILESET_WHITE SPRITE_TILESET_TILE_0 +#define SPRITE_TILESET_BLACK SPRITE_TILESET_TILE_3 +#define SPRITE_TILESET_LIGHT SPRITE_TILESET_TILE_1 +#define SPRITE_TILESET_DARK SPRITE_TILESET_TILE_2 + + +// Shades are mapped where each set of 4 colors is mapped to two bits that will +// specify its darkness. Higher = Darker, Lower = Brighter +// 11 11 11 11 +#define TILESET_SHADE_BLACK 0xFF +// 11 11 11 10 +#define TILESET_SHADE_DARKER 0xFE +// 11 11 10 01 +#define TILESET_SHADE_DARK 0xF9 +// 11 10 01 00 +#define TILESET_SHADE_NORMAL 0xE4 +// 10 01 00 00 +#define TILESET_SHADE_BRIGHT 0x90 +// 01 00 00 00 +#define TILESET_SHADE_BRIGHTER 0x40 +// 00 00 00 00 +#define TILESET_SHADE_WHITE 0x00 + + +/** + * Buffer the TILESET tileset sprites onto VRAM. + * + * @param position Position in VRAM to buuffer on to. + */ +void spriteTilesetBuffer(); \ No newline at end of file diff --git a/src/strings.c b/src/strings.c index 52ebde9..6f34736 100644 --- a/src/strings.c +++ b/src/strings.c @@ -1,33 +1,33 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "strings.h" - -const char STR_ERROR[] = "An error\nhas occured"; -const char STR_HELLO[] = "Hello World, How are you today?\nGood thanks. Thank god!"; - -const char STR_POKER_GAME_START[] = "Poker game started"; -const char STR_POKER_GAME_TAKING_BLINDS[] = "Blinds taken."; -const char STR_POKER_GAME_CARDS_DEALT[] = "Cards dealt."; - -const char STR_POKER_GAME_CARDS_FLOPPED[] = "Cards flopped"; -const char STR_POKER_GAME_CARDS_TURNED[] = "Cards turned"; -const char STR_POKER_GAME_CARDS_RIVERED[] = "Cards river"; - -const char STR_DEBUG_WINNER_DECIDED[] = "DEBUG WINNER"; -const char STR_DEBUG_PLAYER[] = "DEBUG PLAYER"; - -const char STR_POKER_GAME_AI_FOLD[] = "AI Folding"; -const char STR_POKER_GAME_AI_RAISE[] = "AI Raise"; -const char STR_POKER_GAME_AI_RAISE_BLUFF[] = "AI Raise\nBut Bluffing"; -const char STR_POKER_GAME_AI_CALL[] = "AI Calling"; -const char STR_POKER_GAME_AI_CALL_BLUFF[] = "AI Calling\nBut Bluffing"; -const char STR_POKER_GAME_AI_ALL_IN[] = "AI All In"; -const char STR_POKER_GAME_AI_ALL_IN_BLUFF[] = "AI All In\nBut Bluffing"; -const char STR_POKER_GAME_AI_CHECK[] = "AI Checking"; -const char STR_POKER_GAME_AI_CHECK_BLUFF[] = "AI Checking\nBut Bluffing"; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "strings.h" + +const char STR_ERROR[] = "An error\nhas occured"; +const char STR_HELLO[] = "Hello World, How are you today?\nGood thanks. Thank god!"; + +const char STR_POKER_GAME_START[] = "Poker game started"; +const char STR_POKER_GAME_TAKING_BLINDS[] = "Blinds taken."; +const char STR_POKER_GAME_CARDS_DEALT[] = "Cards dealt."; + +const char STR_POKER_GAME_CARDS_FLOPPED[] = "Cards flopped"; +const char STR_POKER_GAME_CARDS_TURNED[] = "Cards turned"; +const char STR_POKER_GAME_CARDS_RIVERED[] = "Cards river"; + +const char STR_DEBUG_WINNER_DECIDED[] = "DEBUG WINNER"; +const char STR_DEBUG_PLAYER[] = "DEBUG PLAYER"; + +const char STR_POKER_GAME_AI_FOLD[] = "AI Folding"; +const char STR_POKER_GAME_AI_RAISE[] = "AI Raise"; +const char STR_POKER_GAME_AI_RAISE_BLUFF[] = "AI Raise\nBut Bluffing"; +const char STR_POKER_GAME_AI_CALL[] = "AI Calling"; +const char STR_POKER_GAME_AI_CALL_BLUFF[] = "AI Calling\nBut Bluffing"; +const char STR_POKER_GAME_AI_ALL_IN[] = "AI All In"; +const char STR_POKER_GAME_AI_ALL_IN_BLUFF[] = "AI All In\nBut Bluffing"; +const char STR_POKER_GAME_AI_CHECK[] = "AI Checking"; +const char STR_POKER_GAME_AI_CHECK_BLUFF[] = "AI Checking\nBut Bluffing"; const char STR_FOX[] = "The quick brown fox jumps over the lazy dog."; \ No newline at end of file diff --git a/src/strings.h b/src/strings.h index 8fc0dee..5328d53 100644 --- a/src/strings.h +++ b/src/strings.h @@ -1,34 +1,34 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "libs.h" - -extern const char STR_ERROR[]; -extern const char STR_HELLO[]; - -extern const char STR_POKER_GAME_START[]; -extern const char STR_POKER_GAME_TAKING_BLINDS[]; -extern const char STR_POKER_GAME_CARDS_DEALT[]; - -extern const char STR_POKER_GAME_CARDS_FLOPPED[]; -extern const char STR_POKER_GAME_CARDS_TURNED[]; -extern const char STR_POKER_GAME_CARDS_RIVERED[]; - -extern const char STR_DEBUG_WINNER_DECIDED[]; -extern const char STR_DEBUG_PLAYER[]; - -extern const char STR_POKER_GAME_AI_FOLD[]; -extern const char STR_POKER_GAME_AI_RAISE[]; -extern const char STR_POKER_GAME_AI_RAISE_BLUFF[]; -extern const char STR_POKER_GAME_AI_CALL[]; -extern const char STR_POKER_GAME_AI_CALL_BLUFF[]; -extern const char STR_POKER_GAME_AI_ALL_IN[]; -extern const char STR_POKER_GAME_AI_ALL_IN_BLUFF[]; -extern const char STR_POKER_GAME_AI_CHECK[]; -extern const char STR_POKER_GAME_AI_CHECK_BLUFF[]; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "libs.h" + +extern const char STR_ERROR[]; +extern const char STR_HELLO[]; + +extern const char STR_POKER_GAME_START[]; +extern const char STR_POKER_GAME_TAKING_BLINDS[]; +extern const char STR_POKER_GAME_CARDS_DEALT[]; + +extern const char STR_POKER_GAME_CARDS_FLOPPED[]; +extern const char STR_POKER_GAME_CARDS_TURNED[]; +extern const char STR_POKER_GAME_CARDS_RIVERED[]; + +extern const char STR_DEBUG_WINNER_DECIDED[]; +extern const char STR_DEBUG_PLAYER[]; + +extern const char STR_POKER_GAME_AI_FOLD[]; +extern const char STR_POKER_GAME_AI_RAISE[]; +extern const char STR_POKER_GAME_AI_RAISE_BLUFF[]; +extern const char STR_POKER_GAME_AI_CALL[]; +extern const char STR_POKER_GAME_AI_CALL_BLUFF[]; +extern const char STR_POKER_GAME_AI_ALL_IN[]; +extern const char STR_POKER_GAME_AI_ALL_IN_BLUFF[]; +extern const char STR_POKER_GAME_AI_CHECK[]; +extern const char STR_POKER_GAME_AI_CHECK_BLUFF[]; extern const char STR_FOX[]; \ No newline at end of file diff --git a/src/time.c b/src/time.c index c2efaf6..af1f38e 100644 --- a/src/time.c +++ b/src/time.c @@ -1,22 +1,22 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#include "time.h" - -uint16_t TIME_CURRENT; -uint16_t TIME_FUTURE; -uint8_t TIME_FUTURE_TYPE; - -inline void timeInit() { - TIME_CURRENT = 0; - TIME_FUTURE = 0; - TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_NULL; -} - -inline void timeUpdate() { - TIME_CURRENT++; +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "time.h" + +uint16_t TIME_CURRENT; +uint16_t TIME_FUTURE; +uint8_t TIME_FUTURE_TYPE; + +inline void timeInit() { + TIME_CURRENT = 0; + TIME_FUTURE = 0; + TIME_FUTURE_TYPE = TIME_FUTURE_TYPE_NULL; +} + +inline void timeUpdate() { + TIME_CURRENT++; } \ No newline at end of file diff --git a/src/time.h b/src/time.h index fe79f87..db0ff7f 100644 --- a/src/time.h +++ b/src/time.h @@ -1,25 +1,25 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "libs.h" - -#define TIME_PER_SECOND 60 - -#define TIME_FUTURE_TYPE_NULL 0x00 -#define TIME_FUTURE_TYPE_PAUSE 0x01 -#define TIME_FUTURE_TYPE_FADE_TO_BLACK 0x02 -#define TIME_FUTURE_TYPE_FADE_FROM_BLACK 0x03 -#define TIME_FUTURE_TYPE_FADE_TO_WHITE 0x04 -#define TIME_FUTURE_TYPE_FADE_FROM_WHITE 0x05 - -extern uint16_t TIME_CURRENT; -extern uint16_t TIME_FUTURE; -extern uint8_t TIME_FUTURE_TYPE; - -inline void timeInit(); +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "libs.h" + +#define TIME_PER_SECOND 60 + +#define TIME_FUTURE_TYPE_NULL 0x00 +#define TIME_FUTURE_TYPE_PAUSE 0x01 +#define TIME_FUTURE_TYPE_FADE_TO_BLACK 0x02 +#define TIME_FUTURE_TYPE_FADE_FROM_BLACK 0x03 +#define TIME_FUTURE_TYPE_FADE_TO_WHITE 0x04 +#define TIME_FUTURE_TYPE_FADE_FROM_WHITE 0x05 + +extern uint16_t TIME_CURRENT; +extern uint16_t TIME_FUTURE; +extern uint8_t TIME_FUTURE_TYPE; + +inline void timeInit(); inline void timeUpdate(); \ No newline at end of file diff --git a/src/util.h b/src/util.h index 0c27a92..d56e1c8 100644 --- a/src/util.h +++ b/src/util.h @@ -1,13 +1,13 @@ -/** - * Copyright (c) 2022 Dominic Masters - * - * This software is released under the MIT License. - * https://opensource.org/licenses/MIT - */ - -#pragma once -#include "libs.h" - -#define MATH_MIN(a, b) a > b ? b : a -#define MATH_MAX(a, b) a < b ? b : a +/** + * Copyright (c) 2022 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "libs.h" + +#define MATH_MIN(a, b) a > b ? b : a +#define MATH_MAX(a, b) a < b ? b : a #define MATH_ABS(n) (n < 0 ? -n : n) \ No newline at end of file diff --git a/test.sh b/test.sh index 94352a0..80fc2e4 100644 --- a/test.sh +++ b/test.sh @@ -1,8 +1,8 @@ -#!/bin/bash -# Send over latest build -scp ./build/Penny.gb root@ywbud3:/storage/roms/gb/Penny.gb - -systemctl stop emustation.service -killall emulationstation -retroarch -L /lib/libretro/gambatte_libretro.so "/storage/roms/gb/Penny.gb" +#!/bin/bash +# Send over latest build +scp ./build/Penny.gb root@ywbud3:/storage/roms/gb/Penny.gb + +systemctl stop emustation.service +killall emulationstation +retroarch -L /lib/libretro/gambatte_libretro.so "/storage/roms/gb/Penny.gb" systemctl start emustation.service \ No newline at end of file