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?
|
||||
// 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");
|
||||
@@ -61,6 +63,8 @@ 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)) {
|
||||
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 });
|
||||
|
||||
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.
|
||||
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 };
|
||||
Vector2 origin = (Vector2) { GRID_CELL_SIZE/2, GRID_CELL_SIZE/2 };
|
||||
|
||||
float sin_frequency = 0.0f;
|
||||
float sin_amplitude = 0.0f;
|
||||
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;
|
||||
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);
|
||||
|
||||
@@ -10,6 +10,7 @@ typedef struct {
|
||||
Camera2D main_camera;
|
||||
|
||||
// Textures.
|
||||
Texture2D texture_shadow_basic;
|
||||
Texture2D texture_grass;
|
||||
Texture2D texture_apple;
|
||||
Texture2D texture_snake_head;
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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));
|
||||
}
|
||||
@@ -5,6 +5,14 @@
|
||||
#include <stdbool.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 {
|
||||
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
|
||||
@@ -3,8 +3,11 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define MIN_LEVEL_WIDTH 8 // TODO: SS - Move these out of here.
|
||||
#define MIN_LEVEL_HEIGHT 8
|
||||
@@ -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->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];
|
||||
}
|
||||
@@ -4,17 +4,25 @@
|
||||
#include <stdint.h>
|
||||
#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
|
||||
@@ -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;
|
||||
@@ -61,3 +67,35 @@ 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;
|
||||
}
|
||||
@@ -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
|
||||
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 <string.h>
|
||||
#include <stdio.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 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;
|
||||
}
|
||||
@@ -4,12 +4,36 @@
|
||||
#include <stdlib.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 {
|
||||
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
|
||||
Reference in New Issue
Block a user