Skip to content

Commit

Permalink
v0.1.0-alpha
Browse files Browse the repository at this point in the history
  • Loading branch information
ActivexDiamond committed Mar 14, 2023
1 parent cc569b5 commit c7c51d3
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 43 deletions.
48 changes: 34 additions & 14 deletions cat-paw/core/patterns/event/EventSystem.lua
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ local Event = require "cat-paw.core.patterns.event.Event"
------------------------------ Constructor ------------------------------
local EventSystem = middleclass("EventSystem")
function EventSystem:initialize()
self.rootSubscribers = setmetatable({}, {__mode = "kv"})
self.events = {}
self.eventQueue = {}
end

------------------------------ Constants ------------------------------
EventSystem.ATTACH_TO_ALL = 0

------------------------------ Core API ------------------------------
function EventSystem:poll()
while #self.eventQueue > 0 do
Expand All @@ -20,6 +24,12 @@ function EventSystem:poll()
end

------------------------------ Internals ------------------------------
function EventSystem:_fetchAllCallbacks(target, code)
assert(code == EventSystem.ATTACH_TO_ALL, "`attach` got called with invalid code. Should be equal to EventSystem.ATTACH_TO_ALL")
--If subscriber is already attached to one or more specific events, will crash!
table.insert(self.rootSubscribers, target)
end

function EventSystem:_rawAttach(target, event, callback)
assert(event:isSubclassOf(Event) or event == Event, "May only attach Event or a subclass of it.")
assert(type(callback) == 'function', "Callback must be a function.")
Expand All @@ -32,12 +42,25 @@ end

function EventSystem:_fire(eventInstance)
local event = eventInstance.class

for _, target in pairs(self.rootSubscribers) do
local hierarchyPosition = event
while hierarchyPosition ~= Event.super do
if type(target[hierarchyPosition]) == 'function' then
target[hierarchyPosition](target, eventInstance)
end
--Go up one step in the hierarchy.
hierarchyPosition = hierarchyPosition.super
end
end

