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/>.
22 love
.window
.setMode(1080/2.5, 1920/2.5)
24 w
= love
.graphics
.getWidth(),
25 h
= love
.graphics
.getHeight()
33 love
.graphics
.setColor(.5, .5, .8)
34 love
.graphics
.circle("fill", self
.x
, self
.y
, self
.size
/ 2)
36 move
= function(self
, x
, y
)
37 if trails
[1] then return end -- no movement until all trails expire
38 trails
:make(x
, y
, self
.x
, self
.y
)
42 destroy
= function(self
)
43 game
.score
= game
.score
- 10
44 explosions
:make(self
.x
, self
.y
, self
.size
)
53 make
= function(self
, x
, y
, sx
, sy
)
55 local d
= ((x
- sx
) ^
2 + (y
- sy
) ^
2) ^
0.5
56 local howmany
= math
.max(math
.min(d
/ expiration
, self
.max), self
.min)
58 for a
= self
.min, howmany
do
59 step
= 1 - a
/ howmany
62 x
= sx
- (sx
- x
) * step
,
63 y
= sy
- (sy
- y
) * step
68 love
.graphics
.setColor(1, .5, 0)
70 love
.graphics
.circle("line", self
[a
].x
, self
[a
].y
, self
[a
].size
/ 2)
73 expire
= function(self
, dt
)
74 self
.timer
= self
.timer
+ dt
75 if self
.timer
> self
.duration
and #self
> 0 then
77 self
.timer
= self
.timer
- self
.duration
80 check
= function(self
)
81 for a
= 1, #self
do for b
= 1, #enemies
do
82 if not enemies
[b
].dying
and collides(trails
[a
], enemies
[b
]) then
83 if enemies
[b
].isbullet
then
91 purge
= function(self
)
92 while #self
> 0 do self
[#self
] = nil end
99 self
.x
= self
.x
+ self
.speedx
100 self
.y
= self
.y
+ self
.speedy
101 self
.dying
= self
.dying
or
102 (self
.x
< 1 or self
.x
> screen
.w
or self
.y
< 1 or self
.y
> screen
.h
)
105 wander
= function(self
)
106 self
.x
= self
.x
+ self
.speedx
107 self
.y
= self
.y
+ self
.speedy
108 if self
.x
< 1 or self
.x
> screen
.w
then
109 self
.speedx
= - self
.speedx
111 if self
.y
< 1 or self
.y
> screen
.h
then
112 self
.speedy
= - self
.speedy
116 chase
= function(self
)
117 self
.speedx
= self
.speedx
* 199/200 + (player
.x
- self
.x
) / 2000
118 self
.speedy
= self
.speedy
* 199/200 + (player
.y
- self
.y
) / 2000
119 self
.speedx
= self
.speedx
/ math
.abs(self
.speedx
) * math
.min(3, math
.abs(self
.speedx
))
120 self
.speedy
= self
.speedy
/ math
.abs(self
.speedy
) * math
.min(3, math
.abs(self
.speedy
))
121 self
.x
= self
.x
+ self
.speedx
122 self
.y
= self
.y
+ self
.speedy
126 draw
= function(self
)
128 love
.graphics
.setColor(self
[a
].kind
.color
)
129 love
.graphics
.rectangle("fill", self
[a
].x
- self
[a
].size
/ 2, self
[a
].y
- self
[a
].size
/ 2, self
[a
].size
, self
[a
].size
)
132 make
= function(self
, kind
, x
, y
, speedx
, speedy
)
134 kind
= kind
, x
= x
, y
= y
, speedx
= speedx
, speedy
= speedy
,
138 explosions
:make(self
.x
, self
.y
, self
.size
)
139 game
.score
= game
.score
+ 1
141 -- will be disposed of in cleanup
145 move
= function(self
)
147 self
[a
] = self
[a
].kind
.movement(self
[a
])
150 check
= function(self
)
152 if not self
[a
].dying
and collides(player
, self
[a
]) then
156 if self
[a
].shoots_at
and game
.timer
>= self
[a
].shoots_at
then
158 self
[a
].shoots_at
= nil
161 if #self
== 0 and game
.happened
== #scenario
[game
.level
] then
165 shoot
= function(self
, a
)
168 cleanup
= function(self
)
169 for a
= #self
, 1, -1 do if self
[a
].dying
then
170 self
[a
] = self
[#self
]
179 movement
= enemies
.movements
.wander
,
184 movement
= enemies
.movements
.chase
,
189 movement
= enemies
.movements
.pass
,
194 movement
= enemies
.movements
.pass
,
201 make
= function(self
, x
, y
, size
)
209 grow
= function(self
)
210 -- must go backwards not to disrupt the loop
211 for a
= #self
, 1, -1 do
212 self
[a
].step
= self
[a
].step
+ 1
213 if self
[a
].step
> self
.maxsteps
then
218 destroy
= function(self
, a
)
219 self
[a
] = self
[#self
]
222 draw
= function(self
)
223 love
.graphics
.setColor(1,1,0)
225 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)
228 purge
= function(self
)
229 while #self
> 0 do self
[#self
] = nil end
234 ul
= { x
= 1, y
= 1 },
235 u
= { x
= screen
.w
/ 2, y
= 1 },
236 ur
= { x
= screen
.w
- 1, y
= 1 },
237 l
= { x
= 1, y
= screen
.h
/ 2 },
238 c
= { x
= screen
.w
/ 2, y
= screen
.h
/ 2 },
239 r
= { x
= screen
.w
- 1, y
= screen
.h
/ 2 },
240 dl
= { x
= 1, y
= screen
.h
- 1 },
241 d
= { x
= screen
.w
/ 2, y
= screen
.h
- 1 },
242 dr
= { x
= screen
.w
- 1, y
= screen
.h
- 1 },
249 player_origin
= origins
.c
,
251 { time
= 0, origin
= origins
.ul
, speed
= { 1, 1 }, kind
= enemies
.kinds
.passer
},
253 { time
= 2, origin
= origins
.l
, speed
= { 1, 0 }, kind
= enemies
.kinds
.passer
},
255 { time
= 4, origin
= origins
.dl
, speed
= { 1, -1 }, kind
= enemies
.kinds
.passer
},
257 { time
= 6, origin
= origins
.d
, speed
= { 0, -1 }, kind
= enemies
.kinds
.passer
},
262 player_origin
= origins
.d
,
264 { time
= 0, origin
= origins
.ul
, speed
= { 1, 1 }, kind
= enemies
.kinds
.wanderer
},
266 { time
= 1, origin
= origins
.r
, speed
= { -5, 0 }, kind
= enemies
.kinds
.bullet
},
268 { time
= 2, origin
= origins
.l
, speed
= { 1, 0 }, kind
= enemies
.kinds
.wanderer
},
273 player_origin
= origins
.c
,
275 { time
= 2, origin
= origins
.u
, speed
= { 1, 1 }, kind
= enemies
.kinds
.chaser
},
277 { time
= 2, origin
= origins
.dr
, speed
= { 1, 0 }, kind
= enemies
.kinds
.chaser
},
279 { time
= 3, origin
= origins
.dl
, speed
= { 1, 0 }, kind
= enemies
.kinds
.chaser
},
284 player_origin
= origins
.d
,
286 { time
= 0, origin
= origins
.ul
, speed
= { 0, 0 }, kind
= enemies
.kinds
.passer
, shoots_in
= 2 },
288 { time
= 0, origin
= origins
.ur
, speed
= { 0, 0 }, kind
= enemies
.kinds
.passer
, shoots_in
= 2.5 },
290 { time
= 0, origin
= origins
.c
, speed
= { 0, 0 }, kind
= enemies
.kinds
.passer
, shoots_in
= 3 },
306 function collides(obj1
, obj2
)
307 local o1x
, o1y
, o2x
, o2y
, d
308 o1x
= obj1
.x
+ obj1
.size
/ 2
309 o1y
= obj1
.y
+ obj1
.size
/ 2
310 o2x
= obj2
.x
+ obj2
.size
/ 2
311 o2y
= obj2
.y
+ obj2
.size
/ 2
312 d
= obj1
.size
/ 2 + obj2
.size
/ 2
313 return (o1x
- o2x
) ^
2 + (o1y
- o2y
) ^
2 < d ^
2
317 function beginlevel()
321 player
.x
= scenario
[game
.level
].player_origin
.x
322 player
.y
= scenario
[game
.level
].player_origin
.y
326 function happen(number)
327 game
.happened
= number
328 local s
= scenario
[game
.level
][number]
329 enemies
:make(s
.kind
, s
.origin
.x
, s
.origin
.y
, s
.speed
[1], s
.speed
[2])
330 enemies
[#enemies
].isbullet
= s
.kind
== enemies
.kinds
.bullet
331 enemies
[#enemies
].shoots_at
= game
.timer
+ s
.shoots_in
339 game
.level
= game
.level
+ 1
340 if game
.level
> #scenario
then
349 function love
.update(dt
)
351 love
.timer
.sleep(1/30 - dt
)
353 game
.timer
= game
.timer
+ dt
354 if game
.mode
== "play" then
360 for a
= game
.happened
+ 1, #scenario
[game
.level
] do
361 if game
.timer
> scenario
[game
.level
][a
].time
then
365 elseif game
.mode
== "title" then
366 if game
.timer
> 3 then
374 love
.graphics
.setColor(1,1,1)
375 love
.graphics
.print(math
.floor(game
.timer
) .. ':' .. game
.score
, 1, 2)
376 love
.graphics
.print('game.mode = ' .. game
.mode
, 1, 20)
377 love
.graphics
.print('#enemies = ' .. #enemies
, 1, 30)
382 if game
.mode
== "play" then
387 elseif game
.mode
== "title" then
388 love
.graphics
.setColor(1,1,1)
389 love
.graphics
.print(scenario
[game
.level
].title
, 100, 100)
390 elseif game
.mode
== "theend" then
391 love
.graphics
.setColor(1,1,1)
392 love
.graphics
.print('The End', 100, 100)
398 function love
.mousepressed(x
, y
, button
)
399 if game
.mode
== "title" or game
.mode
== "theend" then
401 elseif game
.mode
== "play" then
408 function love
.keypressed(k
)
409 if k
== "escape" or k
== "q" then
410 love
.event
.push("quit")