Moved code and assets for the game to a seperate folder. Moved jansson and raylib to third_party.
This commit is contained in:
365
src/game/simulation/simulation_world.c
Normal file
365
src/game/simulation/simulation_world.c
Normal file
@@ -0,0 +1,365 @@
|
||||
#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, bool online, uint8_t max_players, uint16_t width, uint16_t height) {
|
||||
Simulation_World w;
|
||||
memset(&w, 0, sizeof(Simulation_World));
|
||||
|
||||
w.tick = 0;
|
||||
|
||||
w.online = online;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void simulation_destroy_world(Simulation_World *simulation_world) {
|
||||
assert(simulation_world != NULL);
|
||||
|
||||
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;
|
||||
|
||||
assert(game_world_create_entity(
|
||||
simulation_world->game_world,
|
||||
Entity_Type_Snake_Head,
|
||||
i+6, 15, // NOTE: SS - Hardcoded spawn-position.
|
||||
&player->entity_id
|
||||
));
|
||||
|
||||
Entity *player_entity = game_world_try_get_entity_by_id(simulation_world->game_world, player->entity_id);
|
||||
|
||||
// Set initial move-direction for player-entities.
|
||||
// The initial direction should probably take the spawn-position in to account.
|
||||
player_entity->move_direction = Entity_Movement_Direction_Up;
|
||||
|
||||
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;
|
||||
|
||||
switch(simulation_world->match_state) {
|
||||
case Simulation_Match_State_Waiting_To_Start: {
|
||||
if(!simulation_world->online) { // If not online, just go to Counting_Down.
|
||||
simulation_world->match_state = Simulation_Match_State_Counting_Down;
|
||||
}
|
||||
else {
|
||||
if(simulation_world_amount_of_active_players(simulation_world) > 1) {
|
||||
simulation_world->match_state = Simulation_Match_State_Counting_Down;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Simulation_Match_State_Counting_Down: {
|
||||
if(simulation_world->tick > 20) { // TEMP: SS - Hardcoded number and condition to break out of this state.
|
||||
simulation_world->match_state = Simulation_Match_State_Active;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Simulation_Match_State_Active: {
|
||||
if(simulation_world->tick > 500) { // TEMP: SS - Hardcoded number and condition to break out of this state.
|
||||
simulation_world->match_state = Simulation_Match_State_Ended;
|
||||
}
|
||||
else {
|
||||
Game_World *gw = simulation_world->game_world;
|
||||
|
||||
// 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(gw, player->entity_id);
|
||||
assert(player_entity != NULL);
|
||||
|
||||
// Snakes can't go backwards. They have to turn.
|
||||
|
||||
switch(player_entity->move_direction) {
|
||||
case Entity_Movement_Direction_None: {
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Up:
|
||||
case Entity_Movement_Direction_Down: // Snakes can only go right/left if it's currently moving up/down.
|
||||
{
|
||||
if(player->input.right) {
|
||||
player_entity->move_direction = Entity_Movement_Direction_Right;
|
||||
}
|
||||
else if(player->input.left) {
|
||||
player_entity->move_direction = Entity_Movement_Direction_Left;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Right:
|
||||
case Entity_Movement_Direction_Left: // Snakes can only go up/down if it's currently moving right/left.
|
||||
{
|
||||
if(player->input.up) {
|
||||
player_entity->move_direction = Entity_Movement_Direction_Up;
|
||||
}
|
||||
else if(player->input.down) {
|
||||
player_entity->move_direction = Entity_Movement_Direction_Down;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Game-logic! :)
|
||||
|
||||
for(uint16_t i = 0; i < gw->max_entities; i++) {
|
||||
Entity *entity = &gw->entities[i];
|
||||
if(!entity->active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(entity->type) {
|
||||
case Entity_Type_Snake_Head: {
|
||||
|
||||
break;
|
||||
}
|
||||
case Entity_Type_Snake_Body: {
|
||||
// Skip.
|
||||
break;
|
||||
}
|
||||
case Entity_Type_Food: {
|
||||
// Skip (for now).
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
printf("Unhandled entity-type %i.\n", entity->type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
{ // 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(&gw->grid, entity->x, entity->y);
|
||||
assert(current_cell != NULL);
|
||||
Grid_Cell *target_cell = grid_get_cell(&gw->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_should_grow = 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_should_grow = 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(&gw->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(gw, e->child);
|
||||
t = a;
|
||||
}
|
||||
}
|
||||
|
||||
if(snake_should_grow) {
|
||||
Entity *e = entity;
|
||||
uint16_t child_index = 0;
|
||||
while(e->child != INVALID_ENTITY_ID) {
|
||||
e = game_world_try_get_entity_by_id(gw, e->child);
|
||||
child_index += 1;
|
||||
}
|
||||
|
||||
Entity_ID child_entity = INVALID_ENTITY_ID;
|
||||
assert(game_world_create_entity(
|
||||
gw,
|
||||
Entity_Type_Snake_Body,
|
||||
e->prev_x,
|
||||
e->prev_y,
|
||||
&child_entity
|
||||
));
|
||||
|
||||
assert(e->child == INVALID_ENTITY_ID);
|
||||
e->child = child_entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Simulation_Match_State_Ended: {
|
||||
printf("Match over!\n");
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
simulation_world->tick += 1; // TODO: SS - Stop ticking if match has ended.
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint16_t simulation_world_amount_of_active_players(Simulation_World *simulation_world) {
|
||||
assert(simulation_world != NULL);
|
||||
|
||||
uint16_t count = 0;
|
||||
|
||||
for(uint16_t i = 0; i < simulation_world->max_players; i++) {
|
||||
Simulation_Player *player = &simulation_world->players[i];
|
||||
if(player->active) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
Reference in New Issue
Block a user