More progress.

This commit is contained in:
2025-12-11 15:11:01 +01:00
parent f2fc2dadcd
commit cdbd0b2c43
17 changed files with 668 additions and 117 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

View File

@@ -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? // 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. // 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"); ctx->texture_grass = LoadTexture("assets/sprites/spr_floor_grass.png");
assert(IsTextureValid(ctx->texture_grass)); assert(IsTextureValid(ctx->texture_grass));
ctx->texture_apple = LoadTexture("assets/sprites/spr_food_apple.png"); ctx->texture_apple = LoadTexture("assets/sprites/spr_food_apple.png");
@@ -61,6 +63,8 @@ static void state_tick(Presentation_State *state) {
Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context; Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context;
(void)ctx; (void)ctx;
game_session_tick(g_current_session);
{ // TEMP: SS { // TEMP: SS
if(IsKeyPressed(KEY_ESCAPE)) { if(IsKeyPressed(KEY_ESCAPE)) {
presentation_state_machine_go_to(&presentation_state_main_menu); presentation_state_machine_go_to(&presentation_state_main_menu);
@@ -74,7 +78,8 @@ static void state_render(Presentation_State *state) {
ClearBackground((Color) { 240, 236, 226, 255 }); 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; Grid *grid = &world->grid;
uint32_t grid_total_size = grid->width * grid->height; 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; uint32_t pres_y = y * GRID_CELL_SIZE;
Grid_Cell *cell = &grid->cells[i]; Grid_Cell *cell = &grid->cells[i];
Entity *entity = &cell->entity; if(cell->entity != NULL) {
switch(entity->type) { Entity *entity = cell->entity;
case Entity_Type_None: { switch(entity->type) {
break; case Entity_Type_Snake_Head:
} case Entity_Type_Snake_Body: {
case Entity_Type_Snake_Head: Color tint = (Color) { 255, 135, 102, 255 }; // TODO: SS - Get tint based on player ID.
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 }; Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 };
float sin_frequency = 0.0f; float sin_frequency = 0.0f;
float sin_amplitude = 0.0f; float sin_amplitude = 0.0f;
Texture2D *texture = NULL; Texture2D *texture = NULL;
if(entity->type == Entity_Type_Snake_Head) { if(entity->type == Entity_Type_Snake_Head) {
texture = &ctx->texture_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 { case Entity_Type_Food: {
texture = &ctx->texture_snake_body; uint32_t flash = ((sin(GetTime() * 8) + 1)/2) * 32;
// 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. Color tint = (Color) { 255-flash, 255-flash, 255-flash, 255 };
sin_frequency = 4.0f;
sin_amplitude = 1.0f; 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(); EndMode2D();
} }
@@ -184,6 +214,7 @@ static void state_exit(Presentation_State *state) {
(void)ctx; (void)ctx;
printf("Exiting ingame\n"); printf("Exiting ingame\n");
UnloadTexture(ctx->texture_shadow_basic);
UnloadTexture(ctx->texture_grass); UnloadTexture(ctx->texture_grass);
UnloadTexture(ctx->texture_apple); UnloadTexture(ctx->texture_apple);
UnloadTexture(ctx->texture_snake_head); UnloadTexture(ctx->texture_snake_head);

View File

@@ -10,6 +10,7 @@ typedef struct {
Camera2D main_camera; Camera2D main_camera;
// Textures. // Textures.
Texture2D texture_shadow_basic;
Texture2D texture_grass; Texture2D texture_grass;
Texture2D texture_apple; Texture2D texture_apple;
Texture2D texture_snake_head; Texture2D texture_snake_head;

View File

@@ -4,6 +4,9 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "raylib.h"
Game_Session *g_current_session = NULL; 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->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; g_current_session = session;
printf("New Game_Session created.\n"); printf("New Game_Session created.\n");
} }
void game_session_destroy() { void game_session_destroy() {
if(g_current_session == NULL) { Game_Session *session = g_current_session;
if(session == NULL) {
return; 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; g_current_session = NULL;
} }
void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings) { void game_session_init_default_settings(bool is_singleplayer, Game_Session_Settings *out_settings) {
out_settings->seed = 1337; // TODO: SS - Randomize. out_settings->seed = 1337; // TODO: SS - Randomize.
out_settings->level_width = 48; out_settings->level_width = 32;
out_settings->level_height = 48; out_settings->level_height = 32;
out_settings->max_players = is_singleplayer ? 1 : 8; 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));
}

View File

@@ -5,6 +5,14 @@
#include <stdbool.h> #include <stdbool.h>
#include "simulation/simulation_world.h" #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 { typedef struct {
uint32_t seed; uint32_t seed;
@@ -23,9 +31,13 @@ typedef struct {
Game_Session_Settings settings; Game_Session_Settings settings;
// TODO: SS - Game-state (counting down, active, over)
// TODO: SS - Local(?) input-queue.
Simulation_World simulation_world; 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; } Game_Session;
extern Game_Session *g_current_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_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 #endif

View File

@@ -3,8 +3,11 @@
#include <stdint.h> #include <stdint.h>
typedef uint16_t Entity_ID;
#define INVALID_ENTITY_ID Entity_ID(65535)
typedef enum { typedef enum {
Entity_Type_None,
Entity_Type_Snake_Head, Entity_Type_Snake_Head,
Entity_Type_Snake_Body, Entity_Type_Snake_Body,
Entity_Type_Food, Entity_Type_Food,
@@ -12,6 +15,11 @@ typedef enum {
typedef struct { typedef struct {
Entity_Type type; Entity_Type type;
uint16_t x;
uint16_t y;
// TODO: SS - Color/tint?
} Entity; } Entity;
#endif #endif

View File

@@ -2,6 +2,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <stdio.h>
#define MIN_LEVEL_WIDTH 8 // TODO: SS - Move these out of here. #define MIN_LEVEL_WIDTH 8 // TODO: SS - Move these out of here.
#define MIN_LEVEL_HEIGHT 8 #define MIN_LEVEL_HEIGHT 8
@@ -17,40 +18,56 @@ Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t leve
w->max_entities = level_width * level_height; w->max_entities = level_width * level_height;
w->entities = (Entity *)calloc(w->max_entities, sizeof(Entity)); 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); grid_initialize(&w->grid, level_width, level_height);
{ // TEMP: SS - Testing ..
{ // TODO: SS - Create the level. Entity_ID entity_food;
{ assert(game_world_create_entity(
Grid_Cell *cell = grid_get_cell(&w->grid, 5, 3); w,
assert(cell != NULL); Entity_Type_Food,
cell->entity.type = Entity_Type_Food; 4, 8,
} &entity_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;
}
} }
// { // 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; return w;
} }
@@ -62,5 +79,65 @@ void game_world_destroy(Game_World *world) {
world->max_entities = 0; world->max_entities = 0;
free(world->entities); free(world->entities);
grid_dispose(&world->grid); grid_dispose(&world->grid);
squeue_free(&world->entity_id_queue);
free(world); 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];
}

View File

@@ -4,17 +4,25 @@
#include <stdint.h> #include <stdint.h>
#include "entity.h" #include "entity.h"
#include "grid.h" #include "grid.h"
#include "shared/squeue.h"
typedef struct { typedef struct {
uint32_t seed; uint32_t seed;
SQueue entity_id_queue;
Entity *entities; Entity *entities;
uint16_t max_entities; uint16_t max_entities;
Grid grid; Grid grid;
} Game_World; } Game_World;
Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t level_height); Game_World *game_world_create(uint32_t seed, uint16_t level_width, uint16_t level_height);
void game_world_destroy(Game_World *world); 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 #endif

View File

@@ -6,6 +6,19 @@
#define GRID_MIN_WIDTH 8 #define GRID_MIN_WIDTH 8
#define GRID_MIN_HEIGHT 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) { bool grid_initialize(Grid *grid, uint16_t width, uint16_t height) {
assert(grid != NULL); assert(grid != NULL);
@@ -16,6 +29,12 @@ bool grid_initialize(Grid *grid, uint16_t width, uint16_t height) {
grid->width = width; grid->width = width;
grid->height = height; grid->height = height;
grid->cells = calloc(width * height, sizeof(Grid_Cell)); 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; 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; 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) { Grid_Cell *grid_get_cell(Grid *grid, uint16_t x, uint16_t y) {
assert(grid != NULL); assert(grid != NULL);
if(x >= grid->width || y >= grid->height) return NULL; if(x >= grid->width || y >= grid->height) return NULL;
@@ -61,3 +67,35 @@ Grid_Cell *grid_get_cell(Grid *grid, uint16_t x, uint16_t y) {
return &grid->cells[grid_index]; 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;
}

View File

@@ -7,7 +7,10 @@
#include "entity.h" #include "entity.h"
typedef struct { typedef struct {
Entity entity; Entity *entity;
uint16_t x;
uint16_t y;
} Grid_Cell; } Grid_Cell;
typedef struct { typedef struct {
@@ -22,4 +25,7 @@ void grid_dispose(Grid *grid);
Grid_Cell *grid_get_cell(Grid *grid, uint16_t x, uint16_t y); 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 #endif

57
src/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;
}

25
src/shared/squeue.h Normal file
View File

@@ -0,0 +1,25 @@
#ifndef QUEUE_H
#define QUEUE_H
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
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

19
src/simulation/command.h Normal file
View File

@@ -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

12
src/simulation/input.h Normal file
View File

@@ -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

15
src/simulation/player.h Normal file
View File

@@ -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

View File

@@ -1,15 +1,29 @@
#include "simulation_world.h" #include "simulation_world.h"
#include <string.h> #include <string.h>
#include <stdio.h>
#include <assert.h> #include <assert.h>
#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 simulation_create_world(uint32_t seed, uint8_t max_players, uint16_t width, uint16_t height) {
Simulation_World w; Simulation_World w;
memset(&w, 0, sizeof(Simulation_World)); 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); 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; return w;
} }
@@ -18,4 +32,96 @@ void simulation_destroy_world(Simulation_World *simulation_world) {
game_world_destroy(simulation_world->game_world); game_world_destroy(simulation_world->game_world);
simulation_world->game_world = NULL; 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;
} }

View File

@@ -4,12 +4,36 @@
#include <stdlib.h> #include <stdlib.h>
#include "shared/game_world.h" #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 { typedef struct {
uint64_t tick;
Game_World *game_world; 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_World simulation_create_world(uint32_t seed, uint8_t max_players, uint16_t width, uint16_t height); 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_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 #endif