From abb23071b5c90fca6c8fcd26efb375c371f1894a Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sat, 4 Apr 2026 17:20:28 +0200 Subject: [PATCH] ja tenim el control de la finestra i de la imatge treballant en les tecles de funcio --- CLAUDE.md | 4 + CMakeLists.txt | 8 +- source/core/global_inputs.cpp | 37 ++++++ source/core/global_inputs.hpp | 6 + source/core/jdraw8.cpp | 45 +------ source/core/jdraw8.hpp | 2 +- source/core/jinput.cpp | 3 + source/core/jshader.cpp | 225 ---------------------------------- source/core/jshader.hpp | 42 ------- source/core/screen.cpp | 106 ++++++++++++++++ source/core/screen.hpp | 47 +++++++ source/game/defaults.hpp | 5 + source/game/defines.hpp | 6 +- source/game/options.cpp | 18 +++ source/game/options.hpp | 7 ++ source/main.cpp | 6 +- 16 files changed, 249 insertions(+), 318 deletions(-) create mode 100644 source/core/global_inputs.cpp create mode 100644 source/core/global_inputs.hpp delete mode 100644 source/core/jshader.cpp delete mode 100644 source/core/jshader.hpp create mode 100644 source/core/screen.cpp create mode 100644 source/core/screen.hpp diff --git a/CLAUDE.md b/CLAUDE.md index ae8a0c2..f3d7d24 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 diff --git a/CMakeLists.txt b/CMakeLists.txt index f3f9aef..543f5a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 $<$:-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 diff --git a/source/core/global_inputs.cpp b/source/core/global_inputs.cpp new file mode 100644 index 0000000..eb14841 --- /dev/null +++ b/source/core/global_inputs.cpp @@ -0,0 +1,37 @@ +#include "core/global_inputs.hpp" + +#include + +#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 diff --git a/source/core/global_inputs.hpp b/source/core/global_inputs.hpp new file mode 100644 index 0000000..dbd03f5 --- /dev/null +++ b/source/core/global_inputs.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace GlobalInputs { + // Comprovar una vegada per frame, després de JI_Update() + void handle(); +} // namespace GlobalInputs diff --git a/source/core/jdraw8.cpp b/source/core/jdraw8.cpp index f027078..ad363fc 100644 --- a/source/core/jdraw8.cpp +++ b/source/core/jdraw8.cpp @@ -3,54 +3,23 @@ #include #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) { diff --git a/source/core/jdraw8.hpp b/source/core/jdraw8.hpp index dad1cc2..d65a803 100644 --- a/source/core/jdraw8.hpp +++ b/source/core/jdraw8.hpp @@ -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(); diff --git a/source/core/jinput.cpp b/source/core/jinput.cpp index 3b96b91..04528f7 100644 --- a/source/core/jinput.cpp +++ b/source/core/jinput.cpp @@ -2,6 +2,7 @@ #include +#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) { diff --git a/source/core/jshader.cpp b/source/core/jshader.cpp deleted file mode 100644 index 4d78b97..0000000 --- a/source/core/jshader.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "core/jshader.hpp" - -#include - -#ifdef __APPLE__ -#include - -#include "CoreFoundation/CoreFoundation.h" - -#if ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 -#include -#else -#include -#endif //! ESSENTIAL_GL_PRACTICES_SUPPORT_GL3 -#else -#include -#include -#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 diff --git a/source/core/jshader.hpp b/source/core/jshader.hpp deleted file mode 100644 index d82ce49..0000000 --- a/source/core/jshader.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include - -// 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 diff --git a/source/core/screen.cpp b/source/core/screen.cpp new file mode 100644 index 0000000..6765439 --- /dev/null +++ b/source/core/screen.cpp @@ -0,0 +1,106 @@ +#include "core/screen.hpp" + +#include + +#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; + } +} diff --git a/source/core/screen.hpp b/source/core/screen.hpp new file mode 100644 index 0000000..3c886ba --- /dev/null +++ b/source/core/screen.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include + +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; +}; diff --git a/source/game/defaults.hpp b/source/game/defaults.hpp index e59c96d..d013f9c 100644 --- a/source/game/defaults.hpp +++ b/source/game/defaults.hpp @@ -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; diff --git a/source/game/defines.hpp b/source/game/defines.hpp index 17ed643..13fb696 100644 --- a/source/game/defines.hpp +++ b/source/game/defines.hpp @@ -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 diff --git a/source/game/options.cpp b/source/game/options.cpp index 0875908..872ab2e 100644 --- a/source/game/options.cpp +++ b/source/game/options.cpp @@ -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(); + if (node.contains("fullscreen")) + window.fullscreen = node["fullscreen"].get_value(); + } + 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"; diff --git a/source/game/options.hpp b/source/game/options.hpp index ddb71a4..c952103 100644 --- a/source/game/options.hpp +++ b/source/game/options.hpp @@ -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{}; diff --git a/source/main.cpp b/source/main.cpp index 0742c22..e3d62dd 100644 --- a/source/main.cpp +++ b/source/main.cpp @@ -1,10 +1,12 @@ #include #include +#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;