finally make it object oriented and add shim support (nogame 0.9.0 now works)

This commit is contained in:
banepwn 2020-04-04 05:12:07 -04:00
parent f9198fec8b
commit c5dbed728c
9 changed files with 364 additions and 125 deletions

View File

@ -47,12 +47,20 @@
</tr> </tr>
<tr> <tr>
<td><a href="https://github.com/love2d/love/blob/3053278048cceb7ca71b944517e886804314a246/src/scripts/nogame.lua">LÖVE 10.0 <i>nogame.lua</i></a></td> <td><a href="https://github.com/love2d/love/blob/3053278048cceb7ca71b944517e886804314a246/src/scripts/nogame.lua">LÖVE 10.0 <i>nogame.lua</i></a></td>
<td>blending modes</td> <td>SpriteBatch</td>
<td><span class="medium">Running</span></td> <td><span class="medium">Running</span></td>
<td><span class="bad">Broken</span></td> <td><span class="bad">Broken</span></td>
<td><span class="bad">Broken</span></td> <td><span class="bad">Broken</span></td>
<td><span class="bad">Broken</span></td> <td><span class="bad">Broken</span></td>
</tr> </tr>
<tr>
<td><a href="https://bitbucket.org/rude/love/src/63ed6cbfb2853009bb49572d975569d43cba2705/src/scripts/boot.lua">LÖVE 0.9.0 <i>nogame.lua</i></a></td>
<td>blending modes</td>
<td><span class="medium">Running</span></td>
<td><span class="bad">Broken</span></td>
<td><span class="bad">Perfect</span></td>
<td><span class="bad">Perfect</span></td>
</tr>
<tr> <tr>
<td><a href="https://github.com/TurtleP/LovePotion/tree/master/nogame">LovePotion <i>nogame<i></a></td> <td><a href="https://github.com/TurtleP/LovePotion/tree/master/nogame">LovePotion <i>nogame<i></a></td>
<td><a href="https://github.com/TurtleP">Jeremy Postelnek</a></td> <td><a href="https://github.com/TurtleP">Jeremy Postelnek</a></td>

30
main.js
View File

@ -2,35 +2,13 @@ window.infatuated = {};
(function() { (function() {
"use strict"; "use strict";
function assert(cond, msg) { if (!cond) { throw msg; } } infatuated.newImage = function() {
return new Image();
var images = {};
infatuated.newImage = function(path, b64) {
var image = new Image();
images[path] = image;
if (b64) {
image.src = "data:image/png;base64,"+path;
} else {
image.src = infatuated.game+"/"+path;
}
image.getWidth = function() { return image.width; }
image.getHeight = function() { return image.height; }
return {
"type": "Image",
"path": path,
"getWidth": function() { return image.width; },
"getHeight": function() { return image.height; }
};
}
infatuated.drawImage = function(ctx, obj, x, y) {
if (!obj.type) { return; }
assert(obj.type == "Image", "bad argument #2 to drawImage");
if (images[obj.path].complete) {
ctx.drawImage(images[obj.path], x, y);
}
} }
var url = new URL(location.href); var url = new URL(location.href);
var game = url.searchParams.get("game"); var game = url.searchParams.get("game");
infatuated.game = game ? "game-"+game : "game"; infatuated.game = game ? "game-"+game : "game";
var shim = url.searchParams.get("shim");
infatuated.shim = shim ? shim : false;
})(); })();

120
main.lua
View File

