diff options
| author | cursed22bc <admin@pixeldawn.org> | 2026-03-10 23:16:58 +0200 |
|---|---|---|
| committer | cursed22bc <admin@pixeldawn.org> | 2026-03-10 23:16:58 +0200 |
| commit | 0dce23b4b24ae74889550dc0b81ea393c047f929 (patch) | |
| tree | 849eec42d84928ea235a51c1d6df984282e3af91 | |
| parent | d2dab6c8efcf126503c004e935fda9190533729c (diff) | |
evil window hacks
| -rw-r--r-- | conf.lua | 6 | ||||
| -rw-r--r-- | main.lua | 216 | ||||
| -rw-r--r-- | shaders/missing_texture.glsl | 8 | ||||
| -rw-r--r-- | world.lua | 2 |
4 files changed, 216 insertions, 16 deletions
@@ -10,6 +10,12 @@ function love.conf(t) t.audio.mic = false -- Request and use microphone capabilities in Android (boolean) t.audio.mixwithsystem = true -- Keep background music playing when opening LOVE (boolean, iOS and Android only) + t.window.title = "Openformer" + t.window.width = 1280 + t.window.height = 720 + t.window.resizable = true + t.window.minwidth = 800 + t.window.minheight = 450 t.window.vsync = 1 -- Vertical sync mode (number) t.window.msaa = 0 -- The number of samples to use with multi-sampled antialiasing (number) t.window.depth = nil -- The number of bits per sample in the depth buffer @@ -1,7 +1,7 @@ local VIRTUAL_WIDTH, VIRTUAL_HEIGHT = 16*10*5, 9*10*5 local CANVAS_PADDING = 6 -DEBUG = false +DEBUG = true local CANVAS_WIDTH = VIRTUAL_WIDTH + CANVAS_PADDING local CANVAS_HEIGHT = VIRTUAL_HEIGHT + CANVAS_PADDING @@ -13,6 +13,10 @@ local offsetX, offsetY = 0, 0 local dpiScale = 1 local canvas = nil local smoothCameraShader = nil +local menuBgShader = nil +local liquidShader = nil +local menuCanvas = nil +local menuTime = 0 local shaderEnabled = true local cameraModule = require("camera") @@ -22,7 +26,7 @@ local HUD = require("hud") local hud = nil local previousState = nil -local currentState = "game" +local currentState = "menu" local currentMapPath = "assets/maps/tilemap.lua" local currentTilesets = { tileset = { path = "assets/maps/tileset.png", tilewidth = 16, tileheight = 16 }, @@ -30,6 +34,15 @@ local currentTilesets = { } local world = nil +local menuItems = {} +local menuHover = nil + +local gameFinishPhase = nil +local gameFinishTimer = 0 +local irisRadius = 0 +local IRIS_CLOSE_DURATION = 1.5 +local TEXT_DISPLAY_DURATION = 5.0 + local states = { game = {}, menu = {}, @@ -39,41 +52,185 @@ local states = { local function recalcScale(w, h) dpiScale = (love.window.getDPIScale and love.window.getDPIScale()) or 1 - scale = math.max(1, math.floor(math.min(w / VIRTUAL_WIDTH, h / VIRTUAL_HEIGHT) / dpiScale)) + scale = math.max(1, math.min(w / VIRTUAL_WIDTH, h / VIRTUAL_HEIGHT) / dpiScale) finalScale = scale * dpiScale offsetX = math.floor((w - VIRTUAL_WIDTH * finalScale) / 2) offsetY = math.floor((h - VIRTUAL_HEIGHT * finalScale) / 2) end +local function screenToCanvas(sx, sy) + local drawX = offsetX - (CANVAS_PADDING * finalScale) / 2 + local drawY = offsetY - (CANVAS_PADDING * finalScale) / 2 + return (sx - drawX) / finalScale, (sy - drawY) / finalScale +end + +local function buildMenuItems() + local btnW, btnH = 120, 32 + local centerX = CANVAS_WIDTH / 2 + local centerY = CANVAS_HEIGHT / 2 + local spacing = 12 + local totalH = 2 * btnH + spacing + local startY = centerY - totalH / 2 + 20 + menuItems = { + { label = "Play", x = centerX - btnW / 2, y = startY, w = btnW, h = btnH, action = "play" }, + { label = "Exit", x = centerX - btnW / 2, y = startY + btnH + spacing, w = btnW, h = btnH, action = "exit" }, + } +end + function states.game.load() local World = require("world") world = World:new() world:load(currentMapPath, currentTilesets) local target = world:getPlayer() or { x = 0, y = 0, width = 16, height = 16 } - camera = cameraModule:new(target, VIRTUAL_WIDTH/2, VIRTUAL_HEIGHT/2, true, WORLD_TO_CANVAS) + camera = cameraModule:new(target, VIRTUAL_WIDTH / 2, VIRTUAL_HEIGHT / 2, true, WORLD_TO_CANVAS) world:setCamera(camera) hud = HUD:new() + gameFinishPhase = nil + gameFinishTimer = 0 + irisRadius = 0 end function states.game.update(dt) - if world then world:update(dt) end + if not world then return end + + if not gameFinishPhase then + world:update(dt) + if world.doorOpened then + gameFinishPhase = "iris" + gameFinishTimer = 0 + irisRadius = math.sqrt((CANVAS_WIDTH / 2) ^ 2 + (CANVAS_HEIGHT / 2) ^ 2) + end + elseif gameFinishPhase == "iris" then + world:update(dt) + gameFinishTimer = gameFinishTimer + dt + local maxR = math.sqrt((CANVAS_WIDTH / 2) ^ 2 + (CANVAS_HEIGHT / 2) ^ 2) + local progress = math.min(gameFinishTimer / IRIS_CLOSE_DURATION, 1) + local eased = 1 - (1 - progress) ^ 2 + irisRadius = maxR * (1 - eased) + if progress >= 1 then + gameFinishPhase = "text" + gameFinishTimer = 0 + irisRadius = 0 + end + elseif gameFinishPhase == "text" then + gameFinishTimer = gameFinishTimer + dt + if gameFinishTimer >= TEXT_DISPLAY_DURATION then + gameFinishPhase = nil + gameFinishTimer = 0 + currentState = "menu" + states.menu.load() + end + end end function states.game.draw() - if camera then camera:set() end - if world then world:draw() end - if camera then camera:unset() end + if gameFinishPhase ~= "text" then + if camera then camera:set() end + if world then world:draw() end + if camera then camera:unset() end + end + + if gameFinishPhase == "iris" then + local cx = CANVAS_WIDTH / 2 + local cy = CANVAS_HEIGHT / 2 + local outerR = math.sqrt(cx * cx + cy * cy) + 10 + local innerR = math.max(irisRadius, 0) + local segments = 64 + local verts = {} + for i = 0, segments do + local angle = (i / segments) * math.pi * 2 + local cs, sn = math.cos(angle), math.sin(angle) + verts[#verts + 1] = { cx + cs * innerR, cy + sn * innerR, 0, 0, 0, 0, 0, 1 } + verts[#verts + 1] = { cx + cs * outerR, cy + sn * outerR, 0, 0, 0, 0, 0, 1 } + end + local mesh = love.graphics.newMesh(verts, "strip", "stream") + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(mesh) + elseif gameFinishPhase == "text" then + love.graphics.setColor(0, 0, 0, 1) + love.graphics.rectangle("fill", 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT) + local font = fonts.get(14) + love.graphics.setFont(font) + love.graphics.setColor(1, 1, 1, 1) + love.graphics.printf("The Frog Escaped the Evil Aquarium", 0, CANVAS_HEIGHT / 2 - font:getHeight() / 2, CANVAS_WIDTH, "center") + love.graphics.setFont(fonts.default) + end end function states.menu.load() camera = nil world = nil + hud = nil + gameFinishPhase = nil + gameFinishTimer = 0 + irisRadius = 0 + menuTime = 0 + love.mouse.setVisible(true) + menuHover = nil end -function states.menu.update(dt) end +function states.menu.update(dt) + menuTime = menuTime + dt + local mx, my = love.mouse.getPosition() + local cx, cy = screenToCanvas(mx, my) + menuHover = nil + for _, btn in ipairs(menuItems) do + if cx >= btn.x and cx <= btn.x + btn.w and cy >= btn.y and cy <= btn.y + btn.h then + menuHover = btn.action + break + end + end +end function states.menu.draw() - love.graphics.print("Menu (state machine ready)", 10, 10) + local prevCanvas = love.graphics.getCanvas() + + love.graphics.setCanvas(menuCanvas) + love.graphics.clear() + if menuBgShader then + love.graphics.setShader(menuBgShader) + end + love.graphics.setColor(1, 1, 1, 1) + love.graphics.rectangle("fill", 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT) + love.graphics.setShader() + love.graphics.setCanvas(prevCanvas) + + if liquidShader then + liquidShader:send("scene", menuCanvas) + liquidShader:send("time", menuTime) + liquidShader:send("resolution", { CANVAS_WIDTH, CANVAS_HEIGHT }) + liquidShader:send("splashCenter", { -10.0, -10.0 }) + liquidShader:send("splashTime", 0.0) + love.graphics.setShader(liquidShader) + end + love.graphics.setColor(1, 1, 1, 1) + love.graphics.draw(menuCanvas, 0, 0) + love.graphics.setShader() + + local titleFont = fonts.get(20) + love.graphics.setFont(titleFont) + love.graphics.setColor(1, 1, 1, 1) + love.graphics.printf("The Frog Escapes Evil Aquarium", 0, CANVAS_HEIGHT / 2 - 80, CANVAS_WIDTH, "center") + + local btnFont = fonts.get(20) + love.graphics.setFont(btnFont) + for _, btn in ipairs(menuItems) do + local hover = menuHover == btn.action + + if hover then + love.graphics.setColor(1, 1, 1, 1) + love.graphics.setLineWidth(1) + love.graphics.rectangle("line", btn.x, btn.y, btn.w, btn.h) + end + + love.graphics.setColor(1, 1, 1, 1) + local tw = btnFont:getWidth(btn.label) + local th = btnFont:getHeight() + love.graphics.print(btn.label, btn.x + btn.w / 2 - tw / 2, btn.y + btn.h / 2 - th / 2) + end + + love.graphics.setFont(fonts.default) + love.graphics.setColor(1, 1, 1, 1) end function states.settings.load() @@ -103,7 +260,7 @@ end function love.load() love.graphics.setDefaultFilter("nearest", "nearest") - love.window.setTitle("Openformer") + love.window.setTitle("The Frog Escapes Evil Aquarium") fonts.load() love.graphics.setFont(fonts.default) @@ -116,10 +273,20 @@ function love.load() print("Warning: smooth_camera.glsl not loaded, using fallback (no sub-pixel offset)") end + local ok2, shader2 = pcall(love.graphics.newShader, "shaders/missing_texture.glsl") + menuBgShader = ok2 and shader2 or nil + + local ok3, shader3 = pcall(love.graphics.newShader, "shaders/liquid.glsl") + liquidShader = ok3 and shader3 or nil + + menuCanvas = love.graphics.newCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) + menuCanvas:setFilter("nearest", "nearest") + local w, h = love.graphics.getWidth(), love.graphics.getHeight() recalcScale(w, h) + buildMenuItems() - love.mouse.setVisible(false) + love.mouse.setVisible(true) local state = states[currentState] if state and state.load then state.load() end @@ -130,6 +297,9 @@ function love.resize(w, h) if canvas then canvas:release() end canvas = love.graphics.newCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) canvas:setFilter("nearest", "nearest") + if menuCanvas then menuCanvas:release() end + menuCanvas = love.graphics.newCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) + menuCanvas:setFilter("nearest", "nearest") end function love.update(dt) @@ -144,6 +314,12 @@ function love.keypressed(key, scancode, isrepeat) if key == "f1" and DEBUG then shaderEnabled = not shaderEnabled end + if key == "escape" and not isrepeat then + if currentState == "game" or currentState == "hud" then + currentState = "menu" + states.menu.load() + end + end if key == "tab" and not isrepeat then if currentState == "game" then previousState = currentState @@ -164,7 +340,21 @@ function love.keypressed(key, scancode, isrepeat) end function love.mousepressed(x, y, button) - if currentState == "hud" and hud then + if currentState == "menu" and button == 1 then + local cx, cy = screenToCanvas(x, y) + for _, btn in ipairs(menuItems) do + if cx >= btn.x and cx <= btn.x + btn.w and cy >= btn.y and cy <= btn.y + btn.h then + if btn.action == "play" then + currentState = "game" + states.game.load() + love.mouse.setVisible(false) + elseif btn.action == "exit" then + love.event.quit() + end + break + end + end + elseif currentState == "hud" and hud then hud:mousepressed(x, y, button) end end diff --git a/shaders/missing_texture.glsl b/shaders/missing_texture.glsl index 3824ade..e15a5ab 100644 --- a/shaders/missing_texture.glsl +++ b/shaders/missing_texture.glsl @@ -12,8 +12,12 @@ vec4 effect(vec4 color, Image texture, vec2 uv, vec2 screen_coords) float checker = mod(cx + cy, 2.0); - vec3 magenta = vec3(1.0, 0.0, 1.0); - vec3 black = vec3(0.0, 0.0, 0.0); + // vec3 magenta = vec3(1.0, 0.0, 1.0); + //magenta main menu hurts my eyes + // {0.22, 0.38, 0.22, 1}, + // {0.18, 0.30, 0.18, 1}, + vec3 magenta = vec3(0.22, 0.38, 0.22); + vec3 black = vec3(0.18, 0.30, 0.18); vec3 pattern = mix(magenta, black, checker); @@ -258,7 +258,7 @@ function World:handleDoor(door, player) else local remaining = needed - keyCount self.playerTextbox:show( - "Locked. You need " .. remaining .. " more key" .. (remaining > 1 and "s" or "") .. ".", + "Locked. I need " .. remaining .. " more key" .. (remaining > 1 and "s" or "") .. ".", { player.x + player.width / 2, player.y - 20, "center" }, "write", { life = 3, fontSize = 9, centeredText = true, wrapToFit = true } |
