More progress.
This commit is contained in:
BIN
src/assets/sprites/spr_shadow_basic.png
Normal file
BIN
src/assets/sprites/spr_shadow_basic.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 592 B |
@@ -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,11 +112,9 @@ 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) {
|
||||||
|
Entity *entity = cell->entity;
|
||||||
switch(entity->type) {
|
switch(entity->type) {
|
||||||
case Entity_Type_None: {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case Entity_Type_Snake_Head:
|
case Entity_Type_Snake_Head:
|
||||||
case Entity_Type_Snake_Body: {
|
case Entity_Type_Snake_Body: {
|
||||||
Color tint = (Color) { 255, 135, 102, 255 }; // TODO: SS - Get tint based on player ID.
|
Color tint = (Color) { 255, 135, 102, 255 }; // TODO: SS - Get tint based on player ID.
|
||||||
@@ -132,6 +135,15 @@ static void state_render(Presentation_State *state) {
|
|||||||
sin_amplitude = 1.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(
|
DrawTexturePro(
|
||||||
*texture,
|
*texture,
|
||||||
(Rectangle) { // Source.
|
(Rectangle) { // Source.
|
||||||
@@ -156,6 +168,15 @@ static void state_render(Presentation_State *state) {
|
|||||||
|
|
||||||
Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 };
|
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(
|
DrawTexturePro(
|
||||||
ctx->texture_apple,
|
ctx->texture_apple,
|
||||||
(Rectangle) { // Source.
|
(Rectangle) { // Source.
|
||||||
@@ -176,6 +197,15 @@ static void state_render(Presentation_State *state) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{ // 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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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,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->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
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
// { // TODO: SS - Create the level.
|
||||||
Grid_Cell *cell = grid_get_cell(&w->grid, 11, 4);
|
// {
|
||||||
assert(cell != NULL);
|
// Grid_Cell *cell = grid_get_cell(&w->grid, 5, 3);
|
||||||
cell->entity.type = Entity_Type_Food;
|
// assert(cell != NULL);
|
||||||
}
|
// cell->entity.type = Entity_Type_Food;
|
||||||
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Grid_Cell *cell = grid_get_cell(&w->grid, 15, 9);
|
// Grid_Cell *cell = grid_get_cell(&w->grid, 11, 4);
|
||||||
assert(cell != NULL);
|
// assert(cell != NULL);
|
||||||
cell->entity.type = Entity_Type_Food;
|
// cell->entity.type = Entity_Type_Food;
|
||||||
}
|
// }
|
||||||
|
|
||||||
{
|
// {
|
||||||
Grid_Cell *cell = grid_get_cell(&w->grid, 24, 15);
|
// Grid_Cell *cell = grid_get_cell(&w->grid, 15, 9);
|
||||||
assert(cell != NULL);
|
// assert(cell != NULL);
|
||||||
cell->entity.type = Entity_Type_Snake_Head;
|
// cell->entity.type = Entity_Type_Food;
|
||||||
}
|
// }
|
||||||
{
|
|
||||||
Grid_Cell *cell = grid_get_cell(&w->grid, 23, 15);
|
// {
|
||||||
assert(cell != NULL);
|
// Grid_Cell *cell = grid_get_cell(&w->grid, 24, 15);
|
||||||
cell->entity.type = Entity_Type_Snake_Body;
|
// 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];
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
@@ -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
57
src/shared/squeue.c
Normal 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
25
src/shared/squeue.h
Normal 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
19
src/simulation/command.h
Normal 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
12
src/simulation/input.h
Normal 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
15
src/simulation/player.h
Normal 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
|
||||||
@@ -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.tick = 0;
|
||||||
|
|
||||||
w.game_world = game_world_create(seed, width, height);
|
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;
|
||||||
}
|
}
|
||||||
@@ -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
|
||||||
Reference in New Issue
Block a user