diff --git a/CMakeLists.txt b/CMakeLists.txt
index f80a147a..25aa0875 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,13 +9,24 @@ set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
project(Dawn VERSION 1.0)
-set(GAME_NAME DawnGame)
-set(GAME_VERSION 1.0)
+# Targets
+if(TARGET_GAME STREQUAL poker)
+ add_compile_definitions(
+ SETTING_GAME_NAME="Penny's Poker"
+ GAME_FILE="poker/game.h"
+ GAME_TYPE=pokergame_t
+ GAME_INIT=pokerGameInit
+ GAME_UPDATE=pokerGameUpdate
+ GAME_DISPOSE=pokerGameDispose
+ GAME_VERSION=1.0
+ )
+endif()
-##################################### LIBS #####################################
+# Shared
add_subdirectory(lib)
add_subdirectory(src)
+# Targets
if(TARGET_GROUP STREQUAL test)
add_subdirectory(test)
else()
diff --git a/assets/poker/CMakeLists.txt b/assets/poker/CMakeLists.txt
new file mode 100644
index 00000000..46fefe35
--- /dev/null
+++ b/assets/poker/CMakeLists.txt
@@ -0,0 +1 @@
+file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/*/character.xml)
\ No newline at end of file
diff --git a/assets/poker/characters/lilly/concept/gdf8rgvc.bmp b/assets/poker/characters/lilly/concept/gdf8rgvc.bmp
new file mode 100644
index 00000000..78dad2c7
Binary files /dev/null and b/assets/poker/characters/lilly/concept/gdf8rgvc.bmp differ
diff --git a/assets/poker/characters/penny/character.xml b/assets/poker/characters/penny/character.xml
new file mode 100644
index 00000000..5163ceec
--- /dev/null
+++ b/assets/poker/characters/penny/character.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/test-ui.xml b/assets/test-ui.xml
deleted file mode 100644
index bf6b89c2..00000000
--- a/assets/test-ui.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
\ No newline at end of file
diff --git a/client/glfwclient/glfwclient.c b/client/glfwclient/glfwclient.c
index 643636e2..23b8ced0 100644
--- a/client/glfwclient/glfwclient.c
+++ b/client/glfwclient/glfwclient.c
@@ -51,8 +51,7 @@ int32_t main() {
// Init the render resolution
renderSetResolution(&game->engine.render,
WINDOW_WIDTH_DEFAULT, WINDOW_HEIGHT_DEFAULT
- );
-
+ );
// Init the game
if(gameInit(game)) {
// Bind initial keys
@@ -72,8 +71,12 @@ int32_t main() {
// Bind the fake inputs
inputBind(input, INPUT_MOUSE_X, GLFW_PLATFORM_INPUT_MOUSE_X);
inputBind(input, INPUT_MOUSE_Y, GLFW_PLATFORM_INPUT_MOUSE_Y);
- glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ // Set up some GLFW stuff
+ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
+ glfwSetWindowTitle(window, game->engine.name);
+
+ // Begin time.
time = 0;
// Main Render Loop
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..28f9e592
--- /dev/null
+++ b/package.json
@@ -0,0 +1,12 @@
+{
+ "name": "dawn",
+ "version": "1.0.0",
+ "repository": "https://YourWishes@github.com/YourWishes/Dawn.git",
+ "author": "Dominic Masters ",
+ "license": "MIT",
+ "private": true,
+ "dependencies": {
+ "pngjs": "^6.0.0",
+ "xml-js": "^1.6.11"
+ }
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ccd0341e..d763f0e3 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -14,16 +14,10 @@ target_link_libraries(game PUBLIC
stb
)
-
# Set up flags
-target_compile_definitions(game PRIVATE
+add_compile_definitions(
SETTING_PLATFORM_GLFW=1
SETTING_PLATFORM=1
SETTING_PLATFORM_USE_GLAD=1
SETTING_ASSET_PREFIX="../../../assets/"
- SETTING_GAME_NAME="DawnGame"
- SETTING_GAME_POKER=1
- SETTING_GAME_DAWN=2
- SETTING_GAME_SANDBOX=3
- SETTING_GAME=3
)
\ No newline at end of file
diff --git a/src/engine/engine.c b/src/engine/engine.c
index 07f64c93..16e0a177 100644
--- a/src/engine/engine.c
+++ b/src/engine/engine.c
@@ -9,6 +9,8 @@
void engineInit(engine_t *engine) {
randSeed(123);
+
+ engine->name = SETTING_GAME_NAME;
epochInit(&engine->time);
renderInit();
diff --git a/src/file/asset.c b/src/file/asset.c
index de0116e3..fa08e70f 100644
--- a/src/file/asset.c
+++ b/src/file/asset.c
@@ -34,6 +34,7 @@ char * assetStringLoad(char *assetName) {
assetbuffer_t * assetBufferOpen(char *assetName) {
// Get the directory based on the raw input by creating a new string.
+ FILE *fptr;
size_t lenAsset = strlen(assetName);// Get the length of asset
size_t lenPrefix = strlen(SETTING_ASSET_PREFIX);// Get the length of the prefix
@@ -48,7 +49,7 @@ assetbuffer_t * assetBufferOpen(char *assetName) {
printf("Opening up %s\n", joined);
// Open the file pointer now.
- FILE *fptr = fopen(joined, "rb");
+ fptr = fopen(joined, "rb");
free(joined);// Free the string we just created
if(!fptr) return NULL;// File available?
return (assetbuffer_t *)fptr;
diff --git a/src/game/game.h b/src/game/game.h
index 1584f38a..8d80425c 100644
--- a/src/game/game.h
+++ b/src/game/game.h
@@ -4,36 +4,24 @@
// https://opensource.org/licenses/MIT
#pragma once
+#if !defined(GAME_TYPE)
+ #error You need to define the GAME_TYPE struct
+#elif !defined(GAME_INIT)
+ #error You need to define the GAME_INIT method
+#elif !defined(GAME_UPDATE)
+ #error You need to define the GAME_UPDATE method
+#elif !defined(GAME_DISPOSE)
+ #error You need to define the GAME_DISPOSE method
+#elif !defined(GAME_FILE)
+ #error You need to define the GAME_FILE string
+#endif
+
#include "../libs.h"
#include "../engine/engine.h"
#include "../locale/language.h"
+#include GAME_FILE
-#define SETTING_GAME_SANDBOX 3
-#define SETTING_GAME SETTING_GAME_SANDBOX
-
-/** Describes the current game */
-#if SETTING_GAME == SETTING_GAME_POKER
- #include "poker/game.h"
- typedef pokergame_t game_t;
- #define GAME_INIT pokerGameInit
- #define GAME_UPDATE pokerGameUpdate
- #define GAME_DISPOSE pokerGameDispose
-
-#elif SETTING_GAME == SETTING_GAME_DAWN
- #include "dawn/dawngame.h"
- typedef dawngame_t game_t;
- #define GAME_INIT dawnGameInit
- #define GAME_UPDATE dawnGameUpdate
- #define GAME_DISPOSE dawnGameDispose
-
-#elif SETTING_GAME == SETTING_GAME_SANDBOX
- #include "sandbox/sandboxscene.h"
- typedef sandboxscene_t game_t;
- #define GAME_INIT sandboxSceneInit
- #define GAME_UPDATE sandboxSceneUpdate
- #define GAME_DISPOSE sandboxSceneDispose
-
-#endif
+typedef GAME_TYPE game_t;
/**
* Initialize the game context.
diff --git a/src/game/poker/pokergameassets.c b/src/game/poker/pokergameassets.c
index b6061ec4..1cd97323 100644
--- a/src/game/poker/pokergameassets.c
+++ b/src/game/poker/pokergameassets.c
@@ -9,22 +9,22 @@
bool pokerGameAssetsInit(pokergameassets_t *assets) {
// Load the game's shader
assetShaderLoad(&assets->shader,
- "shaders/textured.vert", "shaders/textured.frag"
+ "shared/shaders/textured.vert", "shared/shaders/textured.frag"
);
// Load the game's font
- assetFontLoad(&assets->font, "fonts/opensans/OpenSans-Bold.ttf");
+ assetFontLoad(&assets->font, "shared/fonts/opensans/OpenSans-Bold.ttf");
// Initialize the language buffer.
languageInit(&assets->language, "locale/language/en-US.csv");
// Load the world textures.
- assetTextureLoad(&assets->testTexture, "test_texture.png");
- assetTextureLoad(&assets->cardTexture, "cards_normal.png");
- assetTextureLoad(&assets->roomTexture, "world/pub/pub_skywall.png");
+ assetTextureLoad(&assets->testTexture, "shared/test_texture.png");
+ assetTextureLoad(&assets->cardTexture, "poker/cards_normal.png");
+ assetTextureLoad(&assets->roomTexture, "poker/world/pub/pub_skywall.png");
// Load the character textures.
- assetTextureLoad(&assets->pennyTexture, "characters/penny/sprites/sheet.png");
+ assetTextureLoad(&assets->pennyTexture, "poker/characters/penny/sprites/sheet.png");
return true;
}
diff --git a/tools/utils/args.js b/tools/utils/args.js
new file mode 100644
index 00000000..94efab05
--- /dev/null
+++ b/tools/utils/args.js
@@ -0,0 +1,12 @@
+const args = process.argv.splice(2).reduce((x,y) => {
+ const bits = y.split('=');
+ const name = bits[0].replace('--', '');
+ const val = bits[1];
+
+ x[name] = val;
+ return x;
+}, {});
+
+module.exports = {
+ args
+};
\ No newline at end of file
diff --git a/tools/utils/image.js b/tools/utils/image.js
new file mode 100644
index 00000000..5c4b7e61
--- /dev/null
+++ b/tools/utils/image.js
@@ -0,0 +1,103 @@
+const { PNG } = require("pngjs");
+const path = require('path');
+const fs = require('fs');
+
+/**
+ * Loads an image into memory.
+ * @param image Image to load
+ * @returns A promise that resolves to the loaded image.
+ */
+const imageLoad = (image) => new Promise(resolve => {
+ fs.createReadStream(image)
+ .pipe(new PNG({ filterType: 4 }))
+ .on("parsed", function () {
+ // Normalize
+ const pixels = [];
+ for(let y = 0; y < this.height; y++) {
+ for(let x = 0; x < this.width; x++) {
+ const idx = (this.width * y + x) << 2;
+ const r = this.data[idx];
+ const g = this.data[idx + 1];
+ const b = this.data[idx + 2];
+ const a = this.data[idx + 3];
+
+ pixels.push({ r, g, b, a });
+ }
+ }
+ resolve({ pixels, width: this.width, height: this.height });
+ })
+ ;
+});
+
+/**
+ * Writes an image to an output file.
+ * @param image Image to write.
+ * @param file File to write to.
+ * @returns A promise that, when resolved, has saved the image.
+ */
+const imageWrite = (image, file) => new Promise(resolve => {
+ const png = new PNG({ width: image.width, height: image.height });
+ png.width = image.width;
+ png.height = image.height;
+
+ for(let y = 0; y < image.height; y++) {
+ for(let x = 0; x < image.width; x++) {
+ const i = (image.width * y + x);
+ const idx = i << 2;
+
+ const pixel = image.pixels[i];
+ png.data[idx] = pixel.r;
+ png.data[idx + 1] = pixel.g;
+ png.data[idx + 2] = pixel.b;
+ png.data[idx + 3] = pixel.a;
+ }
+ }
+
+ png.pack().pipe(fs.createWriteStream(file))
+ .on('close', () => resolve(true))
+ ;
+});
+
+/**
+ * Creates a blank image
+ * @param width Width of the image.
+ * @param height Height of the image.
+ * @param fill Optional pixel to fill with, defaults to 0,0,0,0
+ * @returns The newly created image.
+ */
+const imageCreate = (width, height, pixel) => {
+ if(!pixel || !pixel.r) pixel = { r:0, g:0, b:0, a:0 };
+ const pixels = [];
+ for(let i = 0; i < width * height; i++) pixels.push({ ...pixel });
+ return { pixels, width, height };
+}
+
+/**
+ * Copies an area of a source image into a target image.
+ * @param target Target image to copy into.
+ * @param source Source image to copy from.
+ * @param tx Target X position to copy into
+ * @param ty Target Y position to copy into
+ * @param sub Optional source area to use, defined as { x, y, width, height }.
+ */
+const imageCopy = (target, source, tx, ty, sub) => {
+ if(!sub) sub = { x: 0, y: 0, width: source.width, height: source.height };
+
+ for(let x = sub.x; x < sub.x+sub.width; x++) {
+ for(let y = sub.y; y < sub.y+sub.height; y++) {
+ let absX = x - sub.x + tx;
+ let absY = y - sub.y + ty;
+ if(absX > target.width || absY > target.height) continue;
+ let ti = absY * target.width + absX;
+ let si = y * source.width + x;
+ target.pixels[ti] = { ...source.pixels[si] };
+ }
+ }
+}
+
+module.exports = {
+ imageWrite,
+ imageCreate,
+ imageLoad,
+ imageCopy
+}
\ No newline at end of file
diff --git a/tools/vn/character-sheet-generator.js b/tools/vn/character-sheet-generator.js
new file mode 100644
index 00000000..0144d11b
--- /dev/null
+++ b/tools/vn/character-sheet-generator.js
@@ -0,0 +1,88 @@
+const path = require('path');
+const { imageCreate, imageWrite, imageLoad, imageCopy } = require('./../utils/image');
+const fs = require('fs');
+const xml = require('xml-js');
+const { args } = require('./../utils/args');
+
+// Parse Args
+if(!args.in) throw new Error(`Missing in argument`);
+if(!args.out) throw new Error(`Missing out argument`);
+if(!args.in.endsWith('xml')) throw new Error(`Invalid in XML`);
+if(!args.out.endsWith('png')) throw new Error(`Invalid out PNG`);
+
+// Determine in and out.
+const file = path.resolve(args.in);
+const outFile = path.resolve(args.out);
+if(fs.existsSync(outFile)) return;
+
+// Load XML
+const data = xml.xml2js(fs.readFileSync(file, 'utf-8'));
+const [ character ] = data.elements;
+
+// Validate file.
+if(!character.attributes.context) throw new Error(`Missing context`)
+const dir = path.resolve('.', 'assets', character.attributes.context);
+
+// Parse base and layers
+const base = character.elements.find(e => e.name == 'base').attributes;
+if(!base) throw new Error(`Failed to find base`);
+const layers = character.elements
+ .filter(e => e.name == 'layer')
+ .map(e => e.attributes)
+ .map(e => ({
+ ...e,
+ x: parseInt(e.x),
+ y: parseInt(e.y),
+ width: parseInt(e.width),
+ height: parseInt(e.height)
+ }))
+;
+
+(async () => {
+ // Load the base
+ const baseImage = await imageLoad(path.join(dir, base.file));
+
+ let columnsMax = 0;
+ let widthMax = 0;
+ layers.forEach((layer,row) => {
+ if(!layer.width || !layer.height || !layer.x || !layer.y) {
+ throw new Error(`Missing layer info`);
+ }
+
+ const layerDir = path.join(dir, layer.directory);
+ const scan = fs.readdirSync(layerDir);
+ columnsMax = Math.max(scan.length, columnsMax);
+ widthMax = Math.max(widthMax, layer.width);
+ });
+
+ // Create the output buffer
+ const out = imageCreate(
+ baseImage.width + (columnsMax * widthMax),
+ baseImage.height
+ );
+
+ // Copy the base
+ imageCopy(out, baseImage, 0, 0);
+
+ // Now begin copying the children, row is defined by the directory
+ let y = 0;
+ for(let row = 0; row < layers.length; row++) {
+ const layer = layers[row];
+ const layerDir = path.join(dir, layer.directory);
+ const scan = fs.readdirSync(layerDir);
+
+ // Column defined by the file index
+ for(let col = 0; col < scan.length; col++) {
+ const img = await imageLoad(path.join(layerDir, scan[col]));
+ console.log('Copying', scan[col]);
+ imageCopy(out, img,
+ baseImage.width+(col*layer.width), y,
+ layer
+ );
+ }
+
+ y += layer.height;
+ }
+
+ await imageWrite(out, outFile);
+})().catch(console.error);
\ No newline at end of file