@ -61,10 +61,12 @@ infatuated.conf = {
} }
} }
class = require "middleclass"
infatuated.classes = {}
local js = require "js" local js = require "js"
infatuated.js = js infatuated.js = js
local window = js.global local window = js.global
infatuated.path = window.infatuated.game;
local document = window.document local document = window.document
local canvas = document:getElementById("infatuated-canvas") local canvas = document:getElementById("infatuated-canvas")
infatuated.canvas = canvas infatuated.canvas = canvas
@ -109,6 +111,9 @@ function infatuated.msgbox:hide()
end end
--infatuated.msgbox:show("sample title", "the quick brown fox", {"a", "b", "c"}, oldprint) --infatuated.msgbox:show("sample title", "the quick brown fox", {"a", "b", "c"}, oldprint)
infatuated.path = window.infatuated.game
infatuated.shim = window.infatuated.shim
print("Running game from path %q", path) print("Running game from path %q", path)
local function errorprint(...) local function errorprint(...)
@ -139,7 +144,6 @@ function infatuated.parseColor(r, g, b, a)
assert(type(g) == "number", "bad argument #2 to parseColor") assert(type(g) == "number", "bad argument #2 to parseColor")
assert(type(b) == "number", "bad argument #3 to parseColor") assert(type(b) == "number", "bad argument #3 to parseColor")
assert(type(a) == "number", "bad argument #4 to parseColor") assert(type(a) == "number", "bad argument #4 to parseColor")
--return {r, g, b, a}, string.format("rgba(%f, %f, %f, %f)", r, g, b, a)
return {r, g, b, a}, string.format("rgba(%f, %f, %f, %f)", r*255, g*255, b*255, a) return {r, g, b, a}, string.format("rgba(%f, %f, %f, %f)", r*255, g*255, b*255, a)
else else
error("bad argument #1 to parseColor") error("bad argument #1 to parseColor")
@ -209,66 +213,76 @@ end
print("Coroutining now") print("Coroutining now")
infatuated.thread = coroutine.create(function() infatuated.thread = coroutine.create(function()
require "modules.data" xpcall(function()
require "modules.event" require "modules.data"
require "modules.filesystem" require "modules.event"
require "modules.graphics" require "modules.filesystem"
require "modules.handlers" require "modules.graphics"
require "modules.image" require "modules.handlers"
require "modules.math" require "modules.image"
require "modules.system" require "modules.math"
require "modules.timer" require "modules.system"
require "modules.window" require "modules.timer"
require "modules.window"
love.run() love.run()
local oldrequire = require if infatuated.shim then
infatuated.require = oldrequire print("Shimming %s", infatuated.shim)
function require(path) infatuated.shim = infatuated.shim:gsub("%.", "-")
assert(type(path) == "string", "bad argument #1 to require") require("shims."..infatuated.shim)
if path == "love" then return love end end
return oldrequire(infatuated.path.."."..path)
end
print("Importing conf") 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
local status, errmsg = pcall(oldrequire, infatuated.path..".conf") print("Importing conf")
if not status and not errmsg:find("^module '.-%.conf' not found:") then
infatuated.crash(errmsg)
end
local status, errmsg = pcall(love.conf, infatuated.conf)
if not status then infatuated.crash(errmsg) end
print("Importing main") local status, errmsg = pcall(oldrequire, infatuated.path..".conf")
if not status and not errmsg:find("^module '.-%.conf' not found:") then
infatuated.crash(errmsg)
end
local status, errmsg = pcall(love.conf, infatuated.conf)
if not status then infatuated.crash(errmsg) end
local status, errmsg = pcall(oldrequire, infatuated.path..".main") print("Importing 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
print("Starting event loop") local status, errmsg = pcall(oldrequire, infatuated.path..".main")
if not status then infatuated.crash(errmsg) end
infatuated.dt1 = window.performance:now() local status, errmsg = pcall(love.run)
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 if not status then
infatuated.crash(errmsg) infatuated.crash(errmsg)
elseif errmsg then else
print("Exited with code %d", errmsg or -1) infatuated.loopGuest = errmsg
return
end end
window:requestAnimationFrame(resume)
coroutine.yield() print("Starting event loop")
end
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.crash(errmsg)
end)
end) end)
coroutine.resume(infatuated.thread) coroutine.resume(infatuated.thread)

183
middleclass.lua Normal file
View File

@ -0,0 +1,183 @@
local middleclass = {
_VERSION = 'middleclass v4.1.1',
_DESCRIPTION = 'Object Orientation for Lua',
_URL = 'https://github.com/kikito/middleclass',
_LICENSE = [[
MIT LICENSE
Copyright (c) 2011 Enrique García Cota
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
]]
}
local function _createIndexWrapper(aClass, f)
if f == nil then
return aClass.__instanceDict
else
return function(self, name)
local value = aClass.__instanceDict[name]
if value ~= nil then
return value
elseif type(f) == "function" then
return (f(self, name))
else
return f[name]
end
end
end
end
local function _propagateInstanceMethod(aClass, name, f)
f = name == "__index" and _createIndexWrapper(aClass, f) or f
aClass.__instanceDict[name] = f
for subclass in pairs(aClass.subclasses) do
if rawget(subclass.__declaredMethods, name) == nil then
_propagateInstanceMethod(subclass, name, f)
end
end
end
local function _declareInstanceMethod(aClass, name, f)
aClass.__declaredMethods[name] = f
if f == nil and aClass.super then
f = aClass.super.__instanceDict[name]
end
_propagateInstanceMethod(aClass, name, f)
end
local function _tostring(self) return "class " .. self.name end
local function _call(self, ...) return self:new(...) end
local function _createClass(name, super)
local dict = {}
dict.__index = dict
local aClass = { name = name, super = super, static = {},
__instanceDict = dict, __declaredMethods = {},
subclasses = setmetatable({}, {__mode='k'}) }
if super then
setmetatable(aClass.static, {
__index = function(_,k)
local result = rawget(dict,k)
if result == nil then
return super.static[k]
end
return result
end
})
else
setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) end })
end
setmetatable(aClass, { __index = aClass.static, __tostring = _tostring,
__call = _call, __newindex = _declareInstanceMethod })
return aClass
end
local function _includeMixin(aClass, mixin)
assert(type(mixin) == 'table', "mixin must be a table")
for name,method in pairs(mixin) do
if name ~= "included" and name ~= "static" then aClass[name] = method end
end
for name,method in pairs(mixin.static or {}) do
aClass.static[name] = method
end
if type(mixin.included)=="function" then mixin:included(aClass) end
return aClass
end
local DefaultMixin = {
__tostring = function(self) return "instance of " .. tostring(self.class) end,
initialize = function(self, ...) end,
isInstanceOf = function(self, aClass)
return type(aClass) == 'table'
and type(self) == 'table'
and (self.class == aClass
or type(self.class) == 'table'
and type(self.class.isSubclassOf) == 'function'
and self.class:isSubclassOf(aClass))
end,
static = {
allocate = function(self)
assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'")
return setmetatable({ class = self }, self.__instanceDict)
end,
new = function(self, ...)
assert(type(self) == 'table', "Make sure that you are using 'Class:new' instead of 'Class.new'")
local instance = self:allocate()
instance:initialize(...)
return instance
end,
subclass = function(self, name)
assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'")
assert(type(name) == "string", "You must provide a name(string) for your class")
local subclass = _createClass(name, self)
for methodName, f in pairs(self.__instanceDict) do
_propagateInstanceMethod(subclass, methodName, f)
end
subclass.initialize = function(instance, ...) return self.initialize(instance, ...) end
self.subclasses[subclass] = true
self:subclassed(subclass)
return subclass
end,
subclassed = function(self, other) end,
isSubclassOf = function(self, other)
return type(other) == 'table' and
type(self.super) == 'table' and
( self.super == other or self.super:isSubclassOf(other) )
end,
include = function(self, ...)
assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'")
for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end
return self
end
}
}
function middleclass.class(name, super)
assert(type(name) == 'string', "A name (string) is needed for the new class")
return super and super:subclass(name) or _includeMixin(_createClass(name), DefaultMixin)
end
setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end })
return middleclass

