infatuated/main.lua

351 lines
9.2 KiB
Lua

local oldprint = print
local function print(...)
oldprint("[Infatuated] "..string.format(...))
end
infatuated = {}
infatuated.print = print
infatuated.version = "11.3"
infatuated.versionCodename = "Mysterious Mysteries"
infatuated.conf = {
identity = nil,
appendidentity = false,
version = infatuated.version,
console = false,
accelerometerjoystick = true,
externalstorage = false,
gammacorrect = false,
audio = {
mic = false,
mixwithsystem = true,
},
window = {
--title = "Untitled",
icon = nil,
width = 800,
height = 600,
borderless = false,
resizable = false,
minwidth = 1,
minheight = 1,
fullscreen = false,
fullscreentype = "desktop",
vsync = 1,
msaa = 0,
depth = nil,
stencil = nil,
display = 1,
highdpi = false,
usedpiscale = true,
x = nil,
y = nil
},
modules = {
audio = true,
data = true,
event = true,
font = true,
graphics = true,
image = true,
joystick = true,
keyboard = true,
math = true,
mouse = true,
physics = true,
sound = true,
system = true,
thread = true,
timer = true,
touch = true,
video = true,
window = true
}
}
class = require "middleclass"
infatuated.classes = {}
local Object = class("Object")
infatuated.classes.Object = Object
function Object:initialize() end
function Object:release() end
function Object:type()
return tostring(self.class):match("^Class (.+)$")
end
function Object:typeOf(a)
assert(type(a) == "string", "bad argument #1 to typeOf (expected string)")
return self:type() == a or self:isInstanceOf(infatuated.classes[a])
end
local js = require "js"
infatuated.js = js
local window = js.global
local document = window.document
local canvas = document:getElementById("infatuated-canvas")
infatuated.canvas = canvas
local ctx = canvas:getContext("2d")
infatuated.ctx = ctx
infatuated.overlay = document:getElementById("infatuated-overlay")
infatuated.msgbox = {
root = document:getElementById("infatuated-msgbox"),
title = document:getElementById("infatuated-msgbox-title"),
text = document:getElementById("infatuated-msgbox-text"),
close = document:getElementById("infatuated-msgbox-close"),
buttons = document:getElementById("infatuated-msgbox-buttons")
}
function infatuated.msgbox:show(title, text, buttons, callback)
self.title.textContent = title
self.text.textContent = text
for k, v in pairs(document:querySelectorAll("#infatuated-msgbox-buttons button")) do
v:remove()
end
if buttons then
self.close.style.display = "unset"
function self.close.onclick()
self:hide()
callback(0)
end
for i=1, #buttons do
local btn = document:createElement("button")
btn.textContent = buttons[i]
--btn.dataset.id = i
self.buttons:append(btn)
btn:addEventListener("click", function()
self:hide()
callback(i)
end)
end
else
self.close.style.display = "none"
end
infatuated.overlay.dataset.hidden = false
self.root.dataset.hidden = false
end
function infatuated.msgbox:hide()
infatuated.canvas:focus()
infatuated.overlay.dataset.hidden = true
self.root.dataset.hidden = true
end
infatuated.path = window.infatuated.game:gsub("%.", "/")
infatuated.shims = {}
local i = 0
for name in (window.infatuated.shim..","):gmatch("(.+),") do
i = i+1
infatuated.shims[i] = name
end
print("Running game from path %q", infatuated.path)
local function errorprint(...)
window.console:error("[Infatuated] "..string.format(...))
end
function infatuated.stop(status)
print("Stopping with code %s", status or "null")
infatuated.stopDue = status
end
function infatuated.crash(errmsg)
errorprint("%s\n%s", errmsg, debug.traceback())
if infatuated.crashed then
print("Error handler crashed; stopping execution entirely")
infatuated.msgbox:show(
"Fatal error",
"An error occured, and then the error handler experienced an error of its own. Execution has stopped. Check console for details.",
{"OK"},
function() end
)
coroutine.yield()
elseif infatuated.crashInternal then
infatuated.msgbox:show(
"Fatal error",
"An error occurred during initialization and was not caught. Check console for details.",
{"OK"},
function() end
)
coroutine.yield()
else
infatuated.crashed = true
infatuated.loopGuest = love.errorhandler(errmsg)
end
end
function infatuated.parseColor(r, g, b, a)
if type(r) == "table" then
return infatuated.parseColor(r[1], r[2], r[3], r[4])
elseif type(r) == "number" then
a = a or 1
assert(type(g) == "number", "bad argument #2 to parseColor (expected number)")
assert(type(b) == "number", "bad argument #3 to parseColor (expected number)")
assert(type(a) == "number", "bad argument #4 to parseColor (expected number)")
r, g, b, a = r*255, g*255, b*255, a
return {r, g, b, a}, string.format("rgba(%f, %f, %f, %f)", r, g, b, a)
else
error("bad argument #1 to parseColor")
end
end
love = {}
function love.conf() end
function love.errorhandler(msg)
msg = tostring(msg)
if love.mouse then
love.mouse.setVisible(true)
love.mouse.setGrabbed(false)
--love.mouse.setRelativeMode(false)
love.mouse.setCursor()
end
if love.joystick then
for i, joystick in ipairs(love.joystick.getJoysticks()) do
joystick:setVibration()
end
end
if love.audio then love.audio.stop() end
love.graphics.reset()
-- original colors have absolute shit contrast
--local fr, fg, fb = 255, 255, 255
--local br, bg, bb = 89, 157, 220
local fr, fg, fb = 223, 223, 223
local br, bg, bb = 48, 48, 48
if love._version ~= "0.9.0" then
fr, fg, fb, br, bg, bb = fr/255, fg/255, fb/255, br/255, bg/255, bb/255
end
love.graphics.setColor(fr, fg, fb)
love.graphics.setBackgroundColor(br, bg, bb)
local p = 70
local url = (window.location.href.."?"):match("^(.-)?")
local traceback = debug.traceback(nil, 2):gsub(url, ""):gsub("\t", "\t\t\t\t")
local msg = string.format("An error has occurred.\n\n%s\n\n%s", msg, traceback)
return function()
local w, h = love.graphics.getDimensions()
love.graphics.clear()
love.graphics.printf(msg, p, p, w-p-p, "left")
love.graphics.present()
end
end
love.errhand = love.errorhandler
function love.run()
love.graphics.setColor(1, 1, 1, 1)
love.graphics.setBackgroundColor(0, 0, 0, 1)
love.graphics.setLineWidth(2)
if love.load then love.load() end
infatuated.startTime = window.performance:now()
infatuated.dt1 = infatuated.startTime
return function()
if infatuated.stopDue then return infatuated.stopDue end
if love.event then
love.event.pump()
-- TODO: varargs?
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
elseif love.handlers[name] then
love.handlers[name](a, b, c, d, e, f)
end
end
end
if love.update then love.update(infatuated.dt) end
if love.graphics and love.graphics.isActive() then
love.graphics.origin()
love.graphics.clear()
if love.draw then love.draw() end
love.graphics.present()
end
end
end
print("Coroutining now")
infatuated.thread = coroutine.create(function()
xpcall(function()
require "modules.audio"
require "modules.data"
require "modules.event"
require "modules.filesystem"
require "modules.graphics"
require "modules.handlers"
require "modules.image"
require "modules.math"
require "modules.mouse"
require "modules.system"
require "modules.timer"
require "modules.window"
love.run()
for i, path in ipairs(infatuated.shims) do
print("Shimming %s", path)
path = path:gsub("%.", "-")
require("shims."..path)
end
love._version = infatuated.version
love._version_codename = infatuated.versionCodename
print("Running as version %s (%s)", love._version, love._version_codename)
local oldrequire = require
infatuated.require = oldrequire
function require(path)
assert(type(path) == "string", "bad argument #1 to require")
if path == "love" then return love end
return oldrequire(infatuated.path.."."..path)
end
print("Importing conf")
local status, errmsg = pcall(oldrequire, infatuated.path..".conf")
if not status and not errmsg:find("^module '.-%.conf' not found:") then
infatuated.crash(errmsg)
end
if status then
local status, errmsg = pcall(love.conf, infatuated.conf)
if not status then infatuated.crash(errmsg) end
love.window.setTitle(infatuated.conf.window.title or infatuated.conf.title or "Untitled")
print("Importing main")
local status, errmsg = pcall(oldrequire, infatuated.path..".main")
if not status then infatuated.crash(errmsg) end
local status, errmsg = pcall(love.run)
if not status then
infatuated.crash(errmsg)
else
infatuated.loopGuest = errmsg
end
end
print("Starting event loop")
infatuated.dt1 = window.performance:now()
local function resume()
coroutine.resume(infatuated.thread)
end
while true do
local dt2 = window.performance:now()
infatuated.dt = (dt2-infatuated.dt1)/1000
infatuated.dt1 = dt2
local status, errmsg = pcall(infatuated.loopGuest)
if not status then
infatuated.crash(errmsg)
elseif errmsg then
print("Exited with code %d", errmsg or -1)
return
end
window:requestAnimationFrame(resume)
coroutine.yield()
end
end, function(errmsg)
infatuated.crashInternal = true
infatuated.crash(errmsg)
end)
end)
coroutine.resume(infatuated.thread)