2dkg/2dgk_5/2dgk_5/KGame.cpp

359 lines
12 KiB
C++

#include "KGame.h"
#include "SDL.h"
#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() {
//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
for (int i = 0; i <= static_cast<int>(TileType::Dot); ++i)
TileTextures[i].Free();
PlayerTexture.Free();
PlayerTexture2.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("textures/player.bmp", Renderer))
{
printf("Failed to load player texture image!\n");
success = false;
}
if (!PlayerTexture2.LoadFromFile("textures/player2.bmp", Renderer))
{
printf("Failed to load player texture image!\n");
success = false;
}
for (int i = static_cast<int>(TileType::Default); i <= static_cast<int>(TileType::Dot); ++i)
{
if (!TileTextures[i].LoadFromFile("textures/0" + std::to_string(i) + ".bmp", Renderer))
{
printf("Failed to load 0%d texture image!\n", i);
success = false;
}
}
Tiles.clear();
std::ifstream levelFile;
levelFile.open("level.txt");
if (levelFile.fail())
{
printf("Failed to load level.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::VerticalWall;
break;
case '-':
type = TileType::HorizontalWall;
break;
case '.':
type = TileType::Dot;
break;
default:;
}
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 viewport;
viewport.x = Constants::SCREEN_WIDTH / 2;
viewport.y = Constants::SCREEN_HEIGHT / 2;
viewport.w = Constants::SCREEN_WIDTH;
viewport.h = Constants::SCREEN_HEIGHT;
KVector2D playerOnePosition(0, 0);
KVector2D playerOneInput(0, 0);
KVector2D playerOneVelocity(0, 0);
KVector2D playerTwoPosition(0, 0);
KVector2D playerTwoInput(0, 0);
KVector2D playerTwoVelocity(0, 0);
SDL_Rect playerAreaModeGauntlet;
playerAreaModeGauntlet.x = 0;
playerAreaModeGauntlet.y = 0;
playerAreaModeGauntlet.w = Constants::SCREEN_WIDTH - Constants::WINDOW_DEAD_ZONE;
playerAreaModeGauntlet.h = Constants::SCREEN_HEIGHT - Constants::WINDOW_DEAD_ZONE;
SDL_Rect playerAreaModeZoom;
playerAreaModeZoom.x = 0;
playerAreaModeZoom.y = 0;
playerAreaModeZoom.w = (MapWidth <= MapHeight ? MapWidth : static_cast<int>(static_cast<float>(MapHeight) * Constants::SCREEN_RATIO)) - Constants::WINDOW_DEAD_ZONE;
playerAreaModeZoom.h = (MapHeight <= MapWidth ? MapHeight : static_cast<int>(static_cast<float>(MapWidth) / Constants::SCREEN_RATIO)) - Constants::WINDOW_DEAD_ZONE;
float scale = 1.f;
//Normalized direction
int xDir = 0;
int yDir = 0;
uint32_t time, previousTime;
time = previousTime = SDL_GetTicks();
printf("\n");
bool zoom = false;
//While application is running
while (!quit)
{
previousTime = time;
time = SDL_GetTicks();
playerOneInput.X = 0;
playerOneInput.Y = 0;
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();
playerTwoInput.X = Input.GetControllerAxis(Controllers::Controller1, SDL_CONTROLLER_AXIS_LEFTX);
playerTwoInput.Y = Input.GetControllerAxis(Controllers::Controller1, SDL_CONTROLLER_AXIS_LEFTY);
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_W))
{
playerOneInput.Y = -1;
}
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_S))
{
playerOneInput.Y = 1;
}
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_A))
{
playerOneInput.X = -1;
}
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_D))
{
playerOneInput.X = 1;
}
playerOneInput.Normalize();
playerOneVelocity = playerOneInput * Constants::SPEED * (1 - Constants::SMOOTH) + playerOneVelocity * Constants::SMOOTH;
playerOnePosition += playerOneVelocity;
if (Input.IsKeyboardButtonPressed(SDL_SCANCODE_F))
{
zoom = !zoom;
}
const auto& playerArea = zoom ? playerAreaModeZoom : playerAreaModeGauntlet;
// keep player one near player two
if (playerOnePosition.X < playerTwoPosition.X - playerArea.w) playerOnePosition.X = playerTwoPosition.X - playerArea.w;
if (playerOnePosition.X > playerTwoPosition.X + playerArea.w) playerOnePosition.X = playerTwoPosition.X + playerArea.w;
if (playerOnePosition.Y < playerTwoPosition.Y - playerArea.h) playerOnePosition.Y = playerTwoPosition.Y - playerArea.h;
if (playerOnePosition.Y > playerTwoPosition.Y + playerArea.h) playerOnePosition.Y = playerTwoPosition.Y + playerArea.h;
// keep player one in map
if (playerOnePosition.X < 0) playerOnePosition.X = 0;
if (playerOnePosition.X > MapWidth - PlayerTexture.GetWidth()) playerOnePosition.X = (MapWidth - PlayerTexture.GetWidth());
if (playerOnePosition.Y < 0) playerOnePosition.Y = (0);
if (playerOnePosition.Y > MapHeight - PlayerTexture.GetHeight()) playerOnePosition.Y = (MapHeight - PlayerTexture.GetHeight());
//playerTwoInput.Normalize();
//printf("\r \rX:%f, Y:%f", playerTwoInput.x, playerTwoInput.y);
playerTwoVelocity = playerTwoInput * Constants::SPEED * (1 - Constants::SMOOTH) + playerTwoVelocity * Constants::SMOOTH;
playerTwoPosition += playerTwoVelocity;
if (playerTwoPosition.X < playerOnePosition.X - static_cast<float>(playerArea.w)) playerTwoPosition.X = playerOnePosition.X - playerArea.w;
if (playerTwoPosition.X > playerOnePosition.X + playerArea.w) playerTwoPosition.X = playerOnePosition.X + playerArea.w;
if (playerTwoPosition.Y < playerOnePosition.Y - playerArea.h) playerTwoPosition.Y = playerOnePosition.Y - playerArea.h;
if (playerTwoPosition.Y > playerOnePosition.Y + playerArea.h) playerTwoPosition.Y = playerOnePosition.Y + playerArea.h;
if (playerTwoPosition.X < 0) playerTwoPosition.X = 0;
if (playerTwoPosition.X > MapWidth - PlayerTexture.GetWidth()) playerTwoPosition.X = MapWidth - PlayerTexture.GetWidth();
if (playerTwoPosition.Y < 0) playerTwoPosition.Y = 0;
if (playerTwoPosition.Y > MapHeight - PlayerTexture.GetHeight()) playerTwoPosition.Y = MapHeight - PlayerTexture.GetHeight();
scale = 1.f;
if (zoom)
{
float xDist = std::abs(playerTwoPosition.X - playerOnePosition.X) - playerAreaModeGauntlet.w;
float yDist = std::abs(playerTwoPosition.Y - playerOnePosition.Y) - playerAreaModeGauntlet.h;
float xMaxDist = playerAreaModeZoom.w - playerAreaModeGauntlet.w;
float yMaxDist = playerAreaModeZoom.h - playerAreaModeGauntlet.h;
float t1 = Utils::Clamp(xDist / xMaxDist);
float t2 = Utils::Clamp(yDist / yMaxDist);
float t = std::max(std::max(t1, 0.f), std::max(t2, 0.f));
scale = Utils::Lerp(scale, std::max(Constants::SCREEN_WIDTH * 1.0f / MapWidth, Constants::SCREEN_HEIGHT * 1.0f / MapHeight), t);
//printf("\rscale: %f, t1: %f, t2: %f, t: %f", scale, t1, t2, t);
}
KVector2D focusPoint = (playerOnePosition + playerTwoPosition + KVector2D(PlayerTexture.GetWidth(), PlayerTexture.GetHeight())+KVector2D(Constants::WINDOW_DEAD_ZONE, Constants::WINDOW_DEAD_ZONE)/2)*scale/2;
if (Input.IsKeyboardButtonHeld(SDL_SCANCODE_Q))
{
focusPoint = KVector2D(MapWidth / 2.f, MapHeight / 2.f);
}
//viewport.x += clamp((time - previous_time) * .05f) * (focusPoint.x - SCREEN_WIDTH / 2 - viewport.x);
viewport.x = Utils::Lerp(viewport.x, static_cast<int>(focusPoint.X - Constants::SCREEN_WIDTH / 2.f), static_cast<float>(time - previousTime) * .05f);
if ((focusPoint.X - Constants::SCREEN_WIDTH / 2.f) - static_cast<float>(viewport.x) <= 0.005f)
{
viewport.x = static_cast<int>(focusPoint.X - Constants::SCREEN_WIDTH / 2.f)/scale;
}
viewport.y = Utils::Lerp(viewport.y, static_cast<int>(focusPoint.Y - Constants::SCREEN_HEIGHT / 2.f), static_cast<float>(time - previousTime) * .05f);
if (focusPoint.Y - static_cast<float>(Constants::SCREEN_HEIGHT) / 2.0f - static_cast<float>(viewport.y) <= 0.005f)
{
viewport.y = static_cast<int>(focusPoint.Y - Constants::SCREEN_HEIGHT / 2.f) / scale;
}
if (viewport.x < 0)
viewport.x = 0;
if (viewport.y < 0)
viewport.y = 0;
if (viewport.x > MapWidth - static_cast<int>(static_cast<float>(viewport.w) / scale))
viewport.x = MapWidth - static_cast<int>(static_cast<float>(viewport.w) / scale);
if (viewport.y > MapHeight - static_cast<int>(static_cast<float>(viewport.h) / scale))
viewport.y = MapHeight - static_cast<int>(static_cast<float>(viewport.h) / scale);
//printf("\rp1(%f, %f), p2(%f, %f), viewport:(%d, %d, %d, %d), map(%d, %d), focus(%f, %f)", playerOnePosition.X * scale, playerOnePosition.Y * scale, playerTwoPosition.X * scale, playerTwoPosition.Y * scale, static_cast<int>(viewport.x * scale), static_cast<int>(viewport.y * scale), static_cast<int>(static_cast<float>(viewport.w) / scale), static_cast<int>(static_cast<float>(viewport.h) / scale), MapWidth, MapHeight, focusPoint.X, focusPoint.Y);
//Clear screen
SDL_SetRenderDrawColor(Renderer, 0, 0xFF, 0, 0xFF);
SDL_RenderClear(Renderer);
for (auto& tile : Tiles)
tile.Render(Renderer, TileTextures, viewport, scale);
PlayerTexture.Render(Renderer, playerOnePosition.X - viewport.x, playerOnePosition.Y - viewport.y, nullptr, scale);
PlayerTexture2.Render(Renderer, playerTwoPosition.X - viewport.x, playerTwoPosition.Y - viewport.y, nullptr, scale);
//Update screen
SDL_RenderPresent(Renderer);
}
}
return 0;
}
}