View File

@ -1,12 +1,13 @@
love.filesystem = {} love.filesystem = {}
local FileData = class("FileData")
infatuated.classes.FileData = FileData
function FileData:initialize(contents, name)
self.contents = contents
self.name = name
end
function love.filesystem.newFileData(contents, name) function love.filesystem.newFileData(contents, name)
if name then if name then
-- TODO: use an actual class return infatuated.classes.FileData:new(contents, name)
return {
type = "FileData",
data = contents,
name = name
}
else else
return nil, "not implemented" return nil, "not implemented"
end end

View File

@ -85,33 +85,71 @@ function love.graphics.setLineWidth(width)
infatuated.lineWidth = width infatuated.lineWidth = width
end end
function love.graphics.newSpriteBatch(image, maxsprites, usage) local SpriteBatch = class("SpriteBatch")
maxsprites = maxsprites or 1000 infatuated.classes.SpriteBatch = SpriteBatch
assert(type(image) == "userdata" and image.type == "Image", "bad argument #1 to newSpriteBatch (expected Image)") function SpriteBatch:initialize(image, max)
assert(type(maxsprites) == "number" and maxsprites > 0, "bad argument #2 to newSpriteBatch") self._max = max
return {
type = "SpriteBatch",
maxsprites = maxsprites,
getBufferSize = function(self) return self.maxsprites end,
setBufferSize = function(self, maxsprites)
assert(type(maxsprites) == "number" and maxsprites > 0, "bad argument #2 to setBufferSize (expected number)")
self.maxsprites = maxsprites
end,
add = function() end,
flush = function() end,
clear = function() end
}
end end
function love.graphics.newImage(path) function SpriteBatch:getBufferSize()
if path.type == "ImageData" then return self._max
return newImage(nil, path, true) end
function SpriteBatch:setBufferSize(maxsprites)
assert(type(maxsprites) == "number", "bad argument #1 to setBufferSize (expected number)")
assert(maxsprites > 0, "maxsprites must be greater than 0")
self._max = maxsprites
end
function SpriteBatch:add() end
function SpriteBatch:flush() end
function SpriteBatch:clear() end
function love.graphics.newSpriteBatch(image, maxsprites)
maxsprites = maxsprites or 1000
assert(type(image) == "table" and tostring(image.class) == "class Image", "bad argument #1 to newSpriteBatch (expected Image)")
assert(type(maxsprites) == "number", "bad argument #2 to newSpriteBatch (expected number)")
assert(maxsprites > 0, "maxsprites must be greater than 0")
return SpriteBatch:new(image, maxsprites, usage)
end
local Image = class("Image")
infatuated.classes.Image = Image
function Image:initialize(a)
if type(a) == "string" then
infatuated.print("Loading image from %q", a)
local image = newImage()
a = ("/"..a):gsub("/%.%.", "/")
image.src = "game"..a
image.onload = function(self)
infatuated.print("Loaded image")
coroutine.resume(infatuated.thread)
end
coroutine.yield()
self.image = image
else else
assert(type(path) == "string", "bad argument #1 to newImage") infatuated.print("Loading image from ImageData")
return newImage(nil, path) local image = newImage()
image.src = "data:image/png;base64,"..a.data
image.onload = function(self)
infatuated.print("Loaded image")
coroutine.resume(infatuated.thread)
end
coroutine.yield()
self.image = image
end end
end end
function Image:getWidth()
return self.image.width
end
function Image:getHeight()
return self.image.height
end
function love.graphics.newImage(a)
local t = type(a)
assert(t == "string" or (t == "table" and tostring(a.class) == "class ImageData"), "bad argument #1 to newImage (expected string or ImageData)")
return Image:new(a)
end
function love.graphics.draw(obj, x, y, r, sx, sy, ox, oy) function love.graphics.draw(obj, x, y, r, sx, sy, ox, oy)
if type(obj) == "table" and tostring(obj.class) == "class SpriteBatch" then return end
assert(type(obj) == "table" and tostring(obj.class) == "class Image", "bad argument #1 to draw (expected Image)")
r = r or 0 r = r or 0
ox = ox or 0 ox = ox or 0
oy = oy or 0 oy = oy or 0
@ -126,7 +164,7 @@ function love.graphics.draw(obj, x, y, r, sx, sy, ox, oy)
ctx:rotate(r) ctx:rotate(r)
-- color tinting looks HARD! -- color tinting looks HARD!
ctx.globalAlpha = infatuated.color[4] ctx.globalAlpha = infatuated.color[4]
drawImage(nil, ctx, obj, -ox, -oy) ctx:drawImage(obj.image, -ox, -oy)
ctx:restore() ctx:restore()
end end
function love.graphics.print(text, x, y, r, sx, sy, ox, oy, kx, ky) function love.graphics.print(text, x, y, r, sx, sy, ox, oy, kx, ky)

