diff --git a/src/const.hpp b/src/const.hpp index 028167c..f863936 100644 --- a/src/const.hpp +++ b/src/const.hpp @@ -11,5 +11,6 @@ constexpr Uint64 TARGET_FRAME_TIME_NS = 1'000'000'000 / TARGET_FPS; #define WIDTH_RATIO 15 #define HEIGHT_RATIO 41.53846153846154 #define MIN_INPUT_DELAY_MOVEMENT 0 -#define MIN_INPUT_DELAY_FIRE 200 +#define MIN_INPUT_DELAY_FIRE 400 #define NORMAL_PROJECTILE_SPEED 3 +#define LEVEL_UI_RATIO 0.05f diff --git a/src/enemies.cpp b/src/enemies.cpp index 2b6aa43..396c38c 100644 --- a/src/enemies.cpp +++ b/src/enemies.cpp @@ -4,6 +4,7 @@ #include "misc.hpp" #include "projectiles.hpp" #include "types.hpp" +#include #include #include @@ -15,13 +16,23 @@ enemy_type spawn_enemy(enemy_ai_type ai_type, Uint32 reload_time) { enemy.ship.rect = { static_cast(mode->w), - static_cast(get_random_num(0, mode->h - enemy.ship.texture->h)), + static_cast(get_random_num( + level_screen_limit.y, (level_screen_limit.y + level_screen_limit.h) - + enemy.ship.texture->h)), static_cast(enemy.ship.texture->w), static_cast(enemy.ship.texture->h)}; enemy.ship.gun_offset.x = 0; enemy.ship.gun_offset.y = 0.5f * enemy.ship.texture->h; enemy.size_multiplier = 1; + + if (ai_type == FLYER) { + enemy.target = {(level_screen_limit.x + level_screen_limit.w) - + enemy.ship.rect.w, + static_cast(level_screen_limit.y)}; + enemy.reload_time *= 2; + } + return enemy; } @@ -37,10 +48,15 @@ void step_enemy(enemy_type &e, ship_type &player, e.ship.rect.x -= 2; break; case 2: - e.ship.rect.y++; + if (e.ship.rect.y + e.ship.rect.h < + (level_screen_limit.h + level_screen_limit.y)) { + e.ship.rect.y++; + } break; case 3: - e.ship.rect.y--; + if (e.ship.rect.y > level_screen_limit.y) { + e.ship.rect.y--; + } break; } if (get_random_num(0, 1000) == 0 && @@ -49,19 +65,49 @@ void step_enemy(enemy_type &e, ship_type &player, spawn_projectile({e.ship.rect.x + e.ship.gun_offset.x, e.ship.rect.y + e.ship.gun_offset.y}, e.size_multiplier, 270, NORMAL_PROJECTILE_SPEED, - "assets/basic_projectile.svg", nullptr)); + "assets/basic_projectile.svg", nullptr, FOE)); } break; - case FLYER: - if (e.ship.rect.x != e.target.x && e.ship.rect.y != e.target.y) { - if (e.ship.rect.y < mode->h / 2) { - e.target.y = 0; + case FLYER: { + + float frames_to_complete_cycle = + (level_screen_limit.y + level_screen_limit.h) - level_screen_limit.y - + e.ship.rect.h / 5.0f; + float horizontal_speed = e.ship.rect.w / frames_to_complete_cycle; + + e.ship.rect.x -= horizontal_speed; + + e.target.x -= horizontal_speed; + float tolerance = 5.0f; + bool reached_target = std::abs(e.ship.rect.y - e.target.y) <= tolerance; + + if (reached_target) { + + if (e.target.y <= level_screen_limit.y + tolerance) { + + e.target.y = + (level_screen_limit.y + level_screen_limit.h) - e.ship.rect.h; } else { - e.target.y = mode->h - e.ship.rect.h; + e.target.y = level_screen_limit.y; } - e.target.x = e.ship.rect.x - (e.ship.rect.w / 2); } - break; + + if (e.ship.rect.y < e.target.y) { + e.ship.rect.y += 3; + } else if (e.ship.rect.y > e.target.y) { + e.ship.rect.y -= 3; + } + + if (SDL_GetTicks() - e.last_shot > e.reload_time) { + projectiles.push_back( + spawn_projectile({e.ship.rect.x + e.ship.gun_offset.x, + e.ship.rect.y + e.ship.gun_offset.y}, + e.size_multiplier, 270, NORMAL_PROJECTILE_SPEED, + "assets/basic_projectile.svg", nullptr, FOE)); + e.last_shot = SDL_GetTicks(); + } + } break; + case GUNNER: break; case ACE: diff --git a/src/game_logic.cpp b/src/game_logic.cpp index d869577..0fcfff6 100644 --- a/src/game_logic.cpp +++ b/src/game_logic.cpp @@ -1,15 +1,24 @@ #include "const.hpp" #include "enemies.hpp" +#include "macro.hpp" #include "main.hpp" #include "meth.hpp" #include "projectiles.hpp" +#include "text.hpp" #include "types.hpp" -#include +#include +#include +#include +#include +#include #include +#include +#include #include -// returns true on win and false on lose -bool play_level() { +// returns true on win and false on lose and score +std::tuple play_level(float score_multiplyer) { + Uint64 score = 0; ship_type player_ship = []() -> ship_type { SDL_Texture *player_ship_texture = nullptr; @@ -28,13 +37,15 @@ bool play_level() { main_sdl_session.renderer, player_ship_surface); SDL_DestroySurface(player_ship_surface); - SDL_FRect player_ship_rect = {0, 0, ship_width, ship_height}; + SDL_FRect player_ship_rect = {0, (mode->h / 2) - (ship_height / 2), + ship_width, ship_height}; SDL_FPoint gun_offset = {ship_width * 0.9f, ship_height * 0.9f}; return {player_ship_rect, gun_offset, player_ship_texture}; }(); bool running = true; + bool paused = false; Uint64 lastFrameTime = SDL_GetTicksNS(); float deltaTime = 0.0f; @@ -62,7 +73,8 @@ bool play_level() { if ((keystate[SDL_SCANCODE_DOWN] || keystate[SDL_SCANCODE_S]) && (SDL_GetTicks() - last_toggle_direction[0] > MIN_INPUT_DELAY_MOVEMENT)) { - if (player_ship.rect.y + player_ship.rect.h < mode->h) { + if (player_ship.rect.y + player_ship.rect.h < + (level_screen_limit.h + level_screen_limit.y)) { player_ship.rect.y += player_ship_speed; } last_toggle_direction[0] = SDL_GetTicks(); @@ -70,7 +82,7 @@ bool play_level() { if ((keystate[SDL_SCANCODE_UP] || keystate[SDL_SCANCODE_W]) && (SDL_GetTicks() - last_toggle_direction[1] > MIN_INPUT_DELAY_MOVEMENT)) { - if (player_ship.rect.y > 0) { + if (player_ship.rect.y > level_screen_limit.y) { player_ship.rect.y -= player_ship_speed; } last_toggle_direction[1] = SDL_GetTicks(); @@ -91,6 +103,11 @@ bool play_level() { } last_toggle_direction[3] = SDL_GetTicks(); } + static Uint32 last_pouse_tick; + if (keystate[SDL_SCANCODE_P] && SDL_GetTicks() - last_pouse_tick > 200) { + SDL_Delay(100000); + last_pouse_tick = SDL_GetTicks(); + } const SDL_MouseButtonFlags mousestate = SDL_GetMouseState(nullptr, nullptr); @@ -101,36 +118,71 @@ bool play_level() { spawn_projectile({player_ship.rect.x + player_ship.gun_offset.x, player_ship.rect.y + player_ship.gun_offset.y}, 1, 90, NORMAL_PROJECTILE_SPEED, - "assets/basic_projectile.svg", nullptr)); + "assets/basic_projectile.svg", nullptr, ALLY)); last_fire = SDL_GetTicks(); } } // chance to spawn enemy every frame - if (get_random_num(0, 10) == 0) { - enemies.push_back(spawn_enemy( - // static_cast(get_random_num(RANDOM, GUNNER)), - RANDOM, get_random_num(200, 1000))); + if (get_random_num(0, 500) == 0) { + enemies.push_back( + spawn_enemy(static_cast(get_random_num(RANDOM, 1)), + get_random_num(200, 1000))); } SDL_SetRenderDrawColor(main_sdl_session.renderer, 0, 0, 0, 255); SDL_RenderClear(main_sdl_session.renderer); + for (projectile &p : projectiles) { + if (p.rect.x > mode->w || p.rect.y > mode->h) { + projectiles.erase(projectiles.begin() + (&p - projectiles.data())); + } else { + if (p.type == ALLY) { + for (enemy_type &e : enemies) { + if (SDL_HasRectIntersectionFloat(&p.rect, &e.ship.rect)) { + // TODO play explosion or something + score += ((e.type + 1) * 10) * score_multiplyer; + enemies.erase(enemies.begin() + (&e - enemies.data())); + projectiles.erase(projectiles.begin() + + (&p - projectiles.data())); + goto skip_projectile_step; + } + } + } else { + if (SDL_HasRectIntersectionFloat(&p.rect, &player_ship.rect)) { + // TODO add hit points + return std::make_tuple(score, false); + } + } + step_projectile(p); + skip_projectile_step: + } + } for (enemy_type &e : enemies) { step_enemy(e, player_ship, projectiles); } - for (projectile &p : projectiles) { - if (p.rect.x > mode->w || p.rect.y > mode->h) { - projectiles.erase(projectiles.begin() + (&p - projectiles.data())); - } else { // TODO colision detection - step_projectile(p); - } - } - SDL_RenderTexture(main_sdl_session.renderer, player_ship.texture, nullptr, &player_ship.rect); + SDL_SetRenderDrawColor( + main_sdl_session.renderer, + HEX_TO_SDL_COLOR(0xffffffff)); // set white render color + + SDL_RenderLine( + main_sdl_session.renderer, level_screen_limit.x, level_screen_limit.y, + level_screen_limit.x + level_screen_limit.w, level_screen_limit.y); + + SDL_RenderLine(main_sdl_session.renderer, level_screen_limit.x, + level_screen_limit.y + level_screen_limit.h, + level_screen_limit.x + level_screen_limit.w, + level_screen_limit.y + level_screen_limit.h); + + { + std::string score_str = std::to_string(score); + vector_print({5, 5}, level_screen_limit.y - 10, score_str); + } + SDL_RenderPresent(main_sdl_session.renderer); const Uint64 frameTime = SDL_GetTicksNS() - frameStart; @@ -148,5 +200,5 @@ bool play_level() { } SDL_DestroyTexture(player_ship.texture); - return true; + return std::make_tuple(score, true); } \ No newline at end of file diff --git a/src/game_logic.hpp b/src/game_logic.hpp index 3219691..ca4a877 100644 --- a/src/game_logic.hpp +++ b/src/game_logic.hpp @@ -1,2 +1,4 @@ #pragma once -bool play_level(); \ No newline at end of file +#include +#include +std::tuple play_level(float score_multiplyer); \ No newline at end of file diff --git a/src/macro.hpp b/src/macro.hpp index 6d28118..36c5849 100644 --- a/src/macro.hpp +++ b/src/macro.hpp @@ -1,3 +1,9 @@ #pragma once -#define FIND_CENTER(rect) {rect.x + (rect.w / 2), rect.y + (rect.h / 2)} \ No newline at end of file +#define FIND_CENTER(rect) {rect.x + (rect.w / 2), rect.y + (rect.h / 2)} + +#define HEX_TO_SDL_COLOR(hex) \ + ((hex >> 24) & 0xFF), ((hex >> 16) & 0xFF), ((hex >> 8) & 0xFF), (hex & 0xFF) + +#define ANGLE_BETWEEN_POINTS_DEG(p1, p2) \ + (atan2(((p2).y - (p1).y), ((p2).x - (p1).x)) * 180.0 / M_PI) diff --git a/src/main.cpp b/src/main.cpp index 5e2349d..46b1938 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,11 @@ +#include "const.hpp" #include "game_logic.hpp" -#include "meth.hpp" #include "types.hpp" #include #include #include #include +#include #include #include #include @@ -12,15 +13,19 @@ #include #include #include -#include sdl_session main_sdl_session; const SDL_DisplayMode *mode; +SDL_Rect level_screen_limit; + int main(int argc, char *argv[]) { SDL_Init(SDL_INIT_VIDEO); mode = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay()); + level_screen_limit = {0, static_cast(mode->h * LEVEL_UI_RATIO), mode->w, + static_cast((mode->h - (mode->h * LEVEL_UI_RATIO) - + mode->h * LEVEL_UI_RATIO))}; main_sdl_session.window = SDL_CreateWindow("Vector Blow", mode->w, mode->h, SDL_WINDOW_FULLSCREEN); @@ -28,7 +33,9 @@ int main(int argc, char *argv[]) { main_sdl_session.renderer = SDL_CreateRenderer(main_sdl_session.window, "gpu,vulcan"); - play_level(); + auto [score, win] = play_level(1); + std::cout << "you got score: " << score + << " and you: " << (win ? "WON" : "LOST") << "\n"; SDL_DestroyRenderer(main_sdl_session.renderer); SDL_DestroyWindow(main_sdl_session.window); diff --git a/src/main.hpp b/src/main.hpp index 8b64ec1..ed0ee0f 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -1,4 +1,5 @@ #pragma once #include "types.hpp" extern sdl_session main_sdl_session; -extern SDL_DisplayMode *mode; \ No newline at end of file +extern SDL_DisplayMode *mode; +extern const SDL_Rect level_screen_limit; \ No newline at end of file diff --git a/src/projectiles.cpp b/src/projectiles.cpp index 7a4cc62..3fcabf1 100644 --- a/src/projectiles.cpp +++ b/src/projectiles.cpp @@ -13,11 +13,10 @@ void step_projectile(projectile p); projectile spawn_projectile(SDL_FPoint position, float size_multiplier, Angle angle, float speed, char *texture_file_name, - SDL_FRect *target) { + SDL_FRect *target, projectile_type type) { projectile p; - p.texture = - texture_from_SVG_file("assets/basic_projectile.svg", size_multiplier); + p.texture = texture_from_SVG_file(texture_file_name, size_multiplier); p.angle = angle; p.rect = {position.x, position.y, static_cast(p.texture->w), @@ -30,6 +29,7 @@ projectile spawn_projectile(SDL_FPoint position, float size_multiplier, p.target = target; p.guided = true; } + p.type = type; return p; } diff --git a/src/projectiles.hpp b/src/projectiles.hpp index 3042899..43fdf90 100644 --- a/src/projectiles.hpp +++ b/src/projectiles.hpp @@ -3,5 +3,5 @@ #include projectile spawn_projectile(SDL_FPoint position, float size_multiplier, Angle angle, float speed, char *texture_file_name, - SDL_FRect *target); + SDL_FRect *target, projectile_type type); void step_projectile(projectile &p); diff --git a/src/text.cpp b/src/text.cpp new file mode 100644 index 0000000..78db44b --- /dev/null +++ b/src/text.cpp @@ -0,0 +1,134 @@ +#include "main.hpp" +#include +#include +#include +#include +#include +#include +#include + +constexpr std::array, 10> characters = { + {{{{0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 1.0f}, + {0.0f, 1.0f}, + {0.0f, 0.0f}}}, // zero + {{{0.5f, 0.0f}, {0.5f, 1.0f}}}, // one + {{{0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 0.5f}, + {0.0f, 0.5f}, + {0.0f, 1.0f}, + {1.0f, 1.0f}}}, // two + {{{0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 0.5f}, + {0.0f, 0.5f}, + {1.0f, 0.5f}, + {1.0f, 1.0f}, + {0.0f, 1.0f}}}, // three + {{{0.0f, 0.0f}, + {0.0f, 0.5f}, + {1.0f, 0.5f}, + {1.0f, 0.0f}, + {1.0f, 0.5f}, + {1.0f, 1.0f}}}, // four + {{{1.0f, 0.0f}, + {0.0f, 0.0f}, + {0.0f, 0.5f}, + {1.0f, 0.5f}, + {1.0f, 1.0f}, + {0.0f, 1.0f}}}, // five + {{{0.0f, 0.0f}, + {0.0f, 0.5f}, + {1.0f, 0.5f}, + {1.0f, 1.0f}, + {0.0f, 1.0f}, + {0.0f, 0.5f}}}, // six + {{{0.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, 1.0f}}}, // seven + {{{0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 0.5f}, + {0.0f, 0.5f}, + {0.0f, 0.0f}, + {0.0f, 1.0f}, + {1.0f, 1.0f}, + {1.0f, 0.5f}}}, // eight + {{{0.0f, 0.0f}, + {0.0f, 0.5f}, + {1.0f, 0.5f}, + {1.0f, 0.0f}, + {0.0f, 0.0f}, + {1.0f, 0.0f}, + {1.0f, 1.0f}}}}}; // nine + +void multiply_FPoint_array(SDL_FPoint *dst, const SDL_FPoint *src, + SDL_FPoint origin, int multiplyer, + size_t num_points) { + for (size_t i = 0; i < num_points; i++) { + dst[i].x = origin.x + (src[i].x * multiplyer); + dst[i].y = origin.y + (src[i].y * multiplyer); + } +} + +void vector_print(SDL_FPoint pos, int height, std::string &s) { + std::array copybuffer; + int point_count = 0; + for (size_t i = 0; i < s.length(); i++) { + switch (s.data()[i]) { + case '0': + point_count = 5; + multiply_FPoint_array(copybuffer.data(), characters[0].data(), pos, + height, point_count); + + break; + case '1': + point_count = 2; + multiply_FPoint_array(copybuffer.data(), characters[1].data(), pos, + height, point_count); + break; + case '2': + point_count = 6; + multiply_FPoint_array(copybuffer.data(), characters[2].data(), pos, + height, point_count); + break; + case '3': + point_count = 7; + multiply_FPoint_array(copybuffer.data(), characters[3].data(), pos, + height, point_count); + break; + case '4': + point_count = 6; + multiply_FPoint_array(copybuffer.data(), characters[4].data(), pos, + height, point_count); + break; + case '5': + point_count = 6; + multiply_FPoint_array(copybuffer.data(), characters[5].data(), pos, + height, point_count); + break; + case '6': + point_count = 6; + multiply_FPoint_array(copybuffer.data(), characters[6].data(), pos, + height, point_count); + break; + case '7': + point_count = 3; + multiply_FPoint_array(copybuffer.data(), characters[7].data(), pos, + height, point_count); + break; + case '8': + point_count = 8; + multiply_FPoint_array(copybuffer.data(), characters[8].data(), pos, + height, point_count); + break; + case '9': + point_count = 7; + multiply_FPoint_array(copybuffer.data(), characters[9].data(), pos, + height, point_count); + break; + } + SDL_RenderLines(main_sdl_session.renderer, copybuffer.data(), point_count); + pos.x = pos.x + height + height / 6; + } +} \ No newline at end of file diff --git a/src/text.hpp b/src/text.hpp new file mode 100644 index 0000000..31a7584 --- /dev/null +++ b/src/text.hpp @@ -0,0 +1,4 @@ +#pragma once +#include +#include +void vector_print(SDL_FPoint pos, int height, std::string &s); \ No newline at end of file diff --git a/src/types.hpp b/src/types.hpp index b789e57..f31cce4 100644 --- a/src/types.hpp +++ b/src/types.hpp @@ -5,6 +5,8 @@ #include #include +enum projectile_type { ALLY, FOE }; + enum enemy_ai_type { RANDOM, FLYER, @@ -196,5 +198,6 @@ struct projectile { float speed; Angle angle = 0; bool guided; + projectile_type type; SDL_FRect *target; };