ja tenim el control de la finestra i de la imatge
treballant en les tecles de funcio
This commit is contained in:
@@ -65,6 +65,10 @@ Follows the pattern from `jaildoctors_dilemma`:
|
||||
|
||||
A state machine alternates between `ModuleSequence` (state 1) and `ModuleGame` (state 0). Each module's `Go()` returns the next state (-1 to quit). Modules are allocated/freed each transition.
|
||||
|
||||
### Golden Rule: Do Not Touch Gameplay
|
||||
|
||||
The original game logic (gameplay, entities, map, scoring, collisions, animations) must remain untouched. All modernization work targets the presentation layer and infrastructure only: window management, configuration/persistence, rendering pipeline (overlay/UI), and build system. Any new feature (save states, options menu, etc.) must be implemented as an overlay on top of the existing game, never by modifying the original gameplay code.
|
||||
|
||||
### Key Conventions
|
||||
|
||||
- All surfaces are 320x200 = 64000 bytes. Pixel coordinates assume this fixed resolution
|
||||
|
||||
@@ -13,12 +13,13 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
# --- LISTA EXPLÍCITA DE FUENTES ---
|
||||
set(APP_SOURCES
|
||||
# Core - Motor "Jail"
|
||||
source/core/global_inputs.cpp
|
||||
source/core/jail_audio.cpp
|
||||
source/core/jdraw8.cpp
|
||||
source/core/jfile.cpp
|
||||
source/core/jgame.cpp
|
||||
source/core/jinput.cpp
|
||||
source/core/jshader.cpp
|
||||
source/core/screen.cpp
|
||||
|
||||
# Game
|
||||
source/game/options.cpp
|
||||
@@ -58,10 +59,7 @@ target_compile_options(${PROJECT_NAME} PRIVATE $<$<CONFIG:RELEASE>:-Os -ffunctio
|
||||
|
||||
# --- CONFIGURACIÓN POR PLATAFORMA ---
|
||||
if(WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE mingw32 opengl32)
|
||||
elseif(UNIX AND NOT APPLE)
|
||||
find_package(OpenGL REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE OpenGL::GL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE mingw32)
|
||||
endif()
|
||||
|
||||
# Ejecutable en la raíz del proyecto
|
||||
|
||||
37
source/core/global_inputs.cpp
Normal file
37
source/core/global_inputs.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include "core/global_inputs.hpp"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "core/jinput.hpp"
|
||||
#include "core/screen.hpp"
|
||||
|
||||
namespace GlobalInputs {
|
||||
|
||||
static bool f1_was_pressed = false;
|
||||
static bool f2_was_pressed = false;
|
||||
static bool f3_was_pressed = false;
|
||||
|
||||
void handle() {
|
||||
// F1 — decrement zoom
|
||||
bool f1 = JI_KeyPressed(SDL_SCANCODE_F1);
|
||||
if (f1 && !f1_was_pressed) {
|
||||
Screen::get()->decZoom();
|
||||
}
|
||||
f1_was_pressed = f1;
|
||||
|
||||
// F2 — increment zoom
|
||||
bool f2 = JI_KeyPressed(SDL_SCANCODE_F2);
|
||||
if (f2 && !f2_was_pressed) {
|
||||
Screen::get()->incZoom();
|
||||
}
|
||||
f2_was_pressed = f2;
|
||||
|
||||
// F3 — toggle fullscreen
|
||||
bool f3 = JI_KeyPressed(SDL_SCANCODE_F3);
|
||||
if (f3 && !f3_was_pressed) {
|
||||
Screen::get()->toggleFullscreen();
|
||||
}
|
||||
f3_was_pressed = f3;
|
||||
}
|
||||
|
||||
} // namespace GlobalInputs
|
||||
6
source/core/global_inputs.hpp
Normal file
6
source/core/global_inputs.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace GlobalInputs {
|
||||
// Comprovar una vegada per frame, després de JI_Update()
|
||||
void handle();
|
||||
} // namespace GlobalInputs
|
||||
@@ -3,54 +3,23 @@
|
||||
#include <fstream>
|
||||
|
||||
#include "core/jfile.hpp"
|
||||
#include "core/jshader.hpp"
|
||||
#include "core/screen.hpp"
|
||||
#include "external/gif.h"
|
||||
|
||||
#define SCREEN_WIDTH 960
|
||||
#define SCREEN_HEIGHT 720
|
||||
|
||||
JD8_Surface screen = NULL;
|
||||
JD8_Palette main_palette = NULL;
|
||||
Uint32 *pixel_data = NULL;
|
||||
|
||||
int screenWidth = 320;
|
||||
int screenHeight = 200;
|
||||
|
||||
Uint32 contadorFPS = 0;
|
||||
Uint32 tempsFPS = SDL_GetTicks();
|
||||
char *fps = (char *)malloc(10);
|
||||
|
||||
SDL_Window *sdlWindow = NULL;
|
||||
SDL_Renderer *sdlRenderer = NULL;
|
||||
SDL_Texture *sdlTexture = NULL;
|
||||
SDL_Texture *backBuffer = NULL;
|
||||
|
||||
void JD8_Init(const char *title) {
|
||||
void JD8_Init() {
|
||||
screen = (JD8_Surface)calloc(1, 64000);
|
||||
main_palette = (JD8_Palette)calloc(1, 768);
|
||||
pixel_data = (Uint32 *)calloc(1, 320 * 200 * 4); // 1048576 );
|
||||
|
||||
sdlWindow = SDL_CreateWindow(title, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_OPENGL);
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||
// SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
||||
sdlRenderer = SDL_CreateRenderer(sdlWindow, NULL);
|
||||
|
||||
sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, 320, 200);
|
||||
backBuffer = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, 320, 200);
|
||||
|
||||
int filesize = 0;
|
||||
char *buffer = file_getfilebuffer("crtpi.glsl", filesize, true);
|
||||
|
||||
shader::init(sdlWindow, backBuffer, buffer);
|
||||
free(buffer);
|
||||
pixel_data = (Uint32 *)calloc(1, 320 * 200 * 4);
|
||||
}
|
||||
|
||||
void JD8_Quit() {
|
||||
if (screen != NULL) free(screen);
|
||||
if (main_palette != NULL) free(main_palette);
|
||||
if (pixel_data != NULL) free(pixel_data);
|
||||
SDL_DestroyRenderer(sdlRenderer);
|
||||
SDL_DestroyWindow(sdlWindow);
|
||||
}
|
||||
|
||||
void JD8_ClearScreen(Uint8 color) {
|
||||
@@ -177,8 +146,6 @@ void JD8_BlitCKToSurface(int x, int y, JD8_Surface surface, int sx, int sy, int
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Rect rect{0, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
|
||||
|
||||
void JD8_Flip() {
|
||||
for (int x = 0; x < 320; x++) {
|
||||
for (int y = 0; y < 200; y++) {
|
||||
@@ -186,11 +153,7 @@ void JD8_Flip() {
|
||||
pixel_data[x + (y * 320)] = color;
|
||||
}
|
||||
}
|
||||
SDL_UpdateTexture(sdlTexture, NULL, pixel_data, 320 * sizeof(Uint32));
|
||||
SDL_SetRenderTarget(sdlRenderer, backBuffer);
|
||||
SDL_RenderTexture(sdlRenderer, sdlTexture, NULL, NULL);
|
||||
shader::render();
|
||||
// SDL_RenderPresent(sdlRenderer);
|
||||
Screen::get()->present(pixel_data);
|
||||
}
|
||||
|
||||
void JD8_FreeSurface(JD8_Surface surface) {
|
||||
|
||||
@@ -10,7 +10,7 @@ struct Color {
|
||||
typedef Uint8 *JD8_Surface;
|
||||
typedef Color *JD8_Palette;
|
||||
|
||||
void JD8_Init(const char *title);
|
||||
void JD8_Init();
|
||||
|
||||
void JD8_Quit();
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "core/global_inputs.hpp"
|
||||
#include "core/jgame.hpp"
|
||||
|
||||
const bool* keystates; // = SDL_GetKeyboardState( NULL );
|
||||
@@ -35,6 +36,8 @@ void JI_Update() {
|
||||
JI_moveCheats(event.key.scancode);
|
||||
}
|
||||
}
|
||||
|
||||
GlobalInputs::handle();
|
||||
}
|
||||
|
||||
bool JI_KeyPressed(int key) {
|
||||
|
||||
@@ -1,225 +0,0 @@
|
||||
#include "core/jshader.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <OpenGL/OpenGL.h>
|
||||
|
||||
#include "CoreFoundation/CoreFoundation.h"
|
||||
|
||||
#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
|
||||
#include <OpenGL/gl3.h>
|
||||
#else
|
||||
#include <OpenGL/gl.h>
|
||||
#endif //! ESSENTIAL_GL_PRACTICES_SUPPORT_GL3
|
||||
#else
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#include <SDL3/SDL_opengl_glext.h>
|
||||
#endif
|
||||
|
||||
namespace shader {
|
||||
SDL_Window* win = nullptr;
|
||||
SDL_Renderer* renderer = nullptr;
|
||||
GLuint programId = 0;
|
||||
SDL_Texture* backBuffer = nullptr;
|
||||
SDL_Point win_size = {640, 480};
|
||||
SDL_FPoint tex_size = {320, 240};
|
||||
bool usingOpenGL;
|
||||
GLuint texture_number;
|
||||
GLuint nose;
|
||||
|
||||
#ifndef __APPLE__
|
||||
|
||||
// I'm avoiding the use of GLEW or some extensions handler, but that
|
||||
// doesn't mean you should...
|
||||
PFNGLCREATESHADERPROC glCreateShader;
|
||||
PFNGLSHADERSOURCEPROC glShaderSource;
|
||||
PFNGLCOMPILESHADERPROC glCompileShader;
|
||||
PFNGLGETSHADERIVPROC glGetShaderiv;
|
||||
PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog;
|
||||
PFNGLDELETESHADERPROC glDeleteShader;
|
||||
PFNGLATTACHSHADERPROC glAttachShader;
|
||||
PFNGLCREATEPROGRAMPROC glCreateProgram;
|
||||
PFNGLLINKPROGRAMPROC glLinkProgram;
|
||||
PFNGLVALIDATEPROGRAMPROC glValidateProgram;
|
||||
PFNGLGETPROGRAMIVPROC glGetProgramiv;
|
||||
PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog;
|
||||
PFNGLUSEPROGRAMPROC glUseProgram;
|
||||
|
||||
bool initGLExtensions() {
|
||||
glCreateShader = (PFNGLCREATESHADERPROC)SDL_GL_GetProcAddress("glCreateShader");
|
||||
glShaderSource = (PFNGLSHADERSOURCEPROC)SDL_GL_GetProcAddress("glShaderSource");
|
||||
glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
|
||||
glGetShaderiv = (PFNGLGETSHADERIVPROC)SDL_GL_GetProcAddress("glGetShaderiv");
|
||||
glGetShaderInfoLog = (PFNGLGETSHADERINFOLOGPROC)SDL_GL_GetProcAddress("glGetShaderInfoLog");
|
||||
glDeleteShader = (PFNGLDELETESHADERPROC)SDL_GL_GetProcAddress("glDeleteShader");
|
||||
glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
|
||||
glCreateProgram = (PFNGLCREATEPROGRAMPROC)SDL_GL_GetProcAddress("glCreateProgram");
|
||||
glLinkProgram = (PFNGLLINKPROGRAMPROC)SDL_GL_GetProcAddress("glLinkProgram");
|
||||
glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram");
|
||||
glGetProgramiv = (PFNGLGETPROGRAMIVPROC)SDL_GL_GetProcAddress("glGetProgramiv");
|
||||
glGetProgramInfoLog = (PFNGLGETPROGRAMINFOLOGPROC)SDL_GL_GetProcAddress("glGetProgramInfoLog");
|
||||
glUseProgram = (PFNGLUSEPROGRAMPROC)SDL_GL_GetProcAddress("glUseProgram");
|
||||
|
||||
return glCreateShader && glShaderSource && glCompileShader && glGetShaderiv &&
|
||||
glGetShaderInfoLog && glDeleteShader && glAttachShader && glCreateProgram &&
|
||||
glLinkProgram && glValidateProgram && glGetProgramiv && glGetProgramInfoLog &&
|
||||
glUseProgram;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
GLuint compileShader(const char* source, GLuint shaderType) {
|
||||
// Create ID for shader
|
||||
GLuint result = glCreateShader(shaderType);
|
||||
// Add define depending on shader type
|
||||
const char* sources[2] = {shaderType == GL_VERTEX_SHADER ? "#define VERTEX\n" : "#define FRAGMENT\n", source};
|
||||
// Define shader text
|
||||
glShaderSource(result, 2, sources, NULL);
|
||||
// Compile shader
|
||||
glCompileShader(result);
|
||||
|
||||
// Check vertex shader for errors
|
||||
GLint shaderCompiled = GL_FALSE;
|
||||
glGetShaderiv(result, GL_COMPILE_STATUS, &shaderCompiled);
|
||||
if (shaderCompiled != GL_TRUE) {
|
||||
std::cout << "Error en la compilación: " << result << "!" << std::endl;
|
||||
GLint logLength;
|
||||
glGetShaderiv(result, GL_INFO_LOG_LENGTH, &logLength);
|
||||
if (logLength > 0) {
|
||||
GLchar* log = (GLchar*)malloc(logLength);
|
||||
glGetShaderInfoLog(result, logLength, &logLength, log);
|
||||
std::cout << "Shader compile log:" << log << std::endl;
|
||||
free(log);
|
||||
}
|
||||
glDeleteShader(result);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
GLuint compileProgram(const char* vertexShaderSource, const char* fragmentShaderSource) {
|
||||
GLuint programId = 0;
|
||||
GLuint vtxShaderId, fragShaderId;
|
||||
|
||||
programId = glCreateProgram();
|
||||
|
||||
vtxShaderId = compileShader(vertexShaderSource, GL_VERTEX_SHADER);
|
||||
fragShaderId = compileShader(fragmentShaderSource ? fragmentShaderSource : vertexShaderSource, GL_FRAGMENT_SHADER);
|
||||
|
||||
if (vtxShaderId && fragShaderId) {
|
||||
// Associate shader with program
|
||||
glAttachShader(programId, vtxShaderId);
|
||||
glAttachShader(programId, fragShaderId);
|
||||
glLinkProgram(programId);
|
||||
glValidateProgram(programId);
|
||||
|
||||
// Check the status of the compile/link
|
||||
GLint logLen;
|
||||
glGetProgramiv(programId, GL_INFO_LOG_LENGTH, &logLen);
|
||||
if (logLen > 0) {
|
||||
char* log = (char*)malloc(logLen * sizeof(char));
|
||||
// Show any errors as appropriate
|
||||
glGetProgramInfoLog(programId, logLen, &logLen, log);
|
||||
std::cout << "Prog Info Log: " << std::endl
|
||||
<< log << std::endl;
|
||||
free(log);
|
||||
}
|
||||
}
|
||||
if (vtxShaderId) glDeleteShader(vtxShaderId);
|
||||
if (fragShaderId) glDeleteShader(fragShaderId);
|
||||
return programId;
|
||||
}
|
||||
|
||||
const bool init(SDL_Window* win, SDL_Texture* backBuffer, const char* vertexShader, const char* fragmentShader) {
|
||||
shader::win = win;
|
||||
shader::renderer = SDL_GetRenderer(win);
|
||||
shader::backBuffer = backBuffer;
|
||||
SDL_GetWindowSize(win, &win_size.x, &win_size.y);
|
||||
SDL_GetTextureSize(backBuffer, &tex_size.x, &tex_size.y);
|
||||
printf("tex size: %fx%f\n", tex_size.x, tex_size.y);
|
||||
SDL_PropertiesID props = SDL_GetTextureProperties(backBuffer);
|
||||
texture_number = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_NUMBER, -1);
|
||||
printf("texture number: %i\n", texture_number);
|
||||
int access = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_ACCESS_NUMBER, -1);
|
||||
nose = SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_OPENGL_TEXTURE_TARGET_NUMBER, -1);
|
||||
printf("texture target number: %i\n", nose);
|
||||
|
||||
if (access != SDL_TEXTUREACCESS_TARGET) {
|
||||
std::cout << "ERROR FATAL: La textura per al render ha de tindre SDL_TEXTUREACCESS_TARGET definit." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const char* renderer_name = SDL_GetRendererName(renderer);
|
||||
printf("rendererInfo.name: %s\n", renderer_name);
|
||||
|
||||
if (!strncmp(renderer_name, "opengl", 6)) {
|
||||
#ifndef __APPLE__
|
||||
if (!initGLExtensions()) {
|
||||
std::cout << "WARNING: No s'han pogut inicialitzar les extensions d'OpenGL!" << std::endl;
|
||||
usingOpenGL = false;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
// Compilar el shader y dejarlo listo para usar.
|
||||
programId = compileProgram(vertexShader, fragmentShader);
|
||||
} else {
|
||||
std::cout << "WARNING: El driver del renderer no es OpenGL." << std::endl;
|
||||
usingOpenGL = false;
|
||||
return false;
|
||||
}
|
||||
usingOpenGL = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned char pixels[512 * 240 * 4];
|
||||
|
||||
void render() {
|
||||
SDL_FlushRenderer(renderer);
|
||||
SDL_SetRenderTarget(renderer, NULL);
|
||||
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
|
||||
SDL_RenderClear(renderer);
|
||||
SDL_FlushRenderer(renderer);
|
||||
|
||||
if (usingOpenGL) {
|
||||
GLint oldProgramId;
|
||||
if (programId != 0) {
|
||||
glGetIntegerv(GL_CURRENT_PROGRAM, &oldProgramId);
|
||||
glUseProgram(programId);
|
||||
}
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, 1);
|
||||
// glGetTexImage(GL_TEXTURE_2D, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8, pixels);
|
||||
// if (glGetError()) { printf("GLGETERROR!\n"); exit(1);}
|
||||
// GLint param;
|
||||
// glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, ¶m);
|
||||
// printf("tex width: %i\n", param);
|
||||
glViewport(0, 0, win_size.x, win_size.y);
|
||||
|
||||
glBegin(GL_TRIANGLE_STRIP);
|
||||
glTexCoord2f(0.0f, 0.0f);
|
||||
glVertex2f(0.0f, 0.0f);
|
||||
glTexCoord2f(1.0f, 0.0f);
|
||||
glVertex2f(tex_size.x, 0.0f);
|
||||
glTexCoord2f(0.0f, 1.0f);
|
||||
glVertex2f(0.0f, tex_size.y);
|
||||
glTexCoord2f(1.0f, 1.0f);
|
||||
glVertex2f(tex_size.x, tex_size.y);
|
||||
glEnd();
|
||||
|
||||
SDL_GL_SwapWindow(win);
|
||||
|
||||
if (programId != 0) glUseProgram(oldProgramId);
|
||||
|
||||
} else {
|
||||
SDL_RenderTexture(renderer, backBuffer, NULL, NULL);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
if (glGetError()) {
|
||||
printf("GLERROR!\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
} // namespace shader
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
// TIPS:
|
||||
// =======================================================================
|
||||
// Abans de crear el renderer, cridar a la següent funció:
|
||||
//
|
||||
// SDL_SetHint(SDL_HINT_RENDER_DRIVER, "opengl");
|
||||
//
|
||||
// Aixó li diu que volem un renderer que use especificament opengl. A més,
|
||||
// al crear el renderer li tenim que dir que el volem que use acceeració
|
||||
// per hardware, i que soporte render a textura. Per exemple:
|
||||
//
|
||||
// SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED |
|
||||
// SDL_RENDERER_TARGETTEXTURE);
|
||||
//
|
||||
// Per altra part, al crear la textura tenim que definir que puga ser target
|
||||
// de renderitzat (SDL_TEXTUREACCESS_TARGET), per exemple:
|
||||
//
|
||||
// SDL_Texture *tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_ARGB8888,
|
||||
// SDL_TEXTUREACCESS_TARGET, 320, 240);
|
||||
//
|
||||
// Els shaders li'ls passem com una cadena, som nosaltres els que s'encarreguem
|
||||
// de carregarlos de disc, amb fopen, ifstream, jfile o el que vullgues.
|
||||
// Si els tens en un std::string, passa-li-la com "cadena.c_str()".
|
||||
//
|
||||
// Poden ser els dos el mateix arxiu, com fa libRetro, jo desde dins ja fique
|
||||
// els defines necessaris. Si es el mateix arxiu, pots no ficar el quart paràmetre.
|
||||
//
|
||||
// Els shaders de libRetro no funcionen directament, hi ha que fer algunes modificacions.
|
||||
//
|
||||
// El pintat final de la teua escena l'has de fer com si "backBuffer" fora la pantalla.
|
||||
//
|
||||
// Ah! una cosa mes: al compilar, en Linux afegir "-lGL", en Windows afegir "-lopengl32".
|
||||
// En Mac ni idea
|
||||
|
||||
namespace shader {
|
||||
const bool init(SDL_Window* win, SDL_Texture* backBuffer, const char* vertexShader, const char* fragmentShader = nullptr);
|
||||
|
||||
void render();
|
||||
} // namespace shader
|
||||
106
source/core/screen.cpp
Normal file
106
source/core/screen.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "core/screen.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "game/defines.hpp"
|
||||
#include "game/options.hpp"
|
||||
|
||||
Screen* Screen::instance_ = nullptr;
|
||||
|
||||
void Screen::init() {
|
||||
instance_ = new Screen();
|
||||
}
|
||||
|
||||
void Screen::destroy() {
|
||||
delete instance_;
|
||||
instance_ = nullptr;
|
||||
}
|
||||
|
||||
auto Screen::get() -> Screen* {
|
||||
return instance_;
|
||||
}
|
||||
|
||||
Screen::Screen() {
|
||||
// Carrega opcions guardades
|
||||
zoom_ = Options::window.zoom;
|
||||
fullscreen_ = Options::window.fullscreen;
|
||||
|
||||
calculateMaxZoom();
|
||||
|
||||
if (zoom_ < 1) zoom_ = 1;
|
||||
if (zoom_ > max_zoom_) zoom_ = max_zoom_;
|
||||
|
||||
int w = GAME_WIDTH * zoom_;
|
||||
int h = GAME_HEIGHT * zoom_;
|
||||
|
||||
window_ = SDL_CreateWindow(Texts::WINDOW_TITLE, w, h, fullscreen_ ? SDL_WINDOW_FULLSCREEN : 0);
|
||||
renderer_ = SDL_CreateRenderer(window_, nullptr);
|
||||
SDL_SetRenderLogicalPresentation(renderer_, GAME_WIDTH, GAME_HEIGHT, SDL_LOGICAL_PRESENTATION_LETTERBOX);
|
||||
|
||||
texture_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_ABGR8888, SDL_TEXTUREACCESS_STREAMING, GAME_WIDTH, GAME_HEIGHT);
|
||||
SDL_SetTextureScaleMode(texture_, SDL_SCALEMODE_NEAREST);
|
||||
|
||||
std::cout << "Screen initialized: " << w << "x" << h << " (zoom " << zoom_ << ", max " << max_zoom_ << ")\n";
|
||||
}
|
||||
|
||||
Screen::~Screen() {
|
||||
// Guarda opcions abans de destruir
|
||||
Options::window.zoom = zoom_;
|
||||
Options::window.fullscreen = fullscreen_;
|
||||
|
||||
if (texture_) SDL_DestroyTexture(texture_);
|
||||
if (renderer_) SDL_DestroyRenderer(renderer_);
|
||||
if (window_) SDL_DestroyWindow(window_);
|
||||
}
|
||||
|
||||
void Screen::present(const Uint32* pixel_data) {
|
||||
SDL_UpdateTexture(texture_, nullptr, pixel_data, GAME_WIDTH * sizeof(Uint32));
|
||||
SDL_RenderClear(renderer_);
|
||||
SDL_RenderTexture(renderer_, texture_, nullptr, nullptr);
|
||||
SDL_RenderPresent(renderer_);
|
||||
}
|
||||
|
||||
void Screen::toggleFullscreen() {
|
||||
fullscreen_ = !fullscreen_;
|
||||
SDL_SetWindowFullscreen(window_, fullscreen_);
|
||||
if (!fullscreen_) {
|
||||
adjustWindowSize();
|
||||
}
|
||||
std::cout << (fullscreen_ ? "Fullscreen ON\n" : "Fullscreen OFF\n");
|
||||
}
|
||||
|
||||
void Screen::incZoom() {
|
||||
if (fullscreen_ || zoom_ >= max_zoom_) return;
|
||||
zoom_++;
|
||||
adjustWindowSize();
|
||||
}
|
||||
|
||||
void Screen::decZoom() {
|
||||
if (fullscreen_ || zoom_ <= 1) return;
|
||||
zoom_--;
|
||||
adjustWindowSize();
|
||||
}
|
||||
|
||||
void Screen::setZoom(int zoom) {
|
||||
if (zoom < 1 || zoom > max_zoom_ || fullscreen_) return;
|
||||
zoom_ = zoom;
|
||||
adjustWindowSize();
|
||||
}
|
||||
|
||||
void Screen::adjustWindowSize() {
|
||||
int w = GAME_WIDTH * zoom_;
|
||||
int h = GAME_HEIGHT * zoom_;
|
||||
SDL_SetWindowSize(window_, w, h);
|
||||
SDL_SetWindowPosition(window_, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED);
|
||||
}
|
||||
|
||||
void Screen::calculateMaxZoom() {
|
||||
SDL_DisplayID display = SDL_GetPrimaryDisplay();
|
||||
const SDL_DisplayMode* mode = SDL_GetCurrentDisplayMode(display);
|
||||
if (mode) {
|
||||
int max_w = mode->w / GAME_WIDTH;
|
||||
int max_h = mode->h / GAME_HEIGHT;
|
||||
max_zoom_ = (max_w < max_h) ? max_w : max_h;
|
||||
if (max_zoom_ < 1) max_zoom_ = 1;
|
||||
}
|
||||
}
|
||||
47
source/core/screen.hpp
Normal file
47
source/core/screen.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class Screen {
|
||||
public:
|
||||
static void init();
|
||||
static void destroy();
|
||||
static auto get() -> Screen*;
|
||||
|
||||
// Presentació — rep el buffer ARGB de 320x200 de JD8
|
||||
void present(const Uint32* pixel_data);
|
||||
|
||||
// Gestió de finestra
|
||||
void toggleFullscreen();
|
||||
void incZoom();
|
||||
void decZoom();
|
||||
void setZoom(int zoom);
|
||||
|
||||
// Getters
|
||||
[[nodiscard]] auto isFullscreen() const -> bool { return fullscreen_; }
|
||||
[[nodiscard]] auto getZoom() const -> int { return zoom_; }
|
||||
[[nodiscard]] auto getWindow() -> SDL_Window* { return window_; }
|
||||
[[nodiscard]] auto getRenderer() -> SDL_Renderer* { return renderer_; }
|
||||
|
||||
private:
|
||||
Screen();
|
||||
~Screen();
|
||||
|
||||
void adjustWindowSize();
|
||||
void calculateMaxZoom();
|
||||
|
||||
static Screen* instance_;
|
||||
|
||||
SDL_Window* window_{nullptr};
|
||||
SDL_Renderer* renderer_{nullptr};
|
||||
SDL_Texture* texture_{nullptr}; // 320x200 streaming, ARGB8888
|
||||
|
||||
int zoom_{3};
|
||||
int max_zoom_{6};
|
||||
bool fullscreen_{false};
|
||||
|
||||
static constexpr int GAME_WIDTH = 320;
|
||||
static constexpr int GAME_HEIGHT = 200;
|
||||
};
|
||||
@@ -8,6 +8,11 @@ namespace Defaults::Audio {
|
||||
constexpr float SOUND_VOLUME = 1.0F;
|
||||
} // namespace Defaults::Audio
|
||||
|
||||
namespace Defaults::Window {
|
||||
constexpr int ZOOM = 3;
|
||||
constexpr bool FULLSCREEN = false;
|
||||
} // namespace Defaults::Window
|
||||
|
||||
namespace Defaults::Game {
|
||||
constexpr int HABITACIO_INICIAL = 1;
|
||||
constexpr int PIRAMIDE_INICIAL = 255;
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
// Textos
|
||||
namespace Texts {
|
||||
constexpr const char* WINDOW_TITLE = "Aventures En Egipte";
|
||||
constexpr const char* WINDOW_TITLE = "© 2000 Aventures en Egipte — JailDesigner";
|
||||
constexpr const char* VERSION = "1.00";
|
||||
} // namespace Texts
|
||||
|
||||
// Resolución del juego
|
||||
namespace Screen {
|
||||
namespace GameScreen {
|
||||
constexpr int WIDTH = 320;
|
||||
constexpr int HEIGHT = 200;
|
||||
constexpr int BUFFER_SIZE = WIDTH * HEIGHT; // 64000
|
||||
} // namespace Screen
|
||||
} // namespace GameScreen
|
||||
|
||||
@@ -41,6 +41,16 @@ namespace Options {
|
||||
}
|
||||
}
|
||||
|
||||
static void loadWindowConfigFromYaml(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("window")) return;
|
||||
const auto& node = yaml["window"];
|
||||
|
||||
if (node.contains("zoom"))
|
||||
window.zoom = node["zoom"].get_value<int>();
|
||||
if (node.contains("fullscreen"))
|
||||
window.fullscreen = node["fullscreen"].get_value<bool>();
|
||||
}
|
||||
|
||||
static void loadGameConfigFromYaml(const fkyaml::node& yaml) {
|
||||
if (!yaml.contains("game")) return;
|
||||
const auto& node = yaml["game"];
|
||||
@@ -84,6 +94,7 @@ namespace Options {
|
||||
return true;
|
||||
}
|
||||
|
||||
loadWindowConfigFromYaml(yaml);
|
||||
loadAudioConfigFromYaml(yaml);
|
||||
loadGameConfigFromYaml(yaml);
|
||||
|
||||
@@ -118,6 +129,13 @@ namespace Options {
|
||||
file << "version: \"" << Texts::VERSION << "\"\n";
|
||||
file << "\n";
|
||||
|
||||
// WINDOW
|
||||
file << "# WINDOW\n";
|
||||
file << "window:\n";
|
||||
file << " zoom: " << window.zoom << "\n";
|
||||
file << " fullscreen: " << (window.fullscreen ? "true" : "false") << "\n";
|
||||
file << "\n";
|
||||
|
||||
// AUDIO
|
||||
file << "# AUDIO\n";
|
||||
file << "audio:\n";
|
||||
|
||||
@@ -16,6 +16,12 @@ namespace Options {
|
||||
float volume{Defaults::Audio::VOLUME};
|
||||
};
|
||||
|
||||
// Opcions de finestra
|
||||
struct Window {
|
||||
int zoom{Defaults::Window::ZOOM};
|
||||
bool fullscreen{Defaults::Window::FULLSCREEN};
|
||||
};
|
||||
|
||||
// Opcions de joc
|
||||
struct Game {
|
||||
int habitacio_inicial{Defaults::Game::HABITACIO_INICIAL};
|
||||
@@ -26,6 +32,7 @@ namespace Options {
|
||||
// --- Variables globals ---
|
||||
inline std::string version{};
|
||||
inline Audio audio{};
|
||||
inline Window window{};
|
||||
inline Game game{};
|
||||
inline std::string config_file_path{};
|
||||
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#include "core/global_inputs.hpp"
|
||||
#include "core/jail_audio.hpp"
|
||||
#include "core/jdraw8.hpp"
|
||||
#include "core/jfile.hpp"
|
||||
#include "core/jgame.hpp"
|
||||
#include "core/screen.hpp"
|
||||
#include "game/defines.hpp"
|
||||
#include "game/info.hpp"
|
||||
#include "game/modulegame.hpp"
|
||||
@@ -20,7 +22,8 @@ int main(int argc, char* args[]) {
|
||||
Options::loadFromFile();
|
||||
|
||||
JG_Init();
|
||||
JD8_Init(Texts::WINDOW_TITLE);
|
||||
Screen::init();
|
||||
JD8_Init();
|
||||
JA_Init(48000, SDL_AUDIO_S16, 2);
|
||||
|
||||
info::num_habitacio = Options::game.habitacio_inicial;
|
||||
@@ -61,6 +64,7 @@ int main(int argc, char* args[]) {
|
||||
|
||||
JA_Quit();
|
||||
JD8_Quit();
|
||||
Screen::destroy();
|
||||
JG_Finalize();
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user