2
-- This helps you re-use assets in your app instead of creating extraneous
3
-- copies of them. It also hides Love-related calls so that your code is
6
-- If you're using a class built into Zoetrope, you do not need to use
7
-- this class directly. They take care of setting things up for you
8
-- appropriately. However, if you're rolling your own, you'll want to use
9
-- this to save memory.
11
-- This class is not meant to be created directly. Instead, call
12
-- methods on Cached directly, e.g. Cached:sound(), Cached:image(), and so on.
19
-- Property: defaultGlyphs
20
-- The default character order of a bitmap font, if none is specified
22
defaultGlyphs = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`' ..
23
'abcdefghijklmnopqrstuvwxyz{|}~',
25
-- private property: library
26
-- a table to store already-instantiated assets
27
_library = { image = {}, text = {}, sound = {}, font = {}, binds = {}, },
30
-- Returns a cached image asset.
33
-- path - pathname to image file
38
image = function (self, path)
39
assert(type(path) == 'string', 'path must be a string')
41
if not self._library.image[path] then
42
self._library.image[path] = love.graphics.newImage(path)
45
return self._library.image[path]
49
-- Returns a cached text asset.
52
-- path - pathname to text file
57
text = function (self, path)
58
assert(type(path) == 'string', 'path must be a string')
60
if not self._library.text[path] then
61
self._library.text[path] = love.filesystem.read(path)
64
return self._library.text[path]
68
-- Returns a cached sound asset.
71
-- path - pathname to sound file
72
-- length - either 'short' or 'long'. *It's very important to pass
73
-- the correct option here.* A short sound is loaded entirely
74
-- into memory, while a long one is streamed from disk. If you
75
-- mismatch, you'll either hear a delay in the sound (short sounds
76
-- played from disk) or your app will freeze (long sounds played from
80
-- Either a Love SoundData object (for short sounds) or a
81
-- Love Decoder object (for long sounds). Either can be used to
82
-- create a Love Source object.
85
-- <playSound>, <sound>
87
sound = function (self, path, length)
88
assert(type(path) == 'string', 'path must be a string')
90
if not self._library.sound[path] then
91
if length == 'short' then
92
self._library.sound[path] = love.sound.newSoundData(path)
93
elseif length == 'long' then
94
self._library.sound[path] = love.sound.newDecoder(path)
96
error('length must be either "short" or "long"')
100
return self._library.sound[path]
104
-- Returns a cached font asset.
108
-- * A single number. This uses Love's default outline font at that point size.
109
-- * A single string. This uses a bitmap font given by this pathname, and assumes that
110
-- the characters come in
111
-- <printable ASCII order at https://en.wikipedia.org/wiki/ASCII#ASCII_printable_characters>.
112
-- * A string, then a number. This uses an outline font whose pathname is the first argument,
113
-- at the point size given in the second argument.
114
-- * Two strings. The first is treated as a pathname to a bitmap font, the second
115
-- as the character order in the font.
120
font = function (self, ...)
122
local libKey = arg[1]
124
if #arg > 1 then libKey = libKey .. arg[2] end
126
if not self._library.font[libKey] then
130
if type(arg[1]) == 'number' then
131
font = love.graphics.newFont(arg[1])
132
elseif type(arg[1]) == 'string' then
133
image = Cached:image(arg[1])
134
font = love.graphics.newImageFont(image, self.defaultGlyphs)
136
error("don't understand single argument: " .. arg[1])
138
elseif #arg == 2 then
139
if type(arg[2]) == 'number' then
140
font = love.graphics.newFont(arg[1], arg[2])
141
elseif type(arg[2]) == 'string' then
142
image = Cached:image(arg[1])
143
font = love.graphics.newImageFont(image, arg[2])
145
error("don't understand arguments: " .. arg[1] .. ", " .. arg[2])
148
error("too many arguments; should be at most two")
151
self._library.font[libKey] = font
154
return self._library.font[libKey]
158
-- Returns a function that's bound to an object so it can be later called with
159
-- the correct context. This can be abbreviated as just bind().
162
-- obj - object to use as function owner
163
-- func - either a string name of a property of obj, or a free-standing
165
-- ... - any number of extra arguments
167
bind = function (self, obj, func, ...)
170
if STRICT and type(func) == 'string' then
171
assert(type(obj[func]) == 'function', 'asked to bind an object to a non-existent method named ' .. func)
174
-- look for previous bind
176
for key, value in pairs(self._library.binds) do
177
if key[1] == func and key[2] == obj then
181
if key[i + 2] ~= arg[i] then
193
-- have to create a new one
194
-- note that we have to create a compound key, hence the loop above
196
local result = function()
197
if type(func) == 'string' then
198
return obj[func](obj, unpack(arg))
200
return func(obj, unpack(arg))
204
self._library.binds[{func, obj, arg}] = result