diff --git a/CMakeLists.txt b/CMakeLists.txt index 70a8d4d..a26aa7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -275,7 +275,7 @@ if(EMSCRIPTEN) -sALLOW_MEMORY_GROWTH=1 -sSTACK_SIZE=2097152 -sEXPORTED_RUNTIME_METHODS=ccall,cwrap,FS - -sEXPORTED_FUNCTIONS=_main,_run_python_string,_run_python_string_with_output,_reset_python_environment + -sEXPORTED_FUNCTIONS=_main,_run_python_string,_run_python_string_with_output,_reset_python_environment,_notify_canvas_resize -sASSERTIONS=2 -sSTACK_OVERFLOW_CHECK=2 -fexceptions diff --git a/src/GameEngine.cpp b/src/GameEngine.cpp index 50c6078..27a06a8 100644 --- a/src/GameEngine.cpp +++ b/src/GameEngine.cpp @@ -15,6 +15,9 @@ #endif #include #include +#ifdef __EMSCRIPTEN__ +#include +#endif // Static member definitions for shader intermediate texture (#106) std::unique_ptr GameEngine::shaderIntermediate; @@ -84,7 +87,15 @@ GameEngine::GameEngine(const McRogueFaceConfig& cfg) render_target = &headless_renderer->getRenderTarget(); } else { window = std::make_unique(); +#ifdef __EMSCRIPTEN__ + // Read actual canvas size from HTML template (may be fullscreen or layout-constrained) + // gameResolution stays at its default (1024x768) - the viewport system maps it to the canvas + int cw = 1024, ch = 768; + emscripten_get_canvas_element_size("#canvas", &cw, &ch); + window->create(sf::VideoMode(cw, ch), window_title, sf::Style::Titlebar | sf::Style::Close | sf::Style::Resize); +#else window->create(sf::VideoMode(1024, 768), window_title, sf::Style::Titlebar | sf::Style::Close | sf::Style::Resize); +#endif window->setFramerateLimit(60); render_target = window.get(); @@ -243,7 +254,9 @@ void GameEngine::changeScene(std::string sceneName, TransitionType transitionTyp } else { - // Start transition + // Start transition with current game resolution + transition.width = gameResolution.x; + transition.height = gameResolution.y; transition.start(transitionType, scene, sceneName, duration); // Render current scene to texture @@ -829,8 +842,14 @@ void GameEngine::initShaderIntermediate(unsigned int width, unsigned int height) sf::RenderTexture& GameEngine::getShaderIntermediate() { if (!shaderIntermediateInitialized) { - // Initialize with default resolution if not already done - initShaderIntermediate(1024, 768); + // Initialize with game resolution from the engine instance + unsigned int w = 1024, h = 768; + if (Resources::game) { + auto res = Resources::game->getGameResolution(); + w = res.x; + h = res.y; + } + initShaderIntermediate(w, h); } return *shaderIntermediate; } diff --git a/src/SceneTransition.cpp b/src/SceneTransition.cpp index 574f29c..b9ae701 100644 --- a/src/SceneTransition.cpp +++ b/src/SceneTransition.cpp @@ -6,15 +6,15 @@ void SceneTransition::start(TransitionType t, const std::string& from, const std toScene = to; duration = dur; elapsed = 0.0f; - + // Initialize render textures if needed if (!oldSceneTexture) { oldSceneTexture = std::make_unique(); - oldSceneTexture->create(1024, 768); + oldSceneTexture->create(width, height); } if (!newSceneTexture) { newSceneTexture = std::make_unique(); - newSceneTexture->create(1024, 768); + newSceneTexture->create(width, height); } } @@ -25,14 +25,17 @@ void SceneTransition::update(float dt) { void SceneTransition::render(sf::RenderTarget& target) { if (type == TransitionType::None) return; - + float progress = getProgress(); float easedProgress = easeInOut(progress); - + + float w = static_cast(width); + float h = static_cast(height); + // Update sprites with current textures oldSprite.setTexture(oldSceneTexture->getTexture()); newSprite.setTexture(newSceneTexture->getTexture()); - + switch (type) { case TransitionType::Fade: // Fade out old scene, fade in new scene @@ -41,39 +44,39 @@ void SceneTransition::render(sf::RenderTarget& target) { target.draw(oldSprite); target.draw(newSprite); break; - + case TransitionType::SlideLeft: // Old scene slides out to left, new scene slides in from right - oldSprite.setPosition(-1024 * easedProgress, 0); - newSprite.setPosition(1024 * (1.0f - easedProgress), 0); + oldSprite.setPosition(-w * easedProgress, 0); + newSprite.setPosition(w * (1.0f - easedProgress), 0); target.draw(oldSprite); target.draw(newSprite); break; - + case TransitionType::SlideRight: // Old scene slides out to right, new scene slides in from left - oldSprite.setPosition(1024 * easedProgress, 0); - newSprite.setPosition(-1024 * (1.0f - easedProgress), 0); + oldSprite.setPosition(w * easedProgress, 0); + newSprite.setPosition(-w * (1.0f - easedProgress), 0); target.draw(oldSprite); target.draw(newSprite); break; - + case TransitionType::SlideUp: // Old scene slides up, new scene slides in from bottom - oldSprite.setPosition(0, -768 * easedProgress); - newSprite.setPosition(0, 768 * (1.0f - easedProgress)); + oldSprite.setPosition(0, -h * easedProgress); + newSprite.setPosition(0, h * (1.0f - easedProgress)); target.draw(oldSprite); target.draw(newSprite); break; - + case TransitionType::SlideDown: // Old scene slides down, new scene slides in from top - oldSprite.setPosition(0, 768 * easedProgress); - newSprite.setPosition(0, -768 * (1.0f - easedProgress)); + oldSprite.setPosition(0, h * easedProgress); + newSprite.setPosition(0, -h * (1.0f - easedProgress)); target.draw(oldSprite); target.draw(newSprite); break; - + default: break; } @@ -82,4 +85,4 @@ void SceneTransition::render(sf::RenderTarget& target) { float SceneTransition::easeInOut(float t) { // Smooth ease-in-out curve return t < 0.5f ? 2 * t * t : -1 + (4 - 2 * t) * t; -} \ No newline at end of file +} diff --git a/src/SceneTransition.h b/src/SceneTransition.h index 28fb76a..d9178f2 100644 --- a/src/SceneTransition.h +++ b/src/SceneTransition.h @@ -17,6 +17,8 @@ public: TransitionType type = TransitionType::None; float duration = 0.0f; float elapsed = 0.0f; + unsigned int width = 1024; + unsigned int height = 768; std::string fromScene; std::string toScene; diff --git a/src/UIGrid.cpp b/src/UIGrid.cpp index 068f25d..fbd38e9 100644 --- a/src/UIGrid.cpp +++ b/src/UIGrid.cpp @@ -1144,7 +1144,8 @@ PyObject* UIGrid::get_grid_h(PyUIGridObject* self, void* closure) { } PyObject* UIGrid::get_position(PyUIGridObject* self, void* closure) { - return Py_BuildValue("(ff)", self->data->position.x, self->data->position.y); + // #179 - Return position as Vector (consistent with get_size, get_grid_size) + return PyVector(self->data->position).pyObject(); } int UIGrid::set_position(PyUIGridObject* self, PyObject* value, void* closure) { diff --git a/src/platform/EmscriptenStubs.cpp b/src/platform/EmscriptenStubs.cpp index aef83b6..95b8d61 100644 --- a/src/platform/EmscriptenStubs.cpp +++ b/src/platform/EmscriptenStubs.cpp @@ -10,6 +10,8 @@ #include #include #include +#include "Resources.h" +#include "GameEngine.h" extern "C" { @@ -290,6 +292,16 @@ int get_python_globals_count() { return (int)PyDict_Size(main_dict); } +// Notify the engine that the browser canvas was resized (called from JS) +EMSCRIPTEN_KEEPALIVE +void notify_canvas_resize(int width, int height) { + // Forward to SDL so it generates a proper resize event + if (Resources::game) { + auto& win = Resources::game->getWindow(); + win.setSize(sf::Vector2u(width, height)); + } +} + } // extern "C" #endif // __EMSCRIPTEN__ diff --git a/src/platform/SDL2Renderer.cpp b/src/platform/SDL2Renderer.cpp index 057ea6c..3f8bf23 100644 --- a/src/platform/SDL2Renderer.cpp +++ b/src/platform/SDL2Renderer.cpp @@ -567,23 +567,10 @@ void RenderWindow::create(VideoMode mode, const std::string& title, uint32_t sty open_ = true; #ifdef __EMSCRIPTEN__ - // Force canvas size AFTER SDL window creation (SDL may have reset it) + // Force canvas backing buffer size AFTER SDL window creation (SDL may have reset it) + // CSS display size is controlled by the HTML shell template (fullscreen or layout-constrained) emscripten_set_canvas_element_size("#canvas", mode.width, mode.height); - // Also set the CSS size to match - EM_ASM({ - var canvas = document.getElementById('canvas'); - if (canvas) { - canvas.width = $0; - canvas.height = $1; - canvas.style.width = $0 + 'px'; - canvas.style.height = $1 + 'px'; - console.log('EM_ASM: Set canvas to ' + $0 + 'x' + $1); - } else { - console.error('EM_ASM: Canvas element not found!'); - } - }, mode.width, mode.height); - // Re-make context current after canvas resize SDL_GL_MakeCurrent(window, context); #endif @@ -997,6 +984,80 @@ void RenderTarget::draw(const VertexArray& vertices, const RenderStates& states) draw(&vertices[0], vertices.getVertexCount(), vertices.getPrimitiveType(), states); } +void RenderTarget::setView(const View& view) { + view_ = view; + + // Apply the view's viewport (normalized 0-1 coords) to OpenGL + auto vp = view.getViewport(); + int px = static_cast(vp.left * size_.x); + // OpenGL viewport origin is bottom-left, SFML is top-left + int py = static_cast((1.0f - vp.top - vp.height) * size_.y); + int pw = static_cast(vp.width * size_.x); + int ph = static_cast(vp.height * size_.y); + glViewport(px, py, pw, ph); + + // Set projection to map view center/size to the viewport + auto center = view.getCenter(); + auto sz = view.getSize(); + float left = center.x - sz.x / 2.0f; + float right = center.x + sz.x / 2.0f; + float top = center.y - sz.y / 2.0f; + float bottom = center.y + sz.y / 2.0f; + SDL2Renderer::getInstance().setProjection(left, right, bottom, top); +} + +IntRect RenderTarget::getViewport(const View& view) const { + auto vp = view.getViewport(); + return IntRect( + static_cast(vp.left * size_.x), + static_cast(vp.top * size_.y), + static_cast(vp.width * size_.x), + static_cast(vp.height * size_.y) + ); +} + +Vector2f RenderTarget::mapPixelToCoords(const Vector2i& point) const { + return mapPixelToCoords(point, view_); +} + +Vector2f RenderTarget::mapPixelToCoords(const Vector2i& point, const View& view) const { + // Convert pixel position to world coordinates through the view + auto viewport = getViewport(view); + auto center = view.getCenter(); + auto sz = view.getSize(); + + // Normalize point within viewport (0-1) + float nx = (static_cast(point.x) - viewport.left) / viewport.width; + float ny = (static_cast(point.y) - viewport.top) / viewport.height; + + // Map to view coordinates + return Vector2f( + center.x + sz.x * (nx - 0.5f), + center.y + sz.y * (ny - 0.5f) + ); +} + +Vector2i RenderTarget::mapCoordsToPixel(const Vector2f& point) const { + return mapCoordsToPixel(point, view_); +} + +Vector2i RenderTarget::mapCoordsToPixel(const Vector2f& point, const View& view) const { + // Convert world coordinates to pixel position through the view + auto viewport = getViewport(view); + auto center = view.getCenter(); + auto sz = view.getSize(); + + // Normalize within view (0-1) + float nx = (point.x - center.x) / sz.x + 0.5f; + float ny = (point.y - center.y) / sz.y + 0.5f; + + // Map to pixel coordinates within viewport + return Vector2i( + static_cast(viewport.left + nx * viewport.width), + static_cast(viewport.top + ny * viewport.height) + ); +} + // ============================================================================= // RenderTexture Implementation // ============================================================================= diff --git a/src/platform/SDL2Types.h b/src/platform/SDL2Types.h index ef8fbec..4e59d81 100644 --- a/src/platform/SDL2Types.h +++ b/src/platform/SDL2Types.h @@ -821,16 +821,16 @@ public: void draw(const Vertex* vertices, size_t vertexCount, PrimitiveType type, const RenderStates& states = RenderStates::Default); void draw(const VertexArray& vertices, const RenderStates& states = RenderStates::Default); - void setView(const View& view) { view_ = view; } + void setView(const View& view); // Implemented in SDL2Renderer.cpp - applies glViewport + projection const View& getView() const { return view_; } const View& getDefaultView() const { return defaultView_; } - IntRect getViewport(const View& view) const { return IntRect(0, 0, size_.x, size_.y); } + IntRect getViewport(const View& view) const; // Implemented in SDL2Renderer.cpp - Vector2f mapPixelToCoords(const Vector2i& point) const { return Vector2f(static_cast(point.x), static_cast(point.y)); } - Vector2f mapPixelToCoords(const Vector2i& point, const View& view) const { return Vector2f(static_cast(point.x), static_cast(point.y)); } - Vector2i mapCoordsToPixel(const Vector2f& point) const { return Vector2i(static_cast(point.x), static_cast(point.y)); } - Vector2i mapCoordsToPixel(const Vector2f& point, const View& view) const { return Vector2i(static_cast(point.x), static_cast(point.y)); } + Vector2f mapPixelToCoords(const Vector2i& point) const; // Implemented in SDL2Renderer.cpp + Vector2f mapPixelToCoords(const Vector2i& point, const View& view) const; // Implemented in SDL2Renderer.cpp + Vector2i mapCoordsToPixel(const Vector2f& point) const; // Implemented in SDL2Renderer.cpp + Vector2i mapCoordsToPixel(const Vector2f& point, const View& view) const; // Implemented in SDL2Renderer.cpp }; // ============================================================================= diff --git a/src/shell_game.html b/src/shell_game.html new file mode 100644 index 0000000..8584e21 --- /dev/null +++ b/src/shell_game.html @@ -0,0 +1,138 @@ + + + + + + McRogueFace + + + + +
+
+

Loading...

+
+ + {{{ SCRIPT }}} + +