3 Copyright 2019 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/>.
30 {1, 0}, {1, -1}, {0, -1}, {-1, -1},
31 {-1, 0}, {-1, 1}, {0, 1}, {1, 1}
35 screen
= 'playfield', -- 'playfield', 'menu_objects', 'story_beginning' ...
37 change_screen
= function(self
, newscreen
, only_once
)
38 if only_once
and self
.shown_screens
[newscreen
] then return end
39 self
.shown_screens
[newscreen
] = true
40 menu_elements
.drawn
= nil
41 self
.screen
= newscreen
42 if newscreen
== 'playfield' then
44 elseif not screen
.bgcanvas
then
45 screen
.bgcanvas
= love
.graphics
.newCanvas(screen
.width
, screen
.height
)
46 screen
.bgcanvas
:renderTo(draw_playfield
)
49 phase
= 'play', -- see next_phase()
57 events
= function(self
, e
)
58 if e
== 'pill on' then
60 setuplevel(level_labels
.pill
)
61 elseif e
== 'pill off' then
62 self
.underpill
= false
64 setuplevel(level_labels
.devil
)
65 elseif e
== 'devil on' then
74 playfield
= {{}, {}, {}, {}}
80 function explode_clicked()
81 local p
= playfield
[playfield
.clickedx
][playfield
.clickedy
]
82 if p
.kind
== 'pill' then
83 game
:events('pill on')
86 if p
.kind
== 'memory' then -- remember
87 playfield
[playfield
.clickedx
][playfield
.clickedy
] = p
.memory
88 playfield
.remembered
= true
91 -- mark all purple arrow tiles if a purple arrow is clicked
92 -- even if only a memory
93 if p
.kind
== 'purple' then
94 game
.temppurple
= not game
.temppurple
95 inplayfield(function (x
, y
, p
) if p
.kind
== 'purple' or (p
.memory
and p
.memory
.kind
== 'purple') then
96 playfield
[x
][y
].hit
= true
97 local pf
= playfield
[x
][y
].memory
or playfield
[x
][y
]
102 -- clicked arrows should explode, even if player dies
103 -- to explain turning of yellow arrows, and just for fun
104 -- first, copy them to new table
105 local newtile
= recognizeink('0')
106 if p
.shape
== 'x' or p
.shape
== '+' then -- perhaps needs more conditions later
108 newtile
.color
= images
[p
.name
].color
109 newtile
.shape
= p
.shape
110 newtile
.gift
= p
.gift
111 newtile
.arrowgift
= p
.arrowgift
114 newtile
.sleeping_name
= p
.sleeping_name
or p
.name
117 for dir
, arr
in ipairs(p
.arrows
) do
118 -- arrows are false or integer: distance from tile center
119 newtile
.arrows
[dir
] = arr
121 local discard
, distance
, pf
= check_direction(playfield
.clickedx
, playfield
.clickedy
, directions
[dir
][1], directions
[dir
][2])
123 if pf
and pf
.kind
== 'yellow' then
126 end -- mark yellow arrows for rotation
127 explode_arrow(playfield
.clickedx
, playfield
.clickedy
, dir
, distance
, false, target
)
131 if game
.underpill
then return end
132 playfield
[playfield
.clickedx
][playfield
.clickedy
] = newtile
136 function forget_clicked()
137 --only when underpill, transform clicked (and exploded) arrow to the memory
138 if not game
.underpill
then return end
139 if playfield
.remembered
then playfield
.remembered
= false ; return end
140 local p
= playfield
[playfield
.clickedx
][playfield
.clickedy
]
141 local p_d
= p
.devil
-- to preserve devil after becoming a memory
142 playfield
[playfield
.clickedx
][playfield
.clickedy
] = recognizeink(get_memory_name(p
.name
))
143 if playfield
[playfield
.clickedx
][playfield
.clickedy
].memory
then
144 playfield
[playfield
.clickedx
][playfield
.clickedy
].memory
.devil
= p_d
148 function check_player_death()
149 inplayfield(function(x
, y
, p
)
150 if not p
.arrows
then return end
151 for direction
, arrow
in ipairs(p
.arrows
) do
152 if not p
.backgrounded
and arrow
then
153 local death
, distance
= check_direction(x
, y
,
154 directions
[direction
][1], directions
[direction
][2])
156 explode_arrow(x
, y
, direction
, distance
, true) -- kill=true
165 function influence_and_rotate()
166 -- devil influence on yellow arrows
169 local yellow_arrows
= {}
170 inplayfield(function(x
, y
, p
)
171 if p
.kind
== 'yellow' then
173 -- don't insert last influenced yellow arrow
174 playfield
[x
][y
].devil
= nil -- unset but remember in case there are no more arrows
176 elseif not (not playfield
.remembered
and
177 playfield
.clickedx
== x
and playfield
.clickedy
== y
) then
178 -- don't insert arrow just passing into memory
179 table.insert(yellow_arrows
, {x
, y
})
183 if #yellow_arrows
> 0 then
184 -- choose among available yellow arrows
185 dx
, dy
= unpack(yellow_arrows
[math
.random(#yellow_arrows
)])
186 playfield
[dx
][dy
].center
= 'dot'
187 if playfield
[dx
][dy
].hit
then
188 rotate_arrows(dx
, dy
, 0) -- will just remove dot
190 rotate_arrows(dx
, dy
, -1)
192 playfield
[dx
][dy
].hit
= false -- so won't be rotated later
194 if dx
then --set devil on some of the arrows in foreground
195 playfield
[dx
][dy
].devil
= true
196 -- unset any devil in memories
197 inplayfield(function(x
, y
, p
)
198 if p
.memory
and p
.memory
.devil
then
199 playfield
[x
][y
].memory
.devil
= nil
202 end -- new or last dx, dy in case there was only 1
204 -- turn yellow and purple arrows
205 inplayfield(function(x
, y
, p
)
206 if (p
.kind
== 'yellow' or p
.kind
== 'purple' or (p
.kind
== 'memory' and p
.memory
.kind
== 'purple')) and p
.hit
then
211 -- wake regenerating arrows up
212 inplayfield(function(x
, y
, p
)
213 if p
.sleep
and p
.sleep
> 0 then
214 p
.sleep
= p
.sleep
- 1
216 playfield
[x
][y
] = recognizeink(p
.sleeping_name
)
222 inplayfield(function(x
, y
, p
)
223 if p
.kind
== 'red' or p
.kind
== 'loadred' then rotate_arrows(x
, y
) end
228 function next_phase()
229 local nextphases
= {'rotate', 'forget', 'die', 'level', 'play', 'explode'}
230 setmetatable(nextphases
, {__index
= function (table, key
)
232 if rawget(table, a
) == key
then return table[a
+ 1] or table[1] end
236 game
.phase
= nextphases
[game
.phase
] or 'play' -- nextphases['opengifts'] == nil , etc.
237 if game
.phase
== 'explode' then -- 1. explode clicked arrow
239 elseif game
.phase
== 'rotate' then -- 2.
240 influence_and_rotate()
241 elseif game
.phase
== 'forget' then -- 3. pass to memory if underpill
243 elseif game
.phase
== 'die' then -- 4. check for player death
245 elseif game
.phase
== 'level' then -- 5. check for level end
247 elseif game
.phase
== 'play' then -- 6. await next click
252 function remember_arrows()
253 if game
.underpill
then return end
254 playfield
.clickedx
, playfield
.clickedy
= nil, nil
255 inplayfield(function (x
, y
, p
)
256 if p
.memory
then playfield
[x
][y
] = p
.memory
end
261 function love
.resize()
262 screen
.w
= love
.graphics
.getWidth()
263 screen
.h
= love
.graphics
.getHeight()
264 screen
.playfieldsize
= math
.min(screen
.w
, screen
.h
) * .9
265 screen
.tilesize
= screen
.playfieldsize
/ 4
266 screen
.playfieldx
= (screen
.w
/ screen
.playfieldsize
- 1) * 2
267 screen
.playfieldy
= (screen
.h
/ screen
.playfieldsize
- 1) * 2
268 if screen
.bgcanvas
then
269 screen
.bgcanvas
:renderTo(love
.graphics
.clear
)
270 screen
.bgcanvas
:renderTo(draw_playfield
)
272 screen
.defaultfont
= love
.graphics
.newFont(screen
.tilesize
/ 5)
276 function love
.update(dt
)
277 if game
.screen
~= 'playfield' then return end
279 love
.timer
.sleep(1/30 - dt
)
281 game
.timer
= game
.timer
+ dt
287 love
.graphics
.setColor(1, 1, 1)
288 love
.graphics
.print(game
.currentlevel
, 1, 2) --debug
289 if anim
[1] then love
.graphics
.print(anim
[1].step
, 1, 12) end
293 function inplayfield(f
)
294 for y
= 1, 4 do for x
= 1, 4 do
295 f(x
, y
, playfield
[x
][y
])
301 if game
.screen
== 'playfield' then
310 function check_direction(x
, y
, dx
, dy
)
313 xx
, yy
= x
+ dx
* a
, y
+ dy
* a
314 if not playfield
[xx
] then return false, a
end -- arrow hits playfield edge
315 p
= playfield
[xx
][yy
]
316 if not p
then return false, a
end --||--
317 if xx
== playfield
.clickedx
and yy
== playfield
.clickedy
then return true, a
end -- arrow hits player
318 if not p
.passive
then return false, a
, p
end -- arrow hits another object. return that object
324 function check_level()
325 -- checks for the level end (when all arrows are eliminated)
326 -- game should generally check for arrows not shapes!
327 local anymemories
, anyactivearrows
= false, false
328 inplayfield(function(x
, y
, p
)
329 anymemories
= anymemories
or p
.memory
330 if p
.arrows
then for direction
, arrow
in ipairs(p
.arrows
) do
331 anyactivearrows
= anyactivearrows
or arrow
334 if game
.underpill
then
335 if not anymemories
and not game
.died
then
336 game
.purple
= game
.temppurple
--!!
337 setuplevel(game
.currentlevel
- 1)
340 if not anyactivearrows
and not open_gifts() then
344 game
.purple
= game
.temppurple
345 setuplevel(game
.currentlevel
+ 1)
352 function open_gifts()
353 if game
.underpill
then return end
355 inplayfield(function(x
, y
, p
)
356 if p
.name
== 'J' or p
.name
== 'K' then
357 if p
.name
== 'K' then game
:events('devil on') end
358 playfield
[x
][y
] = recognizeink(p
.gift
)
359 playfield
[x
][y
].gift
= p
.gift
360 playfield
[x
][y
].ingift
= true
366 --playfield.clickedx, playfield.clickedy = nil, nil
367 --necessary to preserve clicked for opening arrowgifts
373 function clicked(mx
, my
)
374 if game
.screen
~= 'playfield' then
377 if game
.phase
~= 'play' then
386 -- clickedx, clickedy from previous turn
387 if playfield
.clickedx
and
388 playfield
[playfield
.clickedx
][playfield
.clickedy
].arrowgift
and
389 not (mx
== playfield
.clickedx
and my
== playfield
.clickedy
) and
390 not game
.underpill
then
391 local gift
= playfield
[playfield
.clickedx
][playfield
.clickedy
].gift
393 animate_arrow_gift(playfield
.clickedx
, playfield
.clickedy
)
395 playfield
[playfield
.clickedx
][playfield
.clickedy
].gift
= gift
397 -- new clickedx, clickedy
398 playfield
.clickedx
, playfield
.clickedy
= mx
, my
399 if playfield
[mx
][my
].onclick
then playfield
[mx
][my
].onclick() end
405 function isin(mx
, my
, x
, y
, w
, h
)
406 return mx
>= x
and mx
<= x
+ w
and my
>= y
and my
<= y
+ h
410 function love
.mousepressed(mx
, my
, button
)
411 if game
.screen
== 'playfield' then
412 inplayfield(function (x
, y
, p
)
413 if isin(mx
, my
, (screen
.playfieldx
+ x
- 1) * screen
.tilesize
, (screen
.playfieldy
+ y
- 1) * screen
.tilesize
, screen
.tilesize
, screen
.tilesize
) then
423 function love
.keypressed(k
)
424 if k
== 'escape' then
425 if game
.screen
~= 'playfield' then
426 game
:change_screen('playfield')
428 love
.event
.push('quit')
431 love
.event
.push('quit')
433 setuplevel(game
.currentlevel
+ 1)
435 setuplevel(game
.currentlevel
- 1)
437 game
.purple
= not game
.purple
438 print('purple', game
.purple
)
440 game
.underpill
= not game
.underpill
441 print('underpill', game
.underpill
)
443 game
.devil
= not game
.devil
444 print('devil', game
.devil
)
446 if game
.screen
~= 'playfield' then
447 game
:change_screen('playfield')
449 game
:change_screen('menu_levels')