local t = {}
for e, subscribers in pairs(self.events) do
if event:isSubclassOf(e) or e == event then
t[#t + 1] = {e, subscribers or {{}, {}}}
end
end

table.sort(t, function(a, b)
-- Returns true when the first is less than the second.
return a[1]:isSubclassOf(b[1])
Expand All @@ -52,30 +75,27 @@ end
------------------------------ API ------------------------------
EventSystem.attach = overload({
EventSystem, 'table', Event,
function (self, target, event)
function(self, target, event)
--print("case 1", self, target, event, event.class, event:isSubclassOf(Event), event == Event)
self:_rawAttach(target, event, target[event])
end,

EventSystem, 'table', 'table',
function (self, target, events)
function(self, target, events)
--print("case 2", self, target, events, events.class, events == Event)
--print(events[1].class, events[1] == Event, events[1]:isSubclassOf(Event))
for _, e in pairs(events) do
self:_rawAttach(target, e, target[e])
end
end,


EventSystem, 'table', 'number',
function(self, target, getAllCode)
self:_fetchAllCallbacks(target, getAllCode)
end,

EventSystem, 'table', Event, 'function',
EventSystem._rawAttach,

--Add 4th option, only pass the object. Iterate all its keys and for every;
-- key.subclassof(Event) and type(val) == 'function' call _rawAttach on it.
-- This would be very slow so it should cache the class of every object after the first
-- call, so that subsuquent calls would just read from the cache.
-- WARN: This does mean changes made to the object (or even class) after the first cache will
-- not be noticed.
--EventSystem, 'table',
--function (self, target)
--
--end
})

function EventSystem:queue(event)
Expand Down
10 changes: 10 additions & 0 deletions cat-paw/core/patterns/event/keyboard/EvTextEdit.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
local middleclass = require "cat-paw.core.patterns.oop.middleclass"
local Event = require "cat-paw.core.patterns.event.Event"

local EvTextEdit = middleclass("EvTextEdit", Event)
function EvTextEdit:initilize(text, start, length)
Event.initilize(self)
self.text, self.start, self.length = text, start, length
end

return EvTextEdit
2 changes: 1 addition & 1 deletion cat-paw/core/patterns/state/Fsm.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ function Fsm:goTo(id, ...)
if self.currentState then self.currentState:leave(state) end
local previous = self.currentState
self.currentState = state
self.currentState:enter(previous, ...)
self.currentState.enter(self.currentState, previous, ...)
return true
end

Expand Down
96 changes: 96 additions & 0 deletions cat-paw/engine/AbstractGame.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
local version = require "cat-paw.version"
local middleclass = require "libs.middleclass"

local Fsm = require "cat-paw.core.patterns.state.Fsm"
local ApiHooks = require "cat-paw.hooks.LoveHooks"

--local suit = require "libs.suit"
local Scheduler = require "cat-paw.core.timing.Scheduler"
local EventSystem = require "cat-paw.core.patterns.event.EventSystem"

------------------------------ Constructor ------------------------------
local AbstractGame = middleclass("AbstractGame", Fsm)
function AbstractGame:initialize(title, targetWindowW, targetWindowH)
Fsm.initialize(self)
self.title = title or "Untitled Game"
love.window.setTitle(title)
if targetWindowW == -1 and targetWindowH == -1 then
love.window.setFullscreen(true)
elseif targetWindowW > 0 and targetWindowH > 0 then
love.window.setMode(targetWindowW, targetWindowH)
else
error(string.format("Invalid window size. w/h must both be -1, for fullscreen,"
.. "or positive. Current size: " .. targetWindowW .. ", " .. targetWindowH))
end
self.windowW, self.windowH = love.window.getMode()
self:_printToolVersions()

self.scheduler = Scheduler()
self.eventSystem = EventSystem()
ApiHooks.hookHandler(self)
end

------------------------------ Constants ------------------------------

------------------------------ Core ------------------------------
function AbstractGame:load(args)
end

function AbstractGame:update(dt)
Fsm.update(self, dt)

self.scheduler:update(dt)
self.eventSystem:poll()
end

------------------------------ Other ------------------------------
--Wrapper so AbstractGame can be directly passed to ApiHooks. Shouldn't be used anywhere else.
--If you want to queue stuff, use game:getEventSystem():queue(event)
--TODO: Find a better way to make this class and ApiHooks work nicely.
function AbstractGame:queue(...)
self.eventSystem:queue(...)
end

------------------------------ Internals ------------------------------
function AbstractGame:_printToolVersions()
print("Setting stdout's vbuf mode to 'no'. This is needed for some consoles to work properly.")
io.stdout:setvbuf("no")
print("============================================================")
print("Running Lua version: ", _VERSION)
if jit then
print("Running Luajit version: ", jit.version)
end
print("Running Love2d version: ", love.getVersion())
print("Running CatPaw version: ", version)
print("\nCurrently using the following 3rd-party libraries (and possibly more):")
print("middleclass\tBy Kikito\tSingle inheritance OOP in Lua\t[MIT License]")
print("bump\t\tBy Kikito\tSimple platformer physics.\t[MIT License]")
print("suit\t\tBy vrld\t\tImGUIs for Lua/Love2D\t\t[MIT License]")
print("Huge thanks to (Kikito and vrld) for their wonderful contributions to the community; and for releasing their work under such open licenses!")
print("============================================================")
print("Game loaded: " .. self.title)
print(string.format("Set window size to: (%d, %d)", self:getWindowSize()))
print("============================================================")
end

------------------------------ Getters / Setters ------------------------------
function AbstractGame:getWindowW() return self.windowW end
function AbstractGame:getWindowH() return self.windowH end
function AbstractGame:getWindowSize() return self.windowW, self.windowH end

function AbstractGame:setWindowSize(w, h)
self.windowW, self.windowH = w, h
love.window.setMode(w, h)
end

--TODO: Service locator
function AbstractGame:getEventSystem()
return self.eventSystem
end

function AbstractGame:getScheduler()
return self.scheduler
end

return AbstractGame

Empty file.
Empty file.
110 changes: 110 additions & 0 deletions cat-paw/hooks/LoveHooks.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
local middleclass = require "libs.middleclass"


local EvKeyPress = require "cat-paw.core.patterns.event.keyboard.EvKeyPress"
local EvKeyRelease = require "cat-paw.core.patterns.event.keyboard.EvKeyRelease"
local EvTextInput = require "cat-paw.core.patterns.event.keyboard.EvTextInput"
local EvTextEdit = require "cat-paw.core.patterns.event.keyboard.EvTextEdit"


local EvMousePress = require "cat-paw.core.patterns.event.mouse.EvMousePress"
local EvMouseRelease = require "cat-paw.core.patterns.event.mouse.EvMouseRelease"
local EvMouseMove = require "cat-paw.core.patterns.event.mouse.EvMouseMove"
local EvMouseWheel = require "cat-paw.core.patterns.event.mouse.EvMouseWheel"

local EvWindowFocus = require "cat-paw.core.patterns.event.os.EvWindowFocus"
local EvWindowResize = require "cat-paw.core.patterns.event.os.EvWindowResize"
local EvGameQuit = require "cat-paw.core.patterns.event.os.EvGameQuit"

------------------------------ Constructor ------------------------------
local LoveHooks = middleclass("LoveHooks")
function LoveHooks:initialize()
error("Attempting to initialize static class!" .. LoveHooks)
end

------------------------------ API ------------------------------
-- Can be any object (or even table) with `queue`, `load`, `tick`, and
-- `draw` methods. And optionally a `run` method.
-- `load` can be nil with no issues. `update` and `draw` can technically be null
-- but logic will not process nor will anything be drawn.
function LoveHooks.static.hookHandler(handler)
LoveHooks._hookLoveCallbacks(handler)
end

------------------------------ Hooks ------------------------------
---------LovEvents->Evsys
function LoveHooks.static._hookLoveCallbacks(handler)
local wrap = LoveHooks._loveCallbackWrapper
--Game
if handler.run then love.run = wrap(handler, handler.run)
else
love.load = wrap(handler, handler.load)
love.update = wrap(handler, handler.update)
love.draw = wrap(handler, handler.draw)
end
--Evsys

love.keypressed = wrap(handler, LoveHooks._onKeyPressed)
love.keyreleased = wrap(handler, LoveHooks._onKeyReleased)
love.textinput = wrap(handler, LoveHooks._nTextInput)
love.texteditted = wrap(handler, LoveHooks._nTextEdit)

love.mousepressed = wrap(handler, LoveHooks._onMousePressed)
love.mousereleased = wrap(handler, LoveHooks._onMouseReleased)
love.mousemoved = wrap(handler, LoveHooks._onMouseMoved)
love.wheelmoved = wrap(handler, LoveHooks._onMouseWheel)

love.focus = wrap(handler, LoveHooks._onWindowFocus)
love.resize = wrap(handler, LoveHooks._onWindowResize)
love.quit = wrap(handler, LoveHooks._onGameQuit)
end

------------------------------ Helpers ------------------------------
function LoveHooks.static._loveCallbackWrapper(handler, f)
return f and function(...) -- wrapper or nil
return f(handler, ...)
end
end

------------------------------ Evsys ------------------------------
---------Keyboard
function LoveHooks.static._onKeyPressed(handler, k, code, isRepeat)
handler:queue(EvKeyPress(k, code, isRepeat))
end
function LoveHooks.static._onKeyReleased(handler, k, code, isRepeat)
handler:queue(EvKeyRelease(k, code, isRepeat))
end
function LoveHooks.static._onTextInput(handler, char)
handler:queue(EvTextInput(char))
end

function LoveHooks.static._onTextEdit(handler, text, start, length)
handler:queue(EvTextEdit(text, start, length))
end

---------Mouse
function LoveHooks.static._onMousePressed(handler, x, y, button, touch)
handler:queue(EvMousePress(x, y, button, touch))
end
function LoveHooks.static._onMouseReleased(handler, x, y, button, touch)
handler:queue(EvMouseRelease(x, y, button, touch))
end
function LoveHooks.static._onMouseMoved(handler, x, y, dx, dy, touch)
handler:queue(EvMouseMove(x, y, dx, dy, touch))
end
function LoveHooks.static._onMouseWheel(handler, x, y)
handler:queue(EvMouseWheel(x, y))
end

---------OS
function LoveHooks.static._onWindowFocus(handler, focus)
handler:queue(EvWindowFocus(focus))
end
function LoveHooks.static._onWindowResize(handler, w, h)
handler:queue(EvWindowResize(w, h))
end
function LoveHooks.static._onGameQuit(handler)
handler:queue(EvGameQuit())
end

return LoveHooks
28 changes: 0 additions & 28 deletions cat-paw/main.lua

This file was deleted.

1 change: 1 addition & 0 deletions cat-paw/version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0.1.0-alpha
Loading

0 comments on commit c7c51d3

Please sign in to comment.