-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathNode.lua
More file actions
156 lines (137 loc) · 4.32 KB
/
Copy pathNode.lua
File metadata and controls
156 lines (137 loc) · 4.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
local Signal = require("src.Signal")
local Class = require("lib.classic.classic")
---An ingame object. This is the building block for any class that contributes
---to the gameplay itself; things that change with time and player input.
---It can store children nodes that are automatically updated and drawn, forming
---a tree.
---
---There's always 1 main "root" node at the top of the project.
---@class Node
---@field super table
---@field extend function
---@field is function
---@overload fun(x: number?, y: number?): Node
local Node = Class:extend()
---@param x number?
---@param y number?
function Node:new(x, y)
self._CHILDREN = {}
self.x = x or 0
self.y = y or 0
-- The Node this is a child of, if any. This gets set on the parent's
-- `add_child()` call.
self.parent = nil
-- If `false`, this Node won't update (but still might be drawn).
self.is_active = true
-- If `false`, this Node won't be drawn (but still might update).
self.is_visible = true
-- If `false`, this Node doesn't update or draw, and will be removed
-- from the game in the next update cycle. Usually, you want to set this with
-- `die()` instead of doing it manually.
self.is_alive = true
-- Quick & simple way to give a Node a graphic. Set this to an Image (from
-- IMAGES, for example) to draw it at `self.x + self.image_offset_x, self.y +
-- self.image_offset_y`. Set it to `nil` to disable.
self.image = nil
-- The amount, in pixels, to add to `self.image`'s `x` position when drawing.
self.image_offset_x = 0
-- The amount, in pixels, to add to `self.image`'s `y` position when drawing.
self.image_offset_y = 0
-- If `image` is set, it will automatically be drawn using this shader. By
-- default, it's a simple palette swap shader (see `src/shader/palette_swap.frag`),
-- but you can replace it.
self.image_shader = love.graphics.newShader("src/shader/palette_swap.frag")
-- Fires when this Node dies.
self.died = Signal()
end
---Called by this Node's parent when it becomes a child node. Unlike the constructor,
---code inside `_ready()` is guaranteed to only run when this is inside the scene
---tree (i.e., when everything is initialized and ready to use).
function Node:_ready()
end
function Node:update(dt)
-- Remove dead children (jesus)
for i = #self._CHILDREN, 1, -1 do
if not self._CHILDREN[i].is_alive then
table.remove(self._CHILDREN, i)
end
end
-- Update children
for i = 1, #self._CHILDREN do
local child = self._CHILDREN[i]
if child.is_alive and child.is_active then
child:update(dt)
end
end
end
function Node:draw()
if self.image then
love.graphics.setShader(self.image_shader)
love.graphics.draw(
self.image,
self.x + self.image_offset_x,
self.y + self.image_offset_y
)
love.graphics.setShader()
end
for i = 1, #self._CHILDREN do
local child = self._CHILDREN[i]
if child.is_alive and child.is_visible then
child:draw()
end
end
end
---@generic T: Node
---@param node T
---@return T
function Node:add_child(node)
table.insert(self._CHILDREN, node)
-- Getting weird warnings when accessing fields from generics. This was the
-- best workaround I found that preserves autocompletion.
---@diagnostic disable-next-line
node.parent = self
---@diagnostic disable-next-line
node:_ready()
return node
end
---Generate an iterator for this Node's children. Use inside a for-loop, like:
---```lua
--- for child in node:iter_children() do
--- ...
--- end
---```
---This prevents exposure of how children are kept internally.
---@return function
function Node:iter_children()
local i = 0
return function()
i = i + 1
if self._CHILDREN[i] then
return self._CHILDREN[i]
end
end
end
---Die and kill all children nodes. They stop updating and drawing, and are removed
---from the game in the next update cycle.
function Node:die()
self.is_active = false
self.is_visible = false
self.is_alive = false
for _, child in ipairs(self._CHILDREN) do
child:die()
end
self.died:emit()
end
---Inside a `batteries.async` kernel, stall for `duration` seconds, then continue.
---For an example of async usage, see `demo/camera/CameraDemo.lua`.
---
---CAUTION: Do not call this outside an async kernel, it'll hang!
---@param duration number
function Node:wait(duration)
local timeout = false
LIB.timer.after(duration, function() timeout = true end)
while not timeout do
LIB.batteries.async.stall()
end
end
return Node