Engine 0.0.4
Game engine in lua
Loading...
Searching...
No Matches
game.lua
Go to the documentation of this file.
1--! @par Game FSM
2--! @startuml
3--! hide empty description
4--! state 1 as "menu"
5--! state 2 as "credits"
6--! state 3 as "game_spawn"
7--! state 4 as "game_play"
8--! state 5 as "game_player_dead"
9--! state 6 as "game_player_win"
10--! state 7 as "game_over"
11--! state 8 as "menu"
12--!
13--! [*] -> 1
14--! 1 ---> 2
15--! 1 --> 3
16--! 2 --> 1
17--! 3 --> 4
18--! 4 --> 5
19--! 4 --> 6
20--! 5 --> 3
21--! 5 --> 7
22--! 6 --> 3
23--! 7 --> [*]
24--!
25--! 4 -> 8: pause
26--! 8 -> 4: resume
27--! @enduml
28
29--! @cond
30local function draw_logo(std, game, height, anim)
31 anim = anim or 0
32 std.draw.font('sans', 32)
33 std.draw.color(std.color.white)
34 local s1 = std.draw.text('AsteroidsTv')
35 local s2 = std.draw.text('Tv')
36 std.draw.text(game.width/2 - s1/2, height + anim, 'Asteroids')
37 std.draw.color(std.color.red)
38 std.draw.text(game.width/2 + s1/2 - s2, height - anim, 'Tv')
39 return s1
40end
41
42local function intersect_line_circle(x1, y1, x2, y2, h, k, raio)
43 local m = (y2 - y1) / (x2 - x1)
44 local c = y1 - m * x1
45 local A = 1 + m^2
46 local B = 2 * (m * c - m * k - h)
47 local C = h^2 + k^2 + c^2 - 2 * c * k - raio^2
48 local discriminante = B^2 - 4 * A * C
49 return discriminante >= 0
50end
51
52local function asteroid_fragments(game, size, level)
53 -- level 1,2,3
54 if size == game.asteroid_small_mini then return 0, -1, 50 end
55 if size == game.asteroid_small_size and level <=3 then return 0, -1, 15 end
56 if size == game.asteroid_mid_size and level <= 3 then return 2, game.asteroid_small_size, 10 end
57 if size == game.asteroid_large_size and level <= 3 then return 1, game.asteroid_mid_size, 5 end
58 -- level 4,5,6
59 if size == game.asteroid_small_size and level <= 6 then return 1, game.asteroid_mini_size, 20 end
60 if size == game.asteroid_mid_size and level <= 6 then return 2, game.asteroid_small_size, 15 end
61 if size == game.asteroid_large_size and level <= 6 then return 1, game.asteroid_mid_size, 10 end
62 -- level 7,8,9
63 if size == game.asteroid_small_size and level <= 9 then return 1, game.asteroid_mini_size, 25 end
64 if size == game.asteroid_mid_size and level <= 9 then return 3, game.asteroid_small_size, 20 end
65 if size == game.asteroid_large_size and level <= 9 then return 1, game.asteroid_mid_size, 15 end
66 -- level 10... all asteroids
67 if size == game.asteroid_small_size then return 1, game.asteroid_mini_size, 40 end
68 if size == game.asteroid_mid_size then return 3, game.asteroid_small_size, 30 end
69 if size == game.asteroid_large_size then return 2, game.asteroid_mid_size, 20 end
70 return 0, -1, 0
71end
72
73local function asteroid_nest(std, game, x, y, id)
74 local index = 1
75 while index < #game.asteroid_size do
76 if index ~= id and game.asteroid_size[index] ~= -1 then
77 local distance = std.math.dis(x, y, game.asteroid_pos_x[index], game.asteroid_pos_y[index])
78 if (distance - 3) <= (game.asteroid_size[index] / 2) then
79 return true
80 end
81 end
82 index = index + 1
83 end
84 return false
85end
86
87local function asteroids_rain(std, game)
88 local index = 1
89 local attemps = 1
90 local n1 = 0.5 * std.math.min(game.level/3, 1)
91 local n2 = 1.0 * std.math.min(game.level/3, 1)
92 local n3 = 2.0 * std.math.min(game.level/3, 1)
93 local n4 = 2.5 * std.math.min(game.level/3, 1)
94 local hspeed = {-n1, 0, 0, 0, 0, 0, n1}
95 local vspeed = {-n4, -n3, -n2, n2, n3, n4}
96 local middle_left = game.width/4
97 local middle_right = game.width/4 * 3
98
99 while index <= game.asteroids_max and index <= 10 do
100 repeat
101 local success = true
102 attemps = attemps + 1
103 game.asteroid_size[index] = game.asteroid_large_size
104 game.asteroid_pos_x[index] = std.math.random(1, game.width)
105 game.asteroid_pos_y[index] = std.math.random(1, game.height)
106 game.asteroid_spd_x[index] = hspeed[std.math.random(1, #hspeed)]
107 game.asteroid_spd_y[index] = vspeed[std.math.random(1, #vspeed)]
108
109 if game.asteroid_pos_x[index] > middle_left and game.asteroid_pos_x[index] < middle_right then
110 success = false
111 end
112
113 if asteroid_nest(std, game, game.asteroid_pos_x[index], game.asteroid_pos_x[index], index) then
114 success = false
115 end
116
117 if attemps > 100 then
118 success = true
119 end
120 until success
121 index = index + 1
122 end
123end
124
125local function asteroid_destroy(std, game, id)
126 local index = 1
127 local hspeed = {-1, 1}
128 local vspeed = {-2, -1, 1, 2}
129 local asteroids = #game.asteroid_size
130 local original_size = game.asteroid_size[id]
131 local fragments, size, score = asteroid_fragments(game, original_size, game.level)
132
133 game.asteroid_size[id] = -1
134
135 while index <= fragments and (game.asteroids_count + index) <= (game.asteroids_max + 1) do
136 game.asteroid_size[asteroids + index] = size
137 game.asteroid_pos_x[asteroids + index] = game.asteroid_pos_x[id]
138 game.asteroid_pos_y[asteroids + index] = game.asteroid_pos_y[id]
139 game.asteroid_spd_x[asteroids + index] = hspeed[std.math.random(1, #hspeed)] * std.math.min(game.level/5, 1)
140 game.asteroid_spd_y[asteroids + index] = vspeed[std.math.random(1, #vspeed)] * std.math.min(game.level/5, 1)
141 index = index + 1
142 end
143
144 return score
145end
146--! @endcond
147
148local function init(std, game)
149 -- game
150 game.boost = 0.12
151 game.speed_max = 5
152 game.asteroids_count = 0
153 -- configs
154 game.state = game.state or 1
155 game.lifes = game.lifes or 3
156 game.level = game.level or 1
157 game.score = game.score or 0
158 game.imortal = game.imortal or 0
159 game.highscore = game.highscore or 0
160 game.asteroids_max = game.asteroids_max or 60
161 game.graphics_fastest = game.graphics_fastest or 0
162 -- player
163 game.player_pos_x = game.width/2
164 game.player_pos_y = game.height/2
165 game.player_spd_x = 0
166 game.player_spd_y = 0
167 game.player_angle = 0
168 game.player_last_teleport = 0
169 -- cannon
170 game.laser_enabled = false
171 game.laser_pos_x1 = 0
172 game.laser_pos_y1 = 0
173 game.laser_pos_x2 = 0
174 game.laser_pos_y2 = 0
175 game.laser_last_fire = 0
176 game.laser_time_fire = 50
177 game.laser_time_recharge = 300
178 game.laser_distance_fire = 300
179 -- asteroids
180 game.asteroid_pos_x = {}
181 game.asteroid_pos_y = {}
182 game.asteroid_spd_x = {}
183 game.asteroid_spd_y = {}
184 game.asteroid_size = {}
185 -- polys
186 game.asteroid_large = {27, 0, 27, 15, 15, 12, 0, 30, 18, 39, 9, 48, 15, 60, 30, 66, 48, 66, 57, 57, 60, 51, 66, 42, 66, 33, 54, 12}
187 game.asteroid_mid = {6, 0, 0, 21, 9, 33, 9, 48, 24, 51, 36, 45, 48, 42, 36, 12, 48, 3, 18, 0}
188 game.asteroid_small = {3, 0, 0, 3, 3, 9, 3, 12, 0, 18, 6, 21, 12, 21, 18, 18, 21, 15, 21, 3, 12, 3, 9, 6}
189 game.asteroid_mini = {6, 0, 6, 6, 0, 6, 0, 12, 3, 18, 6, 18, 6, 15, 15, 15, 18, 9, 12, 6, 12, 0}
190 game.spaceship = {-2,3, 0,-2, 2,3}
191 -- sizes
192 game.asteroid_large_size = std.math.max(game.asteroid_large)
193 game.asteroid_mid_size = std.math.max(game.asteroid_mid)
194 game.asteroid_small_size = std.math.max(game.asteroid_small)
195 game.asteroid_mini_size = std.math.max(game.asteroid_mini)
196 -- menu
197 game.menu = 2
198 game.menu_time = 0
199 -- start
200 asteroids_rain(std, game)
201end
202
203local function loop(std, game)
204 if game.state == 1 then
205 local keyv = std.key.press.down - std.key.press.up
206 local keyh = std.key.press.right - std.key.press.left + std.key.press.enter + std.key.press.red
207 if keyv ~= 0 and game.milis > game.menu_time + 250 then
208 game.menu = std.math.clamp(game.menu + keyv, game.player_pos_x == (game.width/2) and 2 or 1, 8)
209 game.menu_time = game.milis
210 end
211 if keyh ~= 0 and game.milis > game.menu_time + 100 then
212 game.menu_time = game.milis
213 if game.menu == 1 then
214 game.state = 4
215 elseif game.menu == 2 then
216 std.game.reset()
217 game.state = 4
218 game.score = 0
219 elseif game.menu == 3 then
220 game.level = std.math.clamp2(game.level + keyh, 1, 99)
221 elseif game.menu == 4 then
222 game.imortal = std.math.clamp(game.imortal + keyh, 0, 1)
223 elseif game.menu == 5 then
224 game.asteroids_max = std.math.clamp2(game.asteroids_max + keyh, 5, 60)
225 elseif game.menu == 6 then
226 game.graphics_fastest = std.math.clamp(game.graphics_fastest + keyh, 0, 1)
227 game.fps_max = 100
228 elseif game.menu == 7 then
229 game.state = 2
230 elseif game.menu == 8 then
231 std.game.exit()
232 end
233 end
234 return
235 elseif game.state == 2 then
236 local key = std.key.press.down + std.key.press.up + std.key.press.right + std.key.press.left + std.key.press.enter + std.key.press.red
237 if key ~= 0 and game.milis > game.menu_time + 250 then
238 game.menu_time = game.milis
239 game.state = 1
240 end
241 return
242 end
243 -- enter in the menu
244 if std.key.press.green == 1 then
245 game.state = 1
246 end
247 -- player move
248 game.player_angle = std.math.cycle(game.player_angle + (std.key.press.right - std.key.press.left) * 0.1, std.math.pi * 2) * std.math.pi * 2
249 game.player_pos_x = game.player_pos_x + (game.player_spd_x/16 * game.dt)
250 game.player_pos_y = game.player_pos_y + (game.player_spd_y/16 * game.dt)
251 if std.key.press.up == 0 and (std.math.abs(game.player_spd_x) + std.math.abs(game.player_spd_y)) < 0.45 then
252 game.player_spd_x = 0
253 game.player_spd_y = 0
254 end
255 if std.key.press.up == 1 then
256 game.player_spd_x = game.player_spd_x + (game.boost * std.math.cos(game.player_angle - std.math.pi/2))
257 game.player_spd_y = game.player_spd_y + (game.boost * std.math.sin(game.player_angle - std.math.pi/2))
258 local max_spd_x = std.math.abs(game.speed_max * std.math.cos(game.player_angle - std.math.pi/2))
259 local max_spd_y = std.math.abs(game.speed_max * std.math.sin(game.player_angle - std.math.pi/2))
260 game.player_spd_x = std.math.clamp(game.player_spd_x, -max_spd_x, max_spd_x)
261 game.player_spd_y = std.math.clamp(game.player_spd_y, -max_spd_y, max_spd_y)
262 end
263 if game.player_pos_y < 3 then
264 game.player_pos_y = game.height
265 end
266 if game.player_pos_x < 3 then
267 game.player_pos_x = game.width
268 end
269 if game.player_pos_y > game.height then
270 game.player_pos_y = 3
271 end
272 if game.player_pos_x > game.width then
273 game.player_pos_x = 3
274 end
275 -- player teleport
276 if std.key.press.down == 1 and game.milis > game.player_last_teleport + 1000 then
277 game.player_last_teleport = game.milis
278 game.player_spd_x = 0
279 game.player_spd_y = 0
280 repeat
281 game.player_pos_x = std.math.random(1, game.width)
282 game.player_pos_y = std.math.random(1, game.height)
283 until not asteroid_nest(std, game, game.player_pos_x, game.player_pos_y, -1)
284 end
285 -- player shoot
286 if not game.laser_enabled and game.state == 4 and (std.key.press.red == 1 or std.key.press.enter == 1) then
287 local index = 1
288 local asteroids = #game.asteroid_size
289 local sin = std.math.cos(game.player_angle - std.math.pi/2)
290 local cos = std.math.sin(game.player_angle - std.math.pi/2)
291 local laser_fake_x = game.player_pos_x - (game.laser_distance_fire * sin * 2)
292 local laser_fake_y = game.player_pos_y - (game.laser_distance_fire * cos * 2)
293 game.laser_pos_x2 = game.player_pos_x + (game.laser_distance_fire * sin)
294 game.laser_pos_y2 = game.player_pos_y + (game.laser_distance_fire * cos)
295 game.laser_pos_x1 = game.player_pos_x + (12 * sin)
296 game.laser_pos_y1 = game.player_pos_y + (12 * cos)
297 game.laser_last_fire = game.milis
298 game.laser_enabled = true
299 while index <= asteroids do
300 if game.asteroid_size[index] ~= -1 then
301 local size = game.asteroid_size[index]/2
302 local x = game.asteroid_pos_x[index] + size
303 local y = game.asteroid_pos_y[index] + size
304 local dis_p1 = std.math.dis(game.laser_pos_x1, game.laser_pos_y1, x,y)
305 local dis_p2 = std.math.dis(game.laser_pos_x2, game.laser_pos_y2, x,y)
306 local dis_fake = std.math.dis(laser_fake_x, laser_fake_y, x,y)
307 local intersect = intersect_line_circle(game.laser_pos_x1, game.laser_pos_y1, game.laser_pos_x2, game.laser_pos_y2, x, y, size*2)
308 if intersect and dis_p2 < dis_fake and dis_p1 < game.laser_distance_fire then
309 game.score = game.score + asteroid_destroy(std, game, index)
310 end
311 end
312 index = index + 1
313 end
314 end
315 if game.laser_enabled and game.milis > game.laser_last_fire + game.laser_time_recharge then
316 game.laser_enabled = false
317 end
318 -- player death
319 if game.imortal ~= 1 and game.state == 4 and asteroid_nest(std, game, game.player_pos_x, game.player_pos_y, -1) then
320 game.menu_time = game.milis
321 game.lifes = game.lifes - 1
322 game.state = 5
323 end
324 -- asteroids move
325 local index = 1
326 game.asteroids_count = 0
327 while index <= #game.asteroid_size do
328 if game.asteroid_size[index] ~= -1 then
329 game.asteroids_count = game.asteroids_count + 1
330 game.asteroid_pos_x[index] = game.asteroid_pos_x[index] + game.asteroid_spd_x[index]
331 game.asteroid_pos_y[index] = game.asteroid_pos_y[index] + game.asteroid_spd_y[index]
332 if game.asteroid_pos_y[index] < 1 then
333 game.asteroid_pos_y[index] = game.height
334 end
335 if game.asteroid_pos_x[index] < 1 then
336 game.asteroid_pos_x[index] = game.width
337 end
338 if game.asteroid_pos_y[index] > game.height then
339 game.asteroid_pos_y[index] = 1
340 end
341 if game.asteroid_pos_x[index] > game.width then
342 game.asteroid_pos_x[index] = 1
343 end
344 end
345 index = index + 1
346 end
347 -- next level
348 if game.state == 4 and game.asteroids_count == 0 then
349 game.menu_time = game.milis
350 game.state = 6
351 end
352 if game.state == 6 and game.milis > game.menu_time + 3000 then
353 std.game.reset()
354 game.level = game.level + 1
355 game.state = 4
356 end
357 -- restart
358 if game.state == 5 and game.milis > game.menu_time + 3000 then
359 std.game.reset()
360 game.state = 4
361 if game.lifes == 0 then
362 game.score = 0
363 game.lifes = 3
364 game.level = 1
365 end
366 end
367end
368
369local function draw(std, game)
370 std.draw.clear(std.color.black)
371 local s = 0
372 if game.state == 1 then
373 local s2 = 0
374 local h = game.height/16
375 local graphics = game.graphics_fastest == 1 and 'rapido' or 'bonito'
376 local s = draw_logo(std, game, h*2)
377 std.draw.font('sans', 16)
378 std.draw.color(std.color.white)
379 if game.player_pos_x ~= (game.width/2) then
380 std.draw.text(game.width/2 - s, h*6, 'Continuar')
381 end
382 std.draw.text(game.width/2 - s, h*7, 'Novo Jogo')
383 std.draw.text(game.width/2 - s, h*8, 'Dificuldade')
384 std.draw.text(game.width/2 - s, h*9, 'Imortalidade')
385 std.draw.text(game.width/2 - s, h*10, 'Limitador')
386 std.draw.text(game.width/2 - s, h*11, 'Graficos')
387 std.draw.text(game.width/2 - s, h*12, 'Creditos')
388 std.draw.text(game.width/2 - s, h*13, 'Sair')
389 std.draw.line(game.width/2 - s, (h*(5+game.menu)) + 24, game.width/2 + s, (h*(5+game.menu)) + 24)
390 std.draw.color(std.color.red)
391 s2=std.draw.text(game.level)
392 std.draw.text(game.width/2 + s - s2, h*8, game.level)
393 s2=std.draw.text(game.imortal)
394 std.draw.text(game.width/2 + s - s2, h*9, game.imortal)
395 s2=std.draw.text(game.asteroids_max)
396 std.draw.text(game.width/2 + s - s2, h*10, game.asteroids_max)
397 s2=std.draw.text(graphics)
398 std.draw.text(game.width/2 + s - s2, h*11, graphics)
399 return
400 elseif game.state == 2 then
401 local height = game.height/4
402 local w = std.draw.text('Rodrigo Dornelles')
403 local anim = std.math.cos(std.math.cycle(game.milis, 200) * std.math.pi*2)
404 draw_logo(std, game, height, anim)
405 std.draw.font('sans', 16)
406 std.draw.color(std.color.white)
407 std.draw.text(game.width/2 - w/2 + (anim*0.5), height*2, 'Rodrigo Dornelles')
408 return
409 end
410 -- draw asteroids
411 std.draw.color(std.color.white)
412 local index = 1
413 while index <= #game.asteroid_size do
414 if game.asteroid_size[index] ~= -1 then
415 if game.graphics_fastest == 1 then
416 local s = game.asteroid_size[index]
417 std.draw.rect(1, game.asteroid_pos_x[index] - s/2, game.asteroid_pos_y[index] - s/2, s, s)
418 elseif game.asteroid_size[index] == game.asteroid_large_size then
419 std.draw.poly(1, game.asteroid_large, game.asteroid_pos_x[index], game.asteroid_pos_y[index])
420 elseif game.asteroid_size[index] == game.asteroid_mid_size then
421 std.draw.poly(1, game.asteroid_mid, game.asteroid_pos_x[index], game.asteroid_pos_y[index])
422 elseif game.asteroid_size[index] == game.asteroid_small_size then
423 std.draw.poly(1, game.asteroid_small, game.asteroid_pos_x[index], game.asteroid_pos_y[index])
424 else
425 std.draw.poly(1, game.asteroid_mini, game.asteroid_pos_x[index], game.asteroid_pos_y[index])
426 end
427 end
428 index = index + 1
429 end
430 -- draw player
431 std.draw.color(std.color.yellow)
432 if game.state ~= 5 then
433 std.draw.poly(2, game.spaceship, game.player_pos_x, game.player_pos_y, 3, game.player_angle)
434 end
435 -- laser bean
436 if game.laser_enabled and game.milis < game.laser_last_fire + game.laser_time_fire then
437 std.draw.color(std.color.green)
438 std.draw.line(game.laser_pos_x1, game.laser_pos_y1, game.laser_pos_x2, game.laser_pos_y2)
439 end
440 -- draw gui
441 local w = game.width/16
442 std.draw.color(std.color.black)
443 std.draw.rect(0, 0, 0, game.width, 32)
444 std.draw.color(std.color.white)
445 s=std.draw.text(8, 8, 'lifes:')
446 std.draw.text(8+s, 8, game.lifes)
447 s=std.draw.text(w*2, 8, 'level:')
448 std.draw.text(w*2+s, 8, game.level)
449 s=std.draw.text(w*4, 8, 'asteroids:')
450 std.draw.text(w*4+s, 8, game.asteroids_count)
451 s=std.draw.text(w*9, 8, 'score:')
452 std.draw.text(w*9+s, 8, game.score)
453 s=std.draw.text(w*12, 8, 'highscore:')
454 std.draw.text(w*12+s, 8, game.highscore)
455end
456
457local function exit(std, game)
458 game.highscore = std.math.max(game.score, game.highscore)
459 game.asteroid_pos_x = nil
460 game.asteroid_pos_y = nil
461 game.asteroid_spd_x = nil
462 game.asteroid_spd_y = nil
463 game.asteroid_size = nil
464 game.asteroid_large = nil
465 game.asteroid_mid = nil
466 game.asteroid_small = nil
467 game.asteroid_mini = nil
468end
469
470local P = {
471 meta={
472 title='AsteroidsTV',
473 author='RodrigoDornelles',
474 description='similar to the original but with lasers because televisions may have limited hardware.',
475 version='1.0.0'
476 },
477 config = {
478 require = 'math random',
479 fps_drop = 5,
480 fps_time = 5
481 },
482 callbacks={
483 init=init,
484 loop=loop,
485 draw=draw,
486 exit=exit
487 }
488}
489
490return P;
local function move(src_in, dist_out)
local function resume(self)
local function require(std, game, application)
local function success(self, handler_func)
local game
Definition main.lua:17
local std
Definition main.lua:18
local math
Definition draw.lua:1
local function draw(std, game)
local function init(std, game)
local function exit(std, game)
local function loop(std, game)