363 lines
11 KiB
C++
363 lines
11 KiB
C++
#include "KGame.h"
|
|
#include "SDL.h"
|
|
#include <SDL_ttf.h>
|
|
#include <nlohmann/json.hpp>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <fstream>
|
|
#include <vector>
|
|
#include "Utils.h"
|
|
#include "Constants.h"
|
|
#include "KCamera.h"
|
|
#include "KCirclePawn.h"
|
|
#include "KExit.h"
|
|
#include "KRectPawn.h"
|
|
#include "KTexture.h"
|
|
#include "KTile.h"
|
|
#include "KVector2d.h"
|
|
#include "KWall.h"
|
|
|
|
#ifndef M_PI
|
|
#define M_PI 3.14159265358979323846
|
|
#endif
|
|
|
|
namespace KapitanGame {
|
|
KGame::KGame() : Time(0), PreviousTime(0), Map()
|
|
{
|
|
//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();
|
|
|
|
if (TTF_Init() < 0)
|
|
{
|
|
throw std::runtime_error(Utils::StringFormat("SDL_TTF could not initialize! TTF_Error: %s", TTF_GetError()));
|
|
}
|
|
|
|
//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
|
|
Textures.clear();
|
|
Objects.clear();
|
|
Pawns.clear();
|
|
PlayerControllers.clear();
|
|
Fonts.clear();
|
|
TTF_Quit();
|
|
Input.Free();
|
|
|
|
//Destroy window
|
|
SDL_DestroyRenderer(Renderer);
|
|
SDL_DestroyWindow(Window);
|
|
Window = nullptr;
|
|
Renderer = nullptr;
|
|
|
|
//Quit SDL subsystems
|
|
SDL_Quit();
|
|
}
|
|
|
|
bool KGame::LoadLevel()
|
|
{
|
|
bool success = true;
|
|
Objects.clear();
|
|
FreePositions.clear();
|
|
/*std::ifstream levelFile;
|
|
levelFile.open("assets/levels/level" + std::to_string(LvlCounter) + ".txt");
|
|
if (levelFile.fail())
|
|
{
|
|
printf("Failed to load assets/levels/level%d.txt!\n", LvlCounter);
|
|
success = false;
|
|
}
|
|
else
|
|
{
|
|
int y = 0;
|
|
float mapWidth = 0;
|
|
std::string line;
|
|
while (std::getline(levelFile, line)) {
|
|
if (mapWidth < static_cast<float>(line.length() * Constants::TILE_WIDTH))
|
|
mapWidth = static_cast<float>(line.length()) * Constants::TILE_WIDTH;
|
|
for (auto i = 0ull; i < line.length(); ++i) {
|
|
KVector2D position{ static_cast<float>(i * Constants::TILE_WIDTH + Constants::TILE_WIDTH / 2) , static_cast<float>(y * Constants::TILE_HEIGHT + Constants::TILE_HEIGHT / 2) };
|
|
switch (line[i])
|
|
{
|
|
case '#':
|
|
Objects.emplace(std::make_pair(static_cast<int>(i), y), std::make_shared<KWall>(position, Textures["wall.bmp"]));
|
|
break;
|
|
case ' ':
|
|
FreePositions.emplace_back(position);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
++y;
|
|
}
|
|
const auto mapHeight = static_cast<float>(y * Constants::TILE_HEIGHT);
|
|
levelFile.close();
|
|
Map = { 0,0,mapWidth,mapHeight };
|
|
}*/
|
|
Map = { 0,0,Constants::SCREEN_WIDTH,Constants::SCREEN_HEIGHT };
|
|
return success;
|
|
}
|
|
|
|
void KGame::LoadPlayerPawn()
|
|
{
|
|
Pawns.clear();
|
|
KVector2D StartPosition{ 0.f, 0.f };
|
|
Pawns.emplace_back(std::make_shared<KCirclePawn>(StartPosition, Textures["P1.bmp"], PlayerControllers[static_cast<int>(KPlayer::Player1)]));
|
|
PlayerControllers[static_cast<int>(KPlayer::Player1)]->Possess(Pawns.back().get());
|
|
}
|
|
|
|
bool KGame::LoadMedia()
|
|
{
|
|
//Loading success flag
|
|
bool success = true;
|
|
|
|
for (auto player : Utils::KPlayerIterator())
|
|
{
|
|
std::string filename = "P" + std::to_string(static_cast<int>(player) + 1) + ".bmp";
|
|
Textures.emplace(filename, KTexture());
|
|
if (!Textures[filename].LoadFromFile("assets/textures/" + filename, Renderer))
|
|
{
|
|
Textures.erase(filename);
|
|
printf("Failed to load player texture image!\n");
|
|
success = false;
|
|
break;
|
|
}
|
|
|
|
PlayerControllers.emplace_back(std::make_shared<KPlayerController>(player, this));
|
|
PlayerControllers.back()->SetupInputBindings(Input);
|
|
}
|
|
Textures.emplace("wall.bmp", KTexture());
|
|
if (!Textures["wall.bmp"].LoadFromFile("assets/textures/wall.bmp", Renderer))
|
|
{
|
|
printf("Failed to load wall texture image!\n");
|
|
Textures.erase("wall.bmp");
|
|
success = false;
|
|
}
|
|
|
|
Fonts.emplace("PressStart2P-Regular", KFont());
|
|
if (!Fonts["PressStart2P-Regular"].LoadFromFile("assets/fonts/PressStart2P-Regular.ttf", 72))
|
|
{
|
|
printf("Failed to load PressStart2P-Regular font!\n");
|
|
Fonts.erase("PressStart2P-Regular");
|
|
success = false;
|
|
}
|
|
//Textures.emplace("Text_Score", Fonts["Roboto-Thin"].GetTextWithOutlineTexture("0:0", { 0xFF,0xFF,0xFF,0xFF }, { 0,0,0,0xFF }, 1, Renderer));
|
|
//Textures.emplace("Text_Winner", KTexture());
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
if (!LoadLevel())
|
|
success = false;
|
|
return success;
|
|
}
|
|
|
|
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;
|
|
|
|
KCamera camera(Map);
|
|
KCamera debugCamera(Map);
|
|
debugCamera.SetDebug(Map);
|
|
|
|
bool debug = false;
|
|
|
|
LoadPlayerPawn();
|
|
camera.Update(Pawns, Map);
|
|
|
|
Time = PreviousTime = SDL_GetTicks();
|
|
printf("\n");
|
|
|
|
//While application is running
|
|
while (!quit)
|
|
{
|
|
PreviousTime = Time;
|
|
Time = SDL_GetTicks();
|
|
float deltaTime = (Time - PreviousTime) * 0.001f;
|
|
|
|
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(Playing);
|
|
|
|
if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_F1))
|
|
{
|
|
debug = !debug;
|
|
}
|
|
if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_F2))
|
|
{
|
|
CollisionEnabled = !CollisionEnabled;
|
|
}
|
|
|
|
if (Playing) {
|
|
for (const auto& pc : PlayerControllers) {
|
|
pc->Update(deltaTime);
|
|
}
|
|
for (const auto& pawn : Pawns) {
|
|
if (!Playing) break;
|
|
pawn->MovementStep(deltaTime);
|
|
}
|
|
if (CollisionEnabled) {
|
|
for (const auto& pawn : Pawns) {
|
|
if (!Playing) break;
|
|
pawn->CollisionDetectionStep(Objects);
|
|
}
|
|
}
|
|
for (const auto& pawn : Pawns) {
|
|
if (!Playing) break;
|
|
pawn->CollisionDetectionWithMapStep(Map);
|
|
}
|
|
for (const auto& pawn : Pawns) {
|
|
if (!Playing) break;
|
|
pawn->CollisionDetectionWithMapStep(camera.GetPlayArea());
|
|
}
|
|
|
|
camera.Update(Pawns, Map);
|
|
|
|
}
|
|
|
|
if (!Playing)
|
|
{
|
|
if (Time > RestartTick)
|
|
{
|
|
LoadLevel();
|
|
LoadPlayerPawn();
|
|
debugCamera.SetDebug(Map);
|
|
camera.Update(Pawns, Map);
|
|
if (ShowWinner)
|
|
{
|
|
for (auto i : Utils::KPlayerIterator())
|
|
{
|
|
Scores[static_cast<int>(i)] = 0;
|
|
}
|
|
//Textures.erase("Text_Score");
|
|
//Textures.emplace("Text_Score", Fonts["Roboto-Thin"].GetTextWithOutlineTexture(Utils::StringFormat("%d:%d", Scores[0], Scores[1]), { 0xFF,0xFF,0xFF,0xFF }, { 0,0,0,0xFF }, 1, Renderer));
|
|
ShowWinner = false;
|
|
}
|
|
Playing = true;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
//Clear screen
|
|
SDL_SetRenderDrawColor(Renderer, 66, 135, 245, 0xFF);
|
|
SDL_RenderClear(Renderer);
|
|
|
|
const auto& cameraInUse = debug ? debugCamera : camera;
|
|
|
|
|
|
if (Playing) {
|
|
for (const auto& object : Objects)
|
|
object.second->Render(Renderer, cameraInUse);
|
|
for (const auto& pawn : Pawns) {
|
|
pawn->Render(Renderer, cameraInUse);
|
|
}
|
|
if (const auto exit = Exit.lock()) {
|
|
const float exitX = camera.GetViewport().x + Constants::SCREEN_WIDTH / 2.f - Textures["arrow.bmp"].GetWidth() / 2.f - exit->GetPosition().X + exit->GetCollider()->GetWidth() / 2.f;
|
|
const float exitY = camera.GetViewport().y + 25.f - exit->GetPosition().Y + exit->GetCollider()->GetHeight() / 2.f;
|
|
const double degrees = atan2(-exitX, exitY) * (180.f / M_PI);
|
|
Textures["arrow.bmp"].RenderEx(Renderer, Constants::SCREEN_WIDTH / 2.f - Textures["arrow.bmp"].GetWidth() / 2.f, 25.f, degrees);
|
|
}
|
|
}
|
|
if (ShowWinner)
|
|
{
|
|
Textures["Text_Winner"].Render(Renderer, Constants::SCREEN_WIDTH / 2.f - Textures["Text_Winner"].GetWidth() / 2.f, 25.f);
|
|
}
|
|
|
|
Textures["Text_Score"].Render(Renderer, Constants::SCREEN_WIDTH / 2.f - Textures["Text_Score"].GetWidth() / 2.f, Constants::SCREEN_HEIGHT - 10.f - Textures["Text_Score"].GetHeight());
|
|
if (debug)
|
|
{
|
|
SDL_SetRenderDrawColor(Renderer, 0x00, 0x00, 0x00, 0xFF);
|
|
SDL_RenderDrawLineF(Renderer, 0, Constants::SCREEN_HEIGHT / 2.f, Constants::SCREEN_WIDTH, Constants::SCREEN_HEIGHT / 2.f);
|
|
SDL_RenderDrawLineF(Renderer, Constants::SCREEN_WIDTH / 2.f, 0, Constants::SCREEN_WIDTH / 2.f, Constants::SCREEN_HEIGHT);
|
|
SDL_SetRenderDrawColor(Renderer, 0x00, 0xFF, 0x00, 0xFF);
|
|
|
|
SDL_RenderDrawPointF(Renderer, camera.GetFocusPoint().X * debugCamera.GetScale(), camera.GetFocusPoint().Y * debugCamera.GetScale());
|
|
SDL_SetRenderDrawColor(Renderer, 0xFF, 0x00, 0x00, 0xFF);
|
|
SDL_FRect debugViewport = { camera.GetViewport().x * debugCamera.GetScale(),
|
|
camera.GetViewport().y * debugCamera.GetScale(),
|
|
camera.GetViewport().w * debugCamera.GetScale(),
|
|
camera.GetViewport().h * debugCamera.GetScale() };
|
|
SDL_RenderDrawRectF(Renderer, &debugViewport);
|
|
SDL_FRect debug2Viewport = { camera.GetPlayArea().x * debugCamera.GetScale(),
|
|
camera.GetPlayArea().y * debugCamera.GetScale(),
|
|
camera.GetPlayArea().w * debugCamera.GetScale(),
|
|
camera.GetPlayArea().h * debugCamera.GetScale() };
|
|
SDL_SetRenderDrawColor(Renderer, 0x00, 0xFF, 0x00, 0xFF);
|
|
SDL_RenderDrawRectF(Renderer, &debug2Viewport);
|
|
}
|
|
|
|
//Update screen
|
|
SDL_RenderPresent(Renderer);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
}
|