359 lines
12 KiB
C++
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;
|
|
}
|
|
} |