diff --git a/src/assets/sprites/spr_shadow_basic.png b/src/assets/sprites/spr_shadow_basic.png new file mode 100644 index 0000000..f2b6d12 Binary files /dev/null and b/src/assets/sprites/spr_shadow_basic.png differ diff --git a/src/presentation/states/state_ingame.c b/src/presentation/states/state_ingame.c index 1644454..bda6dfc 100644 --- a/src/presentation/states/state_ingame.c +++ b/src/presentation/states/state_ingame.c @@ -47,6 +47,8 @@ static void state_enter(Presentation_State *state) { // TODO: SS - Maybe put the textures in an array and index them using an enum? // These should probably be loaded at start-up instead of here. + ctx->texture_shadow_basic = LoadTexture("assets/sprites/spr_shadow_basic.png"); + assert(IsTextureValid(ctx->texture_shadow_basic)); ctx->texture_grass = LoadTexture("assets/sprites/spr_floor_grass.png"); assert(IsTextureValid(ctx->texture_grass)); ctx->texture_apple = LoadTexture("assets/sprites/spr_food_apple.png"); @@ -60,6 +62,8 @@ static void state_enter(Presentation_State *state) { static void state_tick(Presentation_State *state) { Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context; (void)ctx; + + game_session_tick(g_current_session); { // TEMP: SS if(IsKeyPressed(KEY_ESCAPE)) { @@ -74,7 +78,8 @@ static void state_render(Presentation_State *state) { ClearBackground((Color) { 240, 236, 226, 255 }); - Game_World *world = g_current_session->simulation_world.game_world; + Simulation_World *sim_world = &g_current_session->simulation_world; + Game_World *world = sim_world->game_world; Grid *grid = &world->grid; uint32_t grid_total_size = grid->width * grid->height; @@ -107,74 +112,99 @@ static void state_render(Presentation_State *state) { uint32_t pres_y = y * GRID_CELL_SIZE; Grid_Cell *cell = &grid->cells[i]; - Entity *entity = &cell->entity; - switch(entity->type) { - case Entity_Type_None: { - break; - } - case Entity_Type_Snake_Head: - case Entity_Type_Snake_Body: { - Color tint = (Color) { 255, 135, 102, 255 }; // TODO: SS - Get tint based on player ID. - - Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 }; - - float sin_frequency = 0.0f; - float sin_amplitude = 0.0f; - - Texture2D *texture = NULL; - if(entity->type == Entity_Type_Snake_Head) { - texture = &ctx->texture_snake_head; + if(cell->entity != NULL) { + Entity *entity = cell->entity; + switch(entity->type) { + case Entity_Type_Snake_Head: + case Entity_Type_Snake_Body: { + Color tint = (Color) { 255, 135, 102, 255 }; // TODO: SS - Get tint based on player ID. + + Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 }; + + float sin_frequency = 0.0f; + float sin_amplitude = 0.0f; + + Texture2D *texture = NULL; + if(entity->type == Entity_Type_Snake_Head) { + texture = &ctx->texture_snake_head; + } + else { + texture = &ctx->texture_snake_body; + // TODO: SS - If it's a body, check what index it is and use that as an y-offset to make it look cool, like a wave. + sin_frequency = 4.0f; + sin_amplitude = 1.0f; + } + + // Draw shadow. + DrawTextureEx( + ctx->texture_shadow_basic, + (Vector2) { pres_x, pres_y }, + 0.0f, + 1.0f, + (Color) { 0, 0, 0, 32 } + ); + + DrawTexturePro( + *texture, + (Rectangle) { // Source. + 0, 0, GRID_CELL_SIZE, GRID_CELL_SIZE + }, + (Rectangle) { // Destination. + pres_x + origin.x, + pres_y + origin.y + ENTITY_PRESENTATION_Y_OFFSET - (sin(GetTime() * sin_frequency)) * sin_amplitude, + GRID_CELL_SIZE, + GRID_CELL_SIZE + }, + origin, // Origin. + 0, // Rotation. + tint // Tint. + ); + + break; } - else { - texture = &ctx->texture_snake_body; - // TODO: SS - If it's a body, check what index it is and use that as an y-offset to make it look cool, like a wave. - sin_frequency = 4.0f; - sin_amplitude = 1.0f; + case Entity_Type_Food: { + uint32_t flash = ((sin(GetTime() * 8) + 1)/2) * 32; + Color tint = (Color) { 255-flash, 255-flash, 255-flash, 255 }; + + Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 }; + + // Draw shadow. + DrawTextureEx( + ctx->texture_shadow_basic, + (Vector2) { pres_x, pres_y }, + 0.0f, + 1.0f, + (Color) { 0, 0, 0, 32 } + ); + + DrawTexturePro( + ctx->texture_apple, + (Rectangle) { // Source. + 0, 0, GRID_CELL_SIZE, GRID_CELL_SIZE + }, + (Rectangle) { // Destination. + pres_x + origin.x, + pres_y + origin.y + ENTITY_PRESENTATION_Y_OFFSET - ((sin(GetTime() * 12) + 1)/2) * 1, + GRID_CELL_SIZE, + GRID_CELL_SIZE + }, + origin, // Origin. + 0, // Rotation. + tint // Tint. + ); + break; } - - DrawTexturePro( - *texture, - (Rectangle) { // Source. - 0, 0, GRID_CELL_SIZE, GRID_CELL_SIZE - }, - (Rectangle) { // Destination. - pres_x + origin.x, - pres_y + origin.y + ENTITY_PRESENTATION_Y_OFFSET - (sin(GetTime() * sin_frequency)) * sin_amplitude, - GRID_CELL_SIZE, - GRID_CELL_SIZE - }, - origin, // Origin. - 0, // Rotation. - tint // Tint. - ); - - break; - } - case Entity_Type_Food: { - uint32_t flash = ((sin(GetTime() * 8) + 1)/2) * 32; - Color tint = (Color) { 255-flash, 255-flash, 255-flash, 255 }; - - Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 }; - - DrawTexturePro( - ctx->texture_apple, - (Rectangle) { // Source. - 0, 0, GRID_CELL_SIZE, GRID_CELL_SIZE - }, - (Rectangle) { // Destination. - pres_x + origin.x, - pres_y + origin.y + ENTITY_PRESENTATION_Y_OFFSET - ((sin(GetTime() * 12) + 1)/2) * 1, - GRID_CELL_SIZE, - GRID_CELL_SIZE - }, - origin, // Origin. - 0, // Rotation. - tint // Tint. - ); - break; } } } + + { // TEMP: SS - Render match state. + char buf[512]; + snprintf(&buf[0], sizeof(buf), "Match-state: %i. Tick: %lu.", sim_world->match_state, sim_world->tick); + + DrawText(buf, 32, 32, 12, RED); + } + } EndMode2D(); } @@ -184,6 +214,7 @@ static void state_exit(Presentation_State *state) { (void)ctx; printf("Exiting ingame\n"); + UnloadTexture(ctx->texture_shadow_basic); UnloadTexture(ctx->texture_grass); UnloadTexture(ctx->texture_apple); UnloadTexture(ctx->texture_snake_head); diff --git a/src/presentation/states/state_ingame.h b/src/presentation/states/state_ingame.h index c002898..fdd17b5 100644 --- a/src/presentation/states/state_ingame.h +++ b/src/presentation/states/state_ingame.h @@ -10,6 +10,7 @@ typedef struct { Camera2D main_camera; // Textures. + Texture2D texture_shadow_basic; Texture2D texture_grass; Texture2D texture_apple; Texture2D texture_snake_head; diff --git a/src/session/game_session.c b/src/session/game_session.c index 44c360b..e480d20 100644 --- a/src/session/game_session.c +++ b/src/session/game_session.c @@ -4,6 +4,9 @@ #include #include #include +#include + +#include "raylib.h" Game_Session *g_current_session = NULL; @@ -22,25 +25,127 @@ void game_session_create(bool is_singleplayer, bool is_host, Game_Session_Settin session->settings.level_height ); + session->players = (Game_Session_Player *)calloc(session->settings.max_players, sizeof(Game_Session_Player)); + session->players_prev = (Game_Session_Player *)calloc(session->settings.max_players, sizeof(Game_Session_Player)); + + { // TEMP + session->local_player_index = 0; + assert(game_session_create_player(session, &session->local_player_index)); + } + g_current_session = session; printf("New Game_Session created.\n"); } void game_session_destroy() { - if(g_current_session == NULL) { + Game_Session *session = g_current_session; + + if(session == NULL) { return; } - simulation_destroy_world(&g_current_session->simulation_world); + simulation_destroy_world(&session->simulation_world); - free(g_current_session); + free(session->players); + session->players = NULL; + free(session->players_prev); + session->players_prev = NULL; + + free(session); g_current_session = NULL; } void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings) { out_settings->seed = 1337; // TODO: SS - Randomize. - out_settings->level_width = 48; - out_settings->level_height = 48; + out_settings->level_width = 32; + out_settings->level_height = 32; out_settings->max_players = is_singleplayer ? 1 : 8; +} + +void game_session_tick(Game_Session *session) { + Game_Session_Player *local_session_player = game_session_get_local_player(session); + assert(local_session_player != NULL); + local_session_player->input = (Simulation_Game_Input) { // TODO: SS - Move this somewhere else, maybe. + .up = IsKeyDown(KEY_UP) || IsKeyDown(KEY_W), + .down = IsKeyDown(KEY_DOWN) || IsKeyDown(KEY_S), + .right = IsKeyDown(KEY_RIGHT) || IsKeyDown(KEY_D), + .left = IsKeyDown(KEY_LEFT) || IsKeyDown(KEY_A) + }; + + { // TODO: SS - Use delta-time to accumulate time. Tick the simulation in a constant tick-rate. + // Update input. + for(uint16_t i = 0; i < session->settings.max_players; i++) { + Game_Session_Player *session_player_prev = &session->players_prev[i]; + Game_Session_Player *session_player = &session->players[i]; + + bool was_active = session_player_prev->active; + bool is_active = session_player->active; + + if (!was_active && is_active) { + simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) { + .type = Simulation_Command_Type_Add_Player, + .player_id = i + }); + } else if (was_active && !is_active) { + simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) { + .type = Simulation_Command_Type_Remove_Player, + .player_id = i + }); + } + + if(!session_player->active) { + continue; + } + + simulation_world_enqueue_command(&session->simulation_world, (Simulation_Command) { + .type = Simulation_Command_Type_Set_Player_Input, + .player_id = i, + .player_input = session_player->input, + }); + } + + // Tick. + simulation_world_tick(&session->simulation_world); + + // Copy 'players' from session to 'players_prev'. + memcpy(session->players_prev, session->players, sizeof(Game_Session_Player) * session->settings.max_players); + } +} + +Game_Session_Player *game_session_get_local_player(Game_Session *session) { + assert(session != NULL); + return &session->players[session->local_player_index]; +} + +bool game_session_create_player(Game_Session *session, uint16_t *out_player_index) { + assert(session != NULL); + + int32_t found_index = -1; + for(uint16_t i = 0; i < session->settings.max_players; i++) { + Game_Session_Player *session_player = &session->players[i]; + assert(session_player != NULL); + if(!session_player->active) { + session_player->active = true; + found_index = i; + } + } + + if(found_index < 0) { + return false; + } + + *out_player_index = (uint16_t)found_index; + return true; +} + +void game_session_destroy_player(Game_Session *session, uint16_t player_index) { + assert(session != NULL); + assert(player_index < session->settings.max_players); + + Game_Session_Player *player = &session->players[player_index]; + assert(player != NULL); + assert(player->active); + + memset(player, 0, sizeof(Game_Session_Player)); } \ No newline at end of file diff --git a/src/session/game_session.h b/src/session/game_session.h index d7a0a74..ca003cd 100644 --- a/src/session/game_session.h +++ b/src/session/game_session.h @@ -5,6 +5,14 @@ #include #include "simulation/simulation_world.h" +#include "simulation/input.h" +#include "simulation/player.h" + +typedef struct { + bool active; + + Simulation_Game_Input input; +} Game_Session_Player; typedef struct { uint32_t seed; @@ -23,9 +31,13 @@ typedef struct { Game_Session_Settings settings; - // TODO: SS - Game-state (counting down, active, over) - // TODO: SS - Local(?) input-queue. Simulation_World simulation_world; + + Game_Session_Player *players; + Game_Session_Player *players_prev; + uint16_t local_player_index; + + // TODO: SS - Local + remote input-queue. } Game_Session; extern Game_Session *g_current_session; @@ -35,4 +47,11 @@ void game_session_destroy(); void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings); +void game_session_tick(Game_Session *session); + +Game_Session_Player *game_session_get_local_player(Game_Session *session); + +bool game_session_create_player(Game_Session *session, uint16_t *out_player_index); +void game_session_destroy_player(Game_Session *session, uint16_t player_index); + #endif \ No newline at end of file diff --git a/src/shared/entity.h b/src/shared/entity.h index 9c5bf3a..41bcfaa 100644 --- a/src/shared/entity.h +++ b/src/shared/entity.h @@ -3,8 +3,11 @@ #include +typedef uint16_t Entity_ID; + +#define INVALID_ENTITY_ID Entity_ID(65535) + typedef enum { - Entity_Type_None, Entity_Type_Snake_Head, Entity_Type_Snake_Body, Entity_Type_Food, @@ -12,6 +15,11 @@ typedef enum { typedef struct { Entity_Type type; + + uint16_t x; + uint16_t y; + + // TODO: SS - Color/tint? } Entity; #endif \ No newline at end of file diff --git a/src/shared/game_world.c b/src/shared/game_world.c index 0fd558d..ec7448f 100644 --- a/src/shared/game_world.c +++ b/src/shared/game_world.c @@ -2,6 +2,7 @@ #include #include +#include #define MIN_LEVEL_WIDTH 8 // TODO: SS - Move these out of here. #define MIN_LEVEL_HEIGHT 8 @@ -17,39 +18,55 @@ Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t leve 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); - - { // TODO: SS - Create the level. - { - Grid_Cell *cell = grid_get_cell(&w->grid, 5, 3); - assert(cell != NULL); - cell->entity.type = Entity_Type_Food; - } - - { - Grid_Cell *cell = grid_get_cell(&w->grid, 11, 4); - assert(cell != NULL); - cell->entity.type = Entity_Type_Food; - } - - { - Grid_Cell *cell = grid_get_cell(&w->grid, 15, 9); - assert(cell != NULL); - cell->entity.type = Entity_Type_Food; - } - - { - Grid_Cell *cell = grid_get_cell(&w->grid, 24, 15); - assert(cell != NULL); - cell->entity.type = Entity_Type_Snake_Head; - } - { - Grid_Cell *cell = grid_get_cell(&w->grid, 23, 15); - assert(cell != NULL); - cell->entity.type = Entity_Type_Snake_Body; - } + { // TEMP: SS - Testing .. + Entity_ID entity_food; + assert(game_world_create_entity( + w, + Entity_Type_Food, + 4, 8, + &entity_food + )); } + + // { // TODO: SS - Create the level. + // { + // Grid_Cell *cell = grid_get_cell(&w->grid, 5, 3); + // assert(cell != NULL); + // cell->entity.type = Entity_Type_Food; + // } + + // { + // Grid_Cell *cell = grid_get_cell(&w->grid, 11, 4); + // assert(cell != NULL); + // cell->entity.type = Entity_Type_Food; + // } + + // { + // Grid_Cell *cell = grid_get_cell(&w->grid, 15, 9); + // assert(cell != NULL); + // cell->entity.type = Entity_Type_Food; + // } + + // { + // Grid_Cell *cell = grid_get_cell(&w->grid, 24, 15); + // assert(cell != NULL); + // cell->entity.type = Entity_Type_Snake_Head; + // } + // { + // Grid_Cell *cell = grid_get_cell(&w->grid, 23, 15); + // assert(cell != NULL); + // cell->entity.type = Entity_Type_Snake_Body; + // } + // } return w; @@ -62,5 +79,65 @@ void game_world_destroy(Game_World *world) { 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); + + 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; + bool got_id = squeue_pop(&world->entity_id_queue, (void *)&id); + if(!got_id) { + printf("No free entity ids.\n"); + return false; + } + + world->entities[id] = (Entity) { + .type = type, + }; + + 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); + + 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, 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]; } \ No newline at end of file diff --git a/src/shared/game_world.h b/src/shared/game_world.h index 4338e4e..e5f09ad 100644 --- a/src/shared/game_world.h +++ b/src/shared/game_world.h @@ -4,17 +4,25 @@ #include #include "entity.h" #include "grid.h" +#include "shared/squeue.h" typedef struct { uint32_t seed; + 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); + +Entity *game_world_try_get_entity_by_id(Game_World *world, Entity_ID id); + #endif \ No newline at end of file diff --git a/src/shared/grid.c b/src/shared/grid.c index b8431ae..7eab07c 100644 --- a/src/shared/grid.c +++ b/src/shared/grid.c @@ -6,6 +6,19 @@ #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); @@ -16,6 +29,12 @@ bool grid_initialize(Grid *grid, uint16_t width, uint16_t height) { 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; } @@ -37,19 +56,6 @@ static bool to_grid_index(Grid *grid, uint16_t x, uint16_t y, uint16_t *out_grid return true; } -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; -} - 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; @@ -60,4 +66,36 @@ Grid_Cell *grid_get_cell(Grid *grid, uint16_t x, uint16_t y) { } 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; } \ No newline at end of file diff --git a/src/shared/grid.h b/src/shared/grid.h index ba2adb7..3325d83 100644 --- a/src/shared/grid.h +++ b/src/shared/grid.h @@ -7,7 +7,10 @@ #include "entity.h" typedef struct { - Entity entity; + Entity *entity; + + uint16_t x; + uint16_t y; } Grid_Cell; typedef struct { @@ -22,4 +25,7 @@ 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); + #endif \ No newline at end of file diff --git a/src/shared/squeue.c b/src/shared/squeue.c new file mode 100644 index 0000000..21db128 --- /dev/null +++ b/src/shared/squeue.c @@ -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; +} \ No newline at end of file diff --git a/src/shared/squeue.h b/src/shared/squeue.h new file mode 100644 index 0000000..1d5213a --- /dev/null +++ b/src/shared/squeue.h @@ -0,0 +1,25 @@ +#ifndef QUEUE_H +#define QUEUE_H + +#include +#include +#include +#include + +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 \ No newline at end of file diff --git a/src/simulation/command.h b/src/simulation/command.h new file mode 100644 index 0000000..3b34bbe --- /dev/null +++ b/src/simulation/command.h @@ -0,0 +1,19 @@ +#ifndef SIM_COMMAND_H +#define SIM_COMMAND_H + +#include "input.h" + +typedef enum { + Simulation_Command_Type_Add_Player, + Simulation_Command_Type_Remove_Player, + Simulation_Command_Type_Set_Player_Input, +} Simulation_Command_Type; + +typedef struct { + Simulation_Command_Type type; + + uint16_t player_id; + Simulation_Game_Input player_input; +} Simulation_Command; + +#endif \ No newline at end of file diff --git a/src/simulation/input.h b/src/simulation/input.h new file mode 100644 index 0000000..9157f34 --- /dev/null +++ b/src/simulation/input.h @@ -0,0 +1,12 @@ +#ifndef SIM_GAME_INPUT_H +#define SIM_GAME_INPUT_H + +typedef struct { + // Movement can probably just be one byte. For a traditional snake-game, only one directional-input is OK. + bool up; + bool down; + bool right; + bool left; +} Simulation_Game_Input; + +#endif \ No newline at end of file diff --git a/src/simulation/player.h b/src/simulation/player.h new file mode 100644 index 0000000..4c13bb6 --- /dev/null +++ b/src/simulation/player.h @@ -0,0 +1,15 @@ +#ifndef SIM_PLAYER_H +#define SIM_PLAYER_H + +#include "simulation/input.h" + +typedef struct { + bool active; + + Entity_ID entity_id; + Simulation_Game_Input input; + // score, name etc. + +} Simulation_Player; + +#endif \ No newline at end of file diff --git a/src/simulation/simulation_world.c b/src/simulation/simulation_world.c index d55321b..53c9540 100644 --- a/src/simulation/simulation_world.c +++ b/src/simulation/simulation_world.c @@ -1,14 +1,28 @@ #include "simulation_world.h" #include +#include #include +#define SIM_COMMANDS_PER_PLAYER 4 + Simulation_World simulation_create_world(uint32_t seed, uint8_t max_players, uint16_t width, uint16_t height) { Simulation_World w; memset(&w, 0, sizeof(Simulation_World)); - w.game_world = game_world_create(seed, width, height); + w.tick = 0; + + w.game_world = game_world_create(seed, width, height); assert(w.game_world != NULL); + + w.match_state = Simulation_Match_State_Waiting_To_Start; + + w.players = (Simulation_Player *)calloc(max_players, sizeof(Simulation_Player)); + w.max_players = max_players; + + w.max_commands = w.max_players * SIM_COMMANDS_PER_PLAYER; + w.command_count = 0; + w.commands = (Simulation_Command *)calloc(w.max_commands, sizeof(Simulation_Command)); return w; } @@ -18,4 +32,96 @@ void simulation_destroy_world(Simulation_World *simulation_world) { game_world_destroy(simulation_world->game_world); simulation_world->game_world = NULL; + + simulation_world->match_state = Simulation_Match_State_Waiting_To_Start; + + free(simulation_world->players); + simulation_world->players = NULL; + + free(simulation_world->commands); + simulation_world->commands = NULL; +} + +void simulation_world_tick(Simulation_World *simulation_world) { + assert(simulation_world != NULL); + + printf("TICK: %lu.\n", simulation_world->tick); + + for(uint16_t i = 0; i < simulation_world->command_count; i++) { + Simulation_Command *cmd = &simulation_world->commands[i]; + printf("Command type: %i, player id: %i.\n", cmd->type, cmd->player_id); + + Simulation_Player *player = &simulation_world->players[cmd->player_id]; + assert(player != NULL); + + switch(cmd->type) { + case Simulation_Command_Type_Add_Player: { + printf("Simulation_Command_Type_Add_Player\n"); + assert(!player->active); + player->active = true; + + // TODO: SS - Create entity. + assert(game_world_create_entity( + simulation_world->game_world, + Entity_Type_Snake_Head, + 10, 15, + &player->entity_id + )); + + break; + } + case Simulation_Command_Type_Remove_Player: { + printf("Simulation_Command_Type_Remove_Player\n"); + assert(player->active); + player->active = false; + + game_world_destroy_entity(simulation_world->game_world, player->entity_id); + + break; + } + case Simulation_Command_Type_Set_Player_Input: { + printf("Simulation_Command_Type_Set_Player_Input\n"); + assert(player->active); + player->input = cmd->player_input; + + break; + } + } + } + simulation_world->command_count = 0; + + // Loop over all players in the simulation. + for(uint16_t i = 0; i < simulation_world->max_players; i++) { + Simulation_Player *player = &simulation_world->players[i]; + if(!player->active) { + continue; + } + + printf("* Input for player %i - up: %i, down: %i, right: %i, left: %i.\n", + i, + player->input.up, player->input.down, player->input.right, player->input.left + ); + + Entity *player_entity = game_world_try_get_entity_by_id(simulation_world->game_world, player->entity_id); + assert(player_entity != NULL); + + printf("Entity %i ~ x: %u, y: %u.\n", player->entity_id, player_entity->x, player_entity->y); + } + + // TODO: SS - Game-logic! :) + + simulation_world->tick += 1; +} + +bool simulation_world_enqueue_command(Simulation_World *simulation_world, Simulation_Command command) { + assert(simulation_world != NULL); + + if(simulation_world->command_count >= simulation_world->max_commands) { + return false; + } + + simulation_world->commands[simulation_world->command_count] = command; + simulation_world->command_count += 1; + + return true; } \ No newline at end of file diff --git a/src/simulation/simulation_world.h b/src/simulation/simulation_world.h index 8c9202e..c308aec 100644 --- a/src/simulation/simulation_world.h +++ b/src/simulation/simulation_world.h @@ -4,12 +4,36 @@ #include #include "shared/game_world.h" +#include "player.h" +#include "command.h" + +typedef enum { + Simulation_Match_State_Waiting_To_Start, + Simulation_Match_State_Counting_Down, + Simulation_Match_State_Active, + Simulation_Match_State_Ended, +} Simulation_Match_State; typedef struct { + uint64_t tick; + Game_World *game_world; + + Simulation_Match_State match_state; + + Simulation_Player *players; + uint16_t max_players; + + Simulation_Command *commands; + uint16_t command_count; + uint16_t max_commands; } Simulation_World; Simulation_World simulation_create_world(uint32_t seed, uint8_t max_players, uint16_t width, uint16_t height); void simulation_destroy_world(Simulation_World *simulation_world); +void simulation_world_tick(Simulation_World *simulation_world); + +bool simulation_world_enqueue_command(Simulation_World *simulation_world, Simulation_Command command); + #endif \ No newline at end of file