diff -r 966bfca56b9d -r ba7f043f9fdf demo/snake/snake.c --- a/demo/snake/snake.c Thu Aug 21 22:13:51 2025 +0200 +++ b/demo/snake/snake.c Fri Aug 22 23:27:36 2025 +0200 @@ -72,8 +72,9 @@ /** * The new position of the player when @c reset_position is @c true. */ - asc_vec2i new_position; + asc_vec2u new_position; unsigned health; + bool alive; bool reset_position; uint8_t number; } Player; @@ -272,25 +273,46 @@ asc_mesh_draw_triangle_strip(&sprite->mesh); } -static void player_set_health(Player *player, unsigned health) { - player->health = health; - // TODO: probably we want to add more effects when the health changes +static void player_position(Player *pl, unsigned x, unsigned y) { + pl->new_position.x = x; + pl->new_position.y = y; + pl->reset_position = true; +} + +static void player_position_random(Player *pl) { + // TODO: check if the spawn location is viable when there is more action on the board + player_position( + pl, + 4u + asc_util_rand(GAME_FIELD_SIZE - 8u), + 4u + asc_util_rand(GAME_FIELD_SIZE - 8u) + ); } -static unsigned player_get_health(Player *player) { - return player->health; +static void player_main_behavior(AscBehavior *behavior) { + AscSceneNode *node = behavior->node; + Player *player = node->user_data; + + if (player->alive && player->health == 0) { + player->alive = false; + asc_scene_node_hide(node); + // TODO: probably we don't want the entire trace to disappear instantly + cxListClear(player->trace); + // TODO: this should be controlled by another behavior that watches all players + game.state = GAME_STATE_GAME_OVER; + } + + // TODO: replace with respawn event + if (!player->alive && player->health > 0) { + player_position_random(player); + player->alive = true; + asc_scene_node_show(node); + } } static void player_move(AscBehavior *behavior) { AscSceneNode *node = behavior->node; Player *player = node->user_data; - // TODO: instead of skipping this behavior, it should be disabled when health is zero - if (player_get_health(player) == 0) return; - - // TODO: move this to a different behavior - asc_scene_node_show(node); - const float ts = (float) GAME_FIELD_TILE_SIZE; // check if the position is set programmatically @@ -354,12 +376,8 @@ if (node->position.x < 0 || node->position.y < 0 || node->position.x > GAME_FIELD_SIZE*GAME_FIELD_TILE_SIZE || node->position.y > GAME_FIELD_SIZE*GAME_FIELD_TILE_SIZE) { - // TODO: add fancy death animation - asc_scene_node_hide(node); - player_set_health(player, 0); - // TODO: remove the trace gradually (dequeuing the trace should be a different behavior) - cxListClear(player->trace); - game.state = GAME_STATE_GAME_OVER; + // TODO: replace setting health to zero with a "kill" event + player->health = 0; return; } @@ -383,9 +401,6 @@ } static void player_controls(AscBehavior *behavior) { - // TODO: instead of checking the game state, disable the behavior when the player is dead - if (game.state != GAME_STATE_PLAYING) return; - Player *player = behavior->node->user_data; // TODO: different key bindings for different player number in hot seat mode if (asc_key_pressed(ASC_KEY(LEFT))) { @@ -410,21 +425,6 @@ } } -static void player_position(Player *pl, int x, int y) { - pl->new_position.x = x; - pl->new_position.y = y; - pl->reset_position = true; -} - -static void player_position_random(Player *pl) { - // TODO: check if the spawn location is viable when there is more action on the board - player_position( - pl, - 4 + asc_util_rand(GAME_FIELD_SIZE - 8), - 4 + asc_util_rand(GAME_FIELD_SIZE - 8) - ); -} - static void player_destroy(CxAllocator *allocator, Player *player) { cxListFree(player->trace); cxFree(allocator, player); @@ -444,7 +444,6 @@ ); asc_scene_add_node(MAIN_SCENE, node); Player *player = asc_scene_node_allocate_data(node, sizeof(Player)); - player_position_random(player); player->speed = 3.f; // start with 3 tiles/sec player->number = 1; player->health = 100; @@ -453,9 +452,13 @@ cxDefineDestructor(player->trace, player_trace_release_tile); node->draw_func = player_draw; node->user_data_free_func = (cx_destructor_func2)player_destroy; - // add behaviors (the order is important!) + + // add behaviors + asc_behavior_add(node, player_main_behavior, .always_enabled = true); asc_behavior_add(node, player_controls); asc_behavior_add(node, player_move); + asc_behavior_pause_all_while_hidden(node); + return player; } @@ -580,9 +583,9 @@ if (game.state == GAME_STATE_GAME_OVER) { asc_scene_node_show(text_game_over); if (asc_key_pressed(ASC_KEY(R))) { + // TODO: instead of setting the health, send a "respawn" event to the behavior + game.players[0]->health = 100; // TODO: re-load the "level" - player_position_random(game.players[0]); - player_set_health(game.players[0], 100); game.state = GAME_STATE_PLAYING; asc_scene_node_hide(text_game_over); }