Moved code and assets for the game to a seperate folder. Moved jansson and raylib to third_party.

This commit is contained in:
2025-12-16 11:47:55 +01:00
parent fd2dbf232d
commit 8cdbd5b162
63 changed files with 14 additions and 14 deletions

43
src/game/shared/entity.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef ENTITY_H
#define ENTITY_H
#include <stdint.h>
#include <stdbool.h>
typedef uint16_t Entity_ID;
#define INVALID_ENTITY_ID (Entity_ID)65535
typedef enum {
Entity_Movement_Direction_None,
Entity_Movement_Direction_Up,
Entity_Movement_Direction_Down,
Entity_Movement_Direction_Right,
Entity_Movement_Direction_Left,
} Entity_Movement_Direction;
typedef enum {
Entity_Type_Snake_Head,
Entity_Type_Snake_Body,
Entity_Type_Food,
} Entity_Type;
typedef struct {
bool active;
// TODO: SS - Maybe add an ID here.
Entity_Type type;
uint16_t x;
uint16_t y;
uint16_t prev_x;
uint16_t prev_y;
Entity_Movement_Direction move_direction;
Entity_ID child;
} Entity;
#endif

View File

@@ -0,0 +1,118 @@
#include "game_world.h"
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#define MIN_LEVEL_WIDTH 8 // TODO: SS - Move these out of here.
#define MIN_LEVEL_HEIGHT 8
Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t level_height) {
assert(level_width >= MIN_LEVEL_WIDTH); // We should probably have failed earlier.
assert(level_height >= MIN_LEVEL_HEIGHT);
Game_World *w = (Game_World *)calloc(1, sizeof(Game_World));
assert(w != NULL);
w->seed = seed;
random_init(&w->random_generator, w->seed);
w->max_entities = level_width * level_height;
w->entities = (Entity *)calloc(w->max_entities, sizeof(Entity));
// Set up entity-id queue.
assert(squeue_init(&w->entity_id_queue, w->max_entities, sizeof(Entity_ID)));
for(uint16_t i = 0; i < w->entity_id_queue.capacity; i++) {
Entity_ID id = (Entity_ID)i;
assert(squeue_push(&w->entity_id_queue, &id));
}
grid_initialize(&w->grid, level_width, level_height);
{ // TEMP: SS - Testing ..
for(uint16_t i = 0; i < 8; i++) {
Entity_ID entity_food;
assert(game_world_create_entity(
w,
Entity_Type_Food,
(uint16_t)random_u32_range(&w->random_generator, 0, level_width),
(uint16_t)random_u32_range(&w->random_generator, 0, level_height),
&entity_food
));
}
}
return w;
}
void game_world_destroy(Game_World *world) {
assert(world != NULL);
world->seed = 0;
world->max_entities = 0;
free(world->entities);
grid_dispose(&world->grid);
squeue_free(&world->entity_id_queue);
free(world);
}
bool game_world_create_entity(Game_World *world, Entity_Type type, uint16_t x, uint16_t y, Entity_ID *out_entity_id) {
assert(world != NULL);
*out_entity_id = INVALID_ENTITY_ID;
if(x >= world->grid.width) {
printf("Failed to create entity; Invalid x-coordinate.\n");
return false;
}
if(y >= world->grid.height) {
printf("Failed to create entity; Invalid y-coordinate.\n");
return false;
}
Entity_ID id = 0;
if(!squeue_pop(&world->entity_id_queue, (void *)&id)) {
printf("No free entity ids.\n");
return false;
}
world->entities[id] = (Entity) {
.active = true,
.type = type,
.child = INVALID_ENTITY_ID
};
Grid_Cell *cell = grid_get_cell(&world->grid, x, y); // TEMP: SS - Hardcoded coordinates. // TODO: SS - Find good coordinate.
assert(cell != NULL);
assert(grid_try_add_entity_to_cell(cell, &world->entities[id]));
*out_entity_id = id;
return true;
}
void game_world_destroy_entity(Game_World *world, Entity_ID entity_id) {
assert(world != NULL);
Entity *entity = game_world_try_get_entity_by_id(
world,
entity_id
);
assert(entity != NULL);
entity->active = false;
Grid_Cell *cell = grid_get_cell(&world->grid, entity->x, entity->y);
assert(cell != NULL);
assert(grid_try_remove_entity_from_cell(cell));
squeue_push(&world->entity_id_queue, (void *)&entity_id);
}
Entity *game_world_try_get_entity_by_id(Game_World *world, Entity_ID id) {
assert(world != NULL);
if(id >= world->max_entities) {
return NULL;
}
return &world->entities[id];
}

