Added Game_World_Player and dying/respawning.
This commit is contained in:
@@ -7,17 +7,23 @@
|
||||
#define MIN_LEVEL_WIDTH 8 // TODO: SS - Move these out of here.
|
||||
#define MIN_LEVEL_HEIGHT 8
|
||||
|
||||
Game_World *game_world_create(uint32_t seed, uint8_t amount_of_food_to_spawn_initially, uint16_t level_width, uint16_t level_height) {
|
||||
Game_World *game_world_create(uint32_t seed, uint16_t max_players, uint16_t level_width, uint16_t level_height) {
|
||||
assert(level_width >= MIN_LEVEL_WIDTH); // We should probably have failed earlier.
|
||||
assert(level_height >= MIN_LEVEL_HEIGHT);
|
||||
|
||||
Game_World *w = (Game_World *)calloc(1, sizeof(Game_World));
|
||||
assert(w != NULL);
|
||||
|
||||
w->seed = seed;
|
||||
random_init(&w->random_generator, w->seed);
|
||||
|
||||
w->max_players = max_players;
|
||||
w->players = (Game_World_Player *)calloc(w->max_players, sizeof(Game_World_Player));
|
||||
assert(w->players != NULL);
|
||||
|
||||
w->max_entities = level_width * level_height;
|
||||
w->entities = (Entity *)calloc(w->max_entities, sizeof(Entity));
|
||||
assert(w->entities != NULL);
|
||||
|
||||
// Set up entity-id queue.
|
||||
assert(squeue_init(&w->entity_id_queue, w->max_entities, sizeof(Entity_ID)));
|
||||
@@ -28,8 +34,8 @@ Game_World *game_world_create(uint32_t seed, uint8_t amount_of_food_to_spawn_ini
|
||||
|
||||
grid_initialize(&w->grid, level_width, level_height);
|
||||
|
||||
// Spawn initial food(s).
|
||||
for(uint16_t i = 0; i < amount_of_food_to_spawn_initially; i++) {
|
||||
// Spawn initial food(s). One per player.
|
||||
for(uint16_t i = 0; i < w->max_players; i++) {
|
||||
Entity_ID entity_food;
|
||||
|
||||
uint16_t x = 0;
|
||||
@@ -51,8 +57,15 @@ void game_world_destroy(Game_World *world) {
|
||||
assert(world != NULL);
|
||||
|
||||
world->seed = 0;
|
||||
|
||||
world->max_players = 0;
|
||||
free(world->players);
|
||||
world->players = NULL;
|
||||
|
||||
world->max_entities = 0;
|
||||
free(world->entities);
|
||||
world->entities = NULL;
|
||||
|
||||
grid_dispose(&world->grid);
|
||||
squeue_free(&world->entity_id_queue);
|
||||
|
||||
@@ -122,6 +135,57 @@ void game_world_destroy_entity(Game_World *world, Entity_ID entity_id, bool dest
|
||||
}
|
||||
}
|
||||
|
||||
static void kill_and_respawn_player(Game_World *world, Entity *entity) {
|
||||
assert(world != NULL);
|
||||
assert(entity != NULL);
|
||||
|
||||
// Loop over the players to find the owner of this entity.
|
||||
Game_World_Player *owner_player = NULL;
|
||||
for(uint16_t i = 0; i < world->max_players; i++) {
|
||||
Game_World_Player *player = &world->players[i];
|
||||
if(!player->active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(player->entity_id == entity->id) {
|
||||
owner_player = player;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(owner_player != NULL);
|
||||
|
||||
game_world_destroy_entity(world, entity->id, true);
|
||||
owner_player->entity_id = INVALID_ENTITY_ID;
|
||||
|
||||
{ // Respawn.
|
||||
Entity_ID entity_snake_id;
|
||||
|
||||
uint16_t spawn_x = 0;
|
||||
uint16_t spawn_y = 0;
|
||||
assert(game_world_find_position_to_spawn(world, &spawn_x, &spawn_y));
|
||||
|
||||
assert(game_world_create_entity(
|
||||
world,
|
||||
Entity_Type_Snake_Head,
|
||||
spawn_x, spawn_y,
|
||||
&entity_snake_id
|
||||
));
|
||||
|
||||
owner_player->entity_id = entity_snake_id;
|
||||
|
||||
Entity *player_entity = game_world_try_get_entity_by_id(
|
||||
world,
|
||||
owner_player->entity_id
|
||||
);
|
||||
|
||||
// Set initial move-direction for player-entities.
|
||||
player_entity->move_direction = game_world_choose_initial_move_direction_based_on_coords(
|
||||
world,
|
||||
spawn_x, spawn_y
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void game_world_tick(Game_World *world) {
|
||||
// Game-logic! :)
|
||||
|
||||
@@ -133,7 +197,144 @@ void game_world_tick(Game_World *world) {
|
||||
|
||||
switch(entity->type) {
|
||||
case Entity_Type_Snake_Head: {
|
||||
{ // Move entity
|
||||
int16_t dx = 0;
|
||||
int16_t dy = 0;
|
||||
|
||||
switch(entity->move_direction) {
|
||||
case Entity_Movement_Direction_None: {
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Up: {
|
||||
dy = -1;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Down: {
|
||||
dy = 1;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Right: {
|
||||
dx = 1;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Left: {
|
||||
dx = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dx != 0 || dy != 0) {
|
||||
// Try moving.
|
||||
|
||||
// Figure out what cell we're on and what cell we want to go to.
|
||||
Grid_Cell *current_cell = grid_get_cell(&world->grid, entity->x, entity->y);
|
||||
assert(current_cell != NULL);
|
||||
Grid_Cell *target_cell = grid_get_cell(&world->grid, entity->x + dx, entity->y + dy);
|
||||
|
||||
if(target_cell == NULL) {
|
||||
// Target cell does not exist.
|
||||
|
||||
// TODO: SS - Check the session's settings.
|
||||
// Maybe each session could decide whether the snake should "loop-around" or not.
|
||||
// If the snake shouldn't loop around, die. For now, we say that it's not okay to loop around.
|
||||
bool should_loop_around = false;
|
||||
if(should_loop_around) {
|
||||
// TODO: SS - Implement looping around.
|
||||
}
|
||||
else {
|
||||
kill_and_respawn_player(world, entity);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bool should_move_to_target_cell = false;
|
||||
bool snake_ate = false;
|
||||
|
||||
if(target_cell->entity != NULL) {
|
||||
// Target cell is occupied.
|
||||
|
||||
// Check what type of entity it is and determine what should happen.
|
||||
switch(target_cell->entity->type) {
|
||||
case Entity_Type_Snake_Head: {
|
||||
kill_and_respawn_player(world, entity);
|
||||
break;
|
||||
}
|
||||
case Entity_Type_Snake_Body: {
|
||||
kill_and_respawn_player(world, entity);
|
||||
break;
|
||||
}
|
||||
case Entity_Type_Food: {
|
||||
// Eat!
|
||||
assert(grid_try_remove_entity_from_cell(target_cell));
|
||||
|
||||
snake_ate = true;
|
||||
should_move_to_target_cell = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Target cell is unoccupied and free to enter.
|
||||
should_move_to_target_cell = true;
|
||||
}
|
||||
|
||||
if(should_move_to_target_cell) {
|
||||
{ // Move snake recursively.
|
||||
Entity *e = entity;
|
||||
Grid_Cell *t = target_cell;
|
||||
while(e != NULL) {
|
||||
Grid_Cell *a = grid_get_cell(&world->grid, e->x, e->y);
|
||||
assert(a != NULL);
|
||||
|
||||
grid_move_entity_from_cell_to_cell(a, t);
|
||||
|
||||
e = game_world_try_get_entity_by_id(world, e->child);
|
||||
t = a;
|
||||
}
|
||||
}
|
||||
|
||||
if(snake_ate) {
|
||||
Entity *e = entity;
|
||||
uint16_t child_index = 0;
|
||||
while(e->child != INVALID_ENTITY_ID) {
|
||||
e = game_world_try_get_entity_by_id(world, e->child);
|
||||
child_index += 1;
|
||||
}
|
||||
|
||||
Entity_ID child_entity = INVALID_ENTITY_ID;
|
||||
assert(game_world_create_entity(
|
||||
world,
|
||||
Entity_Type_Snake_Body,
|
||||
e->prev_x,
|
||||
e->prev_y,
|
||||
&child_entity
|
||||
));
|
||||
|
||||
assert(e->child == INVALID_ENTITY_ID);
|
||||
e->child = child_entity;
|
||||
|
||||
{ // Spawn food.
|
||||
uint16_t food_spawn_x = 0;
|
||||
uint16_t food_spawn_y = 0;
|
||||
|
||||
if(!game_world_find_position_to_spawn(world, &food_spawn_x, &food_spawn_y)) {
|
||||
printf("Failed to find a position to spawn food.\n");
|
||||
}
|
||||
else {
|
||||
Entity_ID entity_food;
|
||||
assert(game_world_create_entity(
|
||||
world,
|
||||
Entity_Type_Food,
|
||||
food_spawn_x, food_spawn_y,
|
||||
&entity_food
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Entity_Type_Snake_Body: {
|
||||
@@ -150,149 +351,41 @@ void game_world_tick(Game_World *world) {
|
||||
}
|
||||
}
|
||||
|
||||
{ // Move entity
|
||||
int16_t dx = 0;
|
||||
int16_t dy = 0;
|
||||
|
||||
switch(entity->move_direction) {
|
||||
case Entity_Movement_Direction_None: {
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Up: {
|
||||
dy = -1;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Down: {
|
||||
dy = 1;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Right: {
|
||||
dx = 1;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Left: {
|
||||
dx = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dx != 0 || dy != 0) {
|
||||
// Try moving.
|
||||
|
||||
// Figure out what cell we're on and what cell we want to go to.
|
||||
Grid_Cell *current_cell = grid_get_cell(&world->grid, entity->x, entity->y);
|
||||
assert(current_cell != NULL);
|
||||
Grid_Cell *target_cell = grid_get_cell(&world->grid, entity->x + dx, entity->y + dy);
|
||||
|
||||
// { // Debug-logging.
|
||||
// printf("Current cell = (x: %u, y: %u, entity: %p).\n", current_cell->x, current_cell->y, current_cell->entity);
|
||||
// if(target_cell != NULL) {
|
||||
// printf("Target cell = (x: %u, y: %u, entity: %p).\n", target_cell->x, target_cell->y, target_cell->entity);
|
||||
// }
|
||||
// else {
|
||||
// printf("Target cell = NULL!\n");
|
||||
// }
|
||||
// }
|
||||
|
||||
if(target_cell == NULL) {
|
||||
// Target cell does not exist.
|
||||
// TODO: SS - Check the session's settings.
|
||||
// Maybe each session could decide whether the snake should "loop-around" or not.
|
||||
// If the snake shouldn't loop around, die.
|
||||
}
|
||||
else {
|
||||
bool should_move_to_target_cell = false;
|
||||
bool snake_ate = false;
|
||||
|
||||
if(target_cell->entity != NULL) {
|
||||
// Target cell is occupied.
|
||||
|
||||
// Check what type of entity it is and determine what should happen.
|
||||
switch(target_cell->entity->type) {
|
||||
case Entity_Type_Snake_Head: {
|
||||
// TODO: SS - Die.
|
||||
break;
|
||||
}
|
||||
case Entity_Type_Snake_Body: {
|
||||
// TODO: SS - Die.
|
||||
break;
|
||||
}
|
||||
case Entity_Type_Food: {
|
||||
// Eat!
|
||||
assert(grid_try_remove_entity_from_cell(target_cell));
|
||||
|
||||
snake_ate = true;
|
||||
should_move_to_target_cell = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Target cell is unoccupied and free to enter.
|
||||
should_move_to_target_cell = true;
|
||||
}
|
||||
|
||||
if(should_move_to_target_cell) {
|
||||
{ // Move snake recursively.
|
||||
Entity *e = entity;
|
||||
Grid_Cell *t = target_cell;
|
||||
while(e != NULL) {
|
||||
Grid_Cell *a = grid_get_cell(&world->grid, e->x, e->y);
|
||||
assert(a != NULL);
|
||||
|
||||
grid_move_entity_from_cell_to_cell(a, t);
|
||||
|
||||
e = game_world_try_get_entity_by_id(world, e->child);
|
||||
t = a;
|
||||
}
|
||||
}
|
||||
|
||||
if(snake_ate) {
|
||||
Entity *e = entity;
|
||||
uint16_t child_index = 0;
|
||||
while(e->child != INVALID_ENTITY_ID) {
|
||||
e = game_world_try_get_entity_by_id(world, e->child);
|
||||
child_index += 1;
|
||||
}
|
||||
|
||||
Entity_ID child_entity = INVALID_ENTITY_ID;
|
||||
assert(game_world_create_entity(
|
||||
world,
|
||||
Entity_Type_Snake_Body,
|
||||
e->prev_x,
|
||||
e->prev_y,
|
||||
&child_entity
|
||||
));
|
||||
|
||||
assert(e->child == INVALID_ENTITY_ID);
|
||||
e->child = child_entity;
|
||||
|
||||
{ // Spawn food.
|
||||
uint16_t food_spawn_x = 0;
|
||||
uint16_t food_spawn_y = 0;
|
||||
|
||||
if(!game_world_find_position_to_spawn(world, &food_spawn_x, &food_spawn_y)) {
|
||||
printf("Failed to find a position to spawn food.\n");
|
||||
}
|
||||
else {
|
||||
Entity_ID entity_food;
|
||||
assert(game_world_create_entity(
|
||||
world,
|
||||
Entity_Type_Food,
|
||||
food_spawn_x, food_spawn_y,
|
||||
&entity_food
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool game_world_add_player(Game_World *world, Game_World_Player **out_gw_player) {
|
||||
assert(world != NULL);
|
||||
assert(out_gw_player != NULL);
|
||||
|
||||
for(uint16_t i = 0; i < world->max_players; i++) {
|
||||
Game_World_Player *player = &world->players[i];
|
||||
|
||||
if(player->active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found free player.
|
||||
player->active = true;
|
||||
*out_gw_player = player;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool game_world_remove_player(Game_World *world, Game_World_Player *player) {
|
||||
assert(world != NULL);
|
||||
assert(player != NULL);
|
||||
|
||||
game_world_destroy_entity(world, player->entity_id, true);
|
||||
|
||||
memset(player, 0, sizeof(Game_World_Player));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Entity *game_world_try_get_entity_by_id(Game_World *world, Entity_ID id) {
|
||||
assert(world != NULL);
|
||||
|
||||
|
||||
@@ -6,10 +6,14 @@
|
||||
#include "grid.h"
|
||||
#include "shared/squeue.h"
|
||||
#include "shared/random.h"
|
||||
#include "game_world_player.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t seed;
|
||||
Random_Generator random_generator;
|
||||
|
||||
Game_World_Player *players;
|
||||
uint16_t max_players;
|
||||
|
||||
SQueue entity_id_queue;
|
||||
Entity *entities;
|
||||
@@ -19,7 +23,7 @@ typedef struct {
|
||||
|
||||
} Game_World;
|
||||
|
||||
Game_World *game_world_create(uint32_t seed, uint8_t amount_of_food_to_spawn_initially, uint16_t level_width, uint16_t level_height);
|
||||
Game_World *game_world_create(uint32_t seed, uint16_t max_players, 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);
|
||||
@@ -27,7 +31,8 @@ void game_world_destroy_entity(Game_World *world, Entity_ID entity_id, bool dest
|
||||
|
||||
void game_world_tick(Game_World *world);
|
||||
|
||||
// TODO: SS - "void game_world_spawn_player(Game_World *world, ..)"
|
||||
bool game_world_add_player(Game_World *world, Game_World_Player **out_gw_player);
|
||||
bool game_world_remove_player(Game_World *world, Game_World_Player *player);
|
||||
|
||||
Entity *game_world_try_get_entity_by_id(Game_World *world, Entity_ID id);
|
||||
|
||||
|
||||
10
src/game/shared/game_world_player.h
Normal file
10
src/game/shared/game_world_player.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#ifndef GAME_WORLD_PLAYER_H
|
||||
#define GAME_WORLD_PLAYER_H
|
||||
|
||||
typedef struct {
|
||||
bool active;
|
||||
|
||||
Entity_ID entity_id; // The entity that this player is controlling. Can be INVALID_ENTITY_ID.
|
||||
} Game_World_Player;
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user