250 lines
6.6 KiB
C++
250 lines
6.6 KiB
C++
#include "KGame.h"
|
|
#include "SDL.h"
|
|
#include <nlohmann/json.hpp>
|
|
#include <cstdio>
|
|
#include <cmath>
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <vector>
|
|
|
|
#include "Utils.h"
|
|
#include "Constants.h"
|
|
#include "KTexture.h"
|
|
#include "KTile.h"
|
|
#include "KVector2d.h"
|
|
|
|
namespace KapitanGame {
|
|
KGame::KGame() : HasSeparation(true), HasCollision(true) {
|
|
//Initialize SDL
|
|
if (SDL_Init(SDL_INIT_VIDEO) < 0)
|
|
{
|
|
throw std::runtime_error(Utils::StringFormat("SDL could not initialize! SDL_Error: %s", SDL_GetError()));
|
|
}
|
|
//Set texture filtering to linear
|
|
if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1"))
|
|
{
|
|
printf("Warning: Linear texture filtering not enabled!\n");
|
|
}
|
|
|
|
Input.Init();
|
|
|
|
//Create window
|
|
Window = SDL_CreateWindow(Constants::WINDOW_TITLE, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Constants::SCREEN_WIDTH, Constants::SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
|
|
if (Window == nullptr)
|
|
{
|
|
throw std::runtime_error(Utils::StringFormat("Window could not be created! SDL_Error: %s", SDL_GetError()));
|
|
}
|
|
//Create renderer for window
|
|
Renderer = SDL_CreateRenderer(Window, -1, SDL_RENDERER_ACCELERATED);
|
|
if (Renderer == nullptr)
|
|
{
|
|
throw std::runtime_error(Utils::StringFormat("Renderer could not be created! SDL Error: %s", SDL_GetError()));
|
|
}
|
|
//Initialize renderer color
|
|
SDL_SetRenderDrawColor(Renderer, 0, 0xFF, 0, 0xFF);
|
|
}
|
|
|
|
KGame::~KGame() {
|
|
//Free loaded images
|
|
|
|
CircleTexture.Free();
|
|
|
|
Input.Free();
|
|
|
|
//Destroy window
|
|
SDL_DestroyRenderer(Renderer);
|
|
SDL_DestroyWindow(Window);
|
|
Window = nullptr;
|
|
Renderer = nullptr;
|
|
|
|
//Quit SDL subsystems
|
|
SDL_Quit();
|
|
}
|
|
|
|
bool KGame::LoadMedia()
|
|
{
|
|
//Loading success flag
|
|
bool success = true;
|
|
|
|
if (!PlayerTexture.LoadFromFile("assets/textures/P1.bmp", Renderer))
|
|
{
|
|
printf("Failed to load player texture image!\n");
|
|
success = false;
|
|
}
|
|
if (!PlayerTexture2.LoadFromFile("assets/textures/P2.bmp", Renderer))
|
|
{
|
|
printf("Failed to load player texture image!\n");
|
|
success = false;
|
|
}
|
|
if (!WallTexture.LoadFromFile("assets/textures/wall.bmp", Renderer))
|
|
{
|
|
printf("Failed to load wall texture image!\n");
|
|
success = false;
|
|
}
|
|
if (!ExitTexture.LoadFromFile("assets/textures/exit.bmp", Renderer))
|
|
{
|
|
printf("Failed to load exit texture image!\n");
|
|
success = false;
|
|
}
|
|
|
|
std::ifstream configFile("config.json");
|
|
if (configFile.fail()) {
|
|
printf("Failed to load config.json!\n");
|
|
success = false;
|
|
}
|
|
else {
|
|
nlohmann::json configJson;
|
|
configFile >> configJson;
|
|
configFile.close();
|
|
for (auto& player : configJson["Input"].items()) {
|
|
const auto playerEnum = Utils::GetPlayerFromString(player.key());
|
|
for (auto& axisMapping : player.value()["AxisMappings"]) {
|
|
Input.AddAxisMapping(axisMapping["AxisName"].get<std::string>(), axisMapping["Scale"].get<float>(), axisMapping["Key"].get<std::string>(), playerEnum);
|
|
}
|
|
for (auto& actionMapping : player.value()["ActionMappings"]) {
|
|
Input.AddActionMapping(actionMapping["ActionName"].get<std::string>(), actionMapping["Key"].get<std::string>(), playerEnum);
|
|
}
|
|
}
|
|
}
|
|
|
|
Tiles.clear();
|
|
std::ifstream levelFile;
|
|
levelFile.open("assets/level1.txt");
|
|
if (levelFile.fail())
|
|
{
|
|
printf("Failed to load assets/level1.txt!\n");
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
int y = 0;
|
|
std::string line;
|
|
while (std::getline(levelFile, line)) {
|
|
if (MapWidth < static_cast<int>(line.length() * Constants::TILE_WIDTH))
|
|
MapWidth = static_cast<int>(line.length()) * Constants::TILE_WIDTH;
|
|
for (auto i = 0ull; i < line.length(); ++i) {
|
|
auto type = TileType::Default;
|
|
switch (line[i])
|
|
{
|
|
case '#':
|
|
type = TileType::Wall;
|
|
break;
|
|
default:
|
|
continue;
|
|
}
|
|
Tiles.emplace_back(i * Constants::TILE_WIDTH, y * Constants::TILE_HEIGHT, type);
|
|
}
|
|
++y;
|
|
}
|
|
MapHeight = y * Constants::TILE_HEIGHT;
|
|
levelFile.close();
|
|
}
|
|
return success;
|
|
}
|
|
|
|
SDL_Texture* KGame::LoadTexture(const std::string& path) const {
|
|
//The final optimized image
|
|
SDL_Texture* newTexture = nullptr;
|
|
|
|
//Load image at specified path
|
|
SDL_Surface* loadedSurface = SDL_LoadBMP(path.c_str());
|
|
if (loadedSurface == nullptr)
|
|
{
|
|
printf("Unable to load image %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
|
|
}
|
|
else
|
|
{
|
|
//Create texture from surface pixels
|
|
newTexture = SDL_CreateTextureFromSurface(Renderer, loadedSurface);
|
|
if (newTexture == nullptr)
|
|
{
|
|
printf("Unable to optimize image %s! SDL Error: %s\n", path.c_str(), SDL_GetError());
|
|
}
|
|
|
|
//Get rid of old loaded surface
|
|
SDL_FreeSurface(loadedSurface);
|
|
}
|
|
|
|
return newTexture;
|
|
}
|
|
|
|
int KGame::Run(int argc, char* args[])
|
|
{
|
|
//Load media
|
|
if (!LoadMedia())
|
|
{
|
|
printf("Failed to load media!\n");
|
|
}
|
|
else
|
|
{
|
|
//Main loop flag
|
|
bool quit = false;
|
|
|
|
//Event handler
|
|
SDL_Event e;
|
|
|
|
SDL_Rect map{ 0,0,Constants::SCREEN_WIDTH, Constants::SCREEN_HEIGHT };
|
|
|
|
for (int i = 0; i < Constants::CIRCLES_COUNT; ++i) {
|
|
KVector2D position(Utils::RandomNumber() * (Constants::SCREEN_WIDTH - 2 * CircleTexture.GetWidth()) + CircleTexture.GetWidth(), Utils::RandomNumber() * (Constants::SCREEN_HEIGHT - 2 * CircleTexture.GetHeight()) + CircleTexture.GetHeight());
|
|
Circles.emplace_back(position, KVector2D(100.f, 100.f), CircleTexture.GetWidth() / 2, CircleTexture);
|
|
}
|
|
uint32_t previousTime;
|
|
uint32_t time = previousTime = SDL_GetTicks();
|
|
printf("\n");
|
|
|
|
//While application is running
|
|
while (!quit)
|
|
{
|
|
previousTime = time;
|
|
time = SDL_GetTicks();
|
|
|
|
Input.HandleInputPreEvents();
|
|
|
|
//Handle events on queue
|
|
while (SDL_PollEvent(&e))
|
|
{
|
|
//User requests quit
|
|
if (e.type == SDL_QUIT)
|
|
{
|
|
quit = true;
|
|
continue;
|
|
}
|
|
Input.HandleEvent(e);
|
|
}
|
|
Input.HandleInputPostEvents();
|
|
|
|
if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_S) || Input.IsControllerButtonPressed(Controllers::Controller1, SDL_CONTROLLER_BUTTON_START))
|
|
{
|
|
HasSeparation = !HasSeparation;
|
|
}
|
|
if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_C) || Input.IsControllerButtonPressed(Controllers::Controller1, SDL_CONTROLLER_BUTTON_BACK))
|
|
{
|
|
HasCollision = !HasCollision;
|
|
}
|
|
|
|
|
|
for (auto& circle : Circles)
|
|
circle.CollisionDetectionStep(Circles, HasSeparation, HasCollision);
|
|
|
|
for (auto& circle : Circles)
|
|
circle.CollisionDetectionWithMapStep(map);
|
|
|
|
for (auto& circle : Circles)
|
|
circle.MovementStep(static_cast<float>(time - previousTime) / 1000.f);
|
|
|
|
//Clear screen
|
|
SDL_SetRenderDrawColor(Renderer, 0, 0xFF, 0, 0xFF);
|
|
SDL_RenderClear(Renderer);
|
|
|
|
for (auto& circle : Circles)
|
|
circle.Render(Renderer);
|
|
|
|
//Update screen
|
|
SDL_RenderPresent(Renderer);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
} |