pixie_dust 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +18 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README +16 -0
- data/README.md +29 -0
- data/Rakefile +1 -0
- data/game.js +9200 -0
- data/lib/corelib.js +3331 -0
- data/lib/pixie_dust/version.rb +3 -0
- data/lib/pixie_dust.rb +14 -0
- data/pixie.json +15 -0
- data/pixie_dust.gemspec +29 -0
- data/source/active_bounds.coffee +48 -0
- data/source/ageable.coffee +23 -0
- data/source/bounded.coffee +282 -0
- data/source/camera.coffee +138 -0
- data/source/camera.fade.coffee +69 -0
- data/source/camera.flash.coffee +69 -0
- data/source/camera.rotate.coffee +11 -0
- data/source/camera.shake.coffee +27 -0
- data/source/camera.zoom.coffee +25 -0
- data/source/camera.zsort.coffee +13 -0
- data/source/clampable.coffee +61 -0
- data/source/collidable.coffee +126 -0
- data/source/collision.coffee +272 -0
- data/source/collision_response.coffee +28 -0
- data/source/color.coffee +1113 -0
- data/source/color_table.coffee +2534 -0
- data/source/controllable.coffee +66 -0
- data/source/cooldown.coffee +82 -0
- data/source/debuggable.coffee +253 -0
- data/source/drawable.coffee +167 -0
- data/source/dust_emitter.coffee +36 -0
- data/source/easing.coffee +38 -0
- data/source/emitter.coffee +7 -0
- data/source/emitterable.coffee +68 -0
- data/source/engine.coffee +274 -0
- data/source/engine.collision.coffee +77 -0
- data/source/engine.data.coffee +23 -0
- data/source/engine.delay.coffee +41 -0
- data/source/engine.fps_counter.coffee +32 -0
- data/source/engine.game_state.coffee +86 -0
- data/source/engine.joysticks.coffee +47 -0
- data/source/engine.keyboard.coffee +17 -0
- data/source/engine.levels.coffee +69 -0
- data/source/engine.mouse.coffee +16 -0
- data/source/engine.selector.coffee +166 -0
- data/source/engine.stats.coffee +16 -0
- data/source/engine.tilemap.coffee +41 -0
- data/source/engine_background.coffee +32 -0
- data/source/expirable.coffee +47 -0
- data/source/flickerable.coffee +78 -0
- data/source/follow.coffee +65 -0
- data/source/framerate.coffee +42 -0
- data/source/game_object.coffee +181 -0
- data/source/game_object.effect.coffee +33 -0
- data/source/game_object.meter.coffee +191 -0
- data/source/game_over.coffee +40 -0
- data/source/game_state.coffee +67 -0
- data/source/game_state.save_state.coffee +76 -0
- data/source/game_state.single_camera.coffee +40 -0
- data/source/game_state_cameras.coffee +33 -0
- data/source/level_state.coffee +32 -0
- data/source/movable.coffee +57 -0
- data/source/oscillator.coffee +18 -0
- data/source/pixie_dust.coffee +2 -0
- data/source/resource_loader.coffee +35 -0
- data/source/rotatable.coffee +38 -0
- data/source/sprite.coffee +181 -0
- data/source/text_effect.coffee +74 -0
- data/source/text_effect.floating.coffee +22 -0
- data/source/text_screen.coffee +38 -0
- data/source/tilemap.coffee +56 -0
- data/source/timed_events.coffee +78 -0
- data/source/title_screen.coffee +38 -0
- data/source/tween.coffee +70 -0
- data/test/active_bounds.coffee +67 -0
- data/test/bounded.coffee +98 -0
- data/test/camera.coffee +29 -0
- data/test/clampable.coffee +18 -0
- data/test/collidable.coffee +51 -0
- data/test/collision.coffee +70 -0
- data/test/color.coffee +533 -0
- data/test/controllable.coffee +108 -0
- data/test/cooldown.coffee +116 -0
- data/test/debuggable.coffee +71 -0
- data/test/drawable.coffee +31 -0
- data/test/emitter.coffee +0 -0
- data/test/emitterable.coffee +15 -0
- data/test/engine.coffee +228 -0
- data/test/engine_data.coffee +12 -0
- data/test/engine_delay.coffee +14 -0
- data/test/engine_selector.coffee +100 -0
- data/test/expirable.coffee +35 -0
- data/test/flickerable.coffee +51 -0
- data/test/follow.coffee +34 -0
- data/test/game_object.coffee +78 -0
- data/test/game_object_effect.coffee +17 -0
- data/test/metered.coffee +33 -0
- data/test/movable.coffee +46 -0
- data/test/oscillator.coffee +28 -0
- data/test/resource_loader.coffee +7 -0
- data/test/rotatable.coffee +20 -0
- data/test/sprite.coffee +21 -0
- data/test/text.coffee +25 -0
- data/test/timed_events.coffee +23 -0
- data/test/tweening.coffee +18 -0
- metadata +233 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
###*
|
2
|
+
The Controllable module adds simple movement
|
3
|
+
when up, down, left, or right are held.
|
4
|
+
|
5
|
+
# create a player and include Controllable
|
6
|
+
player = GameObject
|
7
|
+
width: 5
|
8
|
+
height: 17
|
9
|
+
x: 15
|
10
|
+
y: 30
|
11
|
+
speed: 2
|
12
|
+
|
13
|
+
player.include Controllable
|
14
|
+
|
15
|
+
# hold the left arrow key, then
|
16
|
+
# update the player
|
17
|
+
player.update()
|
18
|
+
|
19
|
+
# the player is moved left according to his speed
|
20
|
+
player.I.x
|
21
|
+
# => 13
|
22
|
+
|
23
|
+
# We keep track of the direction the object is
|
24
|
+
# facing in case you need that (eg. for attack direction)
|
25
|
+
player.I.facing
|
26
|
+
# => player.I.facing
|
27
|
+
# => Point(-1, 0)
|
28
|
+
|
29
|
+
@name Controllable
|
30
|
+
@module
|
31
|
+
@constructor
|
32
|
+
@param {Object} I Instance variables
|
33
|
+
@param {Core} self Reference to including object
|
34
|
+
###
|
35
|
+
Controllable = (I={}, self) ->
|
36
|
+
Object.reverseMerge I,
|
37
|
+
facing: Point(1, 0)
|
38
|
+
speed: 1
|
39
|
+
velocity: Point(0, 0)
|
40
|
+
|
41
|
+
self.bind "update", ->
|
42
|
+
self.movement()
|
43
|
+
|
44
|
+
movement: ->
|
45
|
+
I.velocity.x = 0
|
46
|
+
I.velocity.y = 0
|
47
|
+
|
48
|
+
if keydown.left
|
49
|
+
I.velocity.x = -1
|
50
|
+
|
51
|
+
if keydown.right
|
52
|
+
I.velocity.x = 1
|
53
|
+
|
54
|
+
if keydown.up
|
55
|
+
I.velocity.y = -1
|
56
|
+
|
57
|
+
if keydown.down
|
58
|
+
I.velocity.y = 1
|
59
|
+
|
60
|
+
I.velocity = I.velocity.norm()
|
61
|
+
|
62
|
+
unless I.velocity.equal(Point.ZERO)
|
63
|
+
I.facing = I.velocity
|
64
|
+
|
65
|
+
I.velocity = I.velocity.scale(I.speed)
|
66
|
+
|
@@ -0,0 +1,82 @@
|
|
1
|
+
###*
|
2
|
+
The Cooldown module provides a declarative way to manage cooldowns on
|
3
|
+
GameObject's properties.
|
4
|
+
|
5
|
+
# Health regeneration
|
6
|
+
player = GameObject
|
7
|
+
health: 50
|
8
|
+
|
9
|
+
# health will approach
|
10
|
+
# 100 by 1 every second
|
11
|
+
player.cooldown "health",
|
12
|
+
target: 100
|
13
|
+
|
14
|
+
elapsedTime = 1
|
15
|
+
player.update(elapsedTime)
|
16
|
+
|
17
|
+
player.I.health
|
18
|
+
# => 51
|
19
|
+
|
20
|
+
# Shoot Timeout
|
21
|
+
player = GameObject()
|
22
|
+
|
23
|
+
# by default the cooldown
|
24
|
+
# approaches 0 by 1 each second
|
25
|
+
player.cooldown "shootTimer"
|
26
|
+
|
27
|
+
player.I.shootTimer = 10 # => Pew! Pew!
|
28
|
+
|
29
|
+
player.update(elapsedTime)
|
30
|
+
|
31
|
+
player.I.shootTimer # => 9
|
32
|
+
|
33
|
+
# Turbo Cooldown
|
34
|
+
player = GameObject()
|
35
|
+
|
36
|
+
# turboTimer starts at 1000
|
37
|
+
# and approaches 12 by 5 each second
|
38
|
+
player.cooldown "turboTimer",
|
39
|
+
approachBy: 5
|
40
|
+
value: 1000
|
41
|
+
target: 12
|
42
|
+
|
43
|
+
player.I.turboTimer = 1000
|
44
|
+
|
45
|
+
player.update(elapsedTime)
|
46
|
+
|
47
|
+
player.I.turboTimer # => 995
|
48
|
+
|
49
|
+
@name Cooldown
|
50
|
+
@module
|
51
|
+
@constructor
|
52
|
+
@param {Object} I Instance variables
|
53
|
+
@param {Core} self Reference to including object
|
54
|
+
###
|
55
|
+
Cooldown = (I, self) ->
|
56
|
+
Object.reverseMerge I,
|
57
|
+
cooldowns: {}
|
58
|
+
|
59
|
+
self.bind "update", (dt) ->
|
60
|
+
for name, cooldownOptions of I.cooldowns
|
61
|
+
{approachBy, target} = cooldownOptions
|
62
|
+
|
63
|
+
I[name] = I[name].approach(target, approachBy * dt)
|
64
|
+
|
65
|
+
cooldown: (name, options={}) ->
|
66
|
+
{target, approachBy, value} = options
|
67
|
+
|
68
|
+
target ||= 0
|
69
|
+
approachBy = 1 unless approachBy?
|
70
|
+
|
71
|
+
# Set the cooldown data
|
72
|
+
I.cooldowns[name] = {
|
73
|
+
target
|
74
|
+
approachBy
|
75
|
+
}
|
76
|
+
|
77
|
+
if value?
|
78
|
+
# Use the value given if any
|
79
|
+
I[name] = options.value
|
80
|
+
else
|
81
|
+
# Initialize field if it doesn't exist
|
82
|
+
I[name] = 0 unless I[name]
|
@@ -0,0 +1,253 @@
|
|
1
|
+
###*
|
2
|
+
The Debuggable Module provides a simple API to easily display
|
3
|
+
an object's properties onscreen. This mixin comes with predefined
|
4
|
+
attribute filters so that you can exclude irrelevant data.
|
5
|
+
|
6
|
+
player = GameObject
|
7
|
+
x: 40
|
8
|
+
y: 14
|
9
|
+
spriteName: null
|
10
|
+
numericErrorProperty: NaN
|
11
|
+
|
12
|
+
player.include Debuggable
|
13
|
+
|
14
|
+
# sets up debug output for all player's properties
|
15
|
+
# at the starting position (0, 0)
|
16
|
+
player.debug
|
17
|
+
filter: 'all'
|
18
|
+
|
19
|
+
@name Debuggable
|
20
|
+
@module
|
21
|
+
@constructor
|
22
|
+
@param {Object} I Instance variables
|
23
|
+
@param {Core} self Reference to including object
|
24
|
+
###
|
25
|
+
Debuggable = (I={}, self) ->
|
26
|
+
COL_HEIGHT = 175
|
27
|
+
ROW_HEIGHT = 9
|
28
|
+
FONT_SIZE = 9
|
29
|
+
|
30
|
+
debugX = 0
|
31
|
+
debugY = 0
|
32
|
+
|
33
|
+
Object.reverseMerge I,
|
34
|
+
debug:
|
35
|
+
enabled: false
|
36
|
+
color: 'black'
|
37
|
+
filter: 'all'
|
38
|
+
bounds: true
|
39
|
+
velocity: true
|
40
|
+
position:
|
41
|
+
x: 0
|
42
|
+
y: 0
|
43
|
+
|
44
|
+
initialI = Object.extend({}, I)
|
45
|
+
|
46
|
+
debugBounds = (canvas) ->
|
47
|
+
canvas.drawRect
|
48
|
+
color: 'rgba(255, 0, 255, 0.4)'
|
49
|
+
bounds: self.bounds()
|
50
|
+
|
51
|
+
debugVelocity = (canvas) ->
|
52
|
+
if I.velocity?
|
53
|
+
canvas.withTransform Matrix.translation(I.x, I.y), (canvas) ->
|
54
|
+
thickness = 4
|
55
|
+
|
56
|
+
color = 'rgba(255, 0, 0, 0.5)'
|
57
|
+
|
58
|
+
canvas.drawRect
|
59
|
+
x: 0
|
60
|
+
y: -thickness / 2
|
61
|
+
width: I.velocity.x
|
62
|
+
height: thickness
|
63
|
+
color: color
|
64
|
+
|
65
|
+
canvas.drawRect
|
66
|
+
x: -thickness / 2
|
67
|
+
y: 0
|
68
|
+
width: thickness
|
69
|
+
height: I.velocity.y
|
70
|
+
color: color
|
71
|
+
|
72
|
+
filterProperties = (properties) ->
|
73
|
+
results = {}
|
74
|
+
|
75
|
+
switch I.debug.filter
|
76
|
+
when 'all'
|
77
|
+
results = properties
|
78
|
+
when 'undefined'
|
79
|
+
for key, value of properties
|
80
|
+
results[key] = value if not value? or nan(value)
|
81
|
+
when 'changed'
|
82
|
+
for key, value of properties
|
83
|
+
results[key] = value if initialI[key] isnt value
|
84
|
+
|
85
|
+
return results
|
86
|
+
|
87
|
+
sortedKeys = ->
|
88
|
+
keys = []
|
89
|
+
|
90
|
+
for key, value of filterProperties(I)
|
91
|
+
keys.push key
|
92
|
+
|
93
|
+
keys.sort()
|
94
|
+
|
95
|
+
nan = (value) ->
|
96
|
+
typeof value is 'number' and isNaN(value)
|
97
|
+
|
98
|
+
drawDebugLine = (text, canvas, x, y) ->
|
99
|
+
canvas.drawText
|
100
|
+
color: I.debug.color
|
101
|
+
x: x + I.debug.position.x
|
102
|
+
y: y + I.debug.position.y
|
103
|
+
text: text
|
104
|
+
|
105
|
+
debugY += ROW_HEIGHT
|
106
|
+
|
107
|
+
getPropertyRow = (key, value, canvas) ->
|
108
|
+
# exclude functions returned by iterating over
|
109
|
+
# objects like Color, Point, etc.
|
110
|
+
if typeof value is 'function'
|
111
|
+
return
|
112
|
+
else if Object.isObject(value)
|
113
|
+
drawDebugLine(key, canvas, debugX, debugY)
|
114
|
+
|
115
|
+
debugX += 8
|
116
|
+
|
117
|
+
for k, v of value
|
118
|
+
getPropertyRow(k, v, canvas)
|
119
|
+
|
120
|
+
debugX -= 8
|
121
|
+
else if Object.isArray(value)
|
122
|
+
toStringArray = (for v in value
|
123
|
+
if Object.isObject(v)
|
124
|
+
v.I.class || v.toString()
|
125
|
+
else
|
126
|
+
v
|
127
|
+
)
|
128
|
+
|
129
|
+
drawDebugLine("#{key}(#{value.length}): #{toStringArray}", canvas, debugX, debugY)
|
130
|
+
else
|
131
|
+
value = processValue(value)
|
132
|
+
|
133
|
+
drawDebugLine("#{key}: #{value}", canvas, debugX, debugY)
|
134
|
+
|
135
|
+
processValue = (value) ->
|
136
|
+
output = value
|
137
|
+
|
138
|
+
try
|
139
|
+
parsedNumber = parseFloat(value)
|
140
|
+
|
141
|
+
if parsedNumber
|
142
|
+
if typeof value isnt 'string' and parsedNumber isnt parseInt(value)
|
143
|
+
output = value.toFixed(3)
|
144
|
+
|
145
|
+
return output
|
146
|
+
|
147
|
+
self.bind "update", ->
|
148
|
+
if justPressed['0']
|
149
|
+
self.toggleDebug()
|
150
|
+
|
151
|
+
self.bind "overlay", (canvas) ->
|
152
|
+
if I.debug.enabled
|
153
|
+
canvas.font "#{FONT_SIZE}px Monaco"
|
154
|
+
|
155
|
+
debugX = 0
|
156
|
+
debugY = ROW_HEIGHT
|
157
|
+
|
158
|
+
for key in sortedKeys()
|
159
|
+
getPropertyRow(key, I[key], canvas)
|
160
|
+
|
161
|
+
debugX += COL_HEIGHT
|
162
|
+
debugY = ROW_HEIGHT
|
163
|
+
|
164
|
+
debugBounds(canvas) if I.debug.bounds
|
165
|
+
debugVelocity(canvas) if I.debug.velocity
|
166
|
+
|
167
|
+
###*
|
168
|
+
Enable debugging display for the calling GameObject.
|
169
|
+
|
170
|
+
player = GameObject
|
171
|
+
x: 40
|
172
|
+
y: 14
|
173
|
+
spriteName: null
|
174
|
+
numericErrorProperty: NaN
|
175
|
+
|
176
|
+
player.include Debuggable
|
177
|
+
|
178
|
+
# sets up debug output for all player's properties
|
179
|
+
# at the starting position (0, 0)
|
180
|
+
player.debug
|
181
|
+
filter: 'all'
|
182
|
+
|
183
|
+
player.I.y = 45
|
184
|
+
|
185
|
+
# sets up debug output for only properties that have
|
186
|
+
# changed since initialization. In this case only y
|
187
|
+
# would be displayed.
|
188
|
+
player.debug
|
189
|
+
filter: 'changed'
|
190
|
+
|
191
|
+
# sets up debug output for properties that are <code>undefined</code>,
|
192
|
+
# <code>null</code>, or <code>NaN</code>. In this case spriteName and
|
193
|
+
# numericErrorProperty would be displayed.
|
194
|
+
player.debug
|
195
|
+
filter: 'undefined'
|
196
|
+
|
197
|
+
# sets up debug output using all possible configuration options
|
198
|
+
player.debug
|
199
|
+
bounds: true # set this to false to disable visual debugging of the object's bounding box
|
200
|
+
color: 'red' # color of debug text
|
201
|
+
filter: 'all'
|
202
|
+
x: 30 # x position to start printing debug information
|
203
|
+
y: 50 # y position to start printing debug information
|
204
|
+
velocity: true # set this to false to disable visual debugging of the object's velocity
|
205
|
+
|
206
|
+
@name debug
|
207
|
+
@methodOf Debuggable#
|
208
|
+
@param {Object} Options to configure debug output
|
209
|
+
@param {Boolean} bounds Whether or not to visually debug the object's bounds
|
210
|
+
@param {Color|String} color The color of the debug text
|
211
|
+
@param {String} filter Choices include 'all', 'changed', and 'undefined'
|
212
|
+
@param {Number} x The x position to start drawing the debug information
|
213
|
+
@param {Number} y The y position to start drawing the debug information
|
214
|
+
@param {Boolean} velocity Whether or not to visually debug the object's velocity
|
215
|
+
###
|
216
|
+
debug: (options={}) ->
|
217
|
+
{x, y} = options
|
218
|
+
|
219
|
+
I.debug.position.x = x if x?
|
220
|
+
I.debug.position.y = y if y?
|
221
|
+
|
222
|
+
Object.extend I.debug, options
|
223
|
+
|
224
|
+
I.debug.enabled = true
|
225
|
+
|
226
|
+
###*
|
227
|
+
Toggle display of debug information.
|
228
|
+
|
229
|
+
player = GameObject()
|
230
|
+
|
231
|
+
player.include Debuggable
|
232
|
+
|
233
|
+
# enables debug display
|
234
|
+
player.debug()
|
235
|
+
|
236
|
+
# disables debug display
|
237
|
+
player.toggleDisable()
|
238
|
+
|
239
|
+
# if false is passed to toggleDisplay, then debugging is disabled.
|
240
|
+
player.toggleDisplay(false)
|
241
|
+
|
242
|
+
# if true is passed to toggleDisplay, then debugging is enabled.
|
243
|
+
player.toggleDisplay(true)
|
244
|
+
|
245
|
+
@name toggleDebug
|
246
|
+
@methodOf Debuggable#
|
247
|
+
@param {Boolean} newVal If true is passed then debugging is enabled, if false is passed then debugging is disabled, if nothing is passed, then debug state is toggled.
|
248
|
+
###
|
249
|
+
toggleDebug: (newVal) ->
|
250
|
+
if newVal?
|
251
|
+
I.debug.enabled = newVal
|
252
|
+
else
|
253
|
+
I.debug.enabled = not I.debug.enabled
|
@@ -0,0 +1,167 @@
|
|
1
|
+
###*
|
2
|
+
The Drawable module is used to provide a simple draw method to the including
|
3
|
+
object.
|
4
|
+
|
5
|
+
Binds a default draw listener to draw a rectangle or a sprite, if one exists.
|
6
|
+
|
7
|
+
Binds a step listener to update the transform of the object.
|
8
|
+
|
9
|
+
Autoloads the sprite specified in I.sprite, if any.
|
10
|
+
|
11
|
+
player = Core
|
12
|
+
x: 15
|
13
|
+
y: 30
|
14
|
+
width: 5
|
15
|
+
height: 5
|
16
|
+
sprite: "my_cool_sprite"
|
17
|
+
|
18
|
+
engine.bind 'draw', (canvas) ->
|
19
|
+
player.draw(canvas)
|
20
|
+
# => Uncaught TypeError: Object has no method 'draw'
|
21
|
+
|
22
|
+
player.include(Drawable)
|
23
|
+
|
24
|
+
engine.bind 'draw', (canvas) ->
|
25
|
+
player.draw(canvas)
|
26
|
+
# => if you have a sprite named "my_cool_sprite" in your images folder
|
27
|
+
# then it will be drawn. Otherwise, a rectangle positioned at x: 15 and
|
28
|
+
# y: 30 with width and height 5 will be drawn.
|
29
|
+
|
30
|
+
@name Drawable
|
31
|
+
@module
|
32
|
+
@constructor
|
33
|
+
@param {Object} I Instance variables
|
34
|
+
@param {Core} self Reference to including object
|
35
|
+
###
|
36
|
+
|
37
|
+
###*
|
38
|
+
Triggered every time the object should be drawn. A canvas is passed as
|
39
|
+
the first argument.
|
40
|
+
|
41
|
+
player = Core
|
42
|
+
x: 0
|
43
|
+
y: 10
|
44
|
+
width: 5
|
45
|
+
height: 5
|
46
|
+
|
47
|
+
player.bind "draw", (canvas) ->
|
48
|
+
# Text will be drawn positioned relatively to the object.
|
49
|
+
canvas.drawText
|
50
|
+
text: "Hey, drawing stuff is pretty easy."
|
51
|
+
color: "white"
|
52
|
+
x: 5
|
53
|
+
y: 5
|
54
|
+
|
55
|
+
@name draw
|
56
|
+
@methodOf Drawable#
|
57
|
+
@event
|
58
|
+
@param {PowerCanvas} canvas A reference to the canvas to draw on.
|
59
|
+
###
|
60
|
+
|
61
|
+
###*
|
62
|
+
Triggered before the object should be drawn. A canvas is passed as
|
63
|
+
the first argument. This does not apply the current transform.
|
64
|
+
|
65
|
+
@name beforeTransform
|
66
|
+
@methodOf Drawable#
|
67
|
+
@event
|
68
|
+
@param {PowerCanvas} canvas A reference to the canvas to draw on.
|
69
|
+
###
|
70
|
+
|
71
|
+
###*
|
72
|
+
Triggered after the object should be drawn. A canvas is passed as
|
73
|
+
the first argument. This applies the current transform.
|
74
|
+
|
75
|
+
@name afterTransform
|
76
|
+
@methodOf Drawable#
|
77
|
+
@event
|
78
|
+
@param {PowerCanvas} canvas A reference to the canvas to draw on.
|
79
|
+
###
|
80
|
+
|
81
|
+
Drawable = (I={}, self) ->
|
82
|
+
Object.reverseMerge I,
|
83
|
+
alpha: 1
|
84
|
+
color: "#196"
|
85
|
+
scale: 1
|
86
|
+
scaleX: 1
|
87
|
+
scaleY: 1
|
88
|
+
zIndex: 0
|
89
|
+
|
90
|
+
cachedSprite = null
|
91
|
+
|
92
|
+
self.unbind ".Drawable"
|
93
|
+
|
94
|
+
self.bind 'draw.Drawable', (canvas) ->
|
95
|
+
if I.alpha? and I.alpha != 1
|
96
|
+
previousAlpha = canvas.context().globalAlpha
|
97
|
+
canvas.context().globalAlpha = I.alpha
|
98
|
+
|
99
|
+
if sprite = self.sprite()
|
100
|
+
if sprite.draw?
|
101
|
+
sprite.draw(canvas, -sprite.width / 2, -sprite.height / 2)
|
102
|
+
else
|
103
|
+
warn?("Sprite has no draw method!")
|
104
|
+
else
|
105
|
+
if I.radius?
|
106
|
+
canvas.drawCircle
|
107
|
+
x: 0
|
108
|
+
y: 0
|
109
|
+
radius: I.radius
|
110
|
+
color: I.color
|
111
|
+
else
|
112
|
+
canvas.drawRect
|
113
|
+
x: -I.width/2
|
114
|
+
y: -I.height/2
|
115
|
+
width: I.width
|
116
|
+
height: I.height
|
117
|
+
color: I.color
|
118
|
+
|
119
|
+
if I.alpha? and I.alpha != 1
|
120
|
+
canvas.context().globalAlpha = previousAlpha
|
121
|
+
|
122
|
+
###*
|
123
|
+
Draw does not actually do any drawing itself, instead it triggers all of the draw events.
|
124
|
+
Listeners on the events do the actual drawing.
|
125
|
+
|
126
|
+
@name draw
|
127
|
+
@methodOf Drawable#
|
128
|
+
@returns self
|
129
|
+
###
|
130
|
+
draw: (canvas) ->
|
131
|
+
self.trigger 'beforeTransform', canvas
|
132
|
+
|
133
|
+
canvas.withTransform self.transform(), (canvas) ->
|
134
|
+
self.trigger 'beforeDraw', canvas
|
135
|
+
self.trigger 'draw', canvas
|
136
|
+
self.trigger 'afterDraw', canvas
|
137
|
+
|
138
|
+
self.trigger 'afterTransform', canvas
|
139
|
+
|
140
|
+
return self
|
141
|
+
|
142
|
+
sprite: (newSprite) ->
|
143
|
+
if newSprite?
|
144
|
+
cachedSprite = newSprite
|
145
|
+
else
|
146
|
+
if I.sprite
|
147
|
+
Sprite.loadByName(I.sprite)
|
148
|
+
|
149
|
+
###*
|
150
|
+
Returns the current transform, with translation, rotation, and flipping applied.
|
151
|
+
|
152
|
+
@name transform
|
153
|
+
@methodOf Drawable#
|
154
|
+
@returns {Matrix} The current transform
|
155
|
+
###
|
156
|
+
transform: ->
|
157
|
+
center = self.center()
|
158
|
+
|
159
|
+
transform = Matrix.translation(center.x.floor(), center.y.floor())
|
160
|
+
|
161
|
+
transform = transform.concat(Matrix.scale(I.scale * I.scaleX, I.scale * I.scaleY))
|
162
|
+
transform = transform.concat(Matrix.rotation(I.rotation)) if I.rotation
|
163
|
+
|
164
|
+
if I.spriteOffset
|
165
|
+
transform = transform.concat(Matrix.translation(I.spriteOffset.x, I.spriteOffset.y))
|
166
|
+
|
167
|
+
return transform
|
@@ -0,0 +1,36 @@
|
|
1
|
+
DustParticle = (I={}, self) ->
|
2
|
+
Object.extend I,
|
3
|
+
color: 'rgb(100, 100, 100)'
|
4
|
+
duration: 0.2
|
5
|
+
fadeOut: true
|
6
|
+
maxSpeed: 90
|
7
|
+
|
8
|
+
I.velocity = [
|
9
|
+
Point(-60, -30)
|
10
|
+
Point(40, -15)
|
11
|
+
Point(-20, -7)
|
12
|
+
Point(60, -30)
|
13
|
+
Point(40, -15)
|
14
|
+
Point(20, -7)
|
15
|
+
].rand()
|
16
|
+
|
17
|
+
I.acceleration = Point(0, 60)
|
18
|
+
|
19
|
+
return {}
|
20
|
+
|
21
|
+
DustEmitter = (I={}) ->
|
22
|
+
Object.reverseMerge I,
|
23
|
+
duration: 3
|
24
|
+
particleCount: 20
|
25
|
+
batchSize: 5
|
26
|
+
x: 0
|
27
|
+
y: 0
|
28
|
+
zIndex: 50
|
29
|
+
generator:
|
30
|
+
includedModules: ["DustParticle"]
|
31
|
+
radius: (n) ->
|
32
|
+
[2, 3, 1].wrap(n)
|
33
|
+
|
34
|
+
self = Emitter(I)
|
35
|
+
|
36
|
+
return self
|
@@ -0,0 +1,38 @@
|
|
1
|
+
( ->
|
2
|
+
Easing =
|
3
|
+
sinusoidal: (begin, end) ->
|
4
|
+
change = end - begin
|
5
|
+
(t) -> begin + change * (1 - Math.cos(t * Math.TAU / 4))
|
6
|
+
|
7
|
+
sinusoidalOut: (begin, end) ->
|
8
|
+
change = end - begin
|
9
|
+
(t) -> begin + change * (0 + Math.sin(t * Math.TAU / 4))
|
10
|
+
|
11
|
+
polynomialEasings = ["linear", "quadratic", "cubic", "quartic", "quintic"]
|
12
|
+
|
13
|
+
polynomialEasings.each (easing, i) ->
|
14
|
+
exponent = i + 1
|
15
|
+
sign = if exponent % 2 then 1 else -1
|
16
|
+
|
17
|
+
Easing[easing] = (begin, end) ->
|
18
|
+
change = (end - begin)
|
19
|
+
(t) -> begin + change * Math.pow(t, exponent)
|
20
|
+
|
21
|
+
Easing["#{easing}Out"] = (begin, end) ->
|
22
|
+
change = end - begin
|
23
|
+
(t) -> begin + change * (1 + sign * Math.pow(t - 1, exponent))
|
24
|
+
|
25
|
+
["sinusoidal"].concat(polynomialEasings).each (easing) ->
|
26
|
+
Easing["#{easing}InOut"] = (begin, end) ->
|
27
|
+
midpoint = (begin + end)/2
|
28
|
+
easeIn = Easing[easing](begin, midpoint)
|
29
|
+
easeOut = Easing["#{easing}Out"](midpoint, end)
|
30
|
+
|
31
|
+
(t) ->
|
32
|
+
if t < 0.5
|
33
|
+
easeIn(2 * t)
|
34
|
+
else
|
35
|
+
easeOut(2 * t - 1)
|
36
|
+
|
37
|
+
(exports ? this)["Easing"] = Easing
|
38
|
+
)()
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# TODO: LOL at name
|
2
|
+
Emitterable = (I={}, self) ->
|
3
|
+
Object.reverseMerge I,
|
4
|
+
batchSize: 1
|
5
|
+
emissionRate: 1
|
6
|
+
width: 0
|
7
|
+
height: 0
|
8
|
+
sprite: Sprite.EMPTY
|
9
|
+
generator: {}
|
10
|
+
particles: []
|
11
|
+
particleCount: Infinity
|
12
|
+
x: I.x
|
13
|
+
y: I.y
|
14
|
+
particleData:
|
15
|
+
acceleration: Point(0, 0.1)
|
16
|
+
age: 0
|
17
|
+
color: "blue"
|
18
|
+
duration: 1.5
|
19
|
+
height: 2
|
20
|
+
maxSpeed: 120
|
21
|
+
offset: Point(0, 0)
|
22
|
+
sprite: false
|
23
|
+
spriteName: false
|
24
|
+
velocity: Point(-0.25, 1)
|
25
|
+
width: 2
|
26
|
+
x: 0
|
27
|
+
y: 0
|
28
|
+
|
29
|
+
n = 0
|
30
|
+
|
31
|
+
self.bind 'draw', (canvas) ->
|
32
|
+
I.particles.invoke "draw", canvas
|
33
|
+
|
34
|
+
self.bind 'overlay', (canvas) ->
|
35
|
+
I.particles.invoke "trigger", "overlay", canvas
|
36
|
+
|
37
|
+
self.bind 'beforeUpdate', (dt) ->
|
38
|
+
I.particles.invoke "trigger", "beforeUpdate", dt
|
39
|
+
|
40
|
+
self.bind 'afterUpdate', (dt) ->
|
41
|
+
I.particles.invoke "trigger", "afterUpdate", dt
|
42
|
+
|
43
|
+
self.bind 'update', (dt) ->
|
44
|
+
I.batchSize.times ->
|
45
|
+
if n < I.particleCount && rand() < I.emissionRate
|
46
|
+
particleProperties = Object.extend {}, I.particleData
|
47
|
+
|
48
|
+
for key, value of I.generator
|
49
|
+
if I.generator[key].call
|
50
|
+
particleProperties[key] = I.generator[key](n, I)
|
51
|
+
else
|
52
|
+
particleProperties[key] = I.generator[key]
|
53
|
+
|
54
|
+
particleProperties.x += particleProperties.offset.x
|
55
|
+
particleProperties.y += particleProperties.offset.y
|
56
|
+
|
57
|
+
I.particles.push(GameObject(particleProperties))
|
58
|
+
|
59
|
+
n += 1
|
60
|
+
|
61
|
+
I.particles = I.particles.select (particle) ->
|
62
|
+
particle.update(dt)
|
63
|
+
|
64
|
+
if n == I.particleCount && !I.particles.length
|
65
|
+
I.active = false
|
66
|
+
|
67
|
+
return {}
|
68
|
+
|