examples/moles: move crazy moles to the contrib folder
[nit.git] / contrib / crazy_moles / src / effects.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Graphical effects
16 module effects
17
18 intrude import geometry::points_and_lines
19 import mnit::opengles1
20
21 import moles
22
23 # Position, or vector
24 class Pos
25 super Point[Float]
26
27 # Addition of `self` and `other`
28 fun +(other: Pos): Pos do return new Pos(other.x + x, other.y + y)
29 end
30
31 redef class PointerEvent
32 # Convert to `Pos`
33 fun to_pos: Pos do return new Pos(x, y)
34 end
35
36 redef class Float
37 # Pretty alias to `pow`
38 private fun ^(other: Float): Float do return self.pow(other)
39 end
40
41 # Graphical effect
42 abstract class Effect
43 # Time of creation since (appropriate) app lauch
44 var created_at: Float = app.clock.total.to_f
45
46 # Is this effect dead?
47 var dead = false
48
49 # Time to live of this effect, in seconds
50 var ttl: Float = ttl_base + ttl_rand.rand is lazy
51
52 # Time to live base value
53 private var ttl_base: Float
54
55 # Variation range to add to `ttl_base`
56 private var ttl_rand: Float
57
58 private fun update_and_draw(display: Display, t: Float) do end
59 end
60
61 # Full screen flash
62 class Flash
63 super Effect
64
65 # Red value
66 var r: Float
67
68 # Green value
69 var g: Float
70
71 # Blue value
72 var b: Float
73
74 # Start alpha value
75 var a: Float
76
77 # Reduction in alpha value per seconds
78 var da: Float
79
80 redef fun update_and_draw(display, t)
81 do
82 var dt = t - created_at
83 var a = a - da * dt
84 if a <= 0.0 then
85 dead = true
86 return
87 end
88
89 native_flash(r, g, b, a, display.width.to_f, display.height.to_f)
90 end
91
92 private fun native_flash(r, g, b, a, w, h: Float) `{
93 GLfloat colors[] =
94 {
95 r, g, b, a,
96 r, g, b, a,
97 r, g, b, a,
98 r, g, b, a,
99 };
100 GLfloat coords[] =
101 {
102 0.0, 0.0, 0.0,
103 0.0, h, 0.0,
104 w, 0.0, 0.0,
105 w, h, 0.0,
106 };
107
108 glLoadIdentity();
109
110 glEnableClientState(GL_VERTEX_ARRAY);
111 glEnableClientState(GL_COLOR_ARRAY);
112
113 glEnable(GL_BLEND);
114 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
115
116 glDisable(GL_DEPTH_TEST);
117 glDisable(GL_TEXTURE_2D);
118
119 glVertexPointer(3, GL_FLOAT, 0, coords);
120 glColorPointer(4, GL_FLOAT, 0, colors);
121 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
122
123 glDisableClientState(GL_VERTEX_ARRAY);
124 glDisableClientState(GL_COLOR_ARRAY);
125 glDisable(GL_BLEND);
126
127 if ((mnit_opengles_error_code = glGetError()) != GL_NO_ERROR) {
128 fprintf(stderr, "Error drawing: %i\n", mnit_opengles_error_code);
129 }
130 `}
131 end
132
133 # Visible particle
134 abstract class Particle
135 super Effect
136
137 # Effect image
138 var img: Image
139 end
140
141 # Particle moving along a cubic Bézier curve
142 class CubicBezierParticle
143 super Particle
144
145 # Points on this curve, from the start to the end with the handles in the middle
146 #
147 # Require: `points.length == 4`
148 var points: Array[Pos]
149
150 redef fun update_and_draw(display, t)
151 do
152 assert points.length == 4
153
154 var dt = t - created_at
155 var p = dt / ttl
156
157 var i = 1.0-p
158 var bx = i*i*i * points[0].x + 3.0*i*i*p * points[1].x +
159 3.0*i*p*p * points[2].x + p*p*p*points[3].x
160 var by = i*i*i * points[0].y + 3.0*i*i*p * points[1].y +
161 3.0*i*p*p * points[2].y + p*p*p*points[3].y
162
163 img.scale = display_scale
164 if display isa Opengles1Display then display.color(1.0, 1.0, 1.0, p)
165 display.blit_centered(img, bx, by)
166 if display isa Opengles1Display then display.reset_color
167
168 if dt > ttl then dead = true
169 end
170 end
171
172 # Particle falling like a feather
173 class FeatheryParticle
174 super Particle
175
176 # Origin of effect
177 var from: Pos
178
179 # Randomized variation so this particle is unique
180 var ddt: Float = pi.rand
181
182 # Direction: `-1.0` for left, `1.0` for right
183 var dir: Float = if 2.rand == 0 then -1.0 else 1.0
184
185 # Randomized variation on X
186 var ddx: Float = (4.0 - 8.0.rand)^2.0
187
188 # Randomized variation on Y
189 var ddy: Float = (12.0 - 24.0.rand)^2.0
190
191 redef fun update_and_draw(display, t)
192 do
193 var dt = t - created_at
194
195 var dx = ddx + 30.0*(dt+ddt).sin
196 dx *= dir
197 var dy = ddy + 20.0*dt + 16.0*(dt*2.0+ddt*2.0).cos
198 var pos = from + new Pos(dx, dy)
199
200 if display isa Opengles1Display then display.color(1.0, 1.0, 1.0, 5.0-5.0*dt/ttl)
201 display.blit_centered(img, pos.x, pos.y)
202 if display isa Opengles1Display then display.reset_color
203
204 if dt > ttl then dead = true
205 end
206 end
207
208 # Particles that start small then grow bigger and fade out
209 private class BlowUpParticle
210 super Particle
211
212 # Origin/center of effect
213 var from: Pos
214
215 redef fun update_and_draw(display, t)
216 do
217 var dt = t - created_at
218 var p = dt/ttl
219
220 if display isa Opengles1Display then display.color(1.0, 1.0, 1.0, 2.0-p*2.0)
221
222 img.scale = p*4.0*display_scale
223 display.blit_centered(img, from.x, from.y)
224
225 if display isa Opengles1Display then display.reset_color
226
227 if dt > ttl then dead = true
228 end
229 end
230
231 redef class Screen
232 private var particles = new Array[Particle]
233 private var flashes = new Array[Flash]
234
235 redef fun draw_hud(display)
236 do
237 var t = app.clock.total.to_f
238
239 # Particles
240 for particle in particles do particle.update_and_draw(display, t)
241 for particle in particles.reverse_iterator do if particle.dead then
242 particles.remove particle
243 end
244
245 # Flashes
246 for flash in flashes do flash.update_and_draw(display, t)
247 for flash in flashes.reverse_iterator do if flash.dead then
248 flashes.remove flash
249 end
250
251 super
252 end
253
254 private var score_center = new Pos(48.0*display_scale, 32.0*display_scale) is lazy
255 private var score_entry = new Pos(48.0*display_scale, 256.0*display_scale) is lazy
256
257 private var score_history = new List[Int]
258 private var score_history_max_length = 60
259
260 redef fun draw_score(display, score)
261 do
262 # Use an history to smooth the score
263 score_history.add game.points
264 if score_history.length > score_history_max_length then score_history.shift
265 var sum = 0
266 for h in score_history do sum += h
267 var avg = sum.to_f/score_history.length.to_f
268 var d = game.points.to_f - avg
269
270 # Color the score according to positive or negative changes
271 var r = 1.0
272 var g = 1.0
273 var b = 1.0
274 if d > 0.0 then
275 r = 1.0-d.to_f/2.0
276 b = r
277 g = 1.0-d.to_f/10.0
278 else if d < 0.0 then
279 g = 1.0+d.to_f/5.0
280 b = g
281 end
282
283 if display isa Opengles1Display then display.color(r, g, b, 1.0)
284
285 # Draw the score itself
286 super(display, avg.to_i)
287
288 if display isa Opengles1Display then display.reset_color
289 end
290 end
291
292 redef class HoleContent
293 # Add a `CubicBezierParticle` from `event` to the score box with `img`
294 private fun bezier_to_score(img: Image, event: PointerEvent)
295 do
296 app.screen.particles.add new CubicBezierParticle(2.0, 0.0, img,
297 [event.to_pos, event.to_pos + new Pos(0.0, -128.0),
298 app.screen.score_entry, app.screen.score_center])
299 end
300 end
301
302 redef class Mole
303
304 # Number of hair particles
305 private var n_hair_on_hit = 20
306
307 redef fun hit(game, hole, event)
308 do
309 super
310
311 app.assets.hair.scale = display_scale
312 for i in n_hair_on_hit.times do
313 app.screen.particles.add new FeatheryParticle(2.0, 2.0, app.assets.hair, event.to_pos)
314 end
315
316 bezier_to_score(app.assets.point, event)
317 app.screen.particles.add new BlowUpParticle(0.5, 0.0, app.assets.point, event.to_pos)
318 end
319 end
320
321 redef class Trap
322 # Image for `CubicBezierParticle` effect towards the score board
323 protected fun penalty_img: Image do return app.assets.penalty_ten
324
325 # `Flash` effects on hit
326 protected fun flashes: Array[Flash] do
327 return [new Flash(0.5, 0.0, 1.0, 0.0, 0.0, 0.5, 2.0)]
328 end
329
330 redef fun hit(game, hole, event)
331 do
332 super
333
334 bezier_to_score(penalty_img, event)
335 app.screen.particles.add new BlowUpParticle(0.5, 0.0, penalty_img, event.to_pos)
336
337 app.screen.flashes.add_all flashes
338 end
339 end