View File

@@ -0,0 +1,32 @@
#ifndef WORLD_H
#define WORLD_H
#include <stdint.h>
#include "entity.h"
#include "grid.h"
#include "shared/squeue.h"
#include "shared/random.h"
typedef struct {
uint32_t seed;
Random_Generator random_generator;
SQueue entity_id_queue;
Entity *entities;
uint16_t max_entities;
Grid grid;
} Game_World;
Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t level_height);
void game_world_destroy(Game_World *world);
bool game_world_create_entity(Game_World *world, Entity_Type type, uint16_t x, uint16_t y, Entity_ID *out_entity_id);
void game_world_destroy_entity(Game_World *world, Entity_ID entity_id);
// TODO: SS - "void game_world_spawn_player(Game_World *world, ..)"
Entity *game_world_try_get_entity_by_id(Game_World *world, Entity_ID id);
#endif

126
src/game/shared/grid.c Normal file
View File

@@ -0,0 +1,126 @@
#include "grid.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define GRID_MIN_WIDTH 8
#define GRID_MIN_HEIGHT 8
static bool from_grid_index(Grid *grid, uint16_t grid_index, uint16_t *out_x, uint16_t *out_y) {
assert(grid != NULL);
assert(out_x != NULL);
assert(out_y != NULL);
if (!grid || !out_x || !out_y) return false;
if (grid_index >= grid->width * grid->height) return false;
*out_x = grid_index % grid->width;
*out_y = grid_index / grid->width;
return true;
}
bool grid_initialize(Grid *grid, uint16_t width, uint16_t height) {
assert(grid != NULL);
if(width < GRID_MIN_WIDTH || height < GRID_MIN_HEIGHT) {
return false;
}
grid->width = width;
grid->height = height;
grid->cells = calloc(width * height, sizeof(Grid_Cell));
for(uint16_t i = 0; i < (width * height); i++) {
Grid_Cell *cell = &grid->cells[i];
assert(from_grid_index(grid, i, &cell->x, &cell->y));
}
return true;
}
void grid_dispose(Grid *grid) {
assert(grid != NULL);
free(grid->cells);
memset(grid, 0, sizeof(Grid));
}
static bool to_grid_index(Grid *grid, uint16_t x, uint16_t y, uint16_t *out_grid_index) {
assert(grid != NULL);
assert(out_grid_index != NULL);
if(!grid || !out_grid_index) {
return false;
}
if(x >= grid->width || y >= grid->height) {
return false;
}
*out_grid_index = y * grid->width + x;
return true;
}
Grid_Cell *grid_get_cell(Grid *grid, uint16_t x, uint16_t y) {
assert(grid != NULL);
if(x >= grid->width || y >= grid->height) {
return NULL;
}
uint16_t grid_index = 0;
if(!to_grid_index(grid, x, y, &grid_index)) {
return NULL;
}
return &grid->cells[grid_index];
}
bool grid_try_add_entity_to_cell(Grid_Cell *cell, Entity *entity) {
assert(cell != NULL);
assert(entity != NULL);
// TODO: SS - Could check what type of cell it is to determine if
// we're allowed to place an entity here. Is it a wall? Lava?
if(cell->entity != NULL) {
return false;
}
cell->entity = entity;
entity->x = cell->x;
entity->y = cell->y;
return true;
}
bool grid_try_remove_entity_from_cell(Grid_Cell *cell) {
assert(cell != NULL);
if(cell->entity == NULL) {
return false;
}
cell->entity->x = 0;
cell->entity->y = 0;
cell->entity = NULL;
return true;
}
void grid_move_entity_from_cell_to_cell(Grid_Cell *cell_a, Grid_Cell *cell_b) {
assert(cell_a != NULL);
assert(cell_b != NULL);
assert(cell_a->entity != NULL); // We expect cell A to have an Entity to move.
assert(cell_b->entity == NULL); // We expect cell B to NOT have an Entity.
Entity *entity_to_be_moved = cell_a->entity;
uint16_t start_x = cell_a->x;
uint16_t start_y = cell_a->y;
assert(grid_try_remove_entity_from_cell(cell_a));
assert(grid_try_add_entity_to_cell(cell_b, entity_to_be_moved));
entity_to_be_moved->prev_x = start_x;
entity_to_be_moved->prev_y = start_y;
}

