summaryrefslogtreecommitdiff
path: root/main.lua
blob: 5a4e2f004d5b76809f088dc4d9933217ce535b9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
local VIRTUAL_WIDTH, VIRTUAL_HEIGHT = 16*10*3, 9*10*3
local CANVAS_PADDING = 6

DEBUG = true
local CANVAS_WIDTH = VIRTUAL_WIDTH + CANVAS_PADDING
local CANVAS_HEIGHT = VIRTUAL_HEIGHT + CANVAS_PADDING
local WORLD_TO_CANVAS = 3

local scale = 1
local finalScale = 1
local offsetX, offsetY = 0, 0
local dpiScale = 1
local canvas = nil
local smoothCameraShader = nil

local cameraModule = require("camera")
local camera = nil
local fonts = require("fonts")

local currentState = "game"
local currentMapPath = "assets/maps/tilemap.lua"
local currentTilesetPath = "assets/maps/tileset.png"
local world = nil

local states = {
    game = {},
    menu = {},
    settings = {}
}

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))
    finalScale = scale * dpiScale
    offsetX = math.floor((w - VIRTUAL_WIDTH * finalScale) / 2)
    offsetY = math.floor((h - VIRTUAL_HEIGHT * finalScale) / 2)
end

function states.game.load()
    local World = require("world")
    world = World:new()
    world:load(currentMapPath, currentTilesetPath)
    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)
    world:setCamera(camera)
end

function states.game.update(dt)
    if camera then camera:update(dt) end
    if world then world:update(dt) end
end

function states.game.draw()
    if camera then camera:set() end
    if world then world:draw() end
    if camera then camera:unset() end
end

function states.menu.load()
    camera = nil
    world = nil
end

function states.menu.update(dt) end

function states.menu.draw()
    love.graphics.print("Menu (state machine ready)", 10, 10)
end

function states.settings.load()
    camera = nil
    world = nil
end

function states.settings.update(dt) end

function states.settings.draw()
    love.graphics.print("Settings", 10, 10)
end

function love.load()
    love.graphics.setDefaultFilter("nearest", "nearest")
    love.window.setTitle("Openformer")
    fonts.load()
    love.graphics.setFont(fonts.default)

    canvas = love.graphics.newCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
    canvas:setFilter("nearest", "nearest")

    local ok, shader = pcall(love.graphics.newShader, "shaders/smooth_camera.glsl")
    smoothCameraShader = ok and shader or nil
    if not smoothCameraShader then
        print("Warning: smooth_camera.glsl not loaded, using fallback (no sub-pixel offset)")
    end

    local w, h = love.graphics.getWidth(), love.graphics.getHeight()
    recalcScale(w, h)

    local state = states[currentState]
    if state and state.load then state.load() end
end

function love.resize(w, h)
    recalcScale(w, h)
    if canvas then canvas:release() end
    canvas = love.graphics.newCanvas(CANVAS_WIDTH, CANVAS_HEIGHT)
    canvas:setFilter("nearest", "nearest")
end

function love.update(dt)
    local state = states[currentState]
    if state and state.update then state.update(dt) end
end

function love.keypressed(key, scancode, isrepeat)
    if key == "f11" then
        love.window.setFullscreen(not love.window.getFullscreen(), "desktop")
    end
    if (key == "space" or key == "up" or key == "w") and not isrepeat then
        local player = world and world:getPlayer()
        if player then player:jump() end
    end
end

function love.draw()
    love.graphics.setCanvas(canvas)
    love.graphics.clear()
    love.graphics.push()
    local state = states[currentState]
    if state and state.draw then state.draw() end
    love.graphics.pop()
    love.graphics.setCanvas()

    love.graphics.clear()
    local drawX = offsetX - (CANVAS_PADDING * finalScale) / 2
    local drawY = offsetY - (CANVAS_PADDING * finalScale) / 2

    local subDx, subDy = 0, 0
    if world and world.camera and world.camera.getSubPixelOffset then
        subDx, subDy = world.camera:getSubPixelOffset()
    end

    local uvOffsetX = subDx * WORLD_TO_CANVAS / CANVAS_WIDTH
    local uvOffsetY = subDy * WORLD_TO_CANVAS / CANVAS_HEIGHT

    if smoothCameraShader and (subDx ~= 0 or subDy ~= 0) then
        smoothCameraShader:send("offset", { uvOffsetX, uvOffsetY })
        love.graphics.setShader(smoothCameraShader)
    end

    love.graphics.draw(canvas, math.floor(drawX), math.floor(drawY), 0, finalScale, finalScale)

    if smoothCameraShader then
        love.graphics.setShader()
    end
end

return nil