Basic NPC loading (half done)
This commit is contained in:
@ -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)
|
@ -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")
|
||||
|
@ -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": [
|
||||
]
|
@ -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)
|
@ -5,4 +5,6 @@
|
||||
* https://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#include "display/render.h"
|
||||
#include "display/render.h"
|
||||
|
||||
uint8_t RENDER_FRAME = 0;
|
@ -11,6 +11,8 @@
|
||||
#define RENDER_WIDTH 320
|
||||
#define RENDER_HEIGHT 240
|
||||
|
||||
extern uint8_t RENDER_FRAME;
|
||||
|
||||
/**
|
||||
* Initializes the rendering system.
|
||||
*/
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <math.h>
|
||||
|
||||
typedef bool bool_t;
|
||||
typedef int int_t;
|
||||
|
12
src/dusk/entity/CMakeLists.txt
Normal file
12
src/dusk/entity/CMakeLists.txt
Normal file
@ -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
|
||||
)
|
42
src/dusk/entity/entity.c
Normal file
42
src/dusk/entity/entity.c
Normal file
@ -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);
|
||||
}
|
66
src/dusk/entity/entity.h
Normal file
66
src/dusk/entity/entity.h
Normal file
@ -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);
|
16
src/dusk/entity/npc.c
Normal file
16
src/dusk/entity/npc.c
Normal file
@ -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) {
|
||||
|
||||
}
|
23
src/dusk/entity/npc.h
Normal file
23
src/dusk/entity/npc.h
Normal file
@ -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);
|
77
src/dusk/entity/player.c
Normal file
77
src/dusk/entity/player.c
Normal file
@ -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;
|
||||
}
|
||||
}
|
@ -6,4 +6,33 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "dusk.h"
|
||||
#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);
|
39
src/dusk/input.c
Normal file
39
src/dusk/input.c
Normal file
@ -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);
|
||||
}
|
74
src/dusk/input.h
Normal file
74
src/dusk/input.h
Normal file
@ -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);
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
23
src/dusk/world/chunkdata.h
Normal file
23
src/dusk/world/chunkdata.h
Normal file
@ -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;
|
@ -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) {
|
||||
|
@ -27,6 +27,7 @@ target_include_directories(${DUSK_TARGET_NAME}
|
||||
# Sources
|
||||
target_sources(${DUSK_TARGET_NAME}
|
||||
PRIVATE
|
||||
input.c
|
||||
)
|
||||
|
||||
# Subdirs
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
void drawOverworldDraw(void);
|
||||
|
||||
/**
|
||||
* Renders a specific entity in the overworld.
|
||||
*
|
||||
* @param entity The entity to render.
|
||||
*/
|
||||
void drawOverworldDrawEntity(const entity_t *entity);
|
45
src/duskraylib/input.c
Normal file
45
src/duskraylib/input.c
Normal file
@ -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;
|
||||
}
|
Reference in New Issue
Block a user