33
src/game/shared/grid.h Normal file
View File

@@ -0,0 +1,33 @@
#ifndef GRID_H
#define GRID_H
#include <stdint.h>
#include <stdbool.h>
#include "entity.h"
typedef struct {
Entity *entity;
uint16_t x;
uint16_t y;
} Grid_Cell;
typedef struct {
uint16_t width;
uint16_t height;
Grid_Cell *cells;
} Grid;
bool grid_initialize(Grid *grid, uint16_t width, uint16_t height);
void grid_dispose(Grid *grid);
Grid_Cell *grid_get_cell(Grid *grid, uint16_t x, uint16_t y);
bool grid_try_add_entity_to_cell(Grid_Cell *cell, Entity *entity);
bool grid_try_remove_entity_from_cell(Grid_Cell *cell);
void grid_move_entity_from_cell_to_cell(Grid_Cell *cell_a, Grid_Cell *cell_b);
#endif

193
src/game/shared/map.c Normal file
View File

@@ -0,0 +1,193 @@
/**
* Copyright (c) 2014 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#include <stdlib.h>
#include <string.h>
#include "map.h"
struct map_node_t {
unsigned hash;
void *value;
map_node_t *next;
/* char key[]; */
/* char value[]; */
};
static unsigned map_hash(const char *str) {
unsigned hash = 5381;
while (*str) {
hash = ((hash << 5) + hash) ^ *str++;
}
return hash;
}
static map_node_t *map_newnode(const char *key, void *value, int vsize) {
map_node_t *node;
int ksize = strlen(key) + 1;
int voffset = ksize + ((sizeof(void*) - ksize) % sizeof(void*));
node = malloc(sizeof(*node) + voffset + vsize);
if (!node) return NULL;
memcpy(node + 1, key, ksize);
node->hash = map_hash(key);
node->value = ((char*) (node + 1)) + voffset;
memcpy(node->value, value, vsize);
return node;
}
static int map_bucketidx(map_base_t *m, unsigned hash) {
/* If the implementation is changed to allow a non-power-of-2 bucket count,
* the line below should be changed to use mod instead of AND */
return hash & (m->nbuckets - 1);
}
static void map_addnode(map_base_t *m, map_node_t *node) {
int n = map_bucketidx(m, node->hash);
node->next = m->buckets[n];
m->buckets[n] = node;
}
static int map_resize(map_base_t *m, int nbuckets) {
map_node_t *nodes, *node, *next;
map_node_t **buckets;
int i;
/* Chain all nodes together */
nodes = NULL;
i = m->nbuckets;
while (i--) {
node = (m->buckets)[i];
while (node) {
next = node->next;
node->next = nodes;
nodes = node;
node = next;
}
}
/* Reset buckets */
buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets);
if (buckets != NULL) {
m->buckets = buckets;
m->nbuckets = nbuckets;
}
if (m->buckets) {
memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets);
/* Re-add nodes to buckets */
node = nodes;
while (node) {
next = node->next;
map_addnode(m, node);
node = next;
}
}
/* Return error code if realloc() failed */
return (buckets == NULL) ? -1 : 0;
}
static map_node_t **map_getref(map_base_t *m, const char *key) {
unsigned hash = map_hash(key);
map_node_t **next;
if (m->nbuckets > 0) {
next = &m->buckets[map_bucketidx(m, hash)];
while (*next) {
if ((*next)->hash == hash && !strcmp((char*) (*next + 1), key)) {
return next;
}
next = &(*next)->next;
}
}
return NULL;
}
void map_deinit_(map_base_t *m) {
map_node_t *next, *node;
int i;
i = m->nbuckets;
while (i--) {
node = m->buckets[i];
while (node) {
next = node->next;
free(node);
node = next;
}
}
free(m->buckets);
}
void *map_get_(map_base_t *m, const char *key) {
map_node_t **next = map_getref(m, key);
return next ? (*next)->value : NULL;
}
int map_set_(map_base_t *m, const char *key, void *value, int vsize) {
int n, err;
map_node_t **next, *node;
/* Find & replace existing node */
next = map_getref(m, key);
if (next) {
memcpy((*next)->value, value, vsize);
return 0;
}
/* Add new node */
node = map_newnode(key, value, vsize);
if (node == NULL) goto fail;
if (m->nnodes >= m->nbuckets) {
n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1;
err = map_resize(m, n);
if (err) goto fail;
}
map_addnode(m, node);
m->nnodes++;
return 0;
fail:
if (node) free(node);
return -1;
}
void map_remove_(map_base_t *m, const char *key) {
map_node_t *node;
map_node_t **next = map_getref(m, key);
if (next) {
node = *next;
*next = (*next)->next;
free(node);
m->nnodes--;
}
}
map_iter_t map_iter_(void) {
map_iter_t iter;
iter.bucketidx = -1;
iter.node = NULL;
return iter;
}
const char *map_next_(map_base_t *m, map_iter_t *iter) {
if (iter->node) {
iter->node = iter->node->next;
if (iter->node == NULL) goto nextBucket;
} else {
nextBucket:
do {
if (++iter->bucketidx >= m->nbuckets) {
return NULL;
}
iter->node = m->buckets[iter->bucketidx];
} while (iter->node == NULL);
}
return (char*) (iter->node + 1);
}

