diff options
Diffstat (limited to 'src/ei.c')
-rw-r--r-- | src/ei.c | 1184 |
1 files changed, 1184 insertions, 0 deletions
diff --git a/src/ei.c b/src/ei.c new file mode 100644 index 0000000..da2331e --- /dev/null +++ b/src/ei.c @@ -0,0 +1,1184 @@ +/* + * Copyright (c) 2024 Richard Braun. + * + * Permission to use, copy, modify, and/or distribute this software for + * any purpose with or without fee is hereby granted. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL + * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE + * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY + * DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * + * Embedded invaders. + */ + +#include <assert.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "eetg.h" +#include "ei.h" +#include "macros.h" + +#define EI_NR_LIVES 3 + +#define EI_ALIEN_STARTING_ROW 3 + +#define EI_PLAYER_MISSILE_SPEED 25 +#define EI_ALIENS_SPEED 10 +#define EI_ALIEN_MISSILE_SPEED 10 +#define EI_FIRST_ALIEN_MISSILE_DELAY 2 +#define EI_UFO_SPEED 10 + +#define EI_SCORE_MISSILE 40 +#define EI_SCORE_ALIENS0 30 +#define EI_SCORE_ALIENS12 20 +#define EI_SCORE_ALIENS34 10 +#define EI_SCORE_UFO_BASE 100 + +#define EI_TITLE_SPRITE \ +" _____ _____ \n" \ +"( ___ )-------------------------------------------------( ___ )\n" \ +" | | | | \n" \ +" | | ____ __ __ __ __ | | \n" \ +" | | / __/_ _ / / ___ ___/ /__/ /__ ___/ / | | \n" \ +" | | / _// ' \\/ _ \\/ -_) _ / _ / -_) _ / | | \n" \ +" | | /___/_/_/_/_.__/\\__/\\_,_/\\_,_/\\__/\\_,_/ | | \n" \ +" | | _ __ | | \n" \ +" | | (_)__ _ _____ ____/ /__ _______ | | \n" \ +" | | / / _ \\ |/ / _ `/ _ / -_) __(_-< | | \n" \ +" | | /_/_//_/___/\\_,_/\\_,_/\\__/_/ /___/ | | \n" \ +" | | | | \n" \ +" |___| |___| \n" \ +"(_____)-------------------------------------------------(_____)\n" + +#define EI_HELP_SPRITE \ +" s = left \n" \ +" f = right \n" \ +" space = shoot \n" + +#define EI_START_SPRITE \ +"Press SPACE to start\n" \ +"Press X to leave \n" + +#define EI_PLAYER_SPRITE "/-^-\\\n" + +#define EI_BUNKER_SPRITE \ +" ### \n" \ +" ##### \n" \ +"#######\n" \ +"## ##\n" + +#define EI_ALIENS0_SPRITE_1 ",^,\n" +#define EI_ALIENS0_SPRITE_2 ".-.\n" +#define EI_ALIENS12_SPRITE_1 "-O_\n" +#define EI_ALIENS12_SPRITE_2 "_O-\n" +#define EI_ALIENS34_SPRITE_1 "/^\\\n" +#define EI_ALIENS34_SPRITE_2 "-^-\n" + +#define EI_STATUS_SPRITE_FORMAT "SCORE: %08d Lives: %d\n" + +#define EI_END_TITLE_SPRITE \ +" ________ __ _______ ____ _ _________ \n" \ +" / ___/ _ | / |/ / __/ / __ \\ | / / __/ _ \\\n" \ +"/ (_ / __ |/ /|_/ / _/ / /_/ / |/ / _// , _/\n" \ +"\\___/_/ |_/_/ /_/___/ \\____/|___/___/_/|_| \n" + +#define EI_TYPE_TITLE 0 +#define EI_TYPE_HELP 1 +#define EI_TYPE_START 2 +#define EI_TYPE_PLAYER 3 +#define EI_TYPE_PLAYER_MISSILE 4 +#define EI_TYPE_BUNKER 5 +#define EI_TYPE_ALIEN 6 +#define EI_TYPE_ALIEN_MISSILE 7 +#define EI_TYPE_UFO 8 +#define EI_TYPE_STATUS 9 +#define EI_TYPE_END_TITLE 10 + +static const char * +ei_get_group_sprite1(size_t group) +{ + const char *sprite; + + if (group == 0) { + sprite = EI_ALIENS0_SPRITE_1; + } else if ((group == 1) || (group == 2)) { + sprite = EI_ALIENS12_SPRITE_1; + } else { + sprite = EI_ALIENS34_SPRITE_1; + } + + return sprite; +} + +static const char * +ei_get_group_sprite2(size_t group) +{ + const char *sprite; + + if (group == 0) { + sprite = EI_ALIENS0_SPRITE_2; + } else if ((group == 1) || (group == 2)) { + sprite = EI_ALIENS12_SPRITE_2; + } else { + sprite = EI_ALIENS34_SPRITE_2; + } + + return sprite; +} + +static int +ei_get_group_color(size_t group) +{ + int color; + + if (group == 0) { + color = EETG_COLOR_RED; + } else if ((group == 1) || (group == 2)) { + color = EETG_COLOR_GREEN; + } else { + color = EETG_COLOR_BLUE; + } + + return color; +} + +static struct eetg_object * +ei_get_object(struct eetg_object *object1, + struct eetg_object *object2, + int type) +{ + struct eetg_object *object = NULL; + + assert(object1); + assert(object2); + + if (eetg_object_get_type(object1) == type) { + object = object1; + } else if (eetg_object_get_type(object2) == type) { + object = object2; + } + + return object; +} + +static bool +ei_has_type(const struct eetg_object *object1, + const struct eetg_object *object2, + int type) +{ + return (eetg_object_get_type(object1) == type) + || (eetg_object_get_type(object2) == type); +} + +static void +ei_bunker_reset_sprite(struct ei_bunker *bunker) +{ + assert(bunker); + + snprintf(bunker->sprite, sizeof(bunker->sprite), "%s", EI_BUNKER_SPRITE); +} + +static void +ei_bunker_init(struct ei_bunker *bunker) +{ + assert(bunker); + + ei_bunker_reset_sprite(bunker); + + eetg_object_init(&bunker->object, EI_TYPE_BUNKER, bunker->sprite); + eetg_object_set_color(&bunker->object, EETG_COLOR_CYAN); +} + +static struct ei_bunker * +ei_bunker_get(struct eetg_object *object) +{ + assert(eetg_object_get_type(object) == EI_TYPE_BUNKER); + + return structof(object, struct ei_bunker, object); +} + +static struct eetg_object * +ei_bunker_get_object(struct ei_bunker *bunker) +{ + assert(bunker); + + return &bunker->object; +} + +static bool +ei_bunker_damage(struct ei_bunker *bunker, int x, int y) +{ + char *sprite; + int index; + + assert(bunker); + + index = eetg_object_get_cell(&bunker->object, x, y); + + sprite = (char *)eetg_object_get_sprite(&bunker->object); + + sprite[index] = ' '; + + return eetg_object_is_empty(&bunker->object); +} + +static void +ei_alien_init(struct ei_alien *alien, const char *sprite, int color) +{ + assert(alien); + + eetg_object_init(&alien->object, EI_TYPE_ALIEN, sprite); + eetg_object_set_color(&alien->object, color); +} + +static struct ei_alien * +ei_alien_get(struct eetg_object *object) +{ + assert(eetg_object_get_type(object) == EI_TYPE_ALIEN); + + return structof(object, struct ei_alien, object); +} + +static struct eetg_object * +ei_alien_get_object(struct ei_alien *alien) +{ + assert(alien); + + return &alien->object; +} + +static bool +ei_alien_is_dead(const struct ei_alien *alien) +{ + assert(alien); + + return (eetg_object_get_world(&alien->object) == NULL); +} + +static int +ei_alien_get_x(const struct ei_alien *alien) +{ + assert(alien); + + return eetg_object_get_x(&alien->object); +} + +static int +ei_alien_get_width(const struct ei_alien *alien) +{ + assert(alien); + + return eetg_object_get_width(&alien->object); +} + +static bool +ei_alien_move_down(struct ei_alien *alien) +{ + struct eetg_object *object; + bool game_over = false; + int y; + + assert(alien); + + object = &alien->object; + + y = eetg_object_get_y(object) + 1; + + eetg_object_move(object, eetg_object_get_x(object), y); + + if (y >= (EETG_ROWS - 1)) { + game_over = true; + } + + return game_over; +} + +static void +ei_alien_move_left(struct ei_alien *alien) +{ + struct eetg_object *object; + + assert(alien); + + object = &alien->object; + + eetg_object_move(object, eetg_object_get_x(object) - 1, + eetg_object_get_y(object)); +} + +static void +ei_alien_move_right(struct ei_alien *alien) +{ + struct eetg_object *object; + + assert(alien); + + object = &alien->object; + + eetg_object_move(object, eetg_object_get_x(object) + 1, + eetg_object_get_y(object)); +} + +static void +ei_alien_group_init(struct ei_alien_group *group, const char *sprite1, + const char *sprite2, int color) +{ + assert(group); + assert(sprite1); + assert(strlen(sprite1) == (EI_ALIEN_WIDTH + 1)); + assert(sprite2); + assert(strlen(sprite2) == (EI_ALIEN_WIDTH + 1)); + + group->sprites[0] = sprite1; + group->sprites[1] = sprite2; + group->sprite_index = 0; + + memcpy(group->sprite, group->sprites[group->sprite_index], + sizeof(group->sprite)); + + for (size_t i = 0; i < ARRAY_SIZE(group->aliens); i++) { + ei_alien_init(&group->aliens[i], group->sprite, color); + } +} + +static void +ei_alien_group_attach(struct ei_alien_group *group, + struct eetg_world *world, int y) +{ + int x = 0; + + assert(group); + + for (size_t i = 0; i < ARRAY_SIZE(group->aliens); i++) { + struct ei_alien *alien = &group->aliens[i]; + + eetg_world_add(world, ei_alien_get_object(alien), x, y); + + x += EI_ALIEN_WIDTH; + } +} + +static bool +ei_alien_group_has_alien(const struct ei_alien_group *group, + const struct ei_alien *alien) +{ + bool alien_present = false; + const struct ei_alien *end; + + assert(group); + + end = &group->aliens[ARRAY_SIZE(group->aliens)]; + + if ((alien >= group->aliens) && (alien < end)) { + alien_present = true; + } + + return alien_present; +} + +static struct ei_alien * +ei_alien_group_get(struct ei_alien_group *group, size_t index) +{ + assert(group); + assert(index < ARRAY_SIZE(group->aliens)); + + return &group->aliens[index]; +} + +static void +ei_alien_group_twerk(struct ei_alien_group *group) +{ + assert(group); + + group->sprite_index = (group->sprite_index + 1) & 1; + memcpy(group->sprite, group->sprites[group->sprite_index], + sizeof(group->sprite)); +} + +static bool +ei_alien_group_move_down(struct ei_alien_group *group) +{ + bool game_over = false; + + ei_alien_group_twerk(group); + + for (size_t i = 0; i < ARRAY_SIZE(group->aliens); i++) { + struct ei_alien *alien = &group->aliens[i]; + + if (!ei_alien_is_dead(alien)) { + bool tmp; + + tmp = ei_alien_move_down(alien); + + if (tmp) { + game_over = true; + } + } + } + + return game_over; +} + +static bool +ei_alien_group_move_left(struct ei_alien_group *group) +{ + bool border_reached = false; + + ei_alien_group_twerk(group); + + for (size_t i = 0; i < ARRAY_SIZE(group->aliens); i++) { + struct ei_alien *alien = &group->aliens[i]; + + if (!ei_alien_is_dead(alien)) { + ei_alien_move_left(alien); + + if (!border_reached && (ei_alien_get_x(alien) == 0)) { + border_reached = true; + } + } + } + + return border_reached; +} + +static bool +ei_alien_group_move_right(struct ei_alien_group *group) +{ + bool border_reached = false; + + ei_alien_group_twerk(group); + + for (size_t i = ARRAY_SIZE(group->aliens) - 1; + i < ARRAY_SIZE(group->aliens); i--) { + struct ei_alien *alien = &group->aliens[i]; + + if (!ei_alien_is_dead(alien)) { + ei_alien_move_right(alien); + + if (!border_reached) { + int x; + + x = ei_alien_get_x(alien) + ei_alien_get_width(alien) - 1; + + if (x == (EETG_COLUMNS - 1)) { + border_reached = true; + } + } + } + } + + return border_reached; +} + +static struct ei_alien_group * +ei_game_find_alien_group(struct ei_game *game, struct ei_alien *alien) +{ + struct ei_alien_group *group = NULL; + + assert(alien); + + for (size_t i = 0; i < ARRAY_SIZE(game->aliens); i++) { + struct ei_alien_group *tmp = &game->aliens[i]; + + if (ei_alien_group_has_alien(tmp, alien)) { + group = tmp; + break; + } + } + + return group; +} + +static void +ei_game_add_bunkers(struct ei_game *game) +{ + int x = 6; + + assert(game); + + for (size_t i = 0; i < ARRAY_SIZE(game->bunkers); i++) { + struct ei_bunker *bunker = &game->bunkers[i]; + + ei_bunker_reset_sprite(bunker); + eetg_world_add(&game->world, ei_bunker_get_object(bunker), x, 17); + + x += 20; + } +} + +static void +ei_game_add_aliens(struct ei_game *game) +{ + assert(game); + + for (size_t i = 0; i < ARRAY_SIZE(game->aliens); i++) { + struct ei_alien_group *group = &game->aliens[i]; + + ei_alien_group_attach(group, &game->world, + EI_ALIEN_STARTING_ROW + (i * 2)); + } +} + +static void +ei_game_update_status(struct ei_game *game) +{ + assert(game); + + snprintf(game->status_sprite, sizeof(game->status_sprite), + EI_STATUS_SPRITE_FORMAT, game->score, game->nr_lives); +} + +static void +ei_game_prepare(struct ei_game *game) +{ + assert(game); + + eetg_world_clear(&game->world); + + game->state = EI_STATE_PREPARED; +} + +static void +ei_game_start(struct ei_game *game) +{ + assert(game); + + eetg_world_clear(&game->world); + + eetg_world_add(&game->world, &game->player, 37, 23); + + ei_game_add_bunkers(game); + ei_game_add_aliens(game); + + eetg_world_add(&game->world, &game->status, 26, 0); + + game->player_missile_counter_reload = EI_FPS / EI_PLAYER_MISSILE_SPEED; + game->player_missile_counter = game->player_missile_counter_reload; + game->aliens_speed_counter_reload = EI_FPS / EI_ALIENS_SPEED; + game->aliens_speed_counter = game->aliens_speed_counter_reload; + game->first_alien_missile_counter = EI_FPS * EI_FIRST_ALIEN_MISSILE_DELAY; + game->alien_missile_counter_reload = EI_FPS / EI_ALIEN_MISSILE_SPEED; + game->ufo_counter_reload = EI_FPS / EI_UFO_SPEED; + game->nr_dead_aliens = 0; + + game->aliens_move_left = false; + game->aliens_move_down = false; + + game->state = EI_STATE_PLAYING; +} + +static void +ei_game_kill_alien(struct ei_game *game, struct ei_alien *alien) +{ + int group, score; + + assert(alien); + + group = ei_game_find_alien_group(game, alien) - game->aliens; + + if (group == 0) { + score = EI_SCORE_ALIENS0; + } else if ((group == 1) || (group == 2)) { + score = EI_SCORE_ALIENS12; + } else { + score = EI_SCORE_ALIENS34; + } + + game->score += score; + + ei_game_update_status(game); + + eetg_world_remove(&game->world, ei_alien_get_object(alien)); + + game->nr_dead_aliens++; + + if (game->nr_dead_aliens == (EI_NR_ALIEN_GROUPS * EI_ALIEN_GROUP_SIZE)) { + ei_game_prepare(game); + } else { + int aliens_speed; + + aliens_speed = game->nr_dead_aliens / 2; + + if (aliens_speed < (EI_FPS / 5)) { + aliens_speed = EI_FPS / 5; + } else if (aliens_speed > ((EI_FPS * 4) / 5)) { + aliens_speed = ((EI_FPS * 4) / 5); + } + + game->aliens_speed_counter_reload = EI_FPS / aliens_speed; + } +} + +static void +ei_game_damage_bunker(struct ei_game *game, struct ei_bunker *bunker, + int x, int y) +{ + bool destroyed; + + destroyed = ei_bunker_damage(bunker, x, y); + + if (destroyed) { + eetg_world_remove(&game->world, ei_bunker_get_object(bunker)); + } +} + +static void +ei_game_terminate(struct ei_game *game) +{ + assert(game); + + eetg_world_clear(&game->world); + + eetg_world_add(&game->world, &game->end_title, 12, 10); + eetg_world_add(&game->world, &game->status, 26, 6); + eetg_world_add(&game->world, &game->start, 30, 20); + + game->state = EI_STATE_GAME_OVER; +} + +static void +ei_game_kill_player(struct ei_game *game, bool game_over) +{ + assert(game); + assert(game->nr_lives > 0); + + game->nr_lives--; + + ei_game_update_status(game); + + if (game_over || (game->nr_lives == 0)) + { + ei_game_terminate(game); + } +} + +static void +ei_game_handle_player_missile_collision(struct ei_game *game, + struct eetg_object *missile, + struct eetg_object *object, + int x, int y) +{ + assert(game); + + eetg_world_remove(&game->world, missile); + + if (eetg_object_get_type(object) == EI_TYPE_BUNKER) { + ei_game_damage_bunker(game, ei_bunker_get(object), x, y); + } else if (eetg_object_get_type(object) == EI_TYPE_ALIEN_MISSILE) { + game->score += EI_SCORE_MISSILE; + eetg_world_remove(&game->world, object); + } else if (eetg_object_get_type(object) == EI_TYPE_ALIEN) { + ei_game_kill_alien(game, ei_alien_get(object)); + } else if (eetg_object_get_type(object) == EI_TYPE_UFO) { + game->score += EI_SCORE_UFO_BASE * ((eetg_rand() % 5) + 1); + eetg_world_remove(&game->world, object); + } +} + +static void +ei_game_handle_alien_collision(struct ei_game *game, + struct eetg_object *object, + int x, int y) +{ + if (eetg_object_get_type(object) == EI_TYPE_BUNKER) { + ei_game_damage_bunker(game, ei_bunker_get(object), x, y); + } else if (eetg_object_get_type(object) == EI_TYPE_PLAYER) { + ei_game_kill_player(game, true); + } +} + +static void +ei_game_handle_alien_missile_collision(struct ei_game *game, + struct eetg_object *missile, + struct eetg_object *object, + int x, int y) +{ + assert(game); + + if (eetg_object_get_type(object) == EI_TYPE_ALIEN) { + return; + } + + eetg_world_remove(&game->world, missile); + + if (eetg_object_get_type(object) == EI_TYPE_BUNKER) { + ei_game_damage_bunker(game, ei_bunker_get(object), x, y); + } else if (eetg_object_get_type(object) == EI_TYPE_PLAYER) { + ei_game_kill_player(game, false); + } +} + +static void +ei_game_handle_collision(struct eetg_object *object1, + struct eetg_object *object2, + int x, int y, void *arg) +{ + if (ei_has_type(object1, object2, EI_TYPE_PLAYER_MISSILE)) { + struct eetg_object *missile, *other; + + missile = ei_get_object(object1, object2, EI_TYPE_PLAYER_MISSILE); + other = (object1 == missile) ? object2 : object1; + + ei_game_handle_player_missile_collision(arg, missile, other, x, y); + } else if (ei_has_type(object1, object2, EI_TYPE_ALIEN)) { + struct eetg_object *alien, *other; + + alien = ei_get_object(object1, object2, EI_TYPE_ALIEN); + other = (object1 == alien) ? object2 : object1; + + ei_game_handle_alien_collision(arg, other, x, y); + } else if (ei_has_type(object1, object2, EI_TYPE_ALIEN_MISSILE)) { + struct eetg_object *missile, *other; + + missile = ei_get_object(object1, object2, EI_TYPE_ALIEN_MISSILE); + other = (object1 == missile) ? object2 : object1; + + ei_game_handle_alien_missile_collision(arg, missile, other, x, y); + } +} + +static void +ei_game_reset_history(struct ei_game *game) +{ + assert(game); + + game->score = 0; + game->nr_lives = EI_NR_LIVES; + + ei_game_update_status(game); +} + +static bool +ei_game_process_intro_input(struct ei_game *game, char c) +{ + assert(game); + + if (c == 'x') { + return true; + } + + if (c != ' ') { + return false; + } + + ei_game_reset_history(game); + ei_game_prepare(game); + + return false; +} + +static bool +ei_game_process_game_input(struct ei_game *game, char c) +{ + assert(game); + + if (c == 'x') { + return true; + } + + if (c == 's') { + struct eetg_object *player_object = &game->player; + int x; + + x = eetg_object_get_x(player_object); + + if (x > 0) { + eetg_object_move(player_object, x - 1, + eetg_object_get_y(player_object)); + } + } else if (c == 'f') { + struct eetg_object *player_object = &game->player; + int x, width; + + x = eetg_object_get_x(player_object); + width = eetg_object_get_width(player_object); + + if ((x + width) < EETG_COLUMNS) { + eetg_object_move(player_object, x + 1, + eetg_object_get_y(player_object)); + } + } else if (c == ' ') { + struct eetg_object *player_missile = &game->player_missile; + + if (eetg_object_get_world(player_missile) == NULL) { + struct eetg_object *player_object = &game->player; + int x, y; + + x = eetg_object_get_x(player_object); + y = eetg_object_get_y(player_object); + + eetg_world_add(&game->world, player_missile, x + 2, y - 1); + + game->player_missile_counter = game->player_missile_counter_reload; + } + } + + return false; +} + +static void +ei_game_process_player_missile(struct ei_game *game) +{ + int x, y; + + assert(game); + + if (eetg_object_get_world(&game->player_missile) == NULL) { + return; + } + + assert(game->player_missile_counter > 0); + game->player_missile_counter--; + + if (game->player_missile_counter != 0) { + return; + } + + game->player_missile_counter = game->player_missile_counter_reload; + + x = eetg_object_get_x(&game->player_missile); + y = eetg_object_get_y(&game->player_missile) - 1; + + if (y == 0) { + eetg_world_remove(&game->world, &game->player_missile); + } else { + eetg_object_move(&game->player_missile, x, y); + } +} + +static struct ei_alien * +ei_game_select_firing_alien(struct ei_game *game) +{ + struct ei_alien *alien = NULL; + int nr_firing_aliens = 0; + + assert(game); + + for (size_t i = 0; i < EI_ALIEN_GROUP_SIZE; i++) { + for (size_t j = 0; j < ARRAY_SIZE(game->aliens); j++) { + struct ei_alien *tmp = ei_alien_group_get(&game->aliens[j], i); + + if (!ei_alien_is_dead(tmp)) { + nr_firing_aliens++; + break; + } + } + } + + if (nr_firing_aliens > 0) { + int index; + + index = eetg_rand() % nr_firing_aliens; + + for (size_t i = ARRAY_SIZE(game->aliens) - 1; + i < ARRAY_SIZE(game->aliens); i--) { + struct ei_alien *tmp = ei_alien_group_get(&game->aliens[i], index); + + if (!ei_alien_is_dead(tmp)) { + alien = tmp; + break; + } + } + } + + return alien; +} + +static void +ei_game_process_alien_missile(struct ei_game *game) +{ + assert(game); + + if (game->first_alien_missile_counter != 0) { + assert(game->first_alien_missile_counter > 0); + game->first_alien_missile_counter--; + return; + } + + if (eetg_object_get_world(&game->alien_missile) != NULL) { + assert(game->alien_missile_counter > 0); + game->alien_missile_counter--; + + if (game->alien_missile_counter == 0) { + int x, y; + + game->alien_missile_counter = game->alien_missile_counter_reload; + + x = eetg_object_get_x(&game->alien_missile); + y = eetg_object_get_y(&game->alien_missile); + + if (y == EETG_ROWS) { + eetg_world_remove(&game->world, &game->alien_missile); + } else { + eetg_object_move(&game->alien_missile, x, y + 1); + } + } + } else { + struct ei_alien *alien; + int x, y; + + alien = ei_game_select_firing_alien(game); + + if (alien) { + x = eetg_object_get_x(&alien->object); + y = eetg_object_get_y(&alien->object); + + eetg_world_add(&game->world, &game->alien_missile, x, y + 1); + + game->alien_missile_counter = game->alien_missile_counter_reload; + } + } +} + +static void +ei_game_process_aliens(struct ei_game *game) +{ + assert(game); + assert(game->aliens_speed_counter > 0); + + game->aliens_speed_counter--; + + if (game->aliens_speed_counter != 0) { + return; + } + + game->aliens_speed_counter = game->aliens_speed_counter_reload; + + if (game->aliens_move_down) { + for (size_t i = ARRAY_SIZE(game->aliens) - 1; + i < ARRAY_SIZE(game->aliens); i--) { + bool game_over; + + game_over = ei_alien_group_move_down(&game->aliens[i]); + + if (game_over) { + ei_game_terminate(game); + } + } + + game->aliens_move_down = false; + + if (eetg_object_get_world(&game->ufo) == NULL) { + int n; + + n = eetg_rand() % 3; + + if (n == 0) { + int x; + + n = eetg_rand() % 2; + + if (n == 0) { + x = EETG_COLUMNS; + game->ufo_moves_left = true; + } else { + x = -eetg_object_get_width(&game->ufo); + game->ufo_moves_left = false; + } + + eetg_world_add(&game->world, &game->ufo, x, 2); + + game->ufo_counter = game->ufo_counter_reload; + } + } + } else { + bool border_reached = false; + + for (size_t i = 0; i < ARRAY_SIZE(game->aliens); i++) { + struct ei_alien_group *group = &game->aliens[i]; + bool border_reached_by_group; + + if (game->aliens_move_left) { + border_reached_by_group = ei_alien_group_move_left(group); + } else { + border_reached_by_group = ei_alien_group_move_right(group); + } + + if (border_reached_by_group) { + border_reached = true; + } + } + + if (border_reached) { + game->aliens_move_down = true; + game->aliens_move_left = !game->aliens_move_left; + } + } +} + +static void +ei_game_process_ufo(struct ei_game *game) +{ + struct eetg_object *ufo; + + assert(game); + + ufo = &game->ufo; + + if (eetg_object_get_world(ufo) == NULL) { + return; + } + + assert(game->ufo_counter > 0); + + game->ufo_counter--; + + if (game->ufo_counter != 0) { + return; + } + + game->ufo_counter = game->ufo_counter_reload; + + if (game->ufo_moves_left) { + int x; + + x = eetg_object_get_x(ufo) - 1; + + if ((x + eetg_object_get_width(ufo)) <= 0) { + eetg_world_remove(&game->world, ufo); + } else { + eetg_object_move(ufo, x, eetg_object_get_y(ufo)); + } + } else { + int x; + + x = eetg_object_get_x(ufo) + 1; + + if (x >= EETG_COLUMNS) { + eetg_world_remove(&game->world, ufo); + } else { + eetg_object_move(ufo, x, eetg_object_get_y(ufo)); + } + } +} + +static void +ei_game_init_bunkers(struct ei_game *game) +{ + assert(game); + + for (size_t i = 0; i < ARRAY_SIZE(game->bunkers); i++) { + ei_bunker_init(&game->bunkers[i]); + } +} + +static void +ei_game_init_aliens(struct ei_game *game) +{ + assert(game); + + for (size_t i = 0; i < ARRAY_SIZE(game->aliens); i++) { + struct ei_alien_group *group = &game->aliens[i]; + const char *sprite1, *sprite2; + int color; + + sprite1 = ei_get_group_sprite1(i); + sprite2 = ei_get_group_sprite2(i); + color = ei_get_group_color(i); + + ei_alien_group_init(group, sprite1, sprite2, color); + } +} + +void +ei_game_init(struct ei_game *game, eetg_write_fn write_fn, void *arg) +{ + assert(game); + + game->sync_counter_reload = EI_FPS * 2; + game->sync_counter = 1; + + game->state = EI_STATE_INTRO; + + ei_game_reset_history(game); + + eetg_world_init(&game->world, write_fn, arg); + eetg_world_register_collision_fn(&game->world, + ei_game_handle_collision, + game); + + eetg_object_init(&game->title, EI_TYPE_TITLE, EI_TITLE_SPRITE); + eetg_object_set_color(&game->title, EETG_COLOR_BLUE); + + eetg_object_init(&game->help, EI_TYPE_HELP, EI_HELP_SPRITE); + eetg_object_set_color(&game->help, EETG_COLOR_RED); + + eetg_object_init(&game->start, EI_TYPE_START, EI_START_SPRITE); + eetg_object_set_color(&game->start, EETG_COLOR_RED); + + eetg_object_init(&game->player, EI_TYPE_PLAYER, EI_PLAYER_SPRITE); + eetg_object_set_color(&game->player, EETG_COLOR_YELLOW); + + eetg_object_init(&game->player_missile, EI_TYPE_PLAYER_MISSILE, "!\n"); + eetg_object_set_color(&game->player_missile, EETG_COLOR_WHITE); + + ei_game_init_bunkers(game); + ei_game_init_aliens(game); + + eetg_object_init(&game->alien_missile, EI_TYPE_ALIEN_MISSILE, ":\n"); + eetg_object_set_color(&game->alien_missile, EETG_COLOR_MAGENTA); + + eetg_object_init(&game->ufo, EI_TYPE_UFO, "<o~o>\n"); + eetg_object_set_color(&game->ufo, EETG_COLOR_MAGENTA); + + eetg_object_init(&game->status, EI_TYPE_STATUS, game->status_sprite); + eetg_object_set_color(&game->status, EETG_COLOR_RED); + + eetg_object_init(&game->end_title, EI_TYPE_END_TITLE, EI_END_TITLE_SPRITE); + eetg_object_set_color(&game->end_title, EETG_COLOR_WHITE); + + eetg_world_add(&game->world, &game->title, 8, 1); + eetg_world_add(&game->world, &game->help, 30, 16); + eetg_world_add(&game->world, &game->start, 30, 20); +} + +bool +ei_game_process(struct ei_game *game, int8_t c) +{ + bool sync = false; + bool leave = false; + + game->sync_counter--; + + if (game->sync_counter == 0) { + sync = true; + game->sync_counter = game->sync_counter_reload; + } + + eetg_world_render(&game->world, sync); + + switch (game->state) { + case EI_STATE_INTRO: + case EI_STATE_GAME_OVER: + if (c >= 0) { + leave = ei_game_process_intro_input(game, (char)c); + } + + break; + case EI_STATE_PREPARED: + ei_game_start(game); + break; + case EI_STATE_PLAYING: + ei_game_process_player_missile(game); + ei_game_process_aliens(game); + ei_game_process_ufo(game); + ei_game_process_alien_missile(game); + + if (c >= 0) { + leave = ei_game_process_game_input(game, (char)c); + } + + break; + } + + return leave; +} |