Style: indents with tabs instead of spaces.
[batto.git] / main.lua
blobb88a5e89c88deada1bd092ab2fd5cd6cfe5fae11
1 --[[
2 Batto! A clicking arcade game
3 Copyright 2018 Pajo <xpio at tut dot by>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
19 -- love 11.1
21 function love.load()
22 love.window.setMode(1000, 600)
23 screen = {
24 w = love.graphics.getWidth(),
25 h = love.graphics.getHeight()
28 player = {
29 x = screen.w / 2,
30 y = screen.h * 3 / 4,
31 size = screen.h / 6,
32 draw = function(self)
33 love.graphics.setColor(.5, .5, .8)
34 love.graphics.circle("fill", self.x, self.y, self.size / 2)
35 end,
36 move = function(self, x, y)
37 if trails[1] then return end
38 trails:make(x, y, self.x, self.y)
39 player.x = x
40 player.y = y
41 end,
42 destroy = function(self)
43 game.score = game.score - 10
44 explosions:make(self.x, self.y, self.size)
45 end,
48 trails = {
49 max = 20,
50 duration = 1 / 20,
51 timer = 0,
52 make = function(self, x, y, sx, sy)
53 local fall
54 for a = 1, self.max do
55 fall = 1 - a / self.max
56 trails[a] = {
57 size = player.size,
58 x = sx - (sx - x) * fall,
59 y = sy - (sy - y) * fall
61 end
62 end,
63 draw = function(self)
64 love.graphics.setColor(1, .5, 0)
65 for a = 1, #self do
66 love.graphics.circle("line", self[a].x, self[a].y, self[a].size / 2)
67 end
68 end,
69 expire = function(self, dt)
70 self.timer = self.timer + dt
71 if self.timer > self.duration and #self > 0 then
72 self[#self] = nil
73 self.timer = self.timer - self.duration
74 end
75 end,
76 check = function(self)
77 for a = 1, #self do for b = 1, #enemies do
78 if not enemies[b].dying and collides(trails[a], enemies[b]) then
79 enemies[b]:die()
80 end
81 end end
82 end,
85 enemies = {
86 movements = {
87 pass = function(self)
88 self.x = self.x + self.speedx
89 self.y = self.y + self.speedy
90 self.dying = self.dying or
91 (self.x < 1 or self.x > screen.w or self.y < 1 or self.y > screen.h)
92 return self
93 end,
94 wander = function(self)
95 self.x = self.x + self.speedx
96 self.y = self.y + self.speedy
97 if self.x < 1 or self.x > screen.w then
98 self.speedx = - self.speedx
99 end
100 if self.y < 1 or self.y > screen.h then
101 self.speedy = - self.speedy
103 return self
104 end,
105 chase = function(self)
106 self.speedx = self.speedx * 199/200 + (player.x - self.x) / 2000
107 self.speedy = self.speedy * 199/200 + (player.y - self.y) / 2000
108 self.speedx = self.speedx / math.abs(self.speedx) * math.min(3, math.abs(self.speedx))
109 self.speedy = self.speedy / math.abs(self.speedy) * math.min(3, math.abs(self.speedy))
110 self.x = self.x + self.speedx
111 self.y = self.y + self.speedy
112 return self
113 end,
115 draw = function(self)
116 for a = 1, #self do
117 love.graphics.setColor(self[a].kind.color)
118 love.graphics.rectangle("fill", self[a].x - self[a].size / 2, self[a].y - self[a].size / 2, self[a].size, self[a].size)
120 end,
121 make = function(self, kind, x, y, speedx, speedy)
122 self[#self + 1] = {
123 --kind = enemies.kinds.wanderer,
124 --x = math.random(size, screen.w - size),
125 --y = math.random(size, screen.h - size),
126 --speedx = math.random(-3, 3),
127 --speedy = math.random(-3, 3),
128 --size = size,
130 kind = kind, x = x, y = y, speedx = speedx, speedy = speedy,
131 size = kind.size,
132 dying = false,
133 die = function(self)
134 explosions:make(self.x, self.y, self.size)
135 game.score = game.score + 1
136 self.dying = true
137 -- will be disposed of in cleanup
138 end,
140 --[[
141 if #self % 5 == 0 then
142 self[#self].kind = enemies.kinds.chaser
144 if #self % 2 == 0 then
145 self[#self].kind = enemies.kinds.passer
148 end,
149 move = function(self)
150 for a = 1, #self do
151 self[a] = self[a].kind.movement(self[a])
153 end,
154 check = function(self)
155 for a = 1, #self do
156 if not self[a].dying and collides(player, self[a]) then
157 player:destroy()
158 self[a]:die()
161 end,
162 cleanup = function(self)
163 for a = #self, 1, -1 do if self[a].dying then
164 self[a] = self[#self]
165 self[#self] = nil
166 end end
167 end,
170 enemies.kinds = {
171 wanderer = {
172 color = {.8, 0, .4},
173 movement = enemies.movements.wander,
174 size = player.size,
176 chaser = {
177 color = {1, 0, 0},
178 movement = enemies.movements.chase,
179 size = player.size,
181 passer = {
182 color = {0, .5, .8},
183 movement = enemies.movements.pass,
184 size = player.size,
188 explosions = {
189 maxsteps = 15,
190 make = function(self, x, y, size)
191 self[#self + 1] = {
192 step = 0,
193 x = x,
194 y = y,
195 size = size,
197 end,
198 grow = function(self)
199 -- must go backwards not to disrupt the loop
200 for a = #self, 1, -1 do
201 self[a].step = self[a].step + 1
202 if self[a].step > self.maxsteps then
203 self:destroy(a)
206 end,
207 destroy = function(self, a)
208 self[a] = self[#self]
209 self[#self] = nil
210 end,
211 draw = function(self)
212 love.graphics.setColor(1,1,0)
213 for a = 1, #self do
214 love.graphics.rectangle("line", self[a].x - self[a].size / 2 - self[a].step * 2, self[a].y - self[a].size / 2 - self[a].step * 2, self[a].size + self[a].step * 4, self[a].size + self[a].step * 4)
216 end,
219 origins = {
220 ul = { x = 0, y = 0 },
221 u = { x = screen.w / 2, y = 0 },
222 ur = { x = screen.w, y = 0 },
223 l = { x = 0, y = screen.h / 2 },
224 c = { x = screen.w / 2, y = screen.h / 2 },
225 r = { x = screen.w, y = screen.h / 2 },
226 dl = { x = 0, y = screen.h },
227 d = { x = screen.w / 2, y = screen.h },
228 dr = { x = screen.w, y = screen.h },
231 scenario = {
234 title = "Wave 1",
235 player_origin = origins.c,
236 --1st enemy
237 { time = 0, origin = origins.ul, speed = { 1, 1 }, kind = enemies.kinds.passer },
238 --2nd enemy
239 { time = 2, origin = origins.l, speed = { 1, 0 }, kind = enemies.kinds.passer },
240 --3rd enemy
241 { time = 4, origin = origins.dl, speed = { 1, -1 }, kind = enemies.kinds.passer },
242 --4th enemy
243 { time = 6, origin = origins.d, speed = { 0, -1 }, kind = enemies.kinds.passer },
247 title = "Wave 2",
248 player_origin = origins.d,
249 --1st enemy
250 { time = 0, origin = origins.ul, speed = { 1, 1 }, kind = enemies.kinds.wanderer },
251 --2nd enemy
252 { time = 2, origin = origins.l, speed = { 1, 0 }, kind = enemies.kinds.wanderer },
256 game = {
257 timer = 0,
258 level = 1,
259 happened = 0,
260 score = 0,
261 mode = "title",
266 function collides(obj1, obj2)
267 local o1x, o1y, o2x, o2y, d
268 o1x = obj1.x + obj1.size / 2
269 o1y = obj1.y + obj1.size / 2
270 o2x = obj2.x + obj2.size / 2
271 o2y = obj2.y + obj2.size / 2
272 d = obj1.size / 2 + obj2.size / 2
273 return (o1x - o2x) ^ 2 + (o1y - o2y) ^ 2 < d ^ 2
277 function beginlevel()
278 game.timer = 0
279 game.mode = "play"
280 game.happened = 0
281 player.x = scenario[game.level].player_origin.x
282 player.y = scenario[game.level].player_origin.y
286 function happen(number)
287 game.happened = number
288 print(number, ' happened')
292 function love.update(dt)
293 if dt < 1/50 then
294 love.timer.sleep(1/30 - dt)
296 game.timer = game.timer + dt
297 if game.mode == "play" then
298 trails:expire(dt)
299 enemies:move()
300 enemies:check()
301 enemies:cleanup()
302 explosions:grow()
303 for a = game.happened + 1, #scenario[game.level] do
304 if game.timer > scenario[game.level][a].time then
305 happen(a)
308 elseif game.mode == "title" then
309 if game.timer > 3 then
310 beginlevel()
316 function osd()
317 love.graphics.setColor(1,1,1)
318 love.graphics.print(math.floor(game.timer) .. ':' .. game.score, 1, 2)
319 love.graphics.print('game.mode = ' .. game.mode, 1, 20)
323 function love.draw()
324 if game.mode == "play" then
325 trails:draw()
326 player:draw()
327 enemies:draw()
328 explosions:draw()
329 elseif game.mode == "title" then
330 love.graphics.setColor(1,1,1)
331 love.graphics.print(scenario[game.level].title, 100, 100)
333 osd()
337 function love.mousepressed(x, y, button)
338 if game.mode == "title" then
339 game.mode = "play"
340 elseif game.mode == "play" then
341 player:move(x, y)
342 trails:check()
343 enemies:make(
344 enemies.kinds.wanderer, -- kind
345 math.random(enemies.kinds.wanderer.size, screen.w - enemies.kinds.wanderer.size), -- x
346 math.random(enemies.kinds.wanderer.size, screen.h - enemies.kinds.wanderer.size), -- y
347 math.random(-3, 3), math.random(-3, 3) -- speedx, speedy
353 function love.keypressed(k)
354 if k == "escape" then
355 love.event.push("quit")