78
src/game/shared/map.h Normal file
View File

@@ -0,0 +1,78 @@
/**
* Copyright (c) 2014 rxi
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the MIT license. See LICENSE for details.
*/
#ifndef MAP_H
#define MAP_H
#include <string.h>
#define MAP_VERSION "0.1.0"
struct map_node_t;
typedef struct map_node_t map_node_t;
typedef struct {
map_node_t **buckets;
unsigned nbuckets, nnodes;
} map_base_t;
typedef struct {
unsigned bucketidx;
map_node_t *node;
} map_iter_t;
#define map_t(T)\
struct { map_base_t base; T *ref; T tmp; }
#define map_init(m)\
memset(m, 0, sizeof(*(m)))
#define map_deinit(m)\
map_deinit_(&(m)->base)
#define map_get(m, key)\
( (m)->ref = map_get_(&(m)->base, key) )
#define map_set(m, key, value)\
( (m)->tmp = (value),\
map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) )
#define map_remove(m, key)\
map_remove_(&(m)->base, key)
#define map_iter(m)\
map_iter_()
#define map_next(m, iter)\
map_next_(&(m)->base, iter)
void map_deinit_(map_base_t *m);
void *map_get_(map_base_t *m, const char *key);
int map_set_(map_base_t *m, const char *key, void *value, int vsize);
void map_remove_(map_base_t *m, const char *key);
map_iter_t map_iter_(void);
const char *map_next_(map_base_t *m, map_iter_t *iter);
typedef map_t(void*) map_void_t;
typedef map_t(char*) map_str_t;
typedef map_t(int) map_int_t;
typedef map_t(unsigned int) map_uint_t;
typedef map_t(char) map_char_t;
typedef map_t(float) map_float_t;
typedef map_t(double) map_double_t;
#endif

