basic gameplay

This commit is contained in:
PoliEcho 2025-08-25 09:35:22 +02:00
parent a1fa102c4a
commit fac4a603f1
12 changed files with 298 additions and 42 deletions

View File

@ -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

View File

@ -4,6 +4,7 @@
#include "misc.hpp"
#include "projectiles.hpp"
#include "types.hpp"
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_render.h>
#include <vector>
@ -15,13 +16,23 @@ enemy_type spawn_enemy(enemy_ai_type ai_type, Uint32 reload_time) {
enemy.ship.rect = {
static_cast<float>(mode->w),
static_cast<float>(get_random_num(0, mode->h - enemy.ship.texture->h)),
static_cast<float>(get_random_num(
level_screen_limit.y, (level_screen_limit.y + level_screen_limit.h) -
enemy.ship.texture->h)),
static_cast<float>(enemy.ship.texture->w),
static_cast<float>(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<float>(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:

View File

@ -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 <climits>
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_scancode.h>
#include <SDL3/SDL_stdinc.h>
#include <SDL3/SDL_timer.h>
#include <cstring>
#include <string>
#include <tuple>
#include <vector>
// returns true on win and false on lose
bool play_level() {
// returns true on win and false on lose and score
std::tuple<Uint64, bool> 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<enemy_ai_type>(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<enemy_ai_type>(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);
}

View File

@ -1,2 +1,4 @@
#pragma once
bool play_level();
#include <SDL3/SDL_stdinc.h>
#include <tuple>
std::tuple<Uint64, bool> play_level(float score_multiplyer);

View File

@ -1,3 +1,9 @@
#pragma once
#define FIND_CENTER(rect) {rect.x + (rect.w / 2), rect.y + (rect.h / 2)}
#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)

View File

@ -1,10 +1,11 @@
#include "const.hpp"
#include "game_logic.hpp"
#include "meth.hpp"
#include "types.hpp"
#include <SDL3/SDL.h>
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_iostream.h>
#include <SDL3/SDL_mouse.h>
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_render.h>
#include <SDL3/SDL_scancode.h>
#include <SDL3/SDL_stdinc.h>
@ -12,15 +13,19 @@
#include <SDL3/SDL_timer.h>
#include <SDL3/SDL_video.h>
#include <SDL3_image/SDL_image.h>
#include <iomanip>
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<int>(mode->h * LEVEL_UI_RATIO), mode->w,
static_cast<int>((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);

View File

@ -1,4 +1,5 @@
#pragma once
#include "types.hpp"
extern sdl_session main_sdl_session;
extern SDL_DisplayMode *mode;
extern SDL_DisplayMode *mode;
extern const SDL_Rect level_screen_limit;

View File

@ -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<float>(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;
}

View File

@ -3,5 +3,5 @@
#include <SDL3/SDL_rect.h>
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);

134
src/text.cpp Normal file
View File

@ -0,0 +1,134 @@
#include "main.hpp"
#include <SDL3/SDL_oldnames.h>
#include <SDL3/SDL_rect.h>
#include <SDL3/SDL_render.h>
#include <array>
#include <cstddef>
#include <cstring>
#include <string>
constexpr std::array<std::array<SDL_FPoint, 9>, 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<SDL_FPoint, 9> 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;
}
}

4
src/text.hpp Normal file
View File

@ -0,0 +1,4 @@
#pragma once
#include <SDL3/SDL_rect.h>
#include <string>
void vector_print(SDL_FPoint pos, int height, std::string &s);

View File

@ -5,6 +5,8 @@
#include <cmath>
#include <iostream>
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;
};