123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- --[[
- Copyright (c) 2006-2015 LOVE Development Team
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
- --]]
- -- Make sure love exists.
- local love = require("love")
- -- Used for setup:
- love.path = {}
- love.arg = {}
- -- Replace any \ with /.
- function love.path.normalslashes(p)
- return string.gsub(p, "\\", "/")
- end
- -- Makes sure there is a slash at the end
- -- of a path.
- function love.path.endslash(p)
- if string.sub(p, string.len(p)-1) ~= "/" then
- return p .. "/"
- else
- return p
- end
- end
- -- Checks whether a path is absolute or not.
- function love.path.abs(p)
- local tmp = love.path.normalslashes(p)
- -- Path is absolute if it starts with a "/".
- if string.find(tmp, "/") == 1 then
- return true
- end
- -- Path is absolute if it starts with a
- -- letter followed by a colon.
- if string.find(tmp, "%a:") == 1 then
- return true
- end
- -- Relative.
- return false
- end
- -- Converts any path into a full path.
- function love.path.getfull(p)
- if love.path.abs(p) then
- return love.path.normalslashes(p)
- end
- local cwd = love.filesystem.getWorkingDirectory()
- cwd = love.path.normalslashes(cwd)
- cwd = love.path.endslash(cwd)
- -- Construct a full path.
- local full = cwd .. love.path.normalslashes(p)
- -- Remove trailing /., if applicable
- return full:match("(.-)/%.$") or full
- end
- -- Returns the leaf of a full path.
- function love.path.leaf(p)
- p = love.path.normalslashes(p)
- local a = 1
- local last = p
- while a do
- a = string.find(p, "/", a+1)
- if a then
- last = string.sub(p, a+1)
- end
- end
- return last
- end
- -- Finds the key in the table with the lowest integral index. The lowest
- -- will typically the executable, for instance "lua5.1.exe".
- function love.arg.getLow(a)
- local m = math.huge
- for k,v in pairs(a) do
- if k < m then
- m = k
- end
- end
- return a[m]
- end
- love.arg.options = {
- console = { a = 0 },
- fused = {a = 0 },
- game = { a = 1 }
- }
- function love.arg.parse_option(m, i)
- m.set = true
- if m.a > 0 then
- m.arg = {}
- for j=i,i+m.a-1 do
- table.insert(m.arg, arg[j])
- i = j
- end
- end
- return i
- end
- function love.arg.parse_options()
- local game
- local argc = #arg
- for i=1,argc do
- -- Look for options.
- local s, e, m = string.find(arg[i], "%-%-(.+)")
- if m and love.arg.options[m] then
- i = love.arg.parse_option(love.arg.options[m], i+1)
- elseif not game then
- game = i
- end
- end
- if not love.arg.options.game.set then
- love.arg.parse_option(love.arg.options.game, game or 0)
- end
- end
- function love.createhandlers()
- -- Standard callback handlers.
- love.handlers = setmetatable({
- keypressed = function (b,s,r)
- if love.keypressed then return love.keypressed(b,s,r) end
- end,
- keyreleased = function (b,s)
- if love.keyreleased then return love.keyreleased(b,s) end
- end,
- textinput = function (t)
- if love.textinput then return love.textinput(t) end
- end,
- textedited = function (t,s,l)
- if love.textedited then return love.textedited(t,s,l) end
- end,
- mousemoved = function (x,y,dx,dy,t)
- if love.mousemoved then return love.mousemoved(x,y,dx,dy,t) end
- end,
- mousepressed = function (x,y,b,t)
- if love.mousepressed then return love.mousepressed(x,y,b,t) end
- end,
- mousereleased = function (x,y,b,t)
- if love.mousereleased then return love.mousereleased(x,y,b,t) end
- end,
- wheelmoved = function (x,y)
- if love.wheelmoved then return love.wheelmoved(x,y) end
- end,
- touchpressed = function (id,x,y,dx,dy,p)
- if love.touchpressed then return love.touchpressed(id,x,y,dx,dy,p) end
- end,
- touchreleased = function (id,x,y,dx,dy,p)
- if love.touchreleased then return love.touchreleased(id,x,y,dx,dy,p) end
- end,
- touchmoved = function (id,x,y,dx,dy,p)
- if love.touchmoved then return love.touchmoved(id,x,y,dx,dy,p) end
- end,
- joystickpressed = function (j,b)
- if love.joystickpressed then return love.joystickpressed(j,b) end
- end,
- joystickreleased = function (j,b)
- if love.joystickreleased then return love.joystickreleased(j,b) end
- end,
- joystickaxis = function (j,a,v)
- if love.joystickaxis then return love.joystickaxis(j,a,v) end
- end,
- joystickhat = function (j,h,v)
- if love.joystickhat then return love.joystickhat(j,h,v) end
- end,
- gamepadpressed = function (j,b)
- if love.gamepadpressed then return love.gamepadpressed(j,b) end
- end,
- gamepadreleased = function (j,b)
- if love.gamepadreleased then return love.gamepadreleased(j,b) end
- end,
- gamepadaxis = function (j,a,v)
- if love.gamepadaxis then return love.gamepadaxis(j,a,v) end
- end,
- joystickadded = function (j)
- if love.joystickadded then return love.joystickadded(j) end
- end,
- joystickremoved = function (j)
- if love.joystickremoved then return love.joystickremoved(j) end
- end,
- focus = function (f)
- if love.focus then return love.focus(f) end
- end,
- mousefocus = function (f)
- if love.mousefocus then return love.mousefocus(f) end
- end,
- visible = function (v)
- if love.visible then return love.visible(v) end
- end,
- quit = function ()
- return
- end,
- threaderror = function (t, err)
- if love.threaderror then return love.threaderror(t, err) end
- end,
- resize = function (w, h)
- if love.resize then return love.resize(w, h) end
- end,
- filedropped = function (f)
- if love.filedropped then return love.filedropped(f) end
- end,
- directorydropped = function (dir)
- if love.directorydropped then return love.directorydropped(dir) end
- end,
- lowmemory = function ()
- collectgarbage()
- if love.lowmemory then return love.lowmemory() end
- end,
- }, {
- __index = function(self, name)
- error("Unknown event: " .. name)
- end,
- })
- end
- local function uridecode(s)
- return s:gsub("%%%x%x", function(str)
- return string.char(tonumber(str:sub(2), 16))
- end)
- end
- local no_game_code = false
- -- This can't be overriden.
- function love.boot()
- -- This is absolutely needed.
- require("love.filesystem")
- love.arg.parse_options()
- local o = love.arg.options
- local arg0 = love.arg.getLow(arg)
- love.filesystem.init(arg0)
- local exepath = love.filesystem.getExecutablePath()
- if #exepath == 0 then
- -- This shouldn't happen, but just in case we'll fall back to arg0.
- exepath = arg0
- end
- -- Is this one of those fancy "fused" games?
- local can_has_game = pcall(love.filesystem.setSource, exepath)
- local is_fused_game = can_has_game or love.arg.options.fused.set
- love.filesystem.setFused(is_fused_game)
- local identity = ""
- if not can_has_game and o.game.set and o.game.arg[1] then
- local nouri = o.game.arg[1]
- if nouri:sub(1, 7) == "file://" then
- nouri = uridecode(nouri:sub(8))
- end
- local full_source = love.path.getfull(nouri)
- can_has_game = pcall(love.filesystem.setSource, full_source)
- -- Use the name of the source .love as the identity for now.
- identity = love.path.leaf(full_source)
- else
- -- Use the name of the exe as the identity for now.
- identity = love.path.leaf(exepath)
- end
- -- Try to use the archive containing main.lua as the identity name. It
- -- might not be available, in which case the fallbacks above are used.
- local realdir = love.filesystem.getRealDirectory("main.lua")
- if realdir then
- identity = love.path.leaf(realdir)
- end
- identity = identity:gsub("^([%.]+)", "") -- strip leading "."'s
- identity = identity:gsub("%.([^%.]+)$", "") -- strip extension
- identity = identity:gsub("%.", "_") -- replace remaining "."'s with "_"
- identity = #identity > 0 and identity or "lovegame"
- -- When conf.lua is initially loaded, the main source should be checked
- -- before the save directory (the identity should be appended.)
- pcall(love.filesystem.setIdentity, identity, true)
- if can_has_game and not (love.filesystem.isFile("main.lua") or love.filesystem.isFile("conf.lua")) then
- no_game_code = true
- end
- if not can_has_game then
- local nogame = require("love.nogame")
- nogame()
- end
- end
- function love.init()
- -- Create default configuration settings.
- -- NOTE: Adding a new module to the modules list
- -- will NOT make it load, see below.
- local c = {
- title = "Untitled",
- version = love._version,
- window = {
- width = 800,
- height = 600,
- x = nil,
- y = nil,
- minwidth = 1,
- minheight = 1,
- fullscreen = false,
- fullscreentype = "desktop",
- display = 1,
- vsync = true,
- msaa = 0,
- borderless = false,
- resizable = false,
- centered = true,
- highdpi = false,
- },
- modules = {
- event = true,
- keyboard = true,
- mouse = true,
- timer = true,
- joystick = true,
- touch = true,
- image = true,
- graphics = true,
- audio = true,
- math = true,
- physics = true,
- sound = true,
- system = true,
- font = true,
- thread = true,
- window = true,
- video = true,
- },
- console = false, -- Only relevant for windows.
- identity = false,
- appendidentity = false,
- useexternalstorage = false, -- Only relevant for Android.
- accelerometerjoystick = true, -- Only relevant for Android / iOS.
- gammacorrect = false,
- }
- -- Console hack, part 1.
- local openedconsole = false
- if love.arg.options.console.set and love._openConsole then
- love._openConsole()
- openedconsole = true
- end
- -- If config file exists, load it and allow it to update config table.
- local confok, conferr
- if (not love.conf) and love.filesystem and love.filesystem.isFile("conf.lua") then
- confok, conferr = pcall(require, "conf")
- end
- -- Yes, conf.lua might not exist, but there are other ways of making
- -- love.conf appear, so we should check for it anyway.
- if love.conf then
- confok, conferr = pcall(love.conf, c)
- -- If love.conf errors, we'll trigger the error after loading modules so
- -- the error message can be displayed in the window.
- end
- -- Console hack, part 2.
- if c.console and love._openConsole and not openedconsole then
- love._openConsole()
- end
- -- Hack for disabling accelerometer-as-joystick on Android / iOS.
- if love._setAccelerometerAsJoystick then
- love._setAccelerometerAsJoystick(c.accelerometerjoystick)
- end
- if love._setGammaCorrect then
- love._setGammaCorrect(c.gammacorrect)
- end
- -- Gets desired modules.
- for k,v in ipairs{
- "thread",
- "timer",
- "event",
- "keyboard",
- "joystick",
- "mouse",
- "touch",
- "sound",
- "system",
- "audio",
- "image",
- "video",
- "font",
- "window",
- "graphics",
- "math",
- "physics",
- } do
- if c.modules[v] then
- require("love." .. v)
- end
- end
- if love.event then
- love.createhandlers()
- end
- -- Check the version
- c.version = tostring(c.version)
- if not love.isVersionCompatible(c.version) then
- local major, minor, revision = c.version:match("^(%d+)%.(%d+)%.(%d+)$")
- if (not major or not minor or not revision) or (major ~= love._version_major and minor ~= love._version_minor) then
- local msg = ("This game indicates it was made for version '%s' of LOVE.\n"..
- "It may not be compatible with the running version (%s)."):format(c.version, love._version)
- print(msg)
- if love.window then
- love.window.showMessageBox("Compatibility Warning", msg, "warning")
- end
- end
- end
- if not confok and conferr then
- error(conferr)
- end
- -- Setup window here.
- if c.window and c.modules.window then
- assert(love.window.setMode(c.window.width, c.window.height,
- {
- fullscreen = c.window.fullscreen,
- fullscreentype = c.window.fullscreentype,
- vsync = c.window.vsync,
- msaa = c.window.msaa,
- resizable = c.window.resizable,
- minwidth = c.window.minwidth,
- minheight = c.window.minheight,
- borderless = c.window.borderless,
- centered = c.window.centered,
- display = c.window.display,
- highdpi = c.window.highdpi,
- x = c.window.x,
- y = c.window.y,
- }), "Could not set window mode")
- love.window.setTitle(c.window.title or c.title)
- if c.window.icon then
- assert(love.image, "If an icon is set in love.conf, love.image must be loaded!")
- love.window.setIcon(love.image.newImageData(c.window.icon))
- end
- end
- -- Our first timestep, because window creation can take some time
- if love.timer then
- love.timer.step()
- end
- if love.filesystem then
- love.filesystem.setAndroidSaveExternal(c.useexternalstorage)
- love.filesystem.setIdentity(c.identity or love.filesystem.getIdentity(), c.appendidentity)
- if love.filesystem.isFile("main.lua") then
- require("main")
- end
- end
- if no_game_code then
- error("No code to run\nYour game might be packaged incorrectly\nMake sure main.lua is at the top level of the zip")
- end
- end
- function love.run()
- if love.math then
- love.math.setRandomSeed(os.time())
- end
- if love.load then love.load(arg) end
- -- We don't want the first frame's dt to include time taken by love.load.
- if love.timer then love.timer.step() end
- local dt = 0
- -- Main loop time.
- while true do
- -- Process events.
- 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
- end
- end
- love.handlers[name](a,b,c,d,e,f)
- end
- end
- -- Update dt, as we'll be passing it to update
- if love.timer then
- love.timer.step()
- dt = love.timer.getDelta()
- end
- -- Call update and draw
- if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled
- if love.graphics and love.graphics.isActive() then
- love.graphics.clear(love.graphics.getBackgroundColor())
- love.graphics.origin()
- if love.draw then love.draw() end
- love.graphics.present()
- end
- if love.timer then love.timer.sleep(0.001) end
- end
- end
- -----------------------------------------------------------
- -- Error screen.
- -----------------------------------------------------------
- local debug, print = debug, print
- local function error_printer(msg, layer)
- print((debug.traceback("Error: " .. tostring(msg), 1+(layer or 1)):gsub("\n[^\n]+$", "")))
- end
- function love.errhand(msg)
- msg = tostring(msg)
- error_printer(msg, 2)
- if not love.window or not love.graphics or not love.event then
- return
- end
- if not love.graphics.isCreated() or not love.window.isOpen() then
- local success, status = pcall(love.window.setMode, 800, 600)
- if not success or not status then
- return
- end
- end
- -- Reset state.
- if love.mouse then
- love.mouse.setVisible(true)
- love.mouse.setGrabbed(false)
- love.mouse.setRelativeMode(false)
- end
- if love.joystick then
- -- Stop all joystick vibrations.
- for i,v in ipairs(love.joystick.getJoysticks()) do
- v:setVibration()
- end
- end
- if love.audio then love.audio.stop() end
- love.graphics.reset()
- local font = love.graphics.setNewFont(math.floor(love.window.toPixels(14)))
- love.graphics.setBackgroundColor(89, 157, 220)
- love.graphics.setColor(255, 255, 255, 255)
- local trace = debug.traceback()
- love.graphics.clear(love.graphics.getBackgroundColor())
- love.graphics.origin()
- local err = {}
- table.insert(err, "Error\n")
- table.insert(err, msg.."\n\n")
- for l in string.gmatch(trace, "(.-)\n") do
- if not string.match(l, "boot.lua") then
- l = string.gsub(l, "stack traceback:", "Traceback\n")
- table.insert(err, l)
- end
- end
- local p = table.concat(err, "\n")
- p = string.gsub(p, "\t", "")
- p = string.gsub(p, "%[string \"(.-)\"%]", "%1")
- local function draw()
- local pos = love.window.toPixels(70)
- love.graphics.clear(love.graphics.getBackgroundColor())
- love.graphics.printf(p, pos, pos, love.graphics.getWidth() - pos)
- love.graphics.present()
- end
- while true do
- love.event.pump()
- for e, a, b, c in love.event.poll() do
- if e == "quit" then
- return
- elseif e == "keypressed" and a == "escape" then
- return
- elseif e == "touchpressed" then
- local name = love.window.getTitle()
- if #name == 0 or name == "Untitled" then name = "Game" end
- local buttons = {"OK", "Cancel"}
- local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons)
- if pressed == 1 then
- return
- end
- end
- end
- draw()
- if love.timer then
- love.timer.sleep(0.1)
- end
- end
- end
- local function deferErrhand(...)
- local handler = love.errhand or error_printer
- return handler(...)
- end
- -----------------------------------------------------------
- -- The root of all calls.
- -----------------------------------------------------------
- return function()
- local result = xpcall(love.boot, error_printer)
- if not result then return 1 end
- local result = xpcall(love.init, deferErrhand)
- if not result then return 1 end
- local result, retval = xpcall(love.run, deferErrhand)
- if not result then return 1 end
- return tonumber(retval) or 0
- end
|