25
src/game/shared/random.c Normal file
View File

@@ -0,0 +1,25 @@
#include "random.h"
void random_init(Random_Generator *random_generator, uint32_t seed) {
if(seed == 0) {
seed = 1;
}
random_generator->seed = seed;
random_generator->state = random_generator->seed;
}
uint32_t random_u32(Random_Generator *random_generator) {
uint32_t x = random_generator->state;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
random_generator->state = x;
return x;
}
uint32_t random_u32_range(Random_Generator *random_generator, uint32_t min, uint32_t max) {
return min + (random_u32(random_generator) % (max - min + 1));
}

17
src/game/shared/random.h Normal file
View File

@@ -0,0 +1,17 @@
#ifndef RANDOM_H
#define RANDOM_H
#include <stdint.h>
typedef struct {
uint32_t seed;
uint32_t state;
} Random_Generator;
void random_init(Random_Generator *random_generator, uint32_t seed);
uint32_t random_u32(Random_Generator *random_generator);
uint32_t random_u32_range(Random_Generator *random_generator, uint32_t min, uint32_t max);
#endif

57
src/game/shared/squeue.c Normal file
View File

@@ -0,0 +1,57 @@
#include "squeue.h"
bool squeue_init(SQueue *q, uint16_t capacity, size_t element_size) {
q->buffer = malloc(element_size * capacity);
if (!q->buffer) {
return false;
}
q->head = q->tail = q->count = 0;
q->capacity = capacity;
q->element_size = element_size;
return true;
}
void squeue_free(SQueue *q) {
free(q->buffer);
q->buffer = NULL;
q->head = q->tail = q->count = q->capacity = 0;
q->element_size = 0;
}
bool squeue_push(SQueue *q, const void *elem) {
if (q->count == q->capacity) {
return false;
}
void *dest = (uint8_t*)q->buffer + q->tail * q->element_size;
memcpy(dest, elem, q->element_size);
q->tail = (q->tail + 1) % q->capacity;
q->count++;
return true;
}
bool squeue_pop(SQueue *q, void *out) {
if (q->count == 0) {
return false;
}
void *src = (uint8_t*)q->buffer + q->head * q->element_size;
memcpy(out, src, q->element_size);
q->head = (q->head + 1) % q->capacity;
q->count--;
return true;
}
bool squeue_peek(const SQueue *q, void *out) {
if (q->count == 0) {
return false;
}
void *src = (uint8_t*)q->buffer + q->head * q->element_size;
memcpy(out, src, q->element_size);
return true;
}

27
src/game/shared/squeue.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef SQUEUE_H
#define SQUEUE_H
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
// FIFO.
typedef struct {
void *buffer;
uint16_t head;
uint16_t tail;
uint16_t count;
uint16_t capacity;
size_t element_size;
} SQueue;
bool squeue_init(SQueue *q, uint16_t capacity, size_t element_size);
void squeue_free(SQueue *q);
bool squeue_push(SQueue *q, const void *elem);
bool squeue_pop(SQueue *q, void *out);
bool squeue_peek(const SQueue *q, void *out);
#endif

View File

@@ -0,0 +1,13 @@
#ifndef WANG_HASH_H
#define WANG_HASH_H
static inline uint32_t wang_hash(uint32_t v) {
v = (v ^ 61) ^ (v >> 16);
v = v + (v << 3);
v = v ^ (v >> 4);
v = v * 0x27d4eb2d;
v = v ^ (v >> 15);
return v;
}
#endif