diff --git a/data/CMakeLists.txt b/data/CMakeLists.txt index e3241ab..6299e7a 100644 --- a/data/CMakeLists.txt +++ b/data/CMakeLists.txt @@ -6,19 +6,13 @@ find_package(Python3 COMPONENTS Interpreter REQUIRED) # Custom command to generate all header files -add_custom_command( - OUTPUT ${DUSK_GENERATED_HEADERS_DIR}/world/world.h +add_custom_target(DUSK_CHUNKS + # 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 index bfd8287..2de3457 100644 --- a/data/chunks.py +++ b/data/chunks.py @@ -7,6 +7,11 @@ import json # Constants that are defined in the C code CHUNK_WIDTH = 8 CHUNK_HEIGHT = 8 +CHUNK_ENTITY_COUNT_MAX = 8 + +ENTITY_TYPE = { + "npc": "ENTITY_TYPE_NPC", +} # Check if the script is run with the correct arguments parser = argparse.ArgumentParser(description="Generate chunk header files") @@ -38,8 +43,7 @@ now = datetime.now().strftime("%Y-%m-%d %H:%M:%S") worldWidth = 0 worldHeight = 0 chunksDone = [] - - +entityIdNext = 1 # For each chunk file for chunkFile in os.listdir(chunks_dir): @@ -109,6 +113,8 @@ for chunkFile in os.listdir(chunks_dir): print(f"Error: Chunk file '{chunkFile}' has invalid 'overlayLayer' row length.") exit(1) + # Read in entities + entities = data['chunk'].get('entities', []) # Now we generate a chunk header file chunk_header_path = os.path.join(chunksDir, f"chunk_{x}_{y}.h") @@ -116,22 +122,51 @@ for chunkFile in os.listdir(chunks_dir): 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("#include \"world/chunkdata.h\"\n\n") - f.write(f"static const uint8_t CHUNK_{x}_{y}_LAYER_BASE[] = {{\n") + f.write(f"static const chunkdata_t CHUNK_{x}_{y} = {{\n") + + f.write(f" .layerBase = {{\n") for row in baseLayer: - f.write(" ") + f.write(" ") for column in row: f.write(f"0x{column:02x}, ") f.write("\n") - f.write("};\n\n") + f.write(" },\n\n") - f.write(f"static const uint8_t CHUNK_{x}_{y}_LAYER_OVERLAY[] = {{\n") + f.write(f" .layerOverlay = {{\n") for row in overlayLayer: - f.write(" ") + f.write(" ") for column in row: f.write(f"0x{column:02x}, ") f.write("\n") + f.write(" },\n\n") + + f.write(f" .entities = {{\n") + for entity in entities: + if 'id' in entity: + entityId = entity['id'] + else: + entityId = entityIdNext + entityIdNext += 1 + + if 'type' not in entity: + print(f"Error: Entity in chunk ({x}, {y}) does not have 'type' key.") + exit(1) + + if 'x' not in entity or 'y' not in entity: + print(f"Error: Entity in chunk ({x}, {y}) does not have 'x' or 'y' key.") + exit(1) + + f.write(" {\n") + f.write(f" .id = {entityId},\n") + f.write(f" .type = {ENTITY_TYPE.get(entity['type'], 'ENTITY_TYPE_UNKNOWN')},\n"), + f.write(f" .x = {entity['x']},\n") + f.write(f" .y = {entity['y']},\n") + f.write(f" }},\n") + pass + f.write(" },\n\n") + f.write("};\n\n") pass @@ -152,12 +187,12 @@ with open(header_path, 'w') as f: 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") + f.write(f"static const chunkdata_t* WORLD_CHUNKS[] = {{\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, ") + f.write(f"&CHUNK_{j}_{i}, ") else: f.write("NULL, ") f.write("\n") diff --git a/data/chunks/chunk_1_1.json b/data/chunks/chunk_0_0.json similarity index 87% rename from data/chunks/chunk_1_1.json rename to data/chunks/chunk_0_0.json index 937d180..b5cecbd 100644 --- a/data/chunks/chunk_1_1.json +++ b/data/chunks/chunk_0_0.json @@ -1,6 +1,6 @@ { "chunk": { - "position": [ 1, 1 ], + "position": [ 0, 0 ], "baseLayer": [ [ 1, 1, 1, 1, 1, 1, 1, 1 ], [ 1, 1, 1, 1, 1, 1, 1, 1 ], @@ -22,6 +22,11 @@ [ 1, 1, 1, 1, 1, 1, 1, 1 ] ], "entities": [ + { + "type": "npc", + "x": "3", + "y": "3" + } ], "triggers": [ ] diff --git a/src/dusk/CMakeLists.txt b/src/dusk/CMakeLists.txt index 1fadb62..ac89f59 100644 --- a/src/dusk/CMakeLists.txt +++ b/src/dusk/CMakeLists.txt @@ -18,10 +18,12 @@ target_include_directories(${DUSK_TARGET_NAME} target_sources(${DUSK_TARGET_NAME} PRIVATE main.c + input.c ) # Subdirs add_subdirectory(assert) add_subdirectory(display) +add_subdirectory(entity) add_subdirectory(util) add_subdirectory(world) \ No newline at end of file diff --git a/src/dusk/display/renderbase.c b/src/dusk/display/renderbase.c index 9331e01..3cd48c8 100644 --- a/src/dusk/display/renderbase.c +++ b/src/dusk/display/renderbase.c @@ -5,4 +5,6 @@ * https://opensource.org/licenses/MIT */ -#include "display/render.h" \ No newline at end of file +#include "display/render.h" + +uint8_t RENDER_FRAME = 0; \ No newline at end of file diff --git a/src/dusk/display/renderbase.h b/src/dusk/display/renderbase.h index 8c3843e..cb227ff 100644 --- a/src/dusk/display/renderbase.h +++ b/src/dusk/display/renderbase.h @@ -11,6 +11,8 @@ #define RENDER_WIDTH 320 #define RENDER_HEIGHT 240 +extern uint8_t RENDER_FRAME; + /** * Initializes the rendering system. */ diff --git a/src/dusk/dusk.h b/src/dusk/dusk.h index 55264fe..7a00120 100644 --- a/src/dusk/dusk.h +++ b/src/dusk/dusk.h @@ -11,6 +11,7 @@ #include #include #include +#include typedef bool bool_t; typedef int int_t; diff --git a/src/dusk/entity/CMakeLists.txt b/src/dusk/entity/CMakeLists.txt new file mode 100644 index 0000000..c48e5a5 --- /dev/null +++ b/src/dusk/entity/CMakeLists.txt @@ -0,0 +1,12 @@ +# Copyright (c) 2025 Dominic Masters +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +# Sources +target_sources(${DUSK_TARGET_NAME} + PRIVATE + entity.c + player.c + npc.c +) \ No newline at end of file diff --git a/src/dusk/entity/entity.c b/src/dusk/entity/entity.c new file mode 100644 index 0000000..661b00d --- /dev/null +++ b/src/dusk/entity/entity.c @@ -0,0 +1,42 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "entity.h" +#include "assert/assert.h" + +entity_t ENTITIES[ENTITY_COUNT_MAX] = {0}; + +entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = { + {NULL}, // ENTITY_TYPE_NULL + {playerNPCInit, playerNPCUpdate}, // ENTITY_TYPE_PLAYER + {npcInit, npcUpdate} // ENTITY_TYPE_NPC +}; + +void entityInit(entity_t *entity, const entitytype_t type) { + assertNotNull(entity, "Entity pointer cannot be NULL"); + assertTrue(type != ENTITY_TYPE_NULL, "Entity type NULL"); + assertTrue(type < ENTITY_TYPE_COUNT, "Entity type out of bounds"); + assertNotNull( + ENTITY_CALLBACKS[type].init, + "Entity type has no init callback" + ); + + entity->type = type; + ENTITY_CALLBACKS[type].init(entity); +} + +void entityUpdate(entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); + assertTrue(entity->type != ENTITY_TYPE_NULL, "Entity type NULL"); + assertTrue(entity->type < ENTITY_TYPE_COUNT, "Entity type out of bounds"); + assertNotNull( + ENTITY_CALLBACKS[entity->type].update, + "Entity type has no update callback" + ); + + ENTITY_CALLBACKS[entity->type].update(entity); +} \ No newline at end of file diff --git a/src/dusk/entity/entity.h b/src/dusk/entity/entity.h new file mode 100644 index 0000000..1fc8737 --- /dev/null +++ b/src/dusk/entity/entity.h @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "player.h" +#include "npc.h" + +#define ENTITY_COUNT_MAX 32 + +typedef enum { + ENTITY_DIR_NORTH = 0, + ENTITY_DIR_SOUTH = 1, + ENTITY_DIR_WEST = 2, + ENTITY_DIR_EAST = 3, + + ENTITY_DIR_UP = ENTITY_DIR_NORTH, + ENTITY_DIR_DOWN = ENTITY_DIR_SOUTH, + ENTITY_DIR_LEFT = ENTITY_DIR_WEST, + ENTITY_DIR_RIGHT = ENTITY_DIR_EAST, +} entitydir_t; + +typedef enum { + ENTITY_TYPE_NULL = 0, + ENTITY_TYPE_PLAYER = 1, + ENTITY_TYPE_NPC = 2, +} entitytype_t; +#define ENTITY_TYPE_COUNT 3 + +typedef struct _entity_t { + uint32_t id;// Completely unique ID for this entity. + float_t x, y; + entitytype_t type; + entitydir_t dir; + + union { + npc_t npc; + playerentity_t player; + }; +} entity_t; + +typedef struct { + void (*init) (entity_t *entity); + void (*update) (entity_t *entity); +} entitycallback_t; + +extern entity_t ENTITIES[ENTITY_COUNT_MAX]; +extern entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT]; + +/** + * Initializes an entity with the given type. + * + * @param entity Pointer to the entity to initialize. + * @param type The type of the entity to initialize. + */ +void entityInit(entity_t *entity, const entitytype_t type); + +/** + * Updates the entity's state. + * + * @param entity Pointer to the entity to update. + */ +void entityUpdate(entity_t *entity); \ No newline at end of file diff --git a/src/dusk/entity/npc.c b/src/dusk/entity/npc.c new file mode 100644 index 0000000..768db5c --- /dev/null +++ b/src/dusk/entity/npc.c @@ -0,0 +1,16 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "npc.h" + +void npcInit(entity_t *entity) { + +} + +void npcUpdate(entity_t *entity) { + +} \ No newline at end of file diff --git a/src/dusk/entity/npc.h b/src/dusk/entity/npc.h new file mode 100644 index 0000000..ed77799 --- /dev/null +++ b/src/dusk/entity/npc.h @@ -0,0 +1,23 @@ + +#pragma once +#include "dusk.h" + +typedef struct _entity_t entity_t; + +typedef struct { + uint32_t nothing; +} npc_t; + +/** + * Initializes the NPC entity. + * + * @param entity The entity to initialize. + */ +void npcInit(entity_t *entity); + +/** + * Updates the NPC entity. + * + * @param entity The entity to update. + */ +void npcUpdate(entity_t *entity); \ No newline at end of file diff --git a/src/dusk/entity/player.c b/src/dusk/entity/player.c new file mode 100644 index 0000000..02c6c19 --- /dev/null +++ b/src/dusk/entity/player.c @@ -0,0 +1,77 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "entity.h" +#include "assert/assert.h" +#include "input.h" +#include "display/render.h" + +void playerInit() { + entity_t *ent = &ENTITIES[0]; + + entityInit(ent, ENTITY_TYPE_PLAYER); + ent->id = PLAYER_ENTITY_ID; + + ent->x = 32; + ent->y = 32; +} + +void playerNPCInit(entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); + assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER"); +} + +void playerNPCUpdate(entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); + assertTrue(entity->type == ENTITY_TYPE_PLAYER, "Entity type must be PLAYER"); + + if(inputIsDown(INPUT_BIND_UP)) { + if(inputIsDown(INPUT_BIND_LEFT)) { + entity->x -= PLAYER_MOVE_SPEED_XY; + entity->y -= PLAYER_MOVE_SPEED_XY; + + if(entity->dir != ENTITY_DIR_NORTH && entity->dir != ENTITY_DIR_WEST) { + entity->dir = ENTITY_DIR_NORTH; + } + } else if(inputIsDown(INPUT_BIND_RIGHT)) { + entity->x += PLAYER_MOVE_SPEED_XY; + entity->y -= PLAYER_MOVE_SPEED_XY; + + if(entity->dir != ENTITY_DIR_NORTH && entity->dir != ENTITY_DIR_EAST) { + entity->dir = ENTITY_DIR_NORTH; + } + } else { + entity->y -= PLAYER_MOVE_SPEED; + entity->dir = ENTITY_DIR_NORTH; + } + } else if(inputIsDown(INPUT_BIND_DOWN)) { + if(inputIsDown(INPUT_BIND_LEFT)) { + entity->x -= PLAYER_MOVE_SPEED_XY; + entity->y += PLAYER_MOVE_SPEED_XY; + + if(entity->dir != ENTITY_DIR_SOUTH && entity->dir != ENTITY_DIR_WEST) { + entity->dir = ENTITY_DIR_SOUTH; + } + } else if(inputIsDown(INPUT_BIND_RIGHT)) { + entity->x += PLAYER_MOVE_SPEED_XY; + entity->y += PLAYER_MOVE_SPEED_XY; + + if(entity->dir != ENTITY_DIR_SOUTH && entity->dir != ENTITY_DIR_EAST) { + entity->dir = ENTITY_DIR_SOUTH; + } + } else { + entity->y += PLAYER_MOVE_SPEED; + entity->dir = ENTITY_DIR_SOUTH; + } + } else if(inputIsDown(INPUT_BIND_LEFT)) { + entity->x -= PLAYER_MOVE_SPEED; + entity->dir = ENTITY_DIR_WEST; + } else if(inputIsDown(INPUT_BIND_RIGHT)) { + entity->x += PLAYER_MOVE_SPEED; + entity->dir = ENTITY_DIR_EAST; + } +} \ No newline at end of file diff --git a/src/dusk/entity/player.h b/src/dusk/entity/player.h index 1e6be71..1b496ac 100644 --- a/src/dusk/entity/player.h +++ b/src/dusk/entity/player.h @@ -6,4 +6,33 @@ */ #pragma once -#include "dusk.h" \ No newline at end of file +#include "dusk.h" + +typedef struct _entity_t entity_t; + +typedef struct { + uint32_t nothing; +} playerentity_t; + +#define PLAYER_ENTITY_ID (UINT32_MAX-1) +#define PLAYER_MOVE_SPEED 1.0f +#define PLAYER_MOVE_SPEED_XY (PLAYER_MOVE_SPEED * 0.70710678118f) + +/** + * Initializes the player and all player-related entities. + */ +void playerInit(void); + +/** + * Initializes the player entity. + * + * @param entity The entity to initialize. + */ +void playerNPCInit(entity_t *entity); + +/** + * Updates the player entity. + * + * @param entity The entity to update. + */ +void playerNPCUpdate(entity_t *entity); \ No newline at end of file diff --git a/src/dusk/input.c b/src/dusk/input.c new file mode 100644 index 0000000..f4654e8 --- /dev/null +++ b/src/dusk/input.c @@ -0,0 +1,39 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "input.h" +#include "assert/assert.h" +#include "util/memory.h" + +input_t INPUT; + +void inputInit(void) { + memoryZero(&INPUT, sizeof(input_t)); +} + +void inputUpdate(void) { + INPUT.previous = INPUT.current; + INPUT.current = inputStateGet(); +} + +bool_t inputIsDown(const uint8_t bind) { + assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds"); + return (INPUT.current & bind) != 0; +} + +bool_t inputWasDown(const uint8_t bind) { + assertTrue(bind < INPUT_BIND_COUNT, "Input bind out of bounds"); + return (INPUT.previous & bind) != 0; +} + +bool_t inputPressed(const uint8_t bind) { + return inputIsDown(bind) && !inputWasDown(bind); +} + +bool_t inputReleased(const uint8_t bind) { + return !inputIsDown(bind) && inputWasDown(bind); +} \ No newline at end of file diff --git a/src/dusk/input.h b/src/dusk/input.h new file mode 100644 index 0000000..5a28a28 --- /dev/null +++ b/src/dusk/input.h @@ -0,0 +1,74 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "dusk.h" + +#define INPUT_BIND_UP (1 << 0) +#define INPUT_BIND_DOWN (1 << 1) +#define INPUT_BIND_LEFT (1 << 2) +#define INPUT_BIND_RIGHT (1 << 3) +#define INPUT_BIND_ACTION (1 << 4) +#define INPUT_BIND_CANCEL (1 << 5) +#define INPUT_BIND_COUNT (INPUT_BIND_CANCEL + 1) + +typedef struct { + uint8_t current; + uint8_t previous; +} input_t; + +extern input_t INPUT; + +/** + * Initialize the input system. + */ +void inputInit(void); + +/** + * Updates the input state. + */ +void inputUpdate(void); + +/** + * Gets the current input state as a bitmask. + * + * @return The current input state as a bitmask. + */ +uint8_t inputStateGet(void); + +/** + * Checks if a specific input bind is currently pressed. + * + * @param bind The input bind to check. + * @return true if the bind is currently pressed, false otherwise. + */ +bool_t inputIsDown(const uint8_t bind); + +/** + * Checks if a specific input bind was pressed in the last update. + * + * @param bind The input bind to check. + * @return true if the bind was pressed in the last update, false otherwise. + */ +bool_t inputWasDown(const uint8_t bind); + +/** + * Checks if a specific input bind was down this frame but not in the the + * previous frame. + * + * @param bind The input bind to check. + * @return true if the bind is currently pressed, false otherwise. + */ +bool_t inputPressed(const uint8_t bind); + +/** + * Checks if a specific input bind was released this frame. + * + * @param bind The input bind to check. + * @return true if the bind was released this frame, false otherwise. + */ +bool_t inputReleased(const uint8_t bind); \ No newline at end of file diff --git a/src/dusk/main.c b/src/dusk/main.c index bdb612c..9e80948 100644 --- a/src/dusk/main.c +++ b/src/dusk/main.c @@ -10,19 +10,25 @@ #include "world/chunk.h" #include "display/scene.h" #include "world/overworld.h" +#include "input.h" // Press F5 to compile and run the compiled game.gb in the Emulicous Emulator/Debugger void main(void) { renderInit(); + inputInit(); overworldInit(); SCENE_CURRENT = SCENE_OVERWORLD; while(!renderShouldExit()) { + RENDER_FRAME++; renderDraw(); overworldUpdate(); + + // Update input for next frame. + inputUpdate(); } renderDispose(); diff --git a/src/dusk/world/chunk.c b/src/dusk/world/chunk.c index 4c35882..6385c3d 100644 --- a/src/dusk/world/chunk.c +++ b/src/dusk/world/chunk.c @@ -149,7 +149,6 @@ void chunkMapShift(const int16_t x, const int16_t y) { ); } - void chunkMapSetPosition(const uint16_t x, const uint16_t y) { if(x == CHUNK_MAP.topLeftX && y == CHUNK_MAP.topLeftY) { return; @@ -181,18 +180,63 @@ void chunkLoad(chunk_t *chunk, const uint16_t x, const uint16_t y) { return; } - const uint8_t *chunkLayerBase = WORLD_CHUNKS_BASE[y * WORLD_WIDTH + x]; - if(chunkLayerBase == NULL) { + const chunkdata_t *chunkData = WORLD_CHUNKS[y * WORLD_WIDTH + x]; + if(chunkData == NULL) { memorySet(chunk->tiles, 0, sizeof(chunk->tiles)); return; } + // Load tile data into chunk printf("Loading chunk at (%u, %u)\n", x, y); memoryCopy( chunk->tiles, - chunkLayerBase, + chunkData->layerBase, sizeof(chunk->tiles) ); + + // Load chunk entities + const chunkentity_t *data; + entity_t *entity; + data = chunkData->entities; + while(data < chunkData->entities + CHUNK_ENTITY_COUNT_MAX) { + if(data->type == ENTITY_TYPE_NULL) break; + + // Check entity isn't loaded (still). + entity = ENTITIES; + do { + if(entity->id == data->id) break; + entity++; + } while(entity < ENTITIES + ENTITY_COUNT_MAX); + + if(entity != ENTITIES + ENTITY_COUNT_MAX) { + // Entity is already loaded, skip it. + data++; + continue; + } + + // Find an empty entity slot. + entity = ENTITIES; + while(true) { + assertTrue( + entity < ENTITIES + ENTITY_COUNT_MAX, + "Out of available entities" + ); + + if(entity->type == ENTITY_TYPE_NULL) break; + entity++; + }; + + // Initialize this entity. + entityInit(entity, data->type); + entity->id = data->id; + + // Positions are chunk-relative. + entity->x = (chunk->x * CHUNK_WIDTH * TILE_WIDTH) + data->x; + entity->y = (chunk->y * CHUNK_HEIGHT * TILE_HEIGHT) + data->y; + + // Next entity + data++; + } } void chunkUnload(chunk_t *chunk) { diff --git a/src/dusk/world/chunk.h b/src/dusk/world/chunk.h index 20e26d4..0b5d277 100644 --- a/src/dusk/world/chunk.h +++ b/src/dusk/world/chunk.h @@ -15,10 +15,13 @@ #define CHUNK_WIDTH 8 #define CHUNK_HEIGHT 8 #define CHUNK_TILE_COUNT (CHUNK_WIDTH * CHUNK_HEIGHT) +#define CHUNK_ENTITY_COUNT_MAX 8 typedef struct { uint16_t x, y; tile_t tiles[CHUNK_TILE_COUNT]; + // int32_t entityIDs[CHUNK_ENTITY_COUNT_MAX]; + // uint8_t entityCount; } chunk_t; typedef struct { diff --git a/src/dusk/world/chunkdata.h b/src/dusk/world/chunkdata.h new file mode 100644 index 0000000..a477bd3 --- /dev/null +++ b/src/dusk/world/chunkdata.h @@ -0,0 +1,23 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#pragma once +#include "chunk.h" +#include "entity/entity.h" + +typedef struct { + uint32_t id; + entitytype_t type; + uint8_t x, y; + entitydir_t dir; +} chunkentity_t; + +typedef struct { + uint8_t layerBase[CHUNK_TILE_COUNT]; + uint8_t layerOverlay[CHUNK_TILE_COUNT]; + chunkentity_t entities[CHUNK_ENTITY_COUNT_MAX]; +} chunkdata_t; \ No newline at end of file diff --git a/src/dusk/world/overworld.c b/src/dusk/world/overworld.c index 0aa2b81..9b0e16d 100644 --- a/src/dusk/world/overworld.c +++ b/src/dusk/world/overworld.c @@ -9,19 +9,24 @@ #include "chunk.h" #include "display/render.h" #include "assert/assert.h" +#include "entity/entity.h" uint32_t OVERWORLD_CAMERA_X; uint32_t OVERWORLD_CAMERA_Y; overworldcameratype_t OVERWORLD_CAMERA_TYPE; void overworldInit(void) { + playerInit(); chunkMapInit(); + OVERWORLD_CAMERA_X = 0; OVERWORLD_CAMERA_Y = 0; OVERWORLD_CAMERA_TYPE = OVERWORLD_CAMERA_TYPE_CENTERED_POSITION; } void overworldUpdate() { + entity_t *entity; + assertTrue( OVERWORLD_CAMERA_X < OVERWORLD_CAMERA_LIMIT_X, "Camera position limit (just because I haven't tested properly)" @@ -30,6 +35,11 @@ void overworldUpdate() { OVERWORLD_CAMERA_Y < OVERWORLD_CAMERA_LIMIT_Y, "Camera position limit (just because I haven't tested properly)" ); + + entity = ENTITIES; + do { + entityUpdate(entity++); + } while(entity->type != ENTITY_TYPE_NULL); uint16_t x, y; if(OVERWORLD_CAMERA_X < RENDER_WIDTH / 2) { diff --git a/src/duskraylib/CMakeLists.txt b/src/duskraylib/CMakeLists.txt index 3df503d..0dbe76c 100644 --- a/src/duskraylib/CMakeLists.txt +++ b/src/duskraylib/CMakeLists.txt @@ -27,6 +27,7 @@ target_include_directories(${DUSK_TARGET_NAME} # Sources target_sources(${DUSK_TARGET_NAME} PRIVATE + input.c ) # Subdirs diff --git a/src/duskraylib/display/draw/drawoverworld.c b/src/duskraylib/display/draw/drawoverworld.c index 649d670..d579cb5 100644 --- a/src/duskraylib/display/draw/drawoverworld.c +++ b/src/duskraylib/display/draw/drawoverworld.c @@ -9,6 +9,7 @@ #include "world/chunk.h" #include "world/overworld.h" #include "display/render.h" +#include "assert/assert.h" Camera2D DRAW_OVERWORLD_CAMERA = { 0 }; @@ -36,6 +37,7 @@ void drawOverworldDraw(void) { BeginMode2D(DRAW_OVERWORLD_CAMERA); + // Bottom layer chunk_t *chunk = CHUNK_MAP.chunks; do { DrawRectangle( @@ -48,5 +50,62 @@ void drawOverworldDraw(void) { chunk++; } while(chunk < CHUNK_MAP.chunks + CHUNK_MAP_COUNT); + // Entities + entity_t *entity = ENTITIES; + do { + drawOverworldDrawEntity(entity++); + } while(entity->type != ENTITY_TYPE_NULL); + EndMode2D(); +} + + +void drawOverworldDrawEntity(const entity_t *entity) { + assertNotNull(entity, "Entity pointer cannot be NULL"); + + uint32_t x = (uint32_t)floorf(entity->x); + uint32_t y = (uint32_t)floorf(entity->y); + + DrawRectangle( + x, + y, + TILE_WIDTH, + TILE_HEIGHT, + (entity->type == ENTITY_TYPE_PLAYER) ? BLUE : YELLOW + ); + + switch(entity->dir) { + case ENTITY_DIR_NORTH: + DrawTriangle( + (Vector2){ x + TILE_WIDTH / 2, y }, + (Vector2){ x, y + TILE_HEIGHT }, + (Vector2){ x + TILE_WIDTH, y + TILE_HEIGHT }, + WHITE + ); + break; + case ENTITY_DIR_SOUTH: + DrawTriangle( + (Vector2){ x + TILE_WIDTH / 2, y + TILE_HEIGHT }, + (Vector2){ x + TILE_WIDTH, y }, + (Vector2){ x, y }, + WHITE + ); + break; + case ENTITY_DIR_WEST: + DrawTriangle( + (Vector2){ x, y + TILE_HEIGHT / 2 }, + (Vector2){ x + TILE_WIDTH, y + TILE_HEIGHT }, + (Vector2){ x + TILE_WIDTH, y }, + WHITE + ); + break; + case ENTITY_DIR_EAST: + DrawTriangle( + (Vector2){ x + TILE_WIDTH, y + TILE_HEIGHT / 2 }, + (Vector2){ x, y }, + (Vector2){ x, y + TILE_HEIGHT }, + WHITE + ); + break; + } } \ No newline at end of file diff --git a/src/duskraylib/display/draw/drawoverworld.h b/src/duskraylib/display/draw/drawoverworld.h index e84ec71..04c58bd 100644 --- a/src/duskraylib/display/draw/drawoverworld.h +++ b/src/duskraylib/display/draw/drawoverworld.h @@ -7,6 +7,7 @@ #pragma once #include "duskraylib.h" +#include "entity/entity.h" /** * Initializes the overworld drawing system. @@ -16,4 +17,11 @@ void drawOverworldInit(void); /** * Renders the overworld, including map and characters. */ -void drawOverworldDraw(void); \ No newline at end of file +void drawOverworldDraw(void); + +/** + * Renders a specific entity in the overworld. + * + * @param entity The entity to render. + */ +void drawOverworldDrawEntity(const entity_t *entity); \ No newline at end of file diff --git a/src/duskraylib/input.c b/src/duskraylib/input.c new file mode 100644 index 0000000..6b28d99 --- /dev/null +++ b/src/duskraylib/input.c @@ -0,0 +1,45 @@ +/** + * Copyright (c) 2025 Dominic Masters + * + * This software is released under the MIT License. + * https://opensource.org/licenses/MIT + */ + +#include "duskraylib.h" +#include "input.h" + +typedef struct { + int32_t key; + uint8_t bind; +} inputmap_t; + +inputmap_t INPUT_MAP[] = { + { KEY_UP, INPUT_BIND_UP }, + { KEY_W, INPUT_BIND_UP }, + { KEY_DOWN, INPUT_BIND_DOWN }, + { KEY_S, INPUT_BIND_DOWN }, + { KEY_LEFT, INPUT_BIND_LEFT }, + { KEY_A, INPUT_BIND_LEFT }, + { KEY_RIGHT, INPUT_BIND_RIGHT }, + { KEY_D, INPUT_BIND_RIGHT }, + + { KEY_SPACE, INPUT_BIND_ACTION }, + { KEY_E, INPUT_BIND_ACTION }, + { KEY_ENTER, INPUT_BIND_ACTION }, + + { KEY_ESCAPE, INPUT_BIND_CANCEL }, + { KEY_Q, INPUT_BIND_CANCEL }, + { KEY_BACKSPACE, INPUT_BIND_CANCEL }, + + { 0, 0 } +}; + +uint8_t inputStateGet() { + uint8_t state = 0; + inputmap_t *map = INPUT_MAP; + do { + if(IsKeyDown(map->key)) state |= map->bind; + map++; + } while(map->key != 0); + return state; +} \ No newline at end of file