Body-part now follows it's parent. You can also eat food. Shortcut in the main-menu to play default singleplayer, press P.
This commit is contained in:
@@ -10,6 +10,8 @@
|
||||
|
||||
#include "session/game_session.h"
|
||||
|
||||
#include "shared/wang_hash.h"
|
||||
|
||||
#define GRID_CELL_SIZE 8
|
||||
#define ENTITY_PRESENTATION_Y_OFFSET -2
|
||||
|
||||
@@ -84,6 +86,10 @@ static void state_tick(Presentation_State *state) {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t floor_texture_variant(uint32_t x, uint32_t y, uint32_t seed, uint32_t num_variants) {
|
||||
return (wang_hash(x * 73856093u ^ y * 19349663u ^ seed)) % num_variants;
|
||||
}
|
||||
|
||||
static void state_render(Presentation_State *state) {
|
||||
Presentation_State_Ingame_Context *ctx = (Presentation_State_Ingame_Context *)state->context;
|
||||
(void)ctx;
|
||||
@@ -108,7 +114,7 @@ static void state_render(Presentation_State *state) {
|
||||
uint32_t pres_x = x * GRID_CELL_SIZE;
|
||||
uint32_t pres_y = y * GRID_CELL_SIZE;
|
||||
|
||||
uint32_t random_floor_index = i; // TODO: SS - Get a deterministic random value based on seed.
|
||||
uint32_t random_floor_index = floor_texture_variant(x, y, world->seed, 4);
|
||||
|
||||
DrawTextureRec(
|
||||
ctx->texture_grass,
|
||||
@@ -116,7 +122,7 @@ static void state_render(Presentation_State *state) {
|
||||
(Vector2) { pres_x, pres_y },
|
||||
WHITE
|
||||
);
|
||||
DrawRectangleLines(pres_x, pres_y, GRID_CELL_SIZE, GRID_CELL_SIZE, (Color) { 0, 0, 0, 8 });
|
||||
DrawRectangleLines(pres_x, pres_y, GRID_CELL_SIZE, GRID_CELL_SIZE, (Color) { 0, 0, 0, 2 }); // TODO: SS - Let the user customize the alpha locally.
|
||||
}
|
||||
|
||||
for(uint32_t i = 0; i < grid_total_size; i++) {
|
||||
@@ -159,6 +165,30 @@ static void state_render(Presentation_State *state) {
|
||||
(Color) { 0, 0, 0, 32 }
|
||||
);
|
||||
|
||||
float rotation = 0.0f;
|
||||
switch(entity->move_direction){
|
||||
case Entity_Movement_Direction_None: {
|
||||
rotation = 0.0f;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Up: {
|
||||
rotation = -90.0f;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Down: {
|
||||
rotation = 90.0f;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Right: {
|
||||
rotation = 0.0f;
|
||||
break;
|
||||
}
|
||||
case Entity_Movement_Direction_Left: {
|
||||
rotation = 180.0f;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DrawTexturePro(
|
||||
*texture,
|
||||
(Rectangle) { // Source.
|
||||
@@ -171,7 +201,7 @@ static void state_render(Presentation_State *state) {
|
||||
GRID_CELL_SIZE
|
||||
},
|
||||
origin, // Origin.
|
||||
0, // Rotation.
|
||||
rotation, // Rotation.
|
||||
tint // Tint.
|
||||
);
|
||||
|
||||
|
||||
@@ -14,7 +14,23 @@ static void state_enter(Presentation_State *state) {
|
||||
}
|
||||
|
||||
static void state_tick(Presentation_State *state) {
|
||||
(void)state;
|
||||
Presentation_State_Main_Menu_Context *ctx = (Presentation_State_Main_Menu_Context *)state->context;
|
||||
|
||||
{ // DEBUG
|
||||
if(IsKeyPressed(KEY_P)) {
|
||||
ctx->is_singleplayer = true;
|
||||
ctx->mode = Main_Menu_Mode_Game_Setup;
|
||||
game_session_init_default_settings(ctx->is_singleplayer, &ctx->session_settings);
|
||||
|
||||
game_session_create(
|
||||
ctx->is_singleplayer,
|
||||
!ctx->is_singleplayer,
|
||||
ctx->session_settings
|
||||
);
|
||||
|
||||
presentation_state_machine_go_to(&presentation_state_ingame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void state_render(Presentation_State *state) {
|
||||
|
||||
@@ -25,12 +25,17 @@ typedef enum {
|
||||
typedef struct {
|
||||
bool active;
|
||||
|
||||
// TODO: SS - Maybe add an ID here.
|
||||
|
||||
Entity_Type type;
|
||||
|
||||
uint16_t x;
|
||||
uint16_t y;
|
||||
|
||||
Entity_Movement_Direction move_direction;
|
||||
Entity_Movement_Direction prev_move_direction;
|
||||
|
||||
Entity_ID child;
|
||||
|
||||
// TODO: SS - Color/tint?
|
||||
} Entity;
|
||||
|
||||
@@ -66,10 +66,8 @@ bool game_world_create_entity(Game_World *world, Entity_Type type, uint16_t x, u
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
Entity_ID id = 0;
|
||||
bool got_id = squeue_pop(&world->entity_id_queue, (void *)&id);
|
||||
if(!got_id) {
|
||||
if(!squeue_pop(&world->entity_id_queue, (void *)&id)) {
|
||||
printf("No free entity ids.\n");
|
||||
return false;
|
||||
}
|
||||
@@ -77,6 +75,7 @@ bool game_world_create_entity(Game_World *world, Entity_Type type, uint16_t x, u
|
||||
world->entities[id] = (Entity) {
|
||||
.active = true,
|
||||
.type = type,
|
||||
.child = INVALID_ENTITY_ID
|
||||
};
|
||||
|
||||
Grid_Cell *cell = grid_get_cell(&world->grid, x, y); // TEMP: SS - Hardcoded coordinates. // TODO: SS - Find good coordinate.
|
||||
|
||||
34
src/shared/random.h
Normal file
34
src/shared/random.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#ifndef RANDOM_H
|
||||
#define RANDOM_H
|
||||
|
||||
typedef struct {
|
||||
uint32_t seed;
|
||||
uint32_t state;
|
||||
} Random_Generator;
|
||||
|
||||
static inline void random_init(Random_Generator *random_generator, uint32_t seed) {
|
||||
if(seed == 0) {
|
||||
seed = 1;
|
||||
}
|
||||
random_generator->seed = seed;
|
||||
random_generator->state = random_generator->seed;
|
||||
}
|
||||
|
||||
static inline uint32_t random_next_u32(Random_Generator *random_generator) {
|
||||
uint32_t x = random_generator->state;
|
||||
|
||||
x ^= x << 13;
|
||||
x ^= x >> 17;
|
||||
x ^= x << 5;
|
||||
|
||||
random_generator->state = x;
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
static inline uint32_t random_u64_range(Random_Generator *random_generator, uint32_t min, uint32_t max) {
|
||||
return min + (random_next_u32(rng) % (max - min + 1));
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
13
src/shared/wang_hash.h
Normal file
13
src/shared/wang_hash.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef WANG_HASH_H
|
||||
#define WANG_HASH_H
|
||||
|
||||
static inline uint32_t wang_hash(uint32_t v) {
|
||||
v = (v ^ 61) ^ (v >> 16);
|
||||
v = v + (v << 3);
|
||||
v = v ^ (v >> 4);
|
||||
v = v * 0x27d4eb2d;
|
||||
v = v ^ (v >> 15);
|
||||
return v;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -181,7 +181,34 @@ void simulation_world_tick(Simulation_World *simulation_world) {
|
||||
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 entities.
|
||||
// Entity *entity_to_follow = entity;
|
||||
// if(entity->parent != INVALID_ENTITY_ID) {
|
||||
// entity_to_follow = game_world_try_get_entity_by_id(gw, entity->parent);
|
||||
// assert(entity_to_follow != NULL);
|
||||
|
||||
// entity->move_direction = entity_to_follow->prev_move_direction;
|
||||
// }
|
||||
|
||||
int16_t dx = 0;
|
||||
int16_t dy = 0;
|
||||
|
||||
@@ -207,11 +234,14 @@ void simulation_world_tick(Simulation_World *simulation_world) {
|
||||
}
|
||||
}
|
||||
|
||||
entity->prev_move_direction = entity->move_direction;
|
||||
|
||||
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);
|
||||
Grid_Cell *start_cell = current_cell;
|
||||
assert(current_cell != NULL);
|
||||
Grid_Cell *target_cell = grid_get_cell(&gw->grid, entity->x + dx, entity->y + dy);
|
||||
|
||||
@@ -227,9 +257,14 @@ void simulation_world_tick(Simulation_World *simulation_world) {
|
||||
|
||||
if(target_cell == NULL) {
|
||||
// Target cell does not exist.
|
||||
// TODO: SS - Die.
|
||||
// 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.
|
||||
|
||||
@@ -244,14 +279,49 @@ void simulation_world_tick(Simulation_World *simulation_world) {
|
||||
break;
|
||||
}
|
||||
case Entity_Type_Food: {
|
||||
// TODO: SS - Eat!
|
||||
// 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.
|
||||
grid_move_entity_from_cell_to_cell(current_cell, target_cell);
|
||||
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;
|
||||
}
|
||||
}
|
||||
// // TODO: SS - Recurse through this entity's children and move them too.
|
||||
// if(entity->child != INVALID_ENTITY_ID) {
|
||||
// printf("TODO: SS - MOVE CHILDREN TOO!\n");
|
||||
// }
|
||||
|
||||
|
||||
if(snake_should_grow) {
|
||||
// TODO: SS - Spawn the entity and make it a child of the snake's last child.
|
||||
Entity_ID child_entity = INVALID_ENTITY_ID;
|
||||
assert(game_world_create_entity(gw, Entity_Type_Snake_Body, start_cell->x, start_cell->y, &child_entity));
|
||||
|
||||
assert(entity->child == INVALID_ENTITY_ID);
|
||||
entity->child = child_entity;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user