EightBit/Gaming/src/Game.cpp

286 lines
7.3 KiB
C++

#include "stdafx.h"
#include "../inc/Game.h"
#include "../inc/GameController.h"
namespace Gaming {
Game::Game() {}
Game::~Game() {}
int Game::windowWidth() const noexcept {
return displayWidth() * displayScale();
}
int Game::windowHeight() const noexcept {
return displayHeight() * displayScale();
}
int Game::displayWidth() const noexcept {
return rasterWidth();
}
int Game::displayHeight() const noexcept {
return rasterHeight();
}
void Game::raisePOWER() noexcept {
Device::raisePOWER();
m_window.reset(::SDL_CreateWindow(
title().c_str(),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
windowWidth(), windowHeight(),
SDL_WINDOW_SHOWN), ::SDL_DestroyWindow);
if (m_window == nullptr)
SDLWrapper::throwSDLException("Unable to create window: ");
::SDL_DisplayMode mode;
SDLWrapper::verifySDLCall(
::SDL_GetWindowDisplayMode(m_window.get(), &mode),
"Unable to obtain window information: ");
Uint32 rendererFlags = 0;
m_vsync = useVsync();
if (m_vsync) {
const auto required = fps();
if (std::abs(required - mode.refresh_rate) < 0.001) {
rendererFlags |= SDL_RENDERER_PRESENTVSYNC;
::SDL_Log("Attempting to use SDL_RENDERER_PRESENTVSYNC");
} else {
m_vsync = false;
::SDL_LogWarn(SDL_LOG_CATEGORY_APPLICATION, "Display refresh rate is incompatible with required rate (%f)", required);
}
}
m_renderer.reset(::SDL_CreateRenderer(m_window.get(), -1, rendererFlags), ::SDL_DestroyRenderer);
if (m_renderer == nullptr)
SDLWrapper::throwSDLException("Unable to create renderer: ");
::SDL_RendererInfo info;
SDLWrapper::verifySDLCall(
::SDL_GetRendererInfo(m_renderer.get(), &info),
"Unable to obtain renderer information");
if (m_vsync) {
if ((info.flags & SDL_RENDERER_PRESENTVSYNC) == 0) {
::SDL_LogWarn(::SDL_LOG_CATEGORY_APPLICATION, "Renderer does not support VSYNC, reverting to timed delay loop.");
m_vsync = false;
}
}
m_pixelFormat.reset(::SDL_AllocFormat(m_pixelType), ::SDL_FreeFormat);
if (m_pixelFormat == nullptr)
SDLWrapper::throwSDLException("Unable to allocate pixel format: ");
configureBackground();
createBitmapTexture();
m_frames = 0UL;
m_startTicks = ::SDL_GetTicks();
}
void Game::configureBackground() const {
SDLWrapper::verifySDLCall(
::SDL_SetRenderDrawColor(m_renderer.get(), 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE),
"Unable to set render draw colour");
}
void Game::createBitmapTexture() {
m_bitmapTexture.reset(::SDL_CreateTexture(m_renderer.get(), m_pixelType, SDL_TEXTUREACCESS_STREAMING, rasterWidth(), rasterHeight()), ::SDL_DestroyTexture);
if (m_bitmapTexture == nullptr)
SDLWrapper::throwSDLException("Unable to create bitmap texture");
}
void Game::runLoop() {
while (powered()) {
update();
draw();
maybeSynchronise();
}
}
void Game::update() {
handleEvents();
runVerticalBlank();
runRasterLines();
}
void Game::handleEvents() {
::SDL_Event e;
while (::SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT:
lowerPOWER();
break;
case SDL_KEYDOWN:
handleKeyDown(e.key.keysym.sym);
break;
case SDL_KEYUP:
handleKeyUp(e.key.keysym.sym);
break;
case SDL_JOYBUTTONDOWN:
handleJoyButtonDown(e.jbutton);
break;
case SDL_JOYBUTTONUP:
handleJoyButtonUp(e.jbutton);
break;
case SDL_CONTROLLERBUTTONDOWN:
handleControllerButtonDown(e.cbutton);
break;
case SDL_CONTROLLERBUTTONUP:
handleControllerButtonUp(e.cbutton);
break;
case SDL_JOYDEVICEADDED:
addJoystick(e);
break;
case SDL_JOYDEVICEREMOVED:
removeJoystick(e);
break;
}
}
}
void Game::draw() {
updateTexture();
copyTexture();
displayTexture();
}
bool Game::maybeSynchronise() {
++m_frames;
const bool synchronising = !m_vsync;
if (synchronising)
synchronise();
return synchronising;
}
void Game::synchronise() {
const auto elapsedTicks = ::SDL_GetTicks() - m_startTicks;
const auto neededTicks = (m_frames / fps()) * 1000.0;
const auto sleepNeeded = (int)(neededTicks - elapsedTicks);
if (sleepNeeded > 0)
::SDL_Delay(sleepNeeded);
}
void Game::removeJoystick(SDL_Event& e) {
const auto which = e.jdevice.which;
const auto found = m_gameControllers.find(which);
SDL_assert(found != m_gameControllers.end());
auto& controller = found->second;
const auto joystickId = controller->getJoystickId();
m_mappedControllers.erase(joystickId);
m_gameControllers.erase(which);
SDL_Log("Joystick device %d removed (%zd controllers)", which, m_gameControllers.size());
}
void Game::addJoystick(SDL_Event& e) {
const auto which = e.jdevice.which;
SDL_assert(m_gameControllers.find(which) == m_gameControllers.end());
auto controller = std::make_shared<GameController>(which);
const auto joystickId = controller->getJoystickId();
m_gameControllers[which] = controller;
SDL_assert(m_mappedControllers.contains(joystickId));
m_mappedControllers[joystickId] = which;
SDL_Log("Joystick device %d added (%zd controllers)", which, m_gameControllers.size());
}
std::shared_ptr<GameController> Game::gameController(const int which) const {
const auto i = m_gameControllers.find(which);
if (i == m_gameControllers.cend())
throw std::runtime_error("Unknown controller");
return i->second;
}
int Game::mappedController(const SDL_JoystickID which) const {
const auto i = m_mappedControllers.find(which);
if (i == m_mappedControllers.cend())
throw std::runtime_error("Unknown joystick");
return i->second;
}
// -1 if no controllers, otherwise index
int Game::chooseControllerIndex(const int who) const {
const auto count = m_gameControllers.size();
if (count == 0)
return -1;
auto firstController = m_gameControllers.cbegin();
if (count == 1 || (who == 1))
return firstController->first;
auto secondController = (++firstController)->first;
return secondController;
}
std::shared_ptr<GameController> Game::chooseController(const int who) const {
const auto which = chooseControllerIndex(who);
if (which == -1)
return nullptr;
const auto found = m_gameControllers.find(which);
SDL_assert(found != m_gameControllers.cend());
return found->second;
}
void Game::updateTexture() {
SDLWrapper::verifySDLCall(
::SDL_UpdateTexture(m_bitmapTexture.get(), nullptr, pixels(), displayWidth() * sizeof(Uint32)),
"Unable to update texture: ");
}
void Game::copyTexture() {
SDLWrapper::verifySDLCall(
::SDL_RenderCopy(m_renderer.get(), m_bitmapTexture.get(), nullptr, nullptr),
"Unable to copy texture to renderer: ");
}
void Game::displayTexture() {
::SDL_RenderPresent(m_renderer.get());
}
void Game::toggleFullscreen() {
auto wasFullscreen = ::SDL_GetWindowFlags(m_window.get()) & SDL_WINDOW_FULLSCREEN_DESKTOP;
SDLWrapper::verifySDLCall(
::SDL_SetWindowFullscreen(m_window.get(), wasFullscreen ? 0 : SDL_WINDOW_FULLSCREEN_DESKTOP),
"Failed to modify the window full screen setting: ");
::SDL_ShowCursor(wasFullscreen ? 1 : 0);
}
bool Game::handleKeyDown(SDL_Keycode key) {
switch (key) {
case SDLK_F12:
// Don't let it get poked.
return true;
break;
default:
return false;
}
}
bool Game::handleKeyUp(SDL_Keycode key) {
switch (key) {
case SDLK_F12:
toggleFullscreen();
return true;
break;
default:
return false;
}
}
bool Game::handleJoyButtonDown(SDL_JoyButtonEvent event) {
return false;
}
bool Game::handleJoyButtonUp(SDL_JoyButtonEvent event) {
return false;
}
bool Game::handleControllerButtonDown(SDL_ControllerButtonEvent event) {
return false;
}
bool Game::handleControllerButtonUp(SDL_ControllerButtonEvent event) {
return false;
}
}