Files
tromi_mobile/main.lua
2024-10-27 05:56:40 +07:00

575 lines
20 KiB
Lua

if os.getenv("LOCAL_LUA_DEBUGGER_VSCODE")=="1" then
LLDEBUGGER=require('lldebugger')
LLDEBUGGER.start()
end
PENTO_MODE = false
SAVE_DIR = 'saves/'
REPLAY_DIR = 'saves/replays/'
CONFIG_FILE = 'config.sav'
HIscoreFILE = 'hiscores.sav'
if not love.filesystem.getInfo(REPLAY_DIR) then
love.filesystem.createDirectory(REPLAY_DIR)
end
BACKGROUND_COLOR = {0, 0, 0}
-- BACKGROUND_COLOR = {32/255, 120/255, 88/255}
CURRENT_OS = love.system.getOS()
MOBILE = CURRENT_OS == "Android" or CURRENT_OS == "iOS"
LILY = require "libs.lily"
function love.load()
require 'funcs'
math.randomseed(os.time())
require "modules.file" "binser"
require "settings"
-- Window stuffs
love.window.setMode(love.graphics.getWidth(), love.graphics.getHeight(), {resizable = true});
love.window.setFullscreen(SETTINGS["fullscreen"])
-- Make a new transformer
GLOBAL_TRANSFORM = love.math.newTransform()
love.resize(love.graphics.getWidth(), love.graphics.getHeight())
-- Now it's real time to load all stuffs!
require "load" -- Most game's resources are loaded in here
require "scene"
require "game.vctrl" -- VCTRL
SCENE = LoadingScene()
sceneStack = {SCENE}
VCTRL.new(SETTINGS.input.virtual)
end
function love.resize(w, h)
SCREEN_SCALE_FACTOR = math.min(w / 640, h / 480)
GLOBAL_TRANSFORM:setTransformation(
(w - SCREEN_SCALE_FACTOR * 640) / 2,
(h - SCREEN_SCALE_FACTOR * 480) / 2,
0,
SCREEN_SCALE_FACTOR
)
end
function love.draw()
love.graphics.replaceTransform(GLOBAL_TRANSFORM)
love.graphics.clear()
love.graphics.push()
SCENE:render()
-- -- Grid system
-- local grid_width, grid_height = 40, 20
-- love.graphics.setColor(1,1,1,0.5)
-- love.graphics.setLineWidth(1)
-- -- From 0 to X
-- for ix=0,math.floor(640 / grid_width) do
-- love.graphics.line(grid_width * ix, 0 , grid_width * ix, 480)
-- end
-- -- From 0 to Y
-- for iy=0,math.floor(480 / grid_height) do
-- love.graphics.line(0, grid_height * iy, 640, grid_height * iy)
-- end
local x, y = GLOBAL_TRANSFORM:inverseTransformPoint(love.mouse.getPosition())
love.graphics.setColor(0, 0, 0, 0.8)
love.graphics.rectangle("fill", 5, 450, 115, 25)
drawText(string.format("X: %.1d; Y: %.1d", x, y), 10, 455, 110, "left")
love.graphics.pop()
end
function love.mousepressed(x, y, b, isTouch, presses)
if isTouch then return end
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
SCENE:onInputPress{type = "mouse", x = x, y = y, presses = presses}
end
function love.mousereleased(x, y, b, isTouch, presses)
if isTouch then return end
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
SCENE:onInputRelease{type = "mouse", x = x, y = y, presses = presses}
end
function love.mousemoved(x, y, dx, dy, isTouch)
if isTouch then return end
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
local dx,dy=dx/SCREEN_SCALE_FACTOR,dy/SCREEN_SCALE_FACTOR
SCENE:onInputMove{type = "mouse", x = x, y = y, dx = dx, dy = dy}
end
function love.wheelmoved(dx, dy)
SCENE:onInputMove{type = "wheel", dx = dx, dy = dy}
end
function love.touchpressed(id,x,y)
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
SCENE:onInputPress{type = "touch", x = x, y = y, dx = 0, dy = 0, id = id}
end
function love.touchmoved(id,x,y,dx,dy)
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
SCENE:onInputMove{type = "touch", x = x, y = y, dx = dx, dy = dy, id = id}
end
function love.touchreleased(id,x,y)
local x,y=GLOBAL_TRANSFORM:inverseTransformPoint(x,y)
SCENE:onInputRelease{type = "touch", x = x, y = y, dx = 0, dy = 0, id = id}
end
function love.keypressed(key, scancode)
local input_pressed=nil
-- global hotkeys
if scancode == "f4" then
SETTINGS["fullscreen"] = not SETTINGS["fullscreen"]
love.window.setFullscreen(SETTINGS["fullscreen"])
love.resize(love.graphics.getDimensions())
elseif scancode == "f2" and SCENE.title ~= "Input Config" and SCENE.title ~= "Game" then
SCENE = InputConfigScene()
elseif scancode == "f12" then LLDEBUGGER.requestBreak()
-- elseif scancode == "f11" then SETTINGS.firstTime = true
-- function keys are reserved
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f1[0-2]+$") then
return
-- escape is reserved for menu_back
elseif scancode == "escape" then
SCENE:onInputPress{input="menu_back", type="key", key=key, scancode=scancode}
-- pass any other key to the scene, with its configured mapping
else
if SETTINGS.input and SETTINGS.input.keys then
input_pressed = SETTINGS.input.keys[scancode]
end
SCENE:onInputPress{input=input_pressed, type="key", key=key, scancode=scancode}
end
end
function love.keyreleased(key, scancode)
local input_released = nil
-- escape is reserved for menu_back
if scancode == "escape" or scancode == 'acback' then
SCENE:onInputRelease{input="menu_back", type="key", key=key, scancode=scancode}
-- function keys are reserved
elseif string.match(scancode, "^f[1-9]$") or string.match(scancode, "^f1[0-2]+$") then
return
-- handle all other keys; tab is reserved, but the input config scene keeps it from getting configured as a game input, so pass tab to the scene here
else
if SETTINGS.input and SETTINGS.input.keys then
input_released = SETTINGS.input.keys[scancode]
end
SCENE:onInputRelease{input=input_released, type="key", key=key, scancode=scancode}
end
end
function love.joystickpressed(joystick, button)
local input_pressed = nil
if
SETTINGS.input and
SETTINGS.input.joysticks and
SETTINGS.input.joysticks[joystick:getName()] and
SETTINGS.input.joysticks[joystick:getName()].buttons
then
input_pressed = SETTINGS.input.joysticks[joystick:getName()].buttons[button]
end
SCENE:onInputPress({input=input_pressed, type="joybutton", name=joystick:getName(), button=button})
end
function love.joystickreleased(joystick, button)
local input_released = nil
if
SETTINGS.input and
SETTINGS.input.joysticks and
SETTINGS.input.joysticks[joystick:getName()] and
SETTINGS.input.joysticks[joystick:getName()].buttons
then
input_released = SETTINGS.input.joysticks[joystick:getName()].buttons[button]
end
SCENE:onInputRelease({input=input_released, type="joybutton", name=joystick:getName(), button=button})
end
function love.joystickaxis(joystick, axis, value)
local input_pressed = nil
local positive_released = nil
local negative_released = nil
if
SETTINGS.input and
SETTINGS.input.joysticks and
SETTINGS.input.joysticks[joystick:getName()] and
SETTINGS.input.joysticks[joystick:getName()].axes and
SETTINGS.input.joysticks[joystick:getName()].axes[axis]
then
if math.abs(value) >= 1 then
input_pressed = SETTINGS.input.joysticks[joystick:getName()].axes[axis][value >= 1 and "positive" or "negative"]
end
positive_released = SETTINGS.input.joysticks[joystick:getName()].axes[axis].positive
negative_released = SETTINGS.input.joysticks[joystick:getName()].axes[axis].negative
end
if math.abs(value) >= 1 then
SCENE:onInputPress({input=input_pressed, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
else
SCENE:onInputRelease({input=positive_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
SCENE:onInputRelease({input=negative_released, type="joyaxis", name=joystick:getName(), axis=axis, value=value})
end
end
local last_hat_direction = ""
local directions = {
["u"] = "up",
["d"] = "down",
["l"] = "left",
["r"] = "right",
}
function love.joystickhat(joystick, hat, direction)
local input_pressed = nil
local has_hat = false
if
SETTINGS.input and
SETTINGS.input.joysticks and
SETTINGS.input.joysticks[joystick:getName()] and
SETTINGS.input.joysticks[joystick:getName()].hats and
SETTINGS.input.joysticks[joystick:getName()].hats[hat]
then
if direction ~= "c" then
input_pressed = SETTINGS.input.joysticks[joystick:getName()].hats[hat][direction]
end
has_hat = true
end
if input_pressed then
for i = 1, #direction do
local char = direction:sub(i, i)
local _, count = last_hat_direction:gsub(char, char)
if count == 0 then
SCENE:onInputPress({input=SETTINGS.input.joysticks[joystick:getName()].hats[hat][char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
for i = 1, #last_hat_direction do
local char = last_hat_direction:sub(i, i)
local _, count = direction:gsub(char, char)
if count == 0 then
SCENE:onInputRelease({input=SETTINGS.input.joysticks[joystick:getName()].hats[hat][char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
last_hat_direction = direction
elseif has_hat then
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
SCENE:onInputRelease({input=SETTINGS.input.joysticks[joystick:getName()].hats[hat][direction], type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end
last_hat_direction = ""
elseif direction ~= "c" then
for i = 1, #direction do
local char = direction:sub(i, i)
local _, count = last_hat_direction:gsub(char, char)
if count == 0 then
SCENE:onInputPress({input=directions[char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
for i = 1, #last_hat_direction do
local char = last_hat_direction:sub(i, i)
local _, count = direction:gsub(char, char)
if count == 0 then
SCENE:onInputRelease({input=directions[char], type="joyhat", name=joystick:getName(), hat=hat, direction=char})
end
end
last_hat_direction = direction
else
for i, direction in ipairs{"d", "l", "ld", "lu", "r", "rd", "ru", "u"} do
SCENE:onInputRelease({input=nil, type="joyhat", name=joystick:getName(), hat=hat, direction=direction})
end
last_hat_direction = ""
end
end
function love.focus(f) end
function love.quit()
LILY.quit()
end
local TARGET_FPS = 60
function love.run()
if love.load then love.load(love.arg.parseGameArguments(arg), arg) end
if love.timer then love.timer.step() end
local dt = 0
local last_time = love.timer.getTime()
local time_accumulator = 0
return function()
if love.event then
love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do
if name == "quit" then
if not love.quit or not love.quit() then
return a or 0
end
end
love.handlers[name](a,b,c,d,e,f)
end
end
if SCENE and SCENE.update and love.timer then
SCENE:update()
local frame_duration = 1.0 / TARGET_FPS
if time_accumulator < frame_duration then
if love.graphics and love.graphics.isActive() and love.draw then
love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor())
love.draw()
love.graphics.present()
end
local end_time = last_time + frame_duration
local time = love.timer.getTime()
while time < end_time do
love.timer.sleep(0.001)
time = love.timer.getTime()
end
time_accumulator = time_accumulator + time - last_time
end
time_accumulator = time_accumulator - frame_duration
end
last_time = love.timer.getTime()
end
end
function love.errorhandler(msg)
local msg = msg or "I don't know if mycophobia's real name is \"mycophobiatrophilusiania\" or not.\nBUT WHO ATE THE ERROR MESSAGE?! DTET_ENJOYER, DID YOU?\n"
local showScreenshot = false
local errorCopied = false
local enter_fullscreen = SETTINGS and SETTINGS["fullscreen"] or false
-- Handling the error
local err={"Error:"..msg}
local c=2
for l in debug.traceback("",2):gmatch("(.-)\n") do
if c>2 then
if not l:find("boot") then
err[c]=l:gsub("^\t*","\t")
c=c+1
end
else
err[2]="Traceback"
c=3
end
end
print("\n"..table.concat(err,"\n",1,c-2))
local tracebacks = table.concat(err,"\n", 4)
if drawText == nil or FONT_tromi == nil then
love.audio.stop()
love.graphics.origin()
love.graphics.setFont(love.graphics.newFont(20))
return function()
love.event.pump()
for e, a, b, c in love.event.poll() do
if (
e == "quit" or
e == "mousepressed"
) then return 1 end
end
love.graphics.clear()
love.graphics.print(err[1]:sub(7).."\nTraceback:\n"..tracebacks)
love.graphics.present()
love.timer.sleep(0.001)
end
end
-- Try to create a canva
love.graphics.origin()
local screenshot_canva, screenshot_canva_scale
local ok, _ = pcall(function()
if love.graphics.getSystemLimits().texturesize >= 1280 then
screenshot_canva = love.graphics.newCanvas(1280, 960)
screenshot_canva_scale = 0.5
else
error()
end
end)
if not ok then
screenshot_canva = love.graphics.newCanvas(640, 480)
screenshot_canva_scale = 1
end
-- Then draw everything again
love.graphics.setCanvas(screenshot_canva)
pcall(
function()
love.graphics.origin()
local transformer = love.math.newTransform(0, 0, 0, 2, 2)
love.graphics.replaceTransform(transformer)
SCENE:render()
end
)
love.audio.stop()
love.graphics.setCanvas()
local function draw()
love.graphics.origin()
love.graphics.replaceTransform(GLOBAL_TRANSFORM)
love.graphics.clear()
love.graphics.setColor(1, 1, 1)
love.graphics.draw(screenshot_canva, 0, 0, 0, screenshot_canva_scale)
if not showScreenshot then
love.graphics.setColor(0, 0, 0, 0.75)
love.graphics.rectangle("fill", 0, 0, 640, 480)
drawText([[
OH NO! Tromi has crashed.
Since this is not the official port, please do not report any bugs to mycophobia.
Instead, report this to me via my Discord ``sweetsea'' with a screenshot of this.
REMEMBER TO SCREENSHOT ERROR INFO BEFORE QUITTING BECAUSE THEY ARE NOT SAVED!
Ctrl + C: copy the error info | If you click / tap, a window appears with 4 options:
Space : show/hide screenshot | OK : Quit Copy: copy error info
Escape : Quit | Cancel: Go back Show: show/hide screenshot
]], 20, 10, 620, "left")
drawText(err[1]:sub(7).."\nTraceback:"..(errorCopied and " (Copied to clipboard)\n" or "\n")..tracebacks, 20, 180, 620, "left")
else
love.graphics.setColor(0, 0, 0, 0.8)
love.graphics.rectangle("fill", 15, 450, 400, 25, 5, 5)
drawText("Tromi has crashed! Press Space or tap to show error info", 15, 455, 400, "left")
end
love.graphics.present()
end
local fullErrorText = tracebacks
local function copyToClipboard()
love.system.setClipboardText(fullErrorText)
errorCopied = true
end
local buttons = {"OK", "Cancel", "Copy", "Show"}
return function()
love.event.pump()
for e, a, b, c in love.event.poll() do
if e == "quit" then
return 1
elseif e == "keypressed" and a == "escape" then
return 1
elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then
copyToClipboard()
elseif e == "keypressed" and a == "space" then
showScreenshot = not showScreenshot
elseif e == "keypressed" and a == "f4" then
enter_fullscreen = not enter_fullscreen
love.window.setFullscreen(enter_fullscreen)
elseif e == "mousepressed" then
local pressed = love.window.showMessageBox("Quit? Screenshot? Copy?", "Remember to screenshot error info before quitting because they are not saved!", buttons)
if pressed == 1 then
return 1
elseif pressed == 3 then
copyToClipboard()
elseif pressed == 4 then
showScreenshot = not showScreenshot
end
elseif e == "resize" then
love.resize(love.graphics.getDimensions())
end
end
draw()
if love.timer then
love.timer.sleep(0.1)
end
end
end
local minos = {'R_d', 'O_d', 'Y_d', 'G_d', 'C_d', 'B_d', 'M_d'}
local main_bg_grid = {}
for x=1, 40 do
main_bg_grid[x] = {}
for y=1, 30 do
main_bg_grid[x][y] = 0
end
end
local main_bg_cur_pos = {20,6}
local main_bg_cur_color = minos[love.math.random(1,7)]
local main_bg_cur_mino = 1
local main_bg_draw_frame = 0
local main_bg_last_color = nil
function MainBackground()
love.graphics.setColor(BACKGROUND_COLOR)
love.graphics.rectangle("fill", 0, 0, 640, 480)
if SETTINGS["music"] and not SOUNDS["bgm_title"]:isPlaying() then
SOUNDS["bgm_title"]:setVolume(0.3)
SOUNDS["bgm_title"]:play()
end
local y = 40
if main_bg_draw_frame >= 16 then
while y > 1 do
for x = 1, 40 do
main_bg_grid[x][y] = main_bg_grid[x][y-1]
end
y = y - 1
end
for x=1, 40 do
main_bg_grid[x][1] = 0
end
main_bg_draw_frame = 0
main_bg_cur_pos[2] = main_bg_cur_pos[2] + 1
end
local directions = { {0,-1},{1,0}, {-1,0}}
local test_dir = directions[love.math.random(1,3)]
main_bg_cur_pos[1] = main_bg_cur_pos[1] + test_dir[1]
main_bg_cur_pos[2] = main_bg_cur_pos[2] + test_dir[2]
if main_bg_cur_pos[1] > 40 then main_bg_cur_pos[1] = 40 end
if main_bg_cur_pos[1] < 1 then main_bg_cur_pos[1] = 1 end
if main_bg_cur_pos[2] > 30 then main_bg_cur_pos[2] = 30 end
if main_bg_cur_pos[2] < 1 then main_bg_cur_pos[2] = 1 end
if main_bg_grid[main_bg_cur_pos[1]][main_bg_cur_pos[2]] == 0 then
main_bg_grid[main_bg_cur_pos[1]][main_bg_cur_pos[2]] = main_bg_cur_color
main_bg_cur_mino = main_bg_cur_mino + 1
end
for x=1,40 do
for y=1,30 do
if main_bg_grid[x][y] ~= 0 then
love.graphics.setColor(1, 1, 1, 0.4)
if ((x-1)*48)-560 > 0 and ((x-1)*48)-560 < 640 then love.graphics.draw(BLOCKS["2tie"][main_bg_grid[x][y]], ((x-1)*48)-570, (((y+2)*48)+main_bg_draw_frame*3)-480,0, 3) end
love.graphics.setColor(1, 1, 1, 1)
end
end
end
for x=1,40 do
for y=1,30 do
if main_bg_grid[x][y] ~= 0 then
love.graphics.setColor(1, 1, 1, 1)
love.graphics.draw(BLOCKS["2tie"][main_bg_grid[x][y]], (x-1)*16, ((y-1)*16)+main_bg_draw_frame)
end
end
end
for x=1,40 do
for y=1,30 do
if main_bg_grid[x][y] ~= 0 then
love.graphics.setColor(1, 1, 1, 0.6)
if ((x-1)*32)-320 > 0 and ((x-1)*32)-320 < 640 then love.graphics.draw(BLOCKS["2tie"][main_bg_grid[x][y]], ((x-1)*32)-320, (((y+1)*32)+main_bg_draw_frame*2)-320,0, 2) end
love.graphics.setColor(1, 1, 1, 1)
end
end
end
if main_bg_cur_mino == 5 then
--if main_bg_cur_pos[2] < 4 then
-- main_bg_cur_pos[2] = 4
-- main_bg_cur_pos[1] = love.math.random(4, 36)
--end
main_bg_cur_pos = {love.math.random(16,24),6}
main_bg_last_color = main_bg_cur_color
while main_bg_cur_color == main_bg_last_color do main_bg_cur_color = minos[love.math.random(1,7)] end
main_bg_cur_mino = 1
end
main_bg_draw_frame = main_bg_draw_frame + 1
end