View File

@ -1,12 +1,13 @@
love.image = {} love.image = {}
function love.image.newImageData(w, h, f, d) local ImageData = class("ImageData")
if h or f or d then infatuated.classes.ImageData = ImageData
error("not implemented") function ImageData:initialize(a)
else self.data = a.contents
-- TODO: use an actual class end
return { function love.image.newImageData(a, b, c, d)
type = "ImageData", assert(type(a) == "table" and tostring(a.class) == "class FileData", "bad argument #1 to newImageData (expected FileData)")
content = w if b or c or d then
} error("not implemented")
end end
return ImageData:new(a)
end end

View File

@ -27,10 +27,13 @@ local platformIDs = {
win64 = "Windows", win64 = "Windows",
wince = "Windows" -- amirite wince = "Windows" -- amirite
} }
function love.system.getOS() local function getOS()
if useragent:find("android") then return "Android" end -- fucking "Linux 50m3_r4nd0m_5h1t" if useragent:find("android") then return "Android" end -- fucking "Linux 50m3_r4nd0m_5h1t"
if useragent:find("iphone") then return "iOS" end -- fucking "general Mobile Device" if useragent:find("iphone") then return "iOS" end -- fucking "general Mobile Device"
return platformIDs[platform] or platform:find("mobile") and "Android" or "Windows" return platformIDs[platform] or platform:find("mobile") and "Android" or "Windows"
end end
love._os = love.system.getOS() -- 0.8.0 compatibility infatuated.os = getOS()
infatuated.print("Detected OS as %s", love._os) infatuated.print("Detected OS as %s", infatuated.os)
function love.system.getOS()
return infatuated.os
end

13
shims/0-9-0.lua Normal file
View File

@ -0,0 +1,13 @@
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")
assert(type(b) == "number", "bad argument #3 to parseColor")
assert(type(a) == "number", "bad argument #4 to parseColor")
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