diff --git a/CMakeLists.txt b/CMakeLists.txt index aa99c7e..3272084 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,9 @@ add_executable(${DUSK_TARGET_NAME}) # Add tools add_subdirectory(tools) +# Add data +add_subdirectory(data) + # Add code add_subdirectory(src) diff --git a/cmake/toolchains/gbdk.cmake b/cmake/toolchains/gbdk.cmake deleted file mode 100644 index 18631b5..0000000 --- a/cmake/toolchains/gbdk.cmake +++ /dev/null @@ -1,49 +0,0 @@ -set(GBDK_HOME "$ENV{GBDK_HOME}") -if(NOT GBDK_HOME) - set(GBDK_HOME "/opt/gbdk") - message(STATUS "GBDK_HOME not set, using default: ${GBDK_HOME}") -endif() - -# Use Linux to omit any unwanted file extension in the created ROM file -set(CMAKE_SYSTEM_NAME Generic) - -set(CMAKE_C_COMPILER lcc) -set(CMAKE_C_COMPILER_FORCED TRUE) - -set(GBDK_INCLUDE_DIR ${GBDK_HOME}/include) -set(GBDK_LIB_DIR ${GBDK_HOME}/lib) - -set(CMAKE_PROGRAM_PATH ${GBDK_HOME}/bin) -set(CMAKE_INCLUDE_PATH ${GBDK_INCLUDE_DIR}) -set(CMAKE_LIBRARY_PATH ${GBDK_LIB_DIR}) - - -set(CMAKE_SYSTEM_INCLUDE_PATH ${GBDK_INCLUDE_DIR}) -set(CMAKE_SYSTEM_LIBRARY_PATH ${GBDK_LIB_DIR}) - -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - -set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS FALSE) - -function(add_gb_rom target) - set(GB_MBC_TYPE 0) - if(ARGC GREATER_EQUAL 2) - set(GB_MBC_TYPE ${ARGV1}) - endif() - set(GB_ROM_BANKS 2) - if(ARGC GREATER_EQUAL 3) - set(GB_ROM_BANKS ${ARGV2}) - endif() - set(GB_RAM_BANKS 0) - if(ARGC GREATER_EQUAL 4) - set(GB_RAM_BANKS ${ARGV3}) - endif() - - - set_target_properties(${target} PROPERTIES OUTPUT_NAME ${target} SUFFIX ".gb") - target_compile_options(${target} PRIVATE -Wa-l -Wl-m -Wl-j -DUSE_SFR_FOR_REG) - target_link_options(${target} PRIVATE "-Wl-yt${GB_MBC_TYPE}" "-Wl-yo${GB_ROM_BANKS}" "-Wl-ya${GB_RAM_BANKS}") -endfunction() diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt new file mode 100644 index 0000000..e3241ab --- /dev/null +++ b/data/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +find_package(Python3 COMPONENTS Interpreter REQUIRED) + +# Custom command to generate all header files +add_custom_command( + OUTPUT ${DUSK_GENERATED_HEADERS_DIR}/world/world.h + COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/chunks.py --output ${DUSK_GENERATED_HEADERS_DIR} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/chunks.py + COMMENT "Generating chunk header files" + VERBATIM +) + +# Custom target to generate headers before build +add_custom_target(DUSK_CHUNKS + DEPENDS + ${DUSK_GENERATED_HEADERS_DIR}/world/world.h +) + +# Ensure headers are generated before compiling main +add_dependencies(${DUSK_TARGET_NAME} DUSK_CHUNKS) \ No newline at end of file diff --git a/data/chunks.py b/data/chunks.py new file mode 100644 index 0000000..bfd8287 --- /dev/null +++ b/data/chunks.py @@ -0,0 +1,166 @@ +#!/usr/bin/python3 +import os +import argparse +from datetime import datetime +import json + +# Constants that are defined in the C code +CHUNK_WIDTH = 8 +CHUNK_HEIGHT = 8 + +# Check if the script is run with the correct arguments +parser = argparse.ArgumentParser(description="Generate chunk header files") +parser.add_argument('--output', required=True, help='Dir to output headers') +args = parser.parse_args() + +# Ensure outdir exists +outputDir = args.output +os.makedirs(outputDir, exist_ok=True) + +# Create world directory if it does not exist +worldDir = os.path.join(outputDir, "world") +os.makedirs(worldDir, exist_ok=True) + +# Create chunks directory if it does not exist +chunksDir = os.path.join(worldDir, "chunk") +os.makedirs(chunksDir, exist_ok=True) + +# Scan ./chunks folder +chunks_dir = os.path.join(os.path.dirname(__file__), "chunks") +if not os.path.exists(chunks_dir): + print(f"Error: Chunks directory '{chunks_dir}' does not exist.") + exit(1) + +# Some vars used during printing +now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + +# Data sent to the world header file +worldWidth = 0 +worldHeight = 0 +chunksDone = [] + + + +# For each chunk file +for chunkFile in os.listdir(chunks_dir): + data = json.load(open(os.path.join(chunks_dir, chunkFile))) + print(f"Processing chunk: {chunkFile}") + + if not 'chunk' in data: + print(f"Error: Chunk file '{chunkFile}' does not contain 'chunk' key.") + exit(1) + + if not 'position' in data['chunk']: + print(f"Error: Chunk file '{chunkFile}' does not contain 'position' key.") + exit(1) + + # Position must be array of two integers + position = data['chunk']['position'] + if not isinstance(position, list) or len(position) != 2: + print(f"Error: Chunk file '{chunkFile}' has invalid 'position' format.") + exit(1) + if not all(isinstance(x, int) for x in position): + print(f"Error: Chunk file '{chunkFile}' invalid 'position' values.") + exit(1) + + x, y = position + + # Make sure that the filename "chunk_{x}_{y}.json" matches the position + expectedFilename = f"chunk_{x}_{y}.json" + if chunkFile != expectedFilename: + print(f"Error: Chunk file '{chunkFile}' should be '{expectedFilename}'.") + exit(1) + + # Chunk should not be already processed + if (x, y) in chunksDone: + print(f"Error: Chunk at position ({x}, {y}) is already processed. Skipping.") + exit(1) + chunksDone.append((x, y)) + + worldWidth = max(worldWidth, x + 1) + worldHeight = max(worldHeight, y + 1) + + # Read in base layer data + if 'baseLayer' not in data['chunk']: + print(f"Error: Chunk file '{chunkFile}' does not contain 'baseLayer' key.") + exit(1) + baseLayer = data['chunk']['baseLayer'] + + # Base layer should exactly CHUNK_WIDTH * CHUNK_HEIGHT elements + if len(baseLayer) != CHUNK_HEIGHT: + print(f"Error: Chunk file '{chunkFile}' has invalid 'baseLayer' length.") + exit(1) + for row in baseLayer: + if len(row) != CHUNK_WIDTH: + print(f"Error: Chunk file '{chunkFile}' has invalid 'baseLayer' row length.") + exit(1) + + # Read in overlay layer data + if 'overlayLayer' not in data['chunk']: + print(f"Error: Chunk file '{chunkFile}' does not contain 'overlayLayer' key.") + exit(1) + overlayLayer = data['chunk']['overlayLayer'] + # Overlay layer should exactly CHUNK_WIDTH * CHUNK_HEIGHT elements + if len(overlayLayer) != CHUNK_HEIGHT: + print(f"Error: Chunk file '{chunkFile}' has invalid 'overlayLayer' length.") + exit(1) + for row in overlayLayer: + if len(row) != CHUNK_WIDTH: + print(f"Error: Chunk file '{chunkFile}' has invalid 'overlayLayer' row length.") + exit(1) + + + # Now we generate a chunk header file + chunk_header_path = os.path.join(chunksDir, f"chunk_{x}_{y}.h") + with open(chunk_header_path, 'w') as f: + f.write(f"// Generated chunk header for chunk at position ({x}, {y})\n") + f.write(f"// Generated at {now}\n") + f.write("#pragma once\n") + f.write("#include \"dusk.h\"\n\n") + + f.write(f"static const uint8_t CHUNK_{x}_{y}_LAYER_BASE[] = {{\n") + for row in baseLayer: + f.write(" ") + for column in row: + f.write(f"0x{column:02x}, ") + f.write("\n") + f.write("};\n\n") + + f.write(f"static const uint8_t CHUNK_{x}_{y}_LAYER_OVERLAY[] = {{\n") + for row in overlayLayer: + f.write(" ") + for column in row: + f.write(f"0x{column:02x}, ") + f.write("\n") + f.write("};\n\n") + pass + + + +# Output header file. +header_path = os.path.join(worldDir, "world.h") +with open(header_path, 'w') as f: + f.write(f"// Generated chunks file. Generated at {now}\n\n") + f.write("#pragma once\n") + f.write("#include \"dusk.h\"\n") + + # Now, for each chunk, include its header file + for (x, y) in chunksDone: + chunk_header = f"world/chunk/chunk_{x}_{y}.h" + f.write(f"#include \"{chunk_header}\"\n") + + f.write("\n") + f.write(f"#define WORLD_WIDTH {worldWidth}\n") + f.write(f"#define WORLD_HEIGHT {worldHeight}\n\n") + f.write(f"static const uint8_t* WORLD_CHUNKS_BASE[] = {{\n") + for i in range(worldHeight): + f.write(" ") + for j in range(worldWidth): + if (j, i) in chunksDone: + f.write(f"CHUNK_{j}_{i}_LAYER_BASE, ") + else: + f.write("NULL, ") + f.write("\n") + f.write("};\n\n") + +print(f"chunks.h generated at: {header_path}") \ No newline at end of file diff --git a/data/chunks/chunk_0_0.jsonc b/data/chunks/chunk_0_0.jsonc deleted file mode 100644 index d0724c1..0000000 --- a/data/chunks/chunk_0_0.jsonc +++ /dev/null @@ -1,45 +0,0 @@ -{ - "chunk": { - "position": [ 0, 0 ], - "baseLayer": [ - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ], - [ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 ] - ], - "overlayLayer": [ - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], - [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] - ], - "entities": [ - ], - "triggers": [ - ] - } -} diff --git a/data/chunks/chunk_1_1.json b/data/chunks/chunk_1_1.json new file mode 100644 index 0000000..937d180 --- /dev/null +++ b/data/chunks/chunk_1_1.json @@ -0,0 +1,29 @@ +{ + "chunk": { + "position": [ 1, 1 ], + "baseLayer": [ + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ] + ], + "overlayLayer": [ + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ] + ], + "entities": [ + ], + "triggers": [ + ] + } +} diff --git a/data/chunks/chunk_2_1.json b/data/chunks/chunk_2_1.json new file mode 100644 index 0000000..080182f --- /dev/null +++ b/data/chunks/chunk_2_1.json @@ -0,0 +1,29 @@ +{ + "chunk": { + "position": [ 2, 1 ], + "baseLayer": [ + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ] + ], + "overlayLayer": [ + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ], + [ 1, 1, 1, 1, 1, 1, 1, 1 ] + ], + "entities": [ + ], + "triggers": [ + ] + } +} diff --git a/src/dusk/world/chunk.c b/src/dusk/world/chunk.c index 2f691ab..4c35882 100644 --- a/src/dusk/world/chunk.c +++ b/src/dusk/world/chunk.c @@ -8,6 +8,7 @@ #include "chunk.h" #include "util/memory.h" #include "assert/assert.h" +#include "world/world.h" chunkmap_t CHUNK_MAP; @@ -175,14 +176,23 @@ void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y) { chunk->x = x; chunk->y = y; - if( - ((x % 2 == 0) && (y % 2 == 0)) || - ((x % 2 == 1) && (y % 2 == 1)) - ) { - memorySet(chunk->tiles, 1, sizeof(chunk->tiles)); - } else { + if(x >= WORLD_WIDTH || y >= WORLD_HEIGHT) { memorySet(chunk->tiles, 0, sizeof(chunk->tiles)); + return; } + + const uint8_t *chunkLayerBase = WORLD_CHUNKS_BASE[y * WORLD_WIDTH + x]; + if(chunkLayerBase == NULL) { + memorySet(chunk->tiles, 0, sizeof(chunk->tiles)); + return; + } + + printf("Loading chunk at (%u, %u)\n", x, y); + memoryCopy( + chunk->tiles, + chunkLayerBase, + sizeof(chunk->tiles) + ); } void chunkUnload(chunk_t *chunk) { diff --git a/src/dusk/world/overworld.c b/src/dusk/world/overworld.c index 7993dee..0aa2b81 100644 --- a/src/dusk/world/overworld.c +++ b/src/dusk/world/overworld.c @@ -35,13 +35,13 @@ void overworldUpdate() { if(OVERWORLD_CAMERA_X < RENDER_WIDTH / 2) { x = 0; } else { - x = (OVERWORLD_CAMERA_X - (RENDER_WIDTH / 2)) / (CHUNK_WIDTH * TILE_WIDTH); + x = (OVERWORLD_CAMERA_X - (RENDER_WIDTH / 2)) / (CHUNK_WIDTH*TILE_WIDTH); } if(OVERWORLD_CAMERA_Y < RENDER_HEIGHT / 2) { y = 0; } else { - y = (OVERWORLD_CAMERA_Y - (RENDER_HEIGHT / 2)) / (CHUNK_HEIGHT * TILE_HEIGHT); + y = (OVERWORLD_CAMERA_Y -(RENDER_HEIGHT / 2)) / (CHUNK_HEIGHT*TILE_HEIGHT); } chunkMapSetPosition(x, y);