diff --git a/resources/examples/raylib/raylib_arkanoid.c3 b/resources/examples/raylib/raylib_arkanoid.c3 new file mode 100644 index 000000000..316f34ee4 --- /dev/null +++ b/resources/examples/raylib/raylib_arkanoid.c3 @@ -0,0 +1,312 @@ +module arkanoid; +/** + * + * raylib - classic game: arkanoid + * + * Sample game developed by Marc Palau and Ramon Santamaria + * converted to C3 by Christoffer Lerno + * + * Copyright (c) 2015 Ramon Santamaria (@raysan5) + */ + +const int SCREEN_WIDTH = 800; +const int SCREEN_HEIGHT = 450; + +//---------------------------------------------------------------------------------- +// Some Defines +//---------------------------------------------------------------------------------- +const PLAYER_MAX_LIFE = 5; +const LINES_OF_BRICKS = 5; +const BRICKS_PER_LINE = 20; + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +enum GameScreen +{ + LOGO, + TITLE, + GAMEPLAY, + ENDING +} + +struct Player +{ + Vector2 position; + Vector2 size; + int life; +} + +struct Ball +{ + Vector2 position; + Vector2 speed; + int radius; + bool active; +} + +struct Brick +{ + Vector2 position; + bool active; +} + +//------------------------------------------------------------------------------------ +// Global Variables Declaration +//------------------------------------------------------------------------------------ + +bool game_over = false; +bool pause = false; + +Player player; +Ball ball; +Brick[BRICKS_PER_LINE][LINES_OF_BRICKS] brick; +Vector2 brick_size; + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +fn void main() +{ + // Initialization (Note windowTitle is unused on Android) + //--------------------------------------------------------- + raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: arkanoid"); + + init_game(); + + raylib::set_target_fps(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!raylib::window_should_close()) // Detect window close button or ESC key + { + // Update and Draw + //---------------------------------------------------------------------------------- + update_draw_frame(); + //---------------------------------------------------------------------------------- + } + // De-Initialization + //-------------------------------------------------------------------------------------- + unload_game(); // Unload loaded data (textures, sounds, models...) + + raylib::close_window(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + +} + +//------------------------------------------------------------------------------------ +// Module Functions Definitions (local) +//------------------------------------------------------------------------------------ + +// Initialize game variables +fn void init_game() +{ + brick_size = { raylib::get_screen_width() / BRICKS_PER_LINE, 40 }; + + // Initialize player + player.position = { SCREEN_WIDTH/2, SCREEN_HEIGHT * 7 / 8 }; + player.size = { SCREEN_WIDTH / 10, 20 }; + player.life = PLAYER_MAX_LIFE; + + // Initialize ball + ball.position = { SCREEN_WIDTH / 2, SCREEN_HEIGHT * 7 / 8 - 30 }; + ball.speed = { 0, 0 }; + ball.radius = 7; + ball.active = false; + + // Initialize bricks + int initial_down_position = 50; + + for (int i = 0; i < LINES_OF_BRICKS; i++) + { + for (int j = 0; j < BRICKS_PER_LINE; j++) + { + brick[i][j].position = { j * brick_size.x + brick_size.x / 2, i * brick_size.y + initial_down_position }; + brick[i][j].active = true; + } + } +} + +// Update game (one frame) +fn void update_game() +{ + if (game_over) + { + if (raylib::is_key_pressed(keyboard::ENTER)) + { + init_game(); + game_over = false; + } + } + if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause; + + if (pause) return; + // Player movement logic + if (raylib::is_key_down(keyboard::LEFT)) player.position.x -= 5; + if ((player.position.x - player.size.x/2) <= 0) player.position.x = player.size.x/2; + if (raylib::is_key_down(keyboard::RIGHT)) player.position.x += 5; + if ((player.position.x + player.size.x/2) >= SCREEN_WIDTH) player.position.x = SCREEN_WIDTH - player.size.x/2; + + // Ball launching logic + if (!ball.active) + { + if (raylib::is_key_pressed(keyboard::SPACE)) + { + ball.active = true; + ball.speed = { 0, -5 }; + } + } + + // Ball movement logic + if (ball.active) + { + ball.position.x += ball.speed.x; + ball.position.y += ball.speed.y; + } + else + { + ball.position = { player.position.x, SCREEN_HEIGHT * 7 / 8 - 30 }; + } + + // Collision logic: ball vs walls + if (((ball.position.x + ball.radius) >= SCREEN_WIDTH) || ((ball.position.x - ball.radius) <= 0)) ball.speed.x *= -1; + if ((ball.position.y - ball.radius) <= 0) ball.speed.y *= -1; + if ((ball.position.y + ball.radius) >= SCREEN_HEIGHT) + { + ball.speed = { 0, 0 }; + ball.active = false; + + player.life--; + } + + // Collision logic: ball vs player + if (raylib::check_collision_circle_rec(ball.position, ball.radius, + Rectangle{ player.position.x - player.size.x / 2, player.position.y - player.size.y / 2, player.size.x, player.size.y})) + { + if (ball.speed.y > 0) + { + ball.speed.y *= -1; + ball.speed.x = (ball.position.x - player.position.x) / (player.size.x / 2) * 5; + } + } + + // Collision logic: ball vs bricks + for (int i = 0; i < LINES_OF_BRICKS; i++) + { + for (int j = 0; j < BRICKS_PER_LINE; j++) + { + if (brick[i][j].active) + { + // Hit below + if (((ball.position.y - ball.radius) <= (brick[i][j].position.y + brick_size.y / 2)) && + ((ball.position.y - ball.radius) > (brick[i][j].position.y + brick_size.y / 2 + ball.speed.y)) && + ((math::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x / 2 + ball.radius * 2 / 3)) && (ball.speed.y < 0)) + { + brick[i][j].active = false; + ball.speed.y *= -1; + } + // Hit above + else if (((ball.position.y + ball.radius) >= (brick[i][j].position.y - brick_size.y/2)) && + ((ball.position.y + ball.radius) < (brick[i][j].position.y - brick_size.y/2 + ball.speed.y)) && + ((math::fabs((double)ball.position.x - brick[i][j].position.x)) < (double)(brick_size.x/2 + ball.radius*2/3)) && (ball.speed.y > 0)) + { + brick[i][j].active = false; + ball.speed.y *= -1; + } + // Hit left + else if (((ball.position.x + ball.radius) >= (brick[i][j].position.x - brick_size.x/2)) && + ((ball.position.x + ball.radius) < (brick[i][j].position.x - brick_size.x/2 + ball.speed.x)) && + ((math::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x > 0)) + { + brick[i][j].active = false; + ball.speed.x *= -1; + } + // Hit right + else if (((ball.position.x - ball.radius) <= (brick[i][j].position.x + brick_size.x/2)) && + ((ball.position.x - ball.radius) > (brick[i][j].position.x + brick_size.x/2 + ball.speed.x)) && + ((math::fabs((double)ball.position.y - brick[i][j].position.y)) < (double)(brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x < 0)) + { + brick[i][j].active = false; + ball.speed.x *= -1; + } + } + } + } + + // Game over logic + if (player.life <= 0) + { + game_over = true; + } + else + { + game_over = true; + for (int i = 0; i < LINES_OF_BRICKS; i++) + { + for (int j = 0; j < BRICKS_PER_LINE; j++) + { + if (brick[i][j].active) game_over = false; + } + } + } +} + +// Draw game (one frame) +fn void draw_game() +{ + raylib::begin_drawing(); + + raylib::clear_background(raylib::RAYWHITE); + + if (!game_over) + { + // Draw player bar + raylib::draw_rectangle((int)(player.position.x - player.size.x/2), (int)(player.position.y - player.size.y/2), (int)player.size.x, (int)player.size.y, raylib::BLACK); + + // Draw player lives + for (int i = 0; i < player.life; i++) raylib::draw_rectangle(20 + 40*i, SCREEN_HEIGHT - 30, 35, 10, raylib::LIGHTGRAY); + + // Draw ball + raylib::draw_circle_v(ball.position, ball.radius, raylib::MAROON); + + // Draw bricks + for (int i = 0; i < LINES_OF_BRICKS; i++) + { + for (int j = 0; j < BRICKS_PER_LINE; j++) + { + if (brick[i][j].active) + { + if ((i + j) % 2 == 0) + { + raylib::draw_rectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, raylib::GRAY); + } + else + { + raylib::draw_rectangle((int)(brick[i][j].position.x - brick_size.x/2), (int)(brick[i][j].position.y - brick_size.y/2), (int)brick_size.x, (int)brick_size.y, raylib::DARKGRAY); + } + } + } + } + + if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY); + } + else + { + raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY); + } + + raylib::end_drawing(); +} + +// Unload game variables +fn void unload_game() +{ + // TODO: Unload all dynamic loaded data (textures, sounds, models...) +} + +// Update and Draw (one frame) +fn void update_draw_frame() +{ + update_game(); + draw_game(); +} \ No newline at end of file diff --git a/resources/examples/raylib/raylib_snake.c3 b/resources/examples/raylib/raylib_snake.c3 new file mode 100644 index 000000000..dde0d347b --- /dev/null +++ b/resources/examples/raylib/raylib_snake.c3 @@ -0,0 +1,251 @@ +module snake; +/** + * + * raylib - classic game: snake + * + * Sample game developed by Ian Eito, Albert Martos and Ramon Santamaria, + * converted to C3 and modified by Christoffer Lerno + * + * Copyright (c) 2015 Ramon Santamaria (@raysan5) + * + */ + +const SNAKE_LENGTH = 256; +const SQUARE_SIZE = 32; +const int SCREEN_WIDTH = 800; +const int SCREEN_HEIGHT = 450; + +enum SnakeDirection +{ + RIGHT, + DOWN, + LEFT, + UP +} +struct Snake +{ + Vector2 position; + Vector2 size; + Color color; +} + +struct Food +{ + Vector2 position; + Vector2 size; + bool active; + Color color; +} + + +int frames_counter = 0; +bool game_over = false; +bool pause = false; + +Food fruit; +SnakeDirection snake_direction; +Snake[SNAKE_LENGTH] snake; +Vector2[SNAKE_LENGTH] snake_position; +bool allow_move = false; +Vector2 offset; +int counter_tail = 0; + +fn void main() +{ + raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: snake"); + init_game(); + raylib::set_target_fps(60); + + while (!raylib::window_should_close()) // Detect window close button or ESC key + { + update_draw_frame(); + } + + unload_game(); + + raylib::close_window(); +} + +// Initialize game variables +fn void init_game() +{ + frames_counter = 0; + game_over = false; + pause = false; + + counter_tail = 1; + allow_move = false; + snake_direction = SnakeDirection.RIGHT; + offset.x = SCREEN_WIDTH % SQUARE_SIZE; + offset.y = SCREEN_HEIGHT % SQUARE_SIZE; + + for (int i = 0; i < SNAKE_LENGTH; i++) + { + snake[i].position = { offset.x / 2, offset.y / 2 }; + snake[i].size = { SQUARE_SIZE, SQUARE_SIZE }; + + if (i == 0) + { + snake[i].color = raylib::DARKBLUE; + } + else + { + snake[i].color = raylib::BLUE; + } + } + + for (int i = 0; i < SNAKE_LENGTH; i++) + { + snake_position[i] = { 0.0f, 0.0f }; + } + + fruit.size = { SQUARE_SIZE, SQUARE_SIZE }; + fruit.color = raylib::SKYBLUE; + fruit.active = false; +} + + +fn void update_game() +{ + if (game_over) + { + if (raylib::is_key_pressed(keyboard::ENTER)) + { + init_game(); + game_over = false; + } + return; + } + + if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause; + + if (pause) return; + + if (raylib::is_key_pressed(keyboard::RIGHT) && allow_move) + { + snake_direction = (SnakeDirection)((snake_direction + 1) % 4); + allow_move = false; + } + if (raylib::is_key_pressed(keyboard::LEFT) && allow_move) + { + snake_direction = (SnakeDirection)((snake_direction + 3) % 4); + allow_move = false; + } + + // Snake movement + for (int i = 0; i < counter_tail; i++) snake_position[i] = snake[i].position; + + if (frames_counter++ % 5 != 0) return; + + allow_move = true; + switch (snake_direction) + { + case RIGHT: + snake[0].position.x += SQUARE_SIZE; + snake[0].position.y += 0; + case UP: + snake[0].position.x += 0; + snake[0].position.y += -SQUARE_SIZE; + case DOWN: + snake[0].position.x += 0; + snake[0].position.y += SQUARE_SIZE; + case LEFT: + snake[0].position.x += -SQUARE_SIZE; + snake[0].position.y += 0; + default: + @unreachable(); + } + for (int i = 1; i < counter_tail; i++) + { + snake[i].position = snake_position[i - 1]; + } + + // Wall behaviour + if (((snake[0].position.x) > (SCREEN_WIDTH - offset.x)) || + ((snake[0].position.y) > (SCREEN_HEIGHT - offset.y)) || + (snake[0].position.x < 0) || (snake[0].position.y < 0)) + { + game_over = true; + } + + // Collision with yourself + for (int i = 1; i < counter_tail; i++) + { + if ((snake[0].position.x == snake[i].position.x) && (snake[0].position.y == snake[i].position.y)) game_over = true; + } + + // Fruit position calculation + if (!fruit.active) + { + fruit.active = true; + fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 }; + + for (int i = 0; i < counter_tail; i++) + { + while ((fruit.position.x == snake[i].position.x) && (fruit.position.y == snake[i].position.y)) + { + fruit.position = { raylib::get_random_value(0, (SCREEN_WIDTH / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.x/2, raylib::get_random_value(0, (SCREEN_HEIGHT / SQUARE_SIZE) - 1) * SQUARE_SIZE + offset.y / 2 }; + i = 0; + } + } + } + + // Collision + if ((snake[0].position.x < (fruit.position.x + fruit.size.x) && (snake[0].position.x + snake[0].size.x) > fruit.position.x) && + (snake[0].position.y < (fruit.position.y + fruit.size.y) && (snake[0].position.y + snake[0].size.y) > fruit.position.y)) + { + snake[counter_tail].position = snake_position[counter_tail - 1]; + counter_tail += 1; + fruit.active = false; + } +} + +// Draw game (one frame) +fn void draw_game() +{ + raylib::begin_drawing(); + + raylib::clear_background(raylib::RAYWHITE); + + if (!game_over) + { + // Draw grid lines + for (int i = 0; i < SCREEN_WIDTH / SQUARE_SIZE + 1; i++) + { + raylib::draw_line_v({SQUARE_SIZE * i + offset.x/2, offset.y/2}, {SQUARE_SIZE * i + offset.x/2, SCREEN_HEIGHT - offset.y/2}, raylib::LIGHTGRAY); + } + + for (int i = 0; i < SCREEN_HEIGHT/SQUARE_SIZE + 1; i++) + { + raylib::draw_line_v({offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, { SCREEN_WIDTH - offset.x/2, SQUARE_SIZE * i + offset.y / 2 }, raylib::LIGHTGRAY); + } + + // Draw snake + for (int i = 0; i < counter_tail; i++) raylib::draw_rectangle_v(snake[i].position, snake[i].size, snake[i].color); + + // Draw fruit to pick + raylib::draw_rectangle_v(fruit.position, fruit.size, fruit.color); + + if (pause) raylib::draw_text("GAME PAUSED", SCREEN_WIDTH/2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT / 2 - 40, 40, raylib::GRAY); + } + else + { + raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width()/2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20)/2, raylib::get_screen_height()/2 - 50, 20, raylib::GRAY); + } + + raylib::end_drawing(); +} + +// Unload game variables +fn void unload_game() +{ + // TODO: Unload all dynamic loaded data (textures, sounds, models...) +} + +// Update and Draw (one frame) +fn void update_draw_frame() +{ + update_game(); + draw_game(); +} + diff --git a/resources/examples/raylib/raylib_tetris.c3 b/resources/examples/raylib/raylib_tetris.c3 new file mode 100644 index 000000000..4ebe3db00 --- /dev/null +++ b/resources/examples/raylib/raylib_tetris.c3 @@ -0,0 +1,793 @@ +module tetris; +/** + * raylib - classic game: tetris + * + * Sample game developed by Marc Palau and Ramon Santamaria, + * converted to C3 by Christoffer Lerno. + * + * This game has been created using raylib v1.3 (www.raylib.com) + * + * Copyright (c) 2015 Ramon Santamaria (@raysan5) + */ + +//---------------------------------------------------------------------------------- +// Some Defines +//---------------------------------------------------------------------------------- +const SQUARE_SIZE = 20; +const GRID_HORIZONTAL_SIZE = 12; +const GRID_VERTICAL_SIZE = 20; + +const LATERAL_SPEED = 10; +const TURNING_SPEED = 12; +const FAST_FALL_AWAIT_COUNTER = 30; +const FADING_TIME = 33; + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +enum GridSquare { EMPTY, MOVING, FULL, BLOCK, FADING } + +//------------------------------------------------------------------------------------ +// Global Variables Declaration +//------------------------------------------------------------------------------------ +const int SCREEN_WIDTH = 800; +const int SCREEN_HEIGHT = 450; + +bool game_over = false; +bool pause = false; + +// Matrices +GridSquare[GRID_VERTICAL_SIZE][GRID_HORIZONTAL_SIZE] grid; +GridSquare[4][4] piece; +GridSquare[4][4] incoming_piece; + +struct IntVec +{ + int x; + int y; +} +// These variables keep track of the active piece position +int piece_position_x = 0; +int piece_position_y = 0; + +// Game parameters +Color fading_color; +//int fallingSpeed; // In frames + +bool begin_play = true; // This var is only true at the begining of the game, used for the first matrix creations +bool piece_active = false; +bool detection = false; +bool line_to_delete = false; + +// Statistics +int level = 1; +int lines = 0; + +// Counters +int gravity_movement_counter = 0; +int lateral_movement_counter = 0; +int turn_movement_counter = 0; +int fast_fall_movement_counter = 0; + +int fade_line_counter = 0; + +// Based on level +int gravity_speed = 30; + + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ +fn void main() +{ + // Initialization (Note windowTitle is unused on Android) + //--------------------------------------------------------- + raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: tetris"); + + init_game(); + + raylib::set_target_fps(60); + //-------------------------------------------------------------------------------------- + + // Main game loop + while (!raylib::window_should_close()) // Detect window close button or ESC key + { + // Update and Draw + //---------------------------------------------------------------------------------- + update_draw_frame(); + //---------------------------------------------------------------------------------- + } + // De-Initialization + //-------------------------------------------------------------------------------------- + unload_game(); // Unload loaded data (textures, sounds, models...) + + raylib::close_window(); // Close window and OpenGL context + //-------------------------------------------------------------------------------------- + +} + +//-------------------------------------------------------------------------------------- +// Game Module Functions Definition +//-------------------------------------------------------------------------------------- + +// Initialize game variables +fn void init_game() +{ + // Initialize game statistics + level = 1; + lines = 0; + + fading_color = raylib::GRAY; + + piece_position_x = 0; + piece_position_y = 0; + + pause = false; + + begin_play = true; + piece_active = false; + detection = false; + line_to_delete = false; + + // Counters + gravity_movement_counter = 0; + lateral_movement_counter = 0; + turn_movement_counter = 0; + fast_fall_movement_counter = 0; + + fade_line_counter = 0; + gravity_speed = 30; + + // Initialize grid matrices + for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++) + { + for (int j = 0; j < GRID_VERTICAL_SIZE; j++) + { + if ((j == GRID_VERTICAL_SIZE - 1) || (i == 0) || (i == GRID_HORIZONTAL_SIZE - 1)) + { + grid[i][j] = BLOCK; + } + else + { + grid[i][j] = EMPTY; + } + } + } + + // Initialize incoming piece matrices + for (int i = 0; i < 4; i++) + { + for (int j = 0; j< 4; j++) + { + incoming_piece[i][j] = EMPTY; + } + } +} + +// Update game (one frame) +fn void update_game() +{ + if (game_over) + { + if (raylib::is_key_pressed(keyboard::ENTER)) + { + init_game(); + game_over = false; + } + return; + } + if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause; + + if (pause) return; + if (line_to_delete) + { + // Animation when deleting lines + fade_line_counter++; + + fading_color = fade_line_counter % 8 < 4 ? raylib::MAROON : raylib::GRAY; + + if (fade_line_counter >= FADING_TIME) + { + lines += delete_complete_lines(); + fade_line_counter = 0; + line_to_delete = false; + } + return; + } + + if (!piece_active) + { + // Get another piece + piece_active = create_piece(); + + // We leave a little time before starting the fast falling down + fast_fall_movement_counter = 0; + } + else // Piece falling + { + // Counters update + fast_fall_movement_counter++; + gravity_movement_counter++; + lateral_movement_counter++; + turn_movement_counter++; + + // We make sure to move if we've pressed the key this frame + if (raylib::is_key_pressed(keyboard::LEFT) || raylib::is_key_pressed(keyboard::RIGHT)) lateral_movement_counter = LATERAL_SPEED; + if (raylib::is_key_pressed(keyboard::UP)) turn_movement_counter = TURNING_SPEED; + + // Fall down + if (raylib::is_key_down(keyboard::DOWN) && (fast_fall_movement_counter >= FAST_FALL_AWAIT_COUNTER)) + { + // We make sure the piece is going to fall this frame + gravity_movement_counter += gravity_speed; + } + + if (gravity_movement_counter >= gravity_speed) + { + // Basic falling movement + if (check_detection()) detection = true; + + // Check if the piece has collided with another piece or with the boundings + resolve_falling_movement(&detection, &piece_active); + + // Check if we fullfilled a line and if so, erase the line and pull down the the lines above + check_completion(&line_to_delete); + + gravity_movement_counter = 0; + } + + // Move laterally at player's will + if (lateral_movement_counter >= LATERAL_SPEED) + { + // Update the lateral movement and if success, reset the lateral counter + if (!resolve_lateral_movement()) lateral_movement_counter = 0; + } + + // Turn the piece at player's will + if (turn_movement_counter >= TURNING_SPEED) + { + // Update the turning movement and reset the turning counter + if (resolve_turn_movement()) turn_movement_counter = 0; + } + } + + // Game over logic + for (int j = 0; j < 2; j++) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.FULL) + { + game_over = true; + } + } + } +} + +// Draw game (one frame) +fn void draw_game() +{ + raylib::begin_drawing(); + + raylib::clear_background(raylib::RAYWHITE); + + if (game_over) + { + raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width() / 2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20) / 2, raylib::get_screen_height() / 2 - 50, 20, raylib::GRAY); + raylib::end_drawing(); + return; + } + + // Draw gameplay area + IntVec offset = { + SCREEN_WIDTH / 2 - (GRID_HORIZONTAL_SIZE * SQUARE_SIZE / 2) - 50, + SCREEN_HEIGHT / 2 - ((GRID_VERTICAL_SIZE - 1) * SQUARE_SIZE / 2) + SQUARE_SIZE * 2 + }; + offset.y -= 50; // NOTE: Harcoded position! + + int controller = offset.x; + + for (int j = 0; j < GRID_VERTICAL_SIZE; j++) + { + for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++) + { + // Draw each square of the grid + switch (grid[i][j]) + { + case EMPTY: + raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY ); + raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY ); + raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY ); + raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY ); + offset.x += SQUARE_SIZE; + case FULL: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY); + offset.x += SQUARE_SIZE; + case MOVING: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::DARKGRAY); + offset.x += SQUARE_SIZE; + case BLOCK: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::LIGHTGRAY); + offset.x += SQUARE_SIZE; + case FADING: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, fading_color); + offset.x += SQUARE_SIZE; + default: + } + } + + offset.x = controller; + offset.y += SQUARE_SIZE; + } + + // Draw incoming piece (hardcoded) + offset.x = 500; + offset.y = 45; + + controller = offset.x; + + for (int j = 0; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + switch (incoming_piece[i][j]) + { + case EMPTY: + raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY); + raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY); + raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY); + raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY); + offset.x += SQUARE_SIZE; + case MOVING: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY); + offset.x += SQUARE_SIZE; + default: + break; + } + } + + offset.x = controller; + offset.y += SQUARE_SIZE; + } + + raylib::draw_text("INCOMING:", offset.x, offset.y - 100, 10, raylib::GRAY); + raylib::draw_text(raylib::text_format("LINES: %04i", lines), offset.x, offset.y + 20, 10, raylib::GRAY); + + if (pause) + { + raylib::draw_text("GAME PAUSED", SCREEN_WIDTH / 2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY); + } + raylib::end_drawing(); +} + +// Unload game variables +fn void unload_game() +{ + // TODO: Unload all dynamic loaded data (textures, sounds, models...) +} + +// Update and Draw (one frame) +fn void update_draw_frame() +{ + update_game(); + draw_game(); +} + +//-------------------------------------------------------------------------------------- +// Additional module functions +//-------------------------------------------------------------------------------------- +fn bool create_piece() +{ + piece_position_x = (int)((GRID_HORIZONTAL_SIZE - 4)/2); + piece_position_y = 0; + + // If the game is starting and you are going to create the first piece, we create an extra one + if (begin_play) + { + get_random_piece(); + begin_play = false; + } + + // We assign the incoming piece to the actual piece + for (int i = 0; i < 4; i++) + { + for (int j = 0; j< 4; j++) + { + piece[i][j] = incoming_piece[i][j]; + } + } + + // We assign a random piece to the incoming one + get_random_piece(); + + // Assign the piece to the grid + for (int i = piece_position_x; i < piece_position_x + 4; i++) + { + for (int j = 0; j < 4; j++) + { + if (piece[i - (int)piece_position_x][j] == GridSquare.MOVING) grid[i][j] = MOVING; + } + } + + return true; +} + +fn void get_random_piece() +{ + int random = raylib::get_random_value(0, 6); + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + incoming_piece[i][j] = EMPTY; + } + } + + switch (random) + { + case 0: + incoming_piece[1][1] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[1][2] = MOVING; + incoming_piece[2][2] = MOVING; //Cube + case 1: + incoming_piece[1][0] = MOVING; + incoming_piece[1][1] = MOVING; + incoming_piece[1][2] = MOVING; + incoming_piece[2][2] = MOVING; //L + case 2: + incoming_piece[1][2] = MOVING; + incoming_piece[2][0] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[2][2] = MOVING; //L inversa + case 3: + incoming_piece[0][1] = MOVING; + incoming_piece[1][1] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[3][1] = MOVING; //Recta + case 4: + incoming_piece[1][0] = MOVING; + incoming_piece[1][1] = MOVING; + incoming_piece[1][2] = MOVING; + incoming_piece[2][1] = MOVING; //Creu tallada + case 5: + incoming_piece[1][1] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[2][2] = MOVING; + incoming_piece[3][2] = MOVING; //S + case 6: + incoming_piece[1][2] = MOVING; + incoming_piece[2][2] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[3][1] = MOVING; //S inversa + default: + @unreachable(); + } +} + +fn void resolve_falling_movement(bool* detection_ref, bool* piece_active_ref) +{ + // If we finished moving this piece, we stop it + if (*detection_ref) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + grid[i][j] = FULL; + *detection_ref = false; + *piece_active_ref = false; + } + } + } + } + else // We move down the piece + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + grid[i][j+1] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piece_position_y++; + } +} + +fn bool resolve_lateral_movement() +{ + bool collision = false; + + // Piece movement + if (raylib::is_key_down(keyboard::LEFT)) // Move left + { + // Check if is possible to move to left + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + // Check if we are touching the left wall or we have a full square at the left + if ((i-1 == 0) || (grid[i-1][j] == GridSquare.FULL)) collision = true; + } + } + } + + // If able, move left + if (!collision) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) // We check the matrix from left to right + { + // Move everything to the left + if (grid[i][j] == GridSquare.MOVING) + { + grid[i-1][j] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piece_position_x--; + } + } + else if (raylib::is_key_down(keyboard::RIGHT)) // Move right + { + // Check if is possible to move to right + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + // Check if we are touching the right wall or we have a full square at the right + if ((i+1 == GRID_HORIZONTAL_SIZE - 1) || (grid[i+1][j] == GridSquare.FULL)) + { + collision = true; + + } + } + } + } + + // If able move right + if (!collision) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = GRID_HORIZONTAL_SIZE - 1; i >= 1; i--) // We check the matrix from right to left + { + // Move everything to the right + if (grid[i][j] == GridSquare.MOVING) + { + grid[i+1][j] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piece_position_x++; + } + } + + return collision; +} + +fn bool resolve_turn_movement() +{ + // Input for turning the piece + if (raylib::is_key_down(keyboard::UP)) + { + GridSquare aux; + bool checker = false; + + // Check all turning possibilities + if ((grid[piece_position_x + 3][piece_position_y] == GridSquare.MOVING) && + (grid[piece_position_x][piece_position_y] != GridSquare.EMPTY) && + (grid[piece_position_x][piece_position_y] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 3][piece_position_y + 3] == GridSquare.MOVING) && + (grid[piece_position_x + 3][piece_position_y] != GridSquare.EMPTY) && + (grid[piece_position_x + 3][piece_position_y] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x][piece_position_y + 3] == GridSquare.MOVING) && + (grid[piece_position_x + 3][piece_position_y + 3] != GridSquare.EMPTY) && + (grid[piece_position_x + 3][piece_position_y + 3] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x][piece_position_y] == GridSquare.MOVING) && + (grid[piece_position_x][piece_position_y + 3] != GridSquare.EMPTY) && + (grid[piece_position_x][piece_position_y + 3] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 1][piece_position_y] == GridSquare.MOVING) && + (grid[piece_position_x][piece_position_y + 2] != GridSquare.EMPTY) && + (grid[piece_position_x][piece_position_y + 2] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 3][piece_position_y + 1] == GridSquare.MOVING) && + (grid[piece_position_x + 1][piece_position_y] != GridSquare.EMPTY) && + (grid[piece_position_x + 1][piece_position_y] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 2][piece_position_y + 3] == GridSquare.MOVING) && + (grid[piece_position_x + 3][piece_position_y + 1] != GridSquare.EMPTY) && + (grid[piece_position_x + 3][piece_position_y + 1] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x][piece_position_y + 2] == GridSquare.MOVING) && + (grid[piece_position_x + 2][piece_position_y + 3] != GridSquare.EMPTY) && + (grid[piece_position_x + 2][piece_position_y + 3] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 2][piece_position_y] == GridSquare.MOVING) && + (grid[piece_position_x][piece_position_y + 1] != GridSquare.EMPTY) && + (grid[piece_position_x][piece_position_y + 1] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 3][piece_position_y + 2] == GridSquare.MOVING) && + (grid[piece_position_x + 2][piece_position_y] != GridSquare.EMPTY) && + (grid[piece_position_x + 2][piece_position_y] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 1][piece_position_y + 3] == GridSquare.MOVING) && + (grid[piece_position_x + 3][piece_position_y + 2] != GridSquare.EMPTY) && + (grid[piece_position_x + 3][piece_position_y + 2] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x][piece_position_y + 1] == GridSquare.MOVING) && + (grid[piece_position_x + 1][piece_position_y + 3] != GridSquare.EMPTY) && + (grid[piece_position_x + 1][piece_position_y + 3] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 1][piece_position_y + 1] == GridSquare.MOVING) && + (grid[piece_position_x + 1][piece_position_y + 2] != GridSquare.EMPTY) && + (grid[piece_position_x + 1][piece_position_y + 2] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 2][piece_position_y + 1] == GridSquare.MOVING) && + (grid[piece_position_x + 1][piece_position_y + 1] != GridSquare.EMPTY) && + (grid[piece_position_x + 1][piece_position_y + 1] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 2][piece_position_y + 2] == GridSquare.MOVING) && + (grid[piece_position_x + 2][piece_position_y + 1] != GridSquare.EMPTY) && + (grid[piece_position_x + 2][piece_position_y + 1] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 1][piece_position_y + 2] == GridSquare.MOVING) && + (grid[piece_position_x + 2][piece_position_y + 2] != GridSquare.EMPTY) && + (grid[piece_position_x + 2][piece_position_y + 2] != GridSquare.MOVING)) checker = true; + + if (!checker) + { + aux = piece[0][0]; + piece[0][0] = piece[3][0]; + piece[3][0] = piece[3][3]; + piece[3][3] = piece[0][3]; + piece[0][3] = aux; + + aux = piece[1][0]; + piece[1][0] = piece[3][1]; + piece[3][1] = piece[2][3]; + piece[2][3] = piece[0][2]; + piece[0][2] = aux; + + aux = piece[2][0]; + piece[2][0] = piece[3][2]; + piece[3][2] = piece[1][3]; + piece[1][3] = piece[0][1]; + piece[0][1] = aux; + + aux = piece[1][1]; + piece[1][1] = piece[2][1]; + piece[2][1] = piece[2][2]; + piece[2][2] = piece[1][2]; + piece[1][2] = aux; + } + + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + grid[i][j] = EMPTY; + } + } + } + + for (int i = piece_position_x; i < piece_position_x + 4; i++) + { + for (int j = piece_position_y; j < piece_position_y + 4; j++) + { + if (piece[i - piece_position_x][j - piece_position_y] == GridSquare.MOVING) + { + grid[i][j] = MOVING; + } + } + } + + return true; + } + + return false; +} + +fn bool check_detection() +{ + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if ((grid[i][j] == GridSquare.MOVING) && ((grid[i][j+1] == GridSquare.FULL) + || (grid[i][j+1] == GridSquare.BLOCK))) return true; + } + } + return false; +} + +fn void check_completion(bool *line_to_delete_ref) +{ + int calculator = 0; + + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + calculator = 0; + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + // Count each square of the line + if (grid[i][j] == GridSquare.FULL) + { + calculator++; + } + + // Check if we completed the whole line + if (calculator == GRID_HORIZONTAL_SIZE - 2) + { + *line_to_delete_ref = true; + calculator = 0; + // points++; + + // Mark the completed line + for (int z = 1; z < GRID_HORIZONTAL_SIZE - 1; z++) + { + grid[z][j] = FADING; + } + } + } + } +} + +fn int delete_complete_lines() +{ + int lines_to_erase = 0; + // Erase the completed line + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + while (grid[1][j] == GridSquare.FADING) + { + lines_to_erase++; + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + grid[i][j] = GridSquare.EMPTY; + } + + for (int j2 = j-1; j2 >= 0; j2--) + { + for (int i2 = 1; i2 < GRID_HORIZONTAL_SIZE - 1; i2++) + { + switch (grid[i2][j2]) + { + case FULL: + grid[i2][j2+1] = GridSquare.FULL; + grid[i2][j2] = GridSquare.EMPTY; + case FADING: + grid[i2][j2+1] = GridSquare.FADING; + grid[i2][j2] = GridSquare.EMPTY; + default: + } + } + } + } + } + return lines_to_erase; +} + diff --git a/resources/testfragments/raylibtest.c3 b/resources/testfragments/raylibtest.c3 index d6b21dd53..ed3f91d1c 100644 --- a/resources/testfragments/raylibtest.c3 +++ b/resources/testfragments/raylibtest.c3 @@ -1,167 +1,791 @@ -module foo; -extern fn void printf(char*, ...); +module tetris; +/** + * raylib - classic game: tetris + * + * Sample game developed by Marc Palau and Ramon Santamaria, + * converted to C3 by Christoffer Lerno. + * + * This game has been created using raylib v1.3 (www.raylib.com) + * + * Copyright (c) 2015 Ramon Santamaria (@raysan5) + */ -enum Foo : int (int offset, char* extra_name, double x) +//---------------------------------------------------------------------------------- +// Some Defines +//---------------------------------------------------------------------------------- +const SQUARE_SIZE = 20; +const GRID_HORIZONTAL_SIZE = 12; +const GRID_VERTICAL_SIZE = 20; + +const LATERAL_SPEED = 10; +const TURNING_SPEED = 12; +const FAST_FALL_AWAIT_COUNTER = 30; +const FADING_TIME = 33; + +//---------------------------------------------------------------------------------- +// Types and Structures Definition +//---------------------------------------------------------------------------------- +enum GridSquare { EMPTY, MOVING, FULL, BLOCK, FADING } + +//------------------------------------------------------------------------------------ +// Global Variables Declaration +//------------------------------------------------------------------------------------ +const int SCREEN_WIDTH = 800; +const int SCREEN_HEIGHT = 450; + +bool game_over = false; +bool pause = false; + +// Matrices +GridSquare[GRID_VERTICAL_SIZE][GRID_HORIZONTAL_SIZE] grid; +GridSquare[4][4] piece; +GridSquare[4][4] incoming_piece; + +struct IntVec { - BAZ(12, "hello", 3.0), - BOO(33, "oekfe", 4.0) = 3, + int x; + int y; } +// These variables keep track of the active piece position +int piece_position_x = 0; +int piece_position_y = 0; +// Game parameters +Color fading_color; +//int fallingSpeed; // In frames + +bool begin_play = true; // This var is only true at the begining of the game, used for the first matrix creations +bool piece_active = false; +bool detection = false; +bool line_to_delete = false; + +// Statistics +int level = 1; +int lines = 0; + +// Counters +int gravity_movement_counter = 0; +int lateral_movement_counter = 0; +int turn_movement_counter = 0; +int fast_fall_movement_counter = 0; + +int fade_line_counter = 0; + +// Based on level +int gravity_speed = 30; + + +//------------------------------------------------------------------------------------ +// Program main entry point +//------------------------------------------------------------------------------------ fn void main() { - int screenWidth = 800; - int screenHeight = 450; + // Initialization (Note windowTitle is unused on Android) + //--------------------------------------------------------- + raylib::init_window(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: tetris"); - raylib::init_window(screenWidth, screenHeight, "raylib [core] example - keyboard input"); + init_game(); - Vector2 ballPosition = { (float)screenWidth/2, (float)screenHeight/2 }; - - raylib::set_target_fps(60); // Set our game to run at 60 frames-per-second + raylib::set_target_fps(60); //-------------------------------------------------------------------------------------- - foo2::tester(screenHeight); + // Main game loop while (!raylib::window_should_close()) // Detect window close button or ESC key { - // Update + // Update and Draw //---------------------------------------------------------------------------------- - if (raylib::is_key_down(KeyboardKey.RIGHT)) ballPosition.x += 2.0f; - if (raylib::is_key_down(KeyboardKey.LEFT)) ballPosition.x -= 2.0f; - if (raylib::is_key_down(KeyboardKey.UP)) ballPosition.y -= 2.0f; - if (raylib::is_key_down(KeyboardKey.DOWN)) ballPosition.y += 2.0f; - //---------------------------------------------------------------------------------- - - // Draw - //---------------------------------------------------------------------------------- - raylib::begin_drawing(); - - raylib::clear_background(raylib::RAYWHITE); - - raylib::draw_text("move the ball with arrow keys", 10, 10, 20, raylib::DARKGRAY); - - raylib::draw_circle_v(ballPosition, 50, raylib::MAROON); - - raylib::end_drawing(); + update_draw_frame(); //---------------------------------------------------------------------------------- } - // De-Initialization //-------------------------------------------------------------------------------------- + unload_game(); // Unload loaded data (textures, sounds, models...) + raylib::close_window(); // Close window and OpenGL context //-------------------------------------------------------------------------------------- + } -/* -struct Game + +//-------------------------------------------------------------------------------------- +// Game Module Functions Definition +//-------------------------------------------------------------------------------------- + +// Initialize game variables +fn void init_game() { - int answer; - bool done; - int guesses; - int high; + // Initialize game statistics + level = 1; + lines = 0; + + fading_color = raylib::GRAY; + + piece_position_x = 0; + piece_position_y = 0; + + pause = false; + + begin_play = true; + piece_active = false; + detection = false; + line_to_delete = false; + + // Counters + gravity_movement_counter = 0; + lateral_movement_counter = 0; + turn_movement_counter = 0; + fast_fall_movement_counter = 0; + + fade_line_counter = 0; + gravity_speed = 30; + + // Initialize grid matrices + for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++) + { + for (int j = 0; j < GRID_VERTICAL_SIZE; j++) + { + if ((j == GRID_VERTICAL_SIZE - 1) || (i == 0) || (i == GRID_HORIZONTAL_SIZE - 1)) + { + grid[i][j] = BLOCK; + } + else + { + grid[i][j] = EMPTY; + } + } + } + + // Initialize incoming piece matrices + for (int i = 0; i < 4; i++) + { + for (int j = 0; j< 4; j++) + { + incoming_piece[i][j] = EMPTY; + } + } } -int err_count = 0; - - -import "core:bufio" -import "core:fmt" -import "core:io" -import "core:mem" -import "core:os" -import "core:strconv" -import "core:time" -import "core:math/rand" - -Game :: struct { - answer: int, - done: bool, - guesses: int, - high: int, -} - -int err_count = 0; -*/ -/* -fn int ask_guess(int high) +// Update game (one frame) +fn void update_game() { - libc::printf("Guess a number between 1 and %d: ", high); - -} -ask_guess :: proc(high: int) -> (result: int, ok: bool) { - fmt.printf("Guess a number between 1 and %d: ", high) - if text, ok := read_line(); ok { - defer mem.delete(text) - return strconv.parse_int(s = text, base = 10) + if (game_over) + { + if (raylib::is_key_pressed(keyboard::ENTER)) + { + init_game(); + game_over = false; + } + return; } - return -} + if (raylib::is_key_pressed((KeyboardKey)'P')) pause = !pause; -ask_guess_multi :: proc(high: int) -> int { - for { - if result, ok := ask_guess(high); ok { - return result - } - fmt.println("I didn't understand") - err_count += 1 + if (pause) return; + if (line_to_delete) + { + // Animation when deleting lines + fade_line_counter++; + + fading_color = fade_line_counter % 8 < 4 ? raylib::MAROON : raylib::GRAY; + + if (fade_line_counter >= FADING_TIME) + { + lines += delete_complete_lines(); + fade_line_counter = 0; + line_to_delete = false; + } + return; } + + if (!piece_active) + { + // Get another piece + piece_active = create_piece(); + + // We leave a little time before starting the fast falling down + fast_fall_movement_counter = 0; + } + else // Piece falling + { + // Counters update + fast_fall_movement_counter++; + gravity_movement_counter++; + lateral_movement_counter++; + turn_movement_counter++; + + // We make sure to move if we've pressed the key this frame + if (raylib::is_key_pressed(keyboard::LEFT) || raylib::is_key_pressed(keyboard::RIGHT)) lateral_movement_counter = LATERAL_SPEED; + if (raylib::is_key_pressed(keyboard::UP)) turn_movement_counter = TURNING_SPEED; + + // Fall down + if (raylib::is_key_down(keyboard::DOWN) && (fast_fall_movement_counter >= FAST_FALL_AWAIT_COUNTER)) + { + // We make sure the piece is going to fall this frame + gravity_movement_counter += gravity_speed; + } + + if (gravity_movement_counter >= gravity_speed) + { + // Basic falling movement + check_detection(&detection); + + // Check if the piece has collided with another piece or with the boundings + resolve_falling_movement(&detection, &piece_active); + + // Check if we fullfilled a line and if so, erase the line and pull down the the lines above + check_completion(&line_to_delete); + + gravity_movement_counter = 0; + } + + // Move laterally at player's will + if (lateral_movement_counter >= LATERAL_SPEED) + { + // Update the lateral movement and if success, reset the lateral counter + if (!resolve_lateral_movement()) lateral_movement_counter = 0; + } + + // Turn the piece at player's will + if (turn_movement_counter >= TURNING_SPEED) + { + // Update the turning movement and reset the turning counter + if (resolve_turn_movement()) turn_movement_counter = 0; + } + } + + // Game over logic + for (int j = 0; j < 2; j++) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.FULL) + { + game_over = true; + } + } + } } -pick_answer :: proc(high: int, r: ^rand.Rand) -> int { - return rand.int_max(high, r) + 1 -} - -play :: proc(game: ^Game) { - for !game.done { - guess := ask_guess_multi(game.high) - report(game^, guess) - game^ = update(game^, guess) - } -} - -read_line :: proc() -> (result: string, ok: bool) { - // See also: - // - https://github.com/odin-lang/Odin/issues/1214 - // - https://p.teknik.io/Raw/IT996 - s := os.stream_from_handle(os.stdin) - r: bufio.Reader - bufio.reader_init(&r, io.Reader{s}) - defer bufio.reader_destroy(&r) - if line, err := bufio.reader_read_string(&r, '\n'); err == .None { - return line[:len(line) - 1], true - } - return -} - -report :: proc(game: Game, guess: int) { - // game.done = true - // fmt.println(&game) - description := ( - "too low" if guess < game.answer else - "too high" if guess > game.answer else - "the answer!" - ) - fmt.println(guess, "is", description) -} - -update :: proc(game: Game, guess: int) -> (next: Game) { - next = game - next.done = guess == game.answer - next.guesses += 1 - return -} - -main :: proc() { - high :: 100 - r := rand.create(transmute(u64)time.now()) - // Or use nil for default random. - answer := pick_answer(high, &r) - game := Game {answer = answer, done = false, guesses = 0, high = high} - play(&game) - fmt.println("Finished in", game.guesses, "guesses"); - fmt.println("Total input errors:", err_count) -} -*/ - -module foo2; -fn void tester(int x) +// Draw game (one frame) +fn void draw_game() { - assert(x > 1000, "Shit!"); + raylib::begin_drawing(); + + raylib::clear_background(raylib::RAYWHITE); + + if (game_over) + { + raylib::draw_text("PRESS [ENTER] TO PLAY AGAIN", raylib::get_screen_width() / 2 - raylib::measure_text("PRESS [ENTER] TO PLAY AGAIN", 20) / 2, raylib::get_screen_height() / 2 - 50, 20, raylib::GRAY); + raylib::end_drawing(); + return; + } + + // Draw gameplay area + IntVec offset = { + SCREEN_WIDTH / 2 - (GRID_HORIZONTAL_SIZE * SQUARE_SIZE / 2) - 50, + SCREEN_HEIGHT / 2 - ((GRID_VERTICAL_SIZE - 1) * SQUARE_SIZE / 2) + SQUARE_SIZE * 2 + }; + offset.y -= 50; // NOTE: Harcoded position! + + int controller = offset.x; + + for (int j = 0; j < GRID_VERTICAL_SIZE; j++) + { + for (int i = 0; i < GRID_HORIZONTAL_SIZE; i++) + { + // Draw each square of the grid + switch (grid[i][j]) + { + case EMPTY: + raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY ); + raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY ); + raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY ); + raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY ); + offset.x += SQUARE_SIZE; + case FULL: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY); + offset.x += SQUARE_SIZE; + case MOVING: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::DARKGRAY); + offset.x += SQUARE_SIZE; + case BLOCK: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::LIGHTGRAY); + offset.x += SQUARE_SIZE; + case FADING: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, fading_color); + offset.x += SQUARE_SIZE; + default: + } + } + + offset.x = controller; + offset.y += SQUARE_SIZE; + } + + // Draw incoming piece (hardcoded) + offset.x = 500; + offset.y = 45; + + controller = offset.x; + + for (int j = 0; j < 4; j++) + { + for (int i = 0; i < 4; i++) + { + switch (incoming_piece[i][j]) + { + case EMPTY: + raylib::draw_line(offset.x, offset.y, offset.x + SQUARE_SIZE, offset.y, raylib::LIGHTGRAY); + raylib::draw_line(offset.x, offset.y, offset.x, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY); + raylib::draw_line(offset.x + SQUARE_SIZE, offset.y, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY); + raylib::draw_line(offset.x, offset.y + SQUARE_SIZE, offset.x + SQUARE_SIZE, offset.y + SQUARE_SIZE, raylib::LIGHTGRAY); + offset.x += SQUARE_SIZE; + case MOVING: + raylib::draw_rectangle(offset.x, offset.y, SQUARE_SIZE, SQUARE_SIZE, raylib::GRAY); + offset.x += SQUARE_SIZE; + default: + break; + } + } + + offset.x = controller; + offset.y += SQUARE_SIZE; + } + + raylib::draw_text("INCOMING:", offset.x, offset.y - 100, 10, raylib::GRAY); + raylib::draw_text(raylib::text_format("LINES: %04i", lines), offset.x, offset.y + 20, 10, raylib::GRAY); + + if (pause) + { + raylib::draw_text("GAME PAUSED", SCREEN_WIDTH / 2 - raylib::measure_text("GAME PAUSED", 40)/2, SCREEN_HEIGHT/2 - 40, 40, raylib::GRAY); + } + raylib::end_drawing(); } + +// Unload game variables +fn void unload_game() +{ + // TODO: Unload all dynamic loaded data (textures, sounds, models...) +} + +// Update and Draw (one frame) +fn void update_draw_frame() +{ + update_game(); + draw_game(); +} + +//-------------------------------------------------------------------------------------- +// Additional module functions +//-------------------------------------------------------------------------------------- +fn bool create_piece() +{ + piece_position_x = (int)((GRID_HORIZONTAL_SIZE - 4)/2); + piece_position_y = 0; + + // If the game is starting and you are going to create the first piece, we create an extra one + if (begin_play) + { + get_random_piece(); + begin_play = false; + } + + // We assign the incoming piece to the actual piece + for (int i = 0; i < 4; i++) + { + for (int j = 0; j< 4; j++) + { + piece[i][j] = incoming_piece[i][j]; + } + } + + // We assign a random piece to the incoming one + get_random_piece(); + + // Assign the piece to the grid + for (int i = piece_position_x; i < piece_position_x + 4; i++) + { + for (int j = 0; j < 4; j++) + { + if (piece[i - (int)piece_position_x][j] == GridSquare.MOVING) grid[i][j] = MOVING; + } + } + + return true; +} + +fn void get_random_piece() +{ + int random = raylib::get_random_value(0, 6); + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + incoming_piece[i][j] = EMPTY; + } + } + + switch (random) + { + case 0: + incoming_piece[1][1] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[1][2] = MOVING; + incoming_piece[2][2] = MOVING; //Cube + case 1: + incoming_piece[1][0] = MOVING; + incoming_piece[1][1] = MOVING; + incoming_piece[1][2] = MOVING; + incoming_piece[2][2] = MOVING; //L + case 2: + incoming_piece[1][2] = MOVING; + incoming_piece[2][0] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[2][2] = MOVING; //L inversa + case 3: + incoming_piece[0][1] = MOVING; + incoming_piece[1][1] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[3][1] = MOVING; //Recta + case 4: + incoming_piece[1][0] = MOVING; + incoming_piece[1][1] = MOVING; + incoming_piece[1][2] = MOVING; + incoming_piece[2][1] = MOVING; //Creu tallada + case 5: + incoming_piece[1][1] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[2][2] = MOVING; + incoming_piece[3][2] = MOVING; //S + case 6: + incoming_piece[1][2] = MOVING; + incoming_piece[2][2] = MOVING; + incoming_piece[2][1] = MOVING; + incoming_piece[3][1] = MOVING; //S inversa + default: + @unreachable(); + } +} + +fn void resolve_falling_movement(bool* detection_ref, bool* piece_active_ref) +{ + // If we finished moving this piece, we stop it + if (*detection_ref) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + grid[i][j] = FULL; + *detection_ref = false; + *piece_active_ref = false; + } + } + } + } + else // We move down the piece + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + grid[i][j+1] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piece_position_y++; + } +} + +fn bool resolve_lateral_movement() +{ + bool collision = false; + + // Piece movement + if (raylib::is_key_down(keyboard::LEFT)) // Move left + { + // Check if is possible to move to left + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + // Check if we are touching the left wall or we have a full square at the left + if ((i-1 == 0) || (grid[i-1][j] == GridSquare.FULL)) collision = true; + } + } + } + + // If able, move left + if (!collision) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) // We check the matrix from left to right + { + // Move everything to the left + if (grid[i][j] == GridSquare.MOVING) + { + grid[i-1][j] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piece_position_x--; + } + } + else if (raylib::is_key_down(keyboard::RIGHT)) // Move right + { + // Check if is possible to move to right + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + // Check if we are touching the right wall or we have a full square at the right + if ((i+1 == GRID_HORIZONTAL_SIZE - 1) || (grid[i+1][j] == GridSquare.FULL)) + { + collision = true; + + } + } + } + } + + // If able move right + if (!collision) + { + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = GRID_HORIZONTAL_SIZE - 1; i >= 1; i--) // We check the matrix from right to left + { + // Move everything to the right + if (grid[i][j] == GridSquare.MOVING) + { + grid[i+1][j] = MOVING; + grid[i][j] = EMPTY; + } + } + } + + piece_position_x++; + } + } + + return collision; +} + +fn bool resolve_turn_movement() +{ + // Input for turning the piece + if (raylib::is_key_down(keyboard::UP)) + { + GridSquare aux; + bool checker = false; + + // Check all turning possibilities + if ((grid[piece_position_x + 3][piece_position_y] == GridSquare.MOVING) && + (grid[piece_position_x][piece_position_y] != GridSquare.EMPTY) && + (grid[piece_position_x][piece_position_y] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 3][piece_position_y + 3] == GridSquare.MOVING) && + (grid[piece_position_x + 3][piece_position_y] != GridSquare.EMPTY) && + (grid[piece_position_x + 3][piece_position_y] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x][piece_position_y + 3] == GridSquare.MOVING) && + (grid[piece_position_x + 3][piece_position_y + 3] != GridSquare.EMPTY) && + (grid[piece_position_x + 3][piece_position_y + 3] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x][piece_position_y] == GridSquare.MOVING) && + (grid[piece_position_x][piece_position_y + 3] != GridSquare.EMPTY) && + (grid[piece_position_x][piece_position_y + 3] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 1][piece_position_y] == GridSquare.MOVING) && + (grid[piece_position_x][piece_position_y + 2] != GridSquare.EMPTY) && + (grid[piece_position_x][piece_position_y + 2] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 3][piece_position_y + 1] == GridSquare.MOVING) && + (grid[piece_position_x + 1][piece_position_y] != GridSquare.EMPTY) && + (grid[piece_position_x + 1][piece_position_y] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 2][piece_position_y + 3] == GridSquare.MOVING) && + (grid[piece_position_x + 3][piece_position_y + 1] != GridSquare.EMPTY) && + (grid[piece_position_x + 3][piece_position_y + 1] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x][piece_position_y + 2] == GridSquare.MOVING) && + (grid[piece_position_x + 2][piece_position_y + 3] != GridSquare.EMPTY) && + (grid[piece_position_x + 2][piece_position_y + 3] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 2][piece_position_y] == GridSquare.MOVING) && + (grid[piece_position_x][piece_position_y + 1] != GridSquare.EMPTY) && + (grid[piece_position_x][piece_position_y + 1] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 3][piece_position_y + 2] == GridSquare.MOVING) && + (grid[piece_position_x + 2][piece_position_y] != GridSquare.EMPTY) && + (grid[piece_position_x + 2][piece_position_y] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 1][piece_position_y + 3] == GridSquare.MOVING) && + (grid[piece_position_x + 3][piece_position_y + 2] != GridSquare.EMPTY) && + (grid[piece_position_x + 3][piece_position_y + 2] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x][piece_position_y + 1] == GridSquare.MOVING) && + (grid[piece_position_x + 1][piece_position_y + 3] != GridSquare.EMPTY) && + (grid[piece_position_x + 1][piece_position_y + 3] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 1][piece_position_y + 1] == GridSquare.MOVING) && + (grid[piece_position_x + 1][piece_position_y + 2] != GridSquare.EMPTY) && + (grid[piece_position_x + 1][piece_position_y + 2] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 2][piece_position_y + 1] == GridSquare.MOVING) && + (grid[piece_position_x + 1][piece_position_y + 1] != GridSquare.EMPTY) && + (grid[piece_position_x + 1][piece_position_y + 1] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 2][piece_position_y + 2] == GridSquare.MOVING) && + (grid[piece_position_x + 2][piece_position_y + 1] != GridSquare.EMPTY) && + (grid[piece_position_x + 2][piece_position_y + 1] != GridSquare.MOVING)) checker = true; + + if ((grid[piece_position_x + 1][piece_position_y + 2] == GridSquare.MOVING) && + (grid[piece_position_x + 2][piece_position_y + 2] != GridSquare.EMPTY) && + (grid[piece_position_x + 2][piece_position_y + 2] != GridSquare.MOVING)) checker = true; + + if (!checker) + { + aux = piece[0][0]; + piece[0][0] = piece[3][0]; + piece[3][0] = piece[3][3]; + piece[3][3] = piece[0][3]; + piece[0][3] = aux; + + aux = piece[1][0]; + piece[1][0] = piece[3][1]; + piece[3][1] = piece[2][3]; + piece[2][3] = piece[0][2]; + piece[0][2] = aux; + + aux = piece[2][0]; + piece[2][0] = piece[3][2]; + piece[3][2] = piece[1][3]; + piece[1][3] = piece[0][1]; + piece[0][1] = aux; + + aux = piece[1][1]; + piece[1][1] = piece[2][1]; + piece[2][1] = piece[2][2]; + piece[2][2] = piece[1][2]; + piece[1][2] = aux; + } + + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if (grid[i][j] == GridSquare.MOVING) + { + grid[i][j] = EMPTY; + } + } + } + + for (int i = piece_position_x; i < piece_position_x + 4; i++) + { + for (int j = piece_position_y; j < piece_position_y + 4; j++) + { + if (piece[i - piece_position_x][j - piece_position_y] == GridSquare.MOVING) + { + grid[i][j] = MOVING; + } + } + } + + return true; + } + + return false; +} + +fn void check_detection(bool *detection_ref) +{ + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + if ((grid[i][j] == GridSquare.MOVING) && ((grid[i][j+1] == GridSquare.FULL) || (grid[i][j+1] == GridSquare.BLOCK))) *detection_ref = true; + } + } +} + +fn void check_completion(bool *line_to_delete_ref) +{ + int calculator = 0; + + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + calculator = 0; + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + // Count each square of the line + if (grid[i][j] == GridSquare.FULL) + { + calculator++; + } + + // Check if we completed the whole line + if (calculator == GRID_HORIZONTAL_SIZE - 2) + { + *line_to_delete_ref = true; + calculator = 0; + // points++; + + // Mark the completed line + for (int z = 1; z < GRID_HORIZONTAL_SIZE - 1; z++) + { + grid[z][j] = FADING; + } + } + } + } +} + +fn int delete_complete_lines() +{ + int lines_to_erase = 0; + // Erase the completed line + for (int j = GRID_VERTICAL_SIZE - 2; j >= 0; j--) + { + while (grid[1][j] == GridSquare.FADING) + { + lines_to_erase++; + for (int i = 1; i < GRID_HORIZONTAL_SIZE - 1; i++) + { + grid[i][j] = GridSquare.EMPTY; + } + + for (int j2 = j-1; j2 >= 0; j2--) + { + for (int i2 = 1; i2 < GRID_HORIZONTAL_SIZE - 1; i2++) + { + switch (grid[i2][j2]) + { + case FULL: + grid[i2][j2+1] = GridSquare.FULL; + grid[i2][j2] = GridSquare.EMPTY; + case FADING: + grid[i2][j2+1] = GridSquare.FADING; + grid[i2][j2] = GridSquare.EMPTY; + default: + } + } + } + } + } + return lines_to_erase; +} + diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index dc93bb210..baaeda028 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -164,7 +164,7 @@ LLVMValueRef llvm_emit_coerce_alignment(GenContext *c, BEValue *be_value, LLVMTy LLVMValueRef target = LLVMBuildBitCast(c->builder, cast, LLVMPointerType(coerce_type, 0), ""); llvm_store_value_aligned(c, target, be_value, target_alignment); *resulting_alignment = target_alignment; - return cast; + return target; } *resulting_alignment = be_value->alignment; return LLVMBuildBitCast(c->builder, be_value->value, LLVMPointerType(coerce_type, 0), ""); diff --git a/src/compiler/number.c b/src/compiler/number.c index 86e68691e..9e2470bff 100644 --- a/src/compiler/number.c +++ b/src/compiler/number.c @@ -191,6 +191,7 @@ bool float_const_fits_type(const ExprConst *expr_const, TypeKind kind) default: UNREACHABLE } + assert(expr_const->const_kind == CONST_FLOAT); return expr_const->fxx.f >= -lo_limit && expr_const->fxx.f <= hi_limit; } diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index 05eb8b11a..eedab9f8d 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -671,6 +671,16 @@ bool may_convert_float_const_implicit(Expr *expr, Type *to_type) bool may_convert_int_const_implicit(Expr *expr, Type *to_type) { Type *to_type_flat = type_flatten(to_type); + switch (to_type_flat->type_kind) + { + case ALL_FLOATS: + case TYPE_BOOL: + return true; + case ALL_INTS: + break; + default: + return false; + } if (expr_const_will_overflow(&expr->const_expr, to_type_flat->type_kind)) { sema_error_const_int_out_of_range(expr, expr, to_type); diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index 77d82677e..2c91fff3e 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -883,15 +883,17 @@ static inline bool sema_expr_analyse_identifier(SemaContext *context, Type *to, Decl *private_symbol = NULL; DEBUG_LOG("Now resolving %s", expr->identifier_expr.ident); + + // Just start with inference + if (!expr->identifier_expr.path && to) + { + if (find_possible_inferred_identifier(to, expr)) return true; + } + Decl *decl = sema_find_path_symbol(context, expr->identifier_expr.ident, expr->identifier_expr.path); // Is this a broken decl? if (!decl_ok(decl)) return false; - // Just no real way to find it, try inference - if (!decl && !expr->identifier_expr.path && to) - { - if (find_possible_inferred_identifier(to, expr)) return true; - } // Rerun if we can't do inference. if (!decl) @@ -3051,6 +3053,7 @@ static inline void expr_replace_with_enum_array(Expr *enum_array_expr, Decl *enu Expr *expr = expr_new(EXPR_CONST, span); expr->const_expr.const_kind = CONST_ENUM; expr->const_expr.enum_val = decl; + assert(decl_ok(decl)); expr->type = kind; expr->resolve_status = RESOLVE_DONE; vec_add(element_values, expr); diff --git a/src/compiler/sema_name_resolution.c b/src/compiler/sema_name_resolution.c index 558d8b16f..b32455e83 100644 --- a/src/compiler/sema_name_resolution.c +++ b/src/compiler/sema_name_resolution.c @@ -187,13 +187,8 @@ static Decl *sema_resolve_path_symbol(SemaContext *context, NameResolve *name_re return decl ? decl : sema_find_decl_in_global(&global_context.symbols, global_context.module_list, name_resolve, false); } -static Decl *sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name_resolve) +static inline Decl *sema_find_local(SemaContext *context, const char *symbol) { - Decl *decl = NULL; - - const char *symbol = name_resolve->symbol; - assert(name_resolve->path == NULL); - Decl **locals = context->locals; if (context->active_scope.current_local > 0) { @@ -210,7 +205,7 @@ static Decl *sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name VarDeclKind kind = cur->var.kind; // In this case, we erase the value from parent scopes, so it isn't visible here. - if (kind == VARDECL_ERASE) goto JUMP_ERASED; + if (kind == VARDECL_ERASE) return NULL; if (kind == VARDECL_REWRAPPED) return cur->var.alias; } return cur; @@ -218,7 +213,18 @@ static Decl *sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name current--; } } - JUMP_ERASED:; + return NULL; +} + +static Decl *sema_resolve_no_path_symbol(SemaContext *context, NameResolve *name_resolve) +{ + Decl *decl = NULL; + + const char *symbol = name_resolve->symbol; + assert(name_resolve->path == NULL); + + Decl *found = sema_find_local(context, symbol); + if (found) return found; CompilationUnit *unit = context->unit; @@ -507,7 +513,8 @@ bool sema_add_local(SemaContext *context, Decl *decl) // Ignore synthetic locals. if (!decl->name) return true; if (decl->decl_kind == DECL_VAR && decl->var.shadow) goto ADD_VAR; - Decl *other = sema_find_symbol(context, decl->name); + + Decl *other = sema_find_local(context, decl->name); assert(!other || other->module); if (other && (other->module == current_module || other->is_autoimport)) { diff --git a/src/version.h b/src/version.h index fce1d512d..2e6b1dd7f 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.2.0" \ No newline at end of file +#define COMPILER_VERSION "0.2.1" \ No newline at end of file diff --git a/test/test_suite/abi/sysv_direct_coerce.c3t b/test/test_suite/abi/sysv_direct_coerce.c3t new file mode 100644 index 000000000..9f1ede8b8 --- /dev/null +++ b/test/test_suite/abi/sysv_direct_coerce.c3t @@ -0,0 +1,47 @@ +// #target: macos-x64 +module foo; + +struct Rectangle +{ + float x; // Rectangle top-left corner position x + float y; // Rectangle top-left corner position y + float width; // Rectangle width + float height; // Rectangle height +} + +fn void test(Rectangle r) +{ + test(Rectangle { 1, 2, 3, 4 }); +} + +/* #expect: foo.ll + +define void @foo.test(<2 x float> %0, <2 x float> %1) #0 { +entry: + %r = alloca %Rectangle, align 4 + %literal = alloca %Rectangle, align 4 + %coerce = alloca %Rectangle, align 8 + %pair = bitcast %Rectangle* %r to { <2 x float>, <2 x float> }* + %2 = getelementptr inbounds { <2 x float>, <2 x float> }, { <2 x float>, <2 x float> }* %pair, i32 0, i32 0 + store <2 x float> %0, <2 x float>* %2, align 4 + %3 = getelementptr inbounds { <2 x float>, <2 x float> }, { <2 x float>, <2 x float> }* %pair, i32 0, i32 1 + store <2 x float> %1, <2 x float>* %3, align 4 + %4 = getelementptr inbounds %Rectangle, %Rectangle* %literal, i32 0, i32 0 + store float 1.000000e+00, float* %4, align 4 + %5 = getelementptr inbounds %Rectangle, %Rectangle* %literal, i32 0, i32 1 + store float 2.000000e+00, float* %5, align 4 + %6 = getelementptr inbounds %Rectangle, %Rectangle* %literal, i32 0, i32 2 + store float 3.000000e+00, float* %6, align 4 + %7 = getelementptr inbounds %Rectangle, %Rectangle* %literal, i32 0, i32 3 + store float 4.000000e+00, float* %7, align 4 + %8 = bitcast %Rectangle* %coerce to { <2 x float>, <2 x float> }* + %9 = bitcast { <2 x float>, <2 x float> }* %8 to i8* + %10 = bitcast %Rectangle* %literal to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %9, i8* align 4 %10, i32 16, i1 false) + %11 = getelementptr inbounds { <2 x float>, <2 x float> }, { <2 x float>, <2 x float> }* %8, i32 0, i32 0 + %lo = load <2 x float>, <2 x float>* %11, align 8 + %12 = getelementptr inbounds { <2 x float>, <2 x float> }, { <2 x float>, <2 x float> }* %8, i32 0, i32 1 + %hi = load <2 x float>, <2 x float>* %12, align 8 + call void @foo.test(<2 x float> %lo, <2 x float> %hi) + ret void +} \ No newline at end of file diff --git a/test/test_suite/floats/convert_float.c3t b/test/test_suite/floats/convert_float.c3t new file mode 100644 index 000000000..cda573226 --- /dev/null +++ b/test/test_suite/floats/convert_float.c3t @@ -0,0 +1,24 @@ +// #target: macos-x64 +module foo; + +fn void test() +{ + float x = 12; + x *= -1; + double y = x; +} + +/* #expect: foo.ll +define void @foo.test() #0 { +entry: + %x = alloca float, align 4 + %y = alloca double, align 8 + store float 1.200000e+01, float* %x, align 4 + %0 = load float, float* %x, align 4 + %fmul = fmul float %0, -1.000000e+00 + store float %fmul, float* %x, align 4 + %1 = load float, float* %x, align 4 + %fpfpext = fpext float %1 to double + store double %fpfpext, double* %y, align 8 + ret void +} diff --git a/test/test_suite/symbols/allow_local_shadowing.c3 b/test/test_suite/symbols/allow_local_shadowing.c3 new file mode 100644 index 000000000..802eaf090 --- /dev/null +++ b/test/test_suite/symbols/allow_local_shadowing.c3 @@ -0,0 +1,10 @@ +int foo; + +fn void x() +{ + double foo = 123; +} + +fn void y(int foo) +{ +} \